git.fiddlerwoaroof.com
Browse code

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

Kevin Layer authored on 25/10/2013 18:54:04
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