root/trunk/thirdparty/cl-smtp/smtp-output-stream.lisp

Revision 2855, 3.2 KB (checked in by hans, 2 years ago)

update cl-smtp from trunk

Line 
1;; -*- Lisp -*-
2
3;; smtp-output-stream.lisp - Perform protocol and mail specific
4;; processing to convert an email message into an external format
5;; proper to be transfered through SMTP.
6
7;; This stream type is used to do two things:
8
9;; In the message header, convert all non-ASCII characters to their
10;; equivalent Q-encoded representation (RFC2047)
11
12;; In the whole message, convert all line endings to CR+LF as required
13;; by the SMTP protocol.
14
15;; This stream class should also perform automatic dot masking, tbd.
16
17(in-package :cl-smtp)
18
19(defclass smtp-output-stream (trivial-gray-stream-mixin fundamental-character-output-stream)
20  ((encapsulated-stream
21    :initarg :encapsulated-stream
22    :reader encapsulated-stream)
23   (in-header
24    :initform t
25    :accessor in-header
26    :documentation
27    "Currently emitting the header of the message")
28   (line-has-non-ascii
29    :initform nil
30    :accessor line-has-non-ascii
31    :documentation
32    "The current line has non ASCII characters in it")
33   (previous-char
34    :initform nil
35    :accessor previous-char
36    :documentation
37    "Previous character sent to the stream, used to detect end of header")
38   (external-format
39    :initform (flex:make-external-format :iso-8859-15)
40    :initarg :external-format
41    :reader external-format)))
42
43(defmethod stream-element-type ((stream smtp-output-stream))
44  (stream-element-type (stream stream)))
45
46(defmethod close ((stream smtp-output-stream) &key abort)
47  (close (encapsulated-stream stream) :abort abort))
48
49(defmethod stream-write-char ((stream smtp-output-stream) char)
50  (with-accessors ((in-header in-header)
51                   (line-has-non-ascii line-has-non-ascii)
52                   (previous-char previous-char)
53                   (external-format external-format)
54                   (encapsulated-stream encapsulated-stream)) stream
55    (when in-header
56      (cond
57        ;; Newline processing
58        ((eql char #\Newline)
59         ;; Finish quoting
60         (when line-has-non-ascii
61           (format encapsulated-stream "?=")
62           (setf line-has-non-ascii nil))
63         ;; Test for end of header
64         (when (eql previous-char #\Newline)
65           (setf in-header nil)))
66        ((eql char #\Return)
67         ;; CR is suppressed here and added before each #\Newline
68         )
69        ;; Handle non-ASCII characters
70        ((< 127 (char-code char))
71         (unless line-has-non-ascii
72           (format encapsulated-stream "=?~A?Q?" (flex:external-format-name external-format))
73           (setf line-has-non-ascii t))
74         (loop for byte across (flex:string-to-octets (make-string 1 :initial-element char)
75                                                      :external-format external-format)
76            do (format encapsulated-stream "=~2,'0X" byte))))
77      (setf previous-char char))
78      #+nil(when (eql char #\Newline)
79        (write-char #\Return encapsulated-stream))
80      (unless (< 127 (char-code char))
81        (write-char char encapsulated-stream))))
82
83(defmethod stream-write-sequence ((stream smtp-output-stream) sequence start end &key)
84  (if (in-header stream)
85      (loop for i from start below end
86           do (stream-write-char stream (elt sequence i)))
87      (write-sequence sequence (encapsulated-stream stream) :start start :end end)))
Note: See TracBrowser for help on using the browser.