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

Revision 12455, 56.0 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12454, which included commits to RCS files with non-trunk default branches.
Line 
1
2/*
3 * mhparse.c -- routines to parse the contents of MIME messages
4 *
5 * $Id: mhparse.c,v 1.1.1.1 1999-02-07 18:14:14 danw Exp $
6 */
7
8#include <h/mh.h>
9#include <fcntl.h>
10#include <h/signals.h>
11#include <h/md5.h>
12#include <errno.h>
13#include <setjmp.h>
14#include <signal.h>
15#include <zotnet/mts/mts.h>
16#include <zotnet/tws/tws.h>
17#include <h/mime.h>
18#include <h/mhparse.h>
19
20#ifdef HAVE_SYS_WAIT_H
21# include <sys/wait.h>
22#endif
23
24
25extern int errno;
26extern int debugsw;
27
28extern int endian;      /* mhmisc.c     */
29
30extern pid_t xpid;      /* mhshowsbr.c  */
31
32/* cache policies */
33extern int rcachesw;    /* mhcachesbr.c */
34extern int wcachesw;    /* mhcachesbr.c */
35
36int checksw = 0;        /* check Content-MD5 field */
37
38/*
39 * Directory to place temp files.  This must
40 * be set before these routines are called.
41 */
42char *tmp;
43
44/*
45 * Structure for mapping types to their internal flags
46 */
47struct k2v {
48    char *kv_key;
49    int   kv_value;
50};
51
52/*
53 * Structures for TEXT messages
54 */
55static struct k2v SubText[] = {
56    { "plain",    TEXT_PLAIN },
57    { "richtext", TEXT_RICHTEXT },  /* defined in RFC-1341    */
58    { "enriched", TEXT_ENRICHED },  /* defined in RFC-1896    */
59    { NULL,       TEXT_UNKNOWN }    /* this one must be last! */
60};
61
62static struct k2v Charset[] = {
63    { "us-ascii",   CHARSET_USASCII },
64    { "iso-8859-1", CHARSET_LATIN },
65    { NULL,         CHARSET_UNKNOWN }  /* this one must be last! */
66};
67
68/*
69 * Structures for MULTIPART messages
70 */
71static struct k2v SubMultiPart[] = {
72    { "mixed",       MULTI_MIXED },
73    { "alternative", MULTI_ALTERNATE },
74    { "digest",      MULTI_DIGEST },
75    { "parallel",    MULTI_PARALLEL },
76    { NULL,          MULTI_UNKNOWN }    /* this one must be last! */
77};
78
79/*
80 * Structures for MESSAGE messages
81 */
82static struct k2v SubMessage[] = {
83    { "rfc822",        MESSAGE_RFC822 },
84    { "partial",       MESSAGE_PARTIAL },
85    { "external-body", MESSAGE_EXTERNAL },
86    { NULL,            MESSAGE_UNKNOWN }        /* this one must be last! */
87};
88
89/*
90 * Structure for APPLICATION messages
91 */
92static struct k2v SubApplication[] = {
93    { "octet-stream", APPLICATION_OCTETS },
94    { "postscript",   APPLICATION_POSTSCRIPT },
95    { NULL,           APPLICATION_UNKNOWN }     /* this one must be last! */
96};
97
98
99/* ftpsbr.c */
100int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
101
102/* mhcachesbr.c */
103int find_cache (CT, int, int *, char *, char *, int);
104
105/* mhmisc.c */
106int part_ok (CT, int);
107int type_ok (CT, int);
108int make_intermediates (char *);
109void content_error (char *, CT, char *, ...);
110
111/* mhfree.c */
112void free_content (CT);
113void free_encoding (CT, int);
114
115/*
116 * prototypes
117 */
118int pidcheck (int);
119CT parse_mime (char *);
120
121/*
122 * static prototypes
123 */
124static CT get_content (FILE *, char *, int);
125static int add_header (CT, char *, char *);
126static int get_ctinfo (char *, CT);
127static int get_comment (CT, char **, int);
128static int InitGeneric (CT);
129static int InitText (CT);
130static int InitMultiPart (CT);
131static void reverse_parts (CT);
132static int InitMessage (CT);
133static int params_external (CT, int);
134static int InitApplication (CT);
135static int init_encoding (CT, OpenCEFunc);
136static void close_encoding (CT);
137static unsigned long size_encoding (CT);
138static int InitBase64 (CT);
139static int openBase64 (CT, char **);
140static int InitQuoted (CT);
141static int openQuoted (CT, char **);
142static int Init7Bit (CT);
143static int open7Bit (CT, char **);
144static int openExternal (CT, CT, CE, char **, int *);
145static int InitFile (CT);
146static int openFile (CT, char **);
147static int InitFTP (CT);
148static int openFTP (CT, char **);
149static int InitMail (CT);
150static int openMail (CT, char **);
151static int readDigest (CT, char *);
152
153/*
154 * Structures for mapping (content) types to
155 * the functions to handle them.
156 */
157struct str2init {
158    char *si_key;
159    int   si_val;
160    InitFunc si_init;
161};
162
163static struct str2init str2cts[] = {
164    { "application", CT_APPLICATION, InitApplication },
165    { "audio",       CT_AUDIO,       InitGeneric },
166    { "image",       CT_IMAGE,       InitGeneric },
167    { "message",     CT_MESSAGE,     InitMessage },
168    { "multipart",   CT_MULTIPART,   InitMultiPart },
169    { "text",        CT_TEXT,        InitText },
170    { "video",       CT_VIDEO,       InitGeneric },
171    { NULL,          CT_EXTENSION,   NULL },  /* these two must be last! */
172    { NULL,          CT_UNKNOWN,     NULL },
173};
174
175static struct str2init str2ces[] = {
176    { "base64",           CE_BASE64,    InitBase64 },
177    { "quoted-printable", CE_QUOTED,    InitQuoted },
178    { "8bit",             CE_8BIT,      Init7Bit },
179    { "7bit",             CE_7BIT,      Init7Bit },
180    { "binary",           CE_BINARY,    NULL },
181    { NULL,               CE_EXTENSION, NULL },  /* these two must be last! */
182    { NULL,               CE_UNKNOWN,   NULL },
183};
184
185/*
186 * NOTE WELL: si_key MUST NOT have value of NOTOK
187 *
188 * si_key is 1 if access method is anonymous.
189 */
190static struct str2init str2methods[] = {
191    { "afs",         1, InitFile },
192    { "anon-ftp",    1, InitFTP },
193    { "ftp",         0, InitFTP },
194    { "local-file",  0, InitFile },
195    { "mail-server", 0, InitMail },
196    { NULL,          0, NULL }
197};
198
199
200int
201pidcheck (int status)
202{
203    if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
204        return status;
205
206    fflush (stdout);
207    fflush (stderr);
208    done (1);
209    /* NOTREACHED */
210}
211
212
213/*
214 * Main entry point for parsing a MIME message or file.
215 * It returns the Content structure for the top level
216 * entity in the file.
217 */
218
219CT
220parse_mime (char *file)
221{
222    int is_stdin;
223    char buffer[BUFSIZ];
224    FILE *fp;
225    CT ct;
226
227    /*
228     * Check if file is actually standard input
229     */
230    if ((is_stdin = !(strcmp (file, "-")))) {
231        file = add (m_tmpfil (invo_name), NULL);
232        if ((fp = fopen (file, "w+")) == NULL) {
233            advise (file, "unable to fopen for writing and reading");
234            return NULL;
235        }
236        chmod (file, 0600);
237        while (fgets (buffer, sizeof(buffer), stdin))
238            fputs (buffer, fp);
239        fflush (fp);
240
241        if (ferror (stdin)) {
242            unlink (file);
243            advise ("stdin", "error reading");
244            return NULL;
245        }
246        if (ferror (fp)) {
247            unlink (file);
248            advise (file, "error writing");
249            return NULL;
250        }
251        fseek (fp, 0L, SEEK_SET);
252    } else if ((fp = fopen (file, "r")) == NULL) {
253        advise (file, "unable to read");
254        return NULL;
255    }
256
257    if (!(ct = get_content (fp, file, 1))) {
258        if (is_stdin)
259            unlink (file);
260        fclose (fp);
261        advise (NULL, "unable to decode %s", file);
262        return NULL;
263    }
264
265    if (is_stdin)
266        ct->c_unlink = 1;       /* temp file to remove */
267
268    ct->c_fp = NULL;
269
270    if (ct->c_end == 0L) {
271        fseek (fp, 0L, SEEK_END);
272        ct->c_end = ftell (fp);
273    }
274
275    if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
276        fclose (fp);
277        free_content (ct);
278        return NULL;
279    }
280
281    fclose (fp);
282    return ct;
283}
284
285
286/*
287 * Main routine for reading/parsing the headers
288 * of a message content.
289 *
290 * toplevel =  1   # we are at the top level of the message
291 * toplevel =  0   # we are inside message type or multipart type
292 *                 # other than multipart/digest
293 * toplevel = -1   # we are inside multipart/digest
294 */
295
296static CT
297get_content (FILE *in, char *file, int toplevel)
298{
299    int compnum, state;
300    char buf[BUFSIZ], name[NAMESZ];
301    char *np, *vp;
302    CT ct;
303    HF hp;
304
305    /* allocate the content structure */
306    if (!(ct = (CT) calloc (1, sizeof(*ct))))
307        adios (NULL, "out of memory");
308
309    ct->c_fp = in;
310    ct->c_file = add (file, NULL);
311    ct->c_begin = ftell (ct->c_fp) + 1;
312
313    /*
314     * Parse the header fields for this
315     * content into a linked list.
316     */
317    for (compnum = 1, state = FLD;;) {
318        switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
319        case FLD:
320        case FLDPLUS:
321        case FLDEOF:
322            compnum++;
323
324            /* get copies of the buffers */
325            np = add (name, NULL);
326            vp = add (buf, NULL);
327
328            /* if necessary, get rest of field */
329            while (state == FLDPLUS) {
330                state = m_getfld (state, name, buf, sizeof(buf), in);
331                vp = add (buf, vp);     /* add to previous value */
332            }
333
334            /* Now add the header data to the list */
335            add_header (ct, np, vp);
336
337            /* continue, if this isn't the last header field */
338            if (state != FLDEOF) {
339                ct->c_begin = ftell (in) + 1;
340                continue;
341            }
342            /* else fall... */
343
344        case BODY:
345        case BODYEOF:
346            ct->c_begin = ftell (in) - strlen (buf);
347            break;
348
349        case FILEEOF:
350            ct->c_begin = ftell (in);
351            break;
352
353        case LENERR:
354        case FMTERR:
355            adios (NULL, "message format error in component #%d", compnum);
356
357        default:
358            adios (NULL, "getfld() returned %d", state);
359        }
360
361        /* break out of the loop */
362        break;
363    }
364
365    /*
366     * Read the content headers.  We will parse the
367     * MIME related header fields into their various
368     * structures and set internal flags related to
369     * content type/subtype, etc.
370     */
371
372    hp = ct->c_first_hf;        /* start at first header field */
373    while (hp) {
374        /* Get MIME-Version field */
375        if (!strcasecmp (hp->name, VRSN_FIELD)) {
376            int ucmp;
377            char c, *cp, *dp;
378
379            if (ct->c_vrsn) {
380                advise (NULL, "message %s has multiple %s: fields",
381                        ct->c_file, VRSN_FIELD);
382                goto next_header;
383            }
384            ct->c_vrsn = add (hp->value, NULL);
385
386            /* Now, cleanup this field */
387            cp = ct->c_vrsn;
388
389            while (isspace (*cp))
390                cp++;
391            for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
392                *dp++ = ' ';
393            for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
394                if (!isspace (*dp))
395                    break;
396            *++dp = '\0';
397            if (debugsw)
398                fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
399
400            if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
401                goto out;
402
403            for (dp = cp; istoken (*dp); dp++)
404                continue;
405            c = *dp;
406            *dp = '\0';
407            ucmp = !strcasecmp (cp, VRSN_VALUE);
408            *dp = c;
409            if (!ucmp) {
410                admonish (NULL, "message %s has unknown value for %s: field (%s)",
411                ct->c_file, VRSN_FIELD, cp);
412            }
413        }
414        else if (!strcasecmp (hp->name, TYPE_FIELD)) {
415        /* Get Content-Type field */
416            struct str2init *s2i;
417            CI ci = &ct->c_ctinfo;
418
419            /* Check if we've already seen a Content-Type header */
420            if (ct->c_ctline) {
421                advise (NULL, "message %s has multiple %s: fields",
422                        ct->c_file, TYPE_FIELD);
423                goto next_header;
424            }
425
426            /* Parse the Content-Type field */
427            if (get_ctinfo (hp->value, ct) == NOTOK)
428                goto out;
429
430            /*
431             * Set the Init function and the internal
432             * flag for this content type.
433             */
434            for (s2i = str2cts; s2i->si_key; s2i++)
435                if (!strcasecmp (ci->ci_type, s2i->si_key))
436                    break;
437            if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
438                s2i++;
439            ct->c_type = s2i->si_val;
440            ct->c_ctinitfnx = s2i->si_init;
441        }
442        else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
443        /* Get Content-Transfer-Encoding field */
444            char c, *cp, *dp;
445            struct str2init *s2i;
446
447            /*
448             * Check if we've already seen the
449             * Content-Transfer-Encoding field
450             */
451            if (ct->c_celine) {
452                advise (NULL, "message %s has multiple %s: fields",
453                        ct->c_file, ENCODING_FIELD);
454                goto next_header;
455            }
456
457            /* get copy of this field */
458            ct->c_celine = cp = add (hp->value, NULL);
459
460            while (isspace (*cp))
461                cp++;
462            for (dp = cp; istoken (*dp); dp++)
463                continue;
464            c = *dp;
465            *dp = '\0';
466
467            /*
468             * Find the internal flag and Init function
469             * for this transfer encoding.
470             */
471            for (s2i = str2ces; s2i->si_key; s2i++)
472                if (!strcasecmp (cp, s2i->si_key))
473                    break;
474            if (!s2i->si_key && !uprf (cp, "X-"))
475                s2i++;
476            *dp = c;
477            ct->c_encoding = s2i->si_val;
478
479            /* Call the Init function for this encoding */
480            if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
481                goto out;
482        }
483        else if (!strcasecmp (hp->name, MD5_FIELD)) {
484        /* Get Content-MD5 field */
485            char *cp, *dp, *ep;
486
487            if (!checksw)
488                goto next_header;
489
490            if (ct->c_digested) {
491                advise (NULL, "message %s has multiple %s: fields",
492                        ct->c_file, MD5_FIELD);
493                goto next_header;
494            }
495
496            ep = cp = add (hp->value, NULL);    /* get a copy */
497
498            while (isspace (*cp))
499                cp++;
500            for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
501                *dp++ = ' ';
502            for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
503                if (!isspace (*dp))
504                    break;
505            *++dp = '\0';
506            if (debugsw)
507                fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
508
509            if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
510                free (ep);
511                goto out;
512            }
513
514            for (dp = cp; *dp && !isspace (*dp); dp++)
515                continue;
516            *dp = '\0';
517
518            readDigest (ct, cp);
519            free (ep);
520            ct->c_digested++;
521        }
522        else if (!strcasecmp (hp->name, ID_FIELD)) {
523        /* Get Content-ID field */
524            ct->c_id = add (hp->value, ct->c_id);
525        }
526        else if (!strcasecmp (hp->name, DESCR_FIELD)) {
527        /* Get Content-Description field */
528            ct->c_descr = add (hp->value, ct->c_descr);
529        }
530
531next_header:
532        hp = hp->next;  /* next header field */
533    }
534
535    /*
536     * Check if we saw a Content-Type field.
537     * If not, then assign a default value for
538     * it, and the Init function.
539     */
540    if (!ct->c_ctline) {
541        /*
542         * If we are inside a multipart/digest message,
543         * so default type is message/rfc822
544         */
545        if (toplevel < 0) {
546            if (get_ctinfo ("message/rfc822", ct) == NOTOK)
547                goto out;
548            ct->c_type = CT_MESSAGE;
549            ct->c_ctinitfnx = InitMessage;
550        } else {
551            /*
552             * Else default type is text/plain
553             */
554            if (get_ctinfo ("text/plain", ct) == NOTOK)
555                goto out;
556            ct->c_type = CT_TEXT;
557            ct->c_ctinitfnx = InitText;
558        }
559    }
560
561    /* Use default Transfer-Encoding, if necessary */
562    if (!ct->c_celine) {
563        ct->c_encoding = CE_7BIT;
564        Init7Bit (ct);
565    }
566
567    return ct;
568
569out:
570    free_content (ct);
571    return NULL;
572}
573
574
575/*
576 * small routine to add header field to list
577 */
578
579static int
580add_header (CT ct, char *name, char *value)
581{
582    HF hp;
583
584    /* allocate header field structure */
585    if (!(hp = malloc (sizeof(*hp))))
586        adios (NULL, "out of memory");
587
588    /* link data into header structure */
589    hp->name = name;
590    hp->value = value;
591    hp->next = NULL;
592
593    /* link header structure into the list */
594    if (ct->c_first_hf == NULL) {
595        ct->c_first_hf = hp;            /* this is the first */
596        ct->c_last_hf = hp;
597    } else {
598        ct->c_last_hf->next = hp;       /* add it to the end */
599        ct->c_last_hf = hp;
600    }
601
602    return 0;
603}
604
605
606/*
607 * Parse Content-Type line and fill in the
608 * information of the CTinfo structure.
609 */
610
611static int
612get_ctinfo (char *cp, CT ct)
613{
614    int i;
615    char *dp, **ap, **ep;
616    char c;
617    CI ci;
618
619    ci = &ct->c_ctinfo;
620    i = strlen (invo_name) + 2;
621
622    /* store copy of Content-Type line */
623    cp = ct->c_ctline = add (cp, NULL);
624
625    while (isspace (*cp))       /* trim leading spaces */
626        cp++;
627
628    /* change newlines to spaces */
629    for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
630        *dp++ = ' ';
631
632    /* trim trailing spaces */
633    for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
634        if (!isspace (*dp))
635            break;
636    *++dp = '\0';
637
638    if (debugsw)
639        fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
640
641    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
642        return NOTOK;
643
644    for (dp = cp; istoken (*dp); dp++)
645        continue;
646    c = *dp, *dp = '\0';
647    ci->ci_type = add (cp, NULL);       /* store content type */
648    *dp = c, cp = dp;
649
650    if (!*ci->ci_type) {
651        advise (NULL, "invalid %s: field in message %s (empty type)",
652                TYPE_FIELD, ct->c_file);
653        return NOTOK;
654    }
655
656    /* down case the content type string */
657    for (dp = ci->ci_type; *dp; dp++)
658        if (isalpha(*dp) && isupper (*dp))
659            *dp = tolower (*dp);
660
661    while (isspace (*cp))
662        cp++;
663
664    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
665        return NOTOK;
666
667    if (*cp != '/') {
668        ci->ci_subtype = add ("", NULL);
669        goto magic_skip;
670    }
671
672    cp++;
673    while (isspace (*cp))
674        cp++;
675
676    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
677        return NOTOK;
678
679    for (dp = cp; istoken (*dp); dp++)
680        continue;
681    c = *dp, *dp = '\0';
682    ci->ci_subtype = add (cp, NULL);    /* store the content subtype */
683    *dp = c, cp = dp;
684
685    if (!*ci->ci_subtype) {
686        advise (NULL,
687                "invalid %s: field in message %s (empty subtype for \"%s\")",
688                TYPE_FIELD, ct->c_file, ci->ci_type);
689        return NOTOK;
690    }
691
692    /* down case the content subtype string */
693    for (dp = ci->ci_subtype; *dp; dp++)
694        if (isalpha(*dp) && isupper (*dp))
695            *dp = tolower (*dp);
696
697magic_skip:
698    while (isspace (*cp))
699        cp++;
700
701    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
702        return NOTOK;
703
704    /*
705     * Parse attribute/value pairs given with Content-Type
706     */
707    ep = (ap = ci->ci_attrs) + NPARMS;
708    while (*cp == ';') {
709        char *vp, *up;
710
711        if (ap >= ep) {
712            advise (NULL,
713                    "too many parameters in message %s's %s: field (%d max)",
714                    ct->c_file, TYPE_FIELD, NPARMS);
715            return NOTOK;
716        }
717
718        cp++;
719        while (isspace (*cp))
720            cp++;
721
722        if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
723            return NOTOK;
724
725        if (*cp == 0) {
726            advise (NULL,
727                    "extraneous trailing ';' in message %s's %s: parameter list",
728                    ct->c_file, TYPE_FIELD);
729            return OK;
730        }
731
732        /* down case the attribute name */
733        for (dp = cp; istoken (*dp); dp++)
734            if (isalpha(*dp) && isupper (*dp))
735                *dp = tolower (*dp);
736
737        for (up = dp; isspace (*dp);)
738            dp++;
739        if (dp == cp || *dp != '=') {
740            advise (NULL,
741                    "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
742                    ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
743            return NOTOK;
744        }
745
746        vp = (*ap = add (cp, NULL)) + (up - cp);
747        *vp = '\0';
748        for (dp++; isspace (*dp);)
749            dp++;
750
751        /* now add the attribute value */
752        ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
753
754        if (*dp == '"') {
755            for (cp = ++dp, dp = vp;;) {
756                switch (c = *cp++) {
757                    case '\0':
758bad_quote:
759                        advise (NULL,
760                                "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
761                                ct->c_file, TYPE_FIELD, i, i, "", *ap);
762                        return NOTOK;
763
764                    case '\\':
765                        *dp++ = c;
766                        if ((c = *cp++) == '\0')
767                            goto bad_quote;
768                        /* else fall... */
769
770                    default:
771                        *dp++ = c;
772                        continue;
773
774                    case '"':
775                        *dp = '\0';
776                        break;
777                }
778                break;
779            }
780        } else {
781            for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
782                continue;
783            *dp = '\0';
784        }
785        if (!*vp) {
786            advise (NULL,
787                    "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
788                    ct->c_file, TYPE_FIELD, i, i, "", *ap);
789            return NOTOK;
790        }
791        ap++;
792
793        while (isspace (*cp))
794            cp++;
795
796        if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
797            return NOTOK;
798    }
799
800    /*
801     * Check if anything is left over
802     */
803    if (*cp) {
804        advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
805            ct->c_file, TYPE_FIELD, i, i, "", cp);
806    }
807
808    return OK;
809}
810
811
812static int
813get_comment (CT ct, char **ap, int istype)
814{
815    int i;
816    char *bp, *cp;
817    char c, buffer[BUFSIZ], *dp;
818    CI ci;
819
820    ci = &ct->c_ctinfo;
821    cp = *ap;
822    bp = buffer;
823    cp++;
824
825    for (i = 0;;) {
826        switch (c = *cp++) {
827        case '\0':
828invalid:
829        advise (NULL, "invalid comment in message %s's %s: field",
830                ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
831        return NOTOK;
832
833        case '\\':
834            *bp++ = c;
835            if ((c = *cp++) == '\0')
836                goto invalid;
837            *bp++ = c;
838            continue;
839
840        case '(':
841            i++;
842            /* and fall... */
843        default:
844            *bp++ = c;
845            continue;
846
847        case ')':
848            if (--i < 0)
849                break;
850            *bp++ = c;
851            continue;
852        }
853        break;
854    }
855    *bp = '\0';
856
857    if (istype) {
858        if ((dp = ci->ci_comment)) {
859            ci->ci_comment = concat (dp, " ", buffer, NULL);
860            free (dp);
861        } else {
862            ci->ci_comment = add (buffer, NULL);
863        }
864    }
865
866    while (isspace (*cp))
867        cp++;
868
869    *ap = cp;
870    return OK;
871}
872
873
874/*
875 * CONTENTS
876 *
877 * Handles content types audio, image, and video.
878 * There's not much to do right here.
879 */
880
881static int
882InitGeneric (CT ct)
883{
884    return OK;          /* not much to do here */
885}
886
887
888/*
889 * TEXT
890 */
891
892static int
893InitText (CT ct)
894{
895    char buffer[BUFSIZ];
896    char *chset;
897    char **ap, **ep, *cp;
898    struct k2v *kv;
899    struct text *t;
900    CI ci = &ct->c_ctinfo;
901
902    /* check for missing subtype */
903    if (!*ci->ci_subtype)
904        ci->ci_subtype = add ("plain", ci->ci_subtype);
905
906    /* match subtype */
907    for (kv = SubText; kv->kv_key; kv++)
908        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
909            break;
910    ct->c_subtype = kv->kv_value;
911
912    /* allocate text structure */
913    if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
914        adios (NULL, "out of memory");
915    ct->c_ctparams = (void *) t;
916
917    /* scan for charset parameter */
918    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
919        if (!strcasecmp (*ap, "charset"))
920            break;
921
922    if (*ap)
923        chset = *ep;
924    else
925        chset = "US-ASCII";     /* default for text */
926
927    /* match character set, or set to unknown */
928    for (kv = Charset; kv->kv_key; kv++)
929        if (!strcasecmp (chset, kv->kv_key))
930            break;
931    t->tx_charset = kv->kv_value;
932
933    /*
934     * If we can not handle character set natively,
935     * then check profile for string to modify the
936     * terminal or display method.
937     */
938    if (!check_charset (chset, strlen (chset))) {
939        snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
940        if ((cp = context_find (buffer)))
941            ct->c_termproc = getcpy (cp);
942    }
943
944    return OK;
945}
946
947
948/*
949 * MULTIPART
950 */
951
952static int
953InitMultiPart (CT ct)
954{
955    int inout;
956    long last, pos;
957    char *cp, *dp, **ap, **ep;
958    char *bp, buffer[BUFSIZ];
959    struct multipart *m;
960    struct k2v *kv;
961    struct part *part, **next;
962    CI ci = &ct->c_ctinfo;
963    CT p;
964    FILE *fp;
965
966    /*
967     * The encoding for multipart messages must be either
968     * 7bit, 8bit, or binary (per RFC2045).
969     */
970    if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
971        && ct->c_encoding != CE_BINARY) {
972        admonish (NULL,
973                  "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
974                  ci->ci_type, ci->ci_subtype, ct->c_file);
975        return NOTOK;
976    }
977
978    /* match subtype */
979    for (kv = SubMultiPart; kv->kv_key; kv++)
980        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
981            break;
982    ct->c_subtype = kv->kv_value;
983
984    /*
985     * Check for "boundary" parameter, which is
986     * required for multipart messages.
987     */
988    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
989        if (!strcasecmp (*ap, "boundary")) {
990            bp = *ep;
991            break;
992        }
993    }
994
995    /* complain if boundary parameter is missing */
996    if (!*ap) {
997        advise (NULL,
998                "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
999                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1000        return NOTOK;
1001    }
1002
1003    /* allocate primary structure for multipart info */
1004    if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1005        adios (NULL, "out of memory");
1006    ct->c_ctparams = (void *) m;
1007
1008    /* check if boundary parameter contains only whitespace characters */
1009    for (cp = bp; isspace (*cp); cp++)
1010        continue;
1011    if (!*cp) {
1012        advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1013                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1014        return NOTOK;
1015    }
1016
1017    /* remove trailing whitespace from boundary parameter */
1018    for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1019        if (!isspace (*dp))
1020            break;
1021    *++dp = '\0';
1022
1023    /* record boundary separators */
1024    m->mp_start = concat (bp, "\n", NULL);
1025    m->mp_stop = concat (bp, "--\n", NULL);
1026
1027    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1028        advise (ct->c_file, "unable to open for reading");
1029        return NOTOK;
1030    }
1031
1032    fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1033    last = ct->c_end;
1034    next = &m->mp_parts;
1035    part = NULL;
1036    inout = 1;
1037
1038    while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1039        if (pos > last)
1040            break;
1041
1042        pos += strlen (buffer);
1043        if (buffer[0] != '-' || buffer[1] != '-')
1044            continue;
1045        if (inout) {
1046            if (strcmp (buffer + 2, m->mp_start))
1047                continue;
1048next_part:
1049            if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1050                adios (NULL, "out of memory");
1051            *next = part;
1052            next = &part->mp_next;
1053
1054            if (!(p = get_content (fp, ct->c_file,
1055                        ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1056                fclose (ct->c_fp);
1057                ct->c_fp = NULL;
1058                return NOTOK;
1059            }
1060            p->c_fp = NULL;
1061            part->mp_part = p;
1062            pos = p->c_begin;
1063            fseek (fp, pos, SEEK_SET);
1064            inout = 0;
1065        } else {
1066            if (strcmp (buffer + 2, m->mp_start) == 0) {
1067                inout = 1;
1068end_part:
1069                p = part->mp_part;
1070                p->c_end = ftell(fp) - (strlen(buffer) + 1);
1071                if (p->c_end < p->c_begin)
1072                    p->c_begin = p->c_end;
1073                if (inout)
1074                    goto next_part;
1075                goto last_part;
1076            } else {
1077                if (strcmp (buffer + 2, m->mp_stop) == 0)
1078                    goto end_part;
1079            }
1080        }
1081    }
1082
1083    advise (NULL, "bogus multipart content in message %s", ct->c_file);
1084    if (!inout && part) {
1085        p = part->mp_part;
1086        p->c_end = ct->c_end;
1087
1088        if (p->c_begin >= p->c_end) {
1089            for (next = &m->mp_parts; *next != part;
1090                     next = &((*next)->mp_next))
1091                continue;
1092            *next = NULL;
1093            free_content (p);
1094            free ((char *) part);
1095        }
1096    }
1097
1098last_part:
1099    /* reverse the order of the parts for multipart/alternative */
1100    if (ct->c_subtype == MULTI_ALTERNATE)
1101        reverse_parts (ct);
1102
1103    /*
1104     * label all subparts with part number, and
1105     * then initialize the content of the subpart.
1106     */
1107    {
1108        int partnum;
1109        char *pp;
1110        char partnam[BUFSIZ];
1111
1112        if (ct->c_partno) {
1113            snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1114            pp = partnam + strlen (partnam);
1115        } else {
1116            pp = partnam;
1117        }
1118
1119        for (part = m->mp_parts, partnum = 1; part;
1120                 part = part->mp_next, partnum++) {
1121            p = part->mp_part;
1122
1123            sprintf (pp, "%d", partnum);
1124            p->c_partno = add (partnam, NULL);
1125
1126            /* initialize the content of the subparts */
1127            if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1128                fclose (ct->c_fp);
1129                ct->c_fp = NULL;
1130                return NOTOK;
1131            }
1132        }
1133    }
1134
1135    fclose (ct->c_fp);
1136    ct->c_fp = NULL;
1137    return OK;
1138}
1139
1140
1141/*
1142 * reverse the order of the parts of a multipart
1143 */
1144
1145static void
1146reverse_parts (CT ct)
1147{
1148    int i;
1149    struct multipart *m;
1150    struct part **base, **bmp, **next, *part;
1151
1152    m = (struct multipart *) ct->c_ctparams;
1153
1154    /* if only one part, just return */
1155    if (!m->mp_parts || !m->mp_parts->mp_next)
1156        return;
1157
1158    /* count number of parts */
1159    i = 0;
1160    for (part = m->mp_parts; part; part = part->mp_next)
1161        i++;
1162
1163    /* allocate array of pointers to the parts */
1164    if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1165        adios (NULL, "out of memory");
1166    bmp = base;
1167
1168    /* point at all the parts */
1169    for (part = m->mp_parts; part; part = part->mp_next)
1170        *bmp++ = part;
1171    *bmp = NULL;
1172
1173    /* reverse the order of the parts */
1174    next = &m->mp_parts;
1175    for (bmp--; bmp >= base; bmp--) {
1176        part = *bmp;
1177        *next = part;
1178        next = &part->mp_next;
1179    }
1180    *next = NULL;
1181
1182    /* free array of pointers */
1183    free ((char *) base);
1184}
1185
1186
1187/*
1188 * MESSAGE
1189 */
1190
1191static int
1192InitMessage (CT ct)
1193{
1194    struct k2v *kv;
1195    CI ci = &ct->c_ctinfo;
1196
1197    if (ct->c_encoding != CE_7BIT) {
1198        admonish (NULL,
1199                  "\"%s/%s\" type in message %s should be encoded in 7bit",
1200                  ci->ci_type, ci->ci_subtype, ct->c_file);
1201        return NOTOK;
1202    }
1203
1204    /* check for missing subtype */
1205    if (!*ci->ci_subtype)
1206        ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1207
1208    /* match subtype */
1209    for (kv = SubMessage; kv->kv_key; kv++)
1210        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1211            break;
1212    ct->c_subtype = kv->kv_value;
1213
1214    switch (ct->c_subtype) {
1215        case MESSAGE_RFC822:
1216            break;
1217
1218        case MESSAGE_PARTIAL:
1219            {
1220                char **ap, **ep;
1221                struct partial *p;
1222
1223                if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1224                    adios (NULL, "out of memory");
1225                ct->c_ctparams = (void *) p;
1226
1227                /* scan for parameters "id", "number", and "total" */
1228                for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1229                    if (!strcasecmp (*ap, "id")) {
1230                        p->pm_partid = add (*ep, NULL);
1231                        continue;
1232                    }
1233                    if (!strcasecmp (*ap, "number")) {
1234                        if (sscanf (*ep, "%d", &p->pm_partno) != 1
1235                                || p->pm_partno < 1) {
1236invalid_param:
1237                            advise (NULL,
1238                                    "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1239                                    *ap, ci->ci_type, ci->ci_subtype,
1240                                    ct->c_file, TYPE_FIELD);
1241                            return NOTOK;
1242                        }
1243                        continue;
1244                    }
1245                    if (!strcasecmp (*ap, "total")) {
1246                        if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1247                                || p->pm_maxno < 1)
1248                            goto invalid_param;
1249                        continue;
1250                    }
1251                }
1252
1253                if (!p->pm_partid
1254                        || !p->pm_partno
1255                        || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1256                    advise (NULL,
1257                            "invalid parameters for \"%s/%s\" type in message %s's %s field",
1258                            ci->ci_type, ci->ci_subtype,
1259                            ct->c_file, TYPE_FIELD);
1260                    return NOTOK;
1261                }
1262            }
1263            break;
1264
1265        case MESSAGE_EXTERNAL:
1266            {
1267                int exresult;
1268                struct exbody *e;
1269                CT p;
1270                FILE *fp;
1271
1272                if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1273                    adios (NULL, "out of memory");
1274                ct->c_ctparams = (void *) e;
1275
1276                if (!ct->c_fp
1277                        && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1278                    advise (ct->c_file, "unable to open for reading");
1279                    return NOTOK;
1280                }
1281
1282                fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1283
1284                if (!(p = get_content (fp, ct->c_file, 0))) {
1285                    fclose (ct->c_fp);
1286                    ct->c_fp = NULL;
1287                    return NOTOK;
1288                }
1289
1290                e->eb_parent = ct;
1291                e->eb_content = p;
1292                p->c_ctexbody = e;
1293                if ((exresult = params_external (ct, 0)) != NOTOK
1294                        && p->c_ceopenfnx == openMail) {
1295                    int cc, size;
1296                    char *bp;
1297                   
1298                    if ((size = ct->c_end - p->c_begin) <= 0) {
1299                        if (!e->eb_subject)
1300                            content_error (NULL, ct,
1301                                           "empty body for access-type=mail-server");
1302                        goto no_body;
1303                    }
1304                   
1305                    if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1306                        adios (NULL, "out of memory");
1307                    fseek (p->c_fp, p->c_begin, SEEK_SET);
1308                    while (size > 0)
1309                        switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1310                            case NOTOK:
1311                                adios ("failed", "fread");
1312
1313                            case OK:
1314                                adios (NULL, "unexpected EOF from fread");
1315
1316                            default:
1317                                bp += cc, size -= cc;
1318                                break;
1319                        }
1320                    *bp = 0;
1321                }
1322no_body:
1323                p->c_fp = NULL;
1324                p->c_end = p->c_begin;
1325
1326                fclose (ct->c_fp);
1327                ct->c_fp = NULL;
1328
1329                if (exresult == NOTOK)
1330                    return NOTOK;
1331                if (e->eb_flags == NOTOK)
1332                    return OK;
1333
1334                switch (p->c_type) {
1335                    case CT_MULTIPART:
1336                        break;
1337
1338                    case CT_MESSAGE:
1339                        if (p->c_subtype != MESSAGE_RFC822)
1340                            break;
1341                        /* else fall... */
1342                    default:
1343                        e->eb_partno = ct->c_partno;
1344                        if (p->c_ctinitfnx)
1345                            (*p->c_ctinitfnx) (p);
1346                        break;
1347                }
1348            }
1349            break;
1350
1351        default:
1352            break;
1353    }
1354
1355    return OK;
1356}
1357
1358
1359static int
1360params_external (CT ct, int composing)
1361{
1362    char **ap, **ep;
1363    struct exbody *e = (struct exbody *) ct->c_ctparams;
1364    CI ci = &ct->c_ctinfo;
1365
1366    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1367        if (!strcasecmp (*ap, "access-type")) {
1368            struct str2init *s2i;
1369            CT p = e->eb_content;
1370
1371            for (s2i = str2methods; s2i->si_key; s2i++)
1372                if (!strcasecmp (*ep, s2i->si_key))
1373                    break;
1374            if (!s2i->si_key) {
1375                e->eb_access = *ep;
1376                e->eb_flags = NOTOK;
1377                p->c_encoding = CE_EXTERNAL;
1378                continue;
1379            }
1380            e->eb_access = s2i->si_key;
1381            e->eb_flags = s2i->si_val;
1382            p->c_encoding = CE_EXTERNAL;
1383
1384            /* Call the Init function for this external type */
1385            if ((*s2i->si_init)(p) == NOTOK)
1386                return NOTOK;
1387            continue;
1388        }
1389        if (!strcasecmp (*ap, "name")) {
1390            e->eb_name = *ep;
1391            continue;
1392        }
1393        if (!strcasecmp (*ap, "permission")) {
1394            e->eb_permission = *ep;
1395            continue;
1396        }
1397        if (!strcasecmp (*ap, "site")) {
1398            e->eb_site = *ep;
1399            continue;
1400        }
1401        if (!strcasecmp (*ap, "directory")) {
1402            e->eb_dir = *ep;
1403            continue;
1404        }
1405        if (!strcasecmp (*ap, "mode")) {
1406            e->eb_mode = *ep;
1407            continue;
1408        }
1409        if (!strcasecmp (*ap, "size")) {
1410            sscanf (*ep, "%lu", &e->eb_size);
1411            continue;
1412        }
1413        if (!strcasecmp (*ap, "server")) {
1414            e->eb_server = *ep;
1415            continue;
1416        }
1417        if (!strcasecmp (*ap, "subject")) {
1418            e->eb_subject = *ep;
1419            continue;
1420        }
1421        if (composing && !strcasecmp (*ap, "body")) {
1422            e->eb_body = getcpy (*ep);
1423            continue;
1424        }
1425    }
1426
1427    if (!e->eb_access) {
1428        advise (NULL,
1429                "invalid parameters for \"%s/%s\" type in message %s's %s field",
1430                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1431        return NOTOK;
1432    }
1433
1434    return OK;
1435}
1436
1437
1438/*
1439 * APPLICATION
1440 */
1441
1442static int
1443InitApplication (CT ct)
1444{
1445    struct k2v *kv;
1446    CI ci = &ct->c_ctinfo;
1447
1448    /* match subtype */
1449    for (kv = SubApplication; kv->kv_key; kv++)
1450        if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1451            break;
1452    ct->c_subtype = kv->kv_value;
1453
1454    return OK;
1455}
1456
1457
1458/*
1459 * TRANSFER ENCODINGS
1460 */
1461
1462static int
1463init_encoding (CT ct, OpenCEFunc openfnx)
1464{
1465    CE ce;
1466
1467    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1468        adios (NULL, "out of memory");
1469
1470    ct->c_cefile     = ce;
1471    ct->c_ceopenfnx  = openfnx;
1472    ct->c_ceclosefnx = close_encoding;
1473    ct->c_cesizefnx  = size_encoding;
1474
1475    return OK;
1476}
1477
1478
1479static void
1480close_encoding (CT ct)
1481{
1482    CE ce;
1483
1484    if (!(ce = ct->c_cefile))
1485        return;
1486
1487    if (ce->ce_fp) {
1488        fclose (ce->ce_fp);
1489        ce->ce_fp = NULL;
1490    }
1491}
1492
1493
1494static unsigned long
1495size_encoding (CT ct)
1496{
1497    int fd;
1498    unsigned long size;
1499    char *file;
1500    CE ce;
1501    struct stat st;
1502
1503    if (!(ce = ct->c_cefile))
1504        return (ct->c_end - ct->c_begin);
1505
1506    if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1507        return (long) st.st_size;
1508
1509    if (ce->ce_file) {
1510        if (stat (ce->ce_file, &st) != NOTOK)
1511            return (long) st.st_size;
1512        else
1513            return 0L;
1514    }
1515
1516    if (ct->c_encoding == CE_EXTERNAL)
1517        return (ct->c_end - ct->c_begin);       
1518
1519    file = NULL;
1520    if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1521        return (ct->c_end - ct->c_begin);
1522
1523    if (fstat (fd, &st) != NOTOK)
1524        size = (long) st.st_size;
1525    else
1526        size = 0L;
1527
1528    (*ct->c_ceclosefnx) (ct);
1529    return size;
1530}
1531
1532
1533/*
1534 * BASE64
1535 */
1536
1537static unsigned char b642nib[0x80] = {
1538    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1539    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1540    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1541    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1542    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1543    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1544    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1545    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1546    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1547    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1548    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1549    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1550    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1551    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1552    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1553    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1554};
1555
1556
1557static int
1558InitBase64 (CT ct)
1559{
1560    return init_encoding (ct, openBase64);
1561}
1562
1563
1564static int
1565openBase64 (CT ct, char **file)
1566{
1567    int bitno, cc, digested;
1568    int fd, len, skip;
1569    unsigned long bits;
1570    unsigned char value, *b, *b1, *b2, *b3;
1571    char *cp, *ep, buffer[BUFSIZ];
1572    CE ce;
1573    MD5_CTX mdContext;
1574
1575    b  = (unsigned char *) &bits;
1576    b1 = &b[endian > 0 ? 1 : 2];
1577    b2 = &b[endian > 0 ? 2 : 1];
1578    b3 = &b[endian > 0 ? 3 : 0];
1579
1580    ce = ct->c_cefile;
1581    if (ce->ce_fp) {
1582        fseek (ce->ce_fp, 0L, SEEK_SET);
1583        goto ready_to_go;
1584    }
1585
1586    if (ce->ce_file) {
1587        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1588            content_error (ce->ce_file, ct, "unable to fopen for reading");
1589            return NOTOK;
1590        }
1591        goto ready_to_go;
1592    }
1593
1594    if (*file == NULL) {
1595        ce->ce_file = add (m_scratch ("", tmp), NULL);
1596        ce->ce_unlink = 1;
1597    } else {
1598        ce->ce_file = add (*file, NULL);
1599        ce->ce_unlink = 0;
1600    }
1601
1602    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1603        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1604        return NOTOK;
1605    }
1606
1607    if ((len = ct->c_end - ct->c_begin) < 0)
1608        adios (NULL, "internal error(1)");
1609
1610    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1611        content_error (ct->c_file, ct, "unable to open for reading");
1612        return NOTOK;
1613    }
1614   
1615    if ((digested = ct->c_digested))
1616        MD5Init (&mdContext);
1617
1618    bitno = 18;
1619    bits = 0L;
1620    skip = 0;
1621
1622    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1623    while (len > 0) {
1624        switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1625        case NOTOK:
1626            content_error (ct->c_file, ct, "error reading from");
1627            goto clean_up;
1628
1629        case OK:
1630            content_error (NULL, ct, "premature eof");
1631            goto clean_up;
1632
1633        default:
1634            if (cc > len)
1635                cc = len;
1636            len -= cc;
1637
1638            for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1639                switch (*cp) {
1640                default:
1641                    if (isspace (*cp))
1642                        break;
1643                    if (skip || (*cp & 0x80)
1644                        || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1645                        if (debugsw) {
1646                            fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1647                                *cp,
1648                                (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1649                                skip);
1650                        }
1651                        content_error (NULL, ct,
1652                                       "invalid BASE64 encoding -- continuing");
1653                        continue;
1654                    }
1655
1656                    bits |= value << bitno;
1657test_end:
1658                    if ((bitno -= 6) < 0) {
1659                        putc ((char) *b1, ce->ce_fp);
1660                        if (digested)
1661                            MD5Update (&mdContext, b1, 1);
1662                        if (skip < 2) {
1663                            putc ((char) *b2, ce->ce_fp);
1664                            if (digested)
1665                                MD5Update (&mdContext, b2, 1);
1666                            if (skip < 1) {
1667                                putc ((char) *b3, ce->ce_fp);
1668                                if (digested)
1669                                    MD5Update (&mdContext, b3, 1);
1670                            }
1671                        }
1672
1673                        if (ferror (ce->ce_fp)) {
1674                            content_error (ce->ce_file, ct,
1675                                           "error writing to");
1676                            goto clean_up;
1677                        }
1678                        bitno = 18, bits = 0L, skip = 0;
1679                    }
1680                    break;
1681
1682                case '=':
1683                    if (++skip > 3)
1684                        goto self_delimiting;
1685                    goto test_end;
1686                }
1687            }
1688        }
1689    }
1690
1691    if (bitno != 18) {
1692        if (debugsw)
1693            fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1694
1695        content_error (NULL, ct, "invalid BASE64 encoding");
1696        goto clean_up;
1697    }
1698
1699self_delimiting:
1700    fseek (ct->c_fp, 0L, SEEK_SET);
1701
1702    if (fflush (ce->ce_fp)) {
1703        content_error (ce->ce_file, ct, "error writing to");
1704        goto clean_up;
1705    }
1706
1707    if (digested) {
1708        unsigned char digest[16];
1709
1710        MD5Final (digest, &mdContext);
1711        if (memcmp((char *) digest, (char *) ct->c_digest,
1712                   sizeof(digest) / sizeof(digest[0])))
1713            content_error (NULL, ct,
1714                           "content integrity suspect (digest mismatch) -- continuing");
1715        else
1716            if (debugsw)
1717                fprintf (stderr, "content integrity confirmed\n");
1718    }
1719
1720    fseek (ce->ce_fp, 0L, SEEK_SET);
1721
1722ready_to_go:
1723    *file = ce->ce_file;
1724    return fileno (ce->ce_fp);
1725
1726clean_up:
1727    free_encoding (ct, 0);
1728    return NOTOK;
1729}
1730
1731
1732/*
1733 * QUOTED PRINTABLE
1734 */
1735
1736static char hex2nib[0x80] = {
1737    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1738    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1739    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1740    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1741    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1742    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1743    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1744    0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1745    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1746    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1747    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1748    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1749    0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1750    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1751    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1752    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1753};
1754
1755
1756static int
1757InitQuoted (CT ct)
1758{
1759    return init_encoding (ct, openQuoted);
1760}
1761
1762
1763static int
1764openQuoted (CT ct, char **file)
1765{
1766    int cc, digested, len, quoted;
1767    char *cp, *ep;
1768    char buffer[BUFSIZ];
1769    unsigned char mask;
1770    CE ce;
1771    MD5_CTX mdContext;
1772
1773    ce = ct->c_cefile;
1774    if (ce->ce_fp) {
1775        fseek (ce->ce_fp, 0L, SEEK_SET);
1776        goto ready_to_go;
1777    }
1778
1779    if (ce->ce_file) {
1780        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1781            content_error (ce->ce_file, ct, "unable to fopen for reading");
1782            return NOTOK;
1783        }
1784        goto ready_to_go;
1785    }
1786
1787    if (*file == NULL) {
1788        ce->ce_file = add (m_scratch ("", tmp), NULL);
1789        ce->ce_unlink = 1;
1790    } else {
1791        ce->ce_file = add (*file, NULL);
1792        ce->ce_unlink = 0;
1793    }
1794
1795    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1796        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1797        return NOTOK;
1798    }
1799
1800    if ((len = ct->c_end - ct->c_begin) < 0)
1801        adios (NULL, "internal error(2)");
1802
1803    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1804        content_error (ct->c_file, ct, "unable to open for reading");
1805        return NOTOK;
1806    }
1807
1808    if ((digested = ct->c_digested))
1809        MD5Init (&mdContext);
1810
1811    quoted = 0;
1812#ifdef lint
1813    mask = 0;
1814#endif
1815
1816    fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1817    while (len > 0) {
1818        char *dp;
1819
1820        if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1821            content_error (NULL, ct, "premature eof");
1822            goto clean_up;
1823        }
1824
1825        if ((cc = strlen (buffer)) > len)
1826            cc = len;
1827        len -= cc;
1828
1829        for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1830            if (!isspace (*ep))
1831                break;
1832        *++ep = '\n', ep++;
1833
1834        for (; cp < ep; cp++) {
1835            if (quoted) {
1836                if (quoted > 1) {
1837                    if (!isxdigit (*cp)) {
1838invalid_hex:
1839                        dp = "expecting hexidecimal-digit";
1840                        goto invalid_encoding;
1841                    }
1842                    mask <<= 4;
1843                    mask |= hex2nib[*cp & 0x7f];
1844                    putc (mask, ce->ce_fp);
1845                    if (digested)
1846                        MD5Update (&mdContext, &mask, 1);
1847                } else {
1848                    switch (*cp) {
1849                    case ':':
1850                        putc (*cp, ce->ce_fp);
1851                        if (digested)
1852                            MD5Update (&mdContext, (unsigned char *) ":", 1);
1853                        break;
1854
1855                    default:
1856                        if (!isxdigit (*cp))
1857                            goto invalid_hex;
1858                        mask = hex2nib[*cp & 0x7f];
1859                        quoted = 2;
1860                        continue;
1861                    }
1862                }
1863
1864                if (ferror (ce->ce_fp)) {
1865                    content_error (ce->ce_file, ct, "error writing to");
1866                    goto clean_up;
1867                }
1868                quoted = 0;
1869                continue;
1870            }
1871
1872            switch (*cp) {
1873            default:
1874                if (*cp < '!' || *cp > '~') {
1875                    int i;
1876                    dp = "expecting character in range [!..~]";
1877
1878invalid_encoding:
1879                    i = strlen (invo_name) + 2;
1880                    content_error (NULL, ct,
1881                                   "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1882                                   dp, i, i, "", *cp);
1883                    goto clean_up;
1884                }
1885                /* and fall...*/
1886            case ' ':
1887            case '\t':
1888            case '\n':
1889                putc (*cp, ce->ce_fp);
1890                if (digested) {
1891                    if (*cp == '\n')
1892                        MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1893                    else
1894                        MD5Update (&mdContext, (unsigned char *) cp, 1);
1895                }
1896                if (ferror (ce->ce_fp)) {
1897                    content_error (ce->ce_file, ct, "error writing to");
1898                    goto clean_up;
1899                }
1900                break;
1901
1902            case '=':
1903                if (*++cp != '\n') {
1904                    quoted = 1;
1905                    cp--;
1906                }
1907                break;
1908            }
1909        }
1910    }
1911    if (quoted) {
1912        content_error (NULL, ct,
1913                       "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1914        goto clean_up;
1915    }
1916
1917    fseek (ct->c_fp, 0L, SEEK_SET);
1918
1919    if (fflush (ce->ce_fp)) {
1920        content_error (ce->ce_file, ct, "error writing to");
1921        goto clean_up;
1922    }
1923
1924    if (digested) {
1925        unsigned char digest[16];
1926
1927        MD5Final (digest, &mdContext);
1928        if (memcmp((char *) digest, (char *) ct->c_digest,
1929                   sizeof(digest) / sizeof(digest[0])))
1930            content_error (NULL, ct,
1931                           "content integrity suspect (digest mismatch) -- continuing");
1932        else
1933            if (debugsw)
1934                fprintf (stderr, "content integrity confirmed\n");
1935    }
1936
1937    fseek (ce->ce_fp, 0L, SEEK_SET);
1938
1939ready_to_go:
1940    *file = ce->ce_file;
1941    return fileno (ce->ce_fp);
1942
1943clean_up:
1944    free_encoding (ct, 0);
1945    return NOTOK;
1946}
1947
1948
1949/*
1950 * 7BIT
1951 */
1952
1953static int
1954Init7Bit (CT ct)
1955{
1956    if (init_encoding (ct, open7Bit) == NOTOK)
1957        return NOTOK;
1958
1959    ct->c_cesizefnx = NULL;     /* no need to decode for real size */
1960    return OK;
1961}
1962
1963
1964static int
1965open7Bit (CT ct, char **file)
1966{
1967    int cc, fd, len;
1968    char buffer[BUFSIZ];
1969    CE ce;
1970
1971    ce = ct->c_cefile;
1972    if (ce->ce_fp) {
1973        fseek (ce->ce_fp, 0L, SEEK_SET);
1974        goto ready_to_go;
1975    }
1976
1977    if (ce->ce_file) {
1978        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1979            content_error (ce->ce_file, ct, "unable to fopen for reading");
1980            return NOTOK;
1981        }
1982        goto ready_to_go;
1983    }
1984
1985    if (*file == NULL) {
1986        ce->ce_file = add (m_scratch ("", tmp), NULL);
1987        ce->ce_unlink = 1;
1988    } else {
1989        ce->ce_file = add (*file, NULL);
1990        ce->ce_unlink = 0;
1991    }
1992
1993    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1994        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1995        return NOTOK;
1996    }
1997
1998    if (ct->c_type == CT_MULTIPART) {
1999        char **ap, **ep;
2000        CI ci = &ct->c_ctinfo;
2001
2002        len = 0;
2003        fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2004        len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2005            + 1 + strlen (ci->ci_subtype);
2006        for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2007            putc (';', ce->ce_fp);
2008            len++;
2009
2010            snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2011
2012            if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2013                fputs ("\n\t", ce->ce_fp);
2014                len = 8;
2015            } else {
2016                putc (' ', ce->ce_fp);
2017                len++;
2018            }
2019            fprintf (ce->ce_fp, "%s", buffer);
2020            len += cc;
2021        }
2022
2023        if (ci->ci_comment) {
2024            if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2025                fputs ("\n\t", ce->ce_fp);
2026                len = 8;
2027            }
2028            else {
2029                putc (' ', ce->ce_fp);
2030                len++;
2031            }
2032            fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2033            len += cc;
2034        }
2035        fprintf (ce->ce_fp, "\n");
2036        if (ct->c_id)
2037            fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2038        if (ct->c_descr)
2039            fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2040        fprintf (ce->ce_fp, "\n");
2041    }
2042
2043    if ((len = ct->c_end - ct->c_begin) < 0)
2044        adios (NULL, "internal error(3)");
2045
2046    if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2047        content_error (ct->c_file, ct, "unable to open for reading");
2048        return NOTOK;
2049    }
2050
2051    lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2052    while (len > 0)
2053        switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2054        case NOTOK:
2055            content_error (ct->c_file, ct, "error reading from");
2056            goto clean_up;
2057
2058        case OK:
2059            content_error (NULL, ct, "premature eof");
2060            goto clean_up;
2061
2062        default:
2063            if (cc > len)
2064                cc = len;
2065            len -= cc;
2066
2067            fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2068            if (ferror (ce->ce_fp)) {
2069                content_error (ce->ce_file, ct, "error writing to");
2070                goto clean_up;
2071            }
2072        }
2073
2074    fseek (ct->c_fp, 0L, SEEK_SET);
2075
2076    if (fflush (ce->ce_fp)) {
2077        content_error (ce->ce_file, ct, "error writing to");
2078        goto clean_up;
2079    }
2080
2081    fseek (ce->ce_fp, 0L, SEEK_SET);
2082
2083ready_to_go:
2084    *file = ce->ce_file;
2085    return fileno (ce->ce_fp);
2086
2087clean_up:
2088    free_encoding (ct, 0);
2089    return NOTOK;
2090}
2091
2092
2093/*
2094 * External
2095 */
2096
2097static int
2098openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2099{
2100    char cachefile[BUFSIZ];
2101
2102    if (ce->ce_fp) {
2103        fseek (ce->ce_fp, 0L, SEEK_SET);
2104        goto ready_already;
2105    }
2106
2107    if (ce->ce_file) {
2108        if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2109            content_error (ce->ce_file, ct, "unable to fopen for reading");
2110            return NOTOK;
2111        }
2112        goto ready_already;
2113    }
2114
2115    if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2116                cachefile, sizeof(cachefile)) != NOTOK) {
2117        if ((ce->ce_fp = fopen (cachefile, "r"))) {
2118            ce->ce_file = getcpy (cachefile);
2119            ce->ce_unlink = 0;
2120            goto ready_already;
2121        } else {
2122            admonish (cachefile, "unable to fopen for reading");
2123        }
2124    }
2125
2126    return OK;
2127
2128ready_already:
2129    *file = ce->ce_file;
2130    *fd = fileno (ce->ce_fp);
2131    return DONE;
2132}
2133
2134/*
2135 * File
2136 */
2137
2138static int
2139InitFile (CT ct)
2140{
2141    return init_encoding (ct, openFile);
2142}
2143
2144
2145static int
2146openFile (CT ct, char **file)
2147{
2148    int fd, cachetype;
2149    char cachefile[BUFSIZ];
2150    struct exbody *e = ct->c_ctexbody;
2151    CE ce = ct->c_cefile;
2152
2153    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2154        case NOTOK:
2155            return NOTOK;
2156
2157        case OK:
2158            break;
2159
2160        case DONE:
2161            return fd;
2162    }
2163
2164    if (!e->eb_name) {
2165        content_error (NULL, ct, "missing name parameter");
2166        return NOTOK;
2167    }
2168
2169    ce->ce_file = getcpy (e->eb_name);
2170    ce->ce_unlink = 0;
2171
2172    if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2173        content_error (ce->ce_file, ct, "unable to fopen for reading");
2174        return NOTOK;
2175    }
2176
2177    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2178            && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2179                cachefile, sizeof(cachefile)) != NOTOK) {
2180        int mask;
2181        FILE *fp;
2182
2183        mask = umask (cachetype ? ~m_gmprot () : 0222);
2184        if ((fp = fopen (cachefile, "w"))) {
2185            int cc;
2186            char buffer[BUFSIZ];
2187            FILE *gp = ce->ce_fp;
2188
2189            fseek (gp, 0L, SEEK_SET);
2190
2191            while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2192                       > 0)
2193                fwrite (buffer, sizeof(*buffer), cc, fp);
2194            fflush (fp);
2195
2196            if (ferror (gp)) {
2197                admonish (ce->ce_file, "error reading");
2198                unlink (cachefile);
2199            }
2200            else
2201                if (ferror (fp)) {
2202                    admonish (cachefile, "error writing");
2203                    unlink (cachefile);
2204                }
2205            fclose (fp);
2206        }
2207        umask (mask);
2208    }
2209
2210    fseek (ce->ce_fp, 0L, SEEK_SET);
2211    *file = ce->ce_file;
2212    return fileno (ce->ce_fp);
2213}
2214
2215/*
2216 * FTP
2217 */
2218
2219static int
2220InitFTP (CT ct)
2221{
2222    return init_encoding (ct, openFTP);
2223}
2224
2225
2226static int
2227openFTP (CT ct, char **file)
2228{
2229    int cachetype, caching, fd;
2230    int len, buflen;
2231    char *bp, *ftp, *user, *pass;
2232    char buffer[BUFSIZ], cachefile[BUFSIZ];
2233    struct exbody *e;
2234    CE ce;
2235    static char *username = NULL;
2236    static char *password = NULL;
2237
2238    e  = ct->c_ctexbody;
2239    ce = ct->c_cefile;
2240
2241    if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2242        ftp = NULL;
2243
2244#ifndef BUILTIN_FTP
2245    if (!ftp)
2246        return NOTOK;
2247#endif
2248
2249    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2250        case NOTOK:
2251            return NOTOK;
2252
2253        case OK:
2254            break;
2255
2256        case DONE:
2257            return fd;
2258    }
2259
2260    if (!e->eb_name || !e->eb_site) {
2261        content_error (NULL, ct, "missing %s parameter",
2262                       e->eb_name ? "site": "name");
2263        return NOTOK;
2264    }
2265
2266    if (xpid) {
2267        if (xpid < 0)
2268            xpid = -xpid;
2269        pidcheck (pidwait (xpid, NOTOK));
2270        xpid = 0;
2271    }
2272
2273    /* Get the buffer ready to go */
2274    bp = buffer;
2275    buflen = sizeof(buffer);
2276
2277    /*
2278     * Construct the query message for user
2279     */
2280    snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2281    len = strlen (bp);
2282    bp += len;
2283    buflen -= len;
2284
2285    if (e->eb_partno) {
2286        snprintf (bp, buflen, " (content %s)", e->eb_partno);
2287        len = strlen (bp);
2288        bp += len;
2289        buflen -= len;
2290    }
2291
2292    snprintf (bp, buflen, "\n    using %sFTP from site %s",
2293                    e->eb_flags ? "anonymous " : "", e->eb_site);
2294    len = strlen (bp);
2295    bp += len;
2296    buflen -= len;
2297
2298    if (e->eb_size > 0) {
2299        snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2300        len = strlen (bp);
2301        bp += len;
2302        buflen -= len;
2303    }
2304    snprintf (bp, buflen, "? ");
2305
2306    /*
2307     * Now, check the answer
2308     */
2309    if (!getanswer (buffer))
2310        return NOTOK;
2311
2312    if (e->eb_flags) {
2313        user = "anonymous";
2314        snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2315        pass = buffer;
2316    } else {
2317        ruserpass (e->eb_site, &username, &password);
2318        user = username;
2319        pass = password;
2320    }
2321
2322    ce->ce_unlink = (*file == NULL);
2323    caching = 0;
2324    cachefile[0] = '\0';
2325    if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2326            && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2327                cachefile, sizeof(cachefile)) != NOTOK) {
2328        if (*file == NULL) {
2329            ce->ce_unlink = 0;
2330            caching = 1;
2331        }
2332    }
2333
2334    if (*file)
2335        ce->ce_file = add (*file, NULL);
2336    else if (caching)
2337        ce->ce_file = add (cachefile, NULL);
2338    else
2339        ce->ce_file = add (m_scratch ("", tmp), NULL);
2340
2341    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2342        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2343        return NOTOK;
2344    }
2345
2346#ifdef BUILTIN_FTP
2347    if (ftp)
2348#endif
2349    {
2350        int child_id, i, vecp;
2351        char *vec[9];
2352
2353        vecp = 0;
2354        vec[vecp++] = r1bindex (ftp, '/');
2355        vec[vecp++] = e->eb_site;
2356        vec[vecp++] = user;
2357        vec[vecp++] = pass;
2358        vec[vecp++] = e->eb_dir;
2359        vec[vecp++] = e->eb_name;
2360        vec[vecp++] = ce->ce_file,
2361        vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2362                        ? "ascii" : "binary";
2363        vec[vecp] = NULL;
2364
2365        fflush (stdout);
2366
2367        for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2368            sleep (5);
2369        switch (child_id) {
2370            case NOTOK:
2371                adios ("fork", "unable to");
2372                /* NOTREACHED */
2373
2374            case OK:
2375                close (fileno (ce->ce_fp));
2376                execvp (ftp, vec);
2377                fprintf (stderr, "unable to exec ");
2378                perror (ftp);
2379                _exit (-1);
2380                /* NOTREACHED */
2381
2382            default:
2383                if (pidXwait (child_id, NULL)) {
2384#ifdef BUILTIN_FTP
2385losing_ftp:
2386#endif
2387                    username = password = NULL;
2388                    ce->ce_unlink = 1;
2389                    return NOTOK;
2390                }
2391                break;
2392        }
2393    }
2394#ifdef BUILTIN_FTP
2395    else
2396        if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2397                     ce->ce_file,
2398                     e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2399                == NOTOK)
2400            goto losing_ftp;
2401#endif
2402
2403    if (cachefile[0])
2404        if (caching)
2405            chmod (cachefile, cachetype ? m_gmprot () : 0444);
2406        else {
2407            int mask;
2408            FILE *fp;
2409
2410            mask = umask (cachetype ? ~m_gmprot () : 0222);
2411            if ((fp = fopen (cachefile, "w"))) {
2412                int cc;
2413                FILE *gp = ce->ce_fp;
2414
2415                fseek (gp, 0L, SEEK_SET);
2416
2417                while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2418                           > 0)
2419                    fwrite (buffer, sizeof(*buffer), cc, fp);
2420                fflush (fp);
2421
2422                if (ferror (gp)) {
2423                    admonish (ce->ce_file, "error reading");
2424                    unlink (cachefile);
2425                }
2426                else
2427                    if (ferror (fp)) {
2428                        admonish (cachefile, "error writing");
2429                        unlink (cachefile);
2430                    }
2431                fclose (fp);
2432            }
2433            umask (mask);
2434        }
2435
2436    fseek (ce->ce_fp, 0L, SEEK_SET);
2437    *file = ce->ce_file;
2438    return fileno (ce->ce_fp);
2439}
2440
2441
2442/*
2443 * Mail
2444 */
2445
2446static int
2447InitMail (CT ct)
2448{
2449    return init_encoding (ct, openMail);
2450}
2451
2452
2453static int
2454openMail (CT ct, char **file)
2455{
2456    int child_id, fd, i, vecp;
2457    int len, buflen;
2458    char *bp, buffer[BUFSIZ], *vec[7];
2459    struct exbody *e = ct->c_ctexbody;
2460    CE ce = ct->c_cefile;
2461
2462    switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2463        case NOTOK:
2464            return NOTOK;
2465
2466        case OK:
2467            break;
2468
2469        case DONE:
2470            return fd;
2471    }
2472
2473    if (!e->eb_server) {
2474        content_error (NULL, ct, "missing server parameter");
2475        return NOTOK;
2476    }
2477
2478    if (xpid) {
2479        if (xpid < 0)
2480            xpid = -xpid;
2481        pidcheck (pidwait (xpid, NOTOK));
2482        xpid = 0;
2483    }
2484
2485    /* Get buffer ready to go */
2486    bp = buffer;
2487    buflen = sizeof(buffer);
2488
2489    /* Now, construct query message */
2490    snprintf (bp, buflen, "Retrieve content");
2491    len = strlen (bp);
2492    bp += len;
2493    buflen -= len;
2494
2495    if (e->eb_partno) {
2496        snprintf (bp, buflen, " %s", e->eb_partno);
2497        len = strlen (bp);
2498        bp += len;
2499        buflen -= len;
2500    }
2501
2502    snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2503                    e->eb_server,
2504                    e->eb_subject ? e->eb_subject : e->eb_body);
2505
2506    /* Now, check answer */
2507    if (!getanswer (buffer))
2508        return NOTOK;
2509
2510    vecp = 0;
2511    vec[vecp++] = r1bindex (mailproc, '/');
2512    vec[vecp++] = e->eb_server;
2513    vec[vecp++] = "-subject";
2514    vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2515    vec[vecp++] = "-body";
2516    vec[vecp++] = e->eb_body;
2517    vec[vecp] = NULL;
2518
2519    for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2520        sleep (5);
2521    switch (child_id) {
2522        case NOTOK:
2523            advise ("fork", "unable to");
2524            return NOTOK;
2525
2526        case OK:
2527            execvp (mailproc, vec);
2528            fprintf (stderr, "unable to exec ");
2529            perror (mailproc);
2530            _exit (-1);
2531            /* NOTREACHED */
2532
2533        default:
2534            if (pidXwait (child_id, NULL) == OK)
2535                advise (NULL, "request sent");
2536            break;
2537    }
2538
2539    if (*file == NULL) {
2540        ce->ce_file = add (m_scratch ("", tmp), NULL);
2541        ce->ce_unlink = 1;
2542    } else {
2543        ce->ce_file = add (*file, NULL);
2544        ce->ce_unlink = 0;
2545    }
2546
2547    if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2548        content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2549        return NOTOK;
2550    }
2551
2552    if (ct->c_showproc)
2553        free (ct->c_showproc);
2554    ct->c_showproc = add ("true", NULL);
2555
2556    fseek (ce->ce_fp, 0L, SEEK_SET);
2557    *file = ce->ce_file;
2558    return fileno (ce->ce_fp);
2559}
2560
2561
2562static int
2563readDigest (CT ct, char *cp)
2564{
2565    int bitno, skip;
2566    unsigned long bits;
2567    char *bp = cp;
2568    unsigned char *dp, value, *ep;
2569    unsigned char *b, *b1, *b2, *b3;
2570
2571    b  = (unsigned char *) &bits,
2572    b1 = &b[endian > 0 ? 1 : 2],
2573    b2 = &b[endian > 0 ? 2 : 1],
2574    b3 = &b[endian > 0 ? 3 : 0];
2575    bitno = 18;
2576    bits = 0L;
2577    skip = 0;
2578
2579    for (ep = (dp = ct->c_digest)
2580                 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2581        switch (*cp) {
2582            default:
2583                if (skip
2584                        || (*cp & 0x80)
2585                        || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2586                    if (debugsw)
2587                        fprintf (stderr, "invalid BASE64 encoding\n");
2588                    return NOTOK;
2589                }
2590
2591                bits |= value << bitno;
2592test_end:
2593                if ((bitno -= 6) < 0) {
2594                    if (dp + (3 - skip) > ep)
2595                        goto invalid_digest;
2596                    *dp++ = *b1;
2597                    if (skip < 2) {
2598                        *dp++ = *b2;
2599                        if (skip < 1)
2600                            *dp++ = *b3;
2601                    }
2602                    bitno = 18;
2603                    bits = 0L;
2604                    skip = 0;
2605                }
2606                break;
2607
2608            case '=':
2609                if (++skip > 3)
2610                    goto self_delimiting;
2611                goto test_end;
2612        }
2613    if (bitno != 18) {
2614        if (debugsw)
2615            fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2616
2617        return NOTOK;
2618    }
2619self_delimiting:
2620    if (dp != ep) {
2621invalid_digest:
2622        if (debugsw) {
2623            while (*cp)
2624                cp++;
2625            fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2626                     cp - bp);
2627        }
2628
2629        return NOTOK;
2630    }
2631
2632    if (debugsw) {
2633        fprintf (stderr, "MD5 digest=");
2634        for (dp = ct->c_digest; dp < ep; dp++)
2635            fprintf (stderr, "%02x", *dp & 0xff);
2636        fprintf (stderr, "\n");
2637    }
2638
2639    return OK;
2640}
Note: See TracBrowser for help on using the repository browser.