Browse code
See ChangeLog
dancy authored on 01/06/2007 16:24:40
Showing 3 changed files
Showing 3 changed files
... | ... |
@@ -1,3 +1,14 @@ |
1 |
+2007-06-01 Ahmon Dancy <dancy@dancy> |
|
2 |
+ |
|
3 |
+ * mime-transfer-encoding.cl: New quoted-printable decoding utility |
|
4 |
+ functions. |
|
5 |
+ |
|
6 |
+ * mime-parse.cl: Improved handling of large or noncompliant |
|
7 |
+ headers. Improved mbox processing support. |
|
8 |
+ |
|
9 |
+ * mime-api.cl: New exported function: decode-header-text. |
|
10 |
+ |
|
11 |
+ |
|
1 | 12 |
2007-05-29 Kevin Layer <layer@gemini.franz.com> |
2 | 13 |
|
3 | 14 |
* mime-parse.cl: fix from dancy for parse-mime-structure |
4 | 15 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,198 @@ |
1 |
+Prelimanary documenation. |
|
2 |
+ |
|
3 |
+Class: mime-part-parsed |
|
4 |
+Subclass of: mime-part |
|
5 |
+ |
|
6 |
+Slot accessors: |
|
7 |
+ |
|
8 |
+ * mime-part-type |
|
9 |
+ <update comment about mime-part-parsed objects> |
|
10 |
+ * mime-part-subtype |
|
11 |
+ <update comment about mime-part-parsed objects> |
|
12 |
+ * mime-part-parameters |
|
13 |
+ * mime-part-id |
|
14 |
+ <update comment about mime-part-parsed objects> |
|
15 |
+ * mime-part-description |
|
16 |
+ <update comment about mime-part-parsed objects> |
|
17 |
+ * mime-part-encoding |
|
18 |
+ <update comment about mime-part-parsed objects> |
|
19 |
+ * mime-part-headers |
|
20 |
+ * mime-part-parts |
|
21 |
+ * mime-part-boundary |
|
22 |
+ <update comment about mime-part-parsed objects> |
|
23 |
+ |
|
24 |
+ * mime-part-headers-size |
|
25 |
+ This is the size, in bytes, of the header portion of the part |
|
26 |
+ (including the blank line terminator) |
|
27 |
+ |
|
28 |
+ * mime-part-body-size |
|
29 |
+ This is the size, in bytes, of the body of the part. |
|
30 |
+ |
|
31 |
+ * mime-part-lines |
|
32 |
+ For non-multipart types, this is the number of lines that make |
|
33 |
+ up the part body. |
|
34 |
+ |
|
35 |
+ * mime-part-position |
|
36 |
+ This is the file positon of the start of the part headers, |
|
37 |
+ relative to the beginning of the topmost part. |
|
38 |
+ |
|
39 |
+ * mime-part-body-position |
|
40 |
+ This is the file position of the start of the part body, |
|
41 |
+ relative to the beginning of the topmost part. |
|
42 |
+ |
|
43 |
+ * mime-part-message |
|
44 |
+ For message/rfc822 parts, this slot contains the |
|
45 |
+ mime-part-parsed object which represents the encapsulated message. |
|
46 |
+ |
|
47 |
+Function: parse-mime-structure |
|
48 |
+ |
|
49 |
+Arguments: stream &key mbox |
|
50 |
+ |
|
51 |
+Return values: |
|
52 |
+The function returns two values: |
|
53 |
+ |
|
54 |
+ 1) A mime-part-parsed object which represents the topmost MIME part |
|
55 |
+ (which may possibly contain subparts). If there was no message to |
|
56 |
+ parse (such as if the stream is at EOF), then nil is returned. |
|
57 |
+ |
|
58 |
+ 2) The number of bytes that were read. |
|
59 |
+ |
|
60 |
+'stream' should be a stream that is positioned at the first header of |
|
61 |
+a MIME-compliant email message. |
|
62 |
+ |
|
63 |
+If 'mbox' is true, then parsing will terminate at EOF or when a line |
|
64 |
+which begins with "From " is read. |
|
65 |
+ |
|
66 |
+MIME messages always have a topmost part and may possibly have |
|
67 |
+multiple subparts which may recursively have their own subparts. This |
|
68 |
+function reads 'stream' and creates a mime-part-parsed object which |
|
69 |
+contains information about these parts and subparts. For each part, |
|
70 |
+the following information is collected: |
|
71 |
+ |
|
72 |
+* The major content type, (e.g, "text", if the Content-Type header |
|
73 |
+ is "text/html"). |
|
74 |
+* The content subtype, (e.g., "html", if the Content-Type header is |
|
75 |
+ "text/html"). |
|
76 |
+* Any parameter information that was supplied in the Content-Type |
|
77 |
+ header. |
|
78 |
+* The part id, as determined by the Content-Id header, if there was |
|
79 |
+ one. |
|
80 |
+* The part description, as determined by the Content-Description |
|
81 |
+ header, if there was one. |
|
82 |
+* The part encoding, as determined by the Content-Transfer-Encoding |
|
83 |
+ header. |
|
84 |
+* The boundary string, for multipart types. |
|
85 |
+* The list of subparts (which are also mime-part-parsed objects) for |
|
86 |
+ multipart parts. |
|
87 |
+* The encapsulated message (which is a mime-part-parsed object) for |
|
88 |
+ message/rfc822 parts. |
|
89 |
+* The size of the part headers (in bytes). |
|
90 |
+* The size of the part body (in bytes). |
|
91 |
+* The number of lines comprising the part body. |
|
92 |
+* The file position of the beginning of the part headers, relative to the |
|
93 |
+ position of the topmost part. For the topmost part, this value will |
|
94 |
+ always be zero. |
|
95 |
+* The file position of the beginning of the part body, relative to the |
|
96 |
+ position of the topmost part. |
|
97 |
+ |
|
98 |
+See also: <the documentation page which lists the slots of the |
|
99 |
+mime-part-parsed class>. |
|
100 |
+ |
|
101 |
+ |
|
102 |
+Function: map-over-parts |
|
103 |
+Arguments: part function |
|
104 |
+ |
|
105 |
+'part' must be a mime-part. |
|
106 |
+ |
|
107 |
+'function' must be a function (or symbol naming a function) which |
|
108 |
+takes a single argument, a mime-part. |
|
109 |
+ |
|
110 |
+map-over-parts calls 'function' on 'part', then, if part contains |
|
111 |
+subparts (or an encapsulated message in the case of a message/rfc822 |
|
112 |
+part), map-over-parts is called recursively for each subpart (or |
|
113 |
+encapsulated message). |
|
114 |
+ |
|
115 |
+Function: qp-encode-stream |
|
116 |
+Arguments: instream outstream &key wrap-at |
|
117 |
+ |
|
118 |
+This function reads bytes from instream and writes them in |
|
119 |
+quoted-printable format to outstream. Lines in the output are wrapped |
|
120 |
+approximately every 'wrap-at' output characters (which defaults to |
|
121 |
+72). Wrapping may be late by up to 3 characters under some |
|
122 |
+circumstances (e.g., when using the default 'wrap-at' value of 72, |
|
123 |
+some lines may be 75 characters long). |
|
124 |
+ |
|
125 |
+'instream' is read until EOF is seen. |
|
126 |
+ |
|
127 |
+'instream' must be a stream capable of being read in an octet-oriented |
|
128 |
+manner. In particular, it cannot be a string stream. |
|
129 |
+ |
|
130 |
+See also: qp-decode-stream, base64-encode-stream, base64-decode-stream |
|
131 |
+ |
|
132 |
+ |
|
133 |
+ |
|
134 |
+Function: qp-decode-stream |
|
135 |
+Arguments: instream outstream |
|
136 |
+ |
|
137 |
+This function reads quoted-printable encoded text from 'instream' and |
|
138 |
+writes the decoded text to 'outstream'. Reading continues until |
|
139 |
+end-of-file is seen on 'instream'. |
|
140 |
+ |
|
141 |
+See also: qp-encode-stream, base64-encode-stream, base64-decoed-stream |
|
142 |
+ |
|
143 |
+ |
|
144 |
+Macro: with-part-stream |
|
145 |
+Arguments: (sym part instream &key (header t)) &body body |
|
146 |
+ |
|
147 |
+with-part-stream evaluates 'body' with 'sym' bound to an input stream |
|
148 |
+which, when read, supplies bytes from 'instream'. 'instream' must be |
|
149 |
+positioned either at the beginning of the part headers (if keyword |
|
150 |
+argument 'header' is true) or at the beginning of the part body (if |
|
151 |
+keyword argument 'header' is false). The 'part' argument is used by |
|
152 |
+the macro to determine how many bytes from 'instream' will be used. |
|
153 |
+ |
|
154 |
+The primary purpose of this macro is to create a stream that will |
|
155 |
+generate an end-of-file indicator when the contents of a part have |
|
156 |
+been completely read. Such a stream is useful for passing to |
|
157 |
+functions which expect to read a stream until EOF (such as |
|
158 |
+'decode-quoted-printable' or 'excl:base64-decode-stream'). |
|
159 |
+ |
|
160 |
+See also: with-decoded-part-body-stream |
|
161 |
+ |
|
162 |
+Macro: with-decoded-part-body-stream |
|
163 |
+Arguments: (sym part instream) &body body |
|
164 |
+ |
|
165 |
+with-decoded-part-body-stream evaluates 'body' with 'sym' bound to an |
|
166 |
+input stream which, when read, supplies decoded bytes. The encoded |
|
167 |
+bytes are read from 'instream', which should be an input stream whose |
|
168 |
+file position is at the beginning of the part body. The 'part' |
|
169 |
+argument is used by the macro to determine the size of the part body |
|
170 |
+and also to determine the content transfer encoding of the part. The |
|
171 |
+input stream bound to 'sym' will signal end-of-file when the part body |
|
172 |
+has been exhausted. |
|
173 |
+ |
|
174 |
+Example: |
|
175 |
+ |
|
176 |
+(use-package :net.post-office) |
|
177 |
+ |
|
178 |
+(defun extract-all-jpegs (filename) |
|
179 |
+ (with-open-file (f filename) |
|
180 |
+ (let ((toppart (parse-mime-structure f)) |
|
181 |
+ (count 0)) |
|
182 |
+ |
|
183 |
+ (flet ((extract-jpeg (part) |
|
184 |
+ (if* (and (equalp (mime-part-type part) "image") |
|
185 |
+ (equalp (mime-part-subtype part) "jpeg")) |
|
186 |
+ then (incf count) |
|
187 |
+ (let ((filename (format nil "image~d.jpg" count))) |
|
188 |
+ (format t "Saving ~a...~%" filename) |
|
189 |
+ (with-open-file (out filename :direction :output) |
|
190 |
+ ;; Position source file pointer to the beginning |
|
191 |
+ ;; of the part body. |
|
192 |
+ (file-position f (mime-part-body-position part)) |
|
193 |
+ (with-decoded-part-body-stream (bod part f) |
|
194 |
+ (sys:copy-file bod out))))))) |
|
195 |
+ |
|
196 |
+ (map-over-parts toppart #'extract-jpeg))))) |
|
197 |
+ |
|
198 |
+See also: mime-part-body-position slot accessor. |
... | ... |
@@ -14,7 +14,7 @@ |
14 | 14 |
;; merchantability or fitness for a particular purpose. See the GNU |
15 | 15 |
;; Lesser General Public License for more details. |
16 | 16 |
;; |
17 |
-;; $Id: mime-transfer-encoding.cl,v 1.10 2007/05/31 23:13:08 dancy Exp $ |
|
17 |
+;; $Id: mime-transfer-encoding.cl,v 1.11 2007/06/01 16:24:40 dancy Exp $ |
|
18 | 18 |
|
19 | 19 |
(defpackage :net.post-office |
20 | 20 |
(:use #:lisp #:excl) |
... | ... |
@@ -116,6 +116,7 @@ |
116 | 116 |
|
117 | 117 |
|
118 | 118 |
;; Used by qp-decode-stream |
119 |
+(eval-when (compile) |
|
119 | 120 |
(defconstant *qp-digit-values* |
120 | 121 |
#.(let ((arr (make-array 257 :element-type 'fixnum))) |
121 | 122 |
(dotimes (n 256) |
... | ... |
@@ -126,7 +127,7 @@ |
126 | 127 |
then (- n (- (char-code #\A) 10)) |
127 | 128 |
else -1))) |
128 | 129 |
(setf (aref arr 256) -2) |
129 |
- arr)) |
|
130 |
+ arr))) |
|
130 | 131 |
|
131 | 132 |
(defun qp-decode-stream (instream outstream &key count) |
132 | 133 |
(declare (optimize (speed 3))) |