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