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

Revision 12455, 23.6 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12454, which included commits to RCS files with non-trunk default branches.
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            return OK;
685        }
686
687        /*
688         * Send to standard output
689         */
690        if (!strcmp (ct->c_storage, "-")) {
691            int gd;
692
693            if ((gd = dup (fileno (stdout))) == NOTOK) {
694                advise ("stdout", "unable to dup");
695losing:
696                (*ct->c_ceclosefnx) (ct);
697                return NOTOK;
698            }
699            if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) {
700                advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd,
701                        appending ? "a" : "w");
702                close (gd);
703                goto losing;
704            }
705        } else {
706            /*
707             * Open output file
708             */
709            if ((fp = fopen (ct->c_storage, appending ? "a" : "w")) == NULL) {
710                advise (ct->c_storage, "unable to fopen for %s",
711                        appending ? "appending" : "writing");
712                goto losing;
713            }
714        }
715
716        /*
717         * Filter the header fields of the initial enclosing
718         * message/partial into the file.
719         */
720        if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
721            struct partial *pm = (struct partial *) ct->c_ctparams;
722
723            if (pm->pm_partno == 1)
724                copy_some_headers (fp, ct);
725        }
726
727        for (;;) {
728            switch (cc = read (fd, buffer, sizeof(buffer))) {
729                case NOTOK:
730                    advise (file, "error reading content from");
731                    break;
732
733                case OK:
734                    break;
735
736                default:
737                    fwrite (buffer, sizeof(*buffer), cc, fp);
738                    continue;
739            }
740            break;
741        }
742
743        (*ct->c_ceclosefnx) (ct);
744
745        if (cc != NOTOK && fflush (fp))
746            advise (ct->c_storage, "error writing to");
747
748        fclose (fp);
749
750        return (cc != NOTOK ? OK : NOTOK);
751    }
752
753    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
754        advise (ct->c_file, "unable to open for reading");
755        return NOTOK;
756    }
757
758    pos = ct->c_begin;
759    last = ct->c_end;
760    fseek (ct->c_fp, pos, SEEK_SET);
761
762    if (!strcmp (ct->c_storage, "-")) {
763        int gd;
764
765        if ((gd = dup (fileno (stdout))) == NOTOK) {
766            advise ("stdout", "unable to dup");
767            return NOTOK;
768        }
769        if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) {
770            advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd,
771                    appending ? "a" : "w");
772            close (gd);
773            return NOTOK;
774        }
775    } else {
776        if ((fp = fopen (ct->c_storage, appending ? "a" : "w")) == NULL) {
777            advise (ct->c_storage, "unable to fopen for %s",
778                    appending ? "appending" : "writing");
779            return NOTOK;
780        }
781    }
782
783    /*
784     * Copy a few of the header fields of the initial
785     * enclosing message/partial into the file.
786     */
787    filterstate = 0;
788    if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) {
789        struct partial *pm = (struct partial *) ct->c_ctparams;
790
791        if (pm->pm_partno == 1) {
792            copy_some_headers (fp, ct);
793            filterstate = 1;
794        }
795    }
796
797    while (fgets (buffer, sizeof(buffer) - 1, ct->c_fp)) {
798        if ((pos += strlen (buffer)) > last) {
799            int diff;
800
801            diff = strlen (buffer) - (pos - last);
802            if (diff >= 0)
803                buffer[diff] = '\0';
804        }
805        /*
806         * If this is the first content of a group of
807         * message/partial contents, then we only copy a few
808         * of the header fields of the enclosed message.
809         */
810        if (filterstate) {
811            switch (buffer[0]) {
812                case ' ':
813                case '\t':
814                    if (filterstate < 0)
815                        buffer[0] = 0;
816                    break;
817
818                case '\n':
819                    filterstate = 0;
820                    break;
821
822                default:
823                    if (!uprf (buffer, XXX_FIELD_PRF)
824                            && !uprf (buffer, VRSN_FIELD)
825                            && !uprf (buffer, "Subject:")
826                            && !uprf (buffer, "Encrypted:")
827                            && !uprf (buffer, "Message-ID:")) {
828                        filterstate = -1;
829                        buffer[0] = 0;
830                        break;
831                    }
832                    filterstate = 1;
833                    break;
834            }
835        }
836        fputs (buffer, fp);
837        if (pos >= last)
838            break;
839    }
840
841    if (fflush (fp))
842        advise (ct->c_storage, "error writing to");
843
844    fclose (fp);
845    fclose (ct->c_fp);
846    ct->c_fp = NULL;
847    return OK;
848}
849
850
851/*
852 * Check if folder exists, and create
853 * if necessary.
854 */
855
856static int
857check_folder (char *folder)
858{
859    char *folderdir;
860    struct stat st;
861
862    /* expand path to the folder */
863    folderdir = m_mailpath (folder);
864
865    /* Check if folder exists */
866    if (stat (folderdir, &st) == NOTOK) {
867        int answer;
868        char *ep;
869
870        if (errno != ENOENT) {
871            advise (folderdir, "error on folder");
872            return NOTOK;
873        }
874
875        ep = concat ("Create folder \"", folderdir, "\"? ", NULL);
876        answer = getanswer (ep);
877        free (ep);
878
879        if (!answer)
880            return NOTOK;
881
882        if (!makedir (folderdir)) {
883            advise (NULL, "unable to create folder %s", folderdir);
884            return NOTOK;
885        }
886    }
887
888    return OK;
889}
890
891
892/*
893 * Add a file to a folder.
894 *
895 * Return the new message number of the file
896 * when added to the folder.  Return -1, if
897 * there is an error.
898 */
899
900static int
901output_content_folder (char *folder, char *filename)
902{
903    int msgnum;
904    struct msgs *mp;
905
906    /* Read the folder. */
907    if ((mp = folder_read (folder))) {
908        /* Link file into folder */
909        msgnum = folder_addmsg (&mp, filename, 0, 0, 0);
910    } else {
911        advise (NULL, "unable to read folder %s", folder);
912        return NOTOK;
913    }
914
915    /* free folder structure */
916    folder_free (mp);
917
918    /*
919     * Return msgnum.  We are relying on the fact that
920     * msgnum will be -1, if folder_addmsg() had an error.
921     */
922    return msgnum;
923}
924
925
926/*
927 * Parse and expand the storage formatting string
928 * pointed to by "cp" into "buffer".
929 */
930
931static int
932parse_format_string (CT ct, char *cp, char *buffer, int buflen, char *dir)
933{
934    int len;
935    char *bp;
936    CI ci = &ct->c_ctinfo;
937
938    /*
939     * If storage string is "-", just copy it, and
940     * return (send content to standard output).
941     */
942    if (cp[0] == '-' && cp[1] == '\0') {
943        strncpy (buffer, cp, buflen);
944        return 0;
945    }
946
947    bp = buffer;
948    bp[0] = '\0';
949
950    /*
951     * If formatting string is a pathname that doesn't
952     * begin with '/', then preface the path with the
953     * appropriate directory.
954     */
955    if (*cp != '/' && *cp != '|' && *cp != '!') {
956        snprintf (bp, buflen, "%s/", dir[1] ? dir : "");
957        len = strlen (bp);
958        bp += len;
959        buflen -= len;
960    }
961
962    for (; *cp; cp++) {
963
964        /* We are processing a storage escape */
965        if (*cp == '%') {
966            switch (*++cp) {
967                case 'a':
968                    /*
969                     * Insert parameters from Content-Type.
970                     * This is only valid for '|' commands.
971                     */
972                    if (buffer[0] != '|' && buffer[0] != '!') {
973                        *bp++ = *--cp;
974                        *bp = '\0';
975                        buflen--;
976                        continue;
977                    } else {
978                        char **ap, **ep;
979                        char *s = "";
980
981                        for (ap = ci->ci_attrs, ep = ci->ci_values;
982                                 *ap; ap++, ep++) {
983                            snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
984                            len = strlen (bp);
985                            bp += len;
986                            buflen -= len;
987                            s = " ";
988                        }
989                    }
990                    break;
991
992                case 'm':
993                    /* insert message number */
994                    snprintf (bp, buflen, "%s", r1bindex (ct->c_file, '/'));
995                    break;
996
997                case 'P':
998                    /* insert part number with leading dot */
999                    if (ct->c_partno)
1000                        snprintf (bp, buflen, ".%s", ct->c_partno);
1001                    break;
1002
1003                case 'p':
1004                    /* insert part number withouth leading dot */
1005                    if (ct->c_partno)
1006                        strncpy (bp, ct->c_partno, buflen);
1007                    break;
1008
1009                case 't':
1010                    /* insert content type */
1011                    strncpy (bp, ci->ci_type, buflen);
1012                    break;
1013
1014                case 's':
1015                    /* insert content subtype */
1016                    strncpy (bp, ci->ci_subtype, buflen);
1017                    break;
1018
1019                case '%':
1020                    /* insert the character % */
1021                    goto raw;
1022
1023                default:
1024                    *bp++ = *--cp;
1025                    *bp = '\0';
1026                    buflen--;
1027                    continue;
1028            }
1029
1030            /* Advance bp and decrement buflen */
1031            len = strlen (bp);
1032            bp += len;
1033            buflen -= len;
1034
1035        } else {
1036raw:
1037            *bp++ = *cp;
1038            *bp = '\0';
1039            buflen--;
1040        }
1041    }
1042
1043    return 0;
1044}
1045
1046
1047/*
1048 * Check if the content specifies a filename
1049 * in its MIME parameters.
1050 */
1051
1052static void
1053get_storeproc (CT ct)
1054{
1055    char **ap, **ep, *cp;
1056    CI ci = &ct->c_ctinfo;
1057
1058    /*
1059     * If the storeproc has already been defined,
1060     * we just return (for instance, if this content
1061     * is part of a "message/external".
1062     */
1063    if (ct->c_storeproc)
1064        return;
1065
1066    /*
1067     * Check the attribute/value pairs, for the attribute "name".
1068     * If found, do a few sanity checks and copy the value into
1069     * the storeproc.
1070     */
1071    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1072        if (!strcasecmp (*ap, "name")
1073            && *(cp = *ep) != '/'
1074            && *cp != '.'
1075            && *cp != '|'
1076            && *cp != '!'
1077            && !strchr (cp, '%')) {
1078            ct->c_storeproc = add (cp, NULL);
1079            return;
1080        }
1081    }
1082}
1083
1084
1085/*
1086 * Copy some of the header fields of the initial message/partial
1087 * message into the header of the reassembled message.
1088 */
1089
1090static int
1091copy_some_headers (FILE *out, CT ct)
1092{
1093    HF hp;
1094
1095    hp = ct->c_first_hf;        /* start at first header field */
1096
1097    while (hp) {
1098        /*
1099         * A few of the header fields of the enclosing
1100         * messages are not copied.
1101         */
1102        if (!uprf (hp->name, XXX_FIELD_PRF)
1103                && strcasecmp (hp->name, VRSN_FIELD)
1104                && strcasecmp (hp->name, "Subject")
1105                && strcasecmp (hp->name, "Encrypted")
1106                && strcasecmp (hp->name, "Message-ID"))
1107            fprintf (out, "%s:%s", hp->name, hp->value);
1108        hp = hp->next;  /* next header field */
1109    }
1110
1111    return OK;
1112}
Note: See TracBrowser for help on using the repository browser.