Back to home page

MITgcm

 
 

    


File indexing completed on 2018-03-02 18:44:46 UTC

view on githubraw file Latest commit e768bd12 on 2008-02-26 17:05:00 UTC
ec6cf3b09d Ed H*0001 /*
                0002  * Decode MIME parts.
                0003  */
                0004 /* (C) Copyright 1993,1994 by Carnegie Mellon University
                0005  * All Rights Reserved.
                0006  *
                0007  * Permission to use, copy, modify, distribute, and sell this software
                0008  * and its documentation for any purpose is hereby granted without
                0009  * fee, provided that the above copyright notice appear in all copies
                0010  * and that both that copyright notice and this permission notice
                0011  * appear in supporting documentation, and that the name of Carnegie
                0012  * Mellon University not be used in advertising or publicity
                0013  * pertaining to distribution of the software without specific,
                0014  * written prior permission.  Carnegie Mellon University makes no
                0015  * representations about the suitability of this software for any
                0016  * purpose.  It is provided "as is" without express or implied
                0017  * warranty.
                0018  *
                0019  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
                0020  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
                0021  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
                0022  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                0023  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
                0024  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                0025  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
                0026  * SOFTWARE.  */
                0027 
                0028 #include <stdio.h>
e768bd1221 Jean*0029 #include <stdlib.h>
ec6cf3b09d Ed H*0030 #include <string.h>
                0031 #include <ctype.h>
e768bd1221 Jean*0032 #include <unistd.h>
ec6cf3b09d Ed H*0033 #include "xmalloc.h"
                0034 #include "common.h"
                0035 #include "part.h"
                0036 #include "md5.h"
                0037 
                0038 extern char *os_idtodir(char *id);
                0039 extern FILE *os_newtypedfile(char *fname, char *contentType, int flags, params contentParams);
                0040 extern FILE *os_createnewfile(char *fname);
                0041 extern char *md5contextTo64(MD5_CTX *context);
e768bd1221 Jean*0042 extern void warn(char *s);
                0043 extern void os_perror(char *str);
                0044 extern void chat(char *s);
                0045 extern void os_donewithdir(char *dir);
                0046 extern void os_warnMD5mismatch(void);
                0047 extern void os_closetypedfile(FILE *outfile);
                0048 
                0049 extern int part_depth(struct part *part);
                0050 extern void part_ungets(char *s, struct part *part);
                0051 extern void part_close(struct part *part);
                0052 extern int part_fill(struct part *part);
                0053 extern void part_addboundary(struct part *part, char *boundary);
                0054 extern int part_readboundary(struct part *part);
ec6cf3b09d Ed H*0055 
                0056 /* The possible content transfer encodings */
                0057 enum encoding { enc_none, enc_qp, enc_base64 };
                0058 
                0059 char *ParseHeaders(struct part *inpart, char **subjectp, char **contentTypep, enum encoding *contentEncodingp, char **contentDispositionp, char **contentMD5p);
                0060 enum encoding parseEncoding(char *s);
                0061 params ParseContent(char **headerp);
                0062 char *getParam(params cParams, char *key);
                0063 char *getDispositionFilename(char *disposition);
                0064 void from64(struct part *inpart, FILE *outfile, char **digestp, int suppressCR);
                0065 void fromqp(struct part *inpart, FILE *outfile, char **digestp);
                0066 void fromnone(struct part *inpart, FILE *outfile, char **digestp);
e768bd1221 Jean*0067 int handlePartial(struct part *inpart, char *headers, params contentParams,
                0068           int extractText);
                0069 int ignoreMessage(struct part *inpart);
                0070 int handleMultipart(struct part *inpart, char *contentType,
                0071             params contentParams, int extractText);
                0072 int handleUuencode(struct part *inpart, char *subject, int extractText);
                0073 int handleText(struct part *inpart, enum encoding contentEncoding);
                0074 int saveToFile(struct part *inpart, int inAppleDouble, char *contentType,
                0075            params contentParams, enum encoding contentEncoding,
                0076            char *contentDisposition, char *contentMD5);
                0077 
ec6cf3b09d Ed H*0078 /*
                0079  * Read and handle an RFC 822 message from the body-part 'inpart'.
                0080  */
                0081 int handleMessage(struct part *inpart, char *defaultContentType, int inAppleDouble, int extractText)
                0082 {
                0083     char *headers, *subject, *contentType, *contentDisposition, *contentMD5;
                0084     enum encoding contentEncoding;
                0085     params contentParams;
                0086 
                0087     /* Parse the headers, getting the ones we're interested in */
                0088     headers = ParseHeaders(inpart, &subject, &contentType, &contentEncoding,
                0089                &contentDisposition, &contentMD5);
                0090     if (!headers) return 1;
                0091 
                0092     /* If no content type, or a non-MIME content type, use the default */
                0093     if (!contentType || !strchr(contentType, '/')) {
                0094     contentType = defaultContentType;
                0095     }
                0096     contentParams = ParseContent(&contentType);
                0097 
                0098     if (!strcasecmp(contentType, "message/rfc822")) {
                0099     if (contentEncoding != enc_none) {
                0100         warn("ignoring invalid content encoding on message/rfc822");
                0101     }
                0102 
                0103     /* Simple recursion */
                0104     return handleMessage(inpart, "text/plain", 0, extractText);
                0105     }
                0106     else if (!strcasecmp(contentType, "message/partial")) {
                0107     if (contentEncoding != enc_none) {
                0108         warn("ignoring invalid content encoding on message/partial");
                0109     }
                0110     return handlePartial(inpart, headers, contentParams, extractText);
                0111     }
                0112     else if (!strncasecmp(contentType, "message/", 8)) {
                0113     /* Probably message/external.  We don't care--toss it */
                0114     return ignoreMessage(inpart);
                0115     }
                0116     else if (!strncasecmp(contentType, "multipart/", 10)) {
                0117     if (contentEncoding != enc_none) {
                0118         warn("ignoring invalid content encoding on multipart");
                0119     }
                0120     return handleMultipart(inpart, contentType, contentParams,
                0121                    extractText);
                0122     }
                0123     else if (part_depth(inpart) == 0 &&
                0124          !strncasecmp(contentType, "text/", 5) &&
                0125          contentEncoding == enc_none &&
                0126          !getDispositionFilename(contentDisposition) &&
                0127          !getParam(contentParams, "name")) {
                0128     /* top-level text message, handle as possible uuencoded file */
                0129     return handleUuencode(inpart, subject, extractText);
                0130     }
                0131     else if (!extractText && !inAppleDouble &&
                0132          !strncasecmp(contentType, "text/", 5) &&
                0133          !getDispositionFilename(contentDisposition) &&
                0134          !getParam(contentParams, "name")) {
                0135     return handleText(inpart, contentEncoding);
                0136     }
                0137     else {
                0138     /* Some sort of attachment, extract it */
                0139     return saveToFile(inpart, inAppleDouble, contentType, contentParams,
                0140               contentEncoding, contentDisposition, contentMD5);
                0141     }
                0142 }
                0143 
                0144 /*
                0145  * Skip whitespace and RFC-822 comments.
                0146  */
                0147 void SkipWhitespace(char **s)
                0148 {
                0149     char *p = *s;
                0150     int commentlevel = 0;
                0151 
                0152     while (*p && (isspace(*p) || *p == '(')) {
                0153     if (*p == '\n') {
                0154         p++;
                0155         if (*p != ' ' && *p != '\t') {
                0156         *s = 0;
                0157         return;
                0158         }
                0159     }
                0160     else if (*p == '(') {
                0161         p++;
                0162         commentlevel++;
                0163         while (commentlevel) {
                0164         switch (*p) {
                0165         case '\n':
                0166             p++;
                0167             if (*p == ' ' || *p == '\t') break;
                0168             /* FALL THROUGH */
                0169         case '\0':
                0170             *s = 0;
                0171             return;
                0172             
                0173         case '\\':
                0174             p++;
                0175             break;
                0176 
                0177         case '(':
                0178             commentlevel++;
                0179             break;
                0180 
                0181         case ')':
                0182             commentlevel--;
                0183             break;
                0184         }
                0185         p++;
                0186         }
                0187     }
                0188     else p++;
                0189     }
                0190     if (*p == 0) {
                0191     *s = 0;
                0192     }
                0193     else {
                0194     *s = p;
                0195     }
                0196 }
                0197 
                0198 /*
                0199  * Read and parse the headers of an RFC 822 message, returning them in
                0200  * a pointer to a static buffer.  The headers are read from 'inpart'.
                0201  * A pointer to the value of any Subject:, Content-Type:,
                0202  * Content-Disposition:, or Content-MD5: header is stored in the space
                0203  * pointed to by 'subjectp', 'contentTypep', contentDispositionp, and
                0204  * contentMD5p, respectively.  The Content-Transfer-Encoding is stored
                0205  * in the enum pointed to by 'contentEncodingp'.
                0206  */
                0207 #define HEADGROWSIZE 1000
                0208 char *ParseHeaders(struct part *inpart, char **subjectp, char **contentTypep, enum encoding *contentEncodingp, char **contentDispositionp, char **contentMD5p)
                0209 {
                0210     static int alloced = 0;
                0211     static char *headers;
                0212     int left, len, i;
                0213     char *next, *val;
                0214 
                0215     /* Read headers into buffer pointed to by "headers" */
                0216     if (!alloced) {
                0217     headers = xmalloc(alloced = HEADGROWSIZE);
                0218     }
                0219     next = headers;
                0220     *next++ = '\n';     /* Leading newline to make matching header names easier */
                0221     left = alloced - 2;     /* Allow room for terminating null */
                0222 
                0223     while (part_gets(next, left, inpart) && (*next != '\n' || next[-1] != '\n')) {
                0224     len = strlen(next);
                0225 
                0226     if (next[-1] == '\n') {
                0227         /* Check for valid header-ness of "next" */
                0228         for (i = 0; i < len; i++) {
                0229         if (next[i] == ':' ||
                0230             next[i] <= ' ' || next[i] >= '\177') break;
                0231         }
                0232         if (i == 0 || next[i] != ':') {
                0233         /* Check for header continuation line */
                0234         if (next == headers+1 || (next[0] != ' ' && next[0] != '\t')) {
                0235             /*
                0236              * Not a valid header, push back on input stream
                0237              * and stop reading input.
                0238              */
                0239             part_ungets(next, inpart);
                0240             break;
                0241         }
                0242         }
                0243     }
                0244 
                0245     left -= len;
                0246     next += len;
                0247 
                0248     if (left < 100) {
                0249         len = next - headers;
                0250         alloced += HEADGROWSIZE;
                0251         left += HEADGROWSIZE;
                0252         headers = xrealloc(headers, alloced);
                0253         next = headers + len;
                0254     }
                0255     }
                0256 
                0257     *next = '\0';
                0258 
                0259     /* Look for the headers we find particularly interesting */
                0260     *subjectp = *contentTypep = *contentDispositionp = *contentMD5p = 0;
                0261     *contentEncodingp = enc_none;
                0262     for (next = headers; *next; next++) {
                0263     if (*next == '\n') {
                0264         switch(next[1]) {
                0265         case 's':
                0266         case 'S':
                0267         if (!strncasecmp(next+2, "ubject:", 7)) {
                0268             val = next+9;
                0269             SkipWhitespace(&val);
                0270             if (val) *subjectp = val;
                0271         }
                0272         break;
                0273 
                0274         case 'c':
                0275         case 'C':
                0276         if (!strncasecmp(next+2, "ontent-type:", 12)) {
                0277             val = next+14;
                0278             SkipWhitespace(&val);
                0279             if (val) *contentTypep = val;
                0280         }
                0281         else if (!strncasecmp(next+2, "ontent-transfer-encoding:", 25)) {
                0282             *contentEncodingp = parseEncoding(next+27);
                0283         }
                0284         else if (!strncasecmp(next+2, "ontent-disposition:", 19)) {
                0285             val = next+21;
                0286             SkipWhitespace(&val);
                0287             if (val) *contentDispositionp = val;
                0288         }
                0289         else if (!strncasecmp(next+2, "ontent-md5:", 11)) {
                0290             val = next+13;
                0291             SkipWhitespace(&val);
                0292             if (val) *contentMD5p = val;
                0293         }
                0294         }
                0295     }
                0296     }
                0297     return headers;
                0298 }
                0299 
                0300 /*
                0301  * Parse the Content-Transfer-Encoding: value pointed to by 's'.
                0302  * Returns the appropriate encoding enum.
                0303  */
                0304 enum encoding parseEncoding(char *s)
                0305 {
                0306     SkipWhitespace(&s);
                0307     if (s) {
                0308     switch (*s) {
                0309     case 'q':
                0310     case 'Q':
                0311         if (!strncasecmp(s+1, "uoted-printable", 15) &&
                0312         (isspace(s[16]) || s[16] == '(')) {
                0313         return enc_qp;
                0314         }
                0315         break;
                0316 
                0317     case '7':
                0318     case '8':
                0319         if (!strncasecmp(s+1, "bit", 3) &&
                0320         (isspace(s[4]) || s[4] == '(')) {
                0321         return enc_none;
                0322         }
                0323         break;
                0324 
                0325     case 'b':
                0326     case 'B':
                0327         if (!strncasecmp(s+1, "ase64", 5) &&
                0328         (isspace(s[6]) || s[6] == '(')) {
                0329         return enc_base64;
                0330         }
                0331         if (!strncasecmp(s+1, "inary", 5) &&
                0332         (isspace(s[6]) || s[6] == '(')) {
                0333         return enc_none;
                0334         }
                0335     }
                0336     warn("ignoring unknown content transfer encoding\n");   
                0337     }
                0338     return enc_none;
                0339 }
                0340 
                0341 /*
                0342  * Parse the value of a Content-Type: header.
                0343  * 'headerp' points to a pointer to the input string.
                0344  * The pointer pointed to by 'headerp' is changed to point to
                0345  * a static buffer containing the content type stripped of whitespace
                0346  * and parameters.  The parameters are converted to a type suitable for
                0347  * getParm() and returned.
                0348  */
                0349 #define PARAMGROWSIZE 10
                0350 params ParseContent(char **headerp)
                0351 {
                0352     char *header;
                0353     static int palloced = 0;
                0354     static char **param;
                0355     static int calloced = 0;
                0356     static char *cbuf;
                0357     char *p;
                0358     int nparam;
                0359 
                0360     p = header = *headerp;
                0361 
                0362     /* Find end of header, including continuation lines */
                0363     do {
                0364     p = strchr(p+1, '\n');
                0365     } while (p && isspace(p[1]));
                0366     if (!p) {
                0367     p = header + strlen(header);
                0368     }
                0369 
                0370     /* If necessary, allocate/grow cbuf to hold header. */
                0371     if (p - header >= calloced) {
                0372     calloced = p - header + 1;
                0373     if (calloced < 200) calloced = 200;
                0374     cbuf = xrealloc(cbuf, calloced);
                0375     }
                0376 
                0377     /* Copy header to cbuf */
                0378     strncpy(cbuf, header, p - header);
                0379     cbuf[p - header] = 0;
                0380     header = *headerp = cbuf;
                0381     
                0382     nparam = 0;
                0383 
                0384     /* Strip whitespace from content type */
                0385     /* ParseHeaders() stripped leading whitespace */
                0386     p = header;
                0387     while (header && *header && *header != ';') {
                0388     while (*header && !isspace(*header) && *header != '(' &&
                0389            *header != ';') {
                0390         *p++ = *header++;
                0391     }
                0392     SkipWhitespace(&header);
                0393     }
                0394     if (!header || !*header) return 0;
                0395     header++;
                0396     *p = '\0';
                0397     
                0398     /* Parse the parameters */
                0399     while (*header) {
                0400     SkipWhitespace(&header);
                0401     if (!header) break;
                0402 
                0403     if (nparam+1 >= palloced) {
                0404         palloced += PARAMGROWSIZE;
                0405         param = (char **) xrealloc((char *)param, palloced * sizeof(char *));
                0406     }
                0407     param[nparam++] = header;
                0408 
                0409     /* Find any separating semicolon.  Pay attention to quoted-strings */
                0410     while (*header && *header != ';') {
                0411         if (*header == '\"') {
                0412         ++header;
                0413         while (*header && *header != '\"') {
                0414             if (*header == '\\') {
                0415             ++header;
                0416             if (!*header) break;
                0417             }
                0418             ++header;
                0419         }
                0420         if (!*header) break;
                0421         }
                0422         else if (*header == '(') {
                0423         /* Convert comments to spaces */
                0424         p = header;
                0425         SkipWhitespace(&p);
                0426         if (!p) {
                0427             break;
                0428         }
                0429         while (header < p) *header++ = ' ';
                0430         header--;
                0431         }
                0432         header++;
                0433     }
                0434     if (*header) *header++ = '\0';
                0435     }
                0436     
                0437     if (nparam == 0)
                0438        return 0;
                0439 
                0440     param[nparam] = 0;
                0441     return param;
                0442 }
                0443 
                0444 /*
                0445  * Get the value of the parameter named 'key' from the content-type
                0446  * parameters 'cParams'.  Returns a pointer to a static bufer which
                0447  * contains the value, or null if no such parameter was found.
                0448  */
                0449 #define VALUEGROWSIZE 100
                0450 char *getParam(params cParams, char *key)
                0451 {
                0452     static char *value;
                0453     static int alloced = 0;
                0454     int left;
                0455     int keylen = strlen(key);
                0456     char *from, *to;
                0457 
                0458     if (!cParams) return 0;
                0459 
                0460     if (!alloced) {
                0461     value = xmalloc(alloced = VALUEGROWSIZE);
                0462     }
                0463 
                0464     /* Find the named parameter */
                0465     while (*cParams) {
                0466     if (!strncasecmp(key, *cParams, keylen) &&
                0467         ((*cParams)[keylen] == '=' || isspace((*cParams)[keylen]))) break;
                0468     cParams++;
                0469     }
                0470     if (!*cParams) return 0;
                0471 
                0472     /* Skip over the "=" and any surrounding whitespace */
                0473     from = *cParams + keylen;
                0474     while (*from && isspace(*from)) from++;
                0475     if (*from++ != '=') return 0;
                0476     while (*from && isspace(*from)) from++;
                0477     if (!*from) return 0;
                0478 
                0479     /* Copy value into buffer */
                0480     to = value;
                0481     left = alloced - 1;
                0482     if (*from == '\"') {
                0483     /* Quoted-string */
                0484     from++;
                0485     while (*from && *from != '\"') {
                0486         if (!--left) {
                0487         alloced += VALUEGROWSIZE;
                0488         left += VALUEGROWSIZE;
                0489         value = xrealloc(value, alloced);
                0490         to = value + alloced - left - 2;
                0491         }
                0492         if (*from == '\\') {
                0493         from++;
                0494         if (!*from) return 0;
                0495         }
                0496         *to++ = *from++;
                0497     }
                0498     if (!*from) return 0;
                0499     }
                0500     else {
                0501     /* Just a token */
                0502     while (*from && !isspace(*from)) {
                0503         if (!--left) {
                0504         alloced += VALUEGROWSIZE;
                0505         left += VALUEGROWSIZE;
                0506         value = xrealloc(value, alloced);
                0507         to = value + alloced - left - 2;
                0508         }
                0509         *to++ = *from++;
                0510     }
                0511     }
                0512     *to = '\0';
                0513     return value;
                0514 }
                0515 
                0516 /*
                0517  * Get the value of the "filename" parameter in a Content-Disposition:
                0518  * header.  Returns a pointer to a static buffer containing the value, or
                0519  * a null pointer if there was no such parameter.
                0520  */
                0521 char *
                0522 getDispositionFilename(char *disposition)
                0523 {
                0524     static char *value;
                0525     static int alloced = 0;
                0526     int left;
                0527     char *to;
                0528 
                0529     if (!disposition) return 0;
                0530 
                0531     /* Skip until we find ";" "filename" "=" tokens. */
                0532     for (;;) {
                0533     /* Skip until we find ";" */
                0534     while (*disposition != ';') {
                0535         if (!*disposition) return 0;
                0536         else if (*disposition == '\"') {
                0537         ++disposition;
                0538         while (*disposition && *disposition != '\"') {
                0539             if (*disposition == '\\') {
                0540             ++disposition;
                0541             if (!*disposition) return 0;
                0542             }
                0543             ++disposition;
                0544         }
                0545         if (!*disposition) return 0;
                0546         }
                0547         else if (*disposition == '(') {
                0548         SkipWhitespace(&disposition);
                0549         if (!disposition) return 0;
                0550         disposition--;
                0551         }
                0552         disposition++;
                0553     }
                0554 
                0555     /* Skip over ";" and trailing whitespace */
                0556     disposition++;
                0557     SkipWhitespace(&disposition);
                0558     if (!disposition) return 0;
                0559 
                0560     /*
                0561      * If we're not looking at a "filename" token, go back
                0562      * and look for another ";".  Otherwise skip it and
                0563      * trailing whitespace.
                0564      */
                0565     if (strncasecmp(disposition, "filename", 8) != 0) continue;
                0566     disposition += 8;
                0567     if (!isspace(*disposition) && *disposition != '=' &&
                0568         *disposition != '(') {
                0569         continue;
                0570     }
                0571     SkipWhitespace(&disposition);
                0572     if (!disposition) return 0;
                0573 
e768bd1221 Jean*0574     /* If we're looking at a "=", we found what we're looking for */
                0575     if (*disposition++ == '=') break;
ec6cf3b09d Ed H*0576     }
                0577 
                0578     SkipWhitespace(&disposition);
                0579     if (!disposition) return 0;
                0580       
                0581     if (!alloced) {
                0582     value = xmalloc(alloced = VALUEGROWSIZE);
                0583     }
                0584 
                0585     /* Copy value into buffer */
                0586     to = value;
                0587     left = alloced - 1;
                0588     if (*disposition == '\"') {
                0589     /* Quoted-string */
                0590     disposition++;
                0591     while (*disposition && *disposition != '\"') {
                0592         if (!--left) {
                0593         alloced += VALUEGROWSIZE;
                0594         left += VALUEGROWSIZE;
                0595         value = xrealloc(value, alloced);
                0596         to = value + alloced - left - 2;
                0597         }
                0598         if (*disposition == '\\') {
                0599         disposition++;
                0600         if (!*disposition) return 0;
                0601         }
                0602         *to++ = *disposition++;
                0603     }
                0604     if (!*disposition) return 0;
                0605     }
                0606     else {
                0607     /* Just a token */
                0608     while (*disposition && !isspace(*disposition) &&
                0609            *disposition != '(') {
                0610         if (!--left) {
                0611         alloced += VALUEGROWSIZE;
                0612         left += VALUEGROWSIZE;
                0613         value = xrealloc(value, alloced);
                0614         to = value + alloced - left - 2;
                0615         }
                0616         *to++ = *disposition++;
                0617     }
                0618     }
                0619     *to = '\0';
                0620     return value;
                0621 }    
                0622 
                0623 /*
                0624  * Read and handle a message/partial object from the file 'inpart'.
                0625  */
                0626 int handlePartial(struct part *inpart, char *headers, params contentParams, int extractText)
                0627 {
                0628     char *id, *dir, *p;
                0629     int thispart;
                0630     int nparts = 0;
                0631     char buf[1024];
                0632     FILE *partfile, *outfile;
                0633     struct part *outpart;
                0634     int i, docopy;
                0635 
                0636     id = getParam(contentParams, "id");
                0637     if (!id) {
                0638     warn("partial message has no id parameter");
                0639     goto ignore;
                0640     }
                0641 
                0642     /* Get directory to store the parts being reassembled */
                0643     dir = os_idtodir(id);
                0644     if (!dir) goto ignore;
                0645 
                0646     p = getParam(contentParams, "number");
                0647     if (!p) {
                0648     warn("partial message doesn't have number parameter");
                0649     goto ignore;
                0650     }
                0651     thispart = atoi(p);
                0652 
e768bd1221 Jean*0653     if ((p = getParam(contentParams, "total"))) {
ec6cf3b09d Ed H*0654     nparts = atoi(p);
                0655     if (nparts <= 0) {
                0656         warn("partial message has invalid number of parts");
                0657         goto ignore;
                0658     }
                0659     /* Store number of parts in reassembly directory */
                0660     sprintf(buf, "%sCT", dir);
                0661     partfile = os_createnewfile(buf);
                0662     if (!partfile) {
                0663         os_perror(buf);
                0664         goto ignore;
                0665     }
                0666     fprintf(partfile, "%d\n", nparts);
                0667     fclose(partfile);
                0668     }
                0669     else {
                0670     /* Try to retrieve number of parts from reassembly directory */
                0671     sprintf(buf, "%sCT", dir);
e768bd1221 Jean*0672     if ((partfile = fopen(buf, "r"))) {
ec6cf3b09d Ed H*0673         if (fgets(buf, sizeof(buf), partfile)) {
                0674         nparts = atoi(buf);
                0675         if (nparts < 0) nparts = 0;
                0676         }
                0677         fclose(partfile);
                0678     }
                0679     }
                0680 
                0681     /* Sanity check */
                0682     if (thispart <= 0 || (nparts && thispart > nparts)) {
                0683     warn("partial message has invalid number");
                0684     goto ignore;
                0685     }
                0686 
                0687     sprintf(buf, "Saving part %d ", thispart);
                0688     if (nparts) sprintf(buf+strlen(buf), "of %d ", nparts);
                0689     strcat(buf, getParam(contentParams, "id"));
                0690     chat(buf);
                0691 
                0692     /* Create file to store this part */
                0693     sprintf(buf, "%s%d", dir, thispart);
                0694     partfile = os_createnewfile(buf);
                0695     if (!partfile) {
                0696     os_perror(buf);
                0697     goto ignore;
                0698     }
                0699 
                0700     /* Do special-case header handling for first part */
                0701     if (thispart == 1) {
                0702     int skippedfirstbyte = 0;
                0703 
                0704     while (*headers) {
                0705         if (*headers == '\n' &&
                0706         (!strncasecmp(headers, "\ncontent-", 9) ||
                0707          !strncasecmp(headers, "\nmessage-id:", 12))) {
                0708         /* Special case, skip header */
                0709         headers++;
                0710         while (*headers && (*headers != '\n' || isspace(headers[1]))) {
                0711             headers++;
                0712         }
                0713         }
                0714         else {
                0715         /* First byte of headers is extra newline, don't write it to file */
                0716         if (skippedfirstbyte++) putc(*headers, partfile);
                0717         headers++;
                0718         }
                0719     }
                0720     docopy = 0;
                0721     /* Handle headers in the multipart/partial body */
                0722     while (part_gets(buf, sizeof(buf), inpart)) {
                0723         if (*buf == '\n') {
                0724         putc('\n', partfile);
                0725         break;
                0726         }
                0727         if (!strncasecmp(buf, "content-", 8) || !strncasecmp(buf, "message-id:", 11)) {
                0728         docopy = 1;
                0729         }
                0730         else if (!isspace(*buf)) {
                0731         docopy = 0;
                0732         }
                0733 
                0734         if (docopy) fputs(buf, partfile);
                0735         while(buf[strlen(buf)-1] != '\n' && part_gets(buf, sizeof(buf), inpart)) {
                0736         if (docopy) fputs(buf, partfile);
                0737         }
                0738     }
                0739     }
                0740 
                0741     /* Copy the contents to the file */
                0742     while (part_gets(buf, sizeof(buf), inpart)) {
                0743     fputs(buf, partfile);
                0744     }
                0745     fclose(partfile);
                0746 
                0747     /* Check to see if we have all parts.  Start from the highest numbers
                0748      * as we are more likely not to have them.
                0749      */
                0750     for (i = nparts; i; i--) {
                0751     sprintf(buf, "%s%d", dir, i);
                0752     partfile = fopen(buf, "r");
                0753     if (partfile) {
                0754         fclose(partfile);
                0755     }
                0756     else {
                0757         break;
                0758     }
                0759     }
                0760 
                0761     if (i || !nparts) {
                0762     /* We don't have all the parts yet */
                0763     return 0;
                0764     }
                0765 
                0766     /* We have everything, concatenate all the parts into a single file */
                0767     sprintf(buf, "%sFULL", dir);
                0768     outfile = os_createnewfile(buf);
                0769     if (!outfile) {
                0770     os_perror(buf);
                0771     return 1;
                0772     }
                0773     for (i=1; i<=nparts; i++) {
                0774     sprintf(buf, "%s%d", dir, i);
                0775     partfile = fopen(buf, "r");
                0776     if (!partfile) {
                0777         os_perror(buf);
                0778         return 1;
                0779     }
                0780     while (fgets(buf, sizeof(buf), partfile)) {
                0781         fputs(buf, outfile);
                0782     }
                0783     fclose(partfile);
                0784 
                0785     /* Done with message part file, delete it */
                0786     sprintf(buf, "%s%d", dir, i);
                0787     remove(buf);
                0788     }
                0789 
                0790     /* Open the concatenated file for reading and handle it */
                0791     fclose(outfile);
                0792     sprintf(buf, "%sFULL", dir);
                0793     outfile = fopen(buf, "r");
                0794     if (!outfile) {
                0795     os_perror(buf);
                0796     return 1;
                0797     }
                0798     outpart = part_init(outfile);
                0799     handleMessage(outpart, "text/plain", 0, extractText);
                0800     part_close(outpart);
                0801 
                0802     /* Clean up the rest of the reassembly directory */
                0803     sprintf(buf, "%sFULL", dir);
                0804     remove(buf);
                0805     sprintf(buf, "%sCT", dir);
                0806     remove(buf);
                0807     os_donewithdir(dir);
                0808 
                0809     return 0;
                0810 
                0811  ignore:
                0812     ignoreMessage(inpart);
                0813     return 1;
                0814 }
                0815 
                0816 /*
                0817  * Skip over a message object from the file 'inpart'.
                0818  */
                0819 int ignoreMessage(struct part *inpart)
                0820 {
                0821     while (part_getc(inpart) != EOF);
                0822     return 0;
                0823 }
                0824 
                0825 /*
                0826  * Read and handle a multipart object from 'inpart'.
                0827  */
                0828 int handleMultipart(struct part *inpart, char *contentType, params contentParams, int extractText)
                0829 {
                0830     char *id;
                0831     char *defaultContentType = "text/plain";
                0832     int isAppleDouble = 0;
                0833 
                0834     /* Components of multipart/digest have a different default content-type */
                0835     if (!strcasecmp(contentType, "multipart/digest")) {
                0836     defaultContentType = "message/rfc822";
                0837     }
                0838     if (!strcasecmp(contentType, "multipart/appledouble")) {
                0839     isAppleDouble++;
                0840     }
                0841 
                0842     if (!(id = getParam(contentParams, "boundary"))) {
                0843     warn("multipart message has no boundary parameter");
                0844     id="";
                0845     }
                0846 
                0847     /* Add the new boundary id */
                0848     part_addboundary(inpart, id);
                0849 
                0850 #ifdef __riscos
                0851     /*
                0852      * "Marcel" encodes RISCOS directory structure in the multipart
                0853      * structure.  That is the Wrong Way to do it, but we hold our
                0854      * nose and pass the information to the OS layer.
                0855      */
                0856     os_boundaryhookopen(part_depth(inpart));
                0857 #endif
                0858 
                0859     /*
                0860      * Skip over preamble.
                0861      * HACK: The initial boundary doesn't have to start with a newline,
                0862      * so we deal with this by stuffing an initial newline into the input
                0863      * stream
                0864      */
                0865     part_ungetc('\n', inpart);
                0866     ignoreMessage(inpart);
                0867 
                0868     /* Handle the component messages */
                0869     while (!part_readboundary(inpart)) {
                0870     handleMessage(inpart, defaultContentType, isAppleDouble, extractText);
                0871     }
                0872 
                0873 #ifdef __riscos
                0874     os_boundaryhookclose(part_depth(inpart));
                0875 #endif
                0876 
                0877     /* Skip over postamble */
                0878     ignoreMessage(inpart);
                0879 
                0880     /* Remove any lingering unused description file */
                0881     (void) remove(TEMPFILENAME);
                0882 
                0883     return 0;
                0884 }
                0885 
                0886 /*
                0887  * Handle a text message object from 'inpart' by saving it to
                0888  * the temporary description file.
                0889  */
                0890 int handleText(struct part *inpart, enum encoding contentEncoding)
                0891 {
                0892     FILE *descfile;
                0893 
                0894     descfile = os_createnewfile(TEMPFILENAME);
                0895     if (!descfile) {
                0896     os_perror(TEMPFILENAME);
                0897     ignoreMessage(inpart);
                0898     return 1;
                0899     }
                0900 
                0901     /* Write the file, handling the appropriate encoding */
                0902     switch (contentEncoding) {
                0903     case enc_none:
                0904     fromnone(inpart, descfile, (char **)0);
                0905     break;
                0906 
                0907     case enc_qp:
                0908     fromqp(inpart, descfile, (char **)0);
                0909     break;
                0910 
                0911     case enc_base64:
                0912     from64(inpart, descfile, (char **)0, 1);
                0913     break;
                0914     }
                0915 
                0916     fclose(descfile);
                0917     return 0;
                0918 }
                0919 
                0920 /*
                0921  * Read a message object from 'inpart' and save it to a file.
                0922  */
                0923 int saveToFile(struct part *inpart, int inAppleDouble, char *contentType, params contentParams, enum encoding contentEncoding, char *contentDisposition, char *contentMD5)
                0924 {
                0925     FILE *outfile = 0;
                0926     int flags = 0;
                0927     int suppressCR = 0;
                0928     char *outputmd5;
                0929     char *fname;
                0930 
                0931     if (!strncasecmp(contentType, "text/", 5)) {
                0932     suppressCR = 1;
                0933     }
                0934     else if (contentEncoding == enc_base64) {
                0935     /*
                0936      * HEURISTIC: It is not in general possible to determine whether
                0937      * any non-text content type is line-oriented.  We guess
                0938      * the "binary" status of a part from the composer's choice
                0939      * of content transfer encoding.
                0940      *
                0941      * If the content transfer encoding is "binary" and the input is
                0942      * not line-oriented, we're screwed anyway--the input file has
                0943      * been opened in text mode.  So the "binary output file" heuristic
                0944      * is not applied in this case.
                0945      */
                0946     flags |= FILE_BINARY;
                0947     }
                0948 
                0949     if (inAppleDouble) flags |= FILE_INAPPLEDOUBLE;
                0950     
                0951     /* Find an appropriate filename and create the output file */
                0952     fname = getDispositionFilename(contentDisposition);
                0953     if (!fname) fname = getParam(contentParams, "name");
                0954     if (fname) fname = strsave(fname);
                0955     outfile = os_newtypedfile(fname, contentType, flags, contentParams);
                0956     if (fname) free(fname);
                0957     if (!outfile) {
                0958     ignoreMessage(inpart);
                0959     return 1;
                0960     }
                0961 
                0962     /* Write the file, handling the appropriate encoding */
                0963     switch (contentEncoding) {
                0964     case enc_none:
                0965     fromnone(inpart, outfile, &outputmd5);
                0966     break;
                0967 
                0968     case enc_qp:
                0969     fromqp(inpart, outfile, &outputmd5);
                0970     break;
                0971 
                0972     case enc_base64:
                0973     from64(inpart, outfile, &outputmd5, suppressCR);
                0974     break;
                0975     }
                0976     rewind(outfile);
                0977 
                0978     /* Check the MD5 digest if it was supplied */
                0979     if (contentMD5) {
                0980     if (strncmp(outputmd5, contentMD5, strlen(outputmd5)) != 0) {
                0981         os_warnMD5mismatch();
                0982     }
                0983     }
                0984     free(outputmd5);
                0985 
                0986     os_closetypedfile(outfile);
                0987     return 0;
                0988 }
                0989 
                0990 #define XX 127
                0991 /*
                0992  * Table for decoding hexadecimal in quoted-printable
                0993  */
                0994 static char index_hex[256] = {
                0995     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
                0996     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
                0997     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
                0998      0, 1, 2, 3,  4, 5, 6, 7,  8, 9,XX,XX, XX,XX,XX,XX,
                0999     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