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