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