+ return store_generic(ct);
+ break;
+ }
+ break;
+
+ case CT_APPLICATION:
+ 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.
+** (application, audio, video, image, text, message/rfc922)
+*/
+static int
+store_generic(CT ct)
+{
+ char **ap, **vp, *cp;
+ CI ci = &ct->c_ctinfo;
+
+ /*
+ ** Check if the content specifies a filename in its MIME parameters.
+ ** Don't bother with this for type "message"
+ ** (only the "message" subtype "rfc822" will use store_generic).
+ */
+ if (autosw && ct->c_type != CT_MESSAGE) {
+ /*
+ ** Check the attribute/value pairs, for the attribute "name".
+ ** If found, take the basename, do a few sanity checks and
+ ** copy the value into c_storeproc.
+ */
+ for (ap = ci->ci_attrs, vp = ci->ci_values; *ap; ap++,vp++) {
+ if (mh_strcasecmp(*ap, "name")!=0) {
+ continue;
+ }
+ cp = mhbasename(*vp);
+ if (*cp && *cp!='.' && *cp!='|' && *cp!='!' &&
+ !strchr(cp, '%')) {
+ /* filename looks good: use it */
+ ct->c_storeproc = getcpy(cp);
+ }
+ break;
+ }
+ }
+
+ 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;
+}
+
+
+/*
+** Show how to retrieve content of type "message/external".
+*/
+static int
+store_external(CT ct)
+{
+ char **ap, **ep;
+ char *msg;
+ FILE *fp;
+ char buf[BUFSIZ];
+
+ msg = add("You need to fetch the contents yourself:", NULL);
+ ap = ct->c_ctinfo.ci_attrs;
+ ep = ct->c_ctinfo.ci_values;
+ for (; *ap; ap++, ep++) {
+ msg = add(concat("\n\t", *ap, ": ", *ep, NULL), msg);
+ }
+ if (!(fp = fopen(ct->c_file, "r"))) {
+ adios(ct->c_file, "unable to open");
+ }
+ fseek(fp, ct->c_begin, SEEK_SET);
+ while (!feof(fp) && ftell(fp) < ct->c_end) {
+ if (!fgets(buf, sizeof buf, fp)) {
+ adios(ct->c_file, "unable to read");
+ }
+ *strchr(buf, '\n') = '\0';
+ msg = add(concat("\n\t", buf, NULL), msg);
+ }
+ fclose(fp);
+ advise(NULL, msg);
+ return OK;
+}
+
+
+/*
+** 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;
+ }
+ }
+
+ /*
+ ** Get storage formatting string.
+ **
+ ** 1) If we have storeproc defined, then use that
+ ** 2) Else check for a mhstore-store-<type>/<subtype> entry
+ ** 3) Else check for a mhstore-store-<type> entry
+ ** 4) Else if content is "message", use "+" (current folder)
+ ** 5) Else use string "%m%P.%s".
+ */
+ if (!(cp = ct->c_storeproc) || !*cp) {
+ CI ci = &ct->c_ctinfo;
+
+ snprintf(buffer, sizeof(buffer), "%s-store-%s/%s",
+ invo_name, ci->ci_type, ci->ci_subtype);
+ if ((cp = context_find(buffer)) == NULL || *cp == '\0') {
+ snprintf(buffer, sizeof(buffer), "%s-store-%s",
+ invo_name, ci->ci_type);
+ if ((cp = context_find(buffer)) == NULL ||
+ *cp == '\0') {
+ cp = ct->c_type == CT_MESSAGE ?
+ "+" : "%m%P.%s";
+ }