* comint.el (comint-replace-by-expanded-history-before-point):
[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,
ae940284 4;; 2008, 2009 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
RS
244 (unwind-protect
245 (save-excursion
246 (set-buffer tembuf)
247 (erase-buffer)
75da36cc
RW
248 ;; Use the same `buffer-file-coding-system' as in the mail
249 ;; buffer, otherwise any `write-region' invocations (e.g., in
362e23e1
EZ
250 ;; mail-do-fcc below) will annoy with asking for a suitable
251 ;; encoding.
252 (set-buffer-file-coding-system smtpmail-code-conv-from nil t)
24975917
RS
253 (insert-buffer-substring mailbuf)
254 (goto-char (point-max))
255 ;; require one newline at the end.
256 (or (= (preceding-char) ?\n)
257 (insert ?\n))
258 ;; Change header-delimiter to be what sendmail expects.
92a3f23d 259 (mail-sendmail-undelimit-header)
24975917 260 (setq delimline (point-marker))
75da36cc 261 ;; (sendmail-synch-aliases)
24975917
RS
262 (if mail-aliases
263 (expand-mail-aliases (point-min) delimline))
264 (goto-char (point-min))
265 ;; ignore any blank lines in the header
266 (while (and (re-search-forward "\n\n\n*" delimline t)
267 (< (point) delimline))
268 (replace-match "\n"))
269 (let ((case-fold-search t))
5feeeae2
RS
270 ;; We used to process Resent-... headers here,
271 ;; but it was not done properly, and the job
75da36cc 272 ;; is done correctly in `smtpmail-deduce-address-list'.
24975917
RS
273 ;; Don't send out a blank subject line
274 (goto-char (point-min))
5feeeae2
RS
275 (if (re-search-forward "^Subject:\\([ \t]*\n\\)+\\b" delimline t)
276 (replace-match "")
277 ;; This one matches a Subject just before the header delimiter.
278 (if (and (re-search-forward "^Subject:\\([ \t]*\n\\)+" delimline t)
279 (= (match-end 0) delimline))
280 (replace-match "")))
0e2701ca
RS
281 ;; Put the "From:" field in unless for some odd reason
282 ;; they put one in themselves.
283 (goto-char (point-min))
284 (if (not (re-search-forward "^From:" delimline t))
850ed7b3 285 (let* ((login smtpmail-mail-address)
0e2701ca
RS
286 (fullname (user-full-name)))
287 (cond ((eq mail-from-style 'angles)
288 (insert "From: " fullname)
289 (let ((fullname-start (+ (point-min) 6))
290 (fullname-end (point-marker)))
291 (goto-char fullname-start)
292 ;; Look for a character that cannot appear unquoted
293 ;; according to RFC 822.
294 (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]"
295 fullname-end 1)
296 (progn
297 ;; Quote fullname, escaping specials.
298 (goto-char fullname-start)
299 (insert "\"")
300 (while (re-search-forward "[\"\\]"
301 fullname-end 1)
302 (replace-match "\\\\\\&" t))
303 (insert "\""))))
304 (insert " <" login ">\n"))
305 ((eq mail-from-style 'parens)
306 (insert "From: " login " (")
307 (let ((fullname-start (point)))
308 (insert fullname)
309 (let ((fullname-end (point-marker)))
310 (goto-char fullname-start)
311 ;; RFC 822 says \ and nonmatching parentheses
312 ;; must be escaped in comments.
313 ;; Escape every instance of ()\ ...
314 (while (re-search-forward "[()\\]" fullname-end 1)
315 (replace-match "\\\\\\&" t))
316 ;; ... then undo escaping of matching parentheses,
317 ;; including matching nested parentheses.
318 (goto-char fullname-start)
a8ba4429 319 (while (re-search-forward
0e2701ca
RS
320 "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
321 fullname-end 1)
322 (replace-match "\\1(\\3)" t)
323 (goto-char fullname-start))))
324 (insert ")\n"))
325 ((null mail-from-style)
326 (insert "From: " login "\n")))))
21bf0d6c
SM
327 ;; Insert a `Message-Id:' field if there isn't one yet.
328 (goto-char (point-min))
329 (unless (re-search-forward "^Message-Id:" delimline t)
330 (insert "Message-Id: " (message-make-message-id) "\n"))
331 ;; Insert a `Date:' field if there isn't one yet.
332 (goto-char (point-min))
333 (unless (re-search-forward "^Date:" delimline t)
334 (insert "Date: " (message-make-date) "\n"))
362e23e1
EZ
335 ;; Possibly add a MIME header for the current coding system
336 (let (charset)
337 (goto-char (point-min))
338 (and (eq mail-send-nonascii 'mime)
339 (not (re-search-forward "^MIME-version:" delimline t))
340 (progn (skip-chars-forward "\0-\177")
341 (/= (point) (point-max)))
342 smtpmail-code-conv-from
343 (setq charset
344 (coding-system-get smtpmail-code-conv-from
345 'mime-charset))
346 (goto-char delimline)
347 (insert "MIME-version: 1.0\n"
348 "Content-type: text/plain; charset="
349 (symbol-name charset)
350 "\nContent-Transfer-Encoding: 8bit\n")))
24975917
RS
351 ;; Insert an extra newline if we need it to work around
352 ;; Sun's bug that swallows newlines.
353 (goto-char (1+ delimline))
354 (if (eval mail-mailer-swallows-blank-line)
355 (newline))
0e2701ca
RS
356 ;; Find and handle any FCC fields.
357 (goto-char (point-min))
358 (if (re-search-forward "^FCC:" delimline t)
75da36cc 359 ;; Force `mail-do-fcc' to use the encoding of the mail
362e23e1
EZ
360 ;; buffer to encode outgoing messages on FCC files.
361 (let ((coding-system-for-write smtpmail-code-conv-from))
362 (mail-do-fcc delimline)))
24975917 363 (if mail-interactive
21bf0d6c 364 (with-current-buffer errbuf
24975917
RS
365 (erase-buffer))))
366 ;;
24975917
RS
367 (setq smtpmail-address-buffer (generate-new-buffer "*smtp-mail*"))
368 (setq smtpmail-recipient-address-list
75da36cc 369 (smtpmail-deduce-address-list tembuf (point-min) delimline))
24975917 370 (kill-buffer smtpmail-address-buffer)
a8ba4429 371
24975917 372 (smtpmail-do-bcc delimline)
75da36cc 373 ;; Send or queue
308bc5d8
RS
374 (if (not smtpmail-queue-mail)
375 (if (not (null smtpmail-recipient-address-list))
a8ba4429 376 (if (not (smtpmail-via-smtp
308bc5d8
RS
377 smtpmail-recipient-address-list tembuf))
378 (error "Sending failed; SMTP protocol error"))
379 (error "Sending failed; no recipients"))
bf6e31bc
SJ
380 (let* ((file-data
381 (expand-file-name
382 (format "%s_%i"
383 (format-time-string "%Y-%m-%d_%H:%M:%S")
384 (setq smtpmail-queue-counter
385 (1+ smtpmail-queue-counter)))
386 smtpmail-queue-dir))
387 (file-data (convert-standard-filename file-data))
388 (file-elisp (concat file-data ".el"))
308bc5d8
RS
389 (buffer-data (create-file-buffer file-data))
390 (buffer-elisp (create-file-buffer file-elisp))
391 (buffer-scratch "*queue-mail*"))
2fbc1118
JB
392 (unless (file-exists-p smtpmail-queue-dir)
393 (make-directory smtpmail-queue-dir t))
21bf0d6c 394 (with-current-buffer buffer-data
308bc5d8 395 (erase-buffer)
362e23e1 396 (set-buffer-file-coding-system smtpmail-code-conv-from nil t)
b0aea09e 397 (insert-buffer-substring tembuf)
308bc5d8
RS
398 (write-file file-data)
399 (set-buffer buffer-elisp)
400 (erase-buffer)
401 (insert (concat
402 "(setq smtpmail-recipient-address-list '"
403 (prin1-to-string smtpmail-recipient-address-list)
a8ba4429 404 ")\n"))
308bc5d8
RS
405 (write-file file-elisp)
406 (set-buffer (generate-new-buffer buffer-scratch))
407 (insert (concat file-data "\n"))
a8ba4429
SS
408 (append-to-file (point-min)
409 (point-max)
0fc1e8fe
GM
410 (expand-file-name smtpmail-queue-index-file
411 smtpmail-queue-dir)))
308bc5d8
RS
412 (kill-buffer buffer-scratch)
413 (kill-buffer buffer-data)
414 (kill-buffer buffer-elisp))))
24975917
RS
415 (kill-buffer tembuf)
416 (if (bufferp errbuf)
417 (kill-buffer errbuf)))))
418
74f39ce9 419;;;###autoload
308bc5d8
RS
420(defun smtpmail-send-queued-mail ()
421 "Send mail that was queued as a result of setting `smtpmail-queue-mail'."
422 (interactive)
7e3fa9f5 423 (with-temp-buffer
75da36cc
RW
424 ;; Get index, get first mail, send it, update index, get second
425 ;; mail, send it, etc...
0fc1e8fe
GM
426 (let ((file-msg "")
427 (qfile (expand-file-name smtpmail-queue-index-file
428 smtpmail-queue-dir)))
429 (insert-file-contents qfile)
88f2c9ad 430 (goto-char (point-min))
308bc5d8 431 (while (not (eobp))
21bf0d6c 432 (setq file-msg (buffer-substring (point) (line-end-position)))
308bc5d8 433 (load file-msg)
19a0baf7
EZ
434 ;; Insert the message literally: it is already encoded as per
435 ;; the MIME headers, and code conversions might guess the
436 ;; encoding wrongly.
7e3fa9f5
EZ
437 (with-temp-buffer
438 (let ((coding-system-for-read 'no-conversion))
439 (insert-file-contents file-msg))
ff981226
GM
440 (let ((smtpmail-mail-address
441 (or (and mail-specify-envelope-from (mail-envelope-from))
442 user-mail-address)))
443 (if (not (null smtpmail-recipient-address-list))
444 (if (not (smtpmail-via-smtp smtpmail-recipient-address-list
445 (current-buffer)))
446 (error "Sending failed; SMTP protocol error"))
447 (error "Sending failed; no recipients"))))
308bc5d8
RS
448 (delete-file file-msg)
449 (delete-file (concat file-msg ".el"))
95754b9f 450 (delete-region (point-at-bol) (point-at-bol 2)))
0fc1e8fe 451 (write-region (point-min) (point-max) qfile))))
24975917 452
75da36cc 453;; (defun smtpmail-via-smtp (host,port,sender,destination,smtpmail-text-buffer)
24975917
RS
454
455(defun smtpmail-fqdn ()
456 (if smtpmail-local-domain
457 (concat (system-name) "." smtpmail-local-domain)
458 (system-name)))
459
9056f1c9
RS
460(defsubst smtpmail-cred-server (cred)
461 (nth 0 cred))
462
463(defsubst smtpmail-cred-port (cred)
464 (nth 1 cred))
465
466(defsubst smtpmail-cred-key (cred)
467 (nth 2 cred))
468
469(defsubst smtpmail-cred-user (cred)
470 (nth 2 cred))
471
472(defsubst smtpmail-cred-cert (cred)
473 (nth 3 cred))
474
475(defsubst smtpmail-cred-passwd (cred)
476 (nth 3 cred))
477
478(defun smtpmail-find-credentials (cred server port)
479 (catch 'done
480 (let ((l cred) el)
481 (while (setq el (pop l))
482 (when (and (equal server (smtpmail-cred-server el))
483 (equal port (smtpmail-cred-port el)))
484 (throw 'done el))))))
485
2b5c7e03
GM
486(defun smtpmail-maybe-append-domain (recipient)
487 (if (or (not smtpmail-sendto-domain)
488 (string-match "@" recipient))
489 recipient
490 (concat recipient "@" smtpmail-sendto-domain)))
491
9056f1c9
RS
492(defun smtpmail-intersection (list1 list2)
493 (let ((result nil))
494 (dolist (el2 list2)
495 (when (memq el2 list1)
496 (push el2 result)))
497 (nreverse result)))
498
88f2c9ad
RS
499(defvar starttls-extra-args)
500(defvar starttls-extra-arguments)
501
9056f1c9
RS
502(defun smtpmail-open-stream (process-buffer host port)
503 (let ((cred (smtpmail-find-credentials
504 smtpmail-starttls-credentials host port)))
373f0312 505 (if (null (and cred (starttls-any-program-available)))
9056f1c9
RS
506 ;; The normal case.
507 (open-network-stream "SMTP" process-buffer host port)
508 (let* ((cred-key (smtpmail-cred-key cred))
509 (cred-cert (smtpmail-cred-cert cred))
510 (starttls-extra-args
e8620cb6
SJ
511 (append
512 starttls-extra-args
513 (when (and (stringp cred-key) (stringp cred-cert)
514 (file-regular-p
515 (setq cred-key (expand-file-name cred-key)))
516 (file-regular-p
517 (setq cred-cert (expand-file-name cred-cert))))
518 (list "--key-file" cred-key "--cert-file" cred-cert))))
4aa609dc 519 (starttls-extra-arguments
e8620cb6
SJ
520 (append
521 starttls-extra-arguments
522 (when (and (stringp cred-key) (stringp cred-cert)
523 (file-regular-p
524 (setq cred-key (expand-file-name cred-key)))
525 (file-regular-p
526 (setq cred-cert (expand-file-name cred-cert))))
527 (list "--x509keyfile" cred-key "--x509certfile" cred-cert)))))
9056f1c9
RS
528 (starttls-open-stream "SMTP" process-buffer host port)))))
529
75da36cc 530;; `password-read' autoloads password-cache.
48a731fe
GM
531(declare-function password-cache-add "password-cache" (key password))
532
9056f1c9
RS
533(defun smtpmail-try-auth-methods (process supported-extensions host port)
534 (let* ((mechs (cdr-safe (assoc 'auth supported-extensions)))
9927d250 535 (mech (car (smtpmail-intersection mechs smtpmail-auth-supported)))
20e43520 536 (auth-user (auth-source-user-or-password
e64a3841 537 "login" host (or port "smtp")))
20e43520 538 (auth-pass (auth-source-user-or-password
e64a3841
TZ
539 "password" host (or port "smtp")))
540 (cred (if (and auth-user auth-pass) ; try user-auth-* before netrc-*
541 (list host port auth-user auth-pass)
542 ;; else, if auth-source didn't return them...
543 (if (stringp smtpmail-auth-credentials)
544 (let* ((netrc (netrc-parse smtpmail-auth-credentials))
545 (port-name (format "%s" (or port "smtp")))
546 (hostentry (netrc-machine netrc host port-name
547 port-name)))
548 (when hostentry
549 (list host port
550 (netrc-get hostentry "login")
551 (netrc-get hostentry "password"))))
75da36cc
RW
552 ;; else, try `smtpmail-find-credentials' since
553 ;; `smtpmail-auth-credentials' is not a string
e64a3841
TZ
554 (smtpmail-find-credentials
555 smtpmail-auth-credentials host port))))
5b5eae56
BG
556 (prompt (when cred (format "SMTP password for %s:%s: "
557 (smtpmail-cred-server cred)
558 (smtpmail-cred-port cred))))
9056f1c9
RS
559 (passwd (when cred
560 (or (smtpmail-cred-passwd cred)
5b5eae56 561 (password-read prompt prompt))))
9056f1c9 562 ret)
dbfa9ed4 563 (when (and cred mech)
9056f1c9
RS
564 (cond
565 ((eq mech 'cram-md5)
1a63439b 566 (smtpmail-send-command process (upcase (format "AUTH %s" mech)))
9056f1c9
RS
567 (if (or (null (car (setq ret (smtpmail-read-response process))))
568 (not (integerp (car ret)))
569 (>= (car ret) 400))
570 (throw 'done nil))
571 (when (eq (car ret) 334)
572 (let* ((challenge (substring (cadr ret) 4))
573 (decoded (base64-decode-string challenge))
574 (hash (rfc2104-hash 'md5 64 16 passwd decoded))
575 (response (concat (smtpmail-cred-user cred) " " hash))
9b3bf5e8
RS
576 ;; Osamu Yamane <yamane@green.ocn.ne.jp>:
577 ;; SMTP auth fails because the SMTP server identifies
578 ;; only the first part of the string (delimited by
579 ;; new line characters) as a response from the
580 ;; client, and the rest as distinct commands.
581
582 ;; In my case, the response string is 80 characters
583 ;; long. Without the no-line-break option for
75da36cc 584 ;; `base64-encode-string', only the first 76 characters
9b3bf5e8
RS
585 ;; are taken as a response to the server, and the
586 ;; authentication fails.
e4eb8462 587 (encoded (base64-encode-string response t)))
9056f1c9
RS
588 (smtpmail-send-command process (format "%s" encoded))
589 (if (or (null (car (setq ret (smtpmail-read-response process))))
590 (not (integerp (car ret)))
591 (>= (car ret) 400))
592 (throw 'done nil)))))
593 ((eq mech 'login)
594 (smtpmail-send-command process "AUTH LOGIN")
595 (if (or (null (car (setq ret (smtpmail-read-response process))))
596 (not (integerp (car ret)))
597 (>= (car ret) 400))
598 (throw 'done nil))
599 (smtpmail-send-command
6c77efbe 600 process (base64-encode-string (smtpmail-cred-user cred) t))
9056f1c9
RS
601 (if (or (null (car (setq ret (smtpmail-read-response process))))
602 (not (integerp (car ret)))
603 (>= (car ret) 400))
604 (throw 'done nil))
e4eb8462 605 (smtpmail-send-command process (base64-encode-string passwd t))
9056f1c9
RS
606 (if (or (null (car (setq ret (smtpmail-read-response process))))
607 (not (integerp (car ret)))
608 (>= (car ret) 400))
609 (throw 'done nil)))
4effb112 610 ((eq mech 'plain)
0c91399a
SJ
611 ;; We used to send an empty initial request, and wait for an
612 ;; empty response, and then send the password, but this
613 ;; violate a SHOULD in RFC 2222 paragraph 5.1. Note that this
614 ;; is not sent if the server did not advertise AUTH PLAIN in
615 ;; the EHLO response. See RFC 2554 for more info.
c3f69831
SJ
616 (smtpmail-send-command process
617 (concat "AUTH PLAIN "
618 (base64-encode-string
4effb112
SJ
619 (concat "\0"
620 (smtpmail-cred-user cred)
621 "\0"
e4eb8462 622 passwd) t)))
4effb112
SJ
623 (if (or (null (car (setq ret (smtpmail-read-response process))))
624 (not (integerp (car ret)))
625 (not (equal (car ret) 235)))
626 (throw 'done nil)))
627
9056f1c9 628 (t
4effb112 629 (error "Mechanism %s not implemented" mech)))
9056f1c9 630 ;; Remember the password.
5b5eae56
BG
631 (when (null (smtpmail-cred-passwd cred))
632 (password-cache-add prompt passwd)))))
9056f1c9 633
24975917
RS
634(defun smtpmail-via-smtp (recipient smtpmail-text-buffer)
635 (let ((process nil)
f38d3514
KH
636 (host (or smtpmail-smtp-server
637 (error "`smtpmail-smtp-server' not defined")))
8805249b 638 (port smtpmail-smtp-service)
75da36cc 639 ;; `smtpmail-mail-address' should be set to the appropriate
ff981226
GM
640 ;; buffer-local value by the caller, but in case not:
641 (envelope-from (or smtpmail-mail-address
642 (and mail-specify-envelope-from
643 (mail-envelope-from))
644 user-mail-address))
24975917 645 response-code
8805249b 646 greeting
c50d5ce0
RS
647 process-buffer
648 (supported-extensions '()))
24975917
RS
649 (unwind-protect
650 (catch 'done
651 ;; get or create the trace buffer
652 (setq process-buffer
653 (get-buffer-create (format "*trace of SMTP session to %s*" host)))
654
655 ;; clear the trace buffer of old output
21bf0d6c 656 (with-current-buffer process-buffer
9cf328cc 657 (setq buffer-undo-list t)
24975917
RS
658 (erase-buffer))
659
660 ;; open the connection to the server
9056f1c9 661 (setq process (smtpmail-open-stream process-buffer host port))
24975917
RS
662 (and (null process) (throw 'done nil))
663
664 ;; set the send-filter
665 (set-process-filter process 'smtpmail-process-filter)
666
21bf0d6c 667 (with-current-buffer process-buffer
4b876894 668 (set-buffer-process-coding-system 'raw-text-unix 'raw-text-unix)
24975917
RS
669 (make-local-variable 'smtpmail-read-point)
670 (setq smtpmail-read-point (point-min))
671
a8ba4429 672
24975917
RS
673 (if (or (null (car (setq greeting (smtpmail-read-response process))))
674 (not (integerp (car greeting)))
675 (>= (car greeting) 400))
75da36cc 676 (throw 'done nil))
24975917 677
9056f1c9
RS
678 (let ((do-ehlo t)
679 (do-starttls t))
680 (while do-ehlo
75da36cc
RW
681 ;; EHLO
682 (smtpmail-send-command process (format "EHLO %s" (smtpmail-fqdn)))
683
684 (if (or (null (car (setq response-code
685 (smtpmail-read-response process))))
686 (not (integerp (car response-code)))
687 (>= (car response-code) 400))
688 (progn
689 ;; HELO
690 (smtpmail-send-command
691 process (format "HELO %s" (smtpmail-fqdn)))
692
693 (if (or (null (car (setq response-code
694 (smtpmail-read-response process))))
695 (not (integerp (car response-code)))
696 (>= (car response-code) 400))
697 (throw 'done nil)))
698 (dolist (line (cdr (cdr response-code)))
699 (let ((name
700 (with-case-table ascii-case-table
701 (mapcar (lambda (s) (intern (downcase s)))
702 (split-string (substring line 4) "[ ]")))))
703 (and (eq (length name) 1)
704 (setq name (car name)))
705 (and name
706 (cond ((memq (if (consp name) (car name) name)
707 '(verb xvrb 8bitmime onex xone
708 expn size dsn etrn
709 enhancedstatuscodes
710 help xusr
711 auth=login auth starttls))
712 (setq supported-extensions
713 (cons name supported-extensions)))
714 (smtpmail-warn-about-unknown-extensions
715 (message "Unknown extension %s" name)))))))
716
717 (if (and do-starttls
718 (smtpmail-find-credentials smtpmail-starttls-credentials host port)
719 (member 'starttls supported-extensions)
720 (numberp (process-id process)))
721 (progn
722 (smtpmail-send-command process (format "STARTTLS"))
723 (if (or (null (car (setq response-code (smtpmail-read-response process))))
724 (not (integerp (car response-code)))
725 (>= (car response-code) 400))
726 (throw 'done nil))
727 (starttls-negotiate process)
728 (setq do-starttls nil))
729 (setq do-ehlo nil))))
a8ba4429 730
9056f1c9 731 (smtpmail-try-auth-methods process supported-extensions host port)
c50d5ce0
RS
732
733 (if (or (member 'onex supported-extensions)
734 (member 'xone supported-extensions))
735 (progn
736 (smtpmail-send-command process (format "ONEX"))
737 (if (or (null (car (setq response-code (smtpmail-read-response process))))
738 (not (integerp (car response-code)))
739 (>= (car response-code) 400))
740 (throw 'done nil))))
741
9056f1c9 742 (if (and smtpmail-debug-verb
c50d5ce0
RS
743 (or (member 'verb supported-extensions)
744 (member 'xvrb supported-extensions)))
745 (progn
746 (smtpmail-send-command process (format "VERB"))
747 (if (or (null (car (setq response-code (smtpmail-read-response process))))
748 (not (integerp (car response-code)))
749 (>= (car response-code) 400))
750 (throw 'done nil))))
751
752 (if (member 'xusr supported-extensions)
753 (progn
754 (smtpmail-send-command process (format "XUSR"))
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))))
24975917 759
a8df98fd 760 ;; MAIL FROM:<sender>
c50d5ce0 761 (let ((size-part
9056f1c9
RS
762 (if (or (member 'size supported-extensions)
763 (assoc 'size supported-extensions))
c50d5ce0 764 (format " SIZE=%d"
21bf0d6c 765 (with-current-buffer smtpmail-text-buffer
c50d5ce0
RS
766 ;; size estimate:
767 (+ (- (point-max) (point-min))
768 ;; Add one byte for each change-of-line
73921ac1
GM
769 ;; because of CR-LF representation:
770 (count-lines (point-min) (point-max)))))
c50d5ce0
RS
771 ""))
772 (body-part
773 (if (member '8bitmime supported-extensions)
774 ;; FIXME:
775 ;; Code should be added here that transforms
776 ;; the contents of the message buffer into
777 ;; something the receiving SMTP can handle.
778 ;; For a receiver that supports 8BITMIME, this
779 ;; may mean converting BINARY to BASE64, or
780 ;; adding Content-Transfer-Encoding and the
781 ;; other MIME headers. The code should also
782 ;; return an indication of what encoding the
783 ;; message buffer is now, i.e. ASCII or
784 ;; 8BITMIME.
785 (if nil
786 " BODY=8BITMIME"
787 "")
788 "")))
75da36cc 789 ;; (smtpmail-send-command process (format "MAIL FROM:%s@%s" (user-login-name) (smtpmail-fqdn)))
a8df98fd 790 (smtpmail-send-command process (format "MAIL FROM:<%s>%s%s"
ff981226 791 envelope-from
c50d5ce0
RS
792 size-part
793 body-part))
a1506d29 794
c50d5ce0
RS
795 (if (or (null (car (setq response-code (smtpmail-read-response process))))
796 (not (integerp (car response-code)))
797 (>= (car response-code) 400))
75da36cc 798 (throw 'done nil)))
a8ba4429 799
a8df98fd 800 ;; RCPT TO:<recipient>
8805249b
RS
801 (let ((n 0))
802 (while (not (null (nth n recipient)))
a8df98fd 803 (smtpmail-send-command process (format "RCPT TO:<%s>" (smtpmail-maybe-append-domain (nth n recipient))))
8805249b
RS
804 (setq n (1+ n))
805
c50d5ce0
RS
806 (setq response-code (smtpmail-read-response process))
807 (if (or (null (car response-code))
8805249b
RS
808 (not (integerp (car response-code)))
809 (>= (car response-code) 400))
75da36cc 810 (throw 'done nil))))
a8ba4429 811
24975917
RS
812 ;; DATA
813 (smtpmail-send-command process "DATA")
814
815 (if (or (null (car (setq response-code (smtpmail-read-response process))))
816 (not (integerp (car response-code)))
817 (>= (car response-code) 400))
75da36cc 818 (throw 'done nil))
24975917
RS
819
820 ;; Mail contents
821 (smtpmail-send-data process smtpmail-text-buffer)
822
75da36cc 823 ;; DATA end "."
24975917
RS
824 (smtpmail-send-command process ".")
825
826 (if (or (null (car (setq response-code (smtpmail-read-response process))))
827 (not (integerp (car response-code)))
828 (>= (car response-code) 400))
75da36cc
RW
829 (throw 'done nil))
830
831 ;; QUIT
832 ;; (smtpmail-send-command process "QUIT")
833 ;; (and (null (car (smtpmail-read-response process)))
834 ;; (throw 'done nil))
835 t))
24975917 836 (if process
21bf0d6c 837 (with-current-buffer (process-buffer process)
24975917
RS
838 (smtpmail-send-command process "QUIT")
839 (smtpmail-read-response process)
840
75da36cc
RW
841 ;; (if (or (null (car (setq response-code (smtpmail-read-response process))))
842 ;; (not (integerp (car response-code)))
843 ;; (>= (car response-code) 400))
844 ;; (throw 'done nil))
1121afb4
SJ
845 (delete-process process)
846 (unless smtpmail-debug-info
847 (kill-buffer process-buffer)))))))
24975917
RS
848
849
24975917 850(defun smtpmail-process-filter (process output)
21bf0d6c 851 (with-current-buffer (process-buffer process)
24975917
RS
852 (goto-char (point-max))
853 (insert output)))
854
24975917
RS
855(defun smtpmail-read-response (process)
856 (let ((case-fold-search nil)
c50d5ce0 857 (response-strings nil)
24975917 858 (response-continue t)
c50d5ce0 859 (return-value '(nil ()))
24975917 860 match-end)
2ceed428
SJ
861 (catch 'done
862 (while response-continue
863 (goto-char smtpmail-read-point)
864 (while (not (search-forward "\r\n" nil t))
865 (unless (memq (process-status process) '(open run))
866 (throw 'done nil))
867 (accept-process-output process)
868 (goto-char smtpmail-read-point))
869
870 (setq match-end (point))
871 (setq response-strings
872 (cons (buffer-substring smtpmail-read-point (- match-end 2))
873 response-strings))
874
875 (goto-char smtpmail-read-point)
876 (if (looking-at "[0-9]+ ")
877 (let ((begin (match-beginning 0))
878 (end (match-end 0)))
879 (if smtpmail-debug-info
880 (message "%s" (car response-strings)))
881
882 (setq smtpmail-read-point match-end)
883
884 ;; ignore lines that start with "0"
885 (if (looking-at "0[0-9]+ ")
886 nil
887 (setq response-continue nil)
888 (setq return-value
027a4b6b 889 (cons (string-to-number
2ceed428
SJ
890 (buffer-substring begin end))
891 (nreverse response-strings)))))
892
893 (if (looking-at "[0-9]+-")
894 (progn (if smtpmail-debug-info
895 (message "%s" (car response-strings)))
896 (setq smtpmail-read-point match-end)
897 (setq response-continue t))
898 (progn
899 (setq smtpmail-read-point match-end)
c50d5ce0
RS
900 (setq response-continue nil)
901 (setq return-value
2ceed428
SJ
902 (cons nil (nreverse response-strings)))))))
903 (setq smtpmail-read-point match-end))
24975917
RS
904 return-value))
905
906
24975917
RS
907(defun smtpmail-send-command (process command)
908 (goto-char (point-max))
909 (if (= (aref command 0) ?P)
910 (insert "PASS <omitted>\r\n")
911 (insert command "\r\n"))
912 (setq smtpmail-read-point (point))
913 (process-send-string process command)
914 (process-send-string process "\r\n"))
915
24975917
RS
916(defun smtpmail-send-data-1 (process data)
917 (goto-char (point-max))
918
83af570e
KH
919 (if (and (multibyte-string-p data)
920 smtpmail-code-conv-from)
921 (setq data (string-as-multibyte
922 (encode-coding-string data smtpmail-code-conv-from))))
a8ba4429 923
24975917
RS
924 (if smtpmail-debug-info
925 (insert data "\r\n"))
926
927 (setq smtpmail-read-point (point))
57810560
KH
928 ;; Escape "." at start of a line
929 (if (eq (string-to-char data) ?.)
24975917 930 (process-send-string process "."))
57810560 931 (process-send-string process data)
75da36cc 932 (process-send-string process "\r\n"))
24975917
RS
933
934(defun smtpmail-send-data (process buffer)
dd64e5e5 935 (let ((data-continue t) sending-data)
21bf0d6c 936 (with-current-buffer buffer
24975917 937 (goto-char (point-min)))
24975917 938 (while data-continue
21bf0d6c 939 (with-current-buffer buffer
dd64e5e5
GM
940 (setq sending-data (buffer-substring (point-at-bol) (point-at-eol)))
941 (end-of-line 2)
942 (setq data-continue (not (eobp))))
943 (smtpmail-send-data-1 process sending-data))))
24975917
RS
944
945(defun smtpmail-deduce-address-list (smtpmail-text-buffer header-start header-end)
946 "Get address list suitable for smtp RCPT TO: <address>."
1188dd37 947 (unwind-protect
21bf0d6c
SM
948 (with-current-buffer smtpmail-address-buffer
949 (erase-buffer)
75da36cc
RW
950 (let ((case-fold-search t)
951 (simple-address-list "")
952 this-line
953 this-line-end
954 addr-regexp)
24975917
RS
955 (insert-buffer-substring smtpmail-text-buffer header-start header-end)
956 (goto-char (point-min))
13f1d088
KH
957 ;; RESENT-* fields should stop processing of regular fields.
958 (save-excursion
21bf0d6c
SM
959 (setq addr-regexp
960 (if (re-search-forward "^Resent-\\(to\\|cc\\|bcc\\):"
961 header-end t)
962 "^Resent-\\(to\\|cc\\|bcc\\):"
963 "^\\(To:\\|Cc:\\|Bcc:\\)")))
13f1d088
KH
964
965 (while (re-search-forward addr-regexp header-end t)
24975917
RS
966 (replace-match "")
967 (setq this-line (match-beginning 0))
968 (forward-line 1)
969 ;; get any continuation lines
970 (while (and (looking-at "^[ \t]+") (< (point) header-end))
971 (forward-line 1))
972 (setq this-line-end (point-marker))
973 (setq simple-address-list
974 (concat simple-address-list " "
75da36cc 975 (mail-strip-quoted-names (buffer-substring this-line this-line-end)))))
24975917 976 (erase-buffer)
92dfd10c 977 (insert " " simple-address-list "\n")
75da36cc
RW
978 (subst-char-in-region (point-min) (point-max) 10 ? t) ; newline --> blank
979 (subst-char-in-region (point-min) (point-max) ?, ? t) ; comma --> blank
980 (subst-char-in-region (point-min) (point-max) 9 ? t) ; tab --> blank
24975917
RS
981
982 (goto-char (point-min))
983 ;; tidyness in case hook is not robust when it looks at this
984 (while (re-search-forward "[ \t]+" header-end t) (replace-match " "))
985
986 (goto-char (point-min))
8805249b 987 (let (recipient-address-list)
e2f7c221 988 (while (re-search-forward " \\([^ ]+\\) " (point-max) t)
8805249b 989 (backward-char 1)
e2f7c221 990 (setq recipient-address-list (cons (buffer-substring (match-beginning 1) (match-end 1))
75da36cc
RW
991 recipient-address-list)))
992 (setq smtpmail-recipient-address-list recipient-address-list))))))
24975917
RS
993
994(defun smtpmail-do-bcc (header-end)
5feeeae2 995 "Delete [Resent-]BCC: and their continuation lines from the header area.
24975917
RS
996There may be multiple BCC: lines, and each may have arbitrarily
997many continuation lines."
998 (let ((case-fold-search t))
067427f5
KH
999 (save-excursion
1000 (goto-char (point-min))
1001 ;; iterate over all BCC: lines
1002 (while (re-search-forward "^\\(RESENT-\\)?BCC:" header-end t)
1003 (delete-region (match-beginning 0)
1004 (progn (forward-line 1) (point)))
1005 ;; get rid of any continuation lines
1006 (while (and (looking-at "^[ \t].*\n") (< (point) header-end))
1007 (replace-match ""))))))
24975917 1008
24975917
RS
1009(provide 'smtpmail)
1010
cbee283d 1011;; arch-tag: a76992df-6d71-43b7-9e72-4bacc6c05466
092af6d8 1012;;; smtpmail.el ends here