diff --git a/collects/net/doc.txt b/collects/net/doc.txt index a021d10cb8..0c794374eb 100644 --- a/collects/net/doc.txt +++ b/collects/net/doc.txt @@ -497,7 +497,12 @@ EXCEPTIONS ----------------------------------------------------------- PROCEDURES ----------------------------------------------------------- > (smtp-send-message server-string from-string to-list-of-strings header - message-list-of-strings/bytes [port]) -> void + message-list-of-strings/bytes + [#:port-no k] + [#:auth-user user-string-or-#f] + [#:auth-passwd pw-string-or-#f] + [#:tcp-connect proc] + [port-no]) -> void The first argument is the IP address of the SMTP server. The `from-string' argument specifies the mail address of the sender, and @@ -509,11 +514,24 @@ PROCEDURES ----------------------------------------------------------- message, where each string or byte string in the list corresponds to a single line of message text; no string in `message-list-of-strings' should contain a carriage return or - newline characters. The optional `port' argument specifies the IP - port to use in contacting the SMTP server; the default is 25. + newline characters. - See the head package for utilities that construct a message headers - and validate mail address strings. + The optional `port-no' argument --- which can be specified either + with the #:port-no keyword or, for backward compatibility, as an + extra argument after keywords --- specifies the IP port to use in + contacting the SMTP server; the default is 25. + + The optional #:auth-user and #:auth-passwd keyword argument supply a + username and password for authenticated SMTP (using the AUTH PLAIN + protocol). + + The optional #:tcp-connect keyword argument supplies a connection + procedure to be used in place of `tcp-connect'. For example, use + `ssl-connect' from `(lib "mzssl.ss" "openssl")' to connect to the + server via SSL. + + See the "head.ss" library for utilities that construct a message + headers and validate mail address strings. > (smtp-sending-end-of-message [proc]) diff --git a/collects/net/smtp-unit.ss b/collects/net/smtp-unit.ss index 980ac248db..5b34a5d214 100644 --- a/collects/net/smtp-unit.ss +++ b/collects/net/smtp-unit.ss @@ -1,6 +1,8 @@ (module smtp-unit mzscheme - (require (lib "unitsig.ss")) + (require (lib "unitsig.ss") + (lib "kw.ss") + "base64.ss") (require "smtp-sig.ss") @@ -60,7 +62,8 @@ (raise-type-error 'smtp-sending-end-of-message "thunk" f)) f))) - (define (smtp-send-message* r w sender recipients header message-lines) + (define (smtp-send-message* r w sender recipients header message-lines + auth-user auth-passwd) (with-handlers ([void (lambda (x) (close-input-port r) (close-output-port w) @@ -70,6 +73,15 @@ (fprintf w "EHLO ~a~a" ID crlf) (check-reply r 250 w) + (when auth-user + (log "auth~n") + (fprintf w "AUTH PLAIN ~a" + ;; Encoding adds CRLF + (base64-encode + (string->bytes/latin-1 + (format "~a\0~a\0~a" auth-user auth-user auth-passwd)))) + (check-reply r 235 w)) + (log "from~n") (fprintf w "MAIL FROM:<~a>~a" sender crlf) (check-reply r 250 w) @@ -107,13 +119,18 @@ (close-input-port r))) (define smtp-send-message - (case-lambda - [(server sender recipients header message-lines) - (smtp-send-message server sender recipients header message-lines 25)] - [(server sender recipients header message-lines pos) + (lambda/kw (server sender recipients header message-lines + #:key + [port-no 25] + [auth-user #f] + [auth-passwd #f] + [tcp-connect tcp-connect] + #:body + (#:optional [opt-port-no port-no])) (when (null? recipients) (error 'send-smtp-message "no receivers")) (let-values ([(r w) (if debug-via-stdio? (values (current-input-port) (current-output-port)) - (tcp-connect server pos))]) - (smtp-send-message* r w sender recipients header message-lines))]))))) \ No newline at end of file + (tcp-connect server opt-port-no))]) + (smtp-send-message* r w sender recipients header message-lines + auth-user auth-passwd))))))) diff --git a/collects/sirmail/doc.txt b/collects/sirmail/doc.txt index 8e7482cf28..e455799c93 100644 --- a/collects/sirmail/doc.txt +++ b/collects/sirmail/doc.txt @@ -54,11 +54,11 @@ Sending panel: - Mail From: The user's email address. - - SMTP Server: The SMTP server's host name (outgoing mail). Use a - ":" suffix on the host name to connect to port - . Supply multiple SMTP hosts by separating the addresses - with a comma; the "File" menu of mail-sending frame will let you - choose a specific server. + - SMTP Server: The SMTP server's host name (outgoing mail). General + syntax: [:][@][:] where is "tcp" + (the default) or "ssl". Supply multiple SMTP hosts by separating + the addresses with a comma; the "File" menu of mail-sending frame + will let you choose a specific server. - Default To Domain: If a destination address that isn't declared as an alias doesn't include a domain name, SirMail automatically diff --git a/collects/sirmail/optionr.ss b/collects/sirmail/optionr.ss index 15c77d10a0..b253b74504 100644 --- a/collects/sirmail/optionr.ss +++ b/collects/sirmail/optionr.ss @@ -29,6 +29,19 @@ (values (cadr m) (string->number (caddr m))) (values s default-port)))) + (define (parse-server-name+user+type s default-port) + (let ([m (regexp-match #rx"^(ssl|tcp):.*:.*" s)]) + (let-values ([(ssl? s) (if m + (values (string=? "ssl" (substring s 0 3)) + (substring s 4)) + (values #f s))]) + (let ([m (regexp-match #rx"^(.*)@(.*)$" s)]) + (let-values ([(user s) (if m + (values (cadr m) (caddr m)) + (values #f s))]) + (let-values ([(server port) (parse-server-name s default-port)]) + (values ssl? user server port))))))) + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Preferences ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/collects/sirmail/pref.ss b/collects/sirmail/pref.ss index df7c3bae51..ce746b09c9 100644 --- a/collects/sirmail/pref.ss +++ b/collects/sirmail/pref.ss @@ -316,9 +316,21 @@ (<= 1 (string->number (caddr m)) 65535) (is-host-address? (cadr m)))))) - (define (is-host-address+port-list? s) + (define (is-host-address+port+user? s) + (or (is-host-address+port? s) + (let ([m (regexp-match "^(?:[-+a-zA-Z0-9_.]+)@(.*)$" s)]) + (and m + (is-host-address+port? (cadr m)))))) + + (define (is-host-address+port+user+type? s) + (or (is-host-address+port+user? s) + (let ([m (regexp-match "^(?:ssl|tcp):(.*)$" s)]) + (and m + (is-host-address+port+user? (cadr m)))))) + + (define (is-host-address+port+user+type-list? s) (let ([l (regexp-split ", *" s)]) - (andmap is-host-address+port? l))) + (andmap is-host-address+port+user+type? l))) (define (check-address ok? who tl s port-ok? multi?) (or (ok? s) @@ -352,8 +364,8 @@ (check-address is-host-address? who tl s #f #f)) (define (check-host-address/port who tl s) (check-address is-host-address+port? who tl s #t #f)) - (define (check-host-address/port/multi who tl s) - (check-address is-host-address+port-list? who tl s #t #t)) + (define (check-host-address/port/user/type/multi who tl s) + (check-address is-host-address+port+user+type-list? who tl s #t #t)) ;; check-biff-delay : (union #f string) (union #f parent) string -> boolean ;; checks to see if the string in the biff delay field makes @@ -472,7 +484,8 @@ (let ([p (instantiate vertical-panel% (parent))]) (make-text-field "Mail From" p 20 'sirmail:mail-from #f check-user-address (lambda (x) x) (lambda (x) x)) - (make-text-field "SMTP Server" p 20 'sirmail:smtp-server #f check-host-address/port/multi (lambda (x) x) (lambda (x) x)) + (make-text-field "SMTP Server" p 20 'sirmail:smtp-server #f check-host-address/port/user/type/multi + (lambda (x) x) (lambda (x) x)) (make-file/directory-button #t #f p 'sirmail:sent-directory diff --git a/collects/sirmail/readr.ss b/collects/sirmail/readr.ss index 365e60082a..d17ac419e3 100644 --- a/collects/sirmail/readr.ss +++ b/collects/sirmail/readr.ss @@ -255,12 +255,8 @@ ;; New connection (begin (let ([pw (or (get-PASSWORD) - (let ([p (get-text-from-user "Password" - (format "Password for ~a:" (USERNAME)) - main-frame - "" - '(password))]) - (unless p (raise-user-error 'connect "connection cancelled")) + (let ([p (get-pw-from-user (USERNAME) main-frame)]) + (unless p (raise-user-error 'connect "connection canceled")) p))]) (let*-values ([(imap count new) (let-values ([(server port-no) (parse-server-name (IMAP-SERVER) diff --git a/collects/sirmail/sendr.ss b/collects/sirmail/sendr.ss index 490b99f01a..d03a70bd15 100644 --- a/collects/sirmail/sendr.ss +++ b/collects/sirmail/sendr.ss @@ -11,7 +11,8 @@ (require (lib "list.ss") (lib "file.ss") (lib "string.ss") - (lib "process.ss")) + (lib "process.ss") + (lib "mzssl.ss" "openssl")) (require "sirmails.ss" "pref.ss" @@ -25,6 +26,8 @@ (require (lib "hierlist-sig.ss" "hierlist")) + (define smtp-passwords (make-hash-table 'equal)) + (provide send@) (define send@ (unit/sig sirmail:send^ @@ -410,12 +413,20 @@ w)) (define (send-msg) - (define-values (smtp-server-to-use smtp-port-to-use) - (parse-server-name (SMTP-SERVER) 25)) + (define-values (smtp-ssl? smtp-auth-user smtp-server-to-use smtp-port-to-use) + (parse-server-name+user+type (SMTP-SERVER) 25)) + (define smtp-auth-passwd (and smtp-auth-user + (or (hash-table-get smtp-passwords (cons smtp-auth-user smtp-server-to-use) + (lambda () #f)) + (let ([p (get-pw-from-user smtp-auth-user mailer-frame)]) + (unless p (raise-user-error 'send "send canceled")) + p)))) (send-message (send message-editor get-text) + smtp-ssl? smtp-server-to-use smtp-port-to-use + smtp-auth-user smtp-auth-passwd (map (lambda (i) (send i user-data)) (send enclosure-list get-items)) enable @@ -433,7 +444,10 @@ (if f (send message-editor save-file f 'text) (loop)))))) - message-count)) + message-count) + (when smtp-auth-passwd + (hash-table-put! smtp-passwords (cons smtp-auth-user smtp-server-to-use) + smtp-auth-passwd))) ;; enq-msg : -> void ;; enqueues a message for a later send @@ -592,7 +606,7 @@ (new-mailer p "" "" "" "" "" (map (lambda (i) (send i user-data)) (send enclosure-list get-items)) - 0) + message-count) (semaphore-post s))) (semaphore-wait s)))] [shortcut #\=]) @@ -711,8 +725,9 @@ ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (send-message message-str + ssl? smtp-server - smtp-port + smtp-port auth-user auth-pw enclosures enable status-message-starting @@ -793,7 +808,10 @@ tos new-header body-lines - smtp-port)))) + #:port-no smtp-port + #:tcp-connect (if ssl? ssl-connect tcp-connect) + #:auth-user auth-user + #:auth-passwd auth-pw)))) save-before-killing)) (status-done)) (message-box diff --git a/collects/sirmail/sirmails.ss b/collects/sirmail/sirmails.ss index 81d8b596ea..ef78a58fd0 100644 --- a/collects/sirmail/sirmails.ss +++ b/collects/sirmail/sirmails.ss @@ -37,6 +37,8 @@ confirm-box + get-pw-from-user + generalize-encoding parse-encoded encode-for-header)) @@ -76,7 +78,8 @@ USE-EXTERNAL-COMPOSER? - parse-server-name)) + parse-server-name + parse-server-name+user+type)) (provide sirmail:read^) (define-signature sirmail:read^ diff --git a/collects/sirmail/utilr.ss b/collects/sirmail/utilr.ss index fc1f5c900f..531d9c9cbf 100644 --- a/collects/sirmail/utilr.ss +++ b/collects/sirmail/utilr.ss @@ -230,6 +230,15 @@ 'yes 'no))) + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (define (get-pw-from-user username parent) + (get-text-from-user "Password" + (format "Password for ~a:" username) + parent + "" + '(password))) + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Decoding `from' names ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;