aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard M. Stallman2001-11-17 22:43:36 +0000
committerRichard M. Stallman2001-11-17 22:43:36 +0000
commit9056f1c9f7fa8d0f6fd25df4a5d96d7764cf7e60 (patch)
tree9180eb5a1317c0796046c88ce24c8783a784c341
parent843f91fdd35ba5398376751f0e89b08636f5dadf (diff)
downloademacs-9056f1c9f7fa8d0f6fd25df4a5d96d7764cf7e60.tar.gz
emacs-9056f1c9f7fa8d0f6fd25df4a5d96d7764cf7e60.zip
(smtpmail-cred-server, smtpmail-cred-port, smtpmail-cred-key)
(smtpmail-cred-user, smtpmail-cred-cert, smtpmail-cred-passwd): Defsubst instead of defmacro. (smtpmail-intersection): Return value in reverse order. (smtpmail-open-stream): Use stringp instead of string-to-list. (smtpmail-open-stream, smtpmail-try-auth-methods): New functions, separated from smtpmail-via-smtp. (top level): Autoload starttls, mail-utils and rfc2104. (smtpmail-smtp-service): Doc fix. :type fix. (smtpmail-debug-info): Doc fix. (smtpmail-debug-verb, smtpmail-auth-credentials) (smtpmail-starttls-credentials, smtpmail-auth-supported): New variables. (smtpmail-deduce-address-list, smtpmail-send-it): Don't require mail-utils (it is autoloaded). (smtpmail-cred-server, smtpmail-cred-port, smtpmail-cred-key) (smtpmail-cred-user, smtpmail-cred-cert, smtpmail-cred-passwd) (smtpmail-find-credentials, smtpmail-intersection): New utility funs. (smtpmail-via-smtp): Support STARTTLS, if binary is installed. (smtpmail-via-smtp): Support AUTH. (smtpmail-via-smtp): Use `smtpmail-debug-verb' to control VERB.
-rw-r--r--lisp/mail/smtpmail.el231
1 files changed, 211 insertions, 20 deletions
diff --git a/lisp/mail/smtpmail.el b/lisp/mail/smtpmail.el
index 50ff26f6f7e..a4eed65c54e 100644
--- a/lisp/mail/smtpmail.el
+++ b/lisp/mail/smtpmail.el
@@ -3,10 +3,12 @@
3;; Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc. 3;; Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc.
4 4
5;; Author: Tomoji Kagatani <kagatani@rbc.ncl.omron.co.jp> 5;; Author: Tomoji Kagatani <kagatani@rbc.ncl.omron.co.jp>
6;; Maintainer: Brian D. Carlstrom <bdc@ai.mit.edu> 6;; Maintainer: Simon Josefsson <simon@josefsson.org>
7;; w32 Maintainer: Brian D. Carlstrom <bdc@ai.mit.edu>
7;; ESMTP support: Simon Leinen <simon@switch.ch> 8;; ESMTP support: Simon Leinen <simon@switch.ch>
8;; Hacked by Mike Taylor, 11th October 1999 to add support for 9;; Hacked by Mike Taylor, 11th October 1999 to add support for
9;; automatically appending a domain to RCPT TO: addresses. 10;; automatically appending a domain to RCPT TO: addresses.
11;; AUTH=LOGIN support: Stephen Cranefield <scranefield@infoscience.otago.ac.nz>
10;; Keywords: mail 12;; Keywords: mail
11 13
12;; This file is part of GNU Emacs. 14;; This file is part of GNU Emacs.
@@ -38,15 +40,37 @@
38;;(setq smtpmail-local-domain "YOUR DOMAIN NAME") 40;;(setq smtpmail-local-domain "YOUR DOMAIN NAME")
39;;(setq smtpmail-sendto-domain "YOUR DOMAIN NAME") 41;;(setq smtpmail-sendto-domain "YOUR DOMAIN NAME")
40;;(setq smtpmail-debug-info t) ; only to debug problems 42;;(setq smtpmail-debug-info t) ; only to debug problems
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")))
41 47
42;; To queue mail, set smtpmail-queue-mail to t and use 48;; To queue mail, set smtpmail-queue-mail to t and use
43;; smtpmail-send-queued-mail to send. 49;; smtpmail-send-queued-mail to send.
44 50
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
45 65
46;;; Code: 66;;; Code:
47 67
48(require 'sendmail) 68(require 'sendmail)
49(require 'time-stamp) 69(require 'time-stamp)
70(autoload 'starttls-open-stream "starttls")
71(autoload 'starttls-negotiate "starttls")
72(autoload 'mail-strip-quoted-names "mail-utils")
73(autoload 'rfc2104-hash "rfc2104")
50 74
51;;; 75;;;
52(defgroup smtpmail nil 76(defgroup smtpmail nil
@@ -66,8 +90,9 @@
66 :group 'smtpmail) 90 :group 'smtpmail)
67 91
68(defcustom smtpmail-smtp-service 25 92(defcustom smtpmail-smtp-service 25
69 "*SMTP service port number. smtp or 25 ." 93 "*SMTP service port number.
70 :type 'integer 94The default value would be \"smtp\" or 25 ."
95 :type '(choice (integer :tag "Port") (string :tag "Service"))
71 :group 'smtpmail) 96 :group 'smtpmail)
72 97
73(defcustom smtpmail-local-domain nil 98(defcustom smtpmail-local-domain nil
@@ -94,7 +119,15 @@ buffer includes an exchange like:
94 :group 'smtpmail) 119 :group 'smtpmail)
95 120
96(defcustom smtpmail-debug-info nil 121(defcustom smtpmail-debug-info nil
97 "*smtpmail debug info printout. messages and process buffer." 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."
98 :type 'boolean 131 :type 'boolean
99 :group 'smtpmail) 132 :group 'smtpmail)
100 133
@@ -115,6 +148,32 @@ and sent with `smtpmail-send-queued-mail'."
115 :type 'directory 148 :type 'directory
116 :group 'smtpmail) 149 :group 'smtpmail)
117 150
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
118(defcustom smtpmail-warn-about-unknown-extensions nil 177(defcustom smtpmail-warn-about-unknown-extensions nil
119 "*If set, print warnings about unknown SMTP extensions. 178 "*If set, print warnings about unknown SMTP extensions.
120This is mainly useful for development purposes, to learn about 179This is mainly useful for development purposes, to learn about
@@ -136,13 +195,15 @@ This is relative to `smtpmail-queue-dir'.")
136(defvar smtpmail-queue-index (concat smtpmail-queue-dir 195(defvar smtpmail-queue-index (concat smtpmail-queue-dir
137 smtpmail-queue-index-file)) 196 smtpmail-queue-index-file))
138 197
198(defconst smtpmail-auth-supported '(cram-md5 login)
199 "List of supported SMTP AUTH mechanisms.")
200
139;;; 201;;;
140;;; 202;;;
141;;; 203;;;
142 204
143;;;###autoload 205;;;###autoload
144(defun smtpmail-send-it () 206(defun smtpmail-send-it ()
145 (require 'mail-utils)
146 (let ((errbuf (if mail-interactive 207 (let ((errbuf (if mail-interactive
147 (generate-new-buffer " smtpmail errors") 208 (generate-new-buffer " smtpmail errors")
148 0)) 209 0))
@@ -332,12 +393,117 @@ This is relative to `smtpmail-queue-dir'.")
332 (concat (system-name) "." smtpmail-local-domain) 393 (concat (system-name) "." smtpmail-local-domain)
333 (system-name))) 394 (system-name)))
334 395
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
335(defun smtpmail-maybe-append-domain (recipient) 422(defun smtpmail-maybe-append-domain (recipient)
336 (if (or (not smtpmail-sendto-domain) 423 (if (or (not smtpmail-sendto-domain)
337 (string-match "@" recipient)) 424 (string-match "@" recipient))
338 recipient 425 recipient
339 (concat recipient "@" smtpmail-sendto-domain))) 426 (concat recipient "@" smtpmail-sendto-domain)))
340 427
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
341(defun smtpmail-via-smtp (recipient smtpmail-text-buffer) 507(defun smtpmail-via-smtp (recipient smtpmail-text-buffer)
342 (let ((process nil) 508 (let ((process nil)
343 (host (or smtpmail-smtp-server 509 (host (or smtpmail-smtp-server
@@ -359,7 +525,7 @@ This is relative to `smtpmail-queue-dir'.")
359 (erase-buffer)) 525 (erase-buffer))
360 526
361 ;; open the connection to the server 527 ;; open the connection to the server
362 (setq process (open-network-stream "SMTP" process-buffer host port)) 528 (setq process (smtpmail-open-stream process-buffer host port))
363 (and (null process) (throw 'done nil)) 529 (and (null process) (throw 'done nil))
364 530
365 ;; set the send-filter 531 ;; set the send-filter
@@ -378,32 +544,58 @@ This is relative to `smtpmail-queue-dir'.")
378 (throw 'done nil) 544 (throw 'done nil)
379 ) 545 )
380 546
547 (let ((do-ehlo t)
548 (do-starttls t))
549 (while do-ehlo
381 ;; EHLO 550 ;; EHLO
382 (smtpmail-send-command process (format "EHLO %s" (smtpmail-fqdn))) 551 (smtpmail-send-command process (format "EHLO %s" (smtpmail-fqdn)))
383 552
384 (if (or (null (car (setq response-code (smtpmail-read-response process)))) 553 (if (or (null (car (setq response-code
554 (smtpmail-read-response process))))
385 (not (integerp (car response-code))) 555 (not (integerp (car response-code)))
386 (>= (car response-code) 400)) 556 (>= (car response-code) 400))
387 (progn 557 (progn
388 ;; HELO 558 ;; HELO
389 (smtpmail-send-command process (format "HELO %s" (smtpmail-fqdn))) 559 (smtpmail-send-command
560 process (format "HELO %s" (smtpmail-fqdn)))
390 561
391 (if (or (null (car (setq response-code (smtpmail-read-response process)))) 562 (if (or (null (car (setq response-code
563 (smtpmail-read-response process))))
392 (not (integerp (car response-code))) 564 (not (integerp (car response-code)))
393 (>= (car response-code) 400)) 565 (>= (car response-code) 400))
394 (throw 'done nil))) 566 (throw 'done nil)))
395 (let ((extension-lines (cdr (cdr response-code)))) 567 (dolist (line (cdr (cdr response-code)))
396 (while extension-lines 568 (let ((name (mapcar (lambda (s) (intern (downcase s)))
397 (let ((name (intern (downcase (car (split-string (substring (car extension-lines) 4) "[ ]")))))) 569 (split-string (substring line 4) "[ ]"))))
570 (and (eq (length name) 1)
571 (setq name (car name)))
398 (and name 572 (and name
399 (cond ((memq name '(verb xvrb 8bitmime onex xone 573 (cond ((memq (if (consp name) (car name) name)
574 '(verb xvrb 8bitmime onex xone
400 expn size dsn etrn 575 expn size dsn etrn
401 help xusr)) 576 enhancedstatuscodes
577 help xusr
578 auth=login auth starttls))
402 (setq supported-extensions 579 (setq supported-extensions
403 (cons name supported-extensions))) 580 (cons name supported-extensions)))
404 (smtpmail-warn-about-unknown-extensions 581 (smtpmail-warn-about-unknown-extensions
405 (message "Unknown extension %s" name))))) 582 (message "Unknown extension %s" name)))))))
406 (setq extension-lines (cdr extension-lines))))) 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)
407 599
408 (if (or (member 'onex supported-extensions) 600 (if (or (member 'onex supported-extensions)
409 (member 'xone supported-extensions)) 601 (member 'xone supported-extensions))
@@ -414,7 +606,7 @@ This is relative to `smtpmail-queue-dir'.")
414 (>= (car response-code) 400)) 606 (>= (car response-code) 400))
415 (throw 'done nil)))) 607 (throw 'done nil))))
416 608
417 (if (and smtpmail-debug-info 609 (if (and smtpmail-debug-verb
418 (or (member 'verb supported-extensions) 610 (or (member 'verb supported-extensions)
419 (member 'xvrb supported-extensions))) 611 (member 'xvrb supported-extensions)))
420 (progn 612 (progn
@@ -434,7 +626,8 @@ This is relative to `smtpmail-queue-dir'.")
434 626
435 ;; MAIL FROM: <sender> 627 ;; MAIL FROM: <sender>
436 (let ((size-part 628 (let ((size-part
437 (if (member 'size supported-extensions) 629 (if (or (member 'size supported-extensions)
630 (assoc 'size supported-extensions))
438 (format " SIZE=%d" 631 (format " SIZE=%d"
439 (save-excursion 632 (save-excursion
440 (set-buffer smtpmail-text-buffer) 633 (set-buffer smtpmail-text-buffer)
@@ -650,8 +843,6 @@ This is relative to `smtpmail-queue-dir'.")
650 843
651(defun smtpmail-deduce-address-list (smtpmail-text-buffer header-start header-end) 844(defun smtpmail-deduce-address-list (smtpmail-text-buffer header-start header-end)
652 "Get address list suitable for smtp RCPT TO: <address>." 845 "Get address list suitable for smtp RCPT TO: <address>."
653 (require 'mail-utils) ;; pick up mail-strip-quoted-names
654
655 (unwind-protect 846 (unwind-protect
656 (save-excursion 847 (save-excursion
657 (set-buffer smtpmail-address-buffer) (erase-buffer) 848 (set-buffer smtpmail-address-buffer) (erase-buffer)