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