Relayouted all switch statements: case aligns with switch.
[mmh] / uip / mhstoresbr.c
1 /*
2 ** mhstoresbr.c -- routines to save/store the contents of MIME messages
3 **
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.
7 */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/signals.h>
12 #include <h/md5.h>
13 #include <errno.h>
14 #include <setjmp.h>
15 #include <signal.h>
16 #include <h/mts.h>
17 #include <h/tws.h>
18 #include <h/mime.h>
19 #include <h/mhparse.h>
20 #include <h/utils.h>
21
22
23 /*
24 ** The list of top-level contents to display
25 */
26 extern CT *cts;
27
28 int autosw = 0;
29
30 /*
31 ** Cache of current directory.  This must be
32 ** set before these routines are called.
33 */
34 char *cwd;
35
36 /*
37 ** The directory in which to store the contents.
38 */
39 static char *dir;
40
41 /*
42 ** Type for a compare function for qsort.  This keeps
43 ** the compiler happy.
44 */
45 typedef int (*qsort_comp) (const void *, const void *);
46
47
48 /* mhmisc.c */
49 int part_ok(CT, int);
50 int type_ok(CT, int);
51 int make_intermediates(char *);
52 void flush_errors(void);
53
54 /* mhshowsbr.c */
55 int show_content_aux(CT, int, int, char *, char *);
56
57 /*
58 ** prototypes
59 */
60 void store_all_messages(CT *);
61
62 /*
63 ** static prototypes
64 */
65 static void store_single_message(CT);
66 static int store_switch(CT);
67 static int store_generic(CT);
68 static int store_application(CT);
69 static int store_multi(CT);
70 static int store_partial(CT);
71 static int store_external(CT);
72 static int ct_compar(CT *, CT *);
73 static int store_content(CT, CT);
74 static int output_content_file(CT, int);
75 static int output_content_folder(char *, char *);
76 static int parse_format_string(CT, char *, char *, int, char *);
77 static void get_storeproc(CT);
78 static int copy_some_headers(FILE *, CT);
79
80
81 /*
82 ** Main entry point to store content
83 ** from a collection of messages.
84 */
85
86 void
87 store_all_messages(CT *cts)
88 {
89         CT ct, *ctp;
90         char *cp;
91
92         /*
93         ** Check for the directory in which to
94         ** store any contents.
95         */
96         if (autosw)
97                 dir = getcpy(cwd);
98         else if ((cp = context_find(nmhstorage)) && *cp)
99                 dir = getcpy(cp);
100         else
101                 dir = getcpy(cwd);
102
103         for (ctp = cts; *ctp; ctp++) {
104                 ct = *ctp;
105                 store_single_message(ct);
106         }
107
108         flush_errors();
109 }
110
111
112 /*
113 ** Entry point to store the content
114 ** in a (single) message
115 */
116
117 static void
118 store_single_message(CT ct)
119 {
120         if (type_ok(ct, 1)) {
121                 umask(ct->c_umask);
122                 store_switch(ct);
123                 if (ct->c_fp) {
124                         fclose(ct->c_fp);
125                         ct->c_fp = NULL;
126                 }
127                 if (ct->c_ceclosefnx)
128                         (*ct->c_ceclosefnx) (ct);
129         }
130 }
131
132
133 /*
134 ** Switching routine to store different content types
135 */
136
137 static int
138 store_switch(CT ct)
139 {
140         switch (ct->c_type) {
141         case CT_MULTIPART:
142                 return store_multi(ct);
143                 break;
144
145         case CT_MESSAGE:
146                 switch (ct->c_subtype) {
147                 case MESSAGE_PARTIAL:
148                         return store_partial(ct);
149                         break;
150
151                 case MESSAGE_EXTERNAL:
152                         return store_external(ct);
153
154                 case MESSAGE_RFC822:
155                 default:
156                         return store_generic(ct);
157                         break;
158                 }
159                 break;
160
161         case CT_APPLICATION:
162                 return store_application(ct);
163                 break;
164
165         case CT_TEXT:
166         case CT_AUDIO:
167         case CT_IMAGE:
168         case CT_VIDEO:
169                 return store_generic(ct);
170                 break;
171
172         default:
173                 adios(NULL, "unknown content type %d", ct->c_type);
174                 break;
175         }
176
177         return OK;  /* NOT REACHED */
178 }
179
180
181 /*
182 ** Generic routine to store a MIME content.
183 ** (audio, video, image, text, message/rfc922)
184 */
185
186 static int
187 store_generic(CT ct)
188 {
189         /*
190         ** Check if the content specifies a filename.
191         ** Don't bother with this for type "message"
192         ** (only "message/rfc822" will use store_generic).
193         */
194         if (autosw && ct->c_type != CT_MESSAGE)
195                 get_storeproc(ct);
196
197         return store_content(ct, NULL);
198 }
199
200
201 /*
202 ** Store content of type "application"
203 */
204
205 static int
206 store_application(CT ct)
207 {
208         char **ap, **ep;
209         CI ci = &ct->c_ctinfo;
210
211         /* Check if the content specifies a filename */
212         if (autosw)
213                 get_storeproc(ct);
214
215         /*
216         ** If storeproc is not defined, and the content is type
217         ** "application/octet-stream", we also check for various
218         ** attribute/value pairs which specify if this a tar file.
219         */
220         if (!ct->c_storeproc && ct->c_subtype == APPLICATION_OCTETS) {
221                 int tarP = 0, zP = 0, gzP = 0;
222
223                 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
224                         /* check for "type=tar" attribute */
225                         if (!mh_strcasecmp(*ap, "type")) {
226                                 if (mh_strcasecmp(*ep, "tar"))
227                                         break;
228
229                                 tarP = 1;
230                                 continue;
231                         }
232
233                         /* check for "conversions=compress" attribute */
234                         if ((!mh_strcasecmp(*ap, "conversions") ||
235                                         !mh_strcasecmp(*ap, "x-conversions"))
236                                         && (!mh_strcasecmp(*ep, "compress") ||
237                                         !mh_strcasecmp(*ep, "x-compress"))) {
238                                 zP = 1;
239                                 continue;
240                         }
241                         /* check for "conversions=gzip" attribute */
242                         if ((!mh_strcasecmp(*ap, "conversions") ||
243                                         !mh_strcasecmp(*ap, "x-conversions"))
244                                         && (!mh_strcasecmp(*ep, "gzip") ||
245                                         !mh_strcasecmp(*ep, "x-gzip"))) {
246                                 gzP = 1;
247                                 continue;
248                         }
249                 }
250
251                 if (tarP) {
252                         ct->c_showproc = getcpy(zP ? "%euncompress | tar tvf -" : (gzP ? "%egzip -dc | tar tvf -" : "%etar tvf -"));
253                         if (!ct->c_storeproc) {
254                                 if (autosw) {
255                                         ct->c_storeproc = getcpy(zP ? "| uncompress | tar xvpf -" : (gzP ? "| gzip -dc | tar xvpf -" : "| tar xvpf -"));
256                                         ct->c_umask = 0022;
257                                 } else {
258                                         ct->c_storeproc= getcpy(zP ? "%m%P.tar.Z" : (gzP ? "%m%P.tar.gz" : "%m%P.tar"));
259                                 }
260                         }
261                 }
262         }
263
264         return store_content(ct, NULL);
265 }
266
267
268 /*
269 ** Store the content of a multipart message
270 */
271
272 static int
273 store_multi(CT ct)
274 {
275         int result;
276         struct multipart *m = (struct multipart *) ct->c_ctparams;
277         struct part *part;
278
279         result = NOTOK;
280         for (part = m->mp_parts; part; part = part->mp_next) {
281                 CT  p = part->mp_part;
282
283                 if (part_ok(p, 1) && type_ok(p, 1)) {
284                         result = store_switch(p);
285                         if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
286                                 break;
287                 }
288         }
289
290         return result;
291 }
292
293
294 /*
295 ** Reassemble and store the contents of a collection
296 ** of messages of type "message/partial".
297 */
298
299 static int
300 store_partial(CT ct)
301 {
302         int cur, hi, i;
303         CT p, *ctp, *ctq;
304         CT *base;
305         struct partial *pm, *qm;
306
307         qm = (struct partial *) ct->c_ctparams;
308         if (qm->pm_stored)
309                 return OK;
310
311         hi = i = 0;
312         for (ctp = cts; *ctp; ctp++) {
313                 p = *ctp;
314                 if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
315                         pm = (struct partial *) p->c_ctparams;
316                         if (!pm->pm_stored &&
317                                         strcmp(qm->pm_partid, pm->pm_partid)
318                                         == 0) {
319                                 pm->pm_marked = pm->pm_partno;
320                                 if (pm->pm_maxno)
321                                         hi = pm->pm_maxno;
322                                 pm->pm_stored = 1;
323                                 i++;
324                         } else
325                                 pm->pm_marked = 0;
326                 }
327         }
328
329         if (hi == 0) {
330                 advise(NULL, "missing (at least) last part of multipart message");
331                 return NOTOK;
332         }
333
334         if ((base = (CT *) calloc((size_t) (i + 1), sizeof(*base))) == NULL)
335                 adios(NULL, "out of memory");
336
337         ctq = base;
338         for (ctp = cts; *ctp; ctp++) {
339                 p = *ctp;
340                 if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
341                         pm = (struct partial *) p->c_ctparams;
342                         if (pm->pm_marked)
343                                 *ctq++ = p;
344                 }
345         }
346         *ctq = NULL;
347
348         if (i > 1)
349                 qsort((char *) base, i, sizeof(*base), (qsort_comp) ct_compar);
350
351         cur = 1;
352         for (ctq = base; *ctq; ctq++) {
353                 p = *ctq;
354                 pm = (struct partial *) p->c_ctparams;
355                 if (pm->pm_marked != cur) {
356                         if (pm->pm_marked == cur - 1) {
357                                 admonish(NULL, "duplicate part %d of %d part multipart message", pm->pm_marked, hi);
358                                 continue;
359                         }
360
361 missing_part:
362                         advise (NULL, "missing %spart %d of %d part multipart message", cur != hi ? "(at least) " : "", cur, hi);
363                         goto losing;
364                 } else
365                         cur++;
366         }
367         if (hi != --cur) {
368                 cur = hi;
369                 goto missing_part;
370         }
371
372         /*
373         ** Now cycle through the sorted list of messages of type
374         ** "message/partial" and save/append them to a file.
375         */
376
377         ctq = base;
378         ct = *ctq++;
379         if (store_content(ct, NULL) == NOTOK) {
380 losing:
381                 free((char *) base);
382                 return NOTOK;
383         }
384
385         for (; *ctq; ctq++) {
386                 p = *ctq;
387                 if (store_content(p, ct) == NOTOK)
388                         goto losing;
389         }
390
391         free((char *) base);
392         return OK;
393 }
394
395
396 /*
397 ** Store content from a message of type "message/external".
398 */
399
400 static int
401 store_external(CT ct)
402 {
403         int result = NOTOK;
404         struct exbody *e = (struct exbody *) ct->c_ctparams;
405         CT p = e->eb_content;
406
407         if (!type_ok(p, 1))
408                 return OK;
409
410         /*
411         ** Check if the parameters for the external body
412         ** specified a filename.
413         */
414         if (autosw) {
415                 char *cp;
416
417                 if ((cp = e->eb_name) && *cp != '/' && *cp != '.' &&
418                                 *cp != '|' && *cp != '!' && !strchr(cp, '%')) {
419                         if (!ct->c_storeproc)
420                                 ct->c_storeproc = getcpy(cp);
421                         if (!p->c_storeproc)
422                                 p->c_storeproc = getcpy(cp);
423                 }
424         }
425
426         /*
427         ** Since we will let the Content structure for the
428         ** external body substitute for the current content,
429         ** we temporarily change its partno (number inside
430         ** multipart), so everything looks right.
431         */
432         p->c_partno = ct->c_partno;
433
434         /* we probably need to check if content is really there */
435         result = store_switch(p);
436
437         p->c_partno = NULL;
438         return result;
439 }
440
441
442 /*
443 ** Compare the numbering from two different
444 ** message/partials (needed for sorting).
445 */
446
447 static int
448 ct_compar(CT *a, CT *b)
449 {
450         struct partial *am = (struct partial *) ((*a)->c_ctparams);
451         struct partial *bm = (struct partial *) ((*b)->c_ctparams);
452
453         return (am->pm_marked - bm->pm_marked);
454 }
455
456
457 /*
458 ** Store contents of a message or message part to
459 ** a folder, a file, the standard output, or pass
460 ** the contents to a command.
461 **
462 ** If the current content to be saved is a followup part
463 ** to a collection of messages of type "message/partial",
464 ** then field "p" is a pointer to the Content structure
465 ** to the first message/partial in the group.
466 */
467
468 static int
469 store_content(CT ct, CT p)
470 {
471         int appending = 0, msgnum = 0;
472         int is_partial = 0, first_partial = 0;
473         int last_partial = 0;
474         char *cp, buffer[BUFSIZ];
475
476         /*
477         ** Do special processing for messages of
478         ** type "message/partial".
479         **
480         ** We first check if this content is of type
481         ** "message/partial".  If it is, then we need to check
482         ** whether it is the first and/or last in the group.
483         **
484         ** Then if "p" is a valid pointer, it points to the Content
485         ** structure of the first partial in the group.  So we copy
486         ** the file name and/or folder name from that message.  In
487         ** this case, we also note that we will be appending.
488         */
489         if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
490                 struct partial *pm = (struct partial *) ct->c_ctparams;
491
492                 /* Yep, it's a message/partial */
493                 is_partial = 1;
494
495                 /* But is it the first and/or last in the collection? */
496                 if (pm->pm_partno == 1)
497                         first_partial = 1;
498                 if (pm->pm_maxno && pm->pm_partno == pm->pm_maxno)
499                         last_partial = 1;
500
501                 /*
502                 ** If "p" is a valid pointer, then it points to the
503                 ** Content structure for the first message in the group.
504                 ** So we just copy the filename or foldername information
505                 ** from the previous iteration of this function.
506                 */
507                 if (p) {
508                         appending = 1;
509                         ct->c_storage = getcpy(p->c_storage);
510
511                         /* record the folder name */
512                         if (p->c_folder) {
513                                 ct->c_folder = getcpy(p->c_folder);
514                         }
515                         goto got_filename;
516                 }
517         }
518
519         /*
520         ** Get storage formatting string.
521         **
522         ** 1) If we have storeproc defined, then use that
523         ** 2) Else check for a mhstore-store-<type>/<subtype> entry
524         ** 3) Else check for a mhstore-store-<type> entry
525         ** 4) Else if content is "message", use "+" (current folder)
526         ** 5) Else use string "%m%P.%s".
527         */
528         if ((cp = ct->c_storeproc) == NULL || *cp == '\0') {
529                 CI ci = &ct->c_ctinfo;
530
531                 snprintf(buffer, sizeof(buffer), "%s-store-%s/%s",
532                                 invo_name, ci->ci_type, ci->ci_subtype);
533                 if ((cp = context_find(buffer)) == NULL || *cp == '\0') {
534                         snprintf(buffer, sizeof(buffer), "%s-store-%s",
535                                         invo_name, ci->ci_type);
536                         if ((cp = context_find(buffer)) == NULL ||
537                                         *cp == '\0') {
538                                 cp = ct->c_type == CT_MESSAGE ?
539                                                 "+" : "%m%P.%s";
540                         }
541                 }
542         }
543
544         /*
545         ** Check the beginning of storage formatting string
546         ** to see if we are saving content to a folder.
547         */
548         if (*cp == '+' || *cp == '@') {
549                 char *tmpfilenam, *folder;
550
551                 /* Store content in temporary file for now */
552                 tmpfilenam = m_mktemp(invo_name, NULL, NULL);
553                 ct->c_storage = getcpy(tmpfilenam);
554
555                 /* Get the folder name */
556                 if (cp[1])
557                         folder = getcpy(expandfol(cp));
558                 else
559                         folder = getcurfol();
560
561                 /* Check if folder exists */
562                 create_folder(toabsdir(folder), 0, exit);
563
564                 /* Record the folder name */
565                 ct->c_folder = getcpy(folder);
566
567                 if (cp[1])
568                         free(folder);
569
570                 goto got_filename;
571         }
572
573         /*
574         ** Parse and expand the storage formatting string
575         ** in `cp' into `buffer'.
576         */
577         parse_format_string(ct, cp, buffer, sizeof(buffer), dir);
578
579         /*
580         ** If formatting begins with '|' or '!', then pass
581         ** content to standard input of a command and return.
582         */
583         if (buffer[0] == '|' || buffer[0] == '!')
584                 return show_content_aux(ct, 1, 0, buffer + 1, dir);
585
586         /* record the filename */
587         ct->c_storage = getcpy(buffer);
588
589 got_filename:
590         /* flush the output stream */
591         fflush(stdout);
592
593         /* Now save or append the content to a file */
594         if (output_content_file(ct, appending) == NOTOK)
595                 return NOTOK;
596
597         /*
598         ** If necessary, link the file into a folder and remove
599         ** the temporary file.  If this message is a partial,
600         ** then only do this if it is the last one in the group.
601         */
602         if (ct->c_folder && (!is_partial || last_partial)) {
603                 msgnum = output_content_folder(ct->c_folder, ct->c_storage);
604                 unlink(ct->c_storage);
605                 if (msgnum == NOTOK)
606                         return NOTOK;
607         }
608
609         /*
610         ** Now print out the name/number of the message
611         ** that we are storing.
612         */
613         if (is_partial) {
614                 if (first_partial)
615                         fprintf(stderr, "reassembling partials ");
616                 if (last_partial)
617                         fprintf(stderr, "%s", ct->c_file);
618                 else
619                         fprintf(stderr, "%s,", ct->c_file);
620         } else {
621                 fprintf(stderr, "storing message %s", ct->c_file);
622                 if (ct->c_partno)
623                         fprintf(stderr, " part %s", ct->c_partno);
624         }
625
626         /*
627         ** Unless we are in the "middle" of group of message/partials,
628         ** we now print the name of the file, folder, and/or message
629         ** to which we are storing the content.
630         */
631         if (!is_partial || last_partial) {
632                 if (ct->c_folder) {
633                         fprintf(stderr, " to folder %s as message %d\n",
634                                         ct->c_folder, msgnum);
635                 } else if (!strcmp(ct->c_storage, "-")) {
636                         fprintf(stderr, " to stdout\n");
637                 } else {
638                         int cwdlen;
639
640                         cwdlen = strlen(cwd);
641                         fprintf(stderr, " as file %s\n",
642                                         strncmp(ct->c_storage, cwd, cwdlen) ||
643                                         ct->c_storage[cwdlen] != '/' ?
644                                         ct->c_storage :
645                                         ct->c_storage + cwdlen + 1);
646                 }
647         }
648
649         return OK;
650 }
651
652
653 /*
654 ** Output content to a file
655 */
656
657 static int
658 output_content_file(CT ct, int appending)
659 {
660         int filterstate;
661         char *file, buffer[BUFSIZ];
662         long pos, last;
663         FILE *fp;
664
665         /*
666         ** If the pathname is absolute, make sure
667         ** all the relevant directories exist.
668         */
669         if (strchr(ct->c_storage, '/') && make_intermediates(ct->c_storage)
670                         == NOTOK)
671                 return NOTOK;
672
673         if (ct->c_encoding != CE_7BIT) {
674                 int cc, fd;
675
676                 if (!ct->c_ceopenfnx) {
677                         advise(NULL, "don't know how to decode part %s of message %s", ct->c_partno, ct->c_file);
678                         return NOTOK;
679                 }
680
681                 file = appending || !strcmp(ct->c_storage, "-") ?
682                                 NULL : ct->c_storage;
683                 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
684                         return NOTOK;
685                 if (!strcmp(file, ct->c_storage)) {
686                         (*ct->c_ceclosefnx) (ct);
687                         return OK;
688                 }
689
690                 /*
691                 ** Send to standard output
692                 */
693                 if (!strcmp(ct->c_storage, "-")) {
694                         int gd;
695
696                         if ((gd = dup(fileno(stdout))) == NOTOK) {
697                                 advise("stdout", "unable to dup");
698 losing:
699                                 (*ct->c_ceclosefnx) (ct);
700                                 return NOTOK;
701                         }
702                         if ((fp = fdopen(gd, appending ? "a" : "w")) == NULL) {
703                                 advise("stdout", "unable to fdopen (%d, \"%s\") from", gd, appending ? "a" : "w");
704                                 close(gd);
705                                 goto losing;
706                         }
707                 } else {
708                         /*
709                         ** Open output file
710                         */
711                         if ((fp = fopen(ct->c_storage, appending ? "a" : "w"))
712                                         == NULL) {
713                                 advise(ct->c_storage, "unable to fopen for %s",
714                                                 appending ?
715                                                 "appending" : "writing");
716                                 goto losing;
717                         }
718                 }
719
720                 /*
721                 ** Filter the header fields of the initial enclosing
722                 ** message/partial into the file.
723                 */
724                 if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
725                         struct partial *pm = (struct partial *) ct->c_ctparams;
726
727                         if (pm->pm_partno == 1)
728                                 copy_some_headers(fp, ct);
729                 }
730
731                 for (;;) {
732                         switch (cc = read(fd, buffer, sizeof(buffer))) {
733                         case NOTOK:
734                                 advise(file, "error reading content from");
735                                 break;
736
737                         case OK:
738                                 break;
739
740                         default:
741                                 fwrite(buffer, sizeof(*buffer), cc, fp);
742                                 continue;
743                         }
744                         break;
745                 }
746
747                 (*ct->c_ceclosefnx) (ct);
748
749                 if (cc != NOTOK && fflush(fp))
750                         advise(ct->c_storage, "error writing to");
751
752                 fclose(fp);
753
754                 return (cc != NOTOK ? OK : NOTOK);
755         }
756
757         if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
758                 advise(ct->c_file, "unable to open for reading");
759                 return NOTOK;
760         }
761
762         pos = ct->c_begin;
763         last = ct->c_end;
764         fseek(ct->c_fp, pos, SEEK_SET);
765
766         if (!strcmp(ct->c_storage, "-")) {
767                 int gd;
768
769                 if ((gd = dup(fileno(stdout))) == NOTOK) {
770                         advise("stdout", "unable to dup");
771                         return NOTOK;
772                 }
773                 if ((fp = fdopen(gd, appending ? "a" : "w")) == NULL) {
774                         advise("stdout", "unable to fdopen (%d, \"%s\") from",
775                                         gd, appending ? "a" : "w");
776                         close(gd);
777                         return NOTOK;
778                 }
779         } else {
780                 if ((fp = fopen(ct->c_storage, appending ? "a" : "w"))
781                                 == NULL) {
782                         advise(ct->c_storage, "unable to fopen for %s",
783                                         appending ? "appending" : "writing");
784                         return NOTOK;
785                 }
786         }
787
788         /*
789         ** Copy a few of the header fields of the initial
790         ** enclosing message/partial into the file.
791         */
792         filterstate = 0;
793         if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
794                 struct partial *pm = (struct partial *) ct->c_ctparams;
795
796                 if (pm->pm_partno == 1) {
797                         copy_some_headers(fp, ct);
798                         filterstate = 1;
799                 }
800         }
801
802         while (fgets(buffer, sizeof(buffer) - 1, ct->c_fp)) {
803                 if ((pos += strlen(buffer)) > last) {
804                         int diff;
805
806                         diff = strlen(buffer) - (pos - last);
807                         if (diff >= 0)
808                                 buffer[diff] = '\0';
809                 }
810                 /*
811                 ** If this is the first content of a group of
812                 ** message/partial contents, then we only copy a few
813                 ** of the header fields of the enclosed message.
814                 */
815                 if (filterstate) {
816                         switch (buffer[0]) {
817                         case ' ':
818                         case '\t':
819                                 if (filterstate < 0)
820                                         buffer[0] = 0;
821                                 break;
822
823                         case '\n':
824                                 filterstate = 0;
825                                 break;
826
827                         default:
828                                 if (!uprf(buffer, XXX_FIELD_PRF) && !uprf(buffer, VRSN_FIELD) && !uprf(buffer, "Subject:") && !uprf(buffer, "Encrypted:") && !uprf(buffer, "Message-ID:")) {
829                                         filterstate = -1;
830                                         buffer[0] = 0;
831                                         break;
832                                 }
833                                 filterstate = 1;
834                                 break;
835                         }
836                 }
837                 fputs(buffer, fp);
838                 if (pos >= last)
839                         break;
840         }
841
842         if (fflush(fp))
843                 advise(ct->c_storage, "error writing to");
844
845         fclose(fp);
846         fclose(ct->c_fp);
847         ct->c_fp = NULL;
848         return OK;
849 }
850
851
852 /*
853 ** Add a file to a folder.
854 **
855 ** Return the new message number of the file
856 ** when added to the folder.  Return -1, if
857 ** there is an error.
858 */
859
860 static int
861 output_content_folder(char *folder, char *filename)
862 {
863         int msgnum;
864         struct msgs *mp;
865
866         /* Read the folder. */
867         if ((mp = folder_read(folder))) {
868                 /* Link file into folder */
869                 msgnum = folder_addmsg(&mp, filename, 0, 0, 0, 0, NULL);
870         } else {
871                 advise(NULL, "unable to read folder %s", folder);
872                 return NOTOK;
873         }
874
875         /* free folder structure */
876         folder_free(mp);
877
878         /*
879         ** Return msgnum.  We are relying on the fact that
880         ** msgnum will be -1, if folder_addmsg() had an error.
881         */
882         return msgnum;
883 }
884
885
886 /*
887 ** Parse and expand the storage formatting string
888 ** pointed to by "cp" into "buffer".
889 */
890
891 static int
892 parse_format_string(CT ct, char *cp, char *buffer, int buflen, char *dir)
893 {
894         int len;
895         char *bp;
896         CI ci = &ct->c_ctinfo;
897
898         /*
899         ** If storage string is "-", just copy it, and
900         ** return (send content to standard output).
901         */
902         if (cp[0] == '-' && cp[1] == '\0') {
903                 strncpy(buffer, cp, buflen);
904                 return 0;
905         }
906
907         bp = buffer;
908         bp[0] = '\0';
909
910         /*
911         ** If formatting string is a pathname that doesn't
912         ** begin with '/', then preface the path with the
913         ** appropriate directory.
914         */
915         if (*cp != '/' && *cp != '|' && *cp != '!') {
916                 snprintf(bp, buflen, "%s/", dir[1] ? dir : "");
917                 len = strlen(bp);
918                 bp += len;
919                 buflen -= len;
920         }
921
922         for (; *cp; cp++) {
923
924                 /* We are processing a storage escape */
925                 if (*cp == '%') {
926                         switch (*++cp) {
927                         case 'a':
928                                 /*
929                                 ** Insert parameters from Content-Type.
930                                 ** This is only valid for '|' commands.
931                                 */
932                                 if (buffer[0] != '|' && buffer[0] != '!') {
933                                         *bp++ = *--cp;
934                                         *bp = '\0';
935                                         buflen--;
936                                         continue;
937                                 } else {
938                                         char **ap, **ep;
939                                         char *s = "";
940
941                                         for (ap=ci->ci_attrs, ep=ci->ci_values;
942                                                          *ap; ap++, ep++) {
943                                                 snprintf(bp, buflen,
944                                                                 "%s%s=\"%s\"",
945                                                                 s, *ap, *ep);
946                                                 len = strlen(bp);
947                                                 bp += len;
948                                                 buflen -= len;
949                                                 s = " ";
950                                         }
951                                 }
952                                 break;
953
954                         case 'm':
955                                 /* insert message number */
956                                 snprintf(bp, buflen, "%s",
957                                                 mhbasename(ct->c_file));
958                                 break;
959
960                         case 'P':
961                                 /* insert part number with leading dot */
962                                 if (ct->c_partno)
963                                         snprintf(bp, buflen, ".%s",
964                                                         ct->c_partno);
965                                 break;
966
967                         case 'p':
968                                 /* insert part number withouth leading dot */
969                                 if (ct->c_partno)
970                                         strncpy(bp, ct->c_partno, buflen);
971                                 break;
972
973                         case 't':
974                                 /* insert content type */
975                                 strncpy(bp, ci->ci_type, buflen);
976                                 break;
977
978                         case 's':
979                                 /* insert content subtype */
980                                 strncpy(bp, ci->ci_subtype, buflen);
981                                 break;
982
983                         case '%':
984                                 /* insert the character % */
985                                 goto raw;
986
987                         default:
988                                 *bp++ = *--cp;
989                                 *bp = '\0';
990                                 buflen--;
991                                 continue;
992                         }
993
994                         /* Advance bp and decrement buflen */
995                         len = strlen(bp);
996                         bp += len;
997                         buflen -= len;
998
999                 } else {
1000 raw:
1001                         *bp++ = *cp;
1002                         *bp = '\0';
1003                         buflen--;
1004                 }
1005         }
1006
1007         return 0;
1008 }
1009
1010
1011 /*
1012 ** Check if the content specifies a filename
1013 ** in its MIME parameters.
1014 */
1015
1016 static void
1017 get_storeproc(CT ct)
1018 {
1019         char **ap, **ep, *cp;
1020         CI ci = &ct->c_ctinfo;
1021
1022         /*
1023         ** If the storeproc has already been defined,
1024         ** we just return (for instance, if this content
1025         ** is part of a "message/external".
1026         */
1027         if (ct->c_storeproc)
1028                 return;
1029
1030         /*
1031         ** Check the attribute/value pairs, for the attribute "name".
1032         ** If found, do a few sanity checks and copy the value into
1033         ** the storeproc.
1034         */
1035         for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1036                 if (!mh_strcasecmp(*ap, "name") && *(cp = *ep) != '/' &&
1037                                 *cp != '.' && *cp != '|' && *cp != '!' &&
1038                                 !strchr(cp, '%')) {
1039                         ct->c_storeproc = getcpy(cp);
1040                         return;
1041                 }
1042         }
1043 }
1044
1045
1046 /*
1047 ** Copy some of the header fields of the initial message/partial
1048 ** message into the header of the reassembled message.
1049 */
1050
1051 static int
1052 copy_some_headers(FILE *out, CT ct)
1053 {
1054         HF hp;
1055
1056         hp = ct->c_first_hf;  /* start at first header field */
1057
1058         while (hp) {
1059                 /*
1060                 ** A few of the header fields of the enclosing
1061                 ** messages are not copied.
1062                 */
1063                 if (!uprf(hp->name, XXX_FIELD_PRF) &&
1064                                 mh_strcasecmp(hp->name, VRSN_FIELD) &&
1065                                 mh_strcasecmp(hp->name, "Subject") &&
1066                                 mh_strcasecmp(hp->name, "Encrypted") &&
1067                                 mh_strcasecmp(hp->name, "Message-ID"))
1068                         fprintf(out, "%s:%s", hp->name, hp->value);
1069                 hp = hp->next;  /* next header field */
1070         }
1071
1072         return OK;
1073 }