git.fiddlerwoaroof.com
Browse code

join from acl7 to trunk

layer authored on 03/08/2005 05:17:29
Showing 3 changed files
... ...
@@ -1,3 +1,18 @@
1
+2005-02-15  Kevin Layer  <layer@gemini>
2
+
3
+	* smtp.cl: bug15190: from jkf: fix nameserver lookup if *dns-mode*
4
+	  is just :clib
5
+
6
+2004-09-28  Kevin Layer  <layer@gemini>
7
+
8
+	* imap.cl: fix copyright notice
9
+
10
+2004-09-14  Kevin Layer  <layer@gemini>
11
+
12
+	* smtp.cl:
13
+	- add new exported `send-smtp-auth' (from dancy)
14
+	- add `login' and `password' keywords to send-letter
15
+
1 16
 2004-08-13  Kevin Layer  <layer@gemini>
2 17
 imap 1.13
3 18
 
... ...
@@ -6,25 +6,20 @@
6 6
 ;; copyright (c) 1999 Franz Inc, Berkeley, CA - All rights reserved.
7 7
 ;; copyright (c) 1999-2004 Franz Inc, Oakland, CA - All rights reserved.
8 8
 ;;
9
-;; The software, data and information contained herein are proprietary
10
-;; to, and comprise valuable trade secrets of, Franz, Inc.  They are
11
-;; given in confidence by Franz, Inc. pursuant to a written license
12
-;; agreement, and may be stored and used only in accordance with the terms
13
-;; of such license.
9
+;; This code is free software; you can redistribute it and/or
10
+;; modify it under the terms of the version 2.1 of
11
+;; the GNU Lesser General Public License as published by 
12
+;; the Free Software Foundation, as clarified by the AllegroServe
13
+;; prequel found in license-allegroserve.txt.
14 14
 ;;
15
-;; Restricted Rights Legend
16
-;; ------------------------
17
-;; Use, duplication, and disclosure of the software, data and information
18
-;; contained herein by any agency, department or entity of the U.S.
19
-;; Government are subject to restrictions of Restricted Rights for
20
-;; Commercial Software developed at private expense as specified in
21
-;; DOD FAR Supplement 52.227-7013 (c) (1) (ii), as applicable.
15
+;; This code is distributed in the hope that it will be useful,
16
+;; but without any warranty; without even the implied warranty of
17
+;; merchantability or fitness for a particular purpose.  See the GNU
18
+;; Lesser General Public License for more details.
22 19
 ;;
23
-;; $Id: imap.cl,v 1.26 2004/08/27 18:06:31 layer Exp $
20
+;; $Id: imap.cl,v 1.27 2005/08/03 05:17:29 layer Exp $
24 21
 
25 22
 ;; Description:
26
-;;
27
-;;
28 23
 ;;- This code in this file obeys the Lisp Coding Standard found in
29 24
 ;;- http://www.franz.com/~jkf/coding_standards.html
30 25
 ;;-
... ...
@@ -24,7 +24,7 @@
24 24
 ;; Suite 330, Boston, MA  02111-1307  USA
25 25
 ;;
26 26
 ;;
27
-;; $Id: smtp.cl,v 1.8 2004/01/16 19:31:52 layer Exp $
27
+;; $Id: smtp.cl,v 1.9 2005/08/03 05:17:29 layer Exp $
28 28
 
29 29
 ;; Description:
30 30
 ;;   send mail to an smtp server.  See rfc821 for the spec.
... ...
@@ -39,10 +39,13 @@
39 39
   (:export 
40 40
    #:send-letter
41 41
    #:send-smtp
42
+   #:send-smtp-auth
42 43
    #:test-email-address))
43 44
 
44 45
 (in-package :net.post-office)
45 46
 
47
+(eval-when (compile load eval)
48
+  (require :sasl))
46 49
 
47 50
 ;; the exported functions:
48 51
 
... ...
@@ -109,7 +112,8 @@
109 112
 
110 113
 
111 114
 (defun send-letter (server from to message
112
-		    &key cc bcc subject reply-to headers)
115
+		    &key cc bcc subject reply-to headers
116
+			 login password)
113 117
   ;;
114 118
   ;; see documentation at the head of this file
115 119
   ;;
... ...
@@ -159,15 +163,19 @@
159 163
     
160 164
     (format header "~c~c" #\return #\linefeed)
161 165
     
162
-    (send-smtp server from (append tos ccs bccs)
166
+    (send-smtp-auth server from (append tos ccs bccs)
167
+	       login password
163 168
 	       (get-output-stream-string header)
164 169
 	       message)))
165 170
     
166 171
     
172
+(defun send-smtp(server from to &rest messages)
173
+  (send-smtp-1 server from to nil nil messages))
167 174
 	  
175
+(defun send-smtp-auth (server from to login password &rest messages)
176
+  (send-smtp-1 server from to login password messages))
168 177
 		    
169
-
170
-(defun send-smtp (server from to &rest messages)
178
+(defun send-smtp-1 (server from to login password messages)
171 179
   ;; send the effective concatenation of the messages via
172 180
   ;; smtp to the mail server
173 181
   ;; Each message should be a string
... ...
@@ -176,7 +184,7 @@
176 184
   ;; each string should be in the official rfc822 format  "foo@bar.com"
177 185
   ;;
178 186
 
179
-  (let ((sock (connect-to-mail-server server)))
187
+  (let ((sock (connect-to-mail-server server login password)))
180 188
   
181 189
     (unwind-protect
182 190
 	(progn
... ...
@@ -245,7 +253,7 @@
245 253
 	    (t (error "quit failed: ~s" msg))))
246 254
       (close sock))))
247 255
 
248
-(defun connect-to-mail-server (server)
256
+(defun connect-to-mail-server (server login password)
249 257
   ;; make that initial connection to the mail server
250 258
   ;; returning a socket connected to it and 
251 259
   ;; signaling an error if it can't be made.
... ...
@@ -273,11 +281,10 @@
273 281
 	       then (setq hostname
274 282
 		      (format nil "[~a]" (socket:ipaddr-to-dotted
275 283
 					  (socket:local-host sock)))))
276
-	    (smtp-command sock "HELO ~a" hostname)
277
-	    (response-case (sock msg)
278
-	      (2 ;; ok
279
-	       nil)
280
-	      (t (error "hello greeting failed: ~s" msg))))
284
+	    (let ((mechs (smtp-ehlo sock hostname)))
285
+	      (if (and mechs login password)
286
+		  (setf sock 
287
+		    (smtp-authenticate sock server mechs login password)))))
281 288
 	  
282 289
 	  ; all is good
283 290
 	  (setq ok t))
... ...
@@ -292,6 +299,61 @@
292 299
     ))
293 300
 	    
294 301
 
302
+;; Returns string with mechanisms, or nil if none.
303
+;; This may need to be expanded in the future as we support
304
+;; more of the features that EHLO responds with.
305
+(defun smtp-ehlo (sock our-name)
306
+  (smtp-command sock "EHLO ~A" our-name)
307
+  (response-case (sock msg)
308
+    (2 ;; ok
309
+     ;; Collect the auth mechanisms.
310
+     (multiple-value-bind (found whole mechs)
311
+	 (match-regexp "250[- ]AUTH \\(.*\\)" msg)
312
+       (declare (ignore whole))
313
+       (if found
314
+	   mechs)))
315
+    (t
316
+     (smtp-command sock "HELO ~A" our-name)
317
+     (response-case (sock msg)
318
+       (2 ;; ok
319
+	nil)
320
+       (t (error "hello greeting failed: ~s" msg))))))
321
+
322
+(defun smtp-authenticate (sock server mechs login password)
323
+  (let ((ctx (net.sasl:sasl-client-new "smtp" server
324
+				       :user login
325
+				       :pass password)))
326
+    (multiple-value-bind (res selected-mech response)
327
+	(net.sasl:sasl-client-start ctx mechs)
328
+      (if (not (eq res :continue))
329
+	  (error "sasl-client-start unexpectedly returned: ~s" res))
330
+      (smtp-command sock "AUTH ~a" selected-mech)
331
+      (loop
332
+	(response-case (sock msg)
333
+	  (3  ;; need more interaction
334
+	   (multiple-value-setq (res response)
335
+	     (net.sasl:sasl-step 
336
+	      ctx 
337
+	      (base64-string-to-usb8-array (subseq msg 4))))
338
+	   (smtp-command sock "~a" 
339
+			 (usb8-array-to-base64-string response nil)))
340
+	  (2 ;; server is satisfied.
341
+	   ;; Make sure the auth process really completed
342
+	   (if (not (net.sasl:sasl-conn-auth-complete-p ctx))
343
+	       (error "SMTP server indicated authentication complete  before mechanisms was satisfied"))
344
+	   ;; It's all good.  
345
+	   (return)) ;; break from loop
346
+	  (t
347
+	   (error "SMTP authentication failed")))))
348
+    
349
+    ;; Reach here if authentication completed.
350
+    ;; If a security layer was negotiated, return an encapsulated sock,
351
+    ;; otherwise just return the original sock.
352
+    (if (net.sasl:sasl-conn-security-layer-p ctx)
353
+	(net.sasl:sasl-make-stream ctx sock :close-base t)
354
+      sock)))
355
+  
356
+
295 357
   
296 358
 (defun test-email-address (address)
297 359
   ;; test to see if we can determine if the address is valid
... ...
@@ -314,7 +376,7 @@
314 376
 	 else (setq name (subseq address 0 pos)
315 377
 		    hostname (subseq address (1+ pos)))))
316 378
   
317
-    (let ((sock (ignore-errors (connect-to-mail-server hostname))))
379
+    (let ((sock (ignore-errors (connect-to-mail-server hostname nil nil))))
318 380
       (if* (null sock) then (return-from test-email-address nil))
319 381
     
320 382
       (unwind-protect
... ...
@@ -478,7 +540,8 @@
478 540
        then ipaddr
479 541
        else ; do mx lookup if acldns is being used
480 542
 	    (if* (or (eq socket:*dns-mode* :acldns)
481
-		     (member :acldns socket:*dns-mode* :test #'eq))
543
+		     (and (consp socket:*dns-mode*)
544
+			  (member :acldns socket:*dns-mode* :test #'eq)))
482 545
 	       then (let ((res (socket:dns-query name :type :mx)))
483 546
 		      (if* (and (consp res) (cadr res))
484 547
 			 then (cadr res) ; the ip address