1 |
edhill |
1.1 |
/* |
2 |
|
|
* Decode MIME parts. |
3 |
|
|
*/ |
4 |
|
|
/* (C) Copyright 1993,1994 by Carnegie Mellon University |
5 |
|
|
* All Rights Reserved. |
6 |
|
|
* |
7 |
|
|
* Permission to use, copy, modify, distribute, and sell this software |
8 |
|
|
* and its documentation for any purpose is hereby granted without |
9 |
|
|
* fee, provided that the above copyright notice appear in all copies |
10 |
|
|
* and that both that copyright notice and this permission notice |
11 |
|
|
* appear in supporting documentation, and that the name of Carnegie |
12 |
|
|
* Mellon University not be used in advertising or publicity |
13 |
|
|
* pertaining to distribution of the software without specific, |
14 |
|
|
* written prior permission. Carnegie Mellon University makes no |
15 |
|
|
* representations about the suitability of this software for any |
16 |
|
|
* purpose. It is provided "as is" without express or implied |
17 |
|
|
* warranty. |
18 |
|
|
* |
19 |
|
|
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
20 |
|
|
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
21 |
|
|
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
22 |
|
|
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
23 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
24 |
|
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
25 |
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
26 |
|
|
* SOFTWARE. */ |
27 |
|
|
|
28 |
|
|
#include <stdio.h> |
29 |
jmc |
1.4 |
#include <stdlib.h> |
30 |
edhill |
1.1 |
#include <string.h> |
31 |
|
|
#include <ctype.h> |
32 |
jmc |
1.4 |
#include <unistd.h> |
33 |
edhill |
1.1 |
#include "xmalloc.h" |
34 |
|
|
#include "common.h" |
35 |
|
|
#include "part.h" |
36 |
|
|
#include "md5.h" |
37 |
|
|
|
38 |
|
|
extern char *os_idtodir(char *id); |
39 |
|
|
extern FILE *os_newtypedfile(char *fname, char *contentType, int flags, params contentParams); |
40 |
|
|
extern FILE *os_createnewfile(char *fname); |
41 |
|
|
extern char *md5contextTo64(MD5_CTX *context); |
42 |
jmc |
1.4 |
extern void warn(char *s); |
43 |
|
|
extern void os_perror(char *str); |
44 |
|
|
extern void chat(char *s); |
45 |
|
|
extern void os_donewithdir(char *dir); |
46 |
|
|
extern void os_warnMD5mismatch(void); |
47 |
|
|
extern void os_closetypedfile(FILE *outfile); |
48 |
|
|
|
49 |
|
|
extern int part_depth(struct part *part); |
50 |
|
|
extern void part_ungets(char *s, struct part *part); |
51 |
|
|
extern void part_close(struct part *part); |
52 |
|
|
extern int part_fill(struct part *part); |
53 |
|
|
extern void part_addboundary(struct part *part, char *boundary); |
54 |
|
|
extern int part_readboundary(struct part *part); |
55 |
edhill |
1.1 |
|
56 |
|
|
/* The possible content transfer encodings */ |
57 |
|
|
enum encoding { enc_none, enc_qp, enc_base64 }; |
58 |
|
|
|
59 |
|
|
char *ParseHeaders(struct part *inpart, char **subjectp, char **contentTypep, enum encoding *contentEncodingp, char **contentDispositionp, char **contentMD5p); |
60 |
|
|
enum encoding parseEncoding(char *s); |
61 |
|
|
params ParseContent(char **headerp); |
62 |
|
|
char *getParam(params cParams, char *key); |
63 |
|
|
char *getDispositionFilename(char *disposition); |
64 |
|
|
void from64(struct part *inpart, FILE *outfile, char **digestp, int suppressCR); |
65 |
|
|
void fromqp(struct part *inpart, FILE *outfile, char **digestp); |
66 |
|
|
void fromnone(struct part *inpart, FILE *outfile, char **digestp); |
67 |
jmc |
1.4 |
int handlePartial(struct part *inpart, char *headers, params contentParams, |
68 |
|
|
int extractText); |
69 |
|
|
int ignoreMessage(struct part *inpart); |
70 |
|
|
int handleMultipart(struct part *inpart, char *contentType, |
71 |
|
|
params contentParams, int extractText); |
72 |
|
|
int handleUuencode(struct part *inpart, char *subject, int extractText); |
73 |
|
|
int handleText(struct part *inpart, enum encoding contentEncoding); |
74 |
|
|
int saveToFile(struct part *inpart, int inAppleDouble, char *contentType, |
75 |
|
|
params contentParams, enum encoding contentEncoding, |
76 |
|
|
char *contentDisposition, char *contentMD5); |
77 |
|
|
|
78 |
edhill |
1.1 |
/* |
79 |
|
|
* Read and handle an RFC 822 message from the body-part 'inpart'. |
80 |
|
|
*/ |
81 |
|
|
int handleMessage(struct part *inpart, char *defaultContentType, int inAppleDouble, int extractText) |
82 |
|
|
{ |
83 |
|
|
char *headers, *subject, *contentType, *contentDisposition, *contentMD5; |
84 |
|
|
enum encoding contentEncoding; |
85 |
|
|
params contentParams; |
86 |
|
|
|
87 |
|
|
/* Parse the headers, getting the ones we're interested in */ |
88 |
|
|
headers = ParseHeaders(inpart, &subject, &contentType, &contentEncoding, |
89 |
|
|
&contentDisposition, &contentMD5); |
90 |
|
|
if (!headers) return 1; |
91 |
|
|
|
92 |
|
|
/* If no content type, or a non-MIME content type, use the default */ |
93 |
|
|
if (!contentType || !strchr(contentType, '/')) { |
94 |
|
|
contentType = defaultContentType; |
95 |
|
|
} |
96 |
|
|
contentParams = ParseContent(&contentType); |
97 |
|
|
|
98 |
|
|
if (!strcasecmp(contentType, "message/rfc822")) { |
99 |
|
|
if (contentEncoding != enc_none) { |
100 |
|
|
warn("ignoring invalid content encoding on message/rfc822"); |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
/* Simple recursion */ |
104 |
|
|
return handleMessage(inpart, "text/plain", 0, extractText); |
105 |
|
|
} |
106 |
|
|
else if (!strcasecmp(contentType, "message/partial")) { |
107 |
|
|
if (contentEncoding != enc_none) { |
108 |
|
|
warn("ignoring invalid content encoding on message/partial"); |
109 |
|
|
} |
110 |
|
|
return handlePartial(inpart, headers, contentParams, extractText); |
111 |
|
|
} |
112 |
|
|
else if (!strncasecmp(contentType, "message/", 8)) { |
113 |
|
|
/* Probably message/external. We don't care--toss it */ |
114 |
|
|
return ignoreMessage(inpart); |
115 |
|
|
} |
116 |
|
|
else if (!strncasecmp(contentType, "multipart/", 10)) { |
117 |
|
|
if (contentEncoding != enc_none) { |
118 |
|
|
warn("ignoring invalid content encoding on multipart"); |
119 |
|
|
} |
120 |
|
|
return handleMultipart(inpart, contentType, contentParams, |
121 |
|
|
extractText); |
122 |
|
|
} |
123 |
|
|
else if (part_depth(inpart) == 0 && |
124 |
|
|
!strncasecmp(contentType, "text/", 5) && |
125 |
|
|
contentEncoding == enc_none && |
126 |
|
|
!getDispositionFilename(contentDisposition) && |
127 |
|
|
!getParam(contentParams, "name")) { |
128 |
|
|
/* top-level text message, handle as possible uuencoded file */ |
129 |
|
|
return handleUuencode(inpart, subject, extractText); |
130 |
|
|
} |
131 |
|
|
else if (!extractText && !inAppleDouble && |
132 |
|
|
!strncasecmp(contentType, "text/", 5) && |
133 |
|
|
!getDispositionFilename(contentDisposition) && |
134 |
|
|
!getParam(contentParams, "name")) { |
135 |
|
|
return handleText(inpart, contentEncoding); |
136 |
|
|
} |
137 |
|
|
else { |
138 |
|
|
/* Some sort of attachment, extract it */ |
139 |
|
|
return saveToFile(inpart, inAppleDouble, contentType, contentParams, |
140 |
|
|
contentEncoding, contentDisposition, contentMD5); |
141 |
|
|
} |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
/* |
145 |
|
|
* Skip whitespace and RFC-822 comments. |
146 |
|
|
*/ |
147 |
|
|
void SkipWhitespace(char **s) |
148 |
|
|
{ |
149 |
|
|
char *p = *s; |
150 |
|
|
int commentlevel = 0; |
151 |
|
|
|
152 |
|
|
while (*p && (isspace(*p) || *p == '(')) { |
153 |
|
|
if (*p == '\n') { |
154 |
|
|
p++; |
155 |
|
|
if (*p != ' ' && *p != '\t') { |
156 |
|
|
*s = 0; |
157 |
|
|
return; |
158 |
|
|
} |
159 |
|
|
} |
160 |
|
|
else if (*p == '(') { |
161 |
|
|
p++; |
162 |
|
|
commentlevel++; |
163 |
|
|
while (commentlevel) { |
164 |
|
|
switch (*p) { |
165 |
|
|
case '\n': |
166 |
|
|
p++; |
167 |
|
|
if (*p == ' ' || *p == '\t') break; |
168 |
|
|
/* FALL THROUGH */ |
169 |
|
|
case '\0': |
170 |
|
|
*s = 0; |
171 |
|
|
return; |
172 |
|
|
|
173 |
|
|
case '\\': |
174 |
|
|
p++; |
175 |
|
|
break; |
176 |
|
|
|
177 |
|
|
case '(': |
178 |
|
|
commentlevel++; |
179 |
|
|
break; |
180 |
|
|
|
181 |
|
|
case ')': |
182 |
|
|
commentlevel--; |
183 |
|
|
break; |
184 |
|
|
} |
185 |
|
|
p++; |
186 |
|
|
} |
187 |
|
|
} |
188 |
|
|
else p++; |
189 |
|
|
} |
190 |
|
|
if (*p == 0) { |
191 |
|
|
*s = 0; |
192 |
|
|
} |
193 |
|
|
else { |
194 |
|
|
*s = p; |
195 |
|
|
} |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
/* |
199 |
|
|
* Read and parse the headers of an RFC 822 message, returning them in |
200 |
|
|
* a pointer to a static buffer. The headers are read from 'inpart'. |
201 |
|
|
* A pointer to the value of any Subject:, Content-Type:, |
202 |
|
|
* Content-Disposition:, or Content-MD5: header is stored in the space |
203 |
|
|
* pointed to by 'subjectp', 'contentTypep', contentDispositionp, and |
204 |
|
|
* contentMD5p, respectively. The Content-Transfer-Encoding is stored |
205 |
|
|
* in the enum pointed to by 'contentEncodingp'. |
206 |
|
|
*/ |
207 |
|
|
#define HEADGROWSIZE 1000 |
208 |
|
|
char *ParseHeaders(struct part *inpart, char **subjectp, char **contentTypep, enum encoding *contentEncodingp, char **contentDispositionp, char **contentMD5p) |
209 |
|
|
{ |
210 |
|
|
static int alloced = 0; |
211 |
|
|
static char *headers; |
212 |
|
|
int left, len, i; |
213 |
|
|
char *next, *val; |
214 |
|
|
|
215 |
|
|
/* Read headers into buffer pointed to by "headers" */ |
216 |
|
|
if (!alloced) { |
217 |
|
|
headers = xmalloc(alloced = HEADGROWSIZE); |
218 |
|
|
} |
219 |
|
|
next = headers; |
220 |
|
|
*next++ = '\n'; /* Leading newline to make matching header names easier */ |
221 |
|
|
left = alloced - 2; /* Allow room for terminating null */ |
222 |
|
|
|
223 |
|
|
while (part_gets(next, left, inpart) && (*next != '\n' || next[-1] != '\n')) { |
224 |
|
|
len = strlen(next); |
225 |
|
|
|
226 |
|
|
if (next[-1] == '\n') { |
227 |
|
|
/* Check for valid header-ness of "next" */ |
228 |
|
|
for (i = 0; i < len; i++) { |
229 |
|
|
if (next[i] == ':' || |
230 |
|
|
next[i] <= ' ' || next[i] >= '\177') break; |
231 |
|
|
} |
232 |
|
|
if (i == 0 || next[i] != ':') { |
233 |
|
|
/* Check for header continuation line */ |
234 |
|
|
if (next == headers+1 || (next[0] != ' ' && next[0] != '\t')) { |
235 |
|
|
/* |
236 |
|
|
* Not a valid header, push back on input stream |
237 |
|
|
* and stop reading input. |
238 |
|
|
*/ |
239 |
|
|
part_ungets(next, inpart); |
240 |
|
|
break; |
241 |
|
|
} |
242 |
|
|
} |
243 |
|
|
} |
244 |
|
|
|
245 |
|
|
left -= len; |
246 |
|
|
next += len; |
247 |
|
|
|
248 |
|
|
if (left < 100) { |
249 |
|
|
len = next - headers; |
250 |
|
|
alloced += HEADGROWSIZE; |
251 |
|
|
left += HEADGROWSIZE; |
252 |
|
|
headers = xrealloc(headers, alloced); |
253 |
|
|
next = headers + len; |
254 |
|
|
} |
255 |
|
|
} |
256 |
|
|
|
257 |
|
|
*next = '\0'; |
258 |
|
|
|
259 |
|
|
/* Look for the headers we find particularly interesting */ |
260 |
|
|
*subjectp = *contentTypep = *contentDispositionp = *contentMD5p = 0; |
261 |
|
|
*contentEncodingp = enc_none; |
262 |
|
|
for (next = headers; *next; next++) { |
263 |
|
|
if (*next == '\n') { |
264 |
|
|
switch(next[1]) { |
265 |
|
|
case 's': |
266 |
|
|
case 'S': |
267 |
|
|
if (!strncasecmp(next+2, "ubject:", 7)) { |
268 |
|
|
val = next+9; |
269 |
|
|
SkipWhitespace(&val); |
270 |
|
|
if (val) *subjectp = val; |
271 |
|
|
} |
272 |
|
|
break; |
273 |
|
|
|
274 |
|
|
case 'c': |
275 |
|
|
case 'C': |
276 |
|
|
if (!strncasecmp(next+2, "ontent-type:", 12)) { |
277 |
|
|
val = next+14; |
278 |
|
|
SkipWhitespace(&val); |
279 |
|
|
if (val) *contentTypep = val; |
280 |
|
|
} |
281 |
|
|
else if (!strncasecmp(next+2, "ontent-transfer-encoding:", 25)) { |
282 |
|
|
*contentEncodingp = parseEncoding(next+27); |
283 |
|
|
} |
284 |
|
|
else if (!strncasecmp(next+2, "ontent-disposition:", 19)) { |
285 |
|
|
val = next+21; |
286 |
|
|
SkipWhitespace(&val); |
287 |
|
|
if (val) *contentDispositionp = val; |
288 |
|
|
} |
289 |
|
|
else if (!strncasecmp(next+2, "ontent-md5:", 11)) { |
290 |
|
|
val = next+13; |
291 |
|
|
SkipWhitespace(&val); |
292 |
|
|
if (val) *contentMD5p = val; |
293 |
|
|
} |
294 |
|
|
} |
295 |
|
|
} |
296 |
|
|
} |
297 |
|
|
return headers; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
/* |
301 |
|
|
* Parse the Content-Transfer-Encoding: value pointed to by 's'. |
302 |
|
|
* Returns the appropriate encoding enum. |
303 |
|
|
*/ |
304 |
|
|
enum encoding parseEncoding(char *s) |
305 |
|
|
{ |
306 |
|
|
SkipWhitespace(&s); |
307 |
|
|
if (s) { |
308 |
|
|
switch (*s) { |
309 |
|
|
case 'q': |
310 |
|
|
case 'Q': |
311 |
|
|
if (!strncasecmp(s+1, "uoted-printable", 15) && |
312 |
|
|
(isspace(s[16]) || s[16] == '(')) { |
313 |
|
|
return enc_qp; |
314 |
|
|
} |
315 |
|
|
break; |
316 |
|
|
|
317 |
|
|
case '7': |
318 |
|
|
case '8': |
319 |
|
|
if (!strncasecmp(s+1, "bit", 3) && |
320 |
|
|
(isspace(s[4]) || s[4] == '(')) { |
321 |
|
|
return enc_none; |
322 |
|
|
} |
323 |
|
|
break; |
324 |
|
|
|
325 |
|
|
case 'b': |
326 |
|
|
case 'B': |
327 |
|
|
if (!strncasecmp(s+1, "ase64", 5) && |
328 |
|
|
(isspace(s[6]) || s[6] == '(')) { |
329 |
|
|
return enc_base64; |
330 |
|
|
} |
331 |
|
|
if (!strncasecmp(s+1, "inary", 5) && |
332 |
|
|
(isspace(s[6]) || s[6] == '(')) { |
333 |
|
|
return enc_none; |
334 |
|
|
} |
335 |
|
|
} |
336 |
|
|
warn("ignoring unknown content transfer encoding\n"); |
337 |
|
|
} |
338 |
|
|
return enc_none; |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
/* |
342 |
|
|
* Parse the value of a Content-Type: header. |
343 |
|
|
* 'headerp' points to a pointer to the input string. |
344 |
|
|
* The pointer pointed to by 'headerp' is changed to point to |
345 |
|
|
* a static buffer containing the content type stripped of whitespace |
346 |
|
|
* and parameters. The parameters are converted to a type suitable for |
347 |
|
|
* getParm() and returned. |
348 |
|
|
*/ |
349 |
|
|
#define PARAMGROWSIZE 10 |
350 |
|
|
params ParseContent(char **headerp) |
351 |
|
|
{ |
352 |
|
|
char *header; |
353 |
|
|
static int palloced = 0; |
354 |
|
|
static char **param; |
355 |
|
|
static int calloced = 0; |
356 |
|
|
static char *cbuf; |
357 |
|
|
char *p; |
358 |
|
|
int nparam; |
359 |
|
|
|
360 |
|
|
p = header = *headerp; |
361 |
|
|
|
362 |
|
|
/* Find end of header, including continuation lines */ |
363 |
|
|
do { |
364 |
|
|
p = strchr(p+1, '\n'); |
365 |
|
|
} while (p && isspace(p[1])); |
366 |
|
|
if (!p) { |
367 |
|
|
p = header + strlen(header); |
368 |
|
|
} |
369 |
|
|
|
370 |
|
|
/* If necessary, allocate/grow cbuf to hold header. */ |
371 |
|
|
if (p - header >= calloced) { |
372 |
|
|
calloced = p - header + 1; |
373 |
|
|
if (calloced < 200) calloced = 200; |
374 |
|
|
cbuf = xrealloc(cbuf, calloced); |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
/* Copy header to cbuf */ |
378 |
|
|
strncpy(cbuf, header, p - header); |
379 |
|
|
cbuf[p - header] = 0; |
380 |
|
|
header = *headerp = cbuf; |
381 |
|
|
|
382 |
|
|
nparam = 0; |
383 |
|
|
|
384 |
|
|
/* Strip whitespace from content type */ |
385 |
|
|
/* ParseHeaders() stripped leading whitespace */ |
386 |
|
|
p = header; |
387 |
|
|
while (header && *header && *header != ';') { |
388 |
|
|
while (*header && !isspace(*header) && *header != '(' && |
389 |
|
|
*header != ';') { |
390 |
|
|
*p++ = *header++; |
391 |
|
|
} |
392 |
|
|
SkipWhitespace(&header); |
393 |
|
|
} |
394 |
|
|
if (!header || !*header) return 0; |
395 |
|
|
header++; |
396 |
|
|
*p = '\0'; |
397 |
|
|
|
398 |
|
|
/* Parse the parameters */ |
399 |
|
|
while (*header) { |
400 |
|
|
SkipWhitespace(&header); |
401 |
|
|
if (!header) break; |
402 |
|
|
|
403 |
|
|
if (nparam+1 >= palloced) { |
404 |
|
|
palloced += PARAMGROWSIZE; |
405 |
|
|
param = (char **) xrealloc((char *)param, palloced * sizeof(char *)); |
406 |
|
|
} |
407 |
|
|
param[nparam++] = header; |
408 |
|
|
|
409 |
|
|
/* Find any separating semicolon. Pay attention to quoted-strings */ |
410 |
|
|
while (*header && *header != ';') { |
411 |
|
|
if (*header == '\"') { |
412 |
|
|
++header; |
413 |
|
|
while (*header && *header != '\"') { |
414 |
|
|
if (*header == '\\') { |
415 |
|
|
++header; |
416 |
|
|
if (!*header) break; |
417 |
|
|
} |
418 |
|
|
++header; |
419 |
|
|
} |
420 |
|
|
if (!*header) break; |
421 |
|
|
} |
422 |
|
|
else if (*header == '(') { |
423 |
|
|
/* Convert comments to spaces */ |
424 |
|
|
p = header; |
425 |
|
|
SkipWhitespace(&p); |
426 |
|
|
if (!p) { |
427 |
|
|
break; |
428 |
|
|
} |
429 |
|
|
while (header < p) *header++ = ' '; |
430 |
|
|
header--; |
431 |
|
|
} |
432 |
|
|
header++; |
433 |
|
|
} |
434 |
|
|
if (*header) *header++ = '\0'; |
435 |
|
|
} |
436 |
|
|
|
437 |
|
|
if (nparam == 0) |
438 |
|
|
return 0; |
439 |
|
|
|
440 |
|
|
param[nparam] = 0; |
441 |
|
|
return param; |
442 |
|
|
} |
443 |
|
|
|
444 |
|
|
/* |
445 |
|
|
* Get the value of the parameter named 'key' from the content-type |
446 |
|
|
* parameters 'cParams'. Returns a pointer to a static bufer which |
447 |
|
|
* contains the value, or null if no such parameter was found. |
448 |
|
|
*/ |
449 |
|
|
#define VALUEGROWSIZE 100 |
450 |
|
|
char *getParam(params cParams, char *key) |
451 |
|
|
{ |
452 |
|
|
static char *value; |
453 |
|
|
static int alloced = 0; |
454 |
|
|
int left; |
455 |
|
|
int keylen = strlen(key); |
456 |
|
|
char *from, *to; |
457 |
|
|
|
458 |
|
|
if (!cParams) return 0; |
459 |
|
|
|
460 |
|
|
if (!alloced) { |
461 |
|
|
value = xmalloc(alloced = VALUEGROWSIZE); |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
/* Find the named parameter */ |
465 |
|
|
while (*cParams) { |
466 |
|
|
if (!strncasecmp(key, *cParams, keylen) && |
467 |
|
|
((*cParams)[keylen] == '=' || isspace((*cParams)[keylen]))) break; |
468 |
|
|
cParams++; |
469 |
|
|
} |
470 |
|
|
if (!*cParams) return 0; |
471 |
|
|
|
472 |
|
|
/* Skip over the "=" and any surrounding whitespace */ |
473 |
|
|
from = *cParams + keylen; |
474 |
|
|
while (*from && isspace(*from)) from++; |
475 |
|
|
if (*from++ != '=') return 0; |
476 |
|
|
while (*from && isspace(*from)) from++; |
477 |
|
|
if (!*from) return 0; |
478 |
|
|
|
479 |
|
|
/* Copy value into buffer */ |
480 |
|
|
to = value; |
481 |
|
|
left = alloced - 1; |
482 |
|
|
if (*from == '\"') { |
483 |
|
|
/* Quoted-string */ |
484 |
|
|
from++; |
485 |
|
|
while (*from && *from != '\"') { |
486 |
|
|
if (!--left) { |
487 |
|
|
alloced += VALUEGROWSIZE; |
488 |
|
|
left += VALUEGROWSIZE; |
489 |
|
|
value = xrealloc(value, alloced); |
490 |
|
|
to = value + alloced - left - 2; |
491 |
|
|
} |
492 |
|
|
if (*from == '\\') { |
493 |
|
|
from++; |
494 |
|
|
if (!*from) return 0; |
495 |
|
|
} |
496 |
|
|
*to++ = *from++; |
497 |
|
|
} |
498 |
|
|
if (!*from) return 0; |
499 |
|
|
} |
500 |
|
|
else { |
501 |
|
|
/* Just a token */ |
502 |
|
|
while (*from && !isspace(*from)) { |
503 |
|
|
if (!--left) { |
504 |
|
|
alloced += VALUEGROWSIZE; |
505 |
|
|
left += VALUEGROWSIZE; |
506 |
|
|
value = xrealloc(value, alloced); |
507 |
|
|
to = value + alloced - left - 2; |
508 |
|
|
} |
509 |
|
|
*to++ = *from++; |
510 |
|
|
} |
511 |
|
|
} |
512 |
|
|
*to = '\0'; |
513 |
|
|
return value; |
514 |
|
|
} |
515 |
|
|
|
516 |
|
|
/* |
517 |
|
|
* Get the value of the "filename" parameter in a Content-Disposition: |
518 |
|
|
* header. Returns a pointer to a static buffer containing the value, or |
519 |
|
|
* a null pointer if there was no such parameter. |
520 |
|
|
*/ |
521 |
|
|
char * |
522 |
|
|
getDispositionFilename(char *disposition) |
523 |
|
|
{ |
524 |
|
|
static char *value; |
525 |
|
|
static int alloced = 0; |
526 |
|
|
int left; |
527 |
|
|
char *to; |
528 |
|
|
|
529 |
|
|
if (!disposition) return 0; |
530 |
|
|
|
531 |
|
|
/* Skip until we find ";" "filename" "=" tokens. */ |
532 |
|
|
for (;;) { |
533 |
|
|
/* Skip until we find ";" */ |
534 |
|
|
while (*disposition != ';') { |
535 |
|
|
if (!*disposition) return 0; |
536 |
|
|
else if (*disposition == '\"') { |
537 |
|
|
++disposition; |
538 |
|
|
while (*disposition && *disposition != '\"') { |
539 |
|
|
if (*disposition == '\\') { |
540 |
|
|
++disposition; |
541 |
|
|
if (!*disposition) return 0; |
542 |
|
|
} |
543 |
|
|
++disposition; |
544 |
|
|
} |
545 |
|
|
if (!*disposition) return 0; |
546 |
|
|
} |
547 |
|
|
else if (*disposition == '(') { |
548 |
|
|
SkipWhitespace(&disposition); |
549 |
|
|
if (!disposition) return 0; |
550 |
|
|
disposition--; |
551 |
|
|
} |
552 |
|
|
disposition++; |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
/* Skip over ";" and trailing whitespace */ |
556 |
|
|
disposition++; |
557 |
|
|
SkipWhitespace(&disposition); |
558 |
|
|
if (!disposition) return 0; |
559 |
|
|
|
560 |
|
|
/* |
561 |
|
|
* If we're not looking at a "filename" token, go back |
562 |
|
|
* and look for another ";". Otherwise skip it and |
563 |
|
|
* trailing whitespace. |
564 |
|
|
*/ |
565 |
|
|
if (strncasecmp(disposition, "filename", 8) != 0) continue; |
566 |
|
|
disposition += 8; |
567 |
|
|
if (!isspace(*disposition) && *disposition != '=' && |
568 |
|
|
*disposition != '(') { |
569 |
|
|
continue; |
570 |
|
|
} |
571 |
|
|
SkipWhitespace(&disposition); |
572 |
|
|
if (!disposition) return 0; |
573 |
|
|
|
574 |
jmc |
1.4 |
/* If we're looking at a "=", we found what we're looking for */ |
575 |
|
|
if (*disposition++ == '=') break; |
576 |
edhill |
1.1 |
} |
577 |
|
|
|
578 |
|
|
SkipWhitespace(&disposition); |
579 |
|
|
if (!disposition) return 0; |
580 |
|
|
|
581 |
|
|
if (!alloced) { |
582 |
|
|
value = xmalloc(alloced = VALUEGROWSIZE); |
583 |
|
|
} |
584 |
|
|
|
585 |
|
|
/* Copy value into buffer */ |
586 |
|
|
to = value; |
587 |
|
|
left = alloced - 1; |
588 |
|
|
if (*disposition == '\"') { |
589 |
|
|
/* Quoted-string */ |
590 |
|
|
disposition++; |
591 |
|
|
while (*disposition && *disposition != '\"') { |
592 |
|
|
if (!--left) { |
593 |
|
|
alloced += VALUEGROWSIZE; |
594 |
|
|
left += VALUEGROWSIZE; |
595 |
|
|
value = xrealloc(value, alloced); |
596 |
|
|
to = value + alloced - left - 2; |
597 |
|
|
} |
598 |
|
|
if (*disposition == '\\') { |
599 |
|
|
disposition++; |
600 |
|
|
if (!*disposition) return 0; |
601 |
|
|
} |
602 |
|
|
*to++ = *disposition++; |
603 |
|
|
} |
604 |
|
|
if (!*disposition) return 0; |
605 |
|
|
} |
606 |
|
|
else { |
607 |
|
|
/* Just a token */ |
608 |
|
|
while (*disposition && !isspace(*disposition) && |
609 |
|
|
*disposition != '(') { |
610 |
|
|
if (!--left) { |
611 |
|
|
alloced += VALUEGROWSIZE; |
612 |
|
|
left += VALUEGROWSIZE; |
613 |
|
|
value = xrealloc(value, alloced); |
614 |
|
|
to = value + alloced - left - 2; |
615 |
|
|
} |
616 |
|
|
*to++ = *disposition++; |
617 |
|
|
} |
618 |
|
|
} |
619 |
|
|
*to = '\0'; |
620 |
|
|
return value; |
621 |
|
|
} |
622 |
|
|
|
623 |
|
|
/* |
624 |
|
|
* Read and handle a message/partial object from the file 'inpart'. |
625 |
|
|
*/ |
626 |
|
|
int handlePartial(struct part *inpart, char *headers, params contentParams, int extractText) |
627 |
|
|
{ |
628 |
|
|
char *id, *dir, *p; |
629 |
|
|
int thispart; |
630 |
|
|
int nparts = 0; |
631 |
|
|
char buf[1024]; |
632 |
|
|
FILE *partfile, *outfile; |
633 |
|
|
struct part *outpart; |
634 |
|
|
int i, docopy; |
635 |
|
|
|
636 |
|
|
id = getParam(contentParams, "id"); |
637 |
|
|
if (!id) { |
638 |
|
|
warn("partial message has no id parameter"); |
639 |
|
|
goto ignore; |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
/* Get directory to store the parts being reassembled */ |
643 |
|
|
dir = os_idtodir(id); |
644 |
|
|
if (!dir) goto ignore; |
645 |
|
|
|
646 |
|
|
p = getParam(contentParams, "number"); |
647 |
|
|
if (!p) { |
648 |
|
|
warn("partial message doesn't have number parameter"); |
649 |
|
|
goto ignore; |
650 |
|
|
} |
651 |
|
|
thispart = atoi(p); |
652 |
|
|
|
653 |
jmc |
1.4 |
if ((p = getParam(contentParams, "total"))) { |
654 |
edhill |
1.1 |
nparts = atoi(p); |
655 |
|
|
if (nparts <= 0) { |
656 |
|
|
warn("partial message has invalid number of parts"); |
657 |
|
|
goto ignore; |
658 |
|
|
} |
659 |
|
|
/* Store number of parts in reassembly directory */ |
660 |
|
|
sprintf(buf, "%sCT", dir); |
661 |
|
|
partfile = os_createnewfile(buf); |
662 |
|
|
if (!partfile) { |
663 |
|
|
os_perror(buf); |
664 |
|
|
goto ignore; |
665 |
|
|
} |
666 |
|
|
fprintf(partfile, "%d\n", nparts); |
667 |
|
|
fclose(partfile); |
668 |
|
|
} |
669 |
|
|
else { |
670 |
|
|
/* Try to retrieve number of parts from reassembly directory */ |
671 |
|
|
sprintf(buf, "%sCT", dir); |
672 |
jmc |
1.4 |
if ((partfile = fopen(buf, "r"))) { |
673 |
edhill |
1.1 |
if (fgets(buf, sizeof(buf), partfile)) { |
674 |
|
|
nparts = atoi(buf); |
675 |
|
|
if (nparts < 0) nparts = 0; |
676 |
|
|
} |
677 |
|
|
fclose(partfile); |
678 |
|
|
} |
679 |
|
|
} |
680 |
|
|
|
681 |
|
|
/* Sanity check */ |
682 |
|
|
if (thispart <= 0 || (nparts && thispart > nparts)) { |
683 |
|
|
warn("partial message has invalid number"); |
684 |
|
|
goto ignore; |
685 |
|
|
} |
686 |
|
|
|
687 |
|
|
sprintf(buf, "Saving part %d ", thispart); |
688 |
|
|
if (nparts) sprintf(buf+strlen(buf), "of %d ", nparts); |
689 |
|
|
strcat(buf, getParam(contentParams, "id")); |
690 |
|
|
chat(buf); |
691 |
|
|
|
692 |
|
|
/* Create file to store this part */ |
693 |
|
|
sprintf(buf, "%s%d", dir, thispart); |
694 |
|
|
partfile = os_createnewfile(buf); |
695 |
|
|
if (!partfile) { |
696 |
|
|
os_perror(buf); |
697 |
|
|
goto ignore; |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
/* Do special-case header handling for first part */ |
701 |
|
|
if (thispart == 1) { |
702 |
|
|
int skippedfirstbyte = 0; |
703 |
|
|
|
704 |
|
|
while (*headers) { |
705 |
|
|
if (*headers == '\n' && |
706 |
|
|
(!strncasecmp(headers, "\ncontent-", 9) || |
707 |
|
|
!strncasecmp(headers, "\nmessage-id:", 12))) { |
708 |
|
|
/* Special case, skip header */ |
709 |
|
|
headers++; |
710 |
|
|
while (*headers && (*headers != '\n' || isspace(headers[1]))) { |
711 |
|
|
headers++; |
712 |
|
|
} |
713 |
|
|
} |
714 |
|
|
else { |
715 |
|
|
/* First byte of headers is extra newline, don't write it to file */ |
716 |
|
|
if (skippedfirstbyte++) putc(*headers, partfile); |
717 |
|
|
headers++; |
718 |
|
|
} |
719 |
|
|
} |
720 |
|
|
docopy = 0; |
721 |
|
|
/* Handle headers in the multipart/partial body */ |
722 |
|
|
while (part_gets(buf, sizeof(buf), inpart)) { |
723 |
|
|
if (*buf == '\n') { |
724 |
|
|
putc('\n', partfile); |
725 |
|
|
break; |
726 |
|
|
} |
727 |
|
|
if (!strncasecmp(buf, "content-", 8) || !strncasecmp(buf, "message-id:", 11)) { |
728 |
|
|
docopy = 1; |
729 |
|
|
} |
730 |
|
|
else if (!isspace(*buf)) { |
731 |
|
|
docopy = 0; |
732 |
|
|
} |
733 |
|
|
|
734 |
|
|
if (docopy) fputs(buf, partfile); |
735 |
|
|
while(buf[strlen(buf)-1] != '\n' && part_gets(buf, sizeof(buf), inpart)) { |
736 |
|
|
if (docopy) fputs(buf, partfile); |
737 |
|
|
} |
738 |
|
|
} |
739 |
|
|
} |
740 |
|
|
|
741 |
|
|
/* Copy the contents to the file */ |
742 |
|
|
while (part_gets(buf, sizeof(buf), inpart)) { |
743 |
|
|
fputs(buf, partfile); |
744 |
|
|
} |
745 |
|
|
fclose(partfile); |
746 |
|
|
|
747 |
|
|
/* Check to see if we have all parts. Start from the highest numbers |
748 |
|
|
* as we are more likely not to have them. |
749 |
|
|
*/ |
750 |
|
|
for (i = nparts; i; i--) { |
751 |
|
|
sprintf(buf, "%s%d", dir, i); |
752 |
|
|
partfile = fopen(buf, "r"); |
753 |
|
|
if (partfile) { |
754 |
|
|
fclose(partfile); |
755 |
|
|
} |
756 |
|
|
else { |
757 |
|
|
break; |
758 |
|
|
} |
759 |
|
|
} |
760 |
|
|
|
761 |
|
|
if (i || !nparts) { |
762 |
|
|
/* We don't have all the parts yet */ |
763 |
|
|
return 0; |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
/* We have everything, concatenate all the parts into a single file */ |
767 |
|
|
sprintf(buf, "%sFULL", dir); |
768 |
|
|
outfile = os_createnewfile(buf); |
769 |
|
|
if (!outfile) { |
770 |
|
|
os_perror(buf); |
771 |
|
|
return 1; |
772 |
|
|
} |
773 |
|
|
for (i=1; i<=nparts; i++) { |
774 |
|
|
sprintf(buf, "%s%d", dir, i); |
775 |
|
|
partfile = fopen(buf, "r"); |
776 |
|
|
if (!partfile) { |
777 |
|
|
os_perror(buf); |
778 |
|
|
return 1; |
779 |
|
|
} |
780 |
|
|
while (fgets(buf, sizeof(buf), partfile)) { |
781 |
|
|
fputs(buf, outfile); |
782 |
|
|
} |
783 |
|
|
fclose(partfile); |
784 |
|
|
|
785 |
|
|
/* Done with message part file, delete it */ |
786 |
|
|
sprintf(buf, "%s%d", dir, i); |
787 |
|
|
remove(buf); |
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
/* Open the concatenated file for reading and handle it */ |
791 |
|
|
fclose(outfile); |
792 |
|
|
sprintf(buf, "%sFULL", dir); |
793 |
|
|
outfile = fopen(buf, "r"); |
794 |
|
|
if (!outfile) { |
795 |
|
|
os_perror(buf); |
796 |
|
|
return 1; |
797 |
|
|
} |
798 |
|
|
outpart = part_init(outfile); |
799 |
|
|
handleMessage(outpart, "text/plain", 0, extractText); |
800 |
|
|
part_close(outpart); |
801 |
|
|
|
802 |
|
|
/* Clean up the rest of the reassembly directory */ |
803 |
|
|
sprintf(buf, "%sFULL", dir); |
804 |
|
|
remove(buf); |
805 |
|
|
sprintf(buf, "%sCT", dir); |
806 |
|
|
remove(buf); |
807 |
|
|
os_donewithdir(dir); |
808 |
|
|
|
809 |
|
|
return 0; |
810 |
|
|
|
811 |
|
|
ignore: |
812 |
|
|
ignoreMessage(inpart); |
813 |
|
|
return 1; |
814 |
|
|
} |
815 |
|
|
|
816 |
|
|
/* |
817 |
|
|
* Skip over a message object from the file 'inpart'. |
818 |
|
|
*/ |
819 |
|
|
int ignoreMessage(struct part *inpart) |
820 |
|
|
{ |
821 |
|
|
while (part_getc(inpart) != EOF); |
822 |
|
|
return 0; |
823 |
|
|
} |
824 |
|
|
|
825 |
|
|
/* |
826 |
|
|
* Read and handle a multipart object from 'inpart'. |
827 |
|
|
*/ |
828 |
|
|
int handleMultipart(struct part *inpart, char *contentType, params contentParams, int extractText) |
829 |
|
|
{ |
830 |
|
|
char *id; |
831 |
|
|
char *defaultContentType = "text/plain"; |
832 |
|
|
int isAppleDouble = 0; |
833 |
|
|
|
834 |
|
|
/* Components of multipart/digest have a different default content-type */ |
835 |
|
|
if (!strcasecmp(contentType, "multipart/digest")) { |
836 |
|
|
defaultContentType = "message/rfc822"; |
837 |
|
|
} |
838 |
|
|
if (!strcasecmp(contentType, "multipart/appledouble")) { |
839 |
|
|
isAppleDouble++; |
840 |
|
|
} |
841 |
|
|
|
842 |
|
|
if (!(id = getParam(contentParams, "boundary"))) { |
843 |
|
|
warn("multipart message has no boundary parameter"); |
844 |
|
|
id=""; |
845 |
|
|
} |
846 |
|
|
|
847 |
|
|
/* Add the new boundary id */ |
848 |
|
|
part_addboundary(inpart, id); |
849 |
|
|
|
850 |
|
|
#ifdef __riscos |
851 |
|
|
/* |
852 |
|
|
* "Marcel" encodes RISCOS directory structure in the multipart |
853 |
|
|
* structure. That is the Wrong Way to do it, but we hold our |
854 |
|
|
* nose and pass the information to the OS layer. |
855 |
|
|
*/ |
856 |
|
|
os_boundaryhookopen(part_depth(inpart)); |
857 |
|
|
#endif |
858 |
|
|
|
859 |
|
|
/* |
860 |
|
|
* Skip over preamble. |
861 |
|
|
* HACK: The initial boundary doesn't have to start with a newline, |
862 |
|
|
* so we deal with this by stuffing an initial newline into the input |
863 |
|
|
* stream |
864 |
|
|
*/ |
865 |
|
|
part_ungetc('\n', inpart); |
866 |
|
|
ignoreMessage(inpart); |
867 |
|
|
|
868 |
|
|
/* Handle the component messages */ |
869 |
|
|
while (!part_readboundary(inpart)) { |
870 |
|
|
handleMessage(inpart, defaultContentType, isAppleDouble, extractText); |
871 |
|
|
} |
872 |
|
|
|
873 |
|
|
#ifdef __riscos |
874 |
|
|
os_boundaryhookclose(part_depth(inpart)); |
875 |
|
|
#endif |
876 |
|
|
|
877 |
|
|
/* Skip over postamble */ |
878 |
|
|
ignoreMessage(inpart); |
879 |
|
|
|
880 |
|
|
/* Remove any lingering unused description file */ |
881 |
|
|
(void) remove(TEMPFILENAME); |
882 |
|
|
|
883 |
|
|
return 0; |
884 |
|
|
} |
885 |
|
|
|
886 |
|
|
/* |
887 |
|
|
* Handle a text message object from 'inpart' by saving it to |
888 |
|
|
* the temporary description file. |
889 |
|
|
*/ |
890 |
|
|
int handleText(struct part *inpart, enum encoding contentEncoding) |
891 |
|
|
{ |
892 |
|
|
FILE *descfile; |
893 |
|
|
|
894 |
|
|
descfile = os_createnewfile(TEMPFILENAME); |
895 |
|
|
if (!descfile) { |
896 |
|
|
os_perror(TEMPFILENAME); |
897 |
|
|
ignoreMessage(inpart); |
898 |
|
|
return 1; |
899 |
|
|
} |
900 |
|
|
|
901 |
|
|
/* Write the file, handling the appropriate encoding */ |
902 |
|
|
switch (contentEncoding) { |
903 |
|
|
case enc_none: |
904 |
|
|
fromnone(inpart, descfile, (char **)0); |
905 |
|
|
break; |
906 |
|
|
|
907 |
|
|
case enc_qp: |
908 |
|
|
fromqp(inpart, descfile, (char **)0); |
909 |
|
|
break; |
910 |
|
|
|
911 |
|
|
case enc_base64: |
912 |
|
|
from64(inpart, descfile, (char **)0, 1); |
913 |
|
|
break; |
914 |
|
|
} |
915 |
|
|
|
916 |
|
|
fclose(descfile); |
917 |
|
|
return 0; |
918 |
|
|
} |
919 |
|
|
|
920 |
|
|
/* |
921 |
|
|
* Read a message object from 'inpart' and save it to a file. |
922 |
|
|
*/ |
923 |
|
|
int saveToFile(struct part *inpart, int inAppleDouble, char *contentType, params contentParams, enum encoding contentEncoding, char *contentDisposition, char *contentMD5) |
924 |
|
|
{ |
925 |
|
|
FILE *outfile = 0; |
926 |
|
|
int flags = 0; |
927 |
|
|
int suppressCR = 0; |
928 |
|
|
char *outputmd5; |
929 |
|
|
char *fname; |
930 |
|
|
|
931 |
|
|
if (!strncasecmp(contentType, "text/", 5)) { |
932 |
|
|
suppressCR = 1; |
933 |
|
|
} |
934 |
|
|
else if (contentEncoding == enc_base64) { |
935 |
|
|
/* |
936 |
|
|
* HEURISTIC: It is not in general possible to determine whether |
937 |
|
|
* any non-text content type is line-oriented. We guess |
938 |
|
|
* the "binary" status of a part from the composer's choice |
939 |
|
|
* of content transfer encoding. |
940 |
|
|
* |
941 |
|
|
* If the content transfer encoding is "binary" and the input is |
942 |
|
|
* not line-oriented, we're screwed anyway--the input file has |
943 |
|
|
* been opened in text mode. So the "binary output file" heuristic |
944 |
|
|
* is not applied in this case. |
945 |
|
|
*/ |
946 |
|
|
flags |= FILE_BINARY; |
947 |
|
|
} |
948 |
|
|
|
949 |
|
|
if (inAppleDouble) flags |= FILE_INAPPLEDOUBLE; |
950 |
|
|
|
951 |
|
|
/* Find an appropriate filename and create the output file */ |
952 |
|
|
fname = getDispositionFilename(contentDisposition); |
953 |
|
|
if (!fname) fname = getParam(contentParams, "name"); |
954 |
|
|
if (fname) fname = strsave(fname); |
955 |
|
|
outfile = os_newtypedfile(fname, contentType, flags, contentParams); |
956 |
|
|
if (fname) free(fname); |
957 |
|
|
if (!outfile) { |
958 |
|
|
ignoreMessage(inpart); |
959 |
|
|
return 1; |
960 |
|
|
} |
961 |
|
|
|
962 |
|
|
/* Write the file, handling the appropriate encoding */ |
963 |
|
|
switch (contentEncoding) { |
964 |
|
|
case enc_none: |
965 |
|
|
fromnone(inpart, outfile, &outputmd5); |
966 |
|
|
break; |
967 |
|
|
|
968 |
|
|
case enc_qp: |
969 |
|
|
fromqp(inpart, outfile, &outputmd5); |
970 |
|
|
break; |
971 |
|
|
|
972 |
|
|
case enc_base64: |
973 |
|
|
from64(inpart, outfile, &outputmd5, suppressCR); |
974 |
|
|
break; |
975 |
|
|
} |
976 |
|
|
rewind(outfile); |
977 |
|
|
|
978 |
|
|
/* Check the MD5 digest if it was supplied */ |
979 |
|
|
if (contentMD5) { |
980 |
|
|
if (strncmp(outputmd5, contentMD5, strlen(outputmd5)) != 0) { |
981 |
|
|
os_warnMD5mismatch(); |
982 |
|
|
} |
983 |
|
|
} |
984 |
|
|
free(outputmd5); |
985 |
|
|
|
986 |
|
|
os_closetypedfile(outfile); |
987 |
|
|
return 0; |
988 |
|
|
} |
989 |
|
|
|
990 |
|
|
#define XX 127 |
991 |
|
|
/* |
992 |
|
|
* Table for decoding hexadecimal in quoted-printable |
993 |
|
|
*/ |
994 |
|
|
static char index_hex[256] = { |
995 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
996 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
997 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
998 |
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX, |
999 |
|
|
XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1000 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1001 |
|
|
XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1002 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1003 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1004 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1005 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1006 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1007 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1008 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1009 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1010 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1011 |
|
|
}; |
1012 |
|
|
#define HEXCHAR(c) (index_hex[(unsigned char)(c)]) |
1013 |
|
|
|
1014 |
|
|
/* |
1015 |
|
|
* Table for decoding base64 |
1016 |
|
|
*/ |
1017 |
|
|
static char index_64[256] = { |
1018 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1019 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1020 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, |
1021 |
|
|
52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, |
1022 |
|
|
XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, |
1023 |
|
|
15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, |
1024 |
|
|
XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, |
1025 |
|
|
41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, |
1026 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1027 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1028 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1029 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1030 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1031 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1032 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1033 |
|
|
XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, |
1034 |
|
|
}; |
1035 |
|
|
#define CHAR64(c) (index_64[(unsigned char)(c)]) |
1036 |
|
|
|
1037 |
|
|
void from64(struct part *inpart, FILE *outfile, char **digestp, int suppressCR) |
1038 |
|
|
{ |
1039 |
|
|
int c1, c2, c3, c4; |
1040 |
|
|
int DataDone = 0; |
1041 |
|
|
char buf[3]; |
1042 |
|
|
MD5_CTX context; |
1043 |
|
|
|
1044 |
|
|
if (digestp) MD5Init(&context); |
1045 |
|
|
while ((c1 = part_getc(inpart)) != EOF) { |
1046 |
|
|
if (c1 != '=' && CHAR64(c1) == XX) { |
1047 |
|
|
continue; |
1048 |
|
|
} |
1049 |
|
|
if (DataDone) continue; |
1050 |
|
|
do { |
1051 |
|
|
c2 = part_getc(inpart); |
1052 |
|
|
} while (c2 != EOF && c2 != '=' && CHAR64(c2) == XX); |
1053 |
|
|
do { |
1054 |
|
|
c3 = part_getc(inpart); |
1055 |
|
|
} while (c3 != EOF && c3 != '=' && CHAR64(c3) == XX); |
1056 |
|
|
do { |
1057 |
|
|
c4 = part_getc(inpart); |
1058 |
|
|
} while (c4 != EOF && c4 != '=' && CHAR64(c4) == XX); |
1059 |
|
|
if (c2 == EOF || c3 == EOF || c4 == EOF) { |
1060 |
|
|
warn("Premature EOF"); |
1061 |
|
|
break; |
1062 |
|
|
} |
1063 |
|
|
if (c1 == '=' || c2 == '=') { |
1064 |
|
|
DataDone=1; |
1065 |
|
|
continue; |
1066 |
|
|
} |
1067 |
|
|
c1 = CHAR64(c1); |
1068 |
|
|
c2 = CHAR64(c2); |
1069 |
|
|
buf[0] = ((c1<<2) | ((c2&0x30)>>4)); |
1070 |
|
|
if (!suppressCR || buf[0] != '\r') putc(buf[0], outfile); |
1071 |
|
|
if (c3 == '=') { |
1072 |
|
|
if (digestp) MD5Update(&context, buf, 1); |
1073 |
|
|
DataDone = 1; |
1074 |
|
|
} else { |
1075 |
|
|
c3 = CHAR64(c3); |
1076 |
|
|
buf[1] = (((c2&0x0F) << 4) | ((c3&0x3C) >> 2)); |
1077 |
|
|
if (!suppressCR || buf[1] != '\r') putc(buf[1], outfile); |
1078 |
|
|
if (c4 == '=') { |
1079 |
|
|
if (digestp) MD5Update(&context, buf, 2); |
1080 |
|
|
DataDone = 1; |
1081 |
|
|
} else { |
1082 |
|
|
c4 = CHAR64(c4); |
1083 |
|
|
buf[2] = (((c3&0x03) << 6) | c4); |
1084 |
|
|
if (!suppressCR || buf[2] != '\r') putc(buf[2], outfile); |
1085 |
|
|
if (digestp) MD5Update(&context, buf, 3); |
1086 |
|
|
} |
1087 |
|
|
} |
1088 |
|
|
} |
1089 |
|
|
if (digestp) *digestp = md5contextTo64(&context); |
1090 |
|
|
} |
1091 |
|
|
|
1092 |
|
|
void fromqp(struct part *inpart, FILE *outfile, char **digestp) |
1093 |
|
|
{ |
1094 |
|
|
int c1, c2; |
1095 |
|
|
MD5_CTX context; |
1096 |
|
|
char c; |
1097 |
|
|
|
1098 |
|
|
if (digestp) MD5Init(&context); |
1099 |
|
|
|
1100 |
|
|
while ((c1 = part_getc(inpart)) != EOF) { |
1101 |
|
|
if (c1 == '=') { |
1102 |
|
|
c1 = part_getc(inpart); |
1103 |
|
|
if (c1 != '\n') { |
1104 |
|
|
c1 = HEXCHAR(c1); |
1105 |
|
|
c2 = part_getc(inpart); |
1106 |
|
|
c2 = HEXCHAR(c2); |
1107 |
|
|
c = c1<<4 | c2; |
1108 |
|
|
if (c != '\r') putc(c, outfile); |
1109 |
|
|
if (digestp) MD5Update(&context, &c, 1); |
1110 |
|
|
} |
1111 |
|
|
} else { |
1112 |
|
|
putc(c1, outfile); |
1113 |
|
|
if (c1 == '\n') { |
1114 |
|
|
if (digestp) MD5Update(&context, "\r", 1); |
1115 |
|
|
} |
1116 |
|
|
c = c1; |
1117 |
|
|
if (digestp) MD5Update(&context, &c, 1); |
1118 |
|
|
} |
1119 |
|
|
} |
1120 |
|
|
if (digestp) *digestp=md5contextTo64(&context); |
1121 |
|
|
} |
1122 |
|
|
|
1123 |
|
|
void fromnone(struct part *inpart, FILE *outfile, char **digestp) |
1124 |
|
|
{ |
1125 |
|
|
int c; |
1126 |
|
|
char ch; |
1127 |
|
|
MD5_CTX context; |
1128 |
|
|
|
1129 |
|
|
if (digestp) MD5Init(&context); |
1130 |
|
|
|
1131 |
|
|
while ((c = part_getc(inpart)) != EOF) { |
1132 |
|
|
putc(c, outfile); |
1133 |
|
|
if (c == '\n') { |
1134 |
|
|
if (digestp) MD5Update(&context, "\r", 1); |
1135 |
|
|
} |
1136 |
|
|
ch = c; |
1137 |
|
|
if (digestp) MD5Update(&context, &ch, 1); |
1138 |
|
|
} |
1139 |
|
|
if (digestp) *digestp=md5contextTo64(&context); |
1140 |
|
|
} |
1141 |
|
|
|