/[MITgcm]/MITgcm/tools/mpack-1.6/decode.c
ViewVC logotype

Contents of /MITgcm/tools/mpack-1.6/decode.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download)
Tue Feb 26 17:05:00 2008 UTC (16 years, 1 month ago) by jmc
Branch: MAIN
CVS Tags: checkpoint64y, checkpoint64x, checkpoint64z, checkpoint64q, checkpoint64p, checkpoint64s, checkpoint64r, checkpoint64u, checkpoint64t, checkpoint64w, checkpoint64v, checkpoint64i, checkpoint64h, checkpoint64k, checkpoint64j, checkpoint64m, checkpoint64l, checkpoint64o, checkpoint64n, checkpoint64a, checkpoint64c, checkpoint64b, checkpoint64e, checkpoint64d, checkpoint64g, checkpoint64f, checkpoint63p, checkpoint63q, checkpoint63r, checkpoint63s, checkpoint63l, checkpoint63m, checkpoint63n, checkpoint63o, checkpoint63h, checkpoint63i, checkpoint63j, checkpoint63k, checkpoint63d, checkpoint63e, checkpoint63f, checkpoint63g, checkpoint63a, checkpoint63b, checkpoint63c, checkpoint64, checkpoint65, checkpoint60, checkpoint61, checkpoint62, checkpoint63, checkpoint66g, checkpoint66f, checkpoint66e, checkpoint66d, checkpoint66c, checkpoint66b, checkpoint66a, checkpoint66o, checkpoint66n, checkpoint66m, checkpoint66l, checkpoint66k, checkpoint66j, checkpoint66i, checkpoint66h, checkpoint65z, checkpoint65x, checkpoint65y, checkpoint65r, checkpoint65s, checkpoint65p, checkpoint65q, checkpoint65v, checkpoint65w, checkpoint65t, checkpoint65u, checkpoint65j, checkpoint65k, checkpoint65h, checkpoint65i, checkpoint65n, checkpoint65o, checkpoint65l, checkpoint65m, checkpoint65b, checkpoint65c, checkpoint65a, checkpoint65f, checkpoint65g, checkpoint65d, checkpoint65e, checkpoint59q, checkpoint59p, checkpoint59r, checkpoint59o, checkpoint62c, checkpoint62b, checkpoint62a, checkpoint62g, checkpoint62f, checkpoint62e, checkpoint62d, checkpoint62k, checkpoint62j, checkpoint62i, checkpoint62h, checkpoint62o, checkpoint62n, checkpoint62m, checkpoint62l, checkpoint62s, checkpoint62r, checkpoint62q, checkpoint62p, checkpoint62w, checkpoint62v, checkpoint62u, checkpoint62t, checkpoint62z, checkpoint62y, checkpoint62x, checkpoint61f, checkpoint61g, checkpoint61d, checkpoint61e, checkpoint61b, checkpoint61c, checkpoint61a, checkpoint61n, checkpoint61o, checkpoint61l, checkpoint61m, checkpoint61j, checkpoint61k, checkpoint61h, checkpoint61i, checkpoint61v, checkpoint61w, checkpoint61t, checkpoint61u, checkpoint61r, checkpoint61s, checkpoint61p, checkpoint61q, checkpoint61z, checkpoint61x, checkpoint61y, HEAD
Changes since 1.3: +30 -4 lines
File MIME type: text/plain
put again new version (1.6-4, see MITgcm_contrib/mpack_src) of mpack sources.
among others:
 - fix MD5 code on some 64 bit platforms.
 - fix lot of compilation warnings.

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 <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #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 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
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 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 /*
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 /* If we're looking at a "=", we found what we're looking for */
575 if (*disposition++ == '=') break;
576 }
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 if ((p = getParam(contentParams, "total"))) {
654 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 if ((partfile = fopen(buf, "r"))) {
673 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

  ViewVC Help
Powered by ViewVC 1.1.22