Add 2012 to FSF copyright years for Emacs files (do not merge to trunk)
[bpt/emacs.git] / lisp / mail / smtpmail.el
CommitLineData
092af6d8 1;;; smtpmail.el --- simple SMTP protocol (RFC 821) for sending mail
24975917 2
20e43520 3;; Copyright (C) 1995, 1996, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
49f70d46 4;; 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
24975917
RS
5
6;; Author: Tomoji Kagatani <kagatani@rbc.ncl.omron.co.jp>
9056f1c9
RS
7;; Maintainer: Simon Josefsson <simon@josefsson.org>
8;; w32 Maintainer: Brian D. Carlstrom <bdc@ai.mit.edu>
c50d5ce0 9;; ESMTP support: Simon Leinen <simon@switch.ch>
2b5c7e03
GM
10;; Hacked by Mike Taylor, 11th October 1999 to add support for
11;; automatically appending a domain to RCPT TO: addresses.
9056f1c9 12;; AUTH=LOGIN support: Stephen Cranefield <scranefield@infoscience.otago.ac.nz>
24975917
RS
13;; Keywords: mail
14
15;; This file is part of GNU Emacs.
16
b1fc2b50 17;; GNU Emacs is free software: you can redistribute it and/or modify
24975917 18;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
19;; the Free Software Foundation, either version 3 of the License, or
20;; (at your option) any later version.
24975917
RS
21
22;; GNU Emacs is distributed in the hope that it will be useful,
23;; but WITHOUT ANY WARRANTY; without even the implied warranty of
24;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25;; GNU General Public License for more details.
26
27;; You should have received a copy of the GNU General Public License
b1fc2b50 28;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24975917
RS
29
30;;; Commentary:
31
32;; Send Mail to smtp host from smtpmail temp buffer.
24975917 33
f38d3514 34;; Please add these lines in your .emacs(_emacs) or use customize.
24975917 35;;
f38d3514 36;;(setq send-mail-function 'smtpmail-send-it) ; if you use `mail'
08b10dd4 37;;(setq message-send-mail-function 'smtpmail-send-it) ; if you use message/Gnus
24975917 38;;(setq smtpmail-default-smtp-server "YOUR SMTP HOST")
24975917 39;;(setq smtpmail-local-domain "YOUR DOMAIN NAME")
e889eabc 40;;(setq smtpmail-sendto-domain "YOUR DOMAIN NAME")
f38d3514 41;;(setq smtpmail-debug-info t) ; only to debug problems
60ed3fa5 42;;(setq smtpmail-auth-credentials ; or use ~/.authinfo
9056f1c9
RS
43;; '(("YOUR SMTP HOST" 25 "username" "password")))
44;;(setq smtpmail-starttls-credentials
45;; '(("YOUR SMTP HOST" 25 "~/.my_smtp_tls.key" "~/.my_smtp_tls.cert")))
758b7b09
SJ
46;; Where the 25 equals the value of `smtpmail-smtp-service', it can be an
47;; integer or a string, just as long as they match (eq).
24975917 48
75da36cc
RW
49;; To queue mail, set `smtpmail-queue-mail' to t and use
50;; `smtpmail-send-queued-mail' to send.
308bc5d8 51
9056f1c9
RS
52;; Modified by Stephen Cranefield <scranefield@infoscience.otago.ac.nz>,
53;; 22/6/99, to support SMTP Authentication by the AUTH=LOGIN mechanism.
54;; See http://help.netscape.com/products/server/messaging/3x/info/smtpauth.html
55;; Rewritten by Simon Josefsson to use same credential variable as AUTH
56;; support below.
57
58;; Modified by Simon Josefsson <jas@pdc.kth.se>, 22/2/99, to support SMTP
59;; Authentication by the AUTH mechanism.
60;; See http://www.ietf.org/rfc/rfc2554.txt
61
62;; Modified by Simon Josefsson <simon@josefsson.org>, 2000-10-07, to support
63;; STARTTLS. Requires external program
64;; ftp://ftp.opaopa.org/pub/elisp/starttls-*.tar.gz.
65;; See http://www.ietf.org/rfc/rfc2246.txt, http://www.ietf.org/rfc/rfc2487.txt
308bc5d8 66
24975917
RS
67;;; Code:
68
69(require 'sendmail)
863e372f 70(autoload 'starttls-any-program-available "starttls")
9056f1c9
RS
71(autoload 'starttls-open-stream "starttls")
72(autoload 'starttls-negotiate "starttls")
73(autoload 'mail-strip-quoted-names "mail-utils")
21bf0d6c
SM
74(autoload 'message-make-date "message")
75(autoload 'message-make-message-id "message")
9056f1c9 76(autoload 'rfc2104-hash "rfc2104")
60ed3fa5 77(autoload 'netrc-parse "netrc")
0d1c958a
RS
78(autoload 'netrc-machine "netrc")
79(autoload 'netrc-get "netrc")
b1cd57bc 80(autoload 'password-read "password-cache")
20e43520 81(autoload 'auth-source-user-or-password "auth-source")
e64a3841 82
24975917 83;;;
00ed33e7
RS
84(defgroup smtpmail nil
85 "SMTP protocol for sending mail."
86 :group 'mail)
24975917 87
00ed33e7
RS
88
89(defcustom smtpmail-default-smtp-server nil
0fc1e8fe 90 "Specify default SMTP server.
b559a041 91This only has effect if you specify it before loading the smtpmail library."
00ed33e7
RS
92 :type '(choice (const nil) string)
93 :group 'smtpmail)
94
a8ba4429 95(defcustom smtpmail-smtp-server
e2f7c221 96 (or (getenv "SMTPSERVER") smtpmail-default-smtp-server)
0fc1e8fe 97 "The name of the host running SMTP server."
00ed33e7
RS
98 :type '(choice (const nil) string)
99 :group 'smtpmail)
24975917 100
00ed33e7 101(defcustom smtpmail-smtp-service 25
0fc1e8fe 102 "SMTP service port number.
1429d866 103The default value would be \"smtp\" or 25."
9056f1c9 104 :type '(choice (integer :tag "Port") (string :tag "Service"))
00ed33e7 105 :group 'smtpmail)
24975917 106
00ed33e7 107(defcustom smtpmail-local-domain nil
0fc1e8fe 108 "Local domain name without a host name.
1429d866 109If the function `system-name' returns the full internet address,
00ed33e7
RS
110don't define this value."
111 :type '(choice (const nil) string)
112 :group 'smtpmail)
113
e889eabc 114(defcustom smtpmail-sendto-domain nil
0fc1e8fe 115 "Local domain name without a host name.
e889eabc
GM
116This is appended (with an @-sign) to any specified recipients which do
117not include an @-sign, so that each RCPT TO address is fully qualified.
118\(Some configurations of sendmail require this.)
119
120Don't bother to set this unless you have get an error like:
121 Sending failed; SMTP protocol error
122when sending mail, and the *trace of SMTP session to <somewhere>*
123buffer includes an exchange like:
124 RCPT TO: <someone>
75da36cc 125 501 <someone>: recipient address must contain a domain."
e889eabc
GM
126 :type '(choice (const nil) string)
127 :group 'smtpmail)
128
00ed33e7 129(defcustom smtpmail-debug-info nil
9056f1c9
RS
130 "Whether to print info in buffer *trace of SMTP session to <somewhere>*.
131See also `smtpmail-debug-verb' which determines if the SMTP protocol should
132be verbose as well."
133 :type 'boolean
134 :group 'smtpmail)
135
136(defcustom smtpmail-debug-verb nil
137 "Whether this library sends the SMTP VERB command or not.
138The commands enables verbose information from the SMTP server."
00ed33e7
RS
139 :type 'boolean
140 :group 'smtpmail)
141
0fc1e8fe
GM
142(defcustom smtpmail-code-conv-from nil
143 "Coding system for encoding outgoing mail.
144Used for the value of `sendmail-coding-system' when
145`select-message-coding-system' is called. "
146 :type 'coding-system
00ed33e7 147 :group 'smtpmail)
24975917 148
a8ba4429 149(defcustom smtpmail-queue-mail nil
0fc1e8fe 150 "Non-nil means mail is queued; otherwise it is sent immediately.
308bc5d8
RS
151If queued, it is stored in the directory `smtpmail-queue-dir'
152and sent with `smtpmail-send-queued-mail'."
153 :type 'boolean
154 :group 'smtpmail)
155
156(defcustom smtpmail-queue-dir "~/Mail/queued-mail/"
0fc1e8fe 157 "Directory where `smtpmail.el' stores queued mail."
308bc5d8
RS
158 :type 'directory
159 :group 'smtpmail)
160
60ed3fa5
RS
161(defcustom smtpmail-auth-credentials "~/.authinfo"
162 "Specify username and password for servers, directly or via .netrc file.
163This variable can either be a filename pointing to a file in netrc(5)
164format, or list of four-element lists that contain, in order,
9056f1c9 165`servername' (a string), `port' (an integer), `user' (a string) and
60ed3fa5
RS
166`password' (a string, or nil to query the user when needed). If you
167need to enter a `realm' too, add it to the user string, so that it
168looks like `user@realm'."
169 :type '(choice file
170 (repeat (list (string :tag "Server")
75da36cc
RW
171 (integer :tag "Port")
172 (string :tag "Username")
173 (choice (const :tag "Query when needed" nil)
60ed3fa5 174 (string :tag "Password")))))
bf247b6e 175 :version "22.1"
9056f1c9
RS
176 :group 'smtpmail)
177
178(defcustom smtpmail-starttls-credentials '(("" 25 "" ""))
179 "Specify STARTTLS keys and certificates for servers.
180This is a list of four-element list with `servername' (a string),
8bf5f8c5
SJ
181`port' (an integer), `key' (a filename) and `certificate' (a
182filename).
183If you do not have a certificate/key pair, leave the `key' and
184`certificate' fields as `nil'. A key/certificate pair is only
185needed if you want to use X.509 client authenticated
186connections."
9056f1c9
RS
187 :type '(repeat (list (string :tag "Server")
188 (integer :tag "Port")
189 (file :tag "Key")
190 (file :tag "Certificate")))
191 :version "21.1"
192 :group 'smtpmail)
193
1b62b062 194(defcustom smtpmail-warn-about-unknown-extensions nil
0fc1e8fe 195 "If set, print warnings about unknown SMTP extensions.
1b62b062
GM
196This is mainly useful for development purposes, to learn about
197new SMTP extensions that might be useful to support."
198 :type 'boolean
fa87f673 199 :version "21.1"
1b62b062
GM
200 :group 'smtpmail)
201
0fc1e8fe 202(defcustom smtpmail-queue-index-file "index"
1429d866 203 "File name of queued mail index.
0fc1e8fe
GM
204This is relative to `smtpmail-queue-dir'."
205 :type 'string
206 :group 'smtpmail)
207
208;; End of customizable variables.
209
308bc5d8 210
fb035bbf
RS
211(defvar smtpmail-address-buffer)
212(defvar smtpmail-recipient-address-list)
213
08b10dd4
RS
214(defvar smtpmail-queue-counter 0)
215
fb035bbf
RS
216;; Buffer-local variable.
217(defvar smtpmail-read-point)
218
e81c51f0 219(defconst smtpmail-auth-supported '(cram-md5 plain login)
9927d250
SJ
220 "List of supported SMTP AUTH mechanisms.
221The list is in preference order.")
9056f1c9 222
850ed7b3 223(defvar smtpmail-mail-address nil
2265c623 224 "Value to use for envelope-from address for mail from ambient buffer.")
850ed7b3 225
f38d3514 226;;;###autoload
24975917
RS
227(defun smtpmail-send-it ()
228 (let ((errbuf (if mail-interactive
229 (generate-new-buffer " smtpmail errors")
230 0))
231 (tembuf (generate-new-buffer " smtpmail temp"))
232 (case-fold-search nil)
24975917 233 delimline
83af570e 234 (mailbuf (current-buffer))
ff981226
GM
235 ;; Examine this variable now, so that
236 ;; local binding in the mail buffer will take effect.
2265c623
GM
237 (smtpmail-mail-address
238 (or (and mail-specify-envelope-from (mail-envelope-from))
239 user-mail-address))
83af570e
KH
240 (smtpmail-code-conv-from
241 (if enable-multibyte-characters
242 (let ((sendmail-coding-system smtpmail-code-conv-from))
243 (select-message-coding-system)))))
24975917 244 (unwind-protect
937e6a56 245 (with-current-buffer tembuf
24975917 246 (erase-buffer)
75da36cc
RW
247 ;; Use the same `buffer-file-coding-system' as in the mail
248 ;; buffer, otherwise any `write-region' invocations (e.g., in
362e23e1
EZ
249 ;; mail-do-fcc below) will annoy with asking for a suitable
250 ;; encoding.
251 (set-buffer-file-coding-system smtpmail-code-conv-from nil t)
24975917
RS
252 (insert-buffer-substring mailbuf)
253 (goto-char (point-max))
254 ;; require one newline at the end.
255 (or (= (preceding-char) ?\n)
256 (insert ?\n))
257 ;; Change header-delimiter to be what sendmail expects.
92a3f23d 258 (mail-sendmail-undelimit-header)
24975917 259 (setq delimline (point-marker))
75da36cc 260 ;; (sendmail-synch-aliases)
24975917
RS
261 (if mail-aliases
262 (expand-mail-aliases (point-min) delimline))
263 (goto-char (point-min))
264 ;; ignore any blank lines in the header
265 (while (and (re-search-forward "\n\n\n*" delimline t)
266 (< (point) delimline))
267 (replace-match "\n"))
268 (let ((case-fold-search t))
5feeeae2
RS
269 ;; We used to process Resent-... headers here,
270 ;; but it was not done properly, and the job
75da36cc 271 ;; is done correctly in `smtpmail-deduce-address-list'.
24975917
RS
272 ;; Don't send out a blank subject line
273 (goto-char (point-min))
5feeeae2
RS
274 (if (re-search-forward "^Subject:\\([ \t]*\n\\)+\\b" delimline t)
275 (replace-match "")
276 ;; This one matches a Subject just before the header delimiter.
277 (if (and (re-search-forward "^Subject:\\([ \t]*\n\\)+" delimline t)
278 (= (match-end 0) delimline))
279 (replace-match "")))
0e2701ca
RS
280 ;; Put the "From:" field in unless for some odd reason
281 ;; they put one in themselves.
282 (goto-char (point-min))
283 (if (not (re-search-forward "^From:" delimline t))
850ed7b3 284 (let* ((login smtpmail-mail-address)
0e2701ca
RS
285 (fullname (user-full-name)))
286 (cond ((eq mail-from-style 'angles)
287 (insert "From: " fullname)
288 (let ((fullname-start (+ (point-min) 6))
289 (fullname-end (point-marker)))
290 (goto-char fullname-start)
291 ;; Look for a character that cannot appear unquoted
292 ;; according to RFC 822.
293 (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]"
294 fullname-end 1)
295 (progn
296 ;; Quote fullname, escaping specials.
297 (goto-char fullname-start)
298 (insert "\"")
299 (while (re-search-forward "[\"\\]"
300 fullname-end 1)
301 (replace-match "\\\\\\&" t))
302 (insert "\""))))
303 (insert " <" login ">\n"))
304 ((eq mail-from-style 'parens)
305 (insert "From: " login " (")
306 (let ((fullname-start (point)))
307 (insert fullname)
308 (let ((fullname-end (point-marker)))
309 (goto-char fullname-start)
310 ;; RFC 822 says \ and nonmatching parentheses
311 ;; must be escaped in comments.
312 ;; Escape every instance of ()\ ...
313 (while (re-search-forward "[()\\]" fullname-end 1)
314 (replace-match "\\\\\\&" t))
315 ;; ... then undo escaping of matching parentheses,
316 ;; including matching nested parentheses.
317 (goto-char fullname-start)
a8ba4429 318 (while (re-search-forward
0e2701ca
RS
319 "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
320 fullname-end 1)
321 (replace-match "\\1(\\3)" t)
322 (goto-char fullname-start))))
323 (insert ")\n"))
324 ((null mail-from-style)
325 (insert "From: " login "\n")))))
21bf0d6c
SM
326 ;; Insert a `Message-Id:' field if there isn't one yet.
327 (goto-char (point-min))
328 (unless (re-search-forward "^Message-Id:" delimline t)
329 (insert "Message-Id: " (message-make-message-id) "\n"))
330 ;; Insert a `Date:' field if there isn't one yet.
331 (goto-char (point-min))
332 (unless (re-search-forward "^Date:" delimline t)
333 (insert "Date: " (message-make-date) "\n"))
362e23e1
EZ
334 ;; Possibly add a MIME header for the current coding system
335 (let (charset)
336 (goto-char (point-min))
337 (and (eq mail-send-nonascii 'mime)
338 (not (re-search-forward "^MIME-version:" delimline t))
339 (progn (skip-chars-forward "\0-\177")
340 (/= (point) (point-max)))
341 smtpmail-code-conv-from
342 (setq charset
343 (coding-system-get smtpmail-code-conv-from
344 'mime-charset))
345 (goto-char delimline)
346 (insert "MIME-version: 1.0\n"
347 "Content-type: text/plain; charset="
348 (symbol-name charset)
349 "\nContent-Transfer-Encoding: 8bit\n")))
24975917
RS
350 ;; Insert an extra newline if we need it to work around
351 ;; Sun's bug that swallows newlines.
352 (goto-char (1+ delimline))
353 (if (eval mail-mailer-swallows-blank-line)
354 (newline))
0e2701ca
RS
355 ;; Find and handle any FCC fields.
356 (goto-char (point-min))
357 (if (re-search-forward "^FCC:" delimline t)
75da36cc 358 ;; Force `mail-do-fcc' to use the encoding of the mail
362e23e1
EZ
359 ;; buffer to encode outgoing messages on FCC files.
360 (let ((coding-system-for-write smtpmail-code-conv-from))
361 (mail-do-fcc delimline)))
24975917 362 (if mail-interactive
21bf0d6c 363 (with-current-buffer errbuf
24975917 364 (erase-buffer))))
b8d747b9
KH
365 ;; Encode the header according to RFC2047.
366 (mail-encode-header (point-min) delimline)
24975917 367 ;;
24975917
RS
368 (setq smtpmail-address-buffer (generate-new-buffer "*smtp-mail*"))
369 (setq smtpmail-recipient-address-list
75da36cc 370 (smtpmail-deduce-address-list tembuf (point-min) delimline))
24975917 371 (kill-buffer smtpmail-address-buffer)
a8ba4429 372
24975917 373 (smtpmail-do-bcc delimline)
75da36cc 374 ;; Send or queue
308bc5d8
RS
375 (if (not smtpmail-queue-mail)
376 (if (not (null smtpmail-recipient-address-list))
a8ba4429 377 (if (not (smtpmail-via-smtp
308bc5d8
RS
378 smtpmail-recipient-address-list tembuf))
379 (error "Sending failed; SMTP protocol error"))
380 (error "Sending failed; no recipients"))
bf6e31bc
SJ
381 (let* ((file-data
382 (expand-file-name
383 (format "%s_%i"
b2364eaa 384 (format-time-string "%Y-%m-%d_%H:%M:%S")
bf6e31bc
SJ
385 (setq smtpmail-queue-counter
386 (1+ smtpmail-queue-counter)))
387 smtpmail-queue-dir))
388 (file-data (convert-standard-filename file-data))
389 (file-elisp (concat file-data ".el"))
308bc5d8
RS
390 (buffer-data (create-file-buffer file-data))
391 (buffer-elisp (create-file-buffer file-elisp))
392 (buffer-scratch "*queue-mail*"))
2fbc1118
JB
393 (unless (file-exists-p smtpmail-queue-dir)
394 (make-directory smtpmail-queue-dir t))
21bf0d6c 395 (with-current-buffer buffer-data
308bc5d8 396 (erase-buffer)
95e4cc85
EZ
397 (set-buffer-file-coding-system
398 ;; We will be reading the file with no-conversion in
399 ;; smtpmail-send-queued-mail below, so write it out
400 ;; with Unix EOLs.
401 (coding-system-change-eol-conversion
402 (or smtpmail-code-conv-from 'undecided)
403 'unix)
404 nil t)
b0aea09e 405 (insert-buffer-substring tembuf)
308bc5d8
RS
406 (write-file file-data)
407 (set-buffer buffer-elisp)
408 (erase-buffer)
409 (insert (concat
410 "(setq smtpmail-recipient-address-list '"
411 (prin1-to-string smtpmail-recipient-address-list)
a8ba4429 412 ")\n"))
308bc5d8
RS
413 (write-file file-elisp)
414 (set-buffer (generate-new-buffer buffer-scratch))
415 (insert (concat file-data "\n"))
a8ba4429
SS
416 (append-to-file (point-min)
417 (point-max)
0fc1e8fe
GM
418 (expand-file-name smtpmail-queue-index-file
419 smtpmail-queue-dir)))
308bc5d8
RS
420 (kill-buffer buffer-scratch)
421 (kill-buffer buffer-data)
422 (kill-buffer buffer-elisp))))
24975917
RS
423 (kill-buffer tembuf)
424 (if (bufferp errbuf)
425 (kill-buffer errbuf)))))
426
74f39ce9 427;;;###autoload
308bc5d8
RS
428(defun smtpmail-send-queued-mail ()
429 "Send mail that was queued as a result of setting `smtpmail-queue-mail'."
430 (interactive)
7e3fa9f5 431 (with-temp-buffer
75da36cc
RW
432 ;; Get index, get first mail, send it, update index, get second
433 ;; mail, send it, etc...
0fc1e8fe
GM
434 (let ((file-msg "")
435 (qfile (expand-file-name smtpmail-queue-index-file
436 smtpmail-queue-dir)))
437 (insert-file-contents qfile)
88f2c9ad 438 (goto-char (point-min))
308bc5d8 439 (while (not (eobp))
21bf0d6c 440 (setq file-msg (buffer-substring (point) (line-end-position)))
308bc5d8 441 (load file-msg)
19a0baf7
EZ
442 ;; Insert the message literally: it is already encoded as per
443 ;; the MIME headers, and code conversions might guess the
444 ;; encoding wrongly.
7e3fa9f5
EZ
445 (with-temp-buffer
446 (let ((coding-system-for-read 'no-conversion))
447 (insert-file-contents file-msg))
ff981226
GM
448 (let ((smtpmail-mail-address
449 (or (and mail-specify-envelope-from (mail-envelope-from))
450 user-mail-address)))
451 (if (not (null smtpmail-recipient-address-list))
452 (if (not (smtpmail-via-smtp smtpmail-recipient-address-list
453 (current-buffer)))
454 (error "Sending failed; SMTP protocol error"))
455 (error "Sending failed; no recipients"))))
308bc5d8
RS
456 (delete-file file-msg)
457 (delete-file (concat file-msg ".el"))
95754b9f 458 (delete-region (point-at-bol) (point-at-bol 2)))
0fc1e8fe 459 (write-region (point-min) (point-max) qfile))))
24975917 460
75da36cc 461;; (defun smtpmail-via-smtp (host,port,sender,destination,smtpmail-text-buffer)
24975917
RS
462
463(defun smtpmail-fqdn ()
464 (if smtpmail-local-domain
465 (concat (system-name) "." smtpmail-local-domain)
466 (system-name)))
467
9056f1c9
RS
468(defsubst smtpmail-cred-server (cred)
469 (nth 0 cred))
470
471(defsubst smtpmail-cred-port (cred)
472 (nth 1 cred))
473
474(defsubst smtpmail-cred-key (cred)
475 (nth 2 cred))
476
477(defsubst smtpmail-cred-user (cred)
478 (nth 2 cred))
479
480(defsubst smtpmail-cred-cert (cred)
481 (nth 3 cred))
482
483(defsubst smtpmail-cred-passwd (cred)
484 (nth 3 cred))
485
486(defun smtpmail-find-credentials (cred server port)
487 (catch 'done
488 (let ((l cred) el)
489 (while (setq el (pop l))
490 (when (and (equal server (smtpmail-cred-server el))
491 (equal port (smtpmail-cred-port el)))
492 (throw 'done el))))))
493
2b5c7e03
GM
494(defun smtpmail-maybe-append-domain (recipient)
495 (if (or (not smtpmail-sendto-domain)
496 (string-match "@" recipient))
497 recipient
498 (concat recipient "@" smtpmail-sendto-domain)))
499
9056f1c9
RS
500(defun smtpmail-intersection (list1 list2)
501 (let ((result nil))
502 (dolist (el2 list2)
503 (when (memq el2 list1)
504 (push el2 result)))
505 (nreverse result)))
506
88f2c9ad
RS
507(defvar starttls-extra-args)
508(defvar starttls-extra-arguments)
509
9056f1c9
RS
510(defun smtpmail-open-stream (process-buffer host port)
511 (let ((cred (smtpmail-find-credentials
512 smtpmail-starttls-credentials host port)))
373f0312 513 (if (null (and cred (starttls-any-program-available)))
9056f1c9
RS
514 ;; The normal case.
515 (open-network-stream "SMTP" process-buffer host port)
516 (let* ((cred-key (smtpmail-cred-key cred))
517 (cred-cert (smtpmail-cred-cert cred))
518 (starttls-extra-args
e8620cb6
SJ
519 (append
520 starttls-extra-args
521 (when (and (stringp cred-key) (stringp cred-cert)
522 (file-regular-p
523 (setq cred-key (expand-file-name cred-key)))
524 (file-regular-p
525 (setq cred-cert (expand-file-name cred-cert))))
526 (list "--key-file" cred-key "--cert-file" cred-cert))))
4aa609dc 527 (starttls-extra-arguments
e8620cb6
SJ
528 (append
529 starttls-extra-arguments
530 (when (and (stringp cred-key) (stringp cred-cert)
531 (file-regular-p
532 (setq cred-key (expand-file-name cred-key)))
533 (file-regular-p
534 (setq cred-cert (expand-file-name cred-cert))))
535 (list "--x509keyfile" cred-key "--x509certfile" cred-cert)))))
9056f1c9
RS
536 (starttls-open-stream "SMTP" process-buffer host port)))))
537
75da36cc 538;; `password-read' autoloads password-cache.
48a731fe
GM
539(declare-function password-cache-add "password-cache" (key password))
540
9056f1c9
RS
541(defun smtpmail-try-auth-methods (process supported-extensions host port)
542 (let* ((mechs (cdr-safe (assoc 'auth supported-extensions)))
9927d250 543 (mech (car (smtpmail-intersection mechs smtpmail-auth-supported)))
20e43520 544 (auth-user (auth-source-user-or-password
e64a3841 545 "login" host (or port "smtp")))
20e43520 546 (auth-pass (auth-source-user-or-password
e64a3841
TZ
547 "password" host (or port "smtp")))
548 (cred (if (and auth-user auth-pass) ; try user-auth-* before netrc-*
549 (list host port auth-user auth-pass)
550 ;; else, if auth-source didn't return them...
551 (if (stringp smtpmail-auth-credentials)
552 (let* ((netrc (netrc-parse smtpmail-auth-credentials))
553 (port-name (format "%s" (or port "smtp")))
554 (hostentry (netrc-machine netrc host port-name
555 port-name)))
556 (when hostentry
557 (list host port
558 (netrc-get hostentry "login")
559 (netrc-get hostentry "password"))))
75da36cc
RW
560 ;; else, try `smtpmail-find-credentials' since
561 ;; `smtpmail-auth-credentials' is not a string
e64a3841
TZ
562 (smtpmail-find-credentials
563 smtpmail-auth-credentials host port))))
5b5eae56
BG
564 (prompt (when cred (format "SMTP password for %s:%s: "
565 (smtpmail-cred-server cred)
566 (smtpmail-cred-port cred))))
9056f1c9
RS
567 (passwd (when cred
568 (or (smtpmail-cred-passwd cred)
5b5eae56 569 (password-read prompt prompt))))
9056f1c9 570 ret)
dbfa9ed4 571 (when (and cred mech)
9056f1c9
RS
572 (cond
573 ((eq mech 'cram-md5)
1a63439b 574 (smtpmail-send-command process (upcase (format "AUTH %s" mech)))
9056f1c9
RS
575 (if (or (null (car (setq ret (smtpmail-read-response process))))
576 (not (integerp (car ret)))
577 (>= (car ret) 400))
578 (throw 'done nil))
579 (when (eq (car ret) 334)
580 (let* ((challenge (substring (cadr ret) 4))
581 (decoded (base64-decode-string challenge))
582 (hash (rfc2104-hash 'md5 64 16 passwd decoded))
583 (response (concat (smtpmail-cred-user cred) " " hash))
9b3bf5e8
RS
584 ;; Osamu Yamane <yamane@green.ocn.ne.jp>:
585 ;; SMTP auth fails because the SMTP server identifies
586 ;; only the first part of the string (delimited by
587 ;; new line characters) as a response from the
588 ;; client, and the rest as distinct commands.
589
590 ;; In my case, the response string is 80 characters
591 ;; long. Without the no-line-break option for
75da36cc 592 ;; `base64-encode-string', only the first 76 characters
9b3bf5e8
RS
593 ;; are taken as a response to the server, and the
594 ;; authentication fails.
e4eb8462 595 (encoded (base64-encode-string response t)))
9056f1c9
RS
596 (smtpmail-send-command process (format "%s" encoded))
597 (if (or (null (car (setq ret (smtpmail-read-response process))))
598 (not (integerp (car ret)))
599 (>= (car ret) 400))
600 (throw 'done nil)))))
601 ((eq mech 'login)
602 (smtpmail-send-command process "AUTH LOGIN")
603 (if (or (null (car (setq ret (smtpmail-read-response process))))
604 (not (integerp (car ret)))
605 (>= (car ret) 400))
606 (throw 'done nil))
607 (smtpmail-send-command
6c77efbe 608 process (base64-encode-string (smtpmail-cred-user cred) t))
9056f1c9
RS
609 (if (or (null (car (setq ret (smtpmail-read-response process))))
610 (not (integerp (car ret)))
611 (>= (car ret) 400))
612 (throw 'done nil))
e4eb8462 613 (smtpmail-send-command process (base64-encode-string passwd t))
9056f1c9
RS
614 (if (or (null (car (setq ret (smtpmail-read-response process))))
615 (not (integerp (car ret)))
616 (>= (car ret) 400))
617 (throw 'done nil)))
4effb112 618 ((eq mech 'plain)
0c91399a
SJ
619 ;; We used to send an empty initial request, and wait for an
620 ;; empty response, and then send the password, but this
621 ;; violate a SHOULD in RFC 2222 paragraph 5.1. Note that this
622 ;; is not sent if the server did not advertise AUTH PLAIN in
623 ;; the EHLO response. See RFC 2554 for more info.
c3f69831
SJ
624 (smtpmail-send-command process
625 (concat "AUTH PLAIN "
626 (base64-encode-string
4effb112
SJ
627 (concat "\0"
628 (smtpmail-cred-user cred)
629 "\0"
e4eb8462 630 passwd) t)))
4effb112
SJ
631 (if (or (null (car (setq ret (smtpmail-read-response process))))
632 (not (integerp (car ret)))
633 (not (equal (car ret) 235)))
634 (throw 'done nil)))
635
9056f1c9 636 (t
4effb112 637 (error "Mechanism %s not implemented" mech)))
9056f1c9 638 ;; Remember the password.
5b5eae56
BG
639 (when (null (smtpmail-cred-passwd cred))
640 (password-cache-add prompt passwd)))))
9056f1c9 641
24975917
RS
642(defun smtpmail-via-smtp (recipient smtpmail-text-buffer)
643 (let ((process nil)
f38d3514
KH
644 (host (or smtpmail-smtp-server
645 (error "`smtpmail-smtp-server' not defined")))
8805249b 646 (port smtpmail-smtp-service)
75da36cc 647 ;; `smtpmail-mail-address' should be set to the appropriate
ff981226
GM
648 ;; buffer-local value by the caller, but in case not:
649 (envelope-from (or smtpmail-mail-address
650 (and mail-specify-envelope-from
651 (mail-envelope-from))
652 user-mail-address))
24975917 653 response-code
8805249b 654 greeting
c50d5ce0
RS
655 process-buffer
656 (supported-extensions '()))
24975917
RS
657 (unwind-protect
658 (catch 'done
659 ;; get or create the trace buffer
660 (setq process-buffer
661 (get-buffer-create (format "*trace of SMTP session to %s*" host)))
662
663 ;; clear the trace buffer of old output
21bf0d6c 664 (with-current-buffer process-buffer
9cf328cc 665 (setq buffer-undo-list t)
24975917
RS
666 (erase-buffer))
667
668 ;; open the connection to the server
9056f1c9 669 (setq process (smtpmail-open-stream process-buffer host port))
24975917
RS
670 (and (null process) (throw 'done nil))
671
672 ;; set the send-filter
673 (set-process-filter process 'smtpmail-process-filter)
674
21bf0d6c 675 (with-current-buffer process-buffer
4b876894 676 (set-buffer-process-coding-system 'raw-text-unix 'raw-text-unix)
24975917
RS
677 (make-local-variable 'smtpmail-read-point)
678 (setq smtpmail-read-point (point-min))
679
a8ba4429 680
24975917
RS
681 (if (or (null (car (setq greeting (smtpmail-read-response process))))
682 (not (integerp (car greeting)))
683 (>= (car greeting) 400))
75da36cc 684 (throw 'done nil))
24975917 685
9056f1c9
RS
686 (let ((do-ehlo t)
687 (do-starttls t))
688 (while do-ehlo
75da36cc
RW
689 ;; EHLO
690 (smtpmail-send-command process (format "EHLO %s" (smtpmail-fqdn)))
691
692 (if (or (null (car (setq response-code
693 (smtpmail-read-response process))))
694 (not (integerp (car response-code)))
695 (>= (car response-code) 400))
696 (progn
697 ;; HELO
698 (smtpmail-send-command
699 process (format "HELO %s" (smtpmail-fqdn)))
700
701 (if (or (null (car (setq response-code
702 (smtpmail-read-response process))))
703 (not (integerp (car response-code)))
704 (>= (car response-code) 400))
705 (throw 'done nil)))
706 (dolist (line (cdr (cdr response-code)))
707 (let ((name
708 (with-case-table ascii-case-table
709 (mapcar (lambda (s) (intern (downcase s)))
710 (split-string (substring line 4) "[ ]")))))
711 (and (eq (length name) 1)
712 (setq name (car name)))
713 (and name
714 (cond ((memq (if (consp name) (car name) name)
715 '(verb xvrb 8bitmime onex xone
716 expn size dsn etrn
717 enhancedstatuscodes
718 help xusr
719 auth=login auth starttls))
720 (setq supported-extensions
721 (cons name supported-extensions)))
722 (smtpmail-warn-about-unknown-extensions
723 (message "Unknown extension %s" name)))))))
724
725 (if (and do-starttls
726 (smtpmail-find-credentials smtpmail-starttls-credentials host port)
727 (member 'starttls supported-extensions)
728 (numberp (process-id process)))
729 (progn
730 (smtpmail-send-command process (format "STARTTLS"))
731 (if (or (null (car (setq response-code (smtpmail-read-response process))))
732 (not (integerp (car response-code)))
733 (>= (car response-code) 400))
734 (throw 'done nil))
735 (starttls-negotiate process)
736 (setq do-starttls nil))
737 (setq do-ehlo nil))))
a8ba4429 738
9056f1c9 739 (smtpmail-try-auth-methods process supported-extensions host port)
c50d5ce0
RS
740
741 (if (or (member 'onex supported-extensions)
742 (member 'xone supported-extensions))
743 (progn
744 (smtpmail-send-command process (format "ONEX"))
745 (if (or (null (car (setq response-code (smtpmail-read-response process))))
746 (not (integerp (car response-code)))
747 (>= (car response-code) 400))
748 (throw 'done nil))))
749
9056f1c9 750 (if (and smtpmail-debug-verb
c50d5ce0
RS
751 (or (member 'verb supported-extensions)
752 (member 'xvrb supported-extensions)))
753 (progn
754 (smtpmail-send-command process (format "VERB"))
755 (if (or (null (car (setq response-code (smtpmail-read-response process))))
756 (not (integerp (car response-code)))
757 (>= (car response-code) 400))
758 (throw 'done nil))))
759
760 (if (member 'xusr supported-extensions)
761 (progn
762 (smtpmail-send-command process (format "XUSR"))
763 (if (or (null (car (setq response-code (smtpmail-read-response process))))
764 (not (integerp (car response-code)))
765 (>= (car response-code) 400))
766 (throw 'done nil))))
24975917 767
a8df98fd 768 ;; MAIL FROM:<sender>
c50d5ce0 769 (let ((size-part
9056f1c9
RS
770 (if (or (member 'size supported-extensions)
771 (assoc 'size supported-extensions))
c50d5ce0 772 (format " SIZE=%d"
21bf0d6c 773 (with-current-buffer smtpmail-text-buffer
c50d5ce0
RS
774 ;; size estimate:
775 (+ (- (point-max) (point-min))
776 ;; Add one byte for each change-of-line
73921ac1
GM
777 ;; because of CR-LF representation:
778 (count-lines (point-min) (point-max)))))
c50d5ce0
RS
779 ""))
780 (body-part
781 (if (member '8bitmime supported-extensions)
782 ;; FIXME:
783 ;; Code should be added here that transforms
784 ;; the contents of the message buffer into
785 ;; something the receiving SMTP can handle.
786 ;; For a receiver that supports 8BITMIME, this
787 ;; may mean converting BINARY to BASE64, or
788 ;; adding Content-Transfer-Encoding and the
789 ;; other MIME headers. The code should also
790 ;; return an indication of what encoding the
791 ;; message buffer is now, i.e. ASCII or
792 ;; 8BITMIME.
793 (if nil
794 " BODY=8BITMIME"
795 "")
796 "")))
75da36cc 797 ;; (smtpmail-send-command process (format "MAIL FROM:%s@%s" (user-login-name) (smtpmail-fqdn)))
a8df98fd 798 (smtpmail-send-command process (format "MAIL FROM:<%s>%s%s"
ff981226 799 envelope-from
c50d5ce0
RS
800 size-part
801 body-part))
a1506d29 802
c50d5ce0
RS
803 (if (or (null (car (setq response-code (smtpmail-read-response process))))
804 (not (integerp (car response-code)))
805 (>= (car response-code) 400))
75da36cc 806 (throw 'done nil)))
a8ba4429 807
a8df98fd 808 ;; RCPT TO:<recipient>
8805249b
RS
809 (let ((n 0))
810 (while (not (null (nth n recipient)))
a8df98fd 811 (smtpmail-send-command process (format "RCPT TO:<%s>" (smtpmail-maybe-append-domain (nth n recipient))))
8805249b
RS
812 (setq n (1+ n))
813
c50d5ce0
RS
814 (setq response-code (smtpmail-read-response process))
815 (if (or (null (car response-code))
8805249b
RS
816 (not (integerp (car response-code)))
817 (>= (car response-code) 400))
75da36cc 818 (throw 'done nil))))
a8ba4429 819
24975917
RS
820 ;; DATA
821 (smtpmail-send-command process "DATA")
822
823 (if (or (null (car (setq response-code (smtpmail-read-response process))))
824 (not (integerp (car response-code)))
825 (>= (car response-code) 400))
75da36cc 826 (throw 'done nil))
24975917
RS
827
828 ;; Mail contents
829 (smtpmail-send-data process smtpmail-text-buffer)
830
75da36cc 831 ;; DATA end "."
24975917
RS
832 (smtpmail-send-command process ".")
833
834 (if (or (null (car (setq response-code (smtpmail-read-response process))))
835 (not (integerp (car response-code)))
836 (>= (car response-code) 400))
75da36cc
RW
837 (throw 'done nil))
838
839 ;; QUIT
840 ;; (smtpmail-send-command process "QUIT")
841 ;; (and (null (car (smtpmail-read-response process)))
842 ;; (throw 'done nil))
843 t))
24975917 844 (if process
21bf0d6c 845 (with-current-buffer (process-buffer process)
24975917
RS
846 (smtpmail-send-command process "QUIT")
847 (smtpmail-read-response process)
848
75da36cc
RW
849 ;; (if (or (null (car (setq response-code (smtpmail-read-response process))))
850 ;; (not (integerp (car response-code)))
851 ;; (>= (car response-code) 400))
852 ;; (throw 'done nil))
1121afb4
SJ
853 (delete-process process)
854 (unless smtpmail-debug-info
855 (kill-buffer process-buffer)))))))
24975917
RS
856
857
24975917 858(defun smtpmail-process-filter (process output)
21bf0d6c 859 (with-current-buffer (process-buffer process)
24975917
RS
860 (goto-char (point-max))
861 (insert output)))
862
24975917
RS
863(defun smtpmail-read-response (process)
864 (let ((case-fold-search nil)
c50d5ce0 865 (response-strings nil)
24975917 866 (response-continue t)
c50d5ce0 867 (return-value '(nil ()))
24975917 868 match-end)
2ceed428
SJ
869 (catch 'done
870 (while response-continue
871 (goto-char smtpmail-read-point)
872 (while (not (search-forward "\r\n" nil t))
873 (unless (memq (process-status process) '(open run))
874 (throw 'done nil))
875 (accept-process-output process)
876 (goto-char smtpmail-read-point))
877
878 (setq match-end (point))
879 (setq response-strings
880 (cons (buffer-substring smtpmail-read-point (- match-end 2))
881 response-strings))
882
883 (goto-char smtpmail-read-point)
884 (if (looking-at "[0-9]+ ")
885 (let ((begin (match-beginning 0))
886 (end (match-end 0)))
887 (if smtpmail-debug-info
888 (message "%s" (car response-strings)))
889
890 (setq smtpmail-read-point match-end)
891
892 ;; ignore lines that start with "0"
893 (if (looking-at "0[0-9]+ ")
894 nil
895 (setq response-continue nil)
896 (setq return-value
027a4b6b 897 (cons (string-to-number
2ceed428
SJ
898 (buffer-substring begin end))
899 (nreverse response-strings)))))
900
901 (if (looking-at "[0-9]+-")
902 (progn (if smtpmail-debug-info
903 (message "%s" (car response-strings)))
904 (setq smtpmail-read-point match-end)
905 (setq response-continue t))
906 (progn
907 (setq smtpmail-read-point match-end)
c50d5ce0
RS
908 (setq response-continue nil)
909 (setq return-value
2ceed428
SJ
910 (cons nil (nreverse response-strings)))))))
911 (setq smtpmail-read-point match-end))
24975917
RS
912 return-value))
913
914
24975917
RS
915(defun smtpmail-send-command (process command)
916 (goto-char (point-max))
917 (if (= (aref command 0) ?P)
918 (insert "PASS <omitted>\r\n")
919 (insert command "\r\n"))
920 (setq smtpmail-read-point (point))
921 (process-send-string process command)
922 (process-send-string process "\r\n"))
923
24975917
RS
924(defun smtpmail-send-data-1 (process data)
925 (goto-char (point-max))
926
83af570e
KH
927 (if (and (multibyte-string-p data)
928 smtpmail-code-conv-from)
929 (setq data (string-as-multibyte
930 (encode-coding-string data smtpmail-code-conv-from))))
a8ba4429 931
24975917
RS
932 (if smtpmail-debug-info
933 (insert data "\r\n"))
934
935 (setq smtpmail-read-point (point))
57810560
KH
936 ;; Escape "." at start of a line
937 (if (eq (string-to-char data) ?.)
24975917 938 (process-send-string process "."))
57810560 939 (process-send-string process data)
75da36cc 940 (process-send-string process "\r\n"))
24975917
RS
941
942(defun smtpmail-send-data (process buffer)
dd64e5e5 943 (let ((data-continue t) sending-data)
21bf0d6c 944 (with-current-buffer buffer
24975917 945 (goto-char (point-min)))
24975917 946 (while data-continue
21bf0d6c 947 (with-current-buffer buffer
dd64e5e5
GM
948 (setq sending-data (buffer-substring (point-at-bol) (point-at-eol)))
949 (end-of-line 2)
950 (setq data-continue (not (eobp))))
951 (smtpmail-send-data-1 process sending-data))))
24975917
RS
952
953(defun smtpmail-deduce-address-list (smtpmail-text-buffer header-start header-end)
954 "Get address list suitable for smtp RCPT TO: <address>."
1188dd37 955 (unwind-protect
21bf0d6c
SM
956 (with-current-buffer smtpmail-address-buffer
957 (erase-buffer)
75da36cc
RW
958 (let ((case-fold-search t)
959 (simple-address-list "")
960 this-line
961 this-line-end
962 addr-regexp)
24975917
RS
963 (insert-buffer-substring smtpmail-text-buffer header-start header-end)
964 (goto-char (point-min))
13f1d088
KH
965 ;; RESENT-* fields should stop processing of regular fields.
966 (save-excursion
21bf0d6c
SM
967 (setq addr-regexp
968 (if (re-search-forward "^Resent-\\(to\\|cc\\|bcc\\):"
969 header-end t)
970 "^Resent-\\(to\\|cc\\|bcc\\):"
971 "^\\(To:\\|Cc:\\|Bcc:\\)")))
13f1d088
KH
972
973 (while (re-search-forward addr-regexp header-end t)
24975917
RS
974 (replace-match "")
975 (setq this-line (match-beginning 0))
976 (forward-line 1)
977 ;; get any continuation lines
978 (while (and (looking-at "^[ \t]+") (< (point) header-end))
979 (forward-line 1))
980 (setq this-line-end (point-marker))
981 (setq simple-address-list
982 (concat simple-address-list " "
75da36cc 983 (mail-strip-quoted-names (buffer-substring this-line this-line-end)))))
24975917 984 (erase-buffer)
92dfd10c 985 (insert " " simple-address-list "\n")
75da36cc
RW
986 (subst-char-in-region (point-min) (point-max) 10 ? t) ; newline --> blank
987 (subst-char-in-region (point-min) (point-max) ?, ? t) ; comma --> blank
988 (subst-char-in-region (point-min) (point-max) 9 ? t) ; tab --> blank
24975917
RS
989
990 (goto-char (point-min))
991 ;; tidyness in case hook is not robust when it looks at this
992 (while (re-search-forward "[ \t]+" header-end t) (replace-match " "))
993
994 (goto-char (point-min))
8805249b 995 (let (recipient-address-list)
e2f7c221 996 (while (re-search-forward " \\([^ ]+\\) " (point-max) t)
8805249b 997 (backward-char 1)
e2f7c221 998 (setq recipient-address-list (cons (buffer-substring (match-beginning 1) (match-end 1))
75da36cc
RW
999 recipient-address-list)))
1000 (setq smtpmail-recipient-address-list recipient-address-list))))))
24975917
RS
1001
1002(defun smtpmail-do-bcc (header-end)
5feeeae2 1003 "Delete [Resent-]BCC: and their continuation lines from the header area.
24975917
RS
1004There may be multiple BCC: lines, and each may have arbitrarily
1005many continuation lines."
1006 (let ((case-fold-search t))
067427f5
KH
1007 (save-excursion
1008 (goto-char (point-min))
1009 ;; iterate over all BCC: lines
1010 (while (re-search-forward "^\\(RESENT-\\)?BCC:" header-end t)
1011 (delete-region (match-beginning 0)
1012 (progn (forward-line 1) (point)))
1013 ;; get rid of any continuation lines
1014 (while (and (looking-at "^[ \t].*\n") (< (point) header-end))
1015 (replace-match ""))))))
24975917 1016
24975917
RS
1017(provide 'smtpmail)
1018
092af6d8 1019;;; smtpmail.el ends here