summaryrefslogtreecommitdiffstats
path: root/rfc2045/rfc2045.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /rfc2045/rfc2045.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'rfc2045/rfc2045.c')
-rw-r--r--rfc2045/rfc2045.c1375
1 files changed, 1375 insertions, 0 deletions
diff --git a/rfc2045/rfc2045.c b/rfc2045/rfc2045.c
new file mode 100644
index 0000000..2245f43
--- /dev/null
+++ b/rfc2045/rfc2045.c
@@ -0,0 +1,1375 @@
+/*
+** Copyright 1998 - 2004 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+/*
+*/
+#if HAVE_CONFIG_H
+#include "rfc2045_config.h"
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <ctype.h>
+#include "rfc2045.h"
+#include "rfc822/rfc822.h"
+#include "rfc2045charset.h"
+
+static char *rfc2045_defcharset=0;
+
+int rfc2045_in_reformime=0;
+
+extern void rfc2045_enomem();
+
+#define MAXLEVELS 20
+#define MAXPARTS 300
+
+/*
+ New RFC2045 structure.
+*/
+
+struct rfc2045 *rfc2045_alloc()
+{
+struct rfc2045 *p=(struct rfc2045 *)malloc(sizeof(struct rfc2045));
+
+ if (!p)
+ {
+ rfc2045_enomem();
+ return (0);
+ }
+
+ /* Initialize everything to nulls, except for one thing */
+
+ memset(p, '\0', sizeof(*p));
+
+ p->pindex=1; /* Start with part #1 */
+ p->workinheader=1;
+ /* Most of the time, we're about to read a header */
+
+ return (p);
+}
+
+const char *rfc2045_getattr(const struct rfc2045attr *p, const char *name)
+{
+ while (p)
+ {
+ if (p->name && strcmp(p->name, name) == 0)
+ return (p->value);
+ p=p->next;
+ }
+ return (0);
+}
+
+int rfc2045_attrset(struct rfc2045attr **p, const char *name, const char *val)
+{
+char *v;
+
+ while (*p)
+ {
+ if (strcmp( (*p)->name, name) == 0) break;
+ p=&(*p)->next;
+ }
+ if (val == 0)
+ {
+ struct rfc2045attr *q= *p;
+
+ if (q)
+ {
+ *p=q->next;
+ if (q->name) free(q->name);
+ if (q->value) free(q->value);
+ free(q);
+ }
+ return 0;
+ }
+
+ v=strdup(val);
+ if (!v)
+ return -1;
+
+ if (!*p)
+ {
+ if (((*p)=(struct rfc2045attr *)malloc(sizeof(**p))) == 0)
+ {
+ free(v);
+ return -1;
+ }
+ memset( (*p), 0, sizeof(**p));
+ if ( ((*p)->name=strdup(name)) == 0)
+ {
+ free( *p );
+ *p=0;
+ free(v);
+ return -1;
+ }
+ }
+ if ( (*p)->value ) free ( (*p)->value );
+ (*p)->value=v;
+ return 0;
+}
+
+/* static const char cb_name[]="boundary"; */
+
+/* #define ContentBoundary(p) (rfc2045_getattr( (p)->content_type_attr, cb_name)) */
+
+#define ContentBoundary(p) ( (p)->boundary )
+
+/*
+ Unallocate the RFC2045 structure. Recursively unallocate
+ all sub-structures. Unallocate all associated buffers.
+*/
+
+static void rfc2045_freeattr(struct rfc2045attr *p)
+{
+ while (p)
+ {
+ struct rfc2045attr *q=p->next;
+
+ if (p->name) free(p->name);
+ if (p->value) free(p->value);
+ free(p);
+ p=q;
+ }
+}
+
+void rfc2045_free(struct rfc2045 *p)
+{
+struct rfc2045 *q, *r;
+
+ for (q=p->firstpart; q; )
+ {
+ r=q->next;
+ rfc2045_free(q);
+ q=r;
+ }
+ rfc2045_freeattr(p->content_type_attr);
+ rfc2045_freeattr(p->content_disposition_attr);
+
+ if (p->header) free(p->header);
+ if (p->content_md5) free(p->content_md5);
+ if (p->content_base) free(p->content_base);
+ if (p->content_location) free(p->content_location);
+ if (p->content_language) free(p->content_language);
+ if (p->content_id) free(p->content_id);
+ if (p->content_description) free(p->content_description);
+ if (p->content_transfer_encoding) free(p->content_transfer_encoding);
+ if (p->boundary) free(p->boundary);
+ if (p->content_type) free(p->content_type);
+ if (p->mime_version) free(p->mime_version);
+ if (p->workbuf) free(p->workbuf);
+ if (p->content_disposition) free(p->content_disposition);
+ if (p->rw_transfer_encoding) free(p->rw_transfer_encoding);
+ free(p);
+}
+
+/*
+ Generic dynamic buffer append.
+*/
+
+void rfc2045_add_buf(
+ char **bufptr, /* Buffer */
+ size_t *bufsize, /* Buffer's maximum size */
+ size_t *buflen, /* Buffer's current size */
+
+ const char *p, size_t len) /* Append this data */
+{
+ if (len + *buflen > *bufsize)
+ {
+ size_t newsize=len+*buflen+256;
+ char *p= *bufptr ? (char *)realloc(*bufptr, newsize):
+ (char *)malloc(newsize);
+
+ if (!p)
+ {
+ rfc2045_enomem();
+ return;
+ }
+ *bufptr=p;
+ *bufsize=newsize;
+ }
+
+ memcpy(*bufptr + *buflen, p, len);
+ *buflen += len;
+}
+
+/* Append to the work buffer */
+
+void rfc2045_add_workbuf(struct rfc2045 *h, const char *p, size_t len)
+{
+ rfc2045_add_buf( &h->workbuf, &h->workbufsize, &h->workbuflen, p, len);
+}
+
+/* Append one character to the work buffer */
+
+void rfc2045_add_workbufch(struct rfc2045 *h, int c)
+{
+char cc= (char)c;
+
+ rfc2045_add_workbuf(h, &cc, 1);
+}
+
+/*
+ Generic function to duplicate contents of a string.
+ The destination string may already be previously allocated,
+ so unallocate it.
+*/
+
+static void set_string(char **p,
+ const char *q)
+{
+ if (*p) free(*p);
+
+ *p=0;
+ if (!q) return;
+
+ if ((*p=(char *)malloc(strlen(q)+1)) == 0)
+ {
+ rfc2045_enomem();
+ return;
+ }
+
+ strcpy(*p, q);
+}
+
+/* Update byte counts for this structure, and all the superstructures */
+
+static void update_counts(struct rfc2045 *p, size_t newcnt, size_t newendcnt,
+ unsigned nlines)
+{
+ while (p)
+ {
+ p->endpos = newcnt;
+ p->endbody = newendcnt;
+ p->nlines += nlines;
+ if (!p->workinheader)
+ p->nbodylines += nlines;
+ p=p->parent;
+ }
+}
+
+/*
+ Main entry point for RFC2045 parsing. External data is fed
+ by repetitively calling rfc2045_parse().
+
+ rfc2045_parse() breaks up input into lines, and calls doline()
+ to process each line.
+*/
+
+static void doline(struct rfc2045 *);
+
+void rfc2045_parse_partial(struct rfc2045 *h);
+
+void rfc2045_parse(struct rfc2045 *h, const char *buf, size_t s)
+{
+ size_t l;
+
+ while (s)
+ {
+ for (l=0; l<s; l++)
+ if (buf[l] == '\n') break;
+ if (l < s && buf[l] == '\n')
+ {
+ ++l;
+ rfc2045_add_workbuf(h, buf, l);
+ doline(h);
+ h->workbuflen=0;
+ }
+ else
+ rfc2045_add_workbuf(h, buf, l);
+ buf += l;
+ s -= l;
+ }
+
+ if (h->workbuflen > 1024)
+ rfc2045_parse_partial(h);
+}
+
+void rfc2045_parse_partial(struct rfc2045 *h)
+{
+ /*
+ ** Our buffer's getting pretty big. Let's see if we can
+ ** partially handle it.
+ */
+
+ if (h->workbuflen > 0)
+ {
+ struct rfc2045 *p;
+ int l, i;
+
+ for (p=h; p->lastpart && !p->lastpart->workclosed;
+ p=p->lastpart)
+ ;
+
+ /* If p->workinheader, we've got a mother of all headers
+ ** here. Well, that's just too bad, we'll end up garbling
+ ** it.
+ */
+
+ l=h->workbuflen;
+
+ /* We do need to make sure that the final \r\n gets
+ ** stripped off, so don't gobble up everything if
+ ** the last character we see is a \r
+ */
+
+ if (h->workbuf[l-1] == '\r')
+ --l;
+
+ /* If we'll be rewriting, make sure rwprep knows about
+ ** stuff that was skipped just now. */
+
+ if (h->rfc2045acptr && !p->workinheader &&
+ (!p->lastpart || !p->lastpart->workclosed))
+ (*h->rfc2045acptr->section_contents)(h->workbuf, l);
+
+ update_counts(p, p->endpos+l, p->endpos+l, 0);
+ p->informdata=1;
+ for (i=0; l<h->workbuflen; l++)
+ h->workbuf[i++]=h->workbuf[l];
+ h->workbuflen=i;
+ }
+}
+
+/*
+ Append a new RFC2045 subpart. Adds new RFC2045 structure to the
+ end of the list of existing RFC2045 substructures.
+*/
+
+static struct rfc2045 *append_part_noinherit(struct rfc2045 *p, size_t startpos){
+struct rfc2045 *newp;
+
+ newp=rfc2045_alloc();
+ if (p->lastpart)
+ {
+ p->lastpart->next=newp;
+ newp->pindex=p->lastpart->pindex+1;
+ }
+ else
+ {
+ p->firstpart=newp;
+ newp->pindex=0;
+ }
+ p->lastpart=newp;
+ newp->parent=p;
+
+ /* Initialize source pointers */
+ newp->startpos=newp->endpos=newp->startbody=newp->endbody=startpos;
+
+ while (p->parent)
+ p=p->parent;
+ ++p->numparts;
+
+ return (newp);
+}
+
+static struct rfc2045 *append_part(struct rfc2045 *p, size_t startpos)
+{
+struct rfc2045 *newp=append_part_noinherit(p, startpos);
+
+ /* Substructures inherit content transfer encoding and character set */
+
+ set_string(&newp->content_transfer_encoding,
+ p->content_transfer_encoding);
+
+ if (rfc2045_attrset(&newp->content_type_attr, "charset",
+ rfc2045_getattr(p->content_type_attr, "charset"))
+ < 0)
+ rfc2045_enomem();
+
+ return (newp);
+}
+
+/*
+ doline() processes next line in the RFC2045 message.
+
+ Drills down the list of all the multipart messages currently open,
+ and checks if the line is a boundary line for the given multipart.
+ In theory the boundary line, if there is one, should be the boundary
+ line only for the inner multipart only, but, this takes into account
+ broken MIME messages.
+*/
+
+static void do_header(struct rfc2045 *);
+
+static void doline(struct rfc2045 *p)
+{
+size_t cnt=p->workbuflen;
+char *c=p->workbuf;
+size_t n=cnt-1; /* Strip \n (we always get at least a \n here) */
+struct rfc2045 *newp;
+struct rfc2045ac *rwp=p->rfc2045acptr;
+unsigned num_levels=0;
+
+size_t k;
+int bit8=0;
+
+ if (p->numparts > MAXPARTS)
+ {
+ p->rfcviolation |= RFC2045_ERR2COMPLEX;
+ return;
+ }
+
+ for (k=0; k<cnt; k++)
+ {
+ if (c[k] == 0)
+ c[k]=' ';
+ if (c[k] & 0x80) bit8=1;
+ }
+
+ if (n && c[n-1] == '\r') /* Strip trailing \r */
+ --n;
+
+ /* Before the main drill down loop before, look ahead and see if we're
+ ** in a middle of a form-data section. */
+
+ for (newp=p; newp->lastpart &&
+ !newp->lastpart->workclosed; newp=newp->lastpart,
+ ++num_levels)
+ {
+ if (ContentBoundary(newp) == 0 || newp->workinheader)
+ continue;
+
+ if (newp->lastpart->informdata)
+ {
+ p=newp->lastpart;
+ p->informdata=0;
+ break;
+ }
+ }
+
+ /* Drill down until we match a boundary, or until we've reached
+ the last RFC2045 section that has been opened.
+ */
+
+ while (p->lastpart)
+ {
+ size_t l;
+ const char *cb;
+
+ if (p->lastpart->workclosed)
+ {
+ update_counts(p, p->endpos+cnt, p->endpos+n, 1);
+ return;
+ }
+ /* Leftover trash -- workclosed is set when the final
+ ** terminating boundary has been seen */
+
+ /* content_boundary may be set before the entire header
+ ** has been seen, so continue drilling down in that case
+ */
+
+ cb=ContentBoundary(p);
+
+ if (cb == 0 || p->workinheader)
+ {
+ p=p->lastpart;
+ ++num_levels;
+ continue;
+ }
+
+ l=strlen(cb);
+
+ if (c[0] == '-' && c[1] == '-' && n >= 2+l &&
+ strncasecmp(cb, c+2, l) == 0)
+ {
+
+ if (rwp && (!p->lastpart || !p->lastpart->isdummy))
+ (*rwp->end_section)();
+
+ /* Ok, we've found a boundary */
+
+ if (n >= 4+l && strncmp(c+2+l, "--", 2) == 0)
+ {
+ /* Last boundary */
+
+ p->lastpart->workclosed=1;
+ update_counts(p, p->endpos+cnt, p->endpos+cnt,
+ 1);
+ return;
+ }
+
+ /* Create new RFC2045 section */
+
+ newp=append_part(p, p->endpos+cnt);
+ update_counts(p, p->endpos+cnt, p->endpos+n, 1);
+
+ /* The new RFC2045 section is MIME compliant */
+
+ if ((newp->mime_version=strdup(p->mime_version)) == 0)
+ rfc2045_enomem();
+ return;
+ }
+ p=p->lastpart;
+ ++num_levels;
+ }
+
+ /* Ok, we've found the RFC2045 section that we're working with.
+ ** No what?
+ */
+
+ if (! p->workinheader)
+ {
+ /* Processing body, just update the counts. */
+
+ size_t cnt_update=cnt;
+
+ if (bit8 && !p->content_8bit &&
+ (p->rfcviolation & RFC2045_ERR8BITCONTENT) == 0)
+ {
+ struct rfc2045 *q;
+
+ for (q=p; q; q=q->parent)
+ q->rfcviolation |= RFC2045_ERR8BITCONTENT;
+ }
+
+ /*
+ ** In multiparts, the final newline in a part belongs to the
+ ** boundary, otherwise, include it in the text.
+ */
+ if (p->parent && p->parent->content_type &&
+ strncasecmp(p->parent->content_type,
+ "multipart/", 10) == 0)
+ cnt_update=n;
+
+ if (!p->lastpart || !p->lastpart->workclosed)
+ {
+ if (rwp && !p->isdummy)
+ (*rwp->section_contents)(c, cnt);
+
+ update_counts(p, p->endpos+cnt, p->endpos+cnt_update,
+ 1);
+ }
+ return;
+ }
+
+ if (bit8 && (p->rfcviolation & RFC2045_ERR8BITHEADER) == 0)
+ {
+ struct rfc2045 *q;
+
+ for (q=p; q; q=q->parent)
+ q->rfcviolation |= RFC2045_ERR8BITHEADER;
+ }
+
+ /* In the header */
+
+ if ( n == 0 ) /* End of header, body begins. Parse header. */
+ {
+ do_header(p); /* Clean up any left over header line */
+ p->workinheader=0;
+
+ /* Message body starts right here */
+
+ p->startbody=p->endpos+cnt;
+ update_counts(p, p->startbody, p->startbody, 1);
+ --p->nbodylines; /* Don't count the blank line */
+
+ /* Discard content type and boundary if I don't understand
+ ** this MIME flavor.
+ */
+
+ if (!RFC2045_ISMIME1(p->mime_version))
+ {
+ set_string(&p->content_type, 0);
+
+ rfc2045_freeattr(p->content_type_attr);
+ p->content_type_attr=0;
+ set_string(&p->content_disposition, 0);
+ rfc2045_freeattr(p->content_disposition_attr);
+ p->content_disposition_attr=0;
+ if (p->boundary)
+ {
+ free(p->boundary);
+ p->boundary=0;
+ }
+ }
+
+ /* Normally, if we don't have a content_type, default it
+ ** to text/plain. However, if the multipart type is
+ ** multipart/digest, it is message/rfc822.
+ */
+
+ if (RFC2045_ISMIME1(p->mime_version) && !p->content_type)
+ {
+ char *q="text/plain";
+
+ if (p->parent && p->parent->content_type &&
+ strcmp(p->parent->content_type,
+ "multipart/digest") == 0)
+ q="message/rfc822";
+ set_string(&p->content_type, q);
+ }
+
+ /* If this is not a multipart section, we don't want to
+ ** hear about any boundaries
+ */
+
+ if (!p->content_type ||
+ strncmp(p->content_type, "multipart/", 10))
+ {
+ if (p->boundary)
+ free(p->boundary);
+ p->boundary=0;
+ }
+
+ /* If this section's a message, we will expect to see
+ ** more RFC2045 stuff, so create a nested RFC2045 structure,
+ ** and indicate that we expect to see headers.
+ */
+
+ if (p->content_type &&
+ strcmp(p->content_type, "message/rfc822") == 0)
+ {
+ newp=append_part_noinherit(p, p->startbody);
+ newp->workinheader=1;
+ return;
+ }
+
+ /*
+ ** If this is a multipart message (boundary defined),
+ ** create a RFC2045 structure for the pseudo-section
+ ** that precedes the first boundary line.
+ */
+
+ if (ContentBoundary(p))
+ {
+ newp=append_part(p, p->startbody);
+ newp->workinheader=0;
+ newp->isdummy=1;
+ /* It's easier just to create it. */
+ return;
+ }
+
+ if (rwp)
+ (*rwp->start_section)(p);
+ return;
+ }
+
+ /* RFC822 header continues */
+
+ update_counts(p, p->endpos + cnt, p->endpos+n, 1);
+
+ /* If this header line starts with a space, append one space
+ ** to the saved contents of the previous line, and append this
+ ** line to it.
+ */
+
+ if (isspace((int)(unsigned char)*c))
+ {
+ rfc2045_add_buf(&p->header, &p->headersize, &p->headerlen, " ", 1);
+ }
+ else
+ {
+ /* Otherwise the previous header line is complete, so process it */
+
+ do_header(p);
+ p->headerlen=0;
+ }
+
+ /* Save this line in the header buffer, because the next line
+ ** could be a continuation.
+ */
+
+ rfc2045_add_buf( &p->header, &p->headersize, &p->headerlen, c, n);
+}
+
+/***********************************************************************/
+
+/*
+** paste_tokens() - recombine an array of RFC822 tokens back as a string.
+** (Comments) are ignored.
+*/
+
+static char *paste_tokens(struct rfc822t *h, int start, int cnt)
+{
+int l;
+int i;
+char *p;
+
+ /* Calculate string size */
+
+ l=1;
+ for (i=0; i<cnt; i++)
+ {
+ if (h->tokens[start+i].token == '(')
+ continue;
+
+ if (rfc822_is_atom(h->tokens[start+i].token))
+ l += h->tokens[start+i].len;
+ else
+ l++;
+ }
+
+ /* Do it */
+
+ p=( char *)malloc(l);
+ if (!p)
+ {
+ rfc2045_enomem();
+ return (0);
+ }
+ l=0;
+
+ for (i=0; i<cnt; i++)
+ {
+ if (h->tokens[start+i].token == '(')
+ continue;
+
+ if (rfc822_is_atom(h->tokens[start+i].token))
+ {
+ int l2=h->tokens[start+i].len;
+
+ memcpy(p+l, h->tokens[start+i].ptr, l2);
+ l += l2;
+ }
+ else p[l++]=h->tokens[start+i].token;
+ }
+ p[l]=0;
+ return (p);
+}
+
+/* Various permutations of the above, including forcing the string to
+** lowercase
+*/
+
+static char *lower_paste_tokens(struct rfc822t *h, int start, int cnt)
+{
+char *p=paste_tokens(h, start, cnt);
+char *q;
+
+ for (q=p; q && *q; q++)
+ *q=tolower(*q);
+ return (p);
+}
+
+static char *paste_token(struct rfc822t *h, int i)
+{
+ if (i >= h->ntokens) return (0);
+ return (paste_tokens(h, i, 1));
+}
+
+static char *lower_paste_token(struct rfc822t *h, int i)
+{
+char *p=paste_token(h, i);
+char *q;
+
+ for (q=p; q && *q; q++)
+ *q=tolower(*q);
+ return (p);
+}
+
+/*
+ do_header() - process completed RFC822 header.
+*/
+
+static void mime_version(struct rfc2045 *, struct rfc822t *);
+static void content_type(struct rfc2045 *, struct rfc822t *);
+static void content_transfer_encoding(struct rfc2045 *, struct rfc822t *);
+static void content_disposition(struct rfc2045 *, struct rfc822t *);
+static void content_id(struct rfc2045 *, struct rfc822t *);
+static void content_description(struct rfc2045 *, const char *);
+static void content_language(struct rfc2045 *, const char *);
+static void content_md5(struct rfc2045 *, const char *);
+static void content_base(struct rfc2045 *, struct rfc822t *);
+static void content_location(struct rfc2045 *, struct rfc822t *);
+
+static void do_header(struct rfc2045 *p)
+{
+struct rfc822t *header;
+char *t;
+
+ if (p->headerlen == 0) return;
+ rfc2045_add_buf( &p->header, &p->headersize, &p->headerlen, "", 1);
+ /* 0 terminate */
+
+ /* Parse the header line according to RFC822 */
+
+ header=rfc822t_alloc_new(p->header, NULL, NULL);
+
+ if (!header) return; /* Broken header */
+
+ if (header->ntokens < 2 ||
+ header->tokens[0].token ||
+ header->tokens[1].token != ':')
+ {
+ rfc822t_free(header);
+ return; /* Broken header */
+ }
+
+ t=lower_paste_token(header, 0);
+
+ if (t == 0)
+ ;
+ else if (strcmp(t, "mime-version") == 0)
+ {
+ free(t);
+ mime_version(p, header);
+ }
+ else if (strcmp(t, "content-type") == 0)
+ {
+ free(t);
+ content_type(p, header);
+ } else if (strcmp(t, "content-transfer-encoding") == 0)
+ {
+ free(t);
+ content_transfer_encoding(p, header);
+ } else if (strcmp(t, "content-disposition") == 0)
+ {
+ free(t);
+ content_disposition(p, header);
+ } else if (strcmp(t, "content-id") == 0)
+ {
+ free(t);
+ content_id(p, header);
+ } else if (strcmp(t, "content-description") == 0)
+ {
+ free(t);
+ t=strchr(p->header, ':');
+ if (t) ++t;
+ while (t && isspace((int)(unsigned char)*t))
+ ++t;
+ content_description(p, t);
+ } else if (strcmp(t, "content-language") == 0)
+ {
+ free(t);
+ t=strchr(p->header, ':');
+ if (t) ++t;
+ while (t && isspace((int)(unsigned char)*t))
+ ++t;
+ content_language(p, t);
+ } else if (strcmp(t, "content-base") == 0)
+ {
+ free(t);
+ content_base(p, header);
+ } else if (strcmp(t, "content-location") == 0)
+ {
+ free(t);
+ content_location(p, header);
+ } else if (strcmp(t, "content-md5") == 0)
+ {
+ free(t);
+ t=strchr(p->header, ':');
+ if (t) ++t;
+ while (t && isspace((int)(unsigned char)*t))
+ ++t;
+ content_md5(p, t);
+ }
+ else free(t);
+ rfc822t_free(header);
+}
+
+/* Mime-Version: and Content-Transfer-Encoding: headers are easy */
+
+static void mime_version(struct rfc2045 *p, struct rfc822t *header)
+{
+char *vers=paste_tokens(header, 2, header->ntokens-2);
+
+ if (!vers) return;
+
+ if (p->mime_version) free(p->mime_version);
+ p->mime_version=vers;
+}
+
+static void content_transfer_encoding(struct rfc2045 *r,
+ struct rfc822t *header)
+{
+char *p;
+
+ p=lower_paste_tokens(header, 2, header->ntokens-2);
+ if (!p) return;
+
+ if (r->content_transfer_encoding)
+ free(r->content_transfer_encoding);
+ r->content_transfer_encoding=p;
+
+ if (strcmp(p, "8bit") == 0)
+ r->content_8bit=1;
+}
+
+/* Dig into the content_type header */
+
+static void parse_content_header(struct rfc822t *header,
+ int init_start,
+ void (*init_token)(char *, void *),
+ void (*init_parameter)(const char *,
+ struct rfc822t *,
+ int, int,
+ void *),
+ void *void_arg)
+{
+int start;
+int i, j;
+char *p;
+
+ /* Look for the 1st ; */
+
+ for (start=init_start; start < header->ntokens; start++)
+ if (header->tokens[start].token == ';')
+ break;
+
+ /* Everything up to the 1st ; is the content type */
+
+ p=lower_paste_tokens(header, init_start, start-init_start);
+ if (!p) return;
+
+ (*init_token)(p, void_arg);
+ if (start < header->ntokens) start++;
+
+ /* Handle the remainder of the Content-Type: header */
+
+ while (start < header->ntokens)
+ {
+ /* Look for next ; */
+
+ for (i=start; i<header->ntokens; i++)
+ if (header->tokens[i].token == ';')
+ break;
+ j=start;
+ if (j < i)
+ {
+ ++j;
+
+ /* We only understand <atom>= */
+
+ while (j < i && header->tokens[j].token == '(')
+ ++j;
+ if (j < i && header->tokens[j].token == '=')
+ {
+ ++j;
+
+ /*
+ ** reformime: loose parsing due to loose
+ ** parsing in MSOE, leading to viruses slipping
+ ** through virus scanners if we strictly
+ ** parsed the content-type header.
+ */
+ if (rfc2045_in_reformime && j < i
+ && header->tokens[j].token == '"')
+ i=j+1;
+
+ p=lower_paste_token(header, start);
+ if (!p) return;
+ (*init_parameter)(p, header, j, i-j, void_arg);
+ free(p);
+ }
+ }
+ if ( i<header->ntokens ) ++i; /* Skip over ; */
+ start=i;
+ }
+}
+
+/* Dig into the content_type header */
+
+static void save_content_type(char *, void *);
+static void save_content_type_parameter( const char *,
+ struct rfc822t *, int, int, void *);
+
+static void content_type(struct rfc2045 *r, struct rfc822t *header)
+{
+ parse_content_header(header, 2, &save_content_type,
+ &save_content_type_parameter, r);
+}
+
+static void save_content_type(char *content_type, void *void_arg)
+{
+ struct rfc2045 *r=(struct rfc2045 *)void_arg;
+
+ if (r->content_type) free(r->content_type);
+ r->content_type=content_type;
+}
+
+static void save_content_type_parameter(const char *name,
+ struct rfc822t *header, int start,
+ int len, void *void_arg)
+{
+ struct rfc2045 *r=(struct rfc2045 *)void_arg;
+ char *p;
+
+ p=strcmp(name, "charset") == 0 ?
+ lower_paste_tokens(header, start, len):
+ paste_tokens(header, start, len);
+ if (!p) return;
+
+ if (rfc2045_attrset(&r->content_type_attr, name, p) < 0)
+ {
+ free(p);
+ rfc2045_enomem();
+ }
+
+ free(p);
+
+ if (strcmp(name, "boundary") == 0)
+ {
+ struct rfc2045 *q;
+
+ if (r->boundary)
+ free(r->boundary);
+ p=lower_paste_tokens(header, start, len);
+ r->boundary=p;
+
+ /*
+ ** Check all the outer MIME boundaries. If this is a
+ ** substring of an outer MIME boundary, or the outer
+ ** boundary is a substring of the inner boundary, we
+ ** have an ambiguity - see "IMPLEMENTOR'S NOTE" in
+ ** section 5.1.1 of RFC 2046.
+ */
+
+ for (q=r->parent; q; q=q->parent)
+ {
+ const char *a, *b;
+
+ if (!q->boundary)
+ continue;
+
+ for (a=q->boundary, b=p; *a && *b; a++, b++)
+ if (*a != *b)
+ break;
+
+ if (!*a || !*b)
+ {
+ while (q->parent)
+ q=q->parent;
+ q->rfcviolation |= RFC2045_ERRBADBOUNDARY;
+ break;
+ }
+ }
+ }
+}
+
+/* Dig into content-disposition */
+
+static void save_content_disposition(char *, void *);
+static void save_content_disposition_parameter( const char *,
+ struct rfc822t *, int, int,
+ void *);
+
+static void content_disposition(struct rfc2045 *r, struct rfc822t *header)
+{
+ parse_content_header(header, 2, &save_content_disposition,
+ &save_content_disposition_parameter, r);
+}
+
+static void save_content_disposition(char *content_disposition, void *void_arg)
+{
+ struct rfc2045 *r=(struct rfc2045 *)void_arg;
+
+ if (r->content_disposition) free(r->content_disposition);
+ r->content_disposition=content_disposition;
+}
+
+static void save_content_disposition_parameter(const char *name,
+ struct rfc822t *header,
+ int start, int len,
+ void *void_arg)
+{
+ struct rfc2045 *r=(struct rfc2045 *)void_arg;
+ char *p;
+
+ p=paste_tokens(header, start, len);
+ if (!p) return;
+
+ if (rfc2045_attrset(&r->content_disposition_attr, name, p) < 0)
+ {
+ free(p);
+ rfc2045_enomem();
+ }
+ free(p);
+}
+
+char *rfc2045_related_start(const struct rfc2045 *p)
+{
+const char *cb=rfc2045_getattr( p->content_type_attr, "start");
+struct rfc822t *t;
+struct rfc822a *a;
+int i;
+
+ if (!cb || !*cb) return (0);
+
+ t=rfc822t_alloc_new(cb, 0, NULL);
+ if (!t)
+ {
+ rfc2045_enomem();
+ return(0);
+ }
+
+ a=rfc822a_alloc(t);
+ if (!a)
+ {
+ rfc822t_free(t);
+ rfc2045_enomem();
+ return (0);
+ }
+ for (i=0; i<a->naddrs; i++)
+ if (a->addrs[i].tokens)
+ {
+ char *s=rfc822_getaddr(a, i);
+
+ rfc822a_free(a);
+ rfc822t_free(t);
+ if (!s)
+ rfc2045_enomem();
+ return (s);
+ }
+
+ rfc822a_free(a);
+ rfc822t_free(t);
+ return (0);
+}
+
+static void content_id(struct rfc2045 *p, struct rfc822t *t)
+{
+struct rfc822a *a=rfc822a_alloc(t);
+int i;
+
+ if (!a)
+ {
+ rfc2045_enomem();
+ return;
+ }
+
+ for (i=0; i<a->naddrs; i++)
+ if (a->addrs[i].tokens)
+ {
+ char *s=rfc822_getaddr(a, i);
+
+ if (!s)
+ {
+ rfc822a_free(a);
+ rfc2045_enomem();
+ return;
+ }
+ if (p->content_id)
+ free(p->content_id);
+ p->content_id=s;
+ break;
+ }
+
+ rfc822a_free(a);
+}
+
+static void content_description(struct rfc2045 *p, const char *s)
+{
+ if (s && *s)
+ set_string(&p->content_description, s);
+}
+
+static void content_language(struct rfc2045 *p, const char *s)
+{
+ if (s && *s)
+ set_string(&p->content_language, s);
+}
+
+static void content_md5(struct rfc2045 *p, const char *s)
+{
+ if (s && *s)
+ set_string(&p->content_md5, s);
+}
+
+static void content_base(struct rfc2045 *p, struct rfc822t *t)
+{
+char *s;
+int i;
+
+ for (i=0; i<t->ntokens; i++)
+ if (t->tokens[i].token == '"')
+ t->tokens[i].token=0;
+
+ s=paste_tokens(t, 2, t->ntokens-2);
+ set_string(&p->content_base, s);
+}
+
+static void content_location(struct rfc2045 *p, struct rfc822t *t)
+{
+char *s;
+int i;
+
+ for (i=0; i<t->ntokens; i++)
+ if (t->tokens[i].token == '"')
+ t->tokens[i].token=0;
+
+ s=paste_tokens(t, 2, t->ntokens-2);
+ set_string(&p->content_location, s);
+ free(s);
+}
+
+/* -------------------- */
+
+#define GETINFO(s, def) ( (s) && (*s) ? (s):def)
+
+void rfc2045_mimeinfo(const struct rfc2045 *p,
+ const char **content_type_s,
+ const char **content_transfer_encoding_s,
+ const char **charset_s)
+{
+const char *c;
+
+ *content_type_s=GETINFO(p->content_type, "text/plain");
+ *content_transfer_encoding_s=GETINFO(p->content_transfer_encoding,
+ "8bit");
+
+ c=rfc2045_getattr(p->content_type_attr, "charset");
+ if (!c) c=rfc2045_getdefaultcharset();
+
+ *charset_s=c;
+}
+
+const char *rfc2045_getdefaultcharset()
+{
+const char *p=rfc2045_defcharset;
+
+ if (!p) p=RFC2045CHARSET;
+ return (p);
+}
+
+void rfc2045_setdefaultcharset(const char *charset)
+{
+char *p=strdup(charset);
+
+ if (!p)
+ {
+ rfc2045_enomem();
+ return;
+ }
+
+ if (rfc2045_defcharset) free(rfc2045_defcharset);
+ rfc2045_defcharset=p;
+}
+
+const char *rfc2045_boundary(const struct rfc2045 *p)
+{
+const char *cb=rfc2045_getattr( p->content_type_attr, "boundary");
+
+ if (!cb) cb="";
+ return (cb);
+}
+
+int rfc2045_isflowed(const struct rfc2045 *p)
+{
+ const char *cb=rfc2045_getattr(p->content_type_attr, "format");
+
+ return (cb && strcmp(cb, "flowed") == 0);
+}
+
+int rfc2045_isdelsp(const struct rfc2045 *p)
+{
+ const char *cb=rfc2045_getattr(p->content_type_attr, "delsp");
+
+ return (cb && strcmp(cb, "yes") == 0);
+}
+
+const char *rfc2045_content_id(const struct rfc2045 *p)
+{
+ return (p->content_id ? p->content_id:"");
+}
+
+const char *rfc2045_content_description(const struct rfc2045 *p)
+{
+ return (p->content_description ? p->content_description:"");
+}
+
+const char *rfc2045_content_language(const struct rfc2045 *p)
+{
+ return (p->content_language ? p->content_language:"");
+}
+
+const char *rfc2045_content_md5(const struct rfc2045 *p)
+{
+ return (p->content_md5 ? p->content_md5:"");
+}
+
+void rfc2045_mimepos(const struct rfc2045 *p,
+ off_t *start_pos, off_t *end_pos, off_t *start_body,
+ off_t *nlines, off_t *nbodylines)
+{
+ *start_pos=p->startpos;
+ *end_pos=p->endpos;
+
+ *nlines=p->nlines;
+ *nbodylines=p->nbodylines;
+ if (p->parent) /* MIME parts do not have the trailing CRLF */
+ {
+ *end_pos=p->endbody;
+ if (*nlines) --*nlines;
+ if (*nbodylines) --*nbodylines;
+ }
+ *start_body=p->startbody;
+
+ if (*start_body == *start_pos) /* No header */
+ {
+ *start_body= *end_pos;
+ }
+}
+
+unsigned rfc2045_mimepartcount(const struct rfc2045 *p)
+{
+const struct rfc2045 *q;
+unsigned n=0;
+
+ for (q=p->firstpart; q; q=q->next) ++n;
+ return (n);
+}
+
+/*
+** Generic interface into parse_content_header
+*/
+
+struct rfc2045_parse_mime_info {
+ void (*header_type_cb)(const char *, void *);
+ void (*header_param_cb)(const char *, const char *, void *);
+ void *void_arg;
+};
+
+static void parse_mime_cb(char *, void *);
+static void parse_param_cb(const char *, struct rfc822t *,
+ int, int, void *);
+
+int rfc2045_parse_mime_header(const char *header,
+ void (*header_type_cb)(const char *, void *),
+ void (*header_param_cb)(const char *,
+ const char *,
+ void *),
+ void *void_arg)
+{
+ struct rfc2045_parse_mime_info mi;
+ struct rfc822t *h=rfc822t_alloc_new(header, NULL, NULL);
+
+ mi.header_type_cb=header_type_cb;
+ mi.header_param_cb=header_param_cb;
+ mi.void_arg=void_arg;
+
+ if (!h)
+ return -1;
+
+ parse_content_header(h, 0, parse_mime_cb, parse_param_cb, &mi);
+ rfc822t_free(h);
+ return 0;
+}
+
+static void parse_mime_cb(char *t, void *void_arg)
+{
+ struct rfc2045_parse_mime_info *mi=
+ (struct rfc2045_parse_mime_info *)void_arg;
+
+ (*mi->header_type_cb)(t, mi->void_arg);
+ free(t);
+}
+
+
+static void parse_param_cb(const char *name,
+ struct rfc822t *header, int start,
+ int len, void *void_arg)
+{
+ struct rfc2045_parse_mime_info *mi=
+ (struct rfc2045_parse_mime_info *)void_arg;
+ char *p=paste_tokens(header, start, len);
+
+ if (!p)
+ return;
+
+ (*mi->header_param_cb)(name, p, mi->void_arg);
+ free(p);
+}