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

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

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


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

  ViewVC Help
Powered by ViewVC 1.1.22