smtpmail.el 36 KB

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