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