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