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