Make SEND-MAIL-VIA-SMTP accept a headers argument.

This commit is contained in:
sperber 2002-09-02 09:02:14 +00:00
parent 1b0bce77e6
commit 0fa5f630e3
1 changed files with 31 additions and 60 deletions

View File

@ -34,7 +34,7 @@
;;; [Andreas:] I've inserted a way to do this. It works fine on my ;;; [Andreas:] I've inserted a way to do this. It works fine on my
;;; system. Does it work on your, too? ;;; system. Does it work on your, too?
;;; (send-mail-via-smtp from to-list body [host]) ;;; (send-mail-via-smtp from to-list headers body [host])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Mail message to recipients in list TO-LIST. Message handed off to server ;;; Mail message to recipients in list TO-LIST. Message handed off to server
;;; running on HOST; default is the local host. Returns two values: code and ;;; running on HOST; default is the local host. Returns two values: code and
@ -45,9 +45,9 @@
;;; that is, for each recipient refused by the server, you get the error ;;; that is, for each recipient refused by the server, you get the error
;;; data sent back for that guy. The success check is (< code 400). ;;; data sent back for that guy. The success check is (< code 400).
;;; ;;;
;;; BODY is a string or an input port. ;;; BODY is a list of strings or an input port.
(define (send-mail-via-smtp from to-list body . maybe-host) (define (send-mail-via-smtp from to-list headers body . maybe-host)
(call-with-current-continuation (call-with-current-continuation
(lambda (bailout) (lambda (bailout)
(let ((local (host-info:name (host-info (system-name)))) (let ((local (host-info:name (host-info (system-name))))
@ -71,7 +71,7 @@
;; Send the message body and wrap things up. ;; Send the message body and wrap things up.
(receive (code text) (smtp-transactions socket (receive (code text) (smtp-transactions socket
(smtp/data body)) (smtp/data headers body))
(if (and (< code 400) (null? losers)) (if (and (< code 400) (null? losers))
(values code text) (values code text)
(values 700 losers)))))))))) (values 700 losers))))))))))
@ -196,7 +196,7 @@
;; DATA ;; DATA
(define smtp/data (define smtp/data
(let ((send-DATA-msg (nullary-smtp-command "DATA"))) (let ((send-DATA-msg (nullary-smtp-command "DATA")))
(lambda (message) ; MESSAGE is a string or an input port. (lambda (headers message) ; MESSAGE is a list of strings or an input port.
(lambda (socket) (lambda (socket)
(receive (code text) (send-DATA-msg socket) (receive (code text) (send-DATA-msg socket)
(if (>= code 400) (if (>= code 400)
@ -205,18 +205,26 @@
;; We got a positive acknowledgement for the DATA msg, ;; We got a positive acknowledgement for the DATA msg,
;; now send the message body. ;; now send the message body.
(let ((p (socket:outport socket))) (let ((p (socket:outport socket)))
(cond ((string? message) (for-each (lambda (pair)
(receive (data last-char) (smtp-stuff message #f) (display (car pair) p)
(write-string data p))) (write-char #\: p)
(display (cdr pair) p)
(write-crlf p))
headers)
(write-crlf p)
(cond ((or (null? message) (pair? message))
(for-each (lambda (line)
(write-data-line line p))
message))
((input-port? message) ((input-port? message)
(let lp ((last-char #f)) (let lp ()
(cond ((read-string/partial 1024 message) => (let ((stuff (read-line message)))
(lambda (chunk) (if (not (eof-object? stuff))
(receive (data last-char) (begin
(smtp-stuff chunk last-char) (write-data-line stuff p)
(write-string data p) (newline))))))
(lp last-char)))))))
(else (error "Message must be string or input-port."))) (else (error "Message must be string or input-port.")))
@ -226,6 +234,13 @@
(force-output p) (force-output p)
(handle-smtp-reply socket)))))))) (handle-smtp-reply socket))))))))
(define (write-data-line line port)
(display (if (string=? line ".")
".."
line)
port)
(write-crlf port))
;; SEND FROM: <sender-address> ;; SEND FROM: <sender-address>
(define smtp/send (unary-smtp-command "SEND FROM:")) (define smtp/send (unary-smtp-command "SEND FROM:"))
@ -327,50 +342,6 @@
(string-append "Improperly-formatted smtp reply: " line) (string-append "Improperly-formatted smtp reply: " line)
#f))) #f)))
;;; The message body of a piece of email is terminated by the sequence
;;; <crlf> <period> <crlf>
;;; If the message body contains this magic sequence, it has to be escaped.
;;; We do this by mapping the sequence <lf> <period> to <lf> <period> <period>;
;;; the SMTP receiver undoes this mapping.
;;; S is a string to stuff, PCHAR was the character read just before S
;;; (which matters if it is a line-feed). If S is the first chunk of the entire
;;; msg, then PCHAR can be #f. Return two values: the stuffed string, and the
;;; last char in S (or PCHAR if S is empty). The last-char value returned can
;;; be used as the PCHAR arg for the following call to SMTP-STUFF.
(define (smtp-stuff s pchar)
(let* ((slen (string-length s))
(hits ; Count up all the <lf> <period> seqs in the string.
(let lp ((count 0)
(nl? (eqv? pchar #\newline)) ; Was last char a newline?
(i 0))
(if (< i slen)
(let ((c (string-ref s i)))
(lp (if (and nl? (char=? c #\.)) (+ count 1) count)
(eq? c #\newline)
(+ i 1)))
count))))
(values (if (zero? hits)
s
;; Make a new string, and do the dot-stuffing copy.
(let ((ns (make-string (+ hits slen))))
(let lp ((nl? (eqv? pchar #\newline))
(i 0) ; S index.
(j 0)) ; NS index.
(if (< i slen)
(let ((c (string-ref s i)))
(string-set! ns j c)
(cond ((and nl? (char=? c #\.))
(string-set! ns (+ j 1) #\.)
(lp #f (+ i 1) (+ j 2)))
(else (lp (char=? c #\newline) (+ i 1) (+ j 1)))))))
ns))
(if (zero? slen) pchar (string-ref s (- slen 1)))))) ; LAST-CHAR
;;; Reply codes ;;; Reply codes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This material taken from the RFC. ;;; This material taken from the RFC.