source: trunk/third/nmh/uip/mhbuildsbr.c @ 17598

Revision 17598, 93.3 KB checked in by rbasch, 22 years ago (diff)
From the nmh mainline: Allow the display of 8-bit encoded messages.
Line 
1
2/*
3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
4 *
5 * $Id: mhbuildsbr.c,v 1.2 2002-05-17 21:32:05 rbasch Exp $
6 */
7
8/*
9 * This code was originally part of mhn.c.  I split it into
10 * a separate program (mhbuild.c) and then later split it
11 * again (mhbuildsbr.c).  But the code still has some of
12 * the mhn.c code in it.  This program needs additional
13 * streamlining and removal of unneeded code.
14 */
15
16#include <h/mh.h>
17#include <fcntl.h>
18#include <h/signals.h>
19#include <h/md5.h>
20#include <errno.h>
21#include <signal.h>
22#include <zotnet/mts/mts.h>
23#include <zotnet/tws/tws.h>
24#include <h/mime.h>
25#include <h/mhparse.h>
26
27#ifdef HAVE_SYS_WAIT_H
28# include <sys/wait.h>
29#endif
30
31
32extern int errno;
33
34extern int debugsw;
35extern int verbosw;
36
37extern int ebcdicsw;
38extern int listsw;
39extern int rfc934sw;
40
41extern int endian;      /* mhmisc.c */
42
43/* cache policies */
44extern int rcachesw;    /* mhcachesbr.c */
45extern int wcachesw;    /* mhcachesbr.c */
46
47int checksw = 0;        /* Add Content-MD5 field */
48
49/*
50 * Directory to place tmp files.  This must
51 * be set before these routines are called.
52 */
53char *tmp;
54
55pid_t xpid = 0;
56
57static char prefix[] = "----- =_aaaaaaaaaa";
58
59/*
60 * Structure for mapping types to their internal flags
61 */
62struct k2v {
63    char *kv_key;
64    int   kv_value;
65};
66
67/*
68 * Structures for TEXT messages
69 */
70static struct k2v SubText[] = {
71    { "plain",    TEXT_PLAIN },
72    { "richtext", TEXT_RICHTEXT },  /* defined in RFC-1341    */
73    { "enriched", TEXT_ENRICHED },  /* defined in RFC-1896    */
74    { NULL,       TEXT_UNKNOWN }    /* this one must be last! */
75};
76
77static struct k2v Charset[] = {
78    { "us-ascii",   CHARSET_USASCII },
79    { "iso-8859-1", CHARSET_LATIN },
80    { NULL,         CHARSET_UNKNOWN }  /* this one must be last! */
81};
82
83/*
84 * Structures for MULTIPART messages
85 */
86static struct k2v SubMultiPart[] = {
87    { "mixed",       MULTI_MIXED },
88    { "alternative", MULTI_ALTERNATE },
89    { "digest",      MULTI_DIGEST },
90    { "parallel",    MULTI_PARALLEL },
91    { NULL,          MULTI_UNKNOWN }    /* this one must be last! */
92};
93
94/*
95 * Structures for MESSAGE messages
96 */
97static struct k2v SubMessage[] = {
98    { "rfc822",        MESSAGE_RFC822 },
99    { "partial",       MESSAGE_PARTIAL },
100    { "external-body", MESSAGE_EXTERNAL },
101    { NULL,            MESSAGE_UNKNOWN }        /* this one must be last! */
102};
103
104/*
105 * Structure for APPLICATION messages
106 */
107static struct k2v SubApplication[] = {
108    { "octet-stream", APPLICATION_OCTETS },
109    { "postscript",   APPLICATION_POSTSCRIPT },
110    { NULL,           APPLICATION_UNKNOWN }     /* this one must be last! */
111};
112
113
114/* mhmisc.c */
115int make_intermediates (char *);
116void content_error (char *, CT, char *, ...);
117
118/* mhcachesbr.c */
119int find_cache (CT, int, int *, char *, char *, int);
120
121/* ftpsbr.c */
122int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
123
124/* mhfree.c */
125void free_content (CT);
126void free_ctinfo (CT);
127void free_encoding (CT, int);
128
129/*
130 * prototypes
131 */
132CT build_mime (char *);
133int pidcheck (int);
134
135/*
136 * static prototypes
137 */
138static CT get_content (FILE *, char *, int);
139static int add_header (CT, char *, char *);
140static int get_ctinfo (char *, CT, int);
141static int get_comment (CT, char **, int);
142static int InitGeneric (CT);
143static int InitText (CT);
144static int InitMultiPart (CT);
145static void reverse_parts (CT);
146static int InitMessage (CT);
147static int params_external (CT, int);
148static int InitApplication (CT);
149static int init_decoded_content (CT);
150static int init_encoding (CT, OpenCEFunc);
151static void close_encoding (CT);
152static unsigned long size_encoding (CT);
153static int InitBase64 (CT);
154static int openBase64 (CT, char **);
155static int InitQuoted (CT);
156static int openQuoted (CT, char **);
157static int Init7Bit (CT);
158static int open7Bit (CT, char **);
159static int openExternal (CT, CT, CE, char **, int *);
160static int InitFile (CT);
161static int openFile (CT, char **);
162static int InitFTP (CT);
163static int openFTP (CT, char **);
164static int InitMail (CT);
165static int openMail (CT, char **);
166static char *fgetstr (char *, int, FILE *);
167static int user_content (FILE *, char *, char *, CT *);
168static void set_id (CT, int);
169static int compose_content (CT);
170static int scan_content (CT);
171static int build_headers (CT);
172static char *calculate_digest (CT, int);
173static int readDigest (CT, char *);
174
175/*
176 * Structures for mapping (content) types to
177 * the functions to handle them.
178 */
179struct str2init {
180    char *si_key;
181    int   si_val;
182    InitFunc si_init;
183};
184
185static struct str2init str2cts[] = {
186    { "application", CT_APPLICATION, InitApplication },
187    { "audio",       CT_AUDIO,       InitGeneric },
188    { "image",       CT_IMAGE,       InitGeneric },
189    { "message",     CT_MESSAGE,     InitMessage },
190    { "multipart",   CT_MULTIPART,   InitMultiPart },
191    { "text",        CT_TEXT,        InitText },
192    { "video",       CT_VIDEO,       InitGeneric },
193    { NULL,          CT_EXTENSION,   NULL },  /* these two must be last! */
194    { NULL,          CT_UNKNOWN,     NULL },
195};
196
197static struct str2init str2ces[] = {
198    { "base64",           CE_BASE64,    InitBase64 },
199    { "quoted-printable", CE_QUOTED,    InitQuoted },
200    { "8bit",             CE_8BIT,      Init7Bit },
201    { "7bit",             CE_7BIT,      Init7Bit },
202    { "binary",           CE_BINARY,    NULL },
203    { NULL,               CE_EXTENSION, NULL },  /* these two must be last! */
204    { NULL,               CE_UNKNOWN,   NULL },
205};
206
207/*
208 * NOTE WELL: si_key MUST NOT have value of NOTOK
209 *
210 * si_key is 1 if access method is anonymous.
211 */
212static struct str2init str2methods[] = {
213    { "afs",         1, InitFile },
214    { "anon-ftp",    1, InitFTP },
215    { "ftp",         0, InitFTP },
216    { "local-file",  0, InitFile },
217    { "mail-server", 0, InitMail },
218    { NULL,          0, NULL }
219};
220
221
222int
223pidcheck (int status)
224{
225    if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
226        return status;
227
228    fflush (stdout);
229    fflush (stderr);
230    done (1);
231    /* NOTREACHED */
232}
233
234
235/*
236 * Main routine for translating composition file
237 * into valid MIME message.  It translates the draft
238 * into a content structure (actually a tree of content
239 * structures).  This message then can be manipulated
240 * in various ways, including being output via
241 * output_message().
242 */
243
244CT
245build_mime (char *infile)
246{
247    int compnum, state;
248    char buf[BUFSIZ], name[NAMESZ];
249    char *cp, *np, *vp;
250    struct multipart *m;
251    struct part **pp;
252    CT ct;
253    FILE *in;
254
255    umask (~m_gmprot ());
256
257    /* open the composition draft */
258    if ((in = fopen (infile, "r")) == NULL)
259        adios (infile, "unable to open for reading");
260
261    /*
262     * Allocate space for primary (outside) content
263     */
264    if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
265        adios (NULL, "out of memory");
266
267    /*
268     * Allocate structure for handling decoded content
269     * for this part.  We don't really need this, but
270     * allocate it to remain consistent.
271     */
272    init_decoded_content (ct);
273
274    /*
275     * Parse some of the header fields in the composition
276     * draft into the linked list of header fields for
277     * the new MIME message.
278     */
279    for (compnum = 1, state = FLD;;) {
280        switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
281        case FLD:
282        case FLDPLUS:
283        case FLDEOF:
284            compnum++;
285
286            /* abort if draft has Mime-Version header field */
287            if (!strcasecmp (name, VRSN_FIELD))
288                adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
289
290            /* abort if draft has Content-Transfer-Encoding header field */
291            if (!strcasecmp (name, ENCODING_FIELD))
292                adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
293
294            /* ignore any Content-Type fields in the header */
295            if (!strcasecmp (name, TYPE_FIELD)) {
296                while (state == FLDPLUS)
297                    state = m_getfld (state, name, buf, sizeof(buf), in);
298                goto finish_field;
299            }
300
301            /* get copies of the buffers */
302            np = add (name, NULL);
303            vp = add (buf, NULL);
304
305            /* if necessary, get rest of field */
306            while (state == FLDPLUS) {
307                state = m_getfld (state, name, buf, sizeof(buf), in);
308                vp = add (buf, vp);     /* add to previous value */
309            }
310
311            /* Now add the header data to the list */
312            add_header (ct, np, vp);
313
314finish_field:
315            /* if this wasn't the last header field, then continue */
316            if (state != FLDEOF)
317                continue;
318            /* else fall... */
319
320        case FILEEOF:
321            adios (NULL, "draft has empty body -- no directives!");
322            /* NOTREACHED */
323
324        case BODY:
325        case BODYEOF:
326            fseek (in, (long) (-strlen (buf)), SEEK_CUR);
327            break;
328
329        case LENERR:
330        case FMTERR:
331            adios (NULL, "message format error in component #%d", compnum);
332
333        default:
334            adios (NULL, "getfld() returned %d", state);
335        }
336        break;
337    }
338
339    /*
340     * Now add the MIME-Version header field
341     * to the list of header fields.
342     */
343    np = add (VRSN_FIELD, NULL);
344    vp = concat (" ", VRSN_VALUE, "\n", NULL);
345    add_header (ct, np, vp);
346
347    /*
348     * We initally assume we will find multiple contents in the
349     * draft.  So create a multipart/mixed content to hold everything.
350     * We can remove this later, if it is not needed.
351     */
352    if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
353        done (1);
354    ct->c_type = CT_MULTIPART;
355    ct->c_subtype = MULTI_MIXED;
356    ct->c_file = add (infile, NULL);
357
358    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
359        adios (NULL, "out of memory");
360    ct->c_ctparams = (void *) m;
361    pp = &m->mp_parts;
362
363    /*
364     * read and parse the composition file
365     * and the directives it contains.
366     */
367    while (fgetstr (buf, sizeof(buf) - 1, in)) {
368        struct part *part;
369        CT p;
370
371        if (user_content (in, infile, buf, &p) == DONE) {
372            admonish (NULL, "ignoring spurious #end");
373            continue;
374        }
375        if (!p)
376            continue;
377
378        if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
379            adios (NULL, "out of memory");
380        *pp = part;
381        pp = &part->mp_next;
382        part->mp_part = p;
383    }
384
385    /*
386     * close the composition draft since
387     * it's not needed any longer.
388     */
389    fclose (in);
390
391    /* check if any contents were found */
392    if (!m->mp_parts)
393        adios (NULL, "no content directives found");
394
395    /*
396     * If only one content was found, then remove and
397     * free the outer multipart content.
398     */
399    if (!m->mp_parts->mp_next) {
400        CT p;
401
402        p = m->mp_parts->mp_part;
403        m->mp_parts->mp_part = NULL;
404
405        /* move header fields */
406        p->c_first_hf = ct->c_first_hf;
407        p->c_last_hf = ct->c_last_hf;
408        ct->c_first_hf = NULL;
409        ct->c_last_hf = NULL;
410
411        free_content (ct);
412        ct = p;
413    } else {
414        set_id (ct, 1);
415    }
416
417    /*
418     * Fill out, or expand directives.  Parse and execute
419     * commands specified by profile composition strings.
420     */
421    compose_content (ct);
422
423    if ((cp = strchr(prefix, 'a')) == NULL)
424        adios (NULL, "internal error(4)");
425
426    /*
427     * Scan the contents.  Choose a transfer encoding, and
428     * check if prefix for multipart boundary clashes with
429     * any of the contents.
430     */
431    while (scan_content (ct) == NOTOK) {
432        if (*cp < 'z') {
433            (*cp)++;
434        } else {
435            if (*++cp == 0)
436                adios (NULL, "giving up trying to find a unique delimiter string");
437            else
438                (*cp)++;
439        }
440    }
441
442    /* Build the rest of the header field structures */
443    build_headers (ct);
444
445    return ct;
446}
447
448
449/*
450 * Main routine for reading/parsing the headers
451 * of a message content.
452 *
453 * toplevel =  1   # we are at the top level of the message
454 * toplevel =  0   # we are inside message type or multipart type
455 *                 # other than multipart/digest
456 * toplevel = -1   # we are inside multipart/digest
457 */
458
459static CT
460get_content (FILE *in, char *file, int toplevel)
461{
462    int compnum, state;
463    char buf[BUFSIZ], name[NAMESZ];
464    CT ct;
465
466    if (!(ct = (CT) calloc (1, sizeof(*ct))))
467        adios (NULL, "out of memory");
468
469    ct->c_fp = in;
470    ct->c_file = add (file, NULL);
471    ct->c_begin = ftell (ct->c_fp) + 1;
472
473    /*
474     * Read the content headers
475     */
476    for (compnum = 1, state = FLD;;) {
477        switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
478        case FLD:
479        case FLDPLUS:
480        case FLDEOF:
481            compnum++;
482
483            /* Get MIME-Version field */
484            if (!strcasecmp (name, VRSN_FIELD)) {
485                int ucmp;
486                char c, *cp, *dp;
487
488                cp = add (buf, NULL);
489                while (state == FLDPLUS) {
490                    state = m_getfld (state, name, buf, sizeof(buf), in);
491                    cp = add (buf, cp);
492                }
493
494                if (ct->c_vrsn) {
495                    advise (NULL, "message %s has multiple %s: fields (%s)",
496                            ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
497                    free (dp);
498                    free (cp);
499                    goto out;
500                }
501
502                ct->c_vrsn = cp;
503                while (isspace (*cp))
504                    cp++;
505                for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
506                    *dp++ = ' ';
507                for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
508                    if (!isspace (*dp))
509                        break;
510                *++dp = '\0';
511                if (debugsw)
512                    fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
513
514                if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
515                    goto out;
516
517                for (dp = cp; istoken (*dp); dp++)
518                    continue;
519                c = *dp, *dp = '\0';
520                ucmp = !strcasecmp (cp, VRSN_VALUE);
521                *dp = c;
522                if (!ucmp)
523                    admonish (NULL,
524                              "message %s has unknown value for %s: field (%s)",
525                              ct->c_file, VRSN_FIELD, cp);
526                goto got_header;
527            }
528
529            /* Get Content-Type field */
530            if (!strcasecmp (name, TYPE_FIELD)) {
531                char *cp;
532                struct str2init *s2i;
533                CI ci = &ct->c_ctinfo;
534
535                cp = add (buf, NULL);
536                while (state == FLDPLUS) {
537                    state = m_getfld (state, name, buf, sizeof(buf), in);
538                    cp = add (buf, cp);
539                }
540
541                /* Check if we've already seen a Content-Type header */
542                if (ct->c_ctline) {
543                    char *dp = trimcpy (cp);
544
545                    advise (NULL, "message %s has multiple %s: fields (%s)",
546                            ct->c_file, TYPE_FIELD, dp);
547                    free (dp);
548                    free (cp);
549                    goto out;
550                }
551
552                /* Parse the Content-Type field */
553                if (get_ctinfo (cp, ct, 0) == NOTOK)
554                    goto out;
555
556                /*
557                 * Set the Init function and the internal
558                 * flag for this content type.
559                 */
560                for (s2i = str2cts; s2i->si_key; s2i++)
561                    if (!strcasecmp (ci->ci_type, s2i->si_key))
562                        break;
563                if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
564                    s2i++;
565                ct->c_type = s2i->si_val;
566                ct->c_ctinitfnx = s2i->si_init;
567                goto got_header;
568            }
569
570            /* Get Content-Transfer-Encoding field */
571            if (!strcasecmp (name, ENCODING_FIELD)) {
572                char *cp, *dp;
573                char c;
574                struct str2init *s2i;
575
576                cp = add (buf, NULL);
577                while (state == FLDPLUS) {
578                    state = m_getfld (state, name, buf, sizeof(buf), in);
579                    cp = add (buf, cp);
580                }
581
582                /*
583                 * Check if we've already seen the
584                 * Content-Transfer-Encoding field
585                 */
586                if (ct->c_celine) {
587                    advise (NULL, "message %s has multiple %s: fields (%s)",
588                            ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
589                    free (dp);
590                    free (cp);
591                    goto out;
592                }
593
594                ct->c_celine = cp;      /* Save copy of this field */
595                while (isspace (*cp))
596                    cp++;
597                for (dp = cp; istoken (*dp); dp++)
598                    continue;
599                c = *dp;
600                *dp = '\0';
601
602                /*
603                 * Find the internal flag and Init function
604                 * for this transfer encoding.
605                 */
606                for (s2i = str2ces; s2i->si_key; s2i++)
607                    if (!strcasecmp (cp, s2i->si_key))
608                        break;
609                if (!s2i->si_key && !uprf (cp, "X-"))
610                    s2i++;
611                *dp = c;
612                ct->c_encoding = s2i->si_val;
613
614                /* Call the Init function for this encoding */
615                if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
616                    goto out;
617                goto got_header;
618            }
619
620            /* Get Content-ID field */
621            if (!strcasecmp (name, ID_FIELD)) {
622                ct->c_id = add (buf, ct->c_id);
623                while (state == FLDPLUS) {
624                    state = m_getfld (state, name, buf, sizeof(buf), in);
625                    ct->c_id = add (buf, ct->c_id);
626                }
627                goto got_header;
628            }
629
630            /* Get Content-Description field */
631            if (!strcasecmp (name, DESCR_FIELD)) {
632                ct->c_descr = add (buf, ct->c_descr);
633                while (state == FLDPLUS) {
634                    state = m_getfld (state, name, buf, sizeof(buf), in);
635                    ct->c_descr = add (buf, ct->c_descr);
636                }
637                goto got_header;
638            }
639
640            /* Get Content-MD5 field */
641            if (!strcasecmp (name, MD5_FIELD)) {
642                char *cp, *dp, *ep;
643
644                cp = add (buf, NULL);
645                while (state == FLDPLUS) {
646                    state = m_getfld (state, name, buf, sizeof(buf), in);
647                    cp = add (buf, cp);
648                }
649
650                if (!checksw) {
651                    free (cp);
652                    goto got_header;
653                }
654
655                if (ct->c_digested) {
656                    advise (NULL, "message %s has multiple %s: fields (%s)",
657                            ct->c_file, MD5_FIELD, dp = trimcpy (cp));
658                    free (dp);
659                    free (cp);
660                    goto out;
661                }
662
663                ep = cp;
664                while (isspace (*cp))
665                    cp++;
666                for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
667                    *dp++ = ' ';
668                for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
669                    if (!isspace (*dp))
670                        break;
671                *++dp = '\0';
672                if (debugsw)
673                    fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
674
675                if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
676                    free (ep);
677                    goto out;
678                }
679
680                for (dp = cp; *dp && !isspace (*dp); dp++)
681                    continue;
682                *dp = '\0';
683
684                readDigest (ct, cp);
685                free (ep);
686                ct->c_digested++;
687                goto got_header;
688            }
689
690#if 0
691            if (uprf (name, XXX_FIELD_PRF))
692                advise (NULL, "unknown field (%s) in message %s",
693                        name, ct->c_file);
694            /* and fall... */
695#endif
696
697            while (state == FLDPLUS)
698                state = m_getfld (state, name, buf, sizeof(buf), in);
699
700got_header:
701            if (state != FLDEOF) {
702                ct->c_begin = ftell (in) + 1;
703                continue;
704            }
705            /* else fall... */
706
707        case BODY:
708        case BODYEOF:
709            ct->c_begin = ftell (in) - strlen (buf);
710            break;
711
712        case FILEEOF:
713            ct->c_begin = ftell (in);
714            break;
715
716        case LENERR:
717        case FMTERR:
718            adios (NULL, "message format error in component #%d", compnum);
719
720        default:
721            adios (NULL, "getfld() returned %d", state);
722        }
723        break;
724    }
725
726    /*
727     * Check if we saw a Content-Type field.
728     * If not, then assign a default value for
729     * it, and the Init function.
730     */
731    if (!ct->c_ctline) {
732        /*
733         * If we are inside a multipart/digest message,
734         * so default type is message/rfc822
735         */
736        if (toplevel < 0) {
737            if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
738                goto out;
739            ct->c_type = CT_MESSAGE;
740            ct->c_ctinitfnx = InitMessage;
741        } else {
742            /*
743             * Else default type is text/plain
744             */
745            if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
746                goto out;
747            ct->c_type = CT_TEXT;
748            ct->c_ctinitfnx = InitText;
749        }
750    }
751
752    /* Use default Transfer-Encoding, if necessary */
753    if (!ct->c_celine) {
754        ct->c_encoding = CE_7BIT;
755        Init7Bit (ct);
756    }
757
758    return ct;
759
760out:
761    free_content (ct);
762    return NULL;
763}
764
765
766/*
767 * small routine to add header field to list
768 */
769
770static int
771add_header (CT ct, char *name, char *value)
772{
773    HF hp;
774
775    /* allocate header field structure */
776    if (!(hp = malloc (sizeof(*hp))))
777        adios (NULL, "out of memory");
778
779    /* link data into header structure */
780    hp->name = name;
781    hp->value = value;
782    hp->next = NULL;
783
784    /* link header structure into the list */
785    if (ct->c_first_hf == NULL) {
786        ct->c_first_hf = hp;            /* this is the first */
787        ct->c_last_hf = hp;
788    } else {
789        ct->c_last_hf->next = hp;       /* add it to the end */
790        ct->c_last_hf = hp;
791    }
792
793    return 0;
794}
795
796
797/*
798 * Used to parse both:
799 *   1) Content-Type line
800 *   2) composition directives
801 *
802 * and fills in the information of the CTinfo structure.
803 */
804
805static int
806get_ctinfo (char *cp, CT ct, int magic)
807{
808    int i;
809    char *dp, **ap, **ep;
810    char c;
811    CI ci;
812
813    ci = &ct->c_ctinfo;
814    i = strlen (invo_name) + 2;
815
816    /* store copy of Content-Type line */
817    cp = ct->c_ctline = add (cp, NULL);
818
819    while (isspace (*cp))       /* trim leading spaces */
820        cp++;
821
822    /* change newlines to spaces */
823    for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
824        *dp++ = ' ';
825
826    /* trim trailing spaces */
827    for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
828        if (!isspace (*dp))
829            break;
830    *++dp = '\0';
831
832    if (debugsw)
833        fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
834
835    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
836        return NOTOK;
837
838    for (dp = cp; istoken (*dp); dp++)
839        continue;
840    c = *dp, *dp = '\0';
841    ci->ci_type = add (cp, NULL);       /* store content type */
842    *dp = c, cp = dp;
843
844    if (!*ci->ci_type) {
845        advise (NULL, "invalid %s: field in message %s (empty type)",
846                TYPE_FIELD, ct->c_file);
847        return NOTOK;
848    }
849
850    /* down case the content type string */
851    for (dp = ci->ci_type; *dp; dp++)
852        if (isalpha(*dp) && isupper (*dp))
853            *dp = tolower (*dp);
854
855    while (isspace (*cp))
856        cp++;
857
858    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
859        return NOTOK;
860
861    if (*cp != '/') {
862        if (!magic)
863            ci->ci_subtype = add ("", NULL);
864        goto magic_skip;
865    }
866
867    cp++;
868    while (isspace (*cp))
869        cp++;
870
871    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
872        return NOTOK;
873
874    for (dp = cp; istoken (*dp); dp++)
875        continue;
876    c = *dp, *dp = '\0';
877    ci->ci_subtype = add (cp, NULL);    /* store the content subtype */
878    *dp = c, cp = dp;
879
880    if (!*ci->ci_subtype) {
881        advise (NULL,
882                "invalid %s: field in message %s (empty subtype for \"%s\")",
883                TYPE_FIELD, ct->c_file, ci->ci_type);
884        return NOTOK;
885    }
886
887    /* down case the content subtype string */
888    for (dp = ci->ci_subtype; *dp; dp++)
889        if (isalpha(*dp) && isupper (*dp))
890            *dp = tolower (*dp);
891
892magic_skip:
893    while (isspace (*cp))
894        cp++;
895
896    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
897        return NOTOK;
898
899    /*
900     * Parse attribute/value pairs given with Content-Type
901     */
902    ep = (ap = ci->ci_attrs) + NPARMS;
903    while (*cp == ';') {
904        char *vp, *up;
905
906        if (ap >= ep) {
907            advise (NULL,
908                    "too many parameters in message %s's %s: field (%d max)",
909                    ct->c_file, TYPE_FIELD, NPARMS);
910            return NOTOK;
911        }
912
913        cp++;
914        while (isspace (*cp))
915            cp++;
916
917        if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
918            return NOTOK;
919
920        if (*cp == 0) {
921            advise (NULL,
922                    "extraneous trailing ';' in message %s's %s: parameter list",
923                    ct->c_file, TYPE_FIELD);
924            return OK;
925        }
926
927        /* down case the attribute name */
928        for (dp = cp; istoken (*dp); dp++)
929            if (isalpha(*dp) && isupper (*dp))
930                *dp = tolower (*dp);
931
932        for (up = dp; isspace (*dp); )
933            dp++;
934        if (dp == cp || *dp != '=') {
935            advise (NULL,
936                    "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
937                    ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
938            return NOTOK;
939        }
940
941        vp = (*ap = add (cp, NULL)) + (up - cp);
942        *vp = '\0';
943        for (dp++; isspace (*dp); )
944            dp++;
945
946        /* now add the attribute value */
947        ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
948
949        if (*dp == '"') {
950            for (cp = ++dp, dp = vp;;) {
951                switch (c = *cp++) {
952                    case '\0':
953bad_quote:
954                        advise (NULL,
955                                "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
956                                ct->c_file, TYPE_FIELD, i, i, "", *ap);
957                        return NOTOK;
958
959                    case '\\':
960                        *dp++ = c;
961                        if ((c = *cp++) == '\0')
962                            goto bad_quote;
963                        /* else fall... */
964
965                    default:
966                        *dp++ = c;
967                        continue;
968
969                    case '"':
970                        *dp = '\0';
971                        break;
972                }
973                break;
974            }
975        } else {
976            for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
977                continue;
978            *dp = '\0';
979        }
980        if (!*vp) {
981            advise (NULL,
982                    "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
983                    ct->c_file, TYPE_FIELD, i, i, "", *ap);
984            return NOTOK;
985        }
986        ap++;
987
988        while (isspace (*cp))
989            cp++;
990
991        if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
992            return NOTOK;
993    }
994
995    /*
996     * Get any <Content-Id> given in buffer
997     */
998    if (magic && *cp == '<') {
999        if (ct->c_id) {
1000            free (ct->c_id);
1001            ct->c_id = NULL;
1002        }
1003        if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1004            advise (NULL, "invalid ID in message %s", ct->c_file);
1005            return NOTOK;
1006        }
1007        c = *dp;
1008        *dp = '\0';
1009        if (*ct->c_id)
1010            ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1011        else
1012            ct->c_id = NULL;
1013        *dp++ = c;
1014        cp = dp;
1015
1016        while (isspace (*cp))
1017            cp++;
1018    }
1019
1020    /*
1021     * Get any [Content-Description] given in buffer.
1022     */
1023    if (magic && *cp == '[') {
1024        ct->c_descr = ++cp;
1025        for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1026            if (*dp == ']')
1027                break;
1028        if (dp < cp) {
1029            advise (NULL, "invalid description in message %s", ct->c_file);
1030            ct->c_descr = NULL;
1031            return NOTOK;
1032        }
1033       
1034        c = *dp;
1035        *dp = '\0';
1036        if (*ct->c_descr)
1037            ct->c_descr = concat (ct->c_descr, "\n", NULL);
1038        else
1039            ct->c_descr = NULL;
1040        *dp++ = c;
1041        cp = dp;
1042
1043        while (isspace (*cp))
1044            cp++;
1045    }
1046
1047    /*
1048     * Check if anything is left over
1049     */
1050    if (*cp) {
1051        if (magic)
1052            ci->ci_magic = add (cp, NULL);
1053        else
1054            advise (NULL,
1055                    "extraneous information in message %s's %s: field\n%*.*s(%s)",
1056                ct->c_file, TYPE_FIELD, i, i, "", cp);
1057    }
1058
1059    return OK;
1060}
1061
1062
1063static int
1064get_comment (CT ct, char **ap, int istype)
1065{
1066    int i;
1067    char *bp, *cp;
1068    char c, buffer[BUFSIZ], *dp;
1069    CI ci;
1070
1071    ci = &ct->c_ctinfo;
1072    cp = *ap;
1073    bp = buffer;
1074    cp++;
1075
1076    for (i = 0;;) {
1077        switch (c = *cp++) {
1078        case '\0':
1079invalid:
1080        advise (NULL, "invalid comment in message %s's %s: field",
1081                ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1082        return NOTOK;
1083
1084        case '\\':
1085            *bp++ = c;
1086            if ((c = *cp++) == '\0')
1087                goto invalid;
1088            *bp++ = c;
1089            continue;
1090
1091        case '(':
1092            i++;
1093            /* and fall... */
1094        default:
1095            *bp++ = c;
1096            continue;
1097
1098        case ')':
1099            if (--i < 0)
1100                break;
1101            *bp++ = c;
1102            continue;
1103        }
1104        break;
1105    }
1106    *bp = '\0';
1107
1108    if (istype) {
1109        if ((dp = ci->ci_comment)) {
1110            ci->ci_comment = concat (dp, " ", buffer, NULL);
1111            free (dp);
1112        } else {
1113            ci->ci_comment = add (buffer, NULL);
1114        }
1115    }
1116
1117    while (isspace (*cp))
1118        cp++;
1119
1120    *ap = cp;
1121    return OK;
1122}
1123
1124
1125/*
1126 * CONTENTS
1127 *
1128 * Handles content types audio, image, and video.
1129 * There's not much to do right here.
1130 */
1131
1132static int
1133InitGeneric (CT ct)
1134{
1135    return OK;          /* not much to do here */
1136}
1137
1138
1139/*
1140 * TEXT
1141 */
1142
1143static int
1144InitText (CT ct)
1145{
1146    char **ap, **ep;
1147    struct k2v *kv;
1148    struct text *t;
1149    CI ci = &ct->c_ctinfo;
1150
1151    /* check for missing subtype */
1152    if (!*ci->ci_subtype)
1153        ci->ci_subtype = add ("plain", ci->ci_subtype);
1154
1155    /* match subtype */
1156    for (kv = SubText; kv->kv_key; kv++)
1157        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1158            break;
1159    ct->c_subtype = kv->kv_value;
1160
1161    /* allocate text character set structure */
1162    if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1163        adios (NULL, "out of memory");
1164    ct->c_ctparams = (void *) t;
1165
1166    /* initially mark character set as unspecified */
1167    t->tx_charset = CHARSET_UNSPECIFIED;
1168
1169    /* scan for charset parameter */
1170    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1171        if (!strcasecmp (*ap, "charset"))
1172            break;
1173
1174    /* check if content specified a character set */
1175    if (*ap) {
1176        /* match character set or set to CHARSET_UNKNOWN */
1177        for (kv = Charset; kv->kv_key; kv++)
1178            if (!strcasecmp (*ep, kv->kv_key))
1179                break;
1180        t->tx_charset = kv->kv_value;
1181    }
1182
1183    return OK;
1184}
1185
1186
1187/*
1188 * MULTIPART
1189 */
1190
1191static int
1192InitMultiPart (CT ct)
1193{
1194    int inout;
1195    long last, pos;
1196    char *cp, *dp, **ap, **ep;
1197    char *bp, buffer[BUFSIZ];
1198    struct multipart *m;
1199    struct k2v *kv;
1200    struct part *part, **next;
1201    CI ci = &ct->c_ctinfo;
1202    CT p;
1203    FILE *fp;
1204
1205    /*
1206     * The encoding for multipart messages must be either
1207     * 7bit, 8bit, or binary (per RFC2045).
1208     */
1209    if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1210        && ct->c_encoding != CE_BINARY) {
1211        admonish (NULL,
1212                  "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1213                  ci->ci_type, ci->ci_subtype, ct->c_file);
1214        return NOTOK;
1215    }
1216
1217    /* match subtype */
1218    for (kv = SubMultiPart; kv->kv_key; kv++)
1219        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1220            break;
1221    ct->c_subtype = kv->kv_value;
1222
1223    /*
1224     * Check for "boundary" parameter, which is
1225     * required for multipart messages.
1226     */
1227    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1228        if (!strcasecmp (*ap, "boundary")) {
1229            bp = *ep;
1230            break;
1231        }
1232    }
1233
1234    /* complain if boundary parameter is missing */
1235    if (!*ap) {
1236        advise (NULL,
1237                "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1238                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1239        return NOTOK;
1240    }
1241
1242    /* allocate primary structure for multipart info */
1243    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1244        adios (NULL, "out of memory");
1245    ct->c_ctparams = (void *) m;
1246
1247    /* check if boundary parameter contains only whitespace characters */
1248    for (cp = bp; isspace (*cp); cp++)
1249        continue;
1250    if (!*cp) {
1251        advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1252                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1253        return NOTOK;
1254    }
1255
1256    /* remove trailing whitespace from boundary parameter */
1257    for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1258        if (!isspace (*dp))
1259            break;
1260    *++dp = '\0';
1261
1262    /* record boundary separators */
1263    m->mp_start = concat (bp, "\n", NULL);
1264    m->mp_stop = concat (bp, "--\n", NULL);
1265
1266    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1267        advise (ct->c_file, "unable to open for reading");
1268        return NOTOK;
1269    }
1270
1271    fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1272    last = ct->c_end;
1273    next = &m->mp_parts;
1274    part = NULL;
1275    inout = 1;
1276
1277    while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1278        if (pos > last)
1279            break;
1280
1281        pos += strlen (buffer);
1282        if (buffer[0] != '-' || buffer[1] != '-')
1283            continue;
1284        if (inout) {
1285            if (strcmp (buffer + 2, m->mp_start))
1286                continue;
1287next_part:
1288            if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1289                adios (NULL, "out of memory");
1290            *next = part;
1291            next = &part->mp_next;
1292
1293            if (!(p = get_content (fp, ct->c_file,
1294                rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1295                fclose (ct->c_fp);
1296                ct->c_fp = NULL;
1297                return NOTOK;
1298            }
1299            p->c_fp = NULL;
1300            part->mp_part = p;
1301            pos = p->c_begin;
1302            fseek (fp, pos, SEEK_SET);
1303            inout = 0;
1304        } else {
1305            if (strcmp (buffer + 2, m->mp_start) == 0) {
1306                inout = 1;
1307end_part:
1308                p = part->mp_part;
1309                p->c_end = ftell(fp) - (strlen(buffer) + 1);
1310                if (p->c_end < p->c_begin)
1311                    p->c_begin = p->c_end;
1312                if (inout)
1313                    goto next_part;
1314                goto last_part;
1315            } else {
1316                if (strcmp (buffer + 2, m->mp_stop) == 0)
1317                    goto end_part;
1318            }
1319        }
1320    }
1321
1322    advise (NULL, "bogus multipart content in message %s", ct->c_file);
1323    if (!inout && part) {
1324        p = part->mp_part;
1325        p->c_end = ct->c_end;
1326
1327        if (p->c_begin >= p->c_end) {
1328            for (next = &m->mp_parts; *next != part;
1329                     next = &((*next)->mp_next))
1330                continue;
1331            *next = NULL;
1332            free_content (p);
1333            free ((char *) part);
1334        }
1335    }
1336
1337last_part:
1338    /* reverse the order of the parts for multipart/alternative */
1339    if (ct->c_subtype == MULTI_ALTERNATE)
1340        reverse_parts (ct);
1341
1342    /*
1343     * label all subparts with part number, and
1344     * then initialize the content of the subpart.
1345     */
1346    {
1347        int partnum;
1348        char *pp;
1349        char partnam[BUFSIZ];
1350
1351        if (ct->c_partno) {
1352            snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1353            pp = partnam + strlen (partnam);
1354        } else {
1355            pp = partnam;
1356        }
1357
1358        for (part = m->mp_parts, partnum = 1; part;
1359                 part = part->mp_next, partnum++) {
1360            p = part->mp_part;
1361
1362            sprintf (pp, "%d", partnum);
1363            p->c_partno = add (partnam, NULL);
1364
1365            /* initialize the content of the subparts */
1366            if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1367                fclose (ct->c_fp);
1368                ct->c_fp = NULL;
1369                return NOTOK;
1370            }
1371        }
1372    }
1373
1374    fclose (ct->c_fp);
1375    ct->c_fp = NULL;
1376    return OK;
1377}
1378
1379
1380/*
1381 * reverse the order of the parts of a multipart
1382 */
1383
1384static void
1385reverse_parts (CT ct)
1386{
1387    int i;
1388    struct multipart *m;
1389    struct part **base, **bmp, **next, *part;
1390
1391    m = (struct multipart *) ct->c_ctparams;
1392
1393    /* if only one part, just return */
1394    if (!m->mp_parts || !m->mp_parts->mp_next)
1395        return;
1396
1397    /* count number of parts */
1398    i = 0;
1399    for (part = m->mp_parts; part; part = part->mp_next)
1400        i++;
1401
1402    /* allocate array of pointers to the parts */
1403    if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1404        adios (NULL, "out of memory");
1405    bmp = base;
1406
1407    /* point at all the parts */
1408    for (part = m->mp_parts; part; part = part->mp_next)
1409        *bmp++ = part;
1410    *bmp = NULL;
1411
1412    /* reverse the order of the parts */
1413    next = &m->mp_parts;
1414    for (bmp--; bmp >= base; bmp--) {
1415        part = *bmp;
1416        *next = part;
1417        next = &part->mp_next;
1418    }
1419    *next = NULL;
1420
1421    /* free array of pointers */
1422    free ((char *) base);
1423}
1424
1425
1426/*
1427 * MESSAGE
1428 */
1429
1430static int
1431InitMessage (CT ct)
1432{
1433    struct k2v *kv;
1434    CI ci = &ct->c_ctinfo;
1435
1436    if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1437        admonish (NULL,
1438                  "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1439                  ci->ci_type, ci->ci_subtype, ct->c_file);
1440        return NOTOK;
1441    }
1442
1443    /* check for missing subtype */
1444    if (!*ci->ci_subtype)
1445        ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1446
1447    /* match subtype */
1448    for (kv = SubMessage; kv->kv_key; kv++)
1449        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1450            break;
1451    ct->c_subtype = kv->kv_value;
1452
1453    switch (ct->c_subtype) {
1454        case MESSAGE_RFC822:
1455            break;
1456
1457        case MESSAGE_PARTIAL:
1458            {
1459                char **ap, **ep;
1460                struct partial *p;
1461
1462                if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1463                    adios (NULL, "out of memory");
1464                ct->c_ctparams = (void *) p;
1465
1466                /* scan for parameters "id", "number", and "total" */
1467                for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1468                    if (!strcasecmp (*ap, "id")) {
1469                        p->pm_partid = add (*ep, NULL);
1470                        continue;
1471                    }
1472                    if (!strcasecmp (*ap, "number")) {
1473                        if (sscanf (*ep, "%d", &p->pm_partno) != 1
1474                                || p->pm_partno < 1) {
1475invalid_param:
1476                            advise (NULL,
1477                                    "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1478                                    *ap, ci->ci_type, ci->ci_subtype,
1479                                    ct->c_file, TYPE_FIELD);
1480                            return NOTOK;
1481                        }
1482                        continue;
1483                    }
1484                    if (!strcasecmp (*ap, "total")) {
1485                        if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1486                                || p->pm_maxno < 1)
1487                            goto invalid_param;
1488                        continue;
1489                    }
1490                }
1491
1492                if (!p->pm_partid
1493                        || !p->pm_partno
1494                        || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1495                    advise (NULL,
1496                            "invalid parameters for \"%s/%s\" type in message %s's %s field",
1497                            ci->ci_type, ci->ci_subtype,
1498                            ct->c_file, TYPE_FIELD);
1499                    return NOTOK;
1500                }
1501            }
1502            break;
1503
1504        case MESSAGE_EXTERNAL:
1505            {
1506                int exresult;
1507                struct exbody *e;
1508                CT p;
1509                FILE *fp;
1510
1511                if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1512                    adios (NULL, "out of memory");
1513                ct->c_ctparams = (void *) e;
1514
1515                if (!ct->c_fp
1516                        && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1517                    advise (ct->c_file, "unable to open for reading");
1518                    return NOTOK;
1519                }
1520
1521                fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1522
1523                if (!(p = get_content (fp, ct->c_file, 0))) {
1524                    fclose (ct->c_fp);
1525                    ct->c_fp = NULL;
1526                    return NOTOK;
1527                }
1528
1529                e->eb_parent = ct;
1530                e->eb_content = p;
1531                p->c_ctexbody = e;
1532                if ((exresult = params_external (ct, 0)) != NOTOK
1533                        && p->c_ceopenfnx == openMail) {
1534                    int cc, size;
1535                    char *bp;
1536                   
1537                    if ((size = ct->c_end - p->c_begin) <= 0) {
1538                        if (!e->eb_subject)
1539                            content_error (NULL, ct,
1540                                           "empty body for access-type=mail-server");
1541                        goto no_body;
1542                    }
1543                   
1544                    if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1545                        adios (NULL, "out of memory");
1546                    fseek (p->c_fp, p->c_begin, SEEK_SET);
1547                    while (size > 0)
1548                        switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1549                            case NOTOK:
1550                                adios ("failed", "fread");
1551
1552                            case OK:
1553                                adios (NULL, "unexpected EOF from fread");
1554
1555                            default:
1556                                bp += cc, size -= cc;
1557                                break;
1558                        }
1559                    *bp = 0;
1560                }
1561no_body:
1562                p->c_fp = NULL;
1563                p->c_end = p->c_begin;
1564
1565                fclose (ct->c_fp);
1566                ct->c_fp = NULL;
1567
1568                if (exresult == NOTOK)
1569                    return NOTOK;
1570                if (e->eb_flags == NOTOK)
1571                    return OK;
1572
1573                switch (p->c_type) {
1574                    case CT_MULTIPART:
1575                        break;
1576
1577                    case CT_MESSAGE:
1578                        if (p->c_subtype != MESSAGE_RFC822)
1579                            break;
1580                        /* else fall... */
1581                    default:
1582                        e->eb_partno = ct->c_partno;
1583                        if (p->c_ctinitfnx)
1584                            (*p->c_ctinitfnx) (p);
1585                        break;
1586                }
1587            }
1588            break;
1589
1590        default:
1591            break;
1592    }
1593
1594    return OK;
1595}
1596
1597
1598static int
1599params_external (CT ct, int composing)
1600{
1601    char **ap, **ep;
1602    struct exbody *e = (struct exbody *) ct->c_ctparams;
1603    CI ci = &ct->c_ctinfo;
1604
1605    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1606        if (!strcasecmp (*ap, "access-type")) {
1607            struct str2init *s2i;
1608            CT p = e->eb_content;
1609
1610            for (s2i = str2methods; s2i->si_key; s2i++)
1611                if (!strcasecmp (*ep, s2i->si_key))
1612                    break;
1613
1614            if (!s2i->si_key) {
1615                e->eb_access = *ep;
1616                e->eb_flags = NOTOK;
1617                p->c_encoding = CE_EXTERNAL;
1618                continue;
1619            }
1620            e->eb_access = s2i->si_key;
1621            e->eb_flags = s2i->si_val;
1622            p->c_encoding = CE_EXTERNAL;
1623
1624            /* Call the Init function for this external type */
1625            if ((*s2i->si_init)(p) == NOTOK)
1626                return NOTOK;
1627            continue;
1628        }
1629        if (!strcasecmp (*ap, "name")) {
1630            e->eb_name = *ep;
1631            continue;
1632        }
1633        if (!strcasecmp (*ap, "permission")) {
1634            e->eb_permission = *ep;
1635            continue;
1636        }
1637        if (!strcasecmp (*ap, "site")) {
1638            e->eb_site = *ep;
1639            continue;
1640        }
1641        if (!strcasecmp (*ap, "directory")) {
1642            e->eb_dir = *ep;
1643            continue;
1644        }
1645        if (!strcasecmp (*ap, "mode")) {
1646            e->eb_mode = *ep;
1647            continue;
1648        }
1649        if (!strcasecmp (*ap, "size")) {
1650            sscanf (*ep, "%lu", &e->eb_size);
1651            continue;
1652        }
1653        if (!strcasecmp (*ap, "server")) {
1654            e->eb_server = *ep;
1655            continue;
1656        }
1657        if (!strcasecmp (*ap, "subject")) {
1658            e->eb_subject = *ep;
1659            continue;
1660        }
1661        if (composing && !strcasecmp (*ap, "body")) {
1662            e->eb_body = getcpy (*ep);
1663            continue;
1664        }
1665    }
1666
1667    if (!e->eb_access) {
1668        advise (NULL,
1669                "invalid parameters for \"%s/%s\" type in message %s's %s field",
1670                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1671        return NOTOK;
1672    }
1673
1674    return OK;
1675}
1676
1677
1678/*
1679 * APPLICATION
1680 */
1681
1682static int
1683InitApplication (CT ct)
1684{
1685    struct k2v *kv;
1686    CI ci = &ct->c_ctinfo;
1687
1688    /* match subtype */
1689    for (kv = SubApplication; kv->kv_key; kv++)
1690        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1691            break;
1692    ct->c_subtype = kv->kv_value;
1693
1694    return OK;
1695}
1696
1697
1698/*
1699 * Set up structures for placing unencoded
1700 * content when building parts.
1701 */
1702
1703static int
1704init_decoded_content (CT ct)
1705{
1706    CE ce;
1707
1708    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1709        adios (NULL, "out of memory");
1710
1711    ct->c_cefile     = ce;
1712    ct->c_ceopenfnx  = open7Bit;        /* since unencoded */
1713    ct->c_ceclosefnx = close_encoding;
1714    ct->c_cesizefnx  = NULL;            /* since unencoded */
1715
1716    return OK;
1717}
1718
1719
1720/*
1721 * TRANSFER ENCODINGS
1722 */
1723
1724static int
1725init_encoding (CT ct, OpenCEFunc openfnx)
1726{
1727    CE ce;
1728
1729    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1730        adios (NULL, "out of memory");
1731
1732    ct->c_cefile     = ce;
1733    ct->c_ceopenfnx  = openfnx;
1734    ct->c_ceclosefnx = close_encoding;
1735    ct->c_cesizefnx  = size_encoding;
1736
1737    return OK;
1738}
1739
1740
1741static void
1742close_encoding (CT ct)
1743{
1744    CE ce;
1745
1746    if (!(ce = ct->c_cefile))
1747        return;
1748
1749    if (ce->ce_fp) {
1750        fclose (ce->ce_fp);
1751        ce->ce_fp = NULL;
1752    }
1753}
1754
1755
1756static unsigned long
1757size_encoding (CT ct)
1758{
1759    int fd;
1760    unsigned long size;
1761    char *file;
1762    CE ce;
1763    struct stat st;
1764
1765    if (!(ce = ct->c_cefile))
1766        return (ct->c_end - ct->c_begin);
1767
1768    if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1769        return (long) st.st_size;
1770
1771    if (ce->ce_file) {
1772        if (stat (ce->ce_file, &st) != NOTOK)
1773            return (long) st.st_size;
1774        else
1775            return 0L;
1776    }
1777
1778    if (ct->c_encoding == CE_EXTERNAL)
1779        return (ct->c_end - ct->c_begin);       
1780
1781    file = NULL;
1782    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1783        return (ct->c_end - ct->c_begin);
1784
1785    if (fstat (fd, &st) != NOTOK)
1786        size = (long) st.st_size;
1787    else
1788        size = 0L;
1789
1790    (*ct->c_ceclosefnx) (ct);
1791    return size;
1792}
1793
1794
1795/*
1796 * BASE64
1797 */
1798
1799static unsigned char b642nib[0x80] = {
1800    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1801    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1802    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1803    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1804    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1805    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1806    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1807    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1808    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1809    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1810    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1811    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1812    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1813    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1814    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1815    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1816};
1817
1818
1819static int
1820InitBase64 (CT ct)
1821{
1822    return init_encoding (ct, openBase64);
1823}
1824
1825
1826static int
1827openBase64 (CT ct, char **file)
1828{
1829    int bitno, cc, digested;
1830    int fd, len, skip;
1831    unsigned long bits;
1832    unsigned char value, *b, *b1, *b2, *b3;
1833    char *cp, *ep, buffer[BUFSIZ];
1834    CE ce;
1835    MD5_CTX mdContext;
1836
1837    b  = (unsigned char *) &bits;
1838    b1 = &b[endian > 0 ? 1 : 2];
1839    b2 = &b[endian > 0 ? 2 : 1];
1840    b3 = &b[endian > 0 ? 3 : 0];
1841
1842    ce = ct->c_cefile;
1843    if (ce->ce_fp) {
1844        fseek (ce->ce_fp, 0L, SEEK_SET);
1845        goto ready_to_go;
1846    }
1847
1848    if (ce->ce_file) {
1849        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1850            content_error (ce->ce_file, ct, "unable to fopen for reading");
1851            return NOTOK;
1852        }
1853        goto ready_to_go;
1854    }
1855
1856    if (*file == NULL) {
1857        ce->ce_file = add (m_scratch ("", tmp), NULL);
1858        ce->ce_unlink = 1;
1859    } else {
1860        ce->ce_file = add (*file, NULL);
1861        ce->ce_unlink = 0;
1862    }
1863
1864    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1865        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1866        return NOTOK;
1867    }
1868
1869    if ((len = ct->c_end - ct->c_begin) < 0)
1870        adios (NULL, "internal error(1)");
1871
1872    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1873        content_error (ct->c_file, ct, "unable to open for reading");
1874        return NOTOK;
1875    }
1876   
1877    if ((digested = ct->c_digested))
1878        MD5Init (&mdContext);
1879
1880    bitno = 18;
1881    bits = 0L;
1882    skip = 0;
1883
1884    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1885    while (len > 0) {
1886        switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1887        case NOTOK:
1888            content_error (ct->c_file, ct, "error reading from");
1889            goto clean_up;
1890
1891        case OK:
1892            content_error (NULL, ct, "premature eof");
1893            goto clean_up;
1894
1895        default:
1896            if (cc > len)
1897                cc = len;
1898            len -= cc;
1899
1900            for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1901                switch (*cp) {
1902                default:
1903                    if (isspace (*cp))
1904                        break;
1905                    if (skip || (*cp & 0x80)
1906                        || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1907                        if (debugsw) {
1908                            fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1909                                *cp,
1910                                (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1911                                skip);
1912                        }
1913                        content_error (NULL, ct,
1914                                       "invalid BASE64 encoding -- continuing");
1915                        continue;
1916                    }
1917
1918                    bits |= value << bitno;
1919test_end:
1920                    if ((bitno -= 6) < 0) {
1921                        putc ((char) *b1, ce->ce_fp);
1922                        if (digested)
1923                            MD5Update (&mdContext, b1, 1);
1924                        if (skip < 2) {
1925                            putc ((char) *b2, ce->ce_fp);
1926                            if (digested)
1927                                MD5Update (&mdContext, b2, 1);
1928                            if (skip < 1) {
1929                                putc ((char) *b3, ce->ce_fp);
1930                                if (digested)
1931                                    MD5Update (&mdContext, b3, 1);
1932                            }
1933                        }
1934
1935                        if (ferror (ce->ce_fp)) {
1936                            content_error (ce->ce_file, ct,
1937                                           "error writing to");
1938                            goto clean_up;
1939                        }
1940                        bitno = 18, bits = 0L, skip = 0;
1941                    }
1942                    break;
1943
1944                case '=':
1945                    if (++skip > 3)
1946                        goto self_delimiting;
1947                    goto test_end;
1948                }
1949            }
1950        }
1951    }
1952
1953    if (bitno != 18) {
1954        if (debugsw)
1955            fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1956
1957        content_error (NULL, ct, "invalid BASE64 encoding");
1958        goto clean_up;
1959    }
1960
1961self_delimiting:
1962    fseek (ct->c_fp, 0L, SEEK_SET);
1963
1964    if (fflush (ce->ce_fp)) {
1965        content_error (ce->ce_file, ct, "error writing to");
1966        goto clean_up;
1967    }
1968
1969    if (digested) {
1970        unsigned char digest[16];
1971
1972        MD5Final (digest, &mdContext);
1973        if (memcmp((char *) digest, (char *) ct->c_digest,
1974                   sizeof(digest) / sizeof(digest[0])))
1975            content_error (NULL, ct,
1976                           "content integrity suspect (digest mismatch) -- continuing");
1977        else
1978            if (debugsw)
1979                fprintf (stderr, "content integrity confirmed\n");
1980    }
1981
1982    fseek (ce->ce_fp, 0L, SEEK_SET);
1983
1984ready_to_go:
1985    *file = ce->ce_file;
1986    return fileno (ce->ce_fp);
1987
1988clean_up:
1989    free_encoding (ct, 0);
1990    return NOTOK;
1991}
1992
1993
1994/*
1995 * QUOTED PRINTABLE
1996 */
1997
1998static char hex2nib[0x80] = {
1999    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2000    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2001    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2002    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2003    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2004    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2005    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2006    0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2007    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2008    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2009    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2010    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2011    0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2012    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2013    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2014    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2015};
2016
2017
2018static int
2019InitQuoted (CT ct)
2020{
2021    return init_encoding (ct, openQuoted);
2022}
2023
2024
2025static int
2026openQuoted (CT ct, char **file)
2027{
2028    int cc, digested, len, quoted;
2029    char *cp, *ep;
2030    char buffer[BUFSIZ];
2031    unsigned char mask;
2032    CE ce;
2033    MD5_CTX mdContext;
2034
2035    ce = ct->c_cefile;
2036    if (ce->ce_fp) {
2037        fseek (ce->ce_fp, 0L, SEEK_SET);
2038        goto ready_to_go;
2039    }
2040
2041    if (ce->ce_file) {
2042        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2043            content_error (ce->ce_file, ct, "unable to fopen for reading");
2044            return NOTOK;
2045        }
2046        goto ready_to_go;
2047    }
2048
2049    if (*file == NULL) {
2050        ce->ce_file = add (m_scratch ("", tmp), NULL);
2051        ce->ce_unlink = 1;
2052    } else {
2053        ce->ce_file = add (*file, NULL);
2054        ce->ce_unlink = 0;
2055    }
2056
2057    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2058        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2059        return NOTOK;
2060    }
2061
2062    if ((len = ct->c_end - ct->c_begin) < 0)
2063        adios (NULL, "internal error(2)");
2064
2065    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2066        content_error (ct->c_file, ct, "unable to open for reading");
2067        return NOTOK;
2068    }
2069
2070    if ((digested = ct->c_digested))
2071        MD5Init (&mdContext);
2072
2073    quoted = 0;
2074#ifdef lint
2075    mask = 0;
2076#endif
2077
2078    fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2079    while (len > 0) {
2080        char *dp;
2081
2082        if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2083            content_error (NULL, ct, "premature eof");
2084            goto clean_up;
2085        }
2086
2087        if ((cc = strlen (buffer)) > len)
2088            cc = len;
2089        len -= cc;
2090
2091        for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2092            if (!isspace (*ep))
2093                break;
2094        *++ep = '\n', ep++;
2095
2096        for (; cp < ep; cp++) {
2097            if (quoted) {
2098                if (quoted > 1) {
2099                    if (!isxdigit (*cp)) {
2100invalid_hex:
2101                        dp = "expecting hexidecimal-digit";
2102                        goto invalid_encoding;
2103                    }
2104                    mask <<= 4;
2105                    mask |= hex2nib[*cp & 0x7f];
2106                    putc (mask, ce->ce_fp);
2107                    if (digested)
2108                        MD5Update (&mdContext, &mask, 1);
2109                } else {
2110                    switch (*cp) {
2111                    case ':':
2112                        putc (*cp, ce->ce_fp);
2113                        if (digested)
2114                            MD5Update (&mdContext, (unsigned char *) ":", 1);
2115                        break;
2116
2117                    default:
2118                        if (!isxdigit (*cp))
2119                            goto invalid_hex;
2120                        mask = hex2nib[*cp & 0x7f];
2121                        quoted = 2;
2122                        continue;
2123                    }
2124                }
2125
2126                if (ferror (ce->ce_fp)) {
2127                    content_error (ce->ce_file, ct, "error writing to");
2128                    goto clean_up;
2129                }
2130                quoted = 0;
2131                continue;
2132            }
2133
2134            switch (*cp) {
2135            default:
2136                if (*cp < '!' || *cp > '~') {
2137                    int i;
2138                    dp = "expecting character in range [!..~]";
2139
2140invalid_encoding:
2141                    i = strlen (invo_name) + 2;
2142                    content_error (NULL, ct,
2143                                   "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2144                                   dp, i, i, "", *cp);
2145                    goto clean_up;
2146                }
2147                /* and fall...*/
2148            case ' ':
2149            case '\t':
2150            case '\n':
2151                putc (*cp, ce->ce_fp);
2152                if (digested) {
2153                    if (*cp == '\n')
2154                        MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2155                    else
2156                        MD5Update (&mdContext, (unsigned char *) cp, 1);
2157                }
2158                if (ferror (ce->ce_fp)) {
2159                    content_error (ce->ce_file, ct, "error writing to");
2160                    goto clean_up;
2161                }
2162                break;
2163
2164            case '=':
2165                if (*++cp != '\n') {
2166                    quoted = 1;
2167                    cp--;
2168                }
2169                break;
2170            }
2171        }
2172    }
2173    if (quoted) {
2174        content_error (NULL, ct,
2175                       "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2176        goto clean_up;
2177    }
2178
2179    fseek (ct->c_fp, 0L, SEEK_SET);
2180
2181    if (fflush (ce->ce_fp)) {
2182        content_error (ce->ce_file, ct, "error writing to");
2183        goto clean_up;
2184    }
2185
2186    if (digested) {
2187        unsigned char digest[16];
2188
2189        MD5Final (digest, &mdContext);
2190        if (memcmp((char *) digest, (char *) ct->c_digest,
2191                   sizeof(digest) / sizeof(digest[0])))
2192            content_error (NULL, ct,
2193                           "content integrity suspect (digest mismatch) -- continuing");
2194        else
2195            if (debugsw)
2196                fprintf (stderr, "content integrity confirmed\n");
2197    }
2198
2199    fseek (ce->ce_fp, 0L, SEEK_SET);
2200
2201ready_to_go:
2202    *file = ce->ce_file;
2203    return fileno (ce->ce_fp);
2204
2205clean_up:
2206    free_encoding (ct, 0);
2207    return NOTOK;
2208}
2209
2210
2211/*
2212 * 7BIT
2213 */
2214
2215static int
2216Init7Bit (CT ct)
2217{
2218    if (init_encoding (ct, open7Bit) == NOTOK)
2219        return NOTOK;
2220
2221    ct->c_cesizefnx = NULL;     /* no need to decode for real size */
2222    return OK;
2223}
2224
2225
2226static int
2227open7Bit (CT ct, char **file)
2228{
2229    int cc, fd, len;
2230    char buffer[BUFSIZ];
2231    CE ce;
2232
2233    ce = ct->c_cefile;
2234    if (ce->ce_fp) {
2235        fseek (ce->ce_fp, 0L, SEEK_SET);
2236        goto ready_to_go;
2237    }
2238
2239    if (ce->ce_file) {
2240        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2241            content_error (ce->ce_file, ct, "unable to fopen for reading");
2242            return NOTOK;
2243        }
2244        goto ready_to_go;
2245    }
2246
2247    if (*file == NULL) {
2248        ce->ce_file = add (m_scratch ("", tmp), NULL);
2249        ce->ce_unlink = 1;
2250    } else {
2251        ce->ce_file = add (*file, NULL);
2252        ce->ce_unlink = 0;
2253    }
2254
2255    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2256        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2257        return NOTOK;
2258    }
2259
2260    if (ct->c_type == CT_MULTIPART) {
2261        char **ap, **ep;
2262        CI ci = &ct->c_ctinfo;
2263
2264        len = 0;
2265        fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2266        len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2267            + 1 + strlen (ci->ci_subtype);
2268        for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2269            putc (';', ce->ce_fp);
2270            len++;
2271
2272            snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2273
2274            if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2275                fputs ("\n\t", ce->ce_fp);
2276                len = 8;
2277            } else {
2278                putc (' ', ce->ce_fp);
2279                len++;
2280            }
2281            fprintf (ce->ce_fp, "%s", buffer);
2282            len += cc;
2283        }
2284
2285        if (ci->ci_comment) {
2286            if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2287                fputs ("\n\t", ce->ce_fp);
2288                len = 8;
2289            }
2290            else {
2291                putc (' ', ce->ce_fp);
2292                len++;
2293            }
2294            fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2295            len += cc;
2296        }
2297        fprintf (ce->ce_fp, "\n");
2298        if (ct->c_id)
2299            fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2300        if (ct->c_descr)
2301            fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2302        fprintf (ce->ce_fp, "\n");
2303    }
2304
2305    if ((len = ct->c_end - ct->c_begin) < 0)
2306        adios (NULL, "internal error(3)");
2307
2308    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2309        content_error (ct->c_file, ct, "unable to open for reading");
2310        return NOTOK;
2311    }
2312
2313    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2314    while (len > 0)
2315        switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2316        case NOTOK:
2317            content_error (ct->c_file, ct, "error reading from");
2318            goto clean_up;
2319
2320        case OK:
2321            content_error (NULL, ct, "premature eof");
2322            goto clean_up;
2323
2324        default:
2325            if (cc > len)
2326                cc = len;
2327            len -= cc;
2328
2329            fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2330            if (ferror (ce->ce_fp)) {
2331                content_error (ce->ce_file, ct, "error writing to");
2332                goto clean_up;
2333            }
2334        }
2335
2336    fseek (ct->c_fp, 0L, SEEK_SET);
2337
2338    if (fflush (ce->ce_fp)) {
2339        content_error (ce->ce_file, ct, "error writing to");
2340        goto clean_up;
2341    }
2342
2343    fseek (ce->ce_fp, 0L, SEEK_SET);
2344
2345ready_to_go:
2346    *file = ce->ce_file;
2347    return fileno (ce->ce_fp);
2348
2349clean_up:
2350    free_encoding (ct, 0);
2351    return NOTOK;
2352}
2353
2354
2355/*
2356 * External
2357 */
2358
2359static int
2360openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2361{
2362    char cachefile[BUFSIZ];
2363
2364    if (ce->ce_fp) {
2365        fseek (ce->ce_fp, 0L, SEEK_SET);
2366        goto ready_already;
2367    }
2368
2369    if (ce->ce_file) {
2370        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2371            content_error (ce->ce_file, ct, "unable to fopen for reading");
2372            return NOTOK;
2373        }
2374        goto ready_already;
2375    }
2376
2377    if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2378                cachefile, sizeof(cachefile)) != NOTOK) {
2379        if ((ce->ce_fp = fopen (cachefile, "r"))) {
2380            ce->ce_file = getcpy (cachefile);
2381            ce->ce_unlink = 0;
2382            goto ready_already;
2383        } else {
2384            admonish (cachefile, "unable to fopen for reading");
2385        }
2386    }
2387
2388    return OK;
2389
2390ready_already:
2391    *file = ce->ce_file;
2392    *fd = fileno (ce->ce_fp);
2393    return DONE;
2394}
2395
2396/*
2397 * File
2398 */
2399
2400static int
2401InitFile (CT ct)
2402{
2403    return init_encoding (ct, openFile);
2404}
2405
2406
2407static int
2408openFile (CT ct, char **file)
2409{
2410    int fd, cachetype;
2411    char cachefile[BUFSIZ];
2412    struct exbody *e = ct->c_ctexbody;
2413    CE ce = ct->c_cefile;
2414
2415    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2416        case NOTOK:
2417            return NOTOK;
2418
2419        case OK:
2420            break;
2421
2422        case DONE:
2423            return fd;
2424    }
2425
2426    if (!e->eb_name) {
2427        content_error (NULL, ct, "missing name parameter");
2428        return NOTOK;
2429    }
2430
2431    ce->ce_file = getcpy (e->eb_name);
2432    ce->ce_unlink = 0;
2433
2434    if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2435        content_error (ce->ce_file, ct, "unable to fopen for reading");
2436        return NOTOK;
2437    }
2438
2439    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2440            && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2441                cachefile, sizeof(cachefile)) != NOTOK) {
2442        int mask;
2443        FILE *fp;
2444
2445        mask = umask (cachetype ? ~m_gmprot () : 0222);
2446        if ((fp = fopen (cachefile, "w"))) {
2447            int cc;
2448            char buffer[BUFSIZ];
2449            FILE *gp = ce->ce_fp;
2450
2451            fseek (gp, 0L, SEEK_SET);
2452
2453            while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2454                       > 0)
2455                fwrite (buffer, sizeof(*buffer), cc, fp);
2456            fflush (fp);
2457
2458            if (ferror (gp)) {
2459                admonish (ce->ce_file, "error reading");
2460                unlink (cachefile);
2461            }
2462            else
2463                if (ferror (fp)) {
2464                    admonish (cachefile, "error writing");
2465                    unlink (cachefile);
2466                }
2467            fclose (fp);
2468        }
2469        umask (mask);
2470    }
2471
2472    fseek (ce->ce_fp, 0L, SEEK_SET);
2473    *file = ce->ce_file;
2474    return fileno (ce->ce_fp);
2475}
2476
2477/*
2478 * FTP
2479 */
2480
2481static int
2482InitFTP (CT ct)
2483{
2484    return init_encoding (ct, openFTP);
2485}
2486
2487
2488static int
2489openFTP (CT ct, char **file)
2490{
2491    int cachetype, caching, fd;
2492    int len, buflen;
2493    char *bp, *ftp, *user, *pass;
2494    char buffer[BUFSIZ], cachefile[BUFSIZ];
2495    struct exbody *e;
2496    CE ce;
2497    static char *username = NULL;
2498    static char *password = NULL;
2499
2500    e  = ct->c_ctexbody;
2501    ce = ct->c_cefile;
2502
2503    if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2504        ftp = NULL;
2505
2506#ifndef BUILTIN_FTP
2507    if (!ftp)
2508        return NOTOK;
2509#endif
2510
2511    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2512        case NOTOK:
2513            return NOTOK;
2514
2515        case OK:
2516            break;
2517
2518        case DONE:
2519            return fd;
2520    }
2521
2522    if (!e->eb_name || !e->eb_site) {
2523        content_error (NULL, ct, "missing %s parameter",
2524                       e->eb_name ? "site": "name");
2525        return NOTOK;
2526    }
2527
2528    if (xpid) {
2529        if (xpid < 0)
2530            xpid = -xpid;
2531        pidcheck (pidwait (xpid, NOTOK));
2532        xpid = 0;
2533    }
2534
2535    /* Get the buffer ready to go */
2536    bp = buffer;
2537    buflen = sizeof(buffer);
2538
2539    /*
2540     * Construct the query message for user
2541     */
2542    snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2543    len = strlen (bp);
2544    bp += len;
2545    buflen -= len;
2546
2547    if (e->eb_partno) {
2548        snprintf (bp, buflen, " (content %s)", e->eb_partno);
2549        len = strlen (bp);
2550        bp += len;
2551        buflen -= len;
2552    }
2553
2554    snprintf (bp, buflen, "\n    using %sFTP from site %s",
2555                    e->eb_flags ? "anonymous " : "", e->eb_site);
2556    len = strlen (bp);
2557    bp += len;
2558    buflen -= len;
2559
2560    if (e->eb_size > 0) {
2561        snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2562        len = strlen (bp);
2563        bp += len;
2564        buflen -= len;
2565    }
2566    snprintf (bp, buflen, "? ");
2567
2568    /*
2569     * Now, check the answer
2570     */
2571    if (!getanswer (buffer))
2572        return NOTOK;
2573
2574    if (e->eb_flags) {
2575        user = "anonymous";
2576        snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2577        pass = buffer;
2578    } else {
2579        ruserpass (e->eb_site, &username, &password);
2580        user = username;
2581        pass = password;
2582    }
2583
2584    ce->ce_unlink = (*file == NULL);
2585    caching = 0;
2586    cachefile[0] = '\0';
2587    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2588            && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2589                cachefile, sizeof(cachefile)) != NOTOK) {
2590        if (*file == NULL) {
2591            ce->ce_unlink = 0;
2592            caching = 1;
2593        }
2594    }
2595
2596    if (*file)
2597        ce->ce_file = add (*file, NULL);
2598    else if (caching)
2599        ce->ce_file = add (cachefile, NULL);
2600    else
2601        ce->ce_file = add (m_scratch ("", tmp), NULL);
2602
2603    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2604        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2605        return NOTOK;
2606    }
2607
2608#ifdef BUILTIN_FTP
2609    if (ftp)
2610#endif
2611    {
2612        int child_id, i, vecp;
2613        char *vec[9];
2614
2615        vecp = 0;
2616        vec[vecp++] = r1bindex (ftp, '/');
2617        vec[vecp++] = e->eb_site;
2618        vec[vecp++] = user;
2619        vec[vecp++] = pass;
2620        vec[vecp++] = e->eb_dir;
2621        vec[vecp++] = e->eb_name;
2622        vec[vecp++] = ce->ce_file,
2623        vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2624                        ? "ascii" : "binary";
2625        vec[vecp] = NULL;
2626
2627        fflush (stdout);
2628
2629        for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2630            sleep (5);
2631        switch (child_id) {
2632            case NOTOK:
2633                adios ("fork", "unable to");
2634                /* NOTREACHED */
2635
2636            case OK:
2637                close (fileno (ce->ce_fp));
2638                execvp (ftp, vec);
2639                fprintf (stderr, "unable to exec ");
2640                perror (ftp);
2641                _exit (-1);
2642                /* NOTREACHED */
2643
2644            default:
2645                if (pidXwait (child_id, NULL)) {
2646#ifdef BUILTIN_FTP
2647losing_ftp:
2648#endif
2649                    username = password = NULL;
2650                    ce->ce_unlink = 1;
2651                    return NOTOK;
2652                }
2653                break;
2654        }
2655    }
2656#ifdef BUILTIN_FTP
2657    else
2658        if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2659                     ce->ce_file,
2660                     e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2661                == NOTOK)
2662            goto losing_ftp;
2663#endif
2664
2665    if (cachefile[0])
2666        if (caching)
2667            chmod (cachefile, cachetype ? m_gmprot () : 0444);
2668        else {
2669            int mask;
2670            FILE *fp;
2671
2672            mask = umask (cachetype ? ~m_gmprot () : 0222);
2673            if ((fp = fopen (cachefile, "w"))) {
2674                int cc;
2675                FILE *gp = ce->ce_fp;
2676
2677                fseek (gp, 0L, SEEK_SET);
2678
2679                while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2680                           > 0)
2681                    fwrite (buffer, sizeof(*buffer), cc, fp);
2682                fflush (fp);
2683
2684                if (ferror (gp)) {
2685                    admonish (ce->ce_file, "error reading");
2686                    unlink (cachefile);
2687                }
2688                else
2689                    if (ferror (fp)) {
2690                        admonish (cachefile, "error writing");
2691                        unlink (cachefile);
2692                    }
2693                fclose (fp);
2694            }
2695            umask (mask);
2696        }
2697
2698    fseek (ce->ce_fp, 0L, SEEK_SET);
2699    *file = ce->ce_file;
2700    return fileno (ce->ce_fp);
2701}
2702
2703
2704/*
2705 * Mail
2706 */
2707
2708static int
2709InitMail (CT ct)
2710{
2711    return init_encoding (ct, openMail);
2712}
2713
2714
2715static int
2716openMail (CT ct, char **file)
2717{
2718    int child_id, fd, i, vecp;
2719    int len, buflen;
2720    char *bp, buffer[BUFSIZ], *vec[7];
2721    struct exbody *e = ct->c_ctexbody;
2722    CE ce = ct->c_cefile;
2723
2724    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2725        case NOTOK:
2726            return NOTOK;
2727
2728        case OK:
2729            break;
2730
2731        case DONE:
2732            return fd;
2733    }
2734
2735    if (!e->eb_server) {
2736        content_error (NULL, ct, "missing server parameter");
2737        return NOTOK;
2738    }
2739
2740    if (xpid) {
2741        if (xpid < 0)
2742            xpid = -xpid;
2743        pidcheck (pidwait (xpid, NOTOK));
2744        xpid = 0;
2745    }
2746
2747    /* Get buffer ready to go */
2748    bp = buffer;
2749    buflen = sizeof(buffer);
2750
2751    /* Now construct query message */
2752    snprintf (bp, buflen, "Retrieve content");
2753    len = strlen (bp);
2754    bp += len;
2755    buflen -= len;
2756
2757    if (e->eb_partno) {
2758        snprintf (bp, buflen, " %s", e->eb_partno);
2759        len = strlen (bp);
2760        bp += len;
2761        buflen -= len;
2762    }
2763
2764    snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2765                    e->eb_server,
2766                    e->eb_subject ? e->eb_subject : e->eb_body);
2767
2768    /* Now, check answer */
2769    if (!getanswer (buffer))
2770        return NOTOK;
2771
2772    vecp = 0;
2773    vec[vecp++] = r1bindex (mailproc, '/');
2774    vec[vecp++] = e->eb_server;
2775    vec[vecp++] = "-subject";
2776    vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2777    vec[vecp++] = "-body";
2778    vec[vecp++] = e->eb_body;
2779    vec[vecp] = NULL;
2780
2781    for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2782        sleep (5);
2783    switch (child_id) {
2784        case NOTOK:
2785            advise ("fork", "unable to");
2786            return NOTOK;
2787
2788        case OK:
2789            execvp (mailproc, vec);
2790            fprintf (stderr, "unable to exec ");
2791            perror (mailproc);
2792            _exit (-1);
2793            /* NOTREACHED */
2794
2795        default:
2796            if (pidXwait (child_id, NULL) == OK)
2797                advise (NULL, "request sent");
2798            break;
2799    }
2800
2801    if (*file == NULL) {
2802        ce->ce_file = add (m_scratch ("", tmp), NULL);
2803        ce->ce_unlink = 1;
2804    } else {
2805        ce->ce_file = add (*file, NULL);
2806        ce->ce_unlink = 0;
2807    }
2808
2809    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2810        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2811        return NOTOK;
2812    }
2813
2814    fseek (ce->ce_fp, 0L, SEEK_SET);
2815    *file = ce->ce_file;
2816    return fileno (ce->ce_fp);
2817}
2818
2819
2820static char *
2821fgetstr (char *s, int n, FILE *stream)
2822{
2823    char *cp, *ep;
2824
2825    for (ep = (cp = s) + n; cp < ep; ) {
2826        int i;
2827
2828        if (!fgets (cp, n, stream))
2829            return (cp != s ? s : NULL);
2830        if (cp == s && *cp != '#')
2831            return s;
2832
2833        cp += (i = strlen (cp)) - 1;
2834        if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2835            break;
2836        *cp = '\0';
2837        n -= (i - 2);
2838    }
2839
2840    return s;
2841}
2842
2843
2844/*
2845 * Parse the composition draft for text and directives.
2846 * Do initial setup of Content structure.
2847 */
2848
2849static int
2850user_content (FILE *in, char *file, char *buf, CT *ctp)
2851{
2852    int extrnal, vrsn;
2853    char *cp, **ap;
2854    char buffer[BUFSIZ];
2855    struct multipart *m;
2856    struct part **pp;
2857    struct stat st;
2858    struct str2init *s2i;
2859    CI ci;
2860    CT ct;
2861    CE ce;
2862
2863    if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2864        *ctp = NULL;
2865        return OK;
2866    }
2867
2868    /* allocate basic Content structure */
2869    if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2870        adios (NULL, "out of memory");
2871    *ctp = ct;
2872
2873    /* allocate basic structure for handling decoded content */
2874    init_decoded_content (ct);
2875    ce = ct->c_cefile;
2876
2877    ci = &ct->c_ctinfo;
2878    set_id (ct, 0);
2879
2880    /*
2881     * Handle inline text.  Check if line
2882     * is one of the following forms:
2883     *
2884     * 1) doesn't begin with '#'        (implicit directive)
2885     * 2) begins with "##"              (implicit directive)
2886     * 3) begins with "#<"
2887     */
2888    if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2889        int headers;
2890        int inlineD;
2891        long pos;
2892        char content[BUFSIZ];
2893        FILE *out;
2894
2895        /* use a temp file to collect the plain text lines */
2896        ce->ce_file = add (m_tmpfil (invo_name), NULL);
2897        ce->ce_unlink = 1;
2898
2899        if ((out = fopen (ce->ce_file, "w")) == NULL)
2900            adios (ce->ce_file, "unable to open for writing");
2901
2902        if (buf[0] == '#' && buf[1] == '<') {
2903            strncpy (content, buf + 2, sizeof(content));
2904            inlineD = 1;
2905            goto rock_and_roll;
2906        } else {
2907            inlineD = 0;
2908        }
2909
2910        /* the directive is implicit */
2911        strncpy (content, "text/plain", sizeof(content));
2912        headers = 0;
2913        strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2914        for (;;) {
2915            int i;
2916
2917            if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2918                && buffer[i = strlen (DESCR_FIELD)] == ':') {
2919                headers = 1;
2920
2921again_descr:
2922                ct->c_descr = add (buffer + i + 1, ct->c_descr);
2923                if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2924                    adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2925                switch (buffer[0]) {
2926                case ' ':
2927                case '\t':
2928                    i = -1;
2929                    goto again_descr;
2930
2931                case '#':
2932                    adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
2933                    /* NOTREACHED */
2934
2935                default:
2936                    break;
2937                }
2938            }
2939
2940            if (headers != 1 || buffer[0] != '\n')
2941                fputs (buffer, out);
2942
2943rock_and_roll:
2944            headers = -1;
2945            pos = ftell (in);
2946            if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
2947                break;
2948            if (buffer[0] == '#') {
2949                char *bp;
2950
2951                if (buffer[1] != '#')
2952                    break;
2953                for (cp = (bp = buffer) + 1; *cp; cp++)
2954                    *bp++ = *cp;
2955                *bp = '\0';
2956            }
2957        }
2958
2959        if (listsw)
2960            ct->c_end = ftell (out);
2961        fclose (out);
2962
2963        /* parse content type */
2964        if (get_ctinfo (content, ct, inlineD) == NOTOK)
2965            done (1);
2966
2967        for (s2i = str2cts; s2i->si_key; s2i++)
2968            if (!strcasecmp (ci->ci_type, s2i->si_key))
2969                break;
2970        if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
2971            s2i++;
2972
2973        /*
2974         * check type specified (possibly implicitly)
2975         */
2976        switch (ct->c_type = s2i->si_val) {
2977        case CT_MESSAGE:
2978            if (!strcasecmp (ci->ci_subtype, "rfc822")) {
2979                ct->c_encoding = CE_7BIT;
2980                goto call_init;
2981            }
2982            /* else fall... */
2983        case CT_MULTIPART:
2984            adios (NULL, "it doesn't make sense to define an in-line %s content",
2985                   ct->c_type == CT_MESSAGE ? "message" : "multipart");
2986            /* NOTREACHED */
2987
2988        default:
2989call_init:
2990            if ((ct->c_ctinitfnx = s2i->si_init))
2991                (*ct->c_ctinitfnx) (ct);
2992            break;
2993        }
2994
2995        if (cp)
2996            fseek (in, pos, SEEK_SET);
2997        return OK;
2998    }
2999
3000    /*
3001     * If we've reached this point, the next line
3002     * must be some type of explicit directive.
3003     */
3004
3005    /* check if directive is external-type */
3006    extrnal = (buf[1] == '@');
3007
3008    /* parse directive */
3009    if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3010        done (1);
3011
3012    /* check directive against the list of MIME types */
3013    for (s2i = str2cts; s2i->si_key; s2i++)
3014        if (!strcasecmp (ci->ci_type, s2i->si_key))
3015            break;
3016
3017    /*
3018     * Check if the directive specified a valid type.
3019     * This will happen if it was one of the following forms:
3020     *
3021     *    #type/subtype  (or)
3022     *    #@type/subtype
3023     */
3024    if (s2i->si_key) {
3025        if (!ci->ci_subtype)
3026            adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3027
3028        switch (ct->c_type = s2i->si_val) {
3029        case CT_MULTIPART:
3030            adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3031                   ci->ci_type, ci->ci_subtype);
3032            /* NOTREACHED */
3033
3034        case CT_MESSAGE:
3035            if (!strcasecmp (ci->ci_subtype, "partial"))
3036                adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3037                       ci->ci_type, ci->ci_subtype);
3038            if (!strcasecmp (ci->ci_subtype, "external-body"))
3039                adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3040                       ci->ci_type, ci->ci_subtype);
3041use_forw:
3042            adios (NULL,
3043                   "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3044                   ci->ci_type, ci->ci_subtype);
3045            /* NOTREACHED */
3046
3047        default:
3048            if ((ct->c_ctinitfnx = s2i->si_init))
3049                (*ct->c_ctinitfnx) (ct);
3050            break;
3051        }
3052
3053        /*
3054         * #@type/subtype (external types directive)
3055         */
3056        if (extrnal) {
3057            struct exbody *e;
3058            CT p;
3059
3060            if (!ci->ci_magic)
3061                adios (NULL, "need external information for \"#@%s/%s\"",
3062                       ci->ci_type, ci->ci_subtype);
3063            p = ct;
3064
3065            snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3066            free (ci->ci_magic);
3067            ci->ci_magic = NULL;
3068
3069            /*
3070             * Since we are using the current Content structure to
3071             * hold information about the type of the external
3072             * reference, we need to create another Content structure
3073             * for the message/external-body to wrap it in.
3074             */
3075            if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3076                adios (NULL, "out of memory");
3077            *ctp = ct;
3078            ci = &ct->c_ctinfo;
3079            if (get_ctinfo (buffer, ct, 0) == NOTOK)
3080                done (1);
3081            ct->c_type = CT_MESSAGE;
3082            ct->c_subtype = MESSAGE_EXTERNAL;
3083
3084            if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3085                adios (NULL, "out of memory");
3086            ct->c_ctparams = (void *) e;
3087
3088            e->eb_parent = ct;
3089            e->eb_content = p;
3090            p->c_ctexbody = e;
3091
3092            if (params_external (ct, 1) == NOTOK)
3093                done (1);
3094
3095            return OK;
3096        }
3097
3098        /* Handle [file] argument */
3099        if (ci->ci_magic) {
3100            /* check if specifies command to execute */
3101            if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3102                for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3103                    continue;
3104                if (!*cp)
3105                    adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3106                cp = add (cp, NULL);
3107                free (ci->ci_magic);
3108                ci->ci_magic = cp;
3109            } else {
3110                /* record filename of decoded contents */
3111                ce->ce_file = ci->ci_magic;
3112                if (access (ce->ce_file, R_OK) == NOTOK)
3113                    adios ("reading", "unable to access %s for", ce->ce_file);
3114                if (listsw && stat (ce->ce_file, &st) != NOTOK)
3115                    ct->c_end = (long) st.st_size;
3116                ci->ci_magic = NULL;
3117            }
3118            return OK;
3119        }
3120
3121        /*
3122         * No [file] argument, so check profile for
3123         * method to compose content.
3124         */
3125        snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3126                invo_name, ci->ci_type, ci->ci_subtype);
3127        if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3128            snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3129            if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3130                content_error (NULL, ct, "don't know how to compose content");
3131                done (1);
3132            }
3133        }
3134        ci->ci_magic = add (cp, NULL);
3135        return OK;
3136    }
3137
3138    if (extrnal)
3139        adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3140
3141    /*
3142     * Message directive
3143     * #forw [+folder] [msgs]
3144     */
3145    if (!strcasecmp (ci->ci_type, "forw")) {
3146        int msgnum;
3147        char *folder, *arguments[MAXARGS];
3148        struct msgs *mp;
3149
3150        if (ci->ci_magic) {
3151            ap = brkstring (ci->ci_magic, " ", "\n");
3152            copyip (ap, arguments, MAXARGS);
3153        } else {
3154            arguments[0] = "cur";
3155            arguments[1] = NULL;
3156        }
3157        folder = NULL;
3158
3159        /* search the arguments for a folder name */
3160        for (ap = arguments; *ap; ap++) {
3161            cp = *ap;
3162            if (*cp == '+' || *cp == '@')
3163                if (folder)
3164                    adios (NULL, "only one folder per #forw directive");
3165                else
3166                    folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3167        }
3168
3169        /* else, use the current folder */
3170        if (!folder)
3171            folder = add (getfolder (1), NULL);
3172
3173        if (!(mp = folder_read (folder)))
3174            adios (NULL, "unable to read folder %s", folder);
3175        for (ap = arguments; *ap; ap++) {
3176            cp = *ap;
3177            if (*cp != '+' && *cp != '@')
3178                if (!m_convert (mp, cp))
3179                    done (1);
3180        }
3181        free (folder);
3182        free_ctinfo (ct);
3183
3184        /*
3185         * If there is more than one message to include, make this
3186         * a content of type "multipart/digest" and insert each message
3187         * as a subpart.  If there is only one message, then make this
3188         * a content of type "message/rfc822".
3189         */
3190        if (mp->numsel > 1) {
3191            /* we are forwarding multiple messages */
3192            if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3193                done (1);
3194            ct->c_type = CT_MULTIPART;
3195            ct->c_subtype = MULTI_DIGEST;
3196
3197            if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3198                adios (NULL, "out of memory");
3199            ct->c_ctparams = (void *) m;
3200            pp = &m->mp_parts;
3201
3202            for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3203                if (is_selected(mp, msgnum)) {
3204                    struct part *part;
3205                    CT p;
3206                    CE pe;
3207
3208                    if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3209                        adios (NULL, "out of memory");
3210                    init_decoded_content (p);
3211                    pe = p->c_cefile;
3212                    if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3213                        done (1);
3214                    p->c_type = CT_MESSAGE;
3215                    p->c_subtype = MESSAGE_RFC822;
3216
3217                    snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3218                    pe->ce_file = add (buffer, NULL);
3219                    if (listsw && stat (pe->ce_file, &st) != NOTOK)
3220                        p->c_end = (long) st.st_size;
3221
3222                    if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3223                        adios (NULL, "out of memory");
3224                    *pp = part;
3225                    pp = &part->mp_next;
3226                    part->mp_part = p;
3227                }
3228            }
3229        } else {
3230            /* we are forwarding one message */
3231            if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3232                done (1);
3233            ct->c_type = CT_MESSAGE;
3234            ct->c_subtype = MESSAGE_RFC822;
3235
3236            msgnum = mp->lowsel;
3237            snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3238            ce->ce_file = add (buffer, NULL);
3239            if (listsw && stat (ce->ce_file, &st) != NOTOK)
3240                ct->c_end = (long) st.st_size;
3241        }
3242
3243        folder_free (mp);       /* free folder/message structure */
3244        return OK;
3245    }
3246
3247    /*
3248     * #end
3249     */
3250    if (!strcasecmp (ci->ci_type, "end")) {
3251        free_content (ct);
3252        *ctp = NULL;
3253        return DONE;
3254    }
3255
3256    /*
3257     * #begin [ alternative | parallel ]
3258     */
3259    if (!strcasecmp (ci->ci_type, "begin")) {
3260        if (!ci->ci_magic) {
3261            vrsn = MULTI_MIXED;
3262            cp = SubMultiPart[vrsn - 1].kv_key;
3263        } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3264            vrsn = MULTI_ALTERNATE;
3265            cp = SubMultiPart[vrsn - 1].kv_key;
3266        } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3267            vrsn = MULTI_PARALLEL;
3268            cp = SubMultiPart[vrsn - 1].kv_key;
3269        } else if (uprf (ci->ci_magic, "digest")) {
3270            goto use_forw;
3271        } else {
3272            vrsn = MULTI_UNKNOWN;
3273            cp = ci->ci_magic;
3274        }
3275
3276        free_ctinfo (ct);
3277        snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3278        if (get_ctinfo (buffer, ct, 0) == NOTOK)
3279            done (1);
3280        ct->c_type = CT_MULTIPART;
3281        ct->c_subtype = vrsn;
3282
3283        if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3284            adios (NULL, "out of memory");
3285        ct->c_ctparams = (void *) m;
3286
3287        pp = &m->mp_parts;
3288        while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3289            struct part *part;
3290            CT p;
3291
3292            if (user_content (in, file, buffer, &p) == DONE) {
3293                if (!m->mp_parts)
3294                    adios (NULL, "empty \"#begin ... #end\" sequence");
3295                return OK;
3296            }
3297            if (!p)
3298                continue;
3299
3300            if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3301                adios (NULL, "out of memory");
3302            *pp = part;
3303            pp = &part->mp_next;
3304            part->mp_part = p;
3305        }
3306        admonish (NULL, "premature end-of-file, missing #end");
3307        return OK;
3308    }
3309
3310    /*
3311     * Unknown directive
3312     */
3313    adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3314    return NOTOK;       /* NOT REACHED */
3315}
3316
3317
3318static void
3319set_id (CT ct, int top)
3320{
3321    char msgid[BUFSIZ];
3322    static int partno;
3323    static time_t clock = 0;
3324    static char *msgfmt;
3325
3326    if (clock == 0) {
3327        time (&clock);
3328        snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3329                (int) getpid(), (long) clock, LocalName());
3330        partno = 0;
3331        msgfmt = getcpy(msgid);
3332    }
3333    snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3334    ct->c_id = getcpy (msgid);
3335}
3336
3337
3338static char ebcdicsafe[0x100] = {
3339    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3340    0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3341    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3342    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3343    0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3344    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3345    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3346    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3347    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3348    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3349    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3350    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3351    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3352    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3353    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3354    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3355    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3356    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3357    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3358    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3359    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3360    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3361    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3362    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3363    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3364    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3365    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3366    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3367    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3368    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3369    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3370    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3371};
3372
3373
3374/*
3375 * Fill out, or expand the various contents in the composition
3376 * draft.  Read-in any necessary files.  Parse and execute any
3377 * commands specified by profile composition strings.
3378 */
3379
3380static int
3381compose_content (CT ct)
3382{
3383    CE ce = ct->c_cefile;
3384
3385    switch (ct->c_type) {
3386    case CT_MULTIPART:
3387    {
3388        int partnum;
3389        char *pp;
3390        char partnam[BUFSIZ];
3391        struct multipart *m = (struct multipart *) ct->c_ctparams;
3392        struct part *part;
3393
3394        if (ct->c_partno) {
3395            snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3396            pp = partnam + strlen (partnam);
3397        } else {
3398            pp = partnam;
3399        }
3400
3401        /* first, we call compose_content on all the subparts */
3402        for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3403            CT p = part->mp_part;
3404
3405            sprintf (pp, "%d", partnum);
3406            p->c_partno = add (partnam, NULL);
3407            if (compose_content (p) == NOTOK)
3408                return NOTOK;
3409        }
3410
3411        /*
3412         * If the -rfc934mode switch is given, then check all
3413         * the subparts of a multipart/digest.  If they are all
3414         * message/rfc822, then mark this content and all
3415         * subparts with the rfc934 compatibility mode flag.
3416         */
3417        if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3418            int is934 = 1;
3419
3420            for (part = m->mp_parts; part; part = part->mp_next) {
3421                CT p = part->mp_part;
3422
3423                if (p->c_subtype != MESSAGE_RFC822) {
3424                    is934 = 0;
3425                    break;
3426                }
3427            }
3428            ct->c_rfc934 = is934;
3429            for (part = m->mp_parts; part; part = part->mp_next) {
3430                CT p = part->mp_part;
3431
3432                if ((p->c_rfc934 = is934))
3433                    p->c_end++;
3434            }
3435        }
3436
3437        if (listsw) {
3438            ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3439            if (ct->c_rfc934)
3440                ct->c_end += 1;
3441
3442            for (part = m->mp_parts; part; part = part->mp_next)
3443                ct->c_end += part->mp_part->c_end + partnum;
3444        }
3445    }
3446    break;
3447
3448    case CT_MESSAGE:
3449        /* Nothing to do for type message */
3450        break;
3451
3452    /*
3453     * Discrete types (text/application/audio/image/video)
3454     */
3455    default:
3456        if (!ce->ce_file) {
3457            pid_t child_id;
3458            int i, xstdout, len, buflen;
3459            char *bp, **ap, *cp;
3460            char *vec[4], buffer[BUFSIZ];
3461            FILE *out;
3462            CI ci = &ct->c_ctinfo;
3463
3464            if (!(cp = ci->ci_magic))
3465                adios (NULL, "internal error(5)");
3466
3467            ce->ce_file = add (m_tmpfil (invo_name), NULL);
3468            ce->ce_unlink = 1;
3469
3470            xstdout = 0;
3471
3472            /* Get buffer ready to go */
3473            bp = buffer;
3474            bp[0] = '\0';
3475            buflen = sizeof(buffer);
3476
3477            /*
3478             * Parse composition string into buffer
3479             */
3480            for ( ; *cp; cp++) {
3481                if (*cp == '%') {
3482                    switch (*++cp) {
3483                    case 'a':
3484                    {
3485                        /* insert parameters from directive */
3486                        char **ep;
3487                        char *s = "";
3488
3489                        for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3490                            snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3491                            len = strlen (bp);
3492                            bp += len;
3493                            buflen -= len;
3494                            s = " ";
3495                        }
3496                    }
3497                    break;
3498
3499                    case 'F':
3500                        /* %f, and stdout is not-redirected */
3501                        xstdout = 1;
3502                        /* and fall... */
3503
3504                    case 'f':
3505                        /*
3506                         * insert temporary filename where
3507                         * content should be written
3508                         */
3509                        snprintf (bp, buflen, "%s", ce->ce_file);
3510                        break;
3511
3512                    case 's':
3513                        /* insert content subtype */
3514                        strncpy (bp, ci->ci_subtype, buflen);
3515                        break;
3516
3517                    case '%':
3518                        /* insert character % */
3519                        goto raw;
3520
3521                    default:
3522                        *bp++ = *--cp;
3523                        *bp = '\0';
3524                        buflen--;
3525                        continue;
3526                    }
3527                    len = strlen (bp);
3528                    bp += len;
3529                    buflen -= len;
3530                } else {
3531raw:
3532                *bp++ = *cp;
3533                *bp = '\0';
3534                buflen--;
3535                }
3536            }
3537
3538            if (verbosw)
3539                printf ("composing content %s/%s from command\n\t%s\n",
3540                        ci->ci_type, ci->ci_subtype, buffer);
3541
3542            fflush (stdout);    /* not sure if need for -noverbose */
3543
3544            vec[0] = "/bin/sh";
3545            vec[1] = "-c";
3546            vec[2] = buffer;
3547            vec[3] = NULL;
3548
3549            if ((out = fopen (ce->ce_file, "w")) == NULL)
3550                adios (ce->ce_file, "unable to open for writing");
3551
3552            for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3553                sleep (5);
3554            switch (child_id) {
3555            case NOTOK:
3556                adios ("fork", "unable to fork");
3557                /* NOTREACHED */
3558
3559            case OK:
3560                if (!xstdout)
3561                    dup2 (fileno (out), 1);
3562                close (fileno (out));
3563                execvp ("/bin/sh", vec);
3564                fprintf (stderr, "unable to exec ");
3565                perror ("/bin/sh");
3566                _exit (-1);
3567                /* NOTREACHED */
3568
3569            default:
3570                fclose (out);
3571                if (pidXwait(child_id, NULL))
3572                    done (1);
3573                break;
3574            }
3575        }
3576
3577        /* Check size of file */
3578        if (listsw && ct->c_end == 0L) {
3579            struct stat st;
3580
3581            if (stat (ce->ce_file, &st) != NOTOK)
3582                ct->c_end = (long) st.st_size;
3583        }
3584        break;
3585    }
3586
3587    return OK;
3588}
3589
3590
3591/*
3592 * Scan the content.
3593 *
3594 *    1) choose a transfer encoding.
3595 *    2) check for clashes with multipart boundary string.
3596 *    3) for text content, figure out which character set is being used.
3597 *
3598 * If there is a clash with one of the contents and the multipart boundary,
3599 * this function will exit with NOTOK.  This will cause the scanning process
3600 * to be repeated with a different multipart boundary.  It is possible
3601 * (although highly unlikely) that this scan will be repeated multiple times.
3602 */
3603
3604static int
3605scan_content (CT ct)
3606{
3607    int len;
3608    int check8bit, contains8bit = 0;      /* check if contains 8bit data                */
3609    int checklinelen, linelen = 0;        /* check for long lines                       */
3610    int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary   */
3611    int checklinespace, linespace = 0;    /* check if any line ends with space          */
3612    int checkebcdic, ebcdicunsafe = 0;    /* check if contains ebcdic unsafe characters */
3613    char *cp, buffer[BUFSIZ];
3614    struct text *t;
3615    FILE *in;
3616    CE ce = ct->c_cefile;
3617
3618    /*
3619     * handle multipart by scanning all subparts
3620     * and then checking their encoding.
3621     */
3622    if (ct->c_type == CT_MULTIPART) {
3623        struct multipart *m = (struct multipart *) ct->c_ctparams;
3624        struct part *part;
3625
3626        /* initially mark the domain of enclosing multipart as 7bit */
3627        ct->c_encoding = CE_7BIT;
3628
3629        for (part = m->mp_parts; part; part = part->mp_next) {
3630            CT p = part->mp_part;
3631
3632            if (scan_content (p) == NOTOK)      /* choose encoding for subpart */
3633                return NOTOK;
3634
3635            /* if necessary, enlarge encoding for enclosing multipart */
3636            if (p->c_encoding == CE_BINARY)
3637                ct->c_encoding = CE_BINARY;
3638            if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3639                ct->c_encoding = CE_8BIT;
3640        }
3641
3642        return OK;
3643    }
3644
3645    /*
3646     * Decide what to check while scanning this content.
3647     */
3648    switch (ct->c_type) {
3649    case CT_TEXT:
3650        check8bit = 1;
3651        checkboundary = 1;
3652        if (ct->c_subtype == TEXT_PLAIN) {
3653            checkebcdic = 0;
3654            checklinelen = 0;
3655            checklinespace = 0;
3656        } else {
3657            checkebcdic = ebcdicsw;
3658            checklinelen = 1;
3659            checklinespace = 1;
3660        }
3661        break;
3662
3663    case CT_APPLICATION:
3664        check8bit = 1;
3665        checkebcdic = ebcdicsw;
3666        checklinelen = 1;
3667        checklinespace = 1;
3668        checkboundary = 1;
3669        break;
3670
3671    case CT_MESSAGE:
3672        check8bit = 0;
3673        checkebcdic = 0;
3674        checklinelen = 0;
3675        checklinespace = 0;
3676
3677        /* don't check anything for message/external */
3678        if (ct->c_subtype == MESSAGE_EXTERNAL)
3679            checkboundary = 0;
3680        else
3681            checkboundary = 1;
3682        break;
3683
3684    case CT_AUDIO:
3685    case CT_IMAGE:
3686    case CT_VIDEO:
3687        /*
3688         * Don't check anything for these types,
3689         * since we are forcing use of base64.
3690         */
3691        check8bit = 0;
3692        checkebcdic = 0;
3693        checklinelen = 0;
3694        checklinespace = 0;
3695        checkboundary = 0;
3696        break;
3697    }
3698
3699    /*
3700     * Scan the unencoded content
3701     */
3702    if (check8bit || checklinelen || checklinespace || checkboundary) {
3703        if ((in = fopen (ce->ce_file, "r")) == NULL)
3704            adios (ce->ce_file, "unable to open for reading");
3705        len = strlen (prefix);
3706
3707        while (fgets (buffer, sizeof(buffer) - 1, in)) {
3708            /*
3709             * Check for 8bit data.
3710             */
3711            if (check8bit) {
3712                for (cp = buffer; *cp; cp++) {
3713                    if (!isascii (*cp)) {
3714                        contains8bit = 1;
3715                        check8bit = 0;  /* no need to keep checking */
3716                    }
3717                    /*
3718                     * Check if character is ebcdic-safe.  We only check
3719                     * this if also checking for 8bit data.
3720                     */
3721                    if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3722                        ebcdicunsafe = 1;
3723                        checkebcdic = 0; /* no need to keep checking */
3724                    }
3725                }
3726            }
3727
3728            /*
3729             * Check line length.
3730             */
3731            if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3732                linelen = 1;
3733                checklinelen = 0;       /* no need to keep checking */
3734            }
3735
3736            /*
3737             * Check if line ends with a space.
3738             */
3739            if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3740                linespace = 1;
3741                checklinespace = 0;     /* no need to keep checking */
3742            }
3743
3744            /*
3745             * Check if content contains a line that clashes
3746             * with our standard boundary for multipart messages.
3747             */
3748            if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3749                for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3750                    if (!isspace (*cp))
3751                        break;
3752                *++cp = '\0';
3753                if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3754                    boundaryclash = 1;
3755                    checkboundary = 0;  /* no need to keep checking */
3756                }
3757            }
3758        }
3759        fclose (in);
3760    }
3761
3762    /*
3763     * Decide which transfer encoding to use.
3764     */
3765    switch (ct->c_type) {
3766    case CT_TEXT:
3767        /*
3768         * If the text content didn't specify a character
3769         * set, we need to figure out which one was used.
3770         */
3771        t = (struct text *) ct->c_ctparams;
3772        if (t->tx_charset == CHARSET_UNSPECIFIED) {
3773            CI ci = &ct->c_ctinfo;
3774            char **ap, **ep;
3775
3776            for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3777                continue;
3778
3779            if (contains8bit) {
3780                t->tx_charset = CHARSET_UNKNOWN;
3781                *ap = concat ("charset=", write_charset_8bit(), NULL);
3782            } else {
3783                t->tx_charset = CHARSET_USASCII;
3784                *ap = add ("charset=us-ascii", NULL);
3785            }
3786
3787            cp = strchr(*ap++, '=');
3788            *ap = NULL;
3789            *cp++ = '\0';
3790            *ep = cp;
3791        }
3792
3793        if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3794            ct->c_encoding = CE_QUOTED;
3795        else
3796            ct->c_encoding = CE_7BIT;
3797        break;
3798
3799    case CT_APPLICATION:
3800        /* For application type, use base64, except when postscript */
3801        if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3802            ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3803                ? CE_QUOTED : CE_BASE64;
3804        else
3805            ct->c_encoding = CE_7BIT;
3806        break;
3807
3808    case CT_MESSAGE:
3809        ct->c_encoding = CE_7BIT;
3810        break;
3811
3812    case CT_AUDIO:
3813    case CT_IMAGE:
3814    case CT_VIDEO:
3815        /* For audio, image, and video contents, just use base64 */
3816        ct->c_encoding = CE_BASE64;
3817        break;
3818    }
3819
3820    return (boundaryclash ? NOTOK : OK);
3821}
3822
3823
3824/*
3825 * Scan the content structures, and build header
3826 * fields that will need to be output into the
3827 * message.
3828 */
3829
3830static int
3831build_headers (CT ct)
3832{
3833    int cc, mailbody, len;
3834    char **ap, **ep;
3835    char *np, *vp, buffer[BUFSIZ];
3836    CI ci = &ct->c_ctinfo;
3837
3838    /*
3839     * If message is type multipart, then add the multipart
3840     * boundary to the list of attribute/value pairs.
3841     */
3842    if (ct->c_type == CT_MULTIPART) {
3843        char *cp;
3844        static int level = 0;   /* store nesting level */
3845
3846        ap = ci->ci_attrs;
3847        ep = ci->ci_values;
3848        snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3849        cp = strchr(*ap++ = add (buffer, NULL), '=');
3850        *ap = NULL;
3851        *cp++ = '\0';
3852        *ep = cp;
3853    }
3854
3855    /*
3856     * Skip the output of Content-Type, parameters, content
3857     * description, and Content-ID if the content is of type
3858     * "message" and the rfc934 compatibility flag is set
3859     * (which means we are inside multipart/digest and the
3860     * switch -rfc934mode was given).
3861     */
3862    if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3863        goto skip_headers;
3864
3865    /*
3866     * output the content type and subtype
3867     */
3868    np = add (TYPE_FIELD, NULL);
3869    vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3870
3871    /* keep track of length of line */
3872    len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3873                + strlen (ci->ci_subtype) + 3;
3874
3875    mailbody = ct->c_type == CT_MESSAGE
3876        && ct->c_subtype == MESSAGE_EXTERNAL
3877        && ((struct exbody *) ct->c_ctparams)->eb_body;
3878
3879    /*
3880     * Append the attribute/value pairs to
3881     * the end of the Content-Type line.
3882     */
3883    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3884        if (mailbody && !strcasecmp (*ap, "body"))
3885            continue;
3886
3887        vp = add (";", vp);
3888        len++;
3889
3890        snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3891        if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3892            vp = add ("\n\t", vp);
3893            len = 8;
3894        } else {
3895            vp = add (" ", vp);
3896            len++;
3897        }
3898        vp = add (buffer, vp);
3899        len += cc;
3900    }
3901
3902    /*
3903     * Append any RFC-822 comment to the end of
3904     * the Content-Type line.
3905     */
3906    if (ci->ci_comment) {
3907        snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3908        if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3909            vp = add ("\n\t", vp);
3910            len = 8;
3911        } else {
3912            vp = add (" ", vp);
3913            len++;
3914        }
3915        vp = add (buffer, vp);
3916        len += cc;
3917    }
3918    vp = add ("\n", vp);
3919    add_header (ct, np, vp);
3920
3921    /*
3922     * output the Content-ID
3923     */
3924    if (ct->c_id) {
3925        np = add (ID_FIELD, NULL);
3926        vp = concat (" ", ct->c_id, NULL);
3927        add_header (ct, np, vp);
3928    }
3929
3930    /*
3931     * output the Content-Description
3932     */
3933    if (ct->c_descr) {
3934        np = add (DESCR_FIELD, NULL);
3935        vp = concat (" ", ct->c_descr, NULL);
3936        add_header (ct, np, vp);
3937    }
3938
3939skip_headers:
3940    /*
3941     * If this is the internal content structure for a
3942     * "message/external", then we are done with the
3943     * headers (since it has no body).
3944     */
3945    if (ct->c_ctexbody)
3946        return OK;
3947
3948    /*
3949     * output the Content-MD5
3950     */
3951    if (checksw) {
3952        np = add (MD5_FIELD, NULL);
3953        vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3954        add_header (ct, np, vp);
3955    }
3956
3957    /*
3958     * output the Content-Transfer-Encoding
3959     */
3960    switch (ct->c_encoding) {
3961    case CE_7BIT:
3962        /* Nothing to output */
3963#if 0
3964        np = add (ENCODING_FIELD, NULL);
3965        vp = concat (" ", "7bit", "\n", NULL);
3966        add_header (ct, np, vp);
3967#endif
3968        break;
3969
3970    case CE_8BIT:
3971        if (ct->c_type == CT_MESSAGE)
3972            adios (NULL, "internal error, invalid encoding");
3973
3974        np = add (ENCODING_FIELD, NULL);
3975        vp = concat (" ", "8bit", "\n", NULL);
3976        add_header (ct, np, vp);
3977        break;
3978
3979    case CE_QUOTED:
3980        if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3981            adios (NULL, "internal error, invalid encoding");
3982
3983        np = add (ENCODING_FIELD, NULL);
3984        vp = concat (" ", "quoted-printable", "\n", NULL);
3985        add_header (ct, np, vp);
3986        break;
3987
3988    case CE_BASE64:
3989        if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3990            adios (NULL, "internal error, invalid encoding");
3991
3992        np = add (ENCODING_FIELD, NULL);
3993        vp = concat (" ", "base64", "\n", NULL);
3994        add_header (ct, np, vp);
3995        break;
3996
3997    case CE_BINARY:
3998        if (ct->c_type == CT_MESSAGE)
3999            adios (NULL, "internal error, invalid encoding");
4000
4001        np = add (ENCODING_FIELD, NULL);
4002        vp = concat (" ", "binary", "\n", NULL);
4003        add_header (ct, np, vp);
4004        break;
4005
4006    default:
4007        adios (NULL, "unknown transfer encoding in content");
4008        break;
4009    }
4010
4011    /*
4012     * Additional content specific header processing
4013     */
4014    switch (ct->c_type) {
4015    case CT_MULTIPART:
4016    {
4017        struct multipart *m;
4018        struct part *part;
4019
4020        m = (struct multipart *) ct->c_ctparams;
4021        for (part = m->mp_parts; part; part = part->mp_next) {
4022            CT p;
4023
4024            p = part->mp_part;
4025            build_headers (p);
4026        }
4027    }
4028        break;
4029
4030    case CT_MESSAGE:
4031        if (ct->c_subtype == MESSAGE_EXTERNAL) {
4032            struct exbody *e;
4033
4034            e = (struct exbody *) ct->c_ctparams;
4035            build_headers (e->eb_content);
4036        }
4037        break;
4038
4039    default:
4040        /* Nothing to do */
4041        break;
4042    }
4043
4044    return OK;
4045}
4046
4047
4048static char nib2b64[0x40+1] =
4049        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4050
4051static char *
4052calculate_digest (CT ct, int asciiP)
4053{
4054    int cc;
4055    char buffer[BUFSIZ], *vp, *op;
4056    unsigned char *dp;
4057    unsigned char digest[16];
4058    unsigned char outbuf[25];
4059    FILE *in;
4060    MD5_CTX mdContext;
4061    CE ce = ct->c_cefile;
4062
4063    /* open content */
4064    if ((in = fopen (ce->ce_file, "r")) == NULL)
4065        adios (ce->ce_file, "unable to open for reading");
4066
4067    /* Initialize md5 context */
4068    MD5Init (&mdContext);
4069
4070    /* calculate md5 message digest */
4071    if (asciiP) {
4072        while (fgets (buffer, sizeof(buffer) - 1, in)) {
4073            char c, *cp;
4074
4075            cp = buffer + strlen (buffer) - 1;
4076            if ((c = *cp) == '\n')
4077                *cp = '\0';
4078
4079            MD5Update (&mdContext, (unsigned char *) buffer,
4080                       (unsigned int) strlen (buffer));
4081
4082            if (c == '\n')
4083                MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4084        }
4085    } else {
4086        while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4087            MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4088    }
4089
4090    /* md5 finalization.  Write digest and zero md5 context */
4091    MD5Final (digest, &mdContext);
4092
4093    /* close content */
4094    fclose (in);
4095
4096    /* print debugging info */
4097    if (debugsw) {
4098        unsigned char *ep;
4099
4100        fprintf (stderr, "MD5 digest=");
4101        for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4102                 dp < ep; dp++)
4103            fprintf (stderr, "%02x", *dp & 0xff);
4104        fprintf (stderr, "\n");
4105    }
4106
4107    /* encode the digest using base64 */
4108    for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4109                cc > 0; cc -= 3, op += 4) {
4110        unsigned long bits;
4111        char *bp;
4112
4113        bits = (*dp++ & 0xff) << 16;
4114        if (cc > 1) {
4115            bits |= (*dp++ & 0xff) << 8;
4116            if (cc > 2)
4117                bits |= *dp++ & 0xff;
4118        }
4119
4120        for (bp = op + 4; bp > op; bits >>= 6)
4121            *--bp = nib2b64[bits & 0x3f];
4122        if (cc < 3) {
4123            *(op + 3) = '=';
4124            if (cc < 2)
4125                *(op + 2) = '=';
4126        }
4127    }
4128
4129    /* null terminate string */
4130    outbuf[24] = '\0';
4131
4132    /* now make copy and return string */
4133    vp = concat (" ", outbuf, "\n", NULL);
4134    return vp;
4135}
4136
4137
4138static int
4139readDigest (CT ct, char *cp)
4140{
4141    int bitno, skip;
4142    unsigned long bits;
4143    char *bp = cp;
4144    unsigned char *dp, value, *ep;
4145    unsigned char *b, *b1, *b2, *b3;
4146
4147    b  = (unsigned char *) &bits,
4148    b1 = &b[endian > 0 ? 1 : 2],
4149    b2 = &b[endian > 0 ? 2 : 1],
4150    b3 = &b[endian > 0 ? 3 : 0];
4151    bitno = 18;
4152    bits = 0L;
4153    skip = 0;
4154
4155    for (ep = (dp = ct->c_digest)
4156                 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4157        switch (*cp) {
4158            default:
4159                if (skip
4160                        || (*cp & 0x80)
4161                        || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4162                    if (debugsw)
4163                        fprintf (stderr, "invalid BASE64 encoding\n");
4164                    return NOTOK;
4165                }
4166
4167                bits |= value << bitno;
4168test_end:
4169                if ((bitno -= 6) < 0) {
4170                    if (dp + (3 - skip) > ep)
4171                        goto invalid_digest;
4172                    *dp++ = *b1;
4173                    if (skip < 2) {
4174                        *dp++ = *b2;
4175                        if (skip < 1)
4176                            *dp++ = *b3;
4177                    }
4178                    bitno = 18;
4179                    bits = 0L;
4180                    skip = 0;
4181                }
4182                break;
4183
4184            case '=':
4185                if (++skip > 3)
4186                    goto self_delimiting;
4187                goto test_end;
4188        }
4189    if (bitno != 18) {
4190        if (debugsw)
4191            fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4192
4193        return NOTOK;
4194    }
4195self_delimiting:
4196    if (dp != ep) {
4197invalid_digest:
4198        if (debugsw) {
4199            while (*cp)
4200                cp++;
4201            fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4202                     cp - bp);
4203        }
4204
4205        return NOTOK;
4206    }
4207
4208    if (debugsw) {
4209        fprintf (stderr, "MD5 digest=");
4210        for (dp = ct->c_digest; dp < ep; dp++)
4211            fprintf (stderr, "%02x", *dp & 0xff);
4212        fprintf (stderr, "\n");
4213    }
4214
4215    return OK;
4216}
Note: See TracBrowser for help on using the repository browser.