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