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