2 ** mhstore.c -- store the contents of MIME messages
4 ** This code is Copyright (c) 2002, by the authors of nmh. See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
11 #include <h/signals.h>
18 #include <h/mhparse.h>
19 #include <h/mhcachesbr.h>
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
26 static struct swit switches[] = {
39 #define FILESW 6 /* interface from show */
44 { "type content", 0 },
46 { "rcache policy", 0 },
48 { "wcache policy", 0 },
55 ** switches for debugging
64 extern char *tmp; /* directory to place temp files */
69 extern char *cache_public;
70 extern char *cache_private;
75 extern char *parts[NPARTS + 1];
76 extern char *types[NTYPES + 1];
82 #define quitser pipeser
85 CT parse_mime(char *);
90 void set_endian(void);
91 void flush_errors(void);
94 void free_content(CT);
95 extern CT *cts; /* The list of top-level contents to display */
96 void freects_done(int) NORETURN;
101 static RETSIGTYPE pipeser(int);
106 ** Cache of current directory. This must be
107 ** set before these routines are called.
112 ** The directory in which to store the contents.
117 ** Type for a compare function for qsort. This keeps
118 ** the compiler happy.
120 typedef int (*qsort_comp) (const void *, const void *);
124 int part_ok(CT, int);
125 int type_ok(CT, int);
126 int make_intermediates(char *);
127 void flush_errors(void);
130 int show_content_aux(CT, int, int, char *, char *);
135 static void store_single_message(CT);
136 static int store_switch(CT);
137 static int store_generic(CT);
138 static int store_application(CT);
139 static int store_multi(CT);
140 static int store_partial(CT);
141 static int store_external(CT);
142 static int ct_compar(CT *, CT *);
143 static int store_content(CT, CT);
144 static int output_content_file(CT, int);
145 static int output_content_folder(char *, char *);
146 static int parse_format_string(CT, char *, char *, int, char *);
147 static void get_storeproc(CT);
148 static int copy_some_headers(FILE *, CT);
149 static void store_all_messages(CT *);
153 main(int argc, char **argv)
155 int msgnum, *icachesw;
156 char *cp, *file = NULL, *folder = NULL;
157 char *maildir, buf[100], **argp;
159 struct msgs_array msgs = { 0, 0, NULL };
160 struct msgs *mp = NULL;
167 setlocale(LC_ALL, "");
169 invo_name = mhbasename(argv[0]);
171 /* read user profile/context */
174 arguments = getarguments(invo_name, argc, argv, 1);
180 while ((cp = *argp++)) {
182 switch (smatch(++cp, switches)) {
184 ambigsw(cp, switches);
187 adios(NULL, "-%s unknown", cp);
190 snprintf(buf, sizeof(buf), "%s [+folder] [msgs] [switches]", invo_name);
191 print_help(buf, switches, 1);
194 print_version(invo_name);
205 icachesw = &rcachesw;
208 icachesw = &wcachesw;
210 if (!(cp = *argp++) || *cp == '-')
211 adios(NULL, "missing argument to %s",
213 switch (*icachesw = smatch(cp, caches)) {
218 adios(NULL, "%s unknown", cp);
232 if (!(cp = *argp++) || *cp == '-')
233 adios(NULL, "missing argument to %s",
236 adios(NULL, "too many parts (starting with %s), %d max", cp, NPARTS);
241 if (!(cp = *argp++) || *cp == '-')
242 adios(NULL, "missing argument to %s",
245 adios(NULL, "too many types (starting with %s), %d max", cp, NTYPES);
250 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
251 adios(NULL, "missing argument to %s",
253 file = *cp == '-' ? cp : getcpy(expanddir(cp));
267 if (*cp == '+' || *cp == '@') {
269 adios(NULL, "only one folder at a time!");
271 folder = getcpy(expandfol(cp));
273 app_msgarg(&msgs, cp);
276 /* null terminate the list of acceptable parts/types */
283 ** Check if we've specified an additional profile
285 if ((cp = getenv("MHSTORE"))) {
286 if ((fp = fopen(cp, "r"))) {
287 readconfig((struct node **) 0, fp, cp, 0);
290 admonish("", "unable to read $MHSTORE profile (%s)",
296 ** Read the standard profile setup
298 if ((fp = fopen(cp = etcpath("mhn.defaults"), "r"))) {
299 readconfig((struct node **) 0, fp, cp, 0);
303 /* Check for public cache location */
304 if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
307 /* Check for private cache location */
308 if (!(cache_private = context_find(nmhprivcache)))
309 cache_private = ".cache";
310 cache_private = getcpy(toabsdir(cache_private));
313 ** Cache the current directory before we do any chdirs()'s.
318 ** Check for storage directory. If specified,
319 ** then store temporary files there. Else we
320 ** store them in standard nmh directory.
322 if ((cp = context_find(nmhstorage)) && *cp)
323 tmp = concat(cp, "/", invo_name, NULL);
325 tmp = getcpy(toabsdir(invo_name));
327 if (file && msgs.size)
328 adios(NULL, "cannot specify msg and file at same time!");
331 ** check if message is coming from file
334 if (!(cts = (CT *) calloc((size_t) 2, sizeof(*cts))))
335 adios(NULL, "out of memory");
338 if ((ct = parse_mime(file)));
342 ** message(s) are coming from a folder
345 app_msgarg(&msgs, seq_cur);
347 folder = getcurfol();
348 maildir = toabsdir(folder);
350 if (chdir(maildir) == NOTOK)
351 adios(maildir, "unable to change directory to");
353 /* read folder and create message structure */
354 if (!(mp = folder_read(folder)))
355 adios(NULL, "unable to read folder %s", folder);
357 /* check for empty folder */
359 adios(NULL, "no messages in %s", folder);
361 /* parse all the message ranges/sequences and set SELECTED */
362 for (msgnum = 0; msgnum < msgs.size; msgnum++)
363 if (!m_convert(mp, msgs.msgs[msgnum]))
365 seq_setprev(mp); /* set the previous-sequence */
367 if (!(cts = (CT *) calloc((size_t) (mp->numsel + 1),
369 adios(NULL, "out of memory");
372 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
373 if (is_selected(mp, msgnum)) {
376 msgnam = m_name(msgnum);
377 if ((ct = parse_mime(msgnam)))
387 SIGNAL(SIGQUIT, quitser);
388 SIGNAL(SIGPIPE, pipeser);
391 ** Get the associated umask for the relevant contents.
393 for (ctp = cts; *ctp; ctp++) {
397 if (type_ok(ct, 1) && !ct->c_umask) {
398 if (stat(ct->c_file, &st) != NOTOK)
399 ct->c_umask = ~(st.st_mode & 0777);
401 ct->c_umask = ~m_gmprot();
406 ** Store the message content
408 store_all_messages(cts);
410 /* Now free all the structures for the content */
411 for (ctp = cts; *ctp; ctp++)
417 /* If reading from a folder, do some updating */
419 context_replace(curfolder, folder); /* update current folder */
420 seq_setcur(mp, mp->hghsel); /* update current message */
421 seq_save(mp); /* synchronize sequences */
422 context_save(); /* save the context file */
436 fprintf(stderr, "\n");
446 ** Main entry point to store content from a collection of messages.
449 store_all_messages(CT *cts)
455 ** Check for the directory in which to
456 ** store any contents.
460 else if ((cp = context_find(nmhstorage)) && *cp)
465 for (ctp = cts; *ctp; ctp++) {
467 store_single_message(ct);
475 ** Entry point to store the content
476 ** in a (single) message
480 store_single_message(CT ct)
482 if (type_ok(ct, 1)) {
489 if (ct->c_ceclosefnx)
490 (*ct->c_ceclosefnx) (ct);
496 ** Switching routine to store different content types
502 switch (ct->c_type) {
504 return store_multi(ct);
508 switch (ct->c_subtype) {
509 case MESSAGE_PARTIAL:
510 return store_partial(ct);
513 case MESSAGE_EXTERNAL:
514 return store_external(ct);
518 return store_generic(ct);
524 return store_application(ct);
531 return store_generic(ct);
535 adios(NULL, "unknown content type %d", ct->c_type);
539 return OK; /* NOT REACHED */
544 ** Generic routine to store a MIME content.
545 ** (audio, video, image, text, message/rfc922)
552 ** Check if the content specifies a filename.
553 ** Don't bother with this for type "message"
554 ** (only "message/rfc822" will use store_generic).
556 if (autosw && ct->c_type != CT_MESSAGE)
559 return store_content(ct, NULL);
564 ** Store content of type "application"
568 store_application(CT ct)
571 CI ci = &ct->c_ctinfo;
573 /* Check if the content specifies a filename */
578 ** If storeproc is not defined, and the content is type
579 ** "application/octet-stream", we also check for various
580 ** attribute/value pairs which specify if this a tar file.
582 if (!ct->c_storeproc && ct->c_subtype == APPLICATION_OCTETS) {
583 int tarP = 0, zP = 0, gzP = 0;
585 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
586 /* check for "type=tar" attribute */
587 if (!mh_strcasecmp(*ap, "type")) {
588 if (mh_strcasecmp(*ep, "tar"))
595 /* check for "conversions=compress" attribute */
596 if ((!mh_strcasecmp(*ap, "conversions") ||
597 !mh_strcasecmp(*ap, "x-conversions"))
598 && (!mh_strcasecmp(*ep, "compress") ||
599 !mh_strcasecmp(*ep, "x-compress"))) {
603 /* check for "conversions=gzip" attribute */
604 if ((!mh_strcasecmp(*ap, "conversions") ||
605 !mh_strcasecmp(*ap, "x-conversions"))
606 && (!mh_strcasecmp(*ep, "gzip") ||
607 !mh_strcasecmp(*ep, "x-gzip"))) {
614 ct->c_showproc = getcpy(zP ? "%euncompress | tar tvf -" : (gzP ? "%egzip -dc | tar tvf -" : "%etar tvf -"));
615 if (!ct->c_storeproc) {
617 ct->c_storeproc = getcpy(zP ? "| uncompress | tar xvpf -" : (gzP ? "| gzip -dc | tar xvpf -" : "| tar xvpf -"));
620 ct->c_storeproc= getcpy(zP ? "%m%P.tar.Z" : (gzP ? "%m%P.tar.gz" : "%m%P.tar"));
626 return store_content(ct, NULL);
631 ** Store the content of a multipart message
638 struct multipart *m = (struct multipart *) ct->c_ctparams;
642 for (part = m->mp_parts; part; part = part->mp_next) {
643 CT p = part->mp_part;
645 if (part_ok(p, 1) && type_ok(p, 1)) {
646 result = store_switch(p);
647 if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
657 ** Reassemble and store the contents of a collection
658 ** of messages of type "message/partial".
667 struct partial *pm, *qm;
669 qm = (struct partial *) ct->c_ctparams;
674 for (ctp = cts; *ctp; ctp++) {
676 if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
677 pm = (struct partial *) p->c_ctparams;
678 if (!pm->pm_stored &&
679 strcmp(qm->pm_partid, pm->pm_partid)
681 pm->pm_marked = pm->pm_partno;
692 advise(NULL, "missing (at least) last part of multipart message");
696 if ((base = (CT *) calloc((size_t) (i + 1), sizeof(*base))) == NULL)
697 adios(NULL, "out of memory");
700 for (ctp = cts; *ctp; ctp++) {
702 if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
703 pm = (struct partial *) p->c_ctparams;
711 qsort((char *) base, i, sizeof(*base), (qsort_comp) ct_compar);
714 for (ctq = base; *ctq; ctq++) {
716 pm = (struct partial *) p->c_ctparams;
717 if (pm->pm_marked != cur) {
718 if (pm->pm_marked == cur - 1) {
719 admonish(NULL, "duplicate part %d of %d part multipart message", pm->pm_marked, hi);
724 advise (NULL, "missing %spart %d of %d part multipart message", cur != hi ? "(at least) " : "", cur, hi);
735 ** Now cycle through the sorted list of messages of type
736 ** "message/partial" and save/append them to a file.
741 if (store_content(ct, NULL) == NOTOK) {
747 for (; *ctq; ctq++) {
749 if (store_content(p, ct) == NOTOK)
759 ** Store content from a message of type "message/external".
763 store_external(CT ct)
766 struct exbody *e = (struct exbody *) ct->c_ctparams;
767 CT p = e->eb_content;
773 ** Check if the parameters for the external body
774 ** specified a filename.
779 if ((cp = e->eb_name) && *cp != '/' && *cp != '.' &&
780 *cp != '|' && *cp != '!' && !strchr(cp, '%')) {
781 if (!ct->c_storeproc)
782 ct->c_storeproc = getcpy(cp);
784 p->c_storeproc = getcpy(cp);
789 ** Since we will let the Content structure for the
790 ** external body substitute for the current content,
791 ** we temporarily change its partno (number inside
792 ** multipart), so everything looks right.
794 p->c_partno = ct->c_partno;
796 /* we probably need to check if content is really there */
797 result = store_switch(p);
805 ** Compare the numbering from two different
806 ** message/partials (needed for sorting).
810 ct_compar(CT *a, CT *b)
812 struct partial *am = (struct partial *) ((*a)->c_ctparams);
813 struct partial *bm = (struct partial *) ((*b)->c_ctparams);
815 return (am->pm_marked - bm->pm_marked);
820 ** Store contents of a message or message part to
821 ** a folder, a file, the standard output, or pass
822 ** the contents to a command.
824 ** If the current content to be saved is a followup part
825 ** to a collection of messages of type "message/partial",
826 ** then field "p" is a pointer to the Content structure
827 ** to the first message/partial in the group.
831 store_content(CT ct, CT p)
833 int appending = 0, msgnum = 0;
834 int is_partial = 0, first_partial = 0;
835 int last_partial = 0;
836 char *cp, buffer[BUFSIZ];
839 ** Do special processing for messages of
840 ** type "message/partial".
842 ** We first check if this content is of type
843 ** "message/partial". If it is, then we need to check
844 ** whether it is the first and/or last in the group.
846 ** Then if "p" is a valid pointer, it points to the Content
847 ** structure of the first partial in the group. So we copy
848 ** the file name and/or folder name from that message. In
849 ** this case, we also note that we will be appending.
851 if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
852 struct partial *pm = (struct partial *) ct->c_ctparams;
854 /* Yep, it's a message/partial */
857 /* But is it the first and/or last in the collection? */
858 if (pm->pm_partno == 1)
860 if (pm->pm_maxno && pm->pm_partno == pm->pm_maxno)
864 ** If "p" is a valid pointer, then it points to the
865 ** Content structure for the first message in the group.
866 ** So we just copy the filename or foldername information
867 ** from the previous iteration of this function.
871 ct->c_storage = getcpy(p->c_storage);
873 /* record the folder name */
875 ct->c_folder = getcpy(p->c_folder);
882 ** Get storage formatting string.
884 ** 1) If we have storeproc defined, then use that
885 ** 2) Else check for a mhstore-store-<type>/<subtype> entry
886 ** 3) Else check for a mhstore-store-<type> entry
887 ** 4) Else if content is "message", use "+" (current folder)
888 ** 5) Else use string "%m%P.%s".
890 if ((cp = ct->c_storeproc) == NULL || *cp == '\0') {
891 CI ci = &ct->c_ctinfo;
893 snprintf(buffer, sizeof(buffer), "%s-store-%s/%s",
894 invo_name, ci->ci_type, ci->ci_subtype);
895 if ((cp = context_find(buffer)) == NULL || *cp == '\0') {
896 snprintf(buffer, sizeof(buffer), "%s-store-%s",
897 invo_name, ci->ci_type);
898 if ((cp = context_find(buffer)) == NULL ||
900 cp = ct->c_type == CT_MESSAGE ?
907 ** Check the beginning of storage formatting string
908 ** to see if we are saving content to a folder.
910 if (*cp == '+' || *cp == '@') {
911 char *tmpfilenam, *folder;
913 /* Store content in temporary file for now */
914 tmpfilenam = m_mktemp(invo_name, NULL, NULL);
915 ct->c_storage = getcpy(tmpfilenam);
917 /* Get the folder name */
919 folder = getcpy(expandfol(cp));
921 folder = getcurfol();
923 /* Check if folder exists */
924 create_folder(toabsdir(folder), 0, exit);
926 /* Record the folder name */
927 ct->c_folder = getcpy(folder);
936 ** Parse and expand the storage formatting string
937 ** in `cp' into `buffer'.
939 parse_format_string(ct, cp, buffer, sizeof(buffer), dir);
942 ** If formatting begins with '|' or '!', then pass
943 ** content to standard input of a command and return.
945 if (buffer[0] == '|' || buffer[0] == '!')
946 return show_content_aux(ct, 1, 0, buffer + 1, dir);
948 /* record the filename */
949 ct->c_storage = getcpy(buffer);
952 /* flush the output stream */
955 /* Now save or append the content to a file */
956 if (output_content_file(ct, appending) == NOTOK)
960 ** If necessary, link the file into a folder and remove
961 ** the temporary file. If this message is a partial,
962 ** then only do this if it is the last one in the group.
964 if (ct->c_folder && (!is_partial || last_partial)) {
965 msgnum = output_content_folder(ct->c_folder, ct->c_storage);
966 unlink(ct->c_storage);
972 ** Now print out the name/number of the message
973 ** that we are storing.
977 fprintf(stderr, "reassembling partials ");
979 fprintf(stderr, "%s", ct->c_file);
981 fprintf(stderr, "%s,", ct->c_file);
983 fprintf(stderr, "storing message %s", ct->c_file);
985 fprintf(stderr, " part %s", ct->c_partno);
989 ** Unless we are in the "middle" of group of message/partials,
990 ** we now print the name of the file, folder, and/or message
991 ** to which we are storing the content.
993 if (!is_partial || last_partial) {
995 fprintf(stderr, " to folder %s as message %d\n",
996 ct->c_folder, msgnum);
997 } else if (strcmp(ct->c_storage, "-")==0) {
998 fprintf(stderr, " to stdout\n");
1002 cwdlen = strlen(cwd);
1003 fprintf(stderr, " as file %s\n",
1004 strncmp(ct->c_storage, cwd,
1006 ct->c_storage[cwdlen] != '/' ?
1008 ct->c_storage + cwdlen + 1);
1017 ** Output content to a file
1021 output_content_file(CT ct, int appending)
1024 char *file, buffer[BUFSIZ];
1029 ** If the pathname is absolute, make sure
1030 ** all the relevant directories exist.
1032 if (strchr(ct->c_storage, '/') && make_intermediates(ct->c_storage)
1036 if (ct->c_encoding != CE_7BIT) {
1039 if (!ct->c_ceopenfnx) {
1040 advise(NULL, "don't know how to decode part %s of message %s", ct->c_partno, ct->c_file);
1044 file = appending || strcmp(ct->c_storage, "-")==0 ?
1045 NULL : ct->c_storage;
1046 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1048 if (strcmp(file, ct->c_storage)==0) {
1049 (*ct->c_ceclosefnx) (ct);
1054 ** Send to standard output
1056 if (strcmp(ct->c_storage, "-")==0) {
1059 if ((gd = dup(fileno(stdout))) == NOTOK) {
1060 advise("stdout", "unable to dup");
1062 (*ct->c_ceclosefnx) (ct);
1065 if ((fp = fdopen(gd, appending ? "a" : "w")) == NULL) {
1066 advise("stdout", "unable to fdopen (%d, \"%s\") from", gd, appending ? "a" : "w");
1074 if ((fp = fopen(ct->c_storage, appending ? "a" : "w"))
1076 advise(ct->c_storage, "unable to fopen for %s",
1078 "appending" : "writing");
1084 ** Filter the header fields of the initial enclosing
1085 ** message/partial into the file.
1087 if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
1088 struct partial *pm = (struct partial *) ct->c_ctparams;
1090 if (pm->pm_partno == 1)
1091 copy_some_headers(fp, ct);
1095 switch (cc = read(fd, buffer, sizeof(buffer))) {
1097 advise(file, "error reading content from");
1104 fwrite(buffer, sizeof(*buffer), cc, fp);
1110 (*ct->c_ceclosefnx) (ct);
1112 if (cc != NOTOK && fflush(fp))
1113 advise(ct->c_storage, "error writing to");
1117 return (cc != NOTOK ? OK : NOTOK);
1120 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1121 advise(ct->c_file, "unable to open for reading");
1127 fseek(ct->c_fp, pos, SEEK_SET);
1129 if (strcmp(ct->c_storage, "-")==0) {
1132 if ((gd = dup(fileno(stdout))) == NOTOK) {
1133 advise("stdout", "unable to dup");
1136 if ((fp = fdopen(gd, appending ? "a" : "w")) == NULL) {
1137 advise("stdout", "unable to fdopen (%d, \"%s\") from",
1138 gd, appending ? "a" : "w");
1143 if ((fp = fopen(ct->c_storage, appending ? "a" : "w"))
1145 advise(ct->c_storage, "unable to fopen for %s",
1146 appending ? "appending" : "writing");
1152 ** Copy a few of the header fields of the initial
1153 ** enclosing message/partial into the file.
1156 if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
1157 struct partial *pm = (struct partial *) ct->c_ctparams;
1159 if (pm->pm_partno == 1) {
1160 copy_some_headers(fp, ct);
1165 while (fgets(buffer, sizeof(buffer) - 1, ct->c_fp)) {
1166 if ((pos += strlen(buffer)) > last) {
1169 diff = strlen(buffer) - (pos - last);
1171 buffer[diff] = '\0';
1174 ** If this is the first content of a group of
1175 ** message/partial contents, then we only copy a few
1176 ** of the header fields of the enclosed message.
1179 switch (buffer[0]) {
1182 if (filterstate < 0)
1191 if (!uprf(buffer, XXX_FIELD_PRF) && !uprf(buffer, VRSN_FIELD) && !uprf(buffer, "Subject:") && !uprf(buffer, "Message-ID:")) {
1206 advise(ct->c_storage, "error writing to");
1216 ** Add a file to a folder.
1218 ** Return the new message number of the file
1219 ** when added to the folder. Return -1, if
1220 ** there is an error.
1224 output_content_folder(char *folder, char *filename)
1229 /* Read the folder. */
1230 if ((mp = folder_read(folder))) {
1231 /* Link file into folder */
1232 msgnum = folder_addmsg(&mp, filename, 0, 0, 0, 0, NULL);
1234 advise(NULL, "unable to read folder %s", folder);
1238 /* free folder structure */
1242 ** Return msgnum. We are relying on the fact that
1243 ** msgnum will be -1, if folder_addmsg() had an error.
1250 ** Parse and expand the storage formatting string
1251 ** pointed to by "cp" into "buffer".
1255 parse_format_string(CT ct, char *cp, char *buffer, int buflen, char *dir)
1259 CI ci = &ct->c_ctinfo;
1262 ** If storage string is "-", just copy it, and
1263 ** return (send content to standard output).
1265 if (cp[0] == '-' && cp[1] == '\0') {
1266 strncpy(buffer, cp, buflen);
1274 ** If formatting string is a pathname that doesn't
1275 ** begin with '/', then preface the path with the
1276 ** appropriate directory.
1278 if (*cp != '/' && *cp != '|' && *cp != '!') {
1279 snprintf(bp, buflen, "%s/", dir[1] ? dir : "");
1287 /* We are processing a storage escape */
1292 ** Insert parameters from Content-Type.
1293 ** This is only valid for '|' commands.
1295 if (buffer[0] != '|' && buffer[0] != '!') {
1304 for (ap=ci->ci_attrs, ep=ci->ci_values;
1306 snprintf(bp, buflen,
1318 /* insert message number */
1319 snprintf(bp, buflen, "%s",
1320 mhbasename(ct->c_file));
1324 /* insert part number with leading dot */
1326 snprintf(bp, buflen, ".%s",
1331 /* insert part number withouth leading dot */
1333 strncpy(bp, ct->c_partno, buflen);
1337 /* insert content type */
1338 strncpy(bp, ci->ci_type, buflen);
1342 /* insert content subtype */
1343 strncpy(bp, ci->ci_subtype, buflen);
1347 /* insert the character % */
1357 /* Advance bp and decrement buflen */
1375 ** Check if the content specifies a filename
1376 ** in its MIME parameters.
1380 get_storeproc(CT ct)
1382 char **ap, **ep, *cp;
1383 CI ci = &ct->c_ctinfo;
1386 ** If the storeproc has already been defined,
1387 ** we just return (for instance, if this content
1388 ** is part of a "message/external".
1390 if (ct->c_storeproc)
1394 ** Check the attribute/value pairs, for the attribute "name".
1395 ** If found, do a few sanity checks and copy the value into
1398 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1399 if (!mh_strcasecmp(*ap, "name") && *(cp = *ep) != '/' &&
1400 *cp != '.' && *cp != '|' && *cp != '!' &&
1402 ct->c_storeproc = getcpy(cp);
1410 ** Copy some of the header fields of the initial message/partial
1411 ** message into the header of the reassembled message.
1415 copy_some_headers(FILE *out, CT ct)
1419 hp = ct->c_first_hf; /* start at first header field */
1423 ** A few of the header fields of the enclosing
1424 ** messages are not copied.
1426 if (!uprf(hp->name, XXX_FIELD_PRF) &&
1427 mh_strcasecmp(hp->name, VRSN_FIELD) &&
1428 mh_strcasecmp(hp->name, "Subject") &&
1429 mh_strcasecmp(hp->name, "Message-ID"))
1430 fprintf(out, "%s:%s", hp->name, hp->value);
1431 hp = hp->next; /* next header field */