/[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.1 - (show annotations) (download)
Tue Aug 26 20:45:25 2003 UTC (20 years, 7 months ago) by edhill
Branch: MAIN
CVS Tags: checkpoint58l_post, checkpoint51k_post, checkpoint57t_post, checkpoint57o_post, checkpoint52l_pre, checkpoint52e_pre, hrcube4, checkpoint58e_post, mitgcm_mapl_00, checkpoint52n_post, checkpoint52j_post, checkpoint53d_post, checkpoint58u_post, checkpoint58w_post, checkpoint54a_pre, checkpoint51o_pre, checkpoint57m_post, checkpoint55c_post, checkpoint54e_post, checkpoint52e_post, checkpoint57s_post, checkpoint51n_pre, checkpoint54a_post, checkpoint53c_post, checkpoint57k_post, checkpoint55d_pre, checkpoint57d_post, checkpoint51l_post, checkpoint57g_post, checkpoint51q_post, checkpoint57b_post, checkpoint57c_pre, checkpoint51j_post, checkpoint58r_post, checkpoint55j_post, checkpoint56b_post, checkpoint57i_post, checkpoint57y_post, hrcube_1, checkpoint57e_post, branch-netcdf, checkpoint52d_pre, checkpoint52l_post, checkpoint55h_post, checkpoint58n_post, checkpoint53b_post, checkpoint58x_post, checkpoint52k_post, checkpoint52b_pre, checkpoint57g_pre, checkpoint54b_post, checkpoint53b_pre, checkpoint55b_post, checkpoint58t_post, checkpoint58h_post, checkpoint54d_post, checkpoint56c_post, checkpoint52m_post, checkpoint57y_pre, checkpoint55, checkpoint53a_post, checkpoint57f_pre, checkpoint57a_post, checkpoint54, checkpoint58q_post, checkpoint54f_post, checkpoint57v_post, checkpoint55g_post, checkpoint51o_post, checkpoint51p_post, checkpoint58j_post, checkpoint52a_pre, checkpoint59e, checkpoint59d, checkpoint59g, checkpoint59f, checkpoint59a, checkpoint55f_post, checkpoint59c, checkpoint59b, checkpoint59m, checkpoint59l, checkpoint59n, checkpoint59i, checkpoint59h, checkpoint59k, checkpoint59j, checkpoint57r_post, checkpoint59, checkpoint58, checkpoint57a_pre, checkpoint55i_post, checkpoint57, checkpoint56, checkpoint51i_post, checkpoint53, checkpoint52, checkpoint51f_post, checkpoint52d_post, eckpoint57e_pre, checkpoint51r_post, checkpoint52a_post, checkpoint57h_done, checkpoint58f_post, checkpoint52b_post, checkpoint53g_post, checkpoint52f_post, branchpoint-genmake2, checkpoint57x_post, checkpoint57n_post, checkpoint52c_post, checkpoint58d_post, checkpoint58c_post, checkpoint57w_post, checkpoint57p_post, checkpint57u_post, checkpoint57f_post, checkpoint58a_post, checkpoint51h_pre, checkpoint51l_pre, checkpoint58i_post, checkpoint57q_post, checkpoint51g_post, checkpoint58g_post, ecco_c52_e35, hrcube5, checkpoint58o_post, checkpoint57z_post, checkpoint57c_post, checkpoint58y_post, checkpoint55e_post, checkpoint58k_post, checkpoint52i_post, checkpoint52j_pre, checkpoint58v_post, checkpoint53f_post, checkpoint55a_post, checkpoint51t_post, checkpoint53d_pre, checkpoint54c_post, checkpoint58s_post, checkpoint58p_post, checkpoint51n_post, checkpoint57j_post, checkpoint58b_post, checkpoint57h_pre, checkpoint51i_pre, checkpoint58m_post, checkpoint57l_post, checkpoint52i_pre, checkpoint51u_post, checkpoint52h_pre, checkpoint52f_pre, checkpoint57h_post, hrcube_2, hrcube_3, checkpoint56a_post, checkpoint51m_post, checkpoint51s_post, checkpoint55d_post
Branch point for: branch-nonh, branch-genmake2, tg2-branch, checkpoint51n_branch, netcdf-sm0
File MIME type: text/plain
Initial check-in of the CMU "mpack" utility.  This allows us to (portably)
send MITgcm output as MIME-encoded email messages and will be used by the
"testreport" script.  The CMU license is basically an "AS-IS" statement.

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

  ViewVC Help
Powered by ViewVC 1.1.22