git.fiddlerwoaroof.com
Browse code

See ChangeLog

dancy authored on 01/06/2007 16:24:40
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)))