Mercurial > hg > xemacs-beta
comparison lisp/utils/smtpmail.el @ 4:b82b59fe008d r19-15b3
Import from CVS: tag r19-15b3
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:46:56 +0200 |
parents | |
children | 56c54cf7c5b6 8619ce7e4c50 |
comparison
equal
deleted
inserted
replaced
3:30df88044ec6 | 4:b82b59fe008d |
---|---|
1 ;; Simple SMTP protocol (RFC 821) for sending mail | |
2 | |
3 ;; Copyright (C) 1995, 1996 Free Software Foundation, Inc. | |
4 | |
5 ;; Author: Tomoji Kagatani <kagatani@rbc.ncl.omron.co.jp> | |
6 ;; Maintainer: Brian D. Carlstrom <bdc@ai.mit.edu> | |
7 ;; Keywords: mail | |
8 | |
9 ;; This file is part of XEmacs. | |
10 | |
11 ;; XEmacs is free software; you can redistribute it and/or modify it | |
12 ;; under the terms of the GNU General Public License as published by | |
13 ;; the Free Software Foundation; either version 2, or (at your option) | |
14 ;; any later version. | |
15 | |
16 ;; XEmacs is distributed in the hope that it will be useful, but | |
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 ;; General Public License for more details. | |
20 | |
21 ;; You should have received a copy of the GNU General Public License | |
22 ;; along with XEmacs; see the file COPYING. If not, write to the Free | |
23 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
24 ;; 02111-1307, USA. | |
25 | |
26 ;;; Synched up with: FSF 19.34. | |
27 | |
28 ;;; Commentary: | |
29 | |
30 ;; Send Mail to smtp host from smtpmail temp buffer. | |
31 | |
32 ;; Please add these lines in your .emacs(_emacs). | |
33 ;; | |
34 ;;(setq send-mail-function 'smtpmail-send-it) | |
35 ;;(setq smtpmail-default-smtp-server "YOUR SMTP HOST") | |
36 ;;(setq smtpmail-smtp-service "smtp") | |
37 ;;(setq smtpmail-local-domain "YOUR DOMAIN NAME") | |
38 ;;(setq smtpmail-debug-info t) | |
39 ;;(load-library "smtpmail") | |
40 ;;(setq smtpmail-code-conv-from nil) | |
41 ;;(setq user-full-name "YOUR NAME HERE") | |
42 | |
43 ;;; Code: | |
44 | |
45 (require 'sendmail) | |
46 | |
47 ;;; | |
48 (defvar smtpmail-default-smtp-server nil | |
49 "*Specify default SMTP server.") | |
50 | |
51 (defvar smtpmail-smtp-server | |
52 (or (getenv "SMTPSERVER") smtpmail-default-smtp-server) | |
53 "*The name of the host running SMTP server.") | |
54 | |
55 (defvar smtpmail-smtp-service 25 | |
56 "*SMTP service port number. smtp or 25 .") | |
57 | |
58 (defvar smtpmail-local-domain nil | |
59 "*Local domain name without a host name. | |
60 If the function (system-name) returns the full internet address, | |
61 don't define this value.") | |
62 | |
63 (defvar smtpmail-debug-info nil | |
64 "*smtpmail debug info printout. messages and process buffer.") | |
65 | |
66 (defvar smtpmail-code-conv-from nil ;; *junet* | |
67 "*smtpmail code convert from this code to *internal*..for tiny-mime..") | |
68 | |
69 ;;; | |
70 ;;; | |
71 ;;; | |
72 | |
73 (defun smtpmail-send-it () | |
74 (require 'mail-utils) | |
75 (let ((errbuf (if mail-interactive | |
76 (generate-new-buffer " smtpmail errors") | |
77 0)) | |
78 (tembuf (generate-new-buffer " smtpmail temp")) | |
79 (case-fold-search nil) | |
80 resend-to-addresses | |
81 delimline | |
82 (mailbuf (current-buffer))) | |
83 (unwind-protect | |
84 (save-excursion | |
85 (set-buffer tembuf) | |
86 (erase-buffer) | |
87 (insert-buffer-substring mailbuf) | |
88 (goto-char (point-max)) | |
89 ;; require one newline at the end. | |
90 (or (= (preceding-char) ?\n) | |
91 (insert ?\n)) | |
92 ;; Change header-delimiter to be what sendmail expects. | |
93 (goto-char (point-min)) | |
94 (re-search-forward | |
95 (concat "^" (regexp-quote mail-header-separator) "\n")) | |
96 (replace-match "\n") | |
97 (backward-char 1) | |
98 (setq delimline (point-marker)) | |
99 ;; (sendmail-synch-aliases) | |
100 (if (and mail-aliases (fboundp expand-mail-aliases)) ; XEmacs | |
101 (expand-mail-aliases (point-min) delimline)) | |
102 (goto-char (point-min)) | |
103 ;; ignore any blank lines in the header | |
104 (while (and (re-search-forward "\n\n\n*" delimline t) | |
105 (< (point) delimline)) | |
106 (replace-match "\n")) | |
107 (let ((case-fold-search t)) | |
108 (goto-char (point-min)) | |
109 (goto-char (point-min)) | |
110 (while (re-search-forward "^Resent-to:" delimline t) | |
111 (setq resend-to-addresses | |
112 (save-restriction | |
113 (narrow-to-region (point) | |
114 (save-excursion | |
115 (end-of-line) | |
116 (point))) | |
117 (append (mail-parse-comma-list) | |
118 resend-to-addresses)))) | |
119 ;;; Apparently this causes a duplicate Sender. | |
120 ;;; ;; If the From is different than current user, insert Sender. | |
121 ;;; (goto-char (point-min)) | |
122 ;;; (and (re-search-forward "^From:" delimline t) | |
123 ;;; (progn | |
124 ;;; (require 'mail-utils) | |
125 ;;; (not (string-equal | |
126 ;;; (mail-strip-quoted-names | |
127 ;;; (save-restriction | |
128 ;;; (narrow-to-region (point-min) delimline) | |
129 ;;; (mail-fetch-field "From"))) | |
130 ;;; (user-login-name)))) | |
131 ;;; (progn | |
132 ;;; (forward-line 1) | |
133 ;;; (insert "Sender: " (user-login-name) "\n"))) | |
134 ;; Don't send out a blank subject line | |
135 (goto-char (point-min)) | |
136 (if (re-search-forward "^Subject:[ \t]*\n" delimline t) | |
137 (replace-match "")) | |
138 ;; Put the "From:" field in unless for some odd reason | |
139 ;; they put one in themselves. | |
140 (goto-char (point-min)) | |
141 (if (not (re-search-forward "^From:" delimline t)) | |
142 (let* ((login user-mail-address) | |
143 (fullname (user-full-name))) | |
144 (cond ((eq mail-from-style 'angles) | |
145 (insert "From: " fullname) | |
146 (let ((fullname-start (+ (point-min) 6)) | |
147 (fullname-end (point-marker))) | |
148 (goto-char fullname-start) | |
149 ;; Look for a character that cannot appear unquoted | |
150 ;; according to RFC 822. | |
151 (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" | |
152 fullname-end 1) | |
153 (progn | |
154 ;; Quote fullname, escaping specials. | |
155 (goto-char fullname-start) | |
156 (insert "\"") | |
157 (while (re-search-forward "[\"\\]" | |
158 fullname-end 1) | |
159 (replace-match "\\\\\\&" t)) | |
160 (insert "\"")))) | |
161 (insert " <" login ">\n")) | |
162 ((eq mail-from-style 'parens) | |
163 (insert "From: " login " (") | |
164 (let ((fullname-start (point))) | |
165 (insert fullname) | |
166 (let ((fullname-end (point-marker))) | |
167 (goto-char fullname-start) | |
168 ;; RFC 822 says \ and nonmatching parentheses | |
169 ;; must be escaped in comments. | |
170 ;; Escape every instance of ()\ ... | |
171 (while (re-search-forward "[()\\]" fullname-end 1) | |
172 (replace-match "\\\\\\&" t)) | |
173 ;; ... then undo escaping of matching parentheses, | |
174 ;; including matching nested parentheses. | |
175 (goto-char fullname-start) | |
176 (while (re-search-forward | |
177 "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)" | |
178 fullname-end 1) | |
179 (replace-match "\\1(\\3)" t) | |
180 (goto-char fullname-start)))) | |
181 (insert ")\n")) | |
182 ((null mail-from-style) | |
183 (insert "From: " login "\n"))))) | |
184 ;; Insert an extra newline if we need it to work around | |
185 ;; Sun's bug that swallows newlines. | |
186 (goto-char (1+ delimline)) | |
187 (if (eval mail-mailer-swallows-blank-line) | |
188 (newline)) | |
189 ;; Find and handle any FCC fields. | |
190 (goto-char (point-min)) | |
191 (if (re-search-forward "^FCC:" delimline t) | |
192 (mail-do-fcc delimline)) | |
193 (if mail-interactive | |
194 (save-excursion | |
195 (set-buffer errbuf) | |
196 (erase-buffer)))) | |
197 ;; | |
198 ;; | |
199 ;; | |
200 (setq smtpmail-address-buffer (generate-new-buffer "*smtp-mail*")) | |
201 (setq smtpmail-recipient-address-list | |
202 (or resend-to-addresses | |
203 (smtpmail-deduce-address-list tembuf (point-min) delimline))) | |
204 (kill-buffer smtpmail-address-buffer) | |
205 | |
206 (smtpmail-do-bcc delimline) | |
207 | |
208 (if (not (null smtpmail-recipient-address-list)) | |
209 (if (not (smtpmail-via-smtp smtpmail-recipient-address-list tembuf)) | |
210 (error "Sending failed; SMTP protocol error")) | |
211 (error "Sending failed; no recipients")) | |
212 ) | |
213 (kill-buffer tembuf) | |
214 (if (bufferp errbuf) | |
215 (kill-buffer errbuf))))) | |
216 | |
217 | |
218 ;(defun smtpmail-via-smtp (host,port,sender,destination,smtpmail-text-buffer) | |
219 | |
220 (defun smtpmail-fqdn () | |
221 (if smtpmail-local-domain | |
222 (concat (system-name) "." smtpmail-local-domain) | |
223 (system-name))) | |
224 | |
225 (defun smtpmail-via-smtp (recipient smtpmail-text-buffer) | |
226 (let ((process nil) | |
227 (host smtpmail-smtp-server) | |
228 (port smtpmail-smtp-service) | |
229 response-code | |
230 greeting | |
231 process-buffer) | |
232 (unwind-protect | |
233 (catch 'done | |
234 ;; get or create the trace buffer | |
235 (setq process-buffer | |
236 (get-buffer-create (format "*trace of SMTP session to %s*" host))) | |
237 | |
238 ;; clear the trace buffer of old output | |
239 (save-excursion | |
240 (set-buffer process-buffer) | |
241 (erase-buffer)) | |
242 | |
243 ;; open the connection to the server | |
244 (setq process (open-network-stream "SMTP" process-buffer host port)) | |
245 (and (null process) (throw 'done nil)) | |
246 | |
247 ;; set the send-filter | |
248 (set-process-filter process 'smtpmail-process-filter) | |
249 | |
250 (save-excursion | |
251 (set-buffer process-buffer) | |
252 (make-local-variable 'smtpmail-read-point) | |
253 (setq smtpmail-read-point (point-min)) | |
254 | |
255 | |
256 (if (or (null (car (setq greeting (smtpmail-read-response process)))) | |
257 (not (integerp (car greeting))) | |
258 (>= (car greeting) 400)) | |
259 (throw 'done nil) | |
260 ) | |
261 | |
262 ;; HELO | |
263 (smtpmail-send-command process (format "HELO %s" (smtpmail-fqdn))) | |
264 | |
265 (if (or (null (car (setq response-code (smtpmail-read-response process)))) | |
266 (not (integerp (car response-code))) | |
267 (>= (car response-code) 400)) | |
268 (throw 'done nil) | |
269 ) | |
270 | |
271 ;; MAIL FROM: <sender> | |
272 ; (smtpmail-send-command process (format "MAIL FROM:%s@%s" (user-login-name) (smtpmail-fqdn))) | |
273 (smtpmail-send-command process (format "MAIL FROM: <%s>" user-mail-address)) | |
274 | |
275 (if (or (null (car (setq response-code (smtpmail-read-response process)))) | |
276 (not (integerp (car response-code))) | |
277 (>= (car response-code) 400)) | |
278 (throw 'done nil) | |
279 ) | |
280 | |
281 ;; RCPT TO: <recipient> | |
282 (let ((n 0)) | |
283 (while (not (null (nth n recipient))) | |
284 (smtpmail-send-command process (format "RCPT TO: <%s>" (nth n recipient))) | |
285 (setq n (1+ n)) | |
286 | |
287 (if (or (null (car (setq response-code (smtpmail-read-response process)))) | |
288 (not (integerp (car response-code))) | |
289 (>= (car response-code) 400)) | |
290 (throw 'done nil) | |
291 ) | |
292 )) | |
293 | |
294 ;; DATA | |
295 (smtpmail-send-command process "DATA") | |
296 | |
297 (if (or (null (car (setq response-code (smtpmail-read-response process)))) | |
298 (not (integerp (car response-code))) | |
299 (>= (car response-code) 400)) | |
300 (throw 'done nil) | |
301 ) | |
302 | |
303 ;; Mail contents | |
304 (smtpmail-send-data process smtpmail-text-buffer) | |
305 | |
306 ;;DATA end "." | |
307 (smtpmail-send-command process ".") | |
308 | |
309 (if (or (null (car (setq response-code (smtpmail-read-response process)))) | |
310 (not (integerp (car response-code))) | |
311 (>= (car response-code) 400)) | |
312 (throw 'done nil) | |
313 ) | |
314 | |
315 ;;QUIT | |
316 ; (smtpmail-send-command process "QUIT") | |
317 ; (and (null (car (smtpmail-read-response process))) | |
318 ; (throw 'done nil)) | |
319 t )) | |
320 (if process | |
321 (save-excursion | |
322 (set-buffer (process-buffer process)) | |
323 (smtpmail-send-command process "QUIT") | |
324 (smtpmail-read-response process) | |
325 | |
326 ; (if (or (null (car (setq response-code (smtpmail-read-response process)))) | |
327 ; (not (integerp (car response-code))) | |
328 ; (>= (car response-code) 400)) | |
329 ; (throw 'done nil) | |
330 ; ) | |
331 (delete-process process)))))) | |
332 | |
333 | |
334 (defun smtpmail-process-filter (process output) | |
335 (save-excursion | |
336 (set-buffer (process-buffer process)) | |
337 (goto-char (point-max)) | |
338 (insert output))) | |
339 | |
340 (defun smtpmail-read-response (process) | |
341 (let ((case-fold-search nil) | |
342 (response-string nil) | |
343 (response-continue t) | |
344 (return-value '(nil "")) | |
345 match-end) | |
346 | |
347 ; (setq response-string nil) | |
348 ; (setq response-continue t) | |
349 ; (setq return-value '(nil "")) | |
350 | |
351 (while response-continue | |
352 (goto-char smtpmail-read-point) | |
353 (while (not (search-forward "\r\n" nil t)) | |
354 (accept-process-output process) | |
355 (goto-char smtpmail-read-point)) | |
356 | |
357 (setq match-end (point)) | |
358 (if (null response-string) | |
359 (setq response-string | |
360 (buffer-substring smtpmail-read-point (- match-end 2)))) | |
361 | |
362 (goto-char smtpmail-read-point) | |
363 (if (looking-at "[0-9]+ ") | |
364 (progn (setq response-continue nil) | |
365 ; (setq return-value response-string) | |
366 | |
367 (if smtpmail-debug-info | |
368 (message response-string)) | |
369 | |
370 (setq smtpmail-read-point match-end) | |
371 (setq return-value | |
372 (cons (string-to-int | |
373 (buffer-substring (match-beginning 0) (match-end 0))) | |
374 response-string))) | |
375 | |
376 (if (looking-at "[0-9]+-") | |
377 (progn (setq smtpmail-read-point match-end) | |
378 (setq response-continue t)) | |
379 (progn | |
380 (setq smtpmail-read-point match-end) | |
381 (setq response-continue nil) | |
382 (setq return-value | |
383 (cons nil response-string)) | |
384 ) | |
385 ))) | |
386 (setq smtpmail-read-point match-end) | |
387 return-value)) | |
388 | |
389 | |
390 (defun smtpmail-send-command (process command) | |
391 (goto-char (point-max)) | |
392 (if (= (aref command 0) ?P) | |
393 (insert "PASS <omitted>\r\n") | |
394 (insert command "\r\n")) | |
395 (setq smtpmail-read-point (point)) | |
396 (process-send-string process command) | |
397 (process-send-string process "\r\n")) | |
398 | |
399 (defun smtpmail-send-data-1 (process data) | |
400 (goto-char (point-max)) | |
401 | |
402 (if (not (null smtpmail-code-conv-from)) | |
403 (setq data (code-convert-string data smtpmail-code-conv-from *internal*))) | |
404 | |
405 (if smtpmail-debug-info | |
406 (insert data "\r\n")) | |
407 | |
408 (setq smtpmail-read-point (point)) | |
409 ;; Escape "." at start of a line | |
410 (if (eq (string-to-char data) ?.) | |
411 (process-send-string process ".")) | |
412 (process-send-string process data) | |
413 (process-send-string process "\r\n") | |
414 ) | |
415 | |
416 (defun smtpmail-send-data (process buffer) | |
417 (let | |
418 ((data-continue t) | |
419 (sending-data nil) | |
420 this-line | |
421 this-line-end) | |
422 | |
423 (save-excursion | |
424 (set-buffer buffer) | |
425 (goto-char (point-min))) | |
426 | |
427 (while data-continue | |
428 (save-excursion | |
429 (set-buffer buffer) | |
430 (beginning-of-line) | |
431 (setq this-line (point)) | |
432 (end-of-line) | |
433 (setq this-line-end (point)) | |
434 (setq sending-data nil) | |
435 (setq sending-data (buffer-substring this-line this-line-end)) | |
436 (if (/= (forward-line 1) 0) | |
437 (setq data-continue nil))) | |
438 | |
439 (smtpmail-send-data-1 process sending-data) | |
440 ) | |
441 ) | |
442 ) | |
443 | |
444 | |
445 (defun smtpmail-deduce-address-list (smtpmail-text-buffer header-start header-end) | |
446 "Get address list suitable for smtp RCPT TO: <address>." | |
447 (require 'mail-utils) ;; pick up mail-strip-quoted-names | |
448 (let | |
449 ((case-fold-search t) | |
450 (simple-address-list "") | |
451 this-line | |
452 this-line-end | |
453 addr-regexp) | |
454 | |
455 (unwind-protect | |
456 (save-excursion | |
457 ;; | |
458 (set-buffer smtpmail-address-buffer) (erase-buffer) | |
459 (insert-buffer-substring smtpmail-text-buffer header-start header-end) | |
460 (goto-char (point-min)) | |
461 ;; RESENT-* fields should stop processing of regular fields. | |
462 (save-excursion | |
463 (if (re-search-forward "^RESENT-TO:" header-end t) | |
464 (setq addr-regexp "^\\(RESENT-TO:\\|RESENT-CC:\\|RESENT-BCC:\\)") | |
465 (setq addr-regexp "^\\(TO:\\|CC:\\|BCC:\\)"))) | |
466 | |
467 (while (re-search-forward addr-regexp header-end t) | |
468 (replace-match "") | |
469 (setq this-line (match-beginning 0)) | |
470 (forward-line 1) | |
471 ;; get any continuation lines | |
472 (while (and (looking-at "^[ \t]+") (< (point) header-end)) | |
473 (forward-line 1)) | |
474 (setq this-line-end (point-marker)) | |
475 (setq simple-address-list | |
476 (concat simple-address-list " " | |
477 (mail-strip-quoted-names (buffer-substring this-line this-line-end)))) | |
478 ) | |
479 (erase-buffer) | |
480 (insert-string " ") | |
481 (insert-string simple-address-list) | |
482 (insert-string "\n") | |
483 (subst-char-in-region (point-min) (point-max) 10 ? t);; newline --> blank | |
484 (subst-char-in-region (point-min) (point-max) ?, ? t);; comma --> blank | |
485 (subst-char-in-region (point-min) (point-max) 9 ? t);; tab --> blank | |
486 | |
487 (goto-char (point-min)) | |
488 ;; tidyness in case hook is not robust when it looks at this | |
489 (while (re-search-forward "[ \t]+" header-end t) (replace-match " ")) | |
490 | |
491 (goto-char (point-min)) | |
492 (let (recipient-address-list) | |
493 (while (re-search-forward " \\([^ ]+\\) " (point-max) t) | |
494 (backward-char 1) | |
495 (setq recipient-address-list (cons (buffer-substring (match-beginning 1) (match-end 1)) | |
496 recipient-address-list)) | |
497 ) | |
498 (setq smtpmail-recipient-address-list recipient-address-list)) | |
499 | |
500 ) | |
501 ) | |
502 ) | |
503 ) | |
504 | |
505 | |
506 (defun smtpmail-do-bcc (header-end) | |
507 "Delete BCC: and their continuation lines from the header area. | |
508 There may be multiple BCC: lines, and each may have arbitrarily | |
509 many continuation lines." | |
510 (let ((case-fold-search t)) | |
511 (save-excursion (goto-char (point-min)) | |
512 ;; iterate over all BCC: lines | |
513 (while (re-search-forward "^BCC:" header-end t) | |
514 (delete-region (match-beginning 0) (progn (forward-line 1) (point))) | |
515 ;; get rid of any continuation lines | |
516 (while (and (looking-at "^[ \t].*\n") (< (point) header-end)) | |
517 (replace-match "")) | |
518 ) | |
519 ) ;; save-excursion | |
520 ) ;; let | |
521 ) | |
522 | |
523 | |
524 | |
525 (provide 'smtpmail) | |
526 | |
527 ;; smtpmail.el ends here |