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