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