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