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