rfe12658: send-letter: allow alternate transport
<release-note>
The first argument to net.post-office:send-letter, the server
specification, can now specify an alternate transport mechanism,
instead of the usual SMTP network protocol.
</release-note>
<documentation>
net.post-office:send-letter's server argument can now be used to
specify a program that does the delivery of the message. As in:
(:program . "/usr/sbin/sendmail -t")
or
(:program . ("/usr/sbin/sendmail" "-odq" "-t"))
The first form will invoke "/usr/sbin/sendmail -t" and pipe the message,
constructed from the other arguments to send-letter, to the program.
The second form will invoke excl:run-shell-command with this first
argument:
(vector "/usr/sbin/sendmail" "/usr/sbin/sendmail" "-odq" "-t")
which allows the program to be executed without an intermediate shell.
</documentation>
Change-Id: I7811b99bee6819dee7e026ba464974bd2ea803d8
Showing 1 changed files
... | ... |
@@ -1,38 +1,5 @@ |
1 |
-#+(or (version= 8 2) |
|
2 |
- (version= 9 0)) |
|
3 |
-(sys:defpatch "smtp" 1 |
|
4 |
- "v1: Handle SMTP servers which violate SMTP SASL AUTH protocol." |
|
5 |
- :type :system |
|
6 |
- :post-loadable t) |
|
7 |
- |
|
8 |
-#+(version= 8 1) |
|
9 |
-(sys:defpatch "smtp" 1 |
|
10 |
- "v1: add smtp support for ssl connections and STARTTLS negotiation." |
|
11 |
- :type :system |
|
12 |
- :post-loadable t) |
|
13 |
- |
|
14 |
-#+(version= 8 0) ;; not current with latest sources |
|
15 |
-(sys:defpatch "smtp" 5 |
|
16 |
- "v1: send-letter w/attachments; send-smtp* can take streams; |
|
17 |
-v2: add :port argument to send-letter, send-smtp, send-smtp-auth; |
|
18 |
-v3: fix incompatibility introduced in v2; |
|
19 |
-v4: remove stray force-output of t; |
|
20 |
-v5: send-smtp-1: New external-format keyword arg." |
|
21 |
- :type :system |
|
22 |
- :post-loadable t) |
|
23 |
- |
|
24 |
-#+(version= 7 0) ;; not current with latest sources |
|
25 |
-(sys:defpatch "smtp" 5 |
|
26 |
- "v2: send-letter w/attachments; send-smtp* can take streams; |
|
27 |
-v3: add :port argument to send-letter, send-smtp, send-smtp-auth; |
|
28 |
-v4: fix incompatibility introduced in v3; |
|
29 |
-v5: rm stray force-output of t; send-smtp-1: New external-format keyword arg." |
|
30 |
- :type :system |
|
31 |
- :post-loadable t) |
|
32 |
- |
|
33 | 1 |
;; -*- mode: common-lisp; package: net.post-office -*- |
34 |
-;; |
|
35 |
-;; smtp.cl |
|
2 |
+;; send mail to an smtp server. See rfc821 for the spec. |
|
36 | 3 |
;; |
37 | 4 |
;; copyright (c) 1986-2002 Franz Inc, Berkeley, CA - All rights reserved. |
38 | 5 |
;; copyright (c) 2002-2013 Franz Inc, Oakland, CA - All rights reserved. |
... | ... |
@@ -54,20 +21,26 @@ v5: rm stray force-output of t; send-smtp-1: New external-format keyword arg." |
54 | 21 |
;; http://www.gnu.org/copyleft/lesser.txt (until superseded by a newer |
55 | 22 |
;; version) or write to the Free Software Foundation, Inc., 59 Temple Place, |
56 | 23 |
;; Suite 330, Boston, MA 02111-1307 USA |
57 |
-;; |
|
58 |
-;; |
|
59 |
-;; $Id: smtp.cl,v 1.24 2008/09/16 23:22:14 layer Exp $ |
|
60 | 24 |
|
61 |
-;; Description: |
|
62 |
-;; send mail to an smtp server. See rfc821 for the spec. |
|
25 |
+#+(or (version= 8 2) |
|
26 |
+ (version= 9 0)) |
|
27 |
+(sys:defpatch "smtp" 2 |
|
28 |
+ "v1: Handle SMTP servers which violate SMTP SASL AUTH protocol; |
|
29 |
+v2: add new type of server argument to send-letter." |
|
30 |
+ :type :system |
|
31 |
+ :post-loadable t) |
|
63 | 32 |
|
64 |
-;;- This code in this file obeys the Lisp Coding Standard found in |
|
65 |
-;;- http://www.franz.com/~jkf/coding_standards.html |
|
66 |
-;;- |
|
33 |
+#+(version= 8 1) |
|
34 |
+(sys:defpatch "smtp" 1 |
|
35 |
+ "v1: add smtp support for ssl connections and STARTTLS negotiation." |
|
36 |
+ :type :system |
|
37 |
+ :post-loadable t) |
|
67 | 38 |
|
39 |
+(eval-when (compile eval load) |
|
40 |
+ (require :osi)) |
|
68 | 41 |
|
69 | 42 |
(defpackage :net.post-office |
70 |
- (:use #:lisp #:excl) |
|
43 |
+ (:use #:lisp #:excl #:excl.osi) |
|
71 | 44 |
(:export |
72 | 45 |
#:send-letter |
73 | 46 |
#:send-smtp |
... | ... |
@@ -268,16 +241,65 @@ Attachments must be filenames, streams, or mime-part-constructed, not ~s" |
268 | 241 |
(setf (mime-part-parts message) (append parts-save res)))) |
269 | 242 |
|
270 | 243 |
(with-mime-part-constructed-stream (s message) |
271 |
- (send-smtp-auth server from (append tos ccs bccs) |
|
272 |
- login password |
|
273 |
- hdrs |
|
274 |
- user-headers |
|
275 |
- s)) |
|
244 |
+ (if* (and (consp server) (eq :program (car server))) |
|
245 |
+ then (send-external-program (cdr server) hdrs user-headers s) |
|
246 |
+ else (send-smtp-auth server from (append tos ccs bccs) |
|
247 |
+ login password |
|
248 |
+ hdrs |
|
249 |
+ user-headers |
|
250 |
+ s))) |
|
276 | 251 |
|
277 | 252 |
(setf (mime-part-parts message) parts-save) |
278 | 253 |
t))) |
279 |
- |
|
280 |
- |
|
254 |
+ |
|
255 |
+(defun send-external-program (program &rest messages |
|
256 |
+ &aux (external-format :default)) |
|
257 |
+ (multiple-value-bind (stdout stderr exit-status) |
|
258 |
+ (command-output |
|
259 |
+ (if* (stringp program) |
|
260 |
+ then program |
|
261 |
+ elseif (consp program) |
|
262 |
+ then #+mswindows program |
|
263 |
+ #-mswindows (apply #'vector (car program) program) |
|
264 |
+ else (error "Bad program argument: ~s." program)) |
|
265 |
+ :input (lambda (stream) |
|
266 |
+ (create-message stream messages external-format))) |
|
267 |
+ (when (/= 0 exit-status) |
|
268 |
+ (error "external program failed to send email (~s, ~s)." |
|
269 |
+ stdout stderr)))) |
|
270 |
+ |
|
271 |
+(defun create-message (output-stream messages external-format) |
|
272 |
+ (let ((at-bol t) |
|
273 |
+ (prev-ch nil) |
|
274 |
+ ch input-stream) |
|
275 |
+ (dolist (message messages) |
|
276 |
+ (when message |
|
277 |
+ (setq input-stream |
|
278 |
+ (if* (streamp message) |
|
279 |
+ then message |
|
280 |
+ else (make-buffer-input-stream |
|
281 |
+ (string-to-octets |
|
282 |
+ message |
|
283 |
+ :null-terminate nil |
|
284 |
+ :external-format external-format)))) |
|
285 |
+ |
|
286 |
+ (while (setf ch (read-byte input-stream nil)) |
|
287 |
+ (if* (and at-bol (eq ch #.(char-code #\.))) |
|
288 |
+ then ;; to prevent . from being interpreted as eol |
|
289 |
+ (write-char #\. output-stream)) |
|
290 |
+ (if* (eq ch #.(char-code #\newline)) |
|
291 |
+ then (setq at-bol t) |
|
292 |
+ (if* (not (eq prev-ch #.(char-code #\return))) |
|
293 |
+ then (write-char #\return output-stream)) |
|
294 |
+ else (setq at-bol nil)) |
|
295 |
+ (write-byte ch output-stream) |
|
296 |
+ (setq prev-ch ch))))) |
|
297 |
+ (write-char #\return output-stream) |
|
298 |
+ (write-char #\linefeed output-stream) |
|
299 |
+ (write-char #\. output-stream) |
|
300 |
+ (write-char #\return output-stream) |
|
301 |
+ (write-char #\linefeed output-stream)) |
|
302 |
+ |
|
281 | 303 |
(defun send-smtp (server from to &rest messages) |
282 | 304 |
(send-smtp-1 server from to nil nil messages)) |
283 | 305 |
|
... | ... |
@@ -285,7 +307,10 @@ Attachments must be filenames, streams, or mime-part-constructed, not ~s" |
285 | 307 |
(send-smtp-1 server from to login password messages)) |
286 | 308 |
|
287 | 309 |
(defun send-smtp-1 (server from to login password messages |
288 |
- &key (external-format :default)) |
|
310 |
+ &key (external-format |
|
311 |
+ ;; Never used, this might as well be an &aux |
|
312 |
+ ;; variable |
|
313 |
+ :default)) |
|
289 | 314 |
;; send the effective concatenation of the messages via |
290 | 315 |
;; smtp to the mail server |
291 | 316 |
;; Each message should be a string or a stream. |
... | ... |
@@ -324,36 +349,8 @@ Attachments must be filenames, streams, or mime-part-constructed, not ~s" |
324 | 349 |
(t (smtp-transaction-error))) |
325 | 350 |
|
326 | 351 |
|
352 |
+ (create-message sock messages external-format) |
|
327 | 353 |
|
328 |
- (let ((at-bol t) |
|
329 |
- (prev-ch nil) |
|
330 |
- ch stream) |
|
331 |
- (dolist (message messages) |
|
332 |
- (when message |
|
333 |
- (setf stream (if* (streamp message) |
|
334 |
- then message |
|
335 |
- else (make-buffer-input-stream |
|
336 |
- (string-to-octets |
|
337 |
- message |
|
338 |
- :null-terminate nil |
|
339 |
- :external-format external-format)))) |
|
340 |
- |
|
341 |
- (while (setf ch (read-byte stream nil)) |
|
342 |
- (if* (and at-bol (eq ch #.(char-code #\.))) |
|
343 |
- then ;; to prevent . from being interpreted as eol |
|
344 |
- (write-char #\. sock)) |
|
345 |
- (if* (eq ch #.(char-code #\newline)) |
|
346 |
- then (setq at-bol t) |
|
347 |
- (if* (not (eq prev-ch #.(char-code #\return))) |
|
348 |
- then (write-char #\return sock)) |
|
349 |
- else (setq at-bol nil)) |
|
350 |
- (write-byte ch sock) |
|
351 |
- (setq prev-ch ch))))) |
|
352 |
- |
|
353 |
- (write-char #\return sock) (write-char #\linefeed sock) |
|
354 |
- (write-char #\. sock) |
|
355 |
- (write-char #\return sock) (write-char #\linefeed sock) |
|
356 |
- |
|
357 | 354 |
(response-case (sock msg) |
358 | 355 |
(2 nil ; (format t "Message sent to ~a~%" to) |
359 | 356 |
) |
... | ... |
@@ -366,6 +363,7 @@ Attachments must be filenames, streams, or mime-part-constructed, not ~s" |
366 | 363 |
(t (smtp-transaction-error)))) |
367 | 364 |
;; Cleanup |
368 | 365 |
(close sock)))) |
366 |
+ |
|
369 | 367 |
|
370 | 368 |
(defun connect-to-mail-server (server login password) |
371 | 369 |
;; make that initial connection to the mail server |