home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / uucp / mpack-1.3-amiga_src.lha / decode.c < prev    next >
C/C++ Source or Header  |  1994-05-10  |  24KB  |  979 lines

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