home *** CD-ROM | disk | FTP | other *** search
- /*
- * Decode MIME parts.
- */
- /* (C) Copyright 1993,1994 by Carnegie Mellon University
- * All Rights Reserved.
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright notice appear in all copies
- * and that both that copyright notice and this permission notice
- * appear in supporting documentation, and that the name of Carnegie
- * Mellon University not be used in advertising or publicity
- * pertaining to distribution of the software without specific,
- * written prior permission. Carnegie Mellon University makes no
- * representations about the suitability of this software for any
- * purpose. It is provided "as is" without express or implied
- * warranty.
- *
- * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
- * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
- * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE. */
-
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include "xmalloc.h"
- #include "common.h"
- #include "part.h"
- #include "md5.h"
-
- extern char *os_idtodir();
- extern FILE *os_newtypedfile();
- extern char *md5contextTo64();
-
- /* The possible content transfer encodings */
- enum encoding { enc_none, enc_qp, enc_base64 };
-
- char *ParseHeaders();
- enum encoding parseEncoding();
- params ParseContent();
- char *getParam();
- char *getDispositionFilename();
-
- /*
- * Read and handle an RFC 822 message from the body-part 'inpart'.
- */
- handleMessage(struct part *inpart, char *defaultContentType, int inAppleDouble, int extractText)
- {
- char *headers, *subject, *contentType, *contentDisposition, *contentMD5;
- enum encoding contentEncoding;
- params contentParams;
-
- /* Parse the headers, getting the ones we're interested in */
-
- headers = ParseHeaders(inpart, &subject, &contentType, &contentEncoding,
- &contentDisposition, &contentMD5);
- if (!headers) return 1;
-
-
- /* If no content type, or a non-MIME content type, use the default */
- if (!contentType || !strchr(contentType, '/')) {
- contentType = defaultContentType;
- }
- contentParams = ParseContent(&contentType);
-
-
-
- if (!cistrcmp(contentType, "message/rfc822")) {
- if (contentEncoding != enc_none) {
- warn("ignoring invalid content encoding on message/rfc822");
- }
-
- /* Simple recursion */
- return handleMessage(inpart, "text/plain", 0, extractText);
- }
- else if (!cistrcmp(contentType, "message/partial")) {
- if (contentEncoding != enc_none) {
- warn("ignoring invalid content encoding on message/partial");
- }
- return handlePartial(inpart, headers, contentParams, extractText);
- }
- else if (!cistrncmp(contentType, "message/", 8)) {
- /* Probably message/external. We don't care--toss it */
- return ignoreMessage(inpart);
- }
- else if (!cistrncmp(contentType, "multipart/", 10)) {
- if (contentEncoding != enc_none) {
- warn("ignoring invalid content encoding on multipart");
- }
- return handleMultipart(inpart, contentType, contentParams,
- extractText);
- }
- else if (part_depth(inpart) == 0 &&
- !cistrncmp(contentType, "text/", 5) &&
- contentEncoding == enc_none &&
- !getDispositionFilename(contentDisposition) &&
- !getParam(contentParams, "name")) {
- /* top-level text message, handle as possible uuencoded file */
- return handleUuencode(inpart, subject, extractText);
- }
- else if (!extractText && !inAppleDouble &&
- !cistrncmp(contentType, "text/", 5) &&
- !getDispositionFilename(contentDisposition) &&
- !getParam(contentParams, "name")) {
- return handleText(inpart, contentEncoding);
- }
- else {
- /* Some sort of attachment, extract it */
- return saveToFile(inpart, inAppleDouble, contentType, contentParams,
- contentEncoding, contentDisposition, contentMD5);
- }
- }
-
- /*
- * Skip whitespace and RFC-822 comments.
- */
- SkipWhitespace(char **s)
- {
- char *p = *s;
- int commentlevel = 0;
-
- while (*p && (isspace(*p) || *p == '(')) {
- if (*p == '\n') {
- p++;
- if (*p != ' ' && *p != '\t') {
- *s = 0;
- return;
- }
- }
- else if (*p == '(') {
- p++;
- commentlevel++;
- while (commentlevel) {
- switch (*p) {
- case '\n':
- p++;
- if (*p == ' ' || *p == '\t') break;
- /* FALL THROUGH */
- case '\0':
- *s = 0;
- return;
-
- case '\\':
- p++;
- break;
-
- case '(':
- commentlevel++;
- break;
-
- case ')':
- commentlevel--;
- break;
- }
- p++;
- }
- }
- else p++;
- }
- if (*p == 0) {
- *s = 0;
- }
- else {
- *s = p;
- }
- }
-
- /*
- * Read and parse the headers of an RFC 822 message, returning them in
- * a pointer to a static buffer. The headers are read from 'inpart'.
- * A pointer to the value of any Subject:, Content-Type:,
- * Content-Disposition:, or Content-MD5: header is stored in the space
- * pointed to by 'subjectp', 'contentTypep', contentDispositionp, and
- * contentMD5p, respectively. The Content-Transfer-Encoding is stored
- * in the enum pointed to by 'contentEncodingp'.
- */
- #define HEADGROWSIZE 1000
- char *ParseHeaders(struct part *inpart, char **subjectp, char **contentTypep, enum encoding *contentEncodingp,
- char **contentDispositionp, char **contentMD5p)
- {
- static int alloced = 0;
- static char *headers;
- int left, len, i;
- char *next, *val;
-
- /* Read headers into buffer pointed to by "headers" */
- if (!alloced) {
- headers = xmalloc(alloced = HEADGROWSIZE);
- }
- next = headers;
- *next++ = '\n'; /* Leading newline to make matching header names easier */
- left = alloced - 2; /* Allow room for terminating null */
-
- while (part_gets(next, left, inpart) && (*next != '\n' || next[-1] != '\n')) {
- len = strlen(next);
-
- if (next[-1] == '\n') {
- /* Check for valid header-ness of "next" */
- for (i = 0; i < len; i++) {
- if (next[i] == ':' ||
- next[i] <= ' ' || next[i] >= '\177') break;
- }
- if (i == 0 || next[i] != ':') {
- /* Check for header continuation line */
- if (next == headers+1 || (next[0] != ' ' && next[0] != '\t')) {
- /*
- * Not a valid header, push back on input stream
- * and stop reading input.
- */
- part_ungets(next, inpart);
- break;
- }
- }
- }
-
- left -= len;
- next += len;
-
- if (left < 100) {
- len = next - headers;
- alloced += HEADGROWSIZE;
- left += HEADGROWSIZE;
- headers = xrealloc(headers, alloced);
- next = headers + len;
- }
- }
-
- *next = '\0';
-
- /* Look for the headers we find particularly interesting */
- *subjectp = *contentTypep = *contentDispositionp = *contentMD5p = 0;
- *contentEncodingp = enc_none;
- for (next = headers; *next; next++) {
- if (*next == '\n') {
- switch(next[1]) {
- case 's':
- case 'S':
- if (!cistrncmp(next+2, "ubject:", 7)) {
- val = next+9;
- SkipWhitespace(&val);
- if (val) *subjectp = val;
- }
- break;
-
- case 'c':
- case 'C':
- if (!cistrncmp(next+2, "ontent-type:", 12)) {
- val = next+14;
- SkipWhitespace(&val);
- if (val) *contentTypep = val;
- }
- else if (!cistrncmp(next+2, "ontent-transfer-encoding:", 25)) {
- *contentEncodingp = parseEncoding(next+27);
- }
- else if (!cistrncmp(next+2, "ontent-disposition:", 19)) {
- val = next+21;
- SkipWhitespace(&val);
- if (val) *contentDispositionp = val;
- }
- else if (!cistrncmp(next+2, "ontent-md5:", 11)) {
- val = next+13;
- SkipWhitespace(&val);
- if (val) *contentMD5p = val;
- }
- }
- }
- }
- return headers;
- }
-
- /*
- * Parse the Content-Transfer-Encoding: value pointed to by 's'.
- * Returns the appropriate encoding enum.
- */
- enum encoding parseEncoding(char *s)
- {
- SkipWhitespace(&s);
- if (s) {
- switch (*s) {
- case 'q':
- case 'Q':
- if (!cistrncmp(s+1, "uoted-printable", 15) &&
- (isspace(s[16]) || s[16] == '(')) {
- return enc_qp;
- }
- break;
-
- case '7':
- case '8':
- if (!cistrncmp(s+1, "bit", 3) &&
- (isspace(s[4]) || s[4] == '(')) {
- return enc_none;
- }
- break;
-
- case 'b':
- case 'B':
- if (!cistrncmp(s+1, "ase64", 5) &&
- (isspace(s[6]) || s[6] == '(')) {
- return enc_base64;
- }
- if (!cistrncmp(s+1, "inary", 5) &&
- (isspace(s[6]) || s[6] == '(')) {
- return enc_none;
- }
- }
- warn("ignoring unknown content transfer encoding\n");
- }
- return enc_none;
- }
-
- /*
- * Parse the value of a Content-Type: header.
- * 'headerp' points to a pointer to the input string.
- * The pointer pointed to by 'headerp' is changed to point to
- * a static buffer containing the content type stripped of whitespace
- * and parameters. The parameters are converted to a type suitable for
- * getParm() and returned.
- */
- #define PARAMGROWSIZE 10
- params ParseContent(char **headerp)
- {
- char *header;
- static int palloced = 0;
- static char **param;
- static int calloced = 0;
- static char *cbuf;
- char *p;
- int nparam;
-
- p = header = *headerp;
-
- /* Find end of header, including continuation lines */
- do {
- p = strchr(p+1, '\n');
- } while (p && isspace(p[1]));
- if (!p) {
- p = header + strlen(header);
- }
-
- /* If necessary, allocate/grow cbuf to hold header. */
- if (p - header >= calloced) {
- calloced = p - header + 1;
- if (calloced < 200) calloced = 200;
- cbuf = xrealloc(cbuf, calloced);
- }
-
- /* Copy header to cbuf */
- strncpy(cbuf, header, p - header);
- cbuf[p - header] = 0;
- header = *headerp = cbuf;
-
- nparam = 0;
-
- /* Strip whitespace from content type */
- /* ParseHeaders() stripped leading whitespace */
- p = header;
- while (header && *header && *header != ';') {
- while (*header && !isspace(*header) && *header != '(' &&
- *header != ';') {
- *p++ = *header++;
- }
- SkipWhitespace(&header);
- }
- if (!header || !*header) return 0;
- header++;
- *p = '\0';
-
- /* Parse the parameters */
- while (*header) {
- SkipWhitespace(&header);
- if (!header) break;
-
- if (nparam+1 >= palloced) {
- palloced += PARAMGROWSIZE;
- param = (char **) xrealloc((char *)param, palloced * sizeof(char *));
- }
- param[nparam++] = header;
-
- /* Find any separating semicolon. Pay attention to quoted-strings */
- while (*header && *header != ';') {
- if (*header == '\"') {
- ++header;
- while (*header && *header != '\"') {
- if (*header == '\\') {
- ++header;
- if (!*header) break;
- }
- ++header;
- }
- if (!*header) break;
- }
- else if (*header == '(') {
- /* Convert comments to spaces */
- p = header;
- SkipWhitespace(&p);
- if (!p) {
- break;
- }
- while (header < p) *header++ = ' ';
- header--;
- }
- header++;
- }
- if (*header) *header++ = '\0';
- }
- param[nparam] = 0;
- return param;
- }
-
- /*
- * Get the value of the parameter named 'key' from the content-type
- * parameters 'cParams'. Returns a pointer to a static bufer which
- * contains the value, or null if no such parameter was found.
- */
- #define VALUEGROWSIZE 100
- char *getParam(params cParams, char *key)
- {
- static char *value;
- static int alloced = 0;
- int left;
- int keylen = strlen(key);
- char *from, *to;
-
- if (!cParams) return 0;
-
- if (!alloced) {
- value = xmalloc(alloced = VALUEGROWSIZE);
- }
-
- /* Find the named parameter */
- while (*cParams) {
- if (!cistrncmp(key, *cParams, keylen) &&
- ((*cParams)[keylen] == '=' || isspace((*cParams)[keylen]))) break;
- cParams++;
- }
- if (!*cParams) return 0;
-
- /* Skip over the "=" and any surrounding whitespace */
- from = *cParams + keylen;
- while (*from && isspace(*from)) from++;
- if (*from++ != '=') return 0;
- while (*from && isspace(*from)) from++;
- if (!*from) return 0;
-
- /* Copy value into buffer */
- to = value;
- left = alloced - 1;
- if (*from == '\"') {
- /* Quoted-string */
- from++;
- while (*from && *from != '\"') {
- if (!--left) {
- alloced += VALUEGROWSIZE;
- value = xrealloc(value, alloced);
- to = value + alloced - left - 2;
- }
- if (*from == '\\') {
- from++;
- if (!*from) return 0;
- }
- *to++ = *from++;
- }
- if (!*from) return 0;
- }
- else {
- /* Just a token */
- while (*from && !isspace(*from)) {
- if (!--left) {
- alloced += VALUEGROWSIZE;
- value = xrealloc(value, alloced);
- to = value + alloced - left - 2;
- }
- *to++ = *from++;
- }
- }
- *to = '\0';
- return value;
- }
-
- /*
- * Get the value of the "filename" parameter in a Content-Disposition:
- * header. Returns a pointer to a static buffer containing the value, or
- * a null pointer if there was no such parameter.
- */
- char *
- getDispositionFilename(char *disposition)
- {
- static char *value;
- static int alloced = 0;
- int left;
- char *to;
-
- if (!disposition) return 0;
-
- /* Skip until we find ";" "filename" "=" tokens. */
- for (;;) {
- /* Skip until we find ";" */
- while (*disposition != ';') {
- if (!*disposition) return 0;
- else if (*disposition == '\"') {
- ++disposition;
- while (*disposition && *disposition != '\"') {
- if (*disposition == '\\') {
- ++disposition;
- if (!*disposition) return 0;
- }
- ++disposition;
- }
- if (!*disposition) return 0;
- }
- else if (*disposition == '(') {
- SkipWhitespace(&disposition);
- if (!disposition) return 0;
- disposition--;
- }
- disposition++;
- }
-
- /* Skip over ";" and trailing whitespace */
- disposition++;
- SkipWhitespace(&disposition);
- if (!disposition) return 0;
-
- /*
- * If we're not looking at a "filename" token, go back
- * and look for another ";". Otherwise skip it and
- * trailing whitespace.
- */
- if (cistrncmp(disposition, "filename", 8) != 0) continue;
- disposition += 8;
- if (!isspace(*disposition) && *disposition != '=' &&
- *disposition != '(') {
- continue;
- }
- SkipWhitespace(&disposition);
- if (!disposition) return 0;
-
- /* If we're looking at a ";", we found what we're looking for */
- if (*disposition++ == '=') break;
- }
-
- SkipWhitespace(&disposition);
- if (!disposition) return 0;
-
- if (!alloced) {
- value = xmalloc(alloced = VALUEGROWSIZE);
- }
-
- /* Copy value into buffer */
- to = value;
- left = alloced - 1;
- if (*disposition == '\"') {
- /* Quoted-string */
- disposition++;
- while (*disposition && *disposition != '\"') {
- if (!--left) {
- alloced += VALUEGROWSIZE;
- value = xrealloc(value, alloced);
- to = value + alloced - left - 2;
- }
- if (*disposition == '\\') {
- disposition++;
- if (!*disposition) return 0;
- }
- *to++ = *disposition++;
- }
- if (!*disposition) return 0;
- }
- else {
- /* Just a token */
- while (*disposition && !isspace(*disposition) &&
- *disposition != '(') {
- if (!--left) {
- alloced += VALUEGROWSIZE;
- value = xrealloc(value, alloced);
- to = value + alloced - left - 2;
- }
- *to++ = *disposition++;
- }
- }
- *to = '\0';
- return value;
- }
-
- /*
- * Read and handle a message/partial object from the file 'inpart'.
- */
- handlePartial(struct part *inpart, char *headers, params contentParams, int extractText)
- {
- char *id, *dir, *p;
- int thispart;
- int nparts = 0;
- char buf[1024];
- FILE *partfile, *outfile;
- struct part *outpart;
- int i, docopy;
-
- id = getParam(contentParams, "id");
- if (!id) {
- warn("partial message has no id parameter");
- goto ignore;
- }
-
- /* Get directory to store the parts being reassembled */
- dir = os_idtodir(id);
- if (!dir) goto ignore;
-
- p = getParam(contentParams, "number");
- if (!p) {
- warn("partial message doesn't have number parameter");
- goto ignore;
- }
- thispart = atoi(p);
-
- if (p = getParam(contentParams, "total")) {
- nparts = atoi(p);
- if (nparts <= 0) {
- warn("partial message has invalid number of parts");
- goto ignore;
- }
- /* Store number of parts in reassembly directory */
- sprintf(buf, "%sCT", dir);
- partfile = fopen(buf, "w");
- if (!partfile) {
- os_perror(buf);
- goto ignore;
- }
- fprintf(partfile, "%d\n", nparts);
- if (partfile) fclose(partfile);
- }
- else {
- /* Try to retrieve number of parts from reassembly directory */
- sprintf(buf, "%sCT", dir);
- if (partfile = fopen(buf, "r")) {
- if (fgets(buf, sizeof(buf), partfile)) {
- nparts = atoi(buf);
- if (nparts < 0) nparts = 0;
- }
- if (partfile) fclose(partfile);
- }
- }
-
- /* Sanity check */
- if (thispart <= 0 || (nparts && thispart > nparts)) {
- warn("partial message has invalid number");
- goto ignore;
- }
-
- sprintf(buf, "Saving part %d ", thispart);
- if (nparts) sprintf(buf+strlen(buf), "of %d ", nparts);
- strcat(buf, getParam(contentParams, "id"));
- chat(buf);
-
- /* Create file to store this part */
- sprintf(buf, "%s%d", dir, thispart);
- partfile = fopen(buf, "w");
- if (!partfile) {
- os_perror(buf);
- goto ignore;
- }
-
- /* Do special-case header handling for first part */
- if (thispart == 1) {
- int skippedfirstbyte = 0;
-
- while (*headers) {
- if (*headers == '\n' &&
- (!cistrncmp(headers, "\ncontent-", 9) ||
- !cistrncmp(headers, "\nmessage-id:", 12))) {
- /* Special case, skip header */
- headers++;
- while (*headers && (*headers != '\n' || isspace(headers[1]))) {
- headers++;
- }
- }
- else {
- /* First byte of headers is extra newline, don't write it to file */
- if (skippedfirstbyte++) putc(*headers, partfile);
- headers++;
- }
- }
- docopy = 0;
- /* Handle headers in the multipart/partial body */
- while (part_gets(buf, sizeof(buf), inpart)) {
- if (*buf == '\n') {
- putc('\n', partfile);
- break;
- }
- if (!cistrncmp(buf, "content-", 8) || !cistrncmp(buf, "message-id:", 11)) {
- docopy = 1;
- }
- else if (!isspace(*buf)) {
- docopy = 0;
- }
-
- if (docopy) fputs(buf, partfile);
- while(buf[strlen(buf)-1] != '\n' && part_gets(buf, sizeof(buf), inpart)) {
- if (docopy) fputs(buf, partfile);
- }
- }
- }
-
- /* Copy the contents to the file */
- while (part_gets(buf, sizeof(buf), inpart)) {
- fputs(buf, partfile);
- }
- if (partfile) fclose(partfile);
-
- /* Check to see if we have all parts. Start from the highest numbers
- * as we are more likely not to have them.
- */
- for (i = nparts; i; i--) {
- sprintf(buf, "%s%d", dir, i);
- partfile = fopen(buf, "r");
- if (partfile) {
- fclose(partfile);
- }
- else {
- break;
- }
- }
-
- if (i || !nparts) {
- /* We don't have all the parts yet */
- return 0;
- }
-
- /* We have everything, concatenate all the parts into a single file */
- sprintf(buf, "%sFULL", dir);
- outfile = fopen(buf, "w");
- if (!outfile) {
- os_perror(buf);
- return 1;
- }
- for (i=1; i<=nparts; i++) {
- sprintf(buf, "%s%d", dir, i);
- partfile = fopen(buf, "r");
- if (!partfile) {
- os_perror(buf);
- return 1;
- }
- while (fgets(buf, sizeof(buf), partfile)) {
- fputs(buf, outfile);
- }
- if (partfile) fclose(partfile);
-
- /* Done with message part file, delete it */
- sprintf(buf, "%s%d", dir, i);
- remove(buf);
- }
-
- /* Open the concatenated file for reading and handle it */
- if (outfile) fclose(outfile);
- sprintf(buf, "%sFULL", dir);
- outfile = fopen(buf, "r");
- if (!outfile) {
- os_perror(buf);
- return 1;
- }
- outpart = part_init(outfile);
- handleMessage(outpart, "text/plain", 0, extractText);
- part_close(outpart);
-
- /* Clean up the rest of the reassembly directory */
- sprintf(buf, "%sFULL", dir);
- remove(buf);
- sprintf(buf, "%sCT", dir);
- remove(buf);
- os_donewithdir(dir);
-
- return 0;
-
- ignore:
- ignoreMessage(inpart);
- return 1;
- }
-
- /*
- * Skip over a message object from the file 'inpart'.
- */
- ignoreMessage(struct part *inpart)
- {
- while (part_getc(inpart) != EOF);
- return 0;
- }
-
- /*
- * Read and handle a multipart object from 'inpart'.
- */
- handleMultipart(struct part *inpart, char *contentType, params contentParams, int extractText)
- {
- char *id;
- char *defaultContentType = "text/plain";
- int isAppleDouble = 0;
-
- /* Components of multipart/digest have a different default content-type */
- if (!cistrcmp(contentType, "multipart/digest")) {
- defaultContentType = "message/rfc822";
- }
- if (!cistrcmp(contentType, "multipart/appledouble")) {
- isAppleDouble++;
- }
-
- if (!(id = getParam(contentParams, "boundary"))) {
- warn("multipart message has no boundary parameter");
- id="";
- }
-
- /* Add the new boundary id */
- part_addboundary(inpart, id);
-
- #ifdef __riscos
- /*
- * "Marcel" encodes RISCOS directory structure in the multipart
- * structure. That is the Wrong Way to do it, but we hold our
- * nose and pass the information to the OS layer.
- */
- os_boundaryhookopen(part_depth(inpart));
- #endif
-
- /*
- * Skip over preamble.
- * HACK: The initial boundary doesn't have to start with a newline,
- * so we deal with this by stuffing an initial newline into the input
- * stream
- */
- part_ungetc('\n', inpart);
- ignoreMessage(inpart);
-
- /* Handle the component messages */
- while (!part_readboundary(inpart)) {
- handleMessage(inpart, defaultContentType, isAppleDouble, extractText);
- }
-
- #ifdef __riscos
- os_boundaryhookclose(part_depth(inpart));
- #endif
-
- /* Skip over postamble */
- ignoreMessage(inpart);
-
- /* Remove any lingering unused description file */
- (void) remove(TEMPFILENAME);
-
- return 0;
- }
-
- /*
- * Handle a text message object from 'inpart' by saving it to
- * the temporary description file.
- */
- int handleText(struct part *inpart, enum encoding contentEncoding)
- {
- FILE *descfile;
-
- descfile = fopen(TEMPFILENAME, "w");
- if (!descfile) {
- os_perror(TEMPFILENAME);
- ignoreMessage(inpart);
- return 1;
- }
-
- /* Write the file, handling the appropriate encoding */
- switch (contentEncoding) {
- case enc_none:
- fromnone(inpart, descfile, (char **)0);
- break;
-
- case enc_qp:
- fromqp(inpart, descfile, (char **)0);
- break;
-
- case enc_base64:
- from64(inpart, descfile, (char **)0, 1);
- break;
- }
-
- if (descfile) fclose(descfile);
- return 0;
- }
-
- /*
- * Read a message object from 'inpart' and save it to a file.
- */
- saveToFile(struct part *inpart, int inAppleDouble, char *contentType, params contentParams,
- enum encoding contentEncoding, char *contentDisposition, char *contentMD5)
- {
- FILE *outfile = 0;
- int flags = 0;
- int suppressCR = 0;
- char *outputmd5;
- char *fname;
-
-
- if (!cistrncmp(contentType, "text/", 5)) {
- suppressCR = 1;
- }
- else if (contentEncoding == enc_base64) {
- /*
- * HEURISTIC: It is not in general possible to determine whether
- * any non-text content type is line-oriented. We guess
- * the "binary" status of a part from the composer's choice
- * of content transfer encoding.
- *
- * If the content transfer encoding is "binary" and the input is
- * not line-oriented, we're screwed anyway--the input file has
- * been opened in text mode. So the "binary output file" heuristic
- * is not applied in this case.
- */
- flags |= FILE_BINARY;
- }
-
-
- if (inAppleDouble) flags |= FILE_INAPPLEDOUBLE;
-
- /* Find an appropriate filename and create the output file */
- fname = getDispositionFilename(contentDisposition);
- if (!fname) fname = getParam(contentParams, "name");
- if (fname) fname = strsave(fname);
- outfile = os_newtypedfile(fname, contentType, flags, contentParams);
- if (fname) free(fname);
- if (!outfile) {
- ignoreMessage(inpart);
- return 1;
- }
-
- /* Write the file, handling the appropriate encoding */
- switch (contentEncoding) {
- case enc_none:
- fromnone(inpart, outfile, &outputmd5);
- break;
-
- case enc_qp:
- fromqp(inpart, outfile, &outputmd5);
- break;
-
- case enc_base64:
- from64(inpart, outfile, &outputmd5, suppressCR);
- break;
- }
- rewind(outfile);
-
- /* Check the MD5 digest if it was supplied */
- if (contentMD5) {
- if (strncmp(outputmd5, contentMD5, strlen(outputmd5)) != 0) {
- os_warnMD5mismatch();
- }
- }
- free(outputmd5);
-
- os_closetypedfile(outfile);
- return 0;
- }
-
- #define XX 127
- /*
- * Table for decoding hexadecimal in quoted-printable
- */
- static char index_hex[256] = {
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
- XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- };
- #define HEXCHAR(c) (index_hex[(unsigned char)(c)])
-
- /*
- * Table for decoding base64
- */
- static char index_64[256] = {
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
- 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
- XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
- 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
- XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
- 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
- };
- #define CHAR64(c) (index_64[(unsigned char)(c)])
-
- from64(struct part *inpart, FILE *outfile, char **digestp, int suppressCR)
- {
- int c1, c2, c3, c4;
- int DataDone = 0;
- char buf[3];
- MD5_CTX context;
-
- if (digestp) MD5Init(&context);
- while ((c1 = part_getc(inpart)) != EOF) {
- if (c1 != '=' && CHAR64(c1) == XX) {
- continue;
- }
- if (DataDone) continue;
- do {
- c2 = part_getc(inpart);
- } while (c2 != EOF && c2 != '=' && CHAR64(c2) == XX);
- do {
- c3 = part_getc(inpart);
- } while (c3 != EOF && c3 != '=' && CHAR64(c3) == XX);
- do {
- c4 = part_getc(inpart);
- } while (c4 != EOF && c4 != '=' && CHAR64(c4) == XX);
- if (c2 == EOF || c3 == EOF || c4 == EOF) {
- warn("Premature EOF");
- break;
- }
- if (c1 == '=' || c2 == '=') {
- DataDone=1;
- continue;
- }
- c1 = CHAR64(c1);
- c2 = CHAR64(c2);
- buf[0] = ((c1<<2) | ((c2&0x30)>>4));
- if (!suppressCR || buf[0] != '\r') putc(buf[0], outfile);
- if (c3 == '=') {
- if (digestp) MD5Update(&context, buf, 1);
- DataDone = 1;
- } else {
- c3 = CHAR64(c3);
- buf[1] = (((c2&0x0F) << 4) | ((c3&0x3C) >> 2));
- if (!suppressCR || buf[1] != '\r') putc(buf[1], outfile);
- if (c4 == '=') {
- if (digestp) MD5Update(&context, buf, 2);
- DataDone = 1;
- } else {
- c4 = CHAR64(c4);
- buf[2] = (((c3&0x03) << 6) | c4);
- if (!suppressCR || buf[2] != '\r') putc(buf[2], outfile);
- if (digestp) MD5Update(&context, buf, 3);
- }
- }
- }
- if (digestp) *digestp = md5contextTo64(&context);
- }
-
- fromqp(struct part *inpart, FILE *outfile, char **digestp)
- {
- int c1, c2;
- MD5_CTX context;
- char c;
-
- if (digestp) MD5Init(&context);
-
- while ((c1 = part_getc(inpart)) != EOF) {
- if (c1 == '=') {
- c1 = part_getc(inpart);
- if (c1 != '\n') {
- c1 = HEXCHAR(c1);
- c2 = part_getc(inpart);
- c2 = HEXCHAR(c2);
- c = c1<<4 | c2;
- if (c != '\r') putc(c, outfile);
- if (digestp) MD5Update(&context, &c, 1);
- }
- } else {
- putc(c1, outfile);
- if (c1 == '\n') {
- if (digestp) MD5Update(&context, "\r", 1);
- }
- c = c1;
- if (digestp) MD5Update(&context, &c, 1);
- }
- }
- if (digestp) *digestp=md5contextTo64(&context);
- }
-
- fromnone(struct part *inpart, FILE *outfile, char **digestp)
- {
- int c;
- char ch;
- MD5_CTX context;
-
- if (digestp) MD5Init(&context);
-
- while ((c = part_getc(inpart)) != EOF) {
- putc(c, outfile);
- if (c == '\n') {
- if (digestp) MD5Update(&context, "\r", 1);
- }
- ch = c;
- if (digestp) MD5Update(&context, &ch, 1);
- }
- if (digestp) *digestp=md5contextTo64(&context);
- }
-
-