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