source: trunk/third/nmh/uip/mhstoresbr.c @ 22937

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