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

  ViewVC Help
Powered by ViewVC 1.1.22