* SLIM: Version 1.13.6 released.
[elisp/flim.git] / smtpmail.el
1 ;;; smtpmail.el --- SMTP interface for mail-mode
2
3 ;; Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc.
4
5 ;; Author: Tomoji Kagatani <kagatani@rbc.ncl.omron.co.jp>
6 ;; Keywords: mail
7
8 ;; This file is part of FLIM (Faithful Library about Internet Message).
9
10 ;; This program is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
14
15 ;; This program is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program; see the file COPYING.  If not, write to
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;; Send Mail to smtp host from smtpmail temp buffer.
28
29 ;; Please add these lines in your .emacs(_emacs).
30 ;;
31 ;;(setq send-mail-function 'smtpmail-send-it)
32 ;;(setq smtp-default-server "YOUR SMTP HOST")
33 ;;(setq smtp-service "smtp")
34 ;;(setq smtp-local-domain "YOUR DOMAIN NAME")
35 ;;(setq smtp-debug-info t)
36 ;;(autoload 'smtpmail-send-it "smtpmail")
37 ;;(setq user-full-name "YOUR NAME HERE")
38
39 ;; To queue mail, set smtpmail-queue-mail to t and use 
40 ;; smtpmail-send-queued-mail to send.
41
42
43 ;;; Code:
44
45 (require 'poe)
46 (require 'pcustom)
47 (require 'smtp)
48 (require 'sendmail)
49 (require 'time-stamp)
50
51 (eval-when-compile (require 'static))
52
53 (static-when (featurep 'xemacs)
54   (define-obsolete-variable-alias 'smtpmail-default-smtp-server
55     'smtp-default-server)
56   (define-obsolete-variable-alias 'smtpmail-smtp-server 'smtp-server)
57   (define-obsolete-variable-alias 'smtpmail-smtp-service 'smtp-service)
58   (define-obsolete-variable-alias 'smtpmail-local-domain 'smtp-local-domain)
59   (define-obsolete-variable-alias 'smtpmail-debug-info 'smtp-debug-info)
60   )
61
62 ;;;
63
64 (defcustom smtpmail-queue-mail nil 
65   "*Specify if mail is queued (if t) or sent immediately (if nil).
66 If queued, it is stored in the directory `smtpmail-queue-dir'
67 and sent with `smtpmail-send-queued-mail'."
68   :type 'boolean
69   :group 'smtp)
70
71 (defcustom smtpmail-queue-dir "~/Mail/queued-mail/"
72   "*Directory where `smtpmail.el' stores queued mail."
73   :type 'directory
74   :group 'smtp)
75
76 (defvar smtpmail-queue-index-file "index"
77   "File name of queued mail index,
78 This is relative to `smtpmail-queue-dir'.")
79
80 (defvar smtpmail-queue-index (concat smtpmail-queue-dir
81                                      smtpmail-queue-index-file))
82
83 (defvar smtpmail-recipient-address-list nil)
84
85
86 ;;;
87 ;;;
88 ;;;
89
90 ;;;###autoload
91 (defun smtpmail-send-it ()
92   (require 'mail-utils)
93   (let ((errbuf (if mail-interactive
94                     (generate-new-buffer " smtpmail errors")
95                   0))
96         (tembuf (generate-new-buffer " smtpmail temp"))
97         (case-fold-search nil)
98         resend-to-addresses
99         delimline
100         (mailbuf (current-buffer)))
101     (unwind-protect
102         (save-excursion
103           (set-buffer tembuf)
104           (erase-buffer)
105           (insert-buffer-substring mailbuf)
106           (goto-char (point-max))
107           ;; require one newline at the end.
108           (or (= (preceding-char) ?\n)
109               (insert ?\n))
110           ;; Change header-delimiter to be what sendmail expects.
111           (goto-char (point-min))
112           (re-search-forward
113             (concat "^" (regexp-quote mail-header-separator) "\n"))
114           (replace-match "\n")
115           (backward-char 1)
116           (setq delimline (point-marker))
117 ;;        (sendmail-synch-aliases)
118           (if (and mail-aliases (fboundp 'expand-mail-aliases)) ; XEmacs
119               (expand-mail-aliases (point-min) delimline))
120           (goto-char (point-min))
121           ;; ignore any blank lines in the header
122           (while (and (re-search-forward "\n\n\n*" delimline t)
123                       (< (point) delimline))
124             (replace-match "\n"))
125           (let ((case-fold-search t))
126             (goto-char (point-min))
127             (goto-char (point-min))
128             (while (re-search-forward "^Resent-to:" delimline t)
129               (setq resend-to-addresses
130                     (save-restriction
131                       (narrow-to-region (point)
132                                         (save-excursion
133                                           (forward-line 1)
134                                           (while (looking-at "^[ \t]")
135                                             (forward-line 1))
136                                           (point)))
137                       (append (mail-parse-comma-list)
138                               resend-to-addresses))))
139 ;;; Apparently this causes a duplicate Sender.
140 ;;;         ;; If the From is different than current user, insert Sender.
141 ;;;         (goto-char (point-min))
142 ;;;         (and (re-search-forward "^From:"  delimline t)
143 ;;;              (progn
144 ;;;                (require 'mail-utils)
145 ;;;                (not (string-equal
146 ;;;                      (mail-strip-quoted-names
147 ;;;                       (save-restriction
148 ;;;                         (narrow-to-region (point-min) delimline)
149 ;;;                         (mail-fetch-field "From")))
150 ;;;                      (user-login-name))))
151 ;;;              (progn
152 ;;;                (forward-line 1)
153 ;;;                (insert "Sender: " (user-login-name) "\n")))
154             ;; Don't send out a blank subject line
155             (goto-char (point-min))
156             (if (re-search-forward "^Subject:[ \t]*\n" delimline t)
157                 (replace-match ""))
158             ;; Put the "From:" field in unless for some odd reason
159             ;; they put one in themselves.
160             (goto-char (point-min))
161             (if (not (re-search-forward "^From:" delimline t))
162                 (let* ((login user-mail-address)
163                        (fullname (user-full-name)))
164                   (cond ((eq mail-from-style 'angles)
165                          (insert "From: " fullname)
166                          (let ((fullname-start (+ (point-min) 6))
167                                (fullname-end (point-marker)))
168                            (goto-char fullname-start)
169                            ;; Look for a character that cannot appear unquoted
170                            ;; according to RFC 822.
171                            (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]"
172                                                   fullname-end 1)
173                                (progn
174                                  ;; Quote fullname, escaping specials.
175                                  (goto-char fullname-start)
176                                  (insert "\"")
177                                  (while (re-search-forward "[\"\\]"
178                                                            fullname-end 1)
179                                    (replace-match "\\\\\\&" t))
180                                  (insert "\""))))
181                          (insert " <" login ">\n"))
182                         ((eq mail-from-style 'parens)
183                          (insert "From: " login " (")
184                          (let ((fullname-start (point)))
185                            (insert fullname)
186                            (let ((fullname-end (point-marker)))
187                              (goto-char fullname-start)
188                              ;; RFC 822 says \ and nonmatching parentheses
189                              ;; must be escaped in comments.
190                              ;; Escape every instance of ()\ ...
191                              (while (re-search-forward "[()\\]" fullname-end 1)
192                                (replace-match "\\\\\\&" t))
193                              ;; ... then undo escaping of matching parentheses,
194                              ;; including matching nested parentheses.
195                              (goto-char fullname-start)
196                              (while (re-search-forward 
197                                      "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
198                                      fullname-end 1)
199                                (replace-match "\\1(\\3)" t)
200                                (goto-char fullname-start))))
201                          (insert ")\n"))
202                         ((null mail-from-style)
203                          (insert "From: " login "\n")))))
204             ;; Insert an extra newline if we need it to work around
205             ;; Sun's bug that swallows newlines.
206             (goto-char (1+ delimline))
207             (if (eval mail-mailer-swallows-blank-line)
208                 (newline))
209             ;; Find and handle any FCC fields.
210             (goto-char (point-min))
211             (if (re-search-forward "^FCC:" delimline t)
212                 (mail-do-fcc delimline))
213             (if mail-interactive
214                 (save-excursion
215                   (set-buffer errbuf)
216                   (erase-buffer))))
217           ;;
218           ;;
219           ;;
220           (setq smtpmail-recipient-address-list
221                 (or resend-to-addresses
222                     (smtp-deduce-address-list tembuf (point-min) delimline)))
223
224           (smtpmail-do-bcc delimline)
225           ; Send or queue
226           (if (not smtpmail-queue-mail)
227               (if smtpmail-recipient-address-list
228                   (if (not (smtp-via-smtp user-mail-address
229                                           smtpmail-recipient-address-list
230                                           tembuf))
231                       (error "Sending failed; SMTP protocol error"))
232                 (error "Sending failed; no recipients"))
233             (let* ((file-data (concat 
234                                smtpmail-queue-dir
235                                    (mapconcat
236                                         (lambda (arg) (format "%x" arg))
237                                         (current-time) "")))
238                    (file-elisp (concat file-data ".el"))
239                    (buffer-data (create-file-buffer file-data))
240                    (buffer-elisp (create-file-buffer file-elisp))
241                    (buffer-scratch "*queue-mail*"))
242               (save-excursion
243                 (set-buffer buffer-data)
244                 (erase-buffer)
245                 (insert-buffer tembuf)
246                 (write-region-as-binary (point-min) (point-max) file-data)
247                 (set-buffer buffer-elisp)
248                 (erase-buffer)
249                 (insert (concat
250                          "(setq smtpmail-recipient-address-list '"
251                          (prin1-to-string smtpmail-recipient-address-list)
252                          ")\n"))                    
253                 (write-file file-elisp)
254                 (set-buffer (generate-new-buffer buffer-scratch))
255                 (insert (concat file-data "\n"))
256                 (append-to-file (point-min) 
257                                 (point-max) 
258                                 smtpmail-queue-index)
259                 )
260               (kill-buffer buffer-scratch)
261               (kill-buffer buffer-data)
262               (kill-buffer buffer-elisp))))
263       (kill-buffer tembuf)
264       (if (bufferp errbuf)
265           (kill-buffer errbuf)))))
266
267 (defun smtpmail-send-queued-mail ()
268   "Send mail that was queued as a result of setting `smtpmail-queue-mail'."
269   (interactive)
270   ;;; Get index, get first mail, send it, get second mail, etc...
271   (let ((buffer-index (find-file-noselect smtpmail-queue-index))
272         (file-msg "")
273         (tembuf nil))
274     (save-excursion
275       (set-buffer buffer-index)
276       (beginning-of-buffer)
277       (while (not (eobp))
278         (setq file-msg (buffer-substring (point) (save-excursion
279                                                    (end-of-line)
280                                                    (point))))
281         (load file-msg)
282         (setq tembuf (find-file-noselect-as-binary file-msg))
283         (if smtpmail-recipient-address-list
284             (if (not (smtp-via-smtp user-mail-address
285                                     smtpmail-recipient-address-list tembuf))
286                 (error "Sending failed; SMTP protocol error"))
287           (error "Sending failed; no recipients"))  
288         (delete-file file-msg)
289         (delete-file (concat file-msg ".el"))
290         (kill-buffer tembuf)
291         (kill-line 1))      
292       (set-buffer buffer-index)
293       (save-buffer smtpmail-queue-index)
294       (kill-buffer buffer-index)
295       )))
296
297
298 (defun smtpmail-do-bcc (header-end)
299   "Delete BCC: and their continuation lines from the header area.
300 There may be multiple BCC: lines, and each may have arbitrarily
301 many continuation lines."
302   (let ((case-fold-search t))
303     (save-excursion
304       (goto-char (point-min))
305       ;; iterate over all BCC: lines
306       (while (re-search-forward "^BCC:" header-end t)
307         (delete-region (match-beginning 0) (progn (forward-line 1) (point)))
308         ;; get rid of any continuation lines
309         (while (and (looking-at "^[ \t].*\n") (< (point) header-end))
310           (replace-match ""))
311         )
312       ) ;; save-excursion
313     ) ;; let
314   )
315
316
317 ;;;
318
319 (provide 'smtpmail)
320
321 ;;; smtpmail.el ends here