+ return store_generic(ct);
+ break;
+ }
+ break;
+
+ case CT_APPLICATION:
+ return store_application(ct);
+ break;
+
+ case CT_TEXT:
+ case CT_AUDIO:
+ case CT_IMAGE:
+ case CT_VIDEO:
+ return store_generic(ct);
+ break;
+
+ default:
+ adios(NULL, "unknown content type %d", ct->c_type);
+ break;
+ }
+
+ return OK; /* NOT REACHED */
+}
+
+
+/*
+** Generic routine to store a MIME content.
+** (audio, video, image, text, message/rfc922)
+*/
+
+static int
+store_generic(CT ct)
+{
+ /*
+ ** Check if the content specifies a filename.
+ ** Don't bother with this for type "message"
+ ** (only "message/rfc822" will use store_generic).
+ */
+ if (autosw && ct->c_type != CT_MESSAGE)
+ get_storeproc(ct);
+
+ return store_content(ct, NULL);
+}
+
+
+/*
+** Store content of type "application"
+*/
+
+static int
+store_application(CT ct)
+{
+ char **ap, **ep;
+ CI ci = &ct->c_ctinfo;
+
+ /* Check if the content specifies a filename */
+ if (autosw)
+ get_storeproc(ct);
+
+ /*
+ ** If storeproc is not defined, and the content is type
+ ** "application/octet-stream", we also check for various
+ ** attribute/value pairs which specify if this a tar file.
+ */
+ if (!ct->c_storeproc && ct->c_subtype == APPLICATION_OCTETS) {
+ int tarP = 0, zP = 0, gzP = 0;
+
+ for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+ /* check for "type=tar" attribute */
+ if (!mh_strcasecmp(*ap, "type")) {
+ if (mh_strcasecmp(*ep, "tar"))
+ break;
+
+ tarP = 1;
+ continue;
+ }
+
+ /* check for "conversions=compress" attribute */
+ if ((!mh_strcasecmp(*ap, "conversions") ||
+ !mh_strcasecmp(*ap, "x-conversions"))
+ && (!mh_strcasecmp(*ep, "compress") ||
+ !mh_strcasecmp(*ep, "x-compress"))) {
+ zP = 1;
+ continue;
+ }
+ /* check for "conversions=gzip" attribute */
+ if ((!mh_strcasecmp(*ap, "conversions") ||
+ !mh_strcasecmp(*ap, "x-conversions"))
+ && (!mh_strcasecmp(*ep, "gzip") ||
+ !mh_strcasecmp(*ep, "x-gzip"))) {
+ gzP = 1;
+ continue;
+ }
+ }
+
+ if (tarP) {
+ ct->c_showproc = getcpy(zP ? "%euncompress | tar tvf -" : (gzP ? "%egzip -dc | tar tvf -" : "%etar tvf -"));
+ if (!ct->c_storeproc) {
+ if (autosw) {
+ ct->c_storeproc = getcpy(zP ? "| uncompress | tar xvpf -" : (gzP ? "| gzip -dc | tar xvpf -" : "| tar xvpf -"));
+ ct->c_umask = 0022;
+ } else {
+ ct->c_storeproc= getcpy(zP ? "%m%P.tar.Z" : (gzP ? "%m%P.tar.gz" : "%m%P.tar"));
+ }
+ }
+ }
+ }
+
+ return store_content(ct, NULL);
+}
+
+
+/*
+** Store the content of a multipart message
+*/
+
+static int
+store_multi(CT ct)
+{
+ int result;
+ struct multipart *m = (struct multipart *) ct->c_ctparams;
+ struct part *part;
+
+ result = NOTOK;
+ for (part = m->mp_parts; part; part = part->mp_next) {
+ CT p = part->mp_part;
+
+ if (part_ok(p, 1) && type_ok(p, 1)) {
+ result = store_switch(p);
+ if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+/*
+** Reassemble and store the contents of a collection
+** of messages of type "message/partial".
+*/
+
+static int
+store_partial(CT ct)
+{
+ int cur, hi, i;
+ CT p, *ctp, *ctq;
+ CT *base;
+ struct partial *pm, *qm;
+
+ qm = (struct partial *) ct->c_ctparams;
+ if (qm->pm_stored)
+ return OK;
+
+ hi = i = 0;
+ for (ctp = cts; *ctp; ctp++) {
+ p = *ctp;
+ if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
+ pm = (struct partial *) p->c_ctparams;
+ if (!pm->pm_stored &&
+ strcmp(qm->pm_partid, pm->pm_partid)
+ == 0) {
+ pm->pm_marked = pm->pm_partno;
+ if (pm->pm_maxno)
+ hi = pm->pm_maxno;
+ pm->pm_stored = 1;
+ i++;
+ } else
+ pm->pm_marked = 0;
+ }
+ }
+
+ if (hi == 0) {
+ advise(NULL, "missing (at least) last part of multipart message");
+ return NOTOK;
+ }
+
+ if ((base = (CT *) calloc((size_t) (i + 1), sizeof(*base))) == NULL)
+ adios(NULL, "out of memory");
+
+ ctq = base;
+ for (ctp = cts; *ctp; ctp++) {
+ p = *ctp;
+ if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
+ pm = (struct partial *) p->c_ctparams;
+ if (pm->pm_marked)
+ *ctq++ = p;
+ }
+ }
+ *ctq = NULL;
+
+ if (i > 1)
+ qsort((char *) base, i, sizeof(*base), (qsort_comp) ct_compar);
+
+ cur = 1;
+ for (ctq = base; *ctq; ctq++) {
+ p = *ctq;
+ pm = (struct partial *) p->c_ctparams;
+ if (pm->pm_marked != cur) {
+ if (pm->pm_marked == cur - 1) {
+ admonish(NULL, "duplicate part %d of %d part multipart message", pm->pm_marked, hi);
+ continue;
+ }
+
+missing_part:
+ advise (NULL, "missing %spart %d of %d part multipart message", cur != hi ? "(at least) " : "", cur, hi);
+ goto losing;
+ } else
+ cur++;
+ }
+ if (hi != --cur) {
+ cur = hi;
+ goto missing_part;
+ }
+
+ /*
+ ** Now cycle through the sorted list of messages of type
+ ** "message/partial" and save/append them to a file.
+ */
+
+ ctq = base;
+ ct = *ctq++;
+ if (store_content(ct, NULL) == NOTOK) {
+losing:
+ free((char *) base);
+ return NOTOK;
+ }
+
+ for (; *ctq; ctq++) {
+ p = *ctq;
+ if (store_content(p, ct) == NOTOK)
+ goto losing;
+ }
+
+ free((char *) base);
+ return OK;
+}
+
+
+/*
+** Store content from a message of type "message/external".
+*/
+
+static int
+store_external(CT ct)
+{
+ int result = NOTOK;
+ struct exbody *e = (struct exbody *) ct->c_ctparams;
+ CT p = e->eb_content;
+
+ if (!type_ok(p, 1))
+ return OK;
+
+ /*
+ ** Check if the parameters for the external body
+ ** specified a filename.
+ */
+ if (autosw) {
+ char *cp;
+
+ if ((cp = e->eb_name) && *cp != '/' && *cp != '.' &&
+ *cp != '|' && *cp != '!' && !strchr(cp, '%')) {
+ if (!ct->c_storeproc)
+ ct->c_storeproc = getcpy(cp);
+ if (!p->c_storeproc)
+ p->c_storeproc = getcpy(cp);
+ }
+ }
+
+ /*
+ ** Since we will let the Content structure for the
+ ** external body substitute for the current content,
+ ** we temporarily change its partno (number inside
+ ** multipart), so everything looks right.
+ */
+ p->c_partno = ct->c_partno;
+
+ /* we probably need to check if content is really there */
+ result = store_switch(p);
+
+ p->c_partno = NULL;
+ return result;
+}
+
+
+/*
+** Compare the numbering from two different
+** message/partials (needed for sorting).
+*/
+
+static int
+ct_compar(CT *a, CT *b)
+{
+ struct partial *am = (struct partial *) ((*a)->c_ctparams);
+ struct partial *bm = (struct partial *) ((*b)->c_ctparams);
+
+ return (am->pm_marked - bm->pm_marked);
+}
+
+
+/*
+** Store contents of a message or message part to
+** a folder, a file, the standard output, or pass
+** the contents to a command.
+**
+** If the current content to be saved is a followup part
+** to a collection of messages of type "message/partial",
+** then field "p" is a pointer to the Content structure
+** to the first message/partial in the group.
+*/
+
+static int
+store_content(CT ct, CT p)
+{
+ int appending = 0, msgnum = 0;
+ int is_partial = 0, first_partial = 0;
+ int last_partial = 0;
+ char *cp, buffer[BUFSIZ];
+
+ /*
+ ** Do special processing for messages of
+ ** type "message/partial".
+ **
+ ** We first check if this content is of type
+ ** "message/partial". If it is, then we need to check
+ ** whether it is the first and/or last in the group.
+ **
+ ** Then if "p" is a valid pointer, it points to the Content
+ ** structure of the first partial in the group. So we copy
+ ** the file name and/or folder name from that message. In
+ ** this case, we also note that we will be appending.
+ */
+ if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
+ struct partial *pm = (struct partial *) ct->c_ctparams;
+
+ /* Yep, it's a message/partial */
+ is_partial = 1;
+
+ /* But is it the first and/or last in the collection? */
+ if (pm->pm_partno == 1)
+ first_partial = 1;
+ if (pm->pm_maxno && pm->pm_partno == pm->pm_maxno)
+ last_partial = 1;
+
+ /*
+ ** If "p" is a valid pointer, then it points to the
+ ** Content structure for the first message in the group.
+ ** So we just copy the filename or foldername information
+ ** from the previous iteration of this function.
+ */
+ if (p) {
+ appending = 1;
+ ct->c_storage = getcpy(p->c_storage);
+
+ /* record the folder name */
+ if (p->c_folder) {
+ ct->c_folder = getcpy(p->c_folder);
+ }
+ goto got_filename;