]> diplodocus.org Git - nmh/blob - uip/mhparse.c
When debugging is enabled, replace the ctype functions with
[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_content (CT);
98 void free_encoding (CT, int);
99
100 /*
101 * static prototypes
102 */
103 static CT get_content (FILE *, char *, int);
104 static int get_comment (CT, char **, int);
105
106 static int InitGeneric (CT);
107 static int InitText (CT);
108 static int InitMultiPart (CT);
109 static void reverse_parts (CT);
110 static int InitMessage (CT);
111 static int InitApplication (CT);
112 static int init_encoding (CT, OpenCEFunc);
113 static unsigned long size_encoding (CT);
114 static int InitBase64 (CT);
115 static int openBase64 (CT, char **);
116 static int InitQuoted (CT);
117 static int openQuoted (CT, char **);
118 static int Init7Bit (CT);
119 static int openExternal (CT, CT, CE, char **, int *);
120 static int InitFile (CT);
121 static int openFile (CT, char **);
122 static int InitFTP (CT);
123 static int openFTP (CT, char **);
124 static int InitMail (CT);
125 static int openMail (CT, char **);
126 static int readDigest (CT, char *);
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 fclose (ct->c_fp);
1299 ct->c_fp = NULL;
1300 return OK;
1301 }
1302
1303
1304 /*
1305 * reverse the order of the parts of a multipart
1306 */
1307
1308 static void
1309 reverse_parts (CT ct)
1310 {
1311 int i;
1312 struct multipart *m;
1313 struct part **base, **bmp, **next, *part;
1314
1315 m = (struct multipart *) ct->c_ctparams;
1316
1317 /* if only one part, just return */
1318 if (!m->mp_parts || !m->mp_parts->mp_next)
1319 return;
1320
1321 /* count number of parts */
1322 i = 0;
1323 for (part = m->mp_parts; part; part = part->mp_next)
1324 i++;
1325
1326 /* allocate array of pointers to the parts */
1327 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1328 adios (NULL, "out of memory");
1329 bmp = base;
1330
1331 /* point at all the parts */
1332 for (part = m->mp_parts; part; part = part->mp_next)
1333 *bmp++ = part;
1334 *bmp = NULL;
1335
1336 /* reverse the order of the parts */
1337 next = &m->mp_parts;
1338 for (bmp--; bmp >= base; bmp--) {
1339 part = *bmp;
1340 *next = part;
1341 next = &part->mp_next;
1342 }
1343 *next = NULL;
1344
1345 /* free array of pointers */
1346 free ((char *) base);
1347 }
1348
1349
1350 /*
1351 * MESSAGE
1352 */
1353
1354 static int
1355 InitMessage (CT ct)
1356 {
1357 struct k2v *kv;
1358 CI ci = &ct->c_ctinfo;
1359
1360 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1361 admonish (NULL,
1362 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1363 ci->ci_type, ci->ci_subtype, ct->c_file);
1364 return NOTOK;
1365 }
1366
1367 /* check for missing subtype */
1368 if (!*ci->ci_subtype)
1369 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1370
1371 /* match subtype */
1372 for (kv = SubMessage; kv->kv_key; kv++)
1373 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1374 break;
1375 ct->c_subtype = kv->kv_value;
1376
1377 switch (ct->c_subtype) {
1378 case MESSAGE_RFC822:
1379 break;
1380
1381 case MESSAGE_PARTIAL:
1382 {
1383 char **ap, **ep;
1384 struct partial *p;
1385
1386 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1387 adios (NULL, "out of memory");
1388 ct->c_ctparams = (void *) p;
1389
1390 /* scan for parameters "id", "number", and "total" */
1391 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1392 if (!mh_strcasecmp (*ap, "id")) {
1393 p->pm_partid = add (*ep, NULL);
1394 continue;
1395 }
1396 if (!mh_strcasecmp (*ap, "number")) {
1397 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1398 || p->pm_partno < 1) {
1399 invalid_param:
1400 advise (NULL,
1401 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1402 *ap, ci->ci_type, ci->ci_subtype,
1403 ct->c_file, TYPE_FIELD);
1404 return NOTOK;
1405 }
1406 continue;
1407 }
1408 if (!mh_strcasecmp (*ap, "total")) {
1409 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1410 || p->pm_maxno < 1)
1411 goto invalid_param;
1412 continue;
1413 }
1414 }
1415
1416 if (!p->pm_partid
1417 || !p->pm_partno
1418 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1419 advise (NULL,
1420 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1421 ci->ci_type, ci->ci_subtype,
1422 ct->c_file, TYPE_FIELD);
1423 return NOTOK;
1424 }
1425 }
1426 break;
1427
1428 case MESSAGE_EXTERNAL:
1429 {
1430 int exresult;
1431 struct exbody *e;
1432 CT p;
1433 FILE *fp;
1434
1435 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1436 adios (NULL, "out of memory");
1437 ct->c_ctparams = (void *) e;
1438
1439 if (!ct->c_fp
1440 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1441 advise (ct->c_file, "unable to open for reading");
1442 return NOTOK;
1443 }
1444
1445 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1446
1447 if (!(p = get_content (fp, ct->c_file, 0))) {
1448 ct->c_fp = NULL;
1449 return NOTOK;
1450 }
1451
1452 e->eb_parent = ct;
1453 e->eb_content = p;
1454 p->c_ctexbody = e;
1455 if ((exresult = params_external (ct, 0)) != NOTOK
1456 && p->c_ceopenfnx == openMail) {
1457 int cc, size;
1458 char *bp;
1459
1460 if ((size = ct->c_end - p->c_begin) <= 0) {
1461 if (!e->eb_subject)
1462 content_error (NULL, ct,
1463 "empty body for access-type=mail-server");
1464 goto no_body;
1465 }
1466
1467 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1468 fseek (p->c_fp, p->c_begin, SEEK_SET);
1469 while (size > 0)
1470 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1471 case NOTOK:
1472 adios ("failed", "fread");
1473
1474 case OK:
1475 adios (NULL, "unexpected EOF from fread");
1476
1477 default:
1478 bp += cc, size -= cc;
1479 break;
1480 }
1481 *bp = 0;
1482 }
1483 no_body:
1484 p->c_fp = NULL;
1485 p->c_end = p->c_begin;
1486
1487 fclose (ct->c_fp);
1488 ct->c_fp = NULL;
1489
1490 if (exresult == NOTOK)
1491 return NOTOK;
1492 if (e->eb_flags == NOTOK)
1493 return OK;
1494
1495 switch (p->c_type) {
1496 case CT_MULTIPART:
1497 break;
1498
1499 case CT_MESSAGE:
1500 if (p->c_subtype != MESSAGE_RFC822)
1501 break;
1502 /* else fall... */
1503 default:
1504 e->eb_partno = ct->c_partno;
1505 if (p->c_ctinitfnx)
1506 (*p->c_ctinitfnx) (p);
1507 break;
1508 }
1509 }
1510 break;
1511
1512 default:
1513 break;
1514 }
1515
1516 return OK;
1517 }
1518
1519
1520 int
1521 params_external (CT ct, int composing)
1522 {
1523 char **ap, **ep;
1524 struct exbody *e = (struct exbody *) ct->c_ctparams;
1525 CI ci = &ct->c_ctinfo;
1526
1527 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1528 if (!mh_strcasecmp (*ap, "access-type")) {
1529 struct str2init *s2i;
1530 CT p = e->eb_content;
1531
1532 for (s2i = str2methods; s2i->si_key; s2i++)
1533 if (!mh_strcasecmp (*ep, s2i->si_key))
1534 break;
1535 if (!s2i->si_key) {
1536 e->eb_access = *ep;
1537 e->eb_flags = NOTOK;
1538 p->c_encoding = CE_EXTERNAL;
1539 continue;
1540 }
1541 e->eb_access = s2i->si_key;
1542 e->eb_flags = s2i->si_val;
1543 p->c_encoding = CE_EXTERNAL;
1544
1545 /* Call the Init function for this external type */
1546 if ((*s2i->si_init)(p) == NOTOK)
1547 return NOTOK;
1548 continue;
1549 }
1550 if (!mh_strcasecmp (*ap, "name")) {
1551 e->eb_name = *ep;
1552 continue;
1553 }
1554 if (!mh_strcasecmp (*ap, "permission")) {
1555 e->eb_permission = *ep;
1556 continue;
1557 }
1558 if (!mh_strcasecmp (*ap, "site")) {
1559 e->eb_site = *ep;
1560 continue;
1561 }
1562 if (!mh_strcasecmp (*ap, "directory")) {
1563 e->eb_dir = *ep;
1564 continue;
1565 }
1566 if (!mh_strcasecmp (*ap, "mode")) {
1567 e->eb_mode = *ep;
1568 continue;
1569 }
1570 if (!mh_strcasecmp (*ap, "size")) {
1571 sscanf (*ep, "%lu", &e->eb_size);
1572 continue;
1573 }
1574 if (!mh_strcasecmp (*ap, "server")) {
1575 e->eb_server = *ep;
1576 continue;
1577 }
1578 if (!mh_strcasecmp (*ap, "subject")) {
1579 e->eb_subject = *ep;
1580 continue;
1581 }
1582 if (composing && !mh_strcasecmp (*ap, "body")) {
1583 e->eb_body = getcpy (*ep);
1584 continue;
1585 }
1586 }
1587
1588 if (!e->eb_access) {
1589 advise (NULL,
1590 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1591 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1592 return NOTOK;
1593 }
1594
1595 return OK;
1596 }
1597
1598
1599 /*
1600 * APPLICATION
1601 */
1602
1603 static int
1604 InitApplication (CT ct)
1605 {
1606 struct k2v *kv;
1607 CI ci = &ct->c_ctinfo;
1608
1609 /* match subtype */
1610 for (kv = SubApplication; kv->kv_key; kv++)
1611 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1612 break;
1613 ct->c_subtype = kv->kv_value;
1614
1615 return OK;
1616 }
1617
1618
1619 /*
1620 * TRANSFER ENCODINGS
1621 */
1622
1623 static int
1624 init_encoding (CT ct, OpenCEFunc openfnx)
1625 {
1626 CE ce;
1627
1628 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1629 adios (NULL, "out of memory");
1630
1631 ct->c_cefile = ce;
1632 ct->c_ceopenfnx = openfnx;
1633 ct->c_ceclosefnx = close_encoding;
1634 ct->c_cesizefnx = size_encoding;
1635
1636 return OK;
1637 }
1638
1639
1640 void
1641 close_encoding (CT ct)
1642 {
1643 CE ce;
1644
1645 if (!(ce = ct->c_cefile))
1646 return;
1647
1648 if (ce->ce_fp) {
1649 fclose (ce->ce_fp);
1650 ce->ce_fp = NULL;
1651 }
1652 }
1653
1654
1655 static unsigned long
1656 size_encoding (CT ct)
1657 {
1658 int fd;
1659 unsigned long size;
1660 char *file;
1661 CE ce;
1662 struct stat st;
1663
1664 if (!(ce = ct->c_cefile))
1665 return (ct->c_end - ct->c_begin);
1666
1667 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1668 return (long) st.st_size;
1669
1670 if (ce->ce_file) {
1671 if (stat (ce->ce_file, &st) != NOTOK)
1672 return (long) st.st_size;
1673 else
1674 return 0L;
1675 }
1676
1677 if (ct->c_encoding == CE_EXTERNAL)
1678 return (ct->c_end - ct->c_begin);
1679
1680 file = NULL;
1681 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1682 return (ct->c_end - ct->c_begin);
1683
1684 if (fstat (fd, &st) != NOTOK)
1685 size = (long) st.st_size;
1686 else
1687 size = 0L;
1688
1689 (*ct->c_ceclosefnx) (ct);
1690 return size;
1691 }
1692
1693
1694 /*
1695 * BASE64
1696 */
1697
1698 static unsigned char b642nib[0x80] = {
1699 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1700 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1701 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1702 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1703 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1704 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1705 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1706 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1707 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1708 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1709 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1710 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1711 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1712 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1713 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1714 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1715 };
1716
1717
1718 static int
1719 InitBase64 (CT ct)
1720 {
1721 return init_encoding (ct, openBase64);
1722 }
1723
1724
1725 static int
1726 openBase64 (CT ct, char **file)
1727 {
1728 int bitno, cc, digested;
1729 int fd, len, skip, own_ct_fp = 0;
1730 uint32_t bits;
1731 unsigned char value, b;
1732 char *cp, *ep, buffer[BUFSIZ];
1733 /* sbeck -- handle suffixes */
1734 CI ci;
1735 CE ce;
1736 MD5_CTX mdContext;
1737
1738 ce = ct->c_cefile;
1739 if (ce->ce_fp) {
1740 fseek (ce->ce_fp, 0L, SEEK_SET);
1741 goto ready_to_go;
1742 }
1743
1744 if (ce->ce_file) {
1745 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1746 content_error (ce->ce_file, ct, "unable to fopen for reading");
1747 return NOTOK;
1748 }
1749 goto ready_to_go;
1750 }
1751
1752 if (*file == NULL) {
1753 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1754 ce->ce_unlink = 1;
1755 } else {
1756 ce->ce_file = add (*file, NULL);
1757 ce->ce_unlink = 0;
1758 }
1759
1760 /* sbeck@cise.ufl.edu -- handle suffixes */
1761 ci = &ct->c_ctinfo;
1762 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1763 invo_name, ci->ci_type, ci->ci_subtype);
1764 cp = context_find (buffer);
1765 if (cp == NULL || *cp == '\0') {
1766 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1767 ci->ci_type);
1768 cp = context_find (buffer);
1769 }
1770 if (cp != NULL && *cp != '\0') {
1771 if (ce->ce_unlink) {
1772 /* Temporary file already exists, so we rename to
1773 version with extension. */
1774 char *file_org = strdup(ce->ce_file);
1775 ce->ce_file = add (cp, ce->ce_file);
1776 if (rename(file_org, ce->ce_file)) {
1777 adios (ce->ce_file, "unable to rename %s to ", file_org);
1778 }
1779 free(file_org);
1780
1781 } else {
1782 ce->ce_file = add (cp, ce->ce_file);
1783 }
1784 }
1785
1786 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1787 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1788 return NOTOK;
1789 }
1790
1791 if ((len = ct->c_end - ct->c_begin) < 0)
1792 adios (NULL, "internal error(1)");
1793
1794 if (! ct->c_fp) {
1795 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1796 content_error (ct->c_file, ct, "unable to open for reading");
1797 return NOTOK;
1798 }
1799 own_ct_fp = 1;
1800 }
1801
1802 if ((digested = ct->c_digested))
1803 MD5Init (&mdContext);
1804
1805 bitno = 18;
1806 bits = 0L;
1807 skip = 0;
1808
1809 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1810 while (len > 0) {
1811 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1812 case NOTOK:
1813 content_error (ct->c_file, ct, "error reading from");
1814 goto clean_up;
1815
1816 case OK:
1817 content_error (NULL, ct, "premature eof");
1818 goto clean_up;
1819
1820 default:
1821 if (cc > len)
1822 cc = len;
1823 len -= cc;
1824
1825 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1826 switch (*cp) {
1827 default:
1828 if (isspace ((unsigned char) *cp))
1829 break;
1830 if (skip || (((unsigned char) *cp) & 0x80)
1831 || (value = b642nib[((unsigned char) *cp) & 0x7f]) > 0x3f) {
1832 if (debugsw) {
1833 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1834 (unsigned char) *cp,
1835 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1836 skip);
1837 }
1838 content_error (NULL, ct,
1839 "invalid BASE64 encoding -- continuing");
1840 continue;
1841 }
1842
1843 bits |= value << bitno;
1844 test_end:
1845 if ((bitno -= 6) < 0) {
1846 b = (bits >> 16) & 0xff;
1847 putc ((char) b, ce->ce_fp);
1848 if (digested)
1849 MD5Update (&mdContext, &b, 1);
1850 if (skip < 2) {
1851 b = (bits >> 8) & 0xff;
1852 putc ((char) b, ce->ce_fp);
1853 if (digested)
1854 MD5Update (&mdContext, &b, 1);
1855 if (skip < 1) {
1856 b = bits & 0xff;
1857 putc ((char) b, ce->ce_fp);
1858 if (digested)
1859 MD5Update (&mdContext, &b, 1);
1860 }
1861 }
1862
1863 if (ferror (ce->ce_fp)) {
1864 content_error (ce->ce_file, ct,
1865 "error writing to");
1866 goto clean_up;
1867 }
1868 bitno = 18, bits = 0L, skip = 0;
1869 }
1870 break;
1871
1872 case '=':
1873 if (++skip > 3)
1874 goto self_delimiting;
1875 goto test_end;
1876 }
1877 }
1878 }
1879 }
1880
1881 if (bitno != 18) {
1882 if (debugsw)
1883 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1884
1885 content_error (NULL, ct, "invalid BASE64 encoding");
1886 goto clean_up;
1887 }
1888
1889 self_delimiting:
1890 fseek (ct->c_fp, 0L, SEEK_SET);
1891
1892 if (fflush (ce->ce_fp)) {
1893 content_error (ce->ce_file, ct, "error writing to");
1894 goto clean_up;
1895 }
1896
1897 if (digested) {
1898 unsigned char digest[16];
1899
1900 MD5Final (digest, &mdContext);
1901 if (memcmp((char *) digest, (char *) ct->c_digest,
1902 sizeof(digest) / sizeof(digest[0])))
1903 content_error (NULL, ct,
1904 "content integrity suspect (digest mismatch) -- continuing");
1905 else
1906 if (debugsw)
1907 fprintf (stderr, "content integrity confirmed\n");
1908 }
1909
1910 fseek (ce->ce_fp, 0L, SEEK_SET);
1911
1912 ready_to_go:
1913 *file = ce->ce_file;
1914 if (own_ct_fp) {
1915 fclose (ct->c_fp);
1916 ct->c_fp = NULL;
1917 }
1918 return fileno (ce->ce_fp);
1919
1920 clean_up:
1921 if (own_ct_fp) {
1922 fclose (ct->c_fp);
1923 ct->c_fp = NULL;
1924 }
1925 free_encoding (ct, 0);
1926 return NOTOK;
1927 }
1928
1929
1930 /*
1931 * QUOTED PRINTABLE
1932 */
1933
1934 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1942 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1951 };
1952
1953
1954 static int
1955 InitQuoted (CT ct)
1956 {
1957 return init_encoding (ct, openQuoted);
1958 }
1959
1960
1961 static int
1962 openQuoted (CT ct, char **file)
1963 {
1964 int cc, digested, len, quoted, own_ct_fp = 0;
1965 char *cp, *ep;
1966 char buffer[BUFSIZ];
1967 unsigned char mask;
1968 CE ce;
1969 /* sbeck -- handle suffixes */
1970 CI ci;
1971 MD5_CTX mdContext;
1972
1973 ce = ct->c_cefile;
1974 if (ce->ce_fp) {
1975 fseek (ce->ce_fp, 0L, SEEK_SET);
1976 goto ready_to_go;
1977 }
1978
1979 if (ce->ce_file) {
1980 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1981 content_error (ce->ce_file, ct, "unable to fopen for reading");
1982 return NOTOK;
1983 }
1984 goto ready_to_go;
1985 }
1986
1987 if (*file == NULL) {
1988 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1989 ce->ce_unlink = 1;
1990 } else {
1991 ce->ce_file = add (*file, NULL);
1992 ce->ce_unlink = 0;
1993 }
1994
1995 /* sbeck@cise.ufl.edu -- handle suffixes */
1996 ci = &ct->c_ctinfo;
1997 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1998 invo_name, ci->ci_type, ci->ci_subtype);
1999 cp = context_find (buffer);
2000 if (cp == NULL || *cp == '\0') {
2001 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2002 ci->ci_type);
2003 cp = context_find (buffer);
2004 }
2005 if (cp != NULL && *cp != '\0') {
2006 if (ce->ce_unlink) {
2007 /* Temporary file already exists, so we rename to
2008 version with extension. */
2009 char *file_org = strdup(ce->ce_file);
2010 ce->ce_file = add (cp, ce->ce_file);
2011 if (rename(file_org, ce->ce_file)) {
2012 adios (ce->ce_file, "unable to rename %s to ", file_org);
2013 }
2014 free(file_org);
2015
2016 } else {
2017 ce->ce_file = add (cp, ce->ce_file);
2018 }
2019 }
2020
2021 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2022 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2023 return NOTOK;
2024 }
2025
2026 if ((len = ct->c_end - ct->c_begin) < 0)
2027 adios (NULL, "internal error(2)");
2028
2029 if (! ct->c_fp) {
2030 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2031 content_error (ct->c_file, ct, "unable to open for reading");
2032 return NOTOK;
2033 }
2034 own_ct_fp = 1;
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 ((unsigned char) *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[((unsigned char) *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[((unsigned char) *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 ((unsigned char) cp[1]) &&
2100 isxdigit ((unsigned char) cp[2])) {
2101 /* Next 2 bytes are hex digits, making this a valid escape
2102 * sequence; let's decode it (above). */
2103 quoted = 1;
2104 continue;
2105 } else {
2106 /* One or both of the next 2 is out of range, making this
2107 * an invalid escape sequence; just show the raw bytes
2108 * (below). */
2109 }
2110 }
2111
2112 /* Just show the raw byte. */
2113 putc (*cp, ce->ce_fp);
2114 if (digested) {
2115 if (*cp == '\n') {
2116 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2117 } else {
2118 MD5Update (&mdContext, (unsigned char *) cp, 1);
2119 }
2120 }
2121 if (ferror (ce->ce_fp)) {
2122 content_error (ce->ce_file, ct, "error writing to");
2123 goto clean_up;
2124 }
2125 }
2126 }
2127 if (quoted) {
2128 content_error (NULL, ct,
2129 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2130 goto clean_up;
2131 }
2132
2133 fseek (ct->c_fp, 0L, SEEK_SET);
2134
2135 if (fflush (ce->ce_fp)) {
2136 content_error (ce->ce_file, ct, "error writing to");
2137 goto clean_up;
2138 }
2139
2140 if (digested) {
2141 unsigned char digest[16];
2142
2143 MD5Final (digest, &mdContext);
2144 if (memcmp((char *) digest, (char *) ct->c_digest,
2145 sizeof(digest) / sizeof(digest[0])))
2146 content_error (NULL, ct,
2147 "content integrity suspect (digest mismatch) -- continuing");
2148 else
2149 if (debugsw)
2150 fprintf (stderr, "content integrity confirmed\n");
2151 }
2152
2153 fseek (ce->ce_fp, 0L, SEEK_SET);
2154
2155 ready_to_go:
2156 *file = ce->ce_file;
2157 if (own_ct_fp) {
2158 fclose (ct->c_fp);
2159 ct->c_fp = NULL;
2160 }
2161 return fileno (ce->ce_fp);
2162
2163 clean_up:
2164 free_encoding (ct, 0);
2165 if (own_ct_fp) {
2166 fclose (ct->c_fp);
2167 ct->c_fp = NULL;
2168 }
2169 return NOTOK;
2170 }
2171
2172
2173 /*
2174 * 7BIT
2175 */
2176
2177 static int
2178 Init7Bit (CT ct)
2179 {
2180 if (init_encoding (ct, open7Bit) == NOTOK)
2181 return NOTOK;
2182
2183 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2184 return OK;
2185 }
2186
2187
2188 int
2189 open7Bit (CT ct, char **file)
2190 {
2191 int cc, fd, len, own_ct_fp = 0;
2192 char buffer[BUFSIZ];
2193 /* sbeck -- handle suffixes */
2194 char *cp;
2195 CI ci;
2196 CE ce;
2197
2198 ce = ct->c_cefile;
2199 if (ce->ce_fp) {
2200 fseek (ce->ce_fp, 0L, SEEK_SET);
2201 goto ready_to_go;
2202 }
2203
2204 if (ce->ce_file) {
2205 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2206 content_error (ce->ce_file, ct, "unable to fopen for reading");
2207 return NOTOK;
2208 }
2209 goto ready_to_go;
2210 }
2211
2212 if (*file == NULL) {
2213 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2214 ce->ce_unlink = 1;
2215 } else {
2216 ce->ce_file = add (*file, NULL);
2217 ce->ce_unlink = 0;
2218 }
2219
2220 /* sbeck@cise.ufl.edu -- handle suffixes */
2221 ci = &ct->c_ctinfo;
2222 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2223 invo_name, ci->ci_type, ci->ci_subtype);
2224 cp = context_find (buffer);
2225 if (cp == NULL || *cp == '\0') {
2226 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2227 ci->ci_type);
2228 cp = context_find (buffer);
2229 }
2230 if (cp != NULL && *cp != '\0') {
2231 if (ce->ce_unlink) {
2232 /* Temporary file already exists, so we rename to
2233 version with extension. */
2234 char *file_org = strdup(ce->ce_file);
2235 ce->ce_file = add (cp, ce->ce_file);
2236 if (rename(file_org, ce->ce_file)) {
2237 adios (ce->ce_file, "unable to rename %s to ", file_org);
2238 }
2239 free(file_org);
2240
2241 } else {
2242 ce->ce_file = add (cp, ce->ce_file);
2243 }
2244 }
2245
2246 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2247 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2248 return NOTOK;
2249 }
2250
2251 if (ct->c_type == CT_MULTIPART) {
2252 char **ap, **ep;
2253 CI ci = &ct->c_ctinfo;
2254
2255 len = 0;
2256 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2257 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2258 + 1 + strlen (ci->ci_subtype);
2259 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2260 putc (';', ce->ce_fp);
2261 len++;
2262
2263 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2264
2265 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2266 fputs ("\n\t", ce->ce_fp);
2267 len = 8;
2268 } else {
2269 putc (' ', ce->ce_fp);
2270 len++;
2271 }
2272 fprintf (ce->ce_fp, "%s", buffer);
2273 len += cc;
2274 }
2275
2276 if (ci->ci_comment) {
2277 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2278 fputs ("\n\t", ce->ce_fp);
2279 len = 8;
2280 }
2281 else {
2282 putc (' ', ce->ce_fp);
2283 len++;
2284 }
2285 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2286 len += cc;
2287 }
2288 fprintf (ce->ce_fp, "\n");
2289 if (ct->c_id)
2290 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2291 if (ct->c_descr)
2292 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2293 if (ct->c_dispo)
2294 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2295 fprintf (ce->ce_fp, "\n");
2296 }
2297
2298 if ((len = ct->c_end - ct->c_begin) < 0)
2299 adios (NULL, "internal error(3)");
2300
2301 if (! ct->c_fp) {
2302 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2303 content_error (ct->c_file, ct, "unable to open for reading");
2304 return NOTOK;
2305 }
2306 own_ct_fp = 1;
2307 }
2308
2309 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2310 while (len > 0)
2311 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2312 case NOTOK:
2313 content_error (ct->c_file, ct, "error reading from");
2314 goto clean_up;
2315
2316 case OK:
2317 content_error (NULL, ct, "premature eof");
2318 goto clean_up;
2319
2320 default:
2321 if (cc > len)
2322 cc = len;
2323 len -= cc;
2324
2325 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2326 if (ferror (ce->ce_fp)) {
2327 content_error (ce->ce_file, ct, "error writing to");
2328 goto clean_up;
2329 }
2330 }
2331
2332 fseek (ct->c_fp, 0L, SEEK_SET);
2333
2334 if (fflush (ce->ce_fp)) {
2335 content_error (ce->ce_file, ct, "error writing to");
2336 goto clean_up;
2337 }
2338
2339 fseek (ce->ce_fp, 0L, SEEK_SET);
2340
2341 ready_to_go:
2342 *file = ce->ce_file;
2343 if (own_ct_fp) {
2344 fclose (ct->c_fp);
2345 ct->c_fp = NULL;
2346 }
2347 return fileno (ce->ce_fp);
2348
2349 clean_up:
2350 free_encoding (ct, 0);
2351 if (own_ct_fp) {
2352 fclose (ct->c_fp);
2353 ct->c_fp = NULL;
2354 }
2355 return NOTOK;
2356 }
2357
2358
2359 /*
2360 * External
2361 */
2362
2363 static int
2364 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2365 {
2366 char cachefile[BUFSIZ];
2367
2368 if (ce->ce_fp) {
2369 fseek (ce->ce_fp, 0L, SEEK_SET);
2370 goto ready_already;
2371 }
2372
2373 if (ce->ce_file) {
2374 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2375 content_error (ce->ce_file, ct, "unable to fopen for reading");
2376 return NOTOK;
2377 }
2378 goto ready_already;
2379 }
2380
2381 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2382 cachefile, sizeof(cachefile)) != NOTOK) {
2383 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2384 ce->ce_file = getcpy (cachefile);
2385 ce->ce_unlink = 0;
2386 goto ready_already;
2387 } else {
2388 admonish (cachefile, "unable to fopen for reading");
2389 }
2390 }
2391
2392 return OK;
2393
2394 ready_already:
2395 *file = ce->ce_file;
2396 *fd = fileno (ce->ce_fp);
2397 return DONE;
2398 }
2399
2400 /*
2401 * File
2402 */
2403
2404 static int
2405 InitFile (CT ct)
2406 {
2407 return init_encoding (ct, openFile);
2408 }
2409
2410
2411 static int
2412 openFile (CT ct, char **file)
2413 {
2414 int fd, cachetype;
2415 char cachefile[BUFSIZ];
2416 struct exbody *e = ct->c_ctexbody;
2417 CE ce = ct->c_cefile;
2418
2419 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2420 case NOTOK:
2421 return NOTOK;
2422
2423 case OK:
2424 break;
2425
2426 case DONE:
2427 return fd;
2428 }
2429
2430 if (!e->eb_name) {
2431 content_error (NULL, ct, "missing name parameter");
2432 return NOTOK;
2433 }
2434
2435 ce->ce_file = getcpy (e->eb_name);
2436 ce->ce_unlink = 0;
2437
2438 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2439 content_error (ce->ce_file, ct, "unable to fopen for reading");
2440 return NOTOK;
2441 }
2442
2443 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2444 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2445 cachefile, sizeof(cachefile)) != NOTOK) {
2446 int mask;
2447 FILE *fp;
2448
2449 mask = umask (cachetype ? ~m_gmprot () : 0222);
2450 if ((fp = fopen (cachefile, "w"))) {
2451 int cc;
2452 char buffer[BUFSIZ];
2453 FILE *gp = ce->ce_fp;
2454
2455 fseek (gp, 0L, SEEK_SET);
2456
2457 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2458 > 0)
2459 fwrite (buffer, sizeof(*buffer), cc, fp);
2460 fflush (fp);
2461
2462 if (ferror (gp)) {
2463 admonish (ce->ce_file, "error reading");
2464 unlink (cachefile);
2465 }
2466 else
2467 if (ferror (fp)) {
2468 admonish (cachefile, "error writing");
2469 unlink (cachefile);
2470 }
2471 fclose (fp);
2472 }
2473 umask (mask);
2474 }
2475
2476 fseek (ce->ce_fp, 0L, SEEK_SET);
2477 *file = ce->ce_file;
2478 return fileno (ce->ce_fp);
2479 }
2480
2481 /*
2482 * FTP
2483 */
2484
2485 static int
2486 InitFTP (CT ct)
2487 {
2488 return init_encoding (ct, openFTP);
2489 }
2490
2491
2492 static int
2493 openFTP (CT ct, char **file)
2494 {
2495 int cachetype, caching, fd;
2496 int len, buflen;
2497 char *bp, *ftp, *user, *pass;
2498 char buffer[BUFSIZ], cachefile[BUFSIZ];
2499 struct exbody *e;
2500 CE ce;
2501 static char *username = NULL;
2502 static char *password = NULL;
2503
2504 e = ct->c_ctexbody;
2505 ce = ct->c_cefile;
2506
2507 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2508 ftp = NULL;
2509
2510 if (!ftp)
2511 return NOTOK;
2512
2513 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2514 case NOTOK:
2515 return NOTOK;
2516
2517 case OK:
2518 break;
2519
2520 case DONE:
2521 return fd;
2522 }
2523
2524 if (!e->eb_name || !e->eb_site) {
2525 content_error (NULL, ct, "missing %s parameter",
2526 e->eb_name ? "site": "name");
2527 return NOTOK;
2528 }
2529
2530 if (xpid) {
2531 if (xpid < 0)
2532 xpid = -xpid;
2533 pidcheck (pidwait (xpid, NOTOK));
2534 xpid = 0;
2535 }
2536
2537 /* Get the buffer ready to go */
2538 bp = buffer;
2539 buflen = sizeof(buffer);
2540
2541 /*
2542 * Construct the query message for user
2543 */
2544 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2545 len = strlen (bp);
2546 bp += len;
2547 buflen -= len;
2548
2549 if (e->eb_partno) {
2550 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2551 len = strlen (bp);
2552 bp += len;
2553 buflen -= len;
2554 }
2555
2556 snprintf (bp, buflen, "\n using %sFTP from site %s",
2557 e->eb_flags ? "anonymous " : "", e->eb_site);
2558 len = strlen (bp);
2559 bp += len;
2560 buflen -= len;
2561
2562 if (e->eb_size > 0) {
2563 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2564 len = strlen (bp);
2565 bp += len;
2566 buflen -= len;
2567 }
2568 snprintf (bp, buflen, "? ");
2569
2570 /*
2571 * Now, check the answer
2572 */
2573 if (!getanswer (buffer))
2574 return NOTOK;
2575
2576 if (e->eb_flags) {
2577 user = "anonymous";
2578 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2579 LocalName (1));
2580 pass = buffer;
2581 } else {
2582 ruserpass (e->eb_site, &username, &password);
2583 user = username;
2584 pass = password;
2585 }
2586
2587 ce->ce_unlink = (*file == NULL);
2588 caching = 0;
2589 cachefile[0] = '\0';
2590 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2591 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2592 cachefile, sizeof(cachefile)) != NOTOK) {
2593 if (*file == NULL) {
2594 ce->ce_unlink = 0;
2595 caching = 1;
2596 }
2597 }
2598
2599 if (*file)
2600 ce->ce_file = add (*file, NULL);
2601 else if (caching)
2602 ce->ce_file = add (cachefile, NULL);
2603 else
2604 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2605
2606 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2607 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2608 return NOTOK;
2609 }
2610
2611 {
2612 int child_id, i, vecp;
2613 char *vec[9];
2614
2615 vecp = 0;
2616 vec[vecp++] = r1bindex (ftp, '/');
2617 vec[vecp++] = e->eb_site;
2618 vec[vecp++] = user;
2619 vec[vecp++] = pass;
2620 vec[vecp++] = e->eb_dir;
2621 vec[vecp++] = e->eb_name;
2622 vec[vecp++] = ce->ce_file,
2623 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2624 ? "ascii" : "binary";
2625 vec[vecp] = NULL;
2626
2627 fflush (stdout);
2628
2629 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2630 sleep (5);
2631 switch (child_id) {
2632 case NOTOK:
2633 adios ("fork", "unable to");
2634 /* NOTREACHED */
2635
2636 case OK:
2637 close (fileno (ce->ce_fp));
2638 execvp (ftp, vec);
2639 fprintf (stderr, "unable to exec ");
2640 perror (ftp);
2641 _exit (-1);
2642 /* NOTREACHED */
2643
2644 default:
2645 if (pidXwait (child_id, NULL)) {
2646 username = password = NULL;
2647 ce->ce_unlink = 1;
2648 return NOTOK;
2649 }
2650 break;
2651 }
2652 }
2653
2654 if (cachefile[0]) {
2655 if (caching)
2656 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2657 else {
2658 int mask;
2659 FILE *fp;
2660
2661 mask = umask (cachetype ? ~m_gmprot () : 0222);
2662 if ((fp = fopen (cachefile, "w"))) {
2663 int cc;
2664 FILE *gp = ce->ce_fp;
2665
2666 fseek (gp, 0L, SEEK_SET);
2667
2668 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2669 > 0)
2670 fwrite (buffer, sizeof(*buffer), cc, fp);
2671 fflush (fp);
2672
2673 if (ferror (gp)) {
2674 admonish (ce->ce_file, "error reading");
2675 unlink (cachefile);
2676 }
2677 else
2678 if (ferror (fp)) {
2679 admonish (cachefile, "error writing");
2680 unlink (cachefile);
2681 }
2682 fclose (fp);
2683 }
2684 umask (mask);
2685 }
2686 }
2687
2688 fseek (ce->ce_fp, 0L, SEEK_SET);
2689 *file = ce->ce_file;
2690 return fileno (ce->ce_fp);
2691 }
2692
2693
2694 /*
2695 * Mail
2696 */
2697
2698 static int
2699 InitMail (CT ct)
2700 {
2701 return init_encoding (ct, openMail);
2702 }
2703
2704
2705 static int
2706 openMail (CT ct, char **file)
2707 {
2708 int child_id, fd, i, vecp;
2709 int len, buflen;
2710 char *bp, buffer[BUFSIZ], *vec[7];
2711 struct exbody *e = ct->c_ctexbody;
2712 CE ce = ct->c_cefile;
2713
2714 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2715 case NOTOK:
2716 return NOTOK;
2717
2718 case OK:
2719 break;
2720
2721 case DONE:
2722 return fd;
2723 }
2724
2725 if (!e->eb_server) {
2726 content_error (NULL, ct, "missing server parameter");
2727 return NOTOK;
2728 }
2729
2730 if (xpid) {
2731 if (xpid < 0)
2732 xpid = -xpid;
2733 pidcheck (pidwait (xpid, NOTOK));
2734 xpid = 0;
2735 }
2736
2737 /* Get buffer ready to go */
2738 bp = buffer;
2739 buflen = sizeof(buffer);
2740
2741 /* Now, construct query message */
2742 snprintf (bp, buflen, "Retrieve content");
2743 len = strlen (bp);
2744 bp += len;
2745 buflen -= len;
2746
2747 if (e->eb_partno) {
2748 snprintf (bp, buflen, " %s", e->eb_partno);
2749 len = strlen (bp);
2750 bp += len;
2751 buflen -= len;
2752 }
2753
2754 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2755 e->eb_server,
2756 e->eb_subject ? e->eb_subject : e->eb_body);
2757
2758 /* Now, check answer */
2759 if (!getanswer (buffer))
2760 return NOTOK;
2761
2762 vecp = 0;
2763 vec[vecp++] = r1bindex (mailproc, '/');
2764 vec[vecp++] = e->eb_server;
2765 vec[vecp++] = "-subject";
2766 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2767 vec[vecp++] = "-body";
2768 vec[vecp++] = e->eb_body;
2769 vec[vecp] = NULL;
2770
2771 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2772 sleep (5);
2773 switch (child_id) {
2774 case NOTOK:
2775 advise ("fork", "unable to");
2776 return NOTOK;
2777
2778 case OK:
2779 execvp (mailproc, vec);
2780 fprintf (stderr, "unable to exec ");
2781 perror (mailproc);
2782 _exit (-1);
2783 /* NOTREACHED */
2784
2785 default:
2786 if (pidXwait (child_id, NULL) == OK)
2787 advise (NULL, "request sent");
2788 break;
2789 }
2790
2791 if (*file == NULL) {
2792 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2793 ce->ce_unlink = 1;
2794 } else {
2795 ce->ce_file = add (*file, NULL);
2796 ce->ce_unlink = 0;
2797 }
2798
2799 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2800 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2801 return NOTOK;
2802 }
2803
2804 /* showproc is for mhshow and mhstore, though mhlist -debug
2805 * prints it, too. */
2806 if (ct->c_showproc)
2807 free (ct->c_showproc);
2808 ct->c_showproc = add ("true", NULL);
2809
2810 fseek (ce->ce_fp, 0L, SEEK_SET);
2811 *file = ce->ce_file;
2812 return fileno (ce->ce_fp);
2813 }
2814
2815
2816 static int
2817 readDigest (CT ct, char *cp)
2818 {
2819 int bitno, skip;
2820 uint32_t bits;
2821 char *bp = cp;
2822 unsigned char *dp, value, *ep;
2823
2824 bitno = 18;
2825 bits = 0L;
2826 skip = 0;
2827
2828 for (ep = (dp = ct->c_digest)
2829 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2830 switch (*cp) {
2831 default:
2832 if (skip
2833 || (*cp & 0x80)
2834 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2835 if (debugsw)
2836 fprintf (stderr, "invalid BASE64 encoding\n");
2837 return NOTOK;
2838 }
2839
2840 bits |= value << bitno;
2841 test_end:
2842 if ((bitno -= 6) < 0) {
2843 if (dp + (3 - skip) > ep)
2844 goto invalid_digest;
2845 *dp++ = (bits >> 16) & 0xff;
2846 if (skip < 2) {
2847 *dp++ = (bits >> 8) & 0xff;
2848 if (skip < 1)
2849 *dp++ = bits & 0xff;
2850 }
2851 bitno = 18;
2852 bits = 0L;
2853 skip = 0;
2854 }
2855 break;
2856
2857 case '=':
2858 if (++skip > 3)
2859 goto self_delimiting;
2860 goto test_end;
2861 }
2862 if (bitno != 18) {
2863 if (debugsw)
2864 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2865
2866 return NOTOK;
2867 }
2868 self_delimiting:
2869 if (dp != ep) {
2870 invalid_digest:
2871 if (debugsw) {
2872 while (*cp)
2873 cp++;
2874 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2875 (int)(cp - bp));
2876 }
2877
2878 return NOTOK;
2879 }
2880
2881 if (debugsw) {
2882 fprintf (stderr, "MD5 digest=");
2883 for (dp = ct->c_digest; dp < ep; dp++)
2884 fprintf (stderr, "%02x", *dp & 0xff);
2885 fprintf (stderr, "\n");
2886 }
2887
2888 return OK;
2889 }