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

Revision 2855, 3.2 kB (checked in by hans, 5 months 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.