]> diplodocus.org Git - nmh/blob - uip/mhparse.c
Sigh. I put the documentation about the -tls switch in the long description,
[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 <setjmp.h>
16 #include <signal.h>
17 #include <h/mts.h>
18 #include <h/tws.h>
19 #include <h/mime.h>
20 #include <h/mhparse.h>
21 #include <h/utils.h>
22
23 #ifdef HAVE_SYS_WAIT_H
24 # include <sys/wait.h>
25 #endif
26
27
28 extern int debugsw;
29
30 extern int endian; /* mhmisc.c */
31
32 extern pid_t xpid; /* mhshowsbr.c */
33
34 /* cache policies */
35 extern int rcachesw; /* mhcachesbr.c */
36 extern int wcachesw; /* mhcachesbr.c */
37
38 int checksw = 0; /* check Content-MD5 field */
39
40 /*
41 * Directory to place temp files. This must
42 * be set before these routines are called.
43 */
44 char *tmp;
45
46 /*
47 * Structures for TEXT messages
48 */
49 struct k2v SubText[] = {
50 { "plain", TEXT_PLAIN },
51 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
52 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
53 { NULL, TEXT_UNKNOWN } /* this one must be last! */
54 };
55
56 struct k2v Charset[] = {
57 { "us-ascii", CHARSET_USASCII },
58 { "iso-8859-1", CHARSET_LATIN },
59 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
60 };
61
62 /*
63 * Structures for MULTIPART messages
64 */
65 struct k2v SubMultiPart[] = {
66 { "mixed", MULTI_MIXED },
67 { "alternative", MULTI_ALTERNATE },
68 { "digest", MULTI_DIGEST },
69 { "parallel", MULTI_PARALLEL },
70 { NULL, MULTI_UNKNOWN } /* this one must be last! */
71 };
72
73 /*
74 * Structures for MESSAGE messages
75 */
76 struct k2v SubMessage[] = {
77 { "rfc822", MESSAGE_RFC822 },
78 { "partial", MESSAGE_PARTIAL },
79 { "external-body", MESSAGE_EXTERNAL },
80 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
81 };
82
83 /*
84 * Structure for APPLICATION messages
85 */
86 struct k2v SubApplication[] = {
87 { "octet-stream", APPLICATION_OCTETS },
88 { "postscript", APPLICATION_POSTSCRIPT },
89 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
90 };
91
92
93 /* ftpsbr.c */
94 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
95
96 /* mhcachesbr.c */
97 int find_cache (CT, int, int *, char *, char *, int);
98
99 /* mhmisc.c */
100 int part_ok (CT, int);
101 int type_ok (CT, int);
102 int make_intermediates (char *);
103 void content_error (char *, CT, char *, ...);
104
105 /* mhfree.c */
106 void free_content (CT);
107 void free_encoding (CT, int);
108
109 /*
110 * static prototypes
111 */
112 static CT get_content (FILE *, char *, int);
113 static int get_comment (CT, unsigned char **, int);
114
115 static int InitGeneric (CT);
116 static int InitText (CT);
117 static int InitMultiPart (CT);
118 static void reverse_parts (CT);
119 static int InitMessage (CT);
120 static int InitApplication (CT);
121 static int init_encoding (CT, OpenCEFunc);
122 static unsigned long size_encoding (CT);
123 static int InitBase64 (CT);
124 static int openBase64 (CT, char **);
125 static int InitQuoted (CT);
126 static int openQuoted (CT, char **);
127 static int Init7Bit (CT);
128 static int openExternal (CT, CT, CE, char **, int *);
129 static int InitFile (CT);
130 static int openFile (CT, char **);
131 static int InitFTP (CT);
132 static int openFTP (CT, char **);
133 static int InitMail (CT);
134 static int openMail (CT, char **);
135 static int readDigest (CT, char *);
136
137 struct str2init str2cts[] = {
138 { "application", CT_APPLICATION, InitApplication },
139 { "audio", CT_AUDIO, InitGeneric },
140 { "image", CT_IMAGE, InitGeneric },
141 { "message", CT_MESSAGE, InitMessage },
142 { "multipart", CT_MULTIPART, InitMultiPart },
143 { "text", CT_TEXT, InitText },
144 { "video", CT_VIDEO, InitGeneric },
145 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
146 { NULL, CT_UNKNOWN, NULL },
147 };
148
149 struct str2init str2ces[] = {
150 { "base64", CE_BASE64, InitBase64 },
151 { "quoted-printable", CE_QUOTED, InitQuoted },
152 { "8bit", CE_8BIT, Init7Bit },
153 { "7bit", CE_7BIT, Init7Bit },
154 { "binary", CE_BINARY, Init7Bit },
155 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
156 { NULL, CE_UNKNOWN, NULL },
157 };
158
159 /*
160 * NOTE WELL: si_key MUST NOT have value of NOTOK
161 *
162 * si_key is 1 if access method is anonymous.
163 */
164 struct str2init str2methods[] = {
165 { "afs", 1, InitFile },
166 { "anon-ftp", 1, InitFTP },
167 { "ftp", 0, InitFTP },
168 { "local-file", 0, InitFile },
169 { "mail-server", 0, InitMail },
170 { NULL, 0, NULL }
171 };
172
173
174 int
175 pidcheck (int status)
176 {
177 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
178 return status;
179
180 fflush (stdout);
181 fflush (stderr);
182 done (1);
183 return 1;
184 }
185
186
187 /*
188 * Main entry point for parsing a MIME message or file.
189 * It returns the Content structure for the top level
190 * entity in the file.
191 */
192
193 CT
194 parse_mime (char *file)
195 {
196 int is_stdin;
197 char buffer[BUFSIZ];
198 FILE *fp;
199 CT ct;
200
201 /*
202 * Check if file is actually standard input
203 */
204 if ((is_stdin = !(strcmp (file, "-")))) {
205 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
206 if (tfile == NULL) {
207 advise("mhparse", "unable to create temporary file");
208 return NULL;
209 }
210 file = add (tfile, NULL);
211 chmod (file, 0600);
212
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_mktemp(tmp, NULL, NULL), 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 if (ce->ce_unlink) {
1786 // Temporary file already exists, so we rename to
1787 // version with extension.
1788 char *file_org = strdup(ce->ce_file);
1789 ce->ce_file = add (cp, ce->ce_file);
1790 if (rename(file_org, ce->ce_file)) {
1791 adios (ce->ce_file, "unable to rename %s to ", file_org);
1792 }
1793 free(file_org);
1794
1795 } else {
1796 ce->ce_file = add (cp, ce->ce_file);
1797 }
1798 }
1799
1800 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1801 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1802 return NOTOK;
1803 }
1804
1805 if ((len = ct->c_end - ct->c_begin) < 0)
1806 adios (NULL, "internal error(1)");
1807
1808 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1809 content_error (ct->c_file, ct, "unable to open for reading");
1810 return NOTOK;
1811 }
1812
1813 if ((digested = ct->c_digested))
1814 MD5Init (&mdContext);
1815
1816 bitno = 18;
1817 bits = 0L;
1818 skip = 0;
1819
1820 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1821 while (len > 0) {
1822 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1823 case NOTOK:
1824 content_error (ct->c_file, ct, "error reading from");
1825 goto clean_up;
1826
1827 case OK:
1828 content_error (NULL, ct, "premature eof");
1829 goto clean_up;
1830
1831 default:
1832 if (cc > len)
1833 cc = len;
1834 len -= cc;
1835
1836 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1837 switch (*cp) {
1838 default:
1839 if (isspace (*cp))
1840 break;
1841 if (skip || (*cp & 0x80)
1842 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1843 if (debugsw) {
1844 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1845 *cp,
1846 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1847 skip);
1848 }
1849 content_error (NULL, ct,
1850 "invalid BASE64 encoding -- continuing");
1851 continue;
1852 }
1853
1854 bits |= value << bitno;
1855 test_end:
1856 if ((bitno -= 6) < 0) {
1857 putc ((char) *b1, ce->ce_fp);
1858 if (digested)
1859 MD5Update (&mdContext, b1, 1);
1860 if (skip < 2) {
1861 putc ((char) *b2, ce->ce_fp);
1862 if (digested)
1863 MD5Update (&mdContext, b2, 1);
1864 if (skip < 1) {
1865 putc ((char) *b3, ce->ce_fp);
1866 if (digested)
1867 MD5Update (&mdContext, b3, 1);
1868 }
1869 }
1870
1871 if (ferror (ce->ce_fp)) {
1872 content_error (ce->ce_file, ct,
1873 "error writing to");
1874 goto clean_up;
1875 }
1876 bitno = 18, bits = 0L, skip = 0;
1877 }
1878 break;
1879
1880 case '=':
1881 if (++skip > 3)
1882 goto self_delimiting;
1883 goto test_end;
1884 }
1885 }
1886 }
1887 }
1888
1889 if (bitno != 18) {
1890 if (debugsw)
1891 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1892
1893 content_error (NULL, ct, "invalid BASE64 encoding");
1894 goto clean_up;
1895 }
1896
1897 self_delimiting:
1898 fseek (ct->c_fp, 0L, SEEK_SET);
1899
1900 if (fflush (ce->ce_fp)) {
1901 content_error (ce->ce_file, ct, "error writing to");
1902 goto clean_up;
1903 }
1904
1905 if (digested) {
1906 unsigned char digest[16];
1907
1908 MD5Final (digest, &mdContext);
1909 if (memcmp((char *) digest, (char *) ct->c_digest,
1910 sizeof(digest) / sizeof(digest[0])))
1911 content_error (NULL, ct,
1912 "content integrity suspect (digest mismatch) -- continuing");
1913 else
1914 if (debugsw)
1915 fprintf (stderr, "content integrity confirmed\n");
1916 }
1917
1918 fseek (ce->ce_fp, 0L, SEEK_SET);
1919
1920 ready_to_go:
1921 *file = ce->ce_file;
1922 return fileno (ce->ce_fp);
1923
1924 clean_up:
1925 free_encoding (ct, 0);
1926 return NOTOK;
1927 }
1928
1929
1930 /*
1931 * QUOTED PRINTABLE
1932 */
1933
1934 static char hex2nib[0x80] = {
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1942 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1951 };
1952
1953
1954 static int
1955 InitQuoted (CT ct)
1956 {
1957 return init_encoding (ct, openQuoted);
1958 }
1959
1960
1961 static int
1962 openQuoted (CT ct, char **file)
1963 {
1964 int cc, digested, len, quoted;
1965 unsigned char *cp, *ep;
1966 char buffer[BUFSIZ];
1967 unsigned char mask;
1968 CE ce;
1969 /* sbeck -- handle suffixes */
1970 CI ci;
1971 MD5_CTX mdContext;
1972
1973 ce = ct->c_cefile;
1974 if (ce->ce_fp) {
1975 fseek (ce->ce_fp, 0L, SEEK_SET);
1976 goto ready_to_go;
1977 }
1978
1979 if (ce->ce_file) {
1980 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1981 content_error (ce->ce_file, ct, "unable to fopen for reading");
1982 return NOTOK;
1983 }
1984 goto ready_to_go;
1985 }
1986
1987 if (*file == NULL) {
1988 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1989 ce->ce_unlink = 1;
1990 } else {
1991 ce->ce_file = add (*file, NULL);
1992 ce->ce_unlink = 0;
1993 }
1994
1995 /* sbeck@cise.ufl.edu -- handle suffixes */
1996 ci = &ct->c_ctinfo;
1997 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1998 invo_name, ci->ci_type, ci->ci_subtype);
1999 cp = context_find (buffer);
2000 if (cp == NULL || *cp == '\0') {
2001 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2002 ci->ci_type);
2003 cp = context_find (buffer);
2004 }
2005 if (cp != NULL && *cp != '\0') {
2006 if (ce->ce_unlink) {
2007 // Temporary file already exists, so we rename to
2008 // version with extension.
2009 char *file_org = strdup(ce->ce_file);
2010 ce->ce_file = add (cp, ce->ce_file);
2011 if (rename(file_org, ce->ce_file)) {
2012 adios (ce->ce_file, "unable to rename %s to ", file_org);
2013 }
2014 free(file_org);
2015
2016 } else {
2017 ce->ce_file = add (cp, ce->ce_file);
2018 }
2019 }
2020
2021 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2022 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2023 return NOTOK;
2024 }
2025
2026 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2027 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2028 return NOTOK;
2029 }
2030
2031 if ((len = ct->c_end - ct->c_begin) < 0)
2032 adios (NULL, "internal error(2)");
2033
2034 if (!ct->c_fp && (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
2039 if ((digested = ct->c_digested))
2040 MD5Init (&mdContext);
2041
2042 quoted = 0;
2043 #ifdef lint
2044 mask = 0;
2045 #endif
2046
2047 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2048 while (len > 0) {
2049 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2050 content_error (NULL, ct, "premature eof");
2051 goto clean_up;
2052 }
2053
2054 if ((cc = strlen (buffer)) > len)
2055 cc = len;
2056 len -= cc;
2057
2058 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2059 if (!isspace (*ep))
2060 break;
2061 *++ep = '\n', ep++;
2062
2063 for (; cp < ep; cp++) {
2064 if (quoted > 0) {
2065 /* in an escape sequence */
2066 if (quoted == 1) {
2067 /* at byte 1 of an escape sequence */
2068 mask = hex2nib[*cp & 0x7f];
2069 /* next is byte 2 */
2070 quoted = 2;
2071 } else {
2072 /* at byte 2 of an escape sequence */
2073 mask <<= 4;
2074 mask |= hex2nib[*cp & 0x7f];
2075 putc (mask, ce->ce_fp);
2076 if (digested)
2077 MD5Update (&mdContext, &mask, 1);
2078 if (ferror (ce->ce_fp)) {
2079 content_error (ce->ce_file, ct, "error writing to");
2080 goto clean_up;
2081 }
2082 /* finished escape sequence; next may be literal or a new
2083 * escape sequence */
2084 quoted = 0;
2085 }
2086 /* on to next byte */
2087 continue;
2088 }
2089
2090 /* not in an escape sequence */
2091 if (*cp == '=') {
2092 /* starting an escape sequence, or invalid '='? */
2093 if (cp + 1 < ep && cp[1] == '\n') {
2094 /* "=\n" soft line break, eat the \n */
2095 cp++;
2096 continue;
2097 }
2098 if (cp + 1 >= ep || cp + 2 >= ep) {
2099 /* We don't have 2 bytes left, so this is an invalid
2100 * escape sequence; just show the raw bytes (below). */
2101 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2102 /* Next 2 bytes are hex digits, making this a valid escape
2103 * sequence; let's decode it (above). */
2104 quoted = 1;
2105 continue;
2106 } else {
2107 /* One or both of the next 2 is out of range, making this
2108 * an invalid escape sequence; just show the raw bytes
2109 * (below). */
2110 }
2111 }
2112
2113 /* Just show the raw byte. */
2114 putc (*cp, ce->ce_fp);
2115 if (digested) {
2116 if (*cp == '\n') {
2117 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2118 } else {
2119 MD5Update (&mdContext, (unsigned char *) cp, 1);
2120 }
2121 }
2122 if (ferror (ce->ce_fp)) {
2123 content_error (ce->ce_file, ct, "error writing to");
2124 goto clean_up;
2125 }
2126 }
2127 }
2128 if (quoted) {
2129 content_error (NULL, ct,
2130 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2131 goto clean_up;
2132 }
2133
2134 fseek (ct->c_fp, 0L, SEEK_SET);
2135
2136 if (fflush (ce->ce_fp)) {
2137 content_error (ce->ce_file, ct, "error writing to");
2138 goto clean_up;
2139 }
2140
2141 if (digested) {
2142 unsigned char digest[16];
2143
2144 MD5Final (digest, &mdContext);
2145 if (memcmp((char *) digest, (char *) ct->c_digest,
2146 sizeof(digest) / sizeof(digest[0])))
2147 content_error (NULL, ct,
2148 "content integrity suspect (digest mismatch) -- continuing");
2149 else
2150 if (debugsw)
2151 fprintf (stderr, "content integrity confirmed\n");
2152 }
2153
2154 fseek (ce->ce_fp, 0L, SEEK_SET);
2155
2156 ready_to_go:
2157 *file = ce->ce_file;
2158 return fileno (ce->ce_fp);
2159
2160 clean_up:
2161 free_encoding (ct, 0);
2162 return NOTOK;
2163 }
2164
2165
2166 /*
2167 * 7BIT
2168 */
2169
2170 static int
2171 Init7Bit (CT ct)
2172 {
2173 if (init_encoding (ct, open7Bit) == NOTOK)
2174 return NOTOK;
2175
2176 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2177 return OK;
2178 }
2179
2180
2181 int
2182 open7Bit (CT ct, char **file)
2183 {
2184 int cc, fd, len;
2185 char buffer[BUFSIZ];
2186 /* sbeck -- handle suffixes */
2187 char *cp;
2188 CI ci;
2189 CE ce;
2190
2191 ce = ct->c_cefile;
2192 if (ce->ce_fp) {
2193 fseek (ce->ce_fp, 0L, SEEK_SET);
2194 goto ready_to_go;
2195 }
2196
2197 if (ce->ce_file) {
2198 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2199 content_error (ce->ce_file, ct, "unable to fopen for reading");
2200 return NOTOK;
2201 }
2202 goto ready_to_go;
2203 }
2204
2205 if (*file == NULL) {
2206 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2207 ce->ce_unlink = 1;
2208 } else {
2209 ce->ce_file = add (*file, NULL);
2210 ce->ce_unlink = 0;
2211 }
2212
2213 /* sbeck@cise.ufl.edu -- handle suffixes */
2214 ci = &ct->c_ctinfo;
2215 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2216 invo_name, ci->ci_type, ci->ci_subtype);
2217 cp = context_find (buffer);
2218 if (cp == NULL || *cp == '\0') {
2219 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2220 ci->ci_type);
2221 cp = context_find (buffer);
2222 }
2223 if (cp != NULL && *cp != '\0') {
2224 if (ce->ce_unlink) {
2225 // Temporary file already exists, so we rename to
2226 // version with extension.
2227 char *file_org = strdup(ce->ce_file);
2228 ce->ce_file = add (cp, ce->ce_file);
2229 if (rename(file_org, ce->ce_file)) {
2230 adios (ce->ce_file, "unable to rename %s to ", file_org);
2231 }
2232 free(file_org);
2233
2234 } else {
2235 ce->ce_file = add (cp, ce->ce_file);
2236 }
2237 }
2238
2239 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2240 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2241 return NOTOK;
2242 }
2243
2244 if (ct->c_type == CT_MULTIPART) {
2245 char **ap, **ep;
2246 CI ci = &ct->c_ctinfo;
2247
2248 len = 0;
2249 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2250 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2251 + 1 + strlen (ci->ci_subtype);
2252 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2253 putc (';', ce->ce_fp);
2254 len++;
2255
2256 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2257
2258 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2259 fputs ("\n\t", ce->ce_fp);
2260 len = 8;
2261 } else {
2262 putc (' ', ce->ce_fp);
2263 len++;
2264 }
2265 fprintf (ce->ce_fp, "%s", buffer);
2266 len += cc;
2267 }
2268
2269 if (ci->ci_comment) {
2270 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2271 fputs ("\n\t", ce->ce_fp);
2272 len = 8;
2273 }
2274 else {
2275 putc (' ', ce->ce_fp);
2276 len++;
2277 }
2278 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2279 len += cc;
2280 }
2281 fprintf (ce->ce_fp, "\n");
2282 if (ct->c_id)
2283 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2284 if (ct->c_descr)
2285 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2286 if (ct->c_dispo)
2287 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2288 fprintf (ce->ce_fp, "\n");
2289 }
2290
2291 if ((len = ct->c_end - ct->c_begin) < 0)
2292 adios (NULL, "internal error(3)");
2293
2294 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2295 content_error (ct->c_file, ct, "unable to open for reading");
2296 return NOTOK;
2297 }
2298
2299 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2300 while (len > 0)
2301 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2302 case NOTOK:
2303 content_error (ct->c_file, ct, "error reading from");
2304 goto clean_up;
2305
2306 case OK:
2307 content_error (NULL, ct, "premature eof");
2308 goto clean_up;
2309
2310 default:
2311 if (cc > len)
2312 cc = len;
2313 len -= cc;
2314
2315 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2316 if (ferror (ce->ce_fp)) {
2317 content_error (ce->ce_file, ct, "error writing to");
2318 goto clean_up;
2319 }
2320 }
2321
2322 fseek (ct->c_fp, 0L, SEEK_SET);
2323
2324 if (fflush (ce->ce_fp)) {
2325 content_error (ce->ce_file, ct, "error writing to");
2326 goto clean_up;
2327 }
2328
2329 fseek (ce->ce_fp, 0L, SEEK_SET);
2330
2331 ready_to_go:
2332 *file = ce->ce_file;
2333 return fileno (ce->ce_fp);
2334
2335 clean_up:
2336 free_encoding (ct, 0);
2337 return NOTOK;
2338 }
2339
2340
2341 /*
2342 * External
2343 */
2344
2345 static int
2346 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2347 {
2348 char cachefile[BUFSIZ];
2349
2350 if (ce->ce_fp) {
2351 fseek (ce->ce_fp, 0L, SEEK_SET);
2352 goto ready_already;
2353 }
2354
2355 if (ce->ce_file) {
2356 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2357 content_error (ce->ce_file, ct, "unable to fopen for reading");
2358 return NOTOK;
2359 }
2360 goto ready_already;
2361 }
2362
2363 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2364 cachefile, sizeof(cachefile)) != NOTOK) {
2365 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2366 ce->ce_file = getcpy (cachefile);
2367 ce->ce_unlink = 0;
2368 goto ready_already;
2369 } else {
2370 admonish (cachefile, "unable to fopen for reading");
2371 }
2372 }
2373
2374 return OK;
2375
2376 ready_already:
2377 *file = ce->ce_file;
2378 *fd = fileno (ce->ce_fp);
2379 return DONE;
2380 }
2381
2382 /*
2383 * File
2384 */
2385
2386 static int
2387 InitFile (CT ct)
2388 {
2389 return init_encoding (ct, openFile);
2390 }
2391
2392
2393 static int
2394 openFile (CT ct, char **file)
2395 {
2396 int fd, cachetype;
2397 char cachefile[BUFSIZ];
2398 struct exbody *e = ct->c_ctexbody;
2399 CE ce = ct->c_cefile;
2400
2401 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2402 case NOTOK:
2403 return NOTOK;
2404
2405 case OK:
2406 break;
2407
2408 case DONE:
2409 return fd;
2410 }
2411
2412 if (!e->eb_name) {
2413 content_error (NULL, ct, "missing name parameter");
2414 return NOTOK;
2415 }
2416
2417 ce->ce_file = getcpy (e->eb_name);
2418 ce->ce_unlink = 0;
2419
2420 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2421 content_error (ce->ce_file, ct, "unable to fopen for reading");
2422 return NOTOK;
2423 }
2424
2425 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2426 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2427 cachefile, sizeof(cachefile)) != NOTOK) {
2428 int mask;
2429 FILE *fp;
2430
2431 mask = umask (cachetype ? ~m_gmprot () : 0222);
2432 if ((fp = fopen (cachefile, "w"))) {
2433 int cc;
2434 char buffer[BUFSIZ];
2435 FILE *gp = ce->ce_fp;
2436
2437 fseek (gp, 0L, SEEK_SET);
2438
2439 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2440 > 0)
2441 fwrite (buffer, sizeof(*buffer), cc, fp);
2442 fflush (fp);
2443
2444 if (ferror (gp)) {
2445 admonish (ce->ce_file, "error reading");
2446 unlink (cachefile);
2447 }
2448 else
2449 if (ferror (fp)) {
2450 admonish (cachefile, "error writing");
2451 unlink (cachefile);
2452 }
2453 fclose (fp);
2454 }
2455 umask (mask);
2456 }
2457
2458 fseek (ce->ce_fp, 0L, SEEK_SET);
2459 *file = ce->ce_file;
2460 return fileno (ce->ce_fp);
2461 }
2462
2463 /*
2464 * FTP
2465 */
2466
2467 static int
2468 InitFTP (CT ct)
2469 {
2470 return init_encoding (ct, openFTP);
2471 }
2472
2473
2474 static int
2475 openFTP (CT ct, char **file)
2476 {
2477 int cachetype, caching, fd;
2478 int len, buflen;
2479 char *bp, *ftp, *user, *pass;
2480 char buffer[BUFSIZ], cachefile[BUFSIZ];
2481 struct exbody *e;
2482 CE ce;
2483 static char *username = NULL;
2484 static char *password = NULL;
2485
2486 e = ct->c_ctexbody;
2487 ce = ct->c_cefile;
2488
2489 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2490 ftp = NULL;
2491
2492 #ifndef BUILTIN_FTP
2493 if (!ftp)
2494 return NOTOK;
2495 #endif
2496
2497 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2498 case NOTOK:
2499 return NOTOK;
2500
2501 case OK:
2502 break;
2503
2504 case DONE:
2505 return fd;
2506 }
2507
2508 if (!e->eb_name || !e->eb_site) {
2509 content_error (NULL, ct, "missing %s parameter",
2510 e->eb_name ? "site": "name");
2511 return NOTOK;
2512 }
2513
2514 if (xpid) {
2515 if (xpid < 0)
2516 xpid = -xpid;
2517 pidcheck (pidwait (xpid, NOTOK));
2518 xpid = 0;
2519 }
2520
2521 /* Get the buffer ready to go */
2522 bp = buffer;
2523 buflen = sizeof(buffer);
2524
2525 /*
2526 * Construct the query message for user
2527 */
2528 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2529 len = strlen (bp);
2530 bp += len;
2531 buflen -= len;
2532
2533 if (e->eb_partno) {
2534 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2535 len = strlen (bp);
2536 bp += len;
2537 buflen -= len;
2538 }
2539
2540 snprintf (bp, buflen, "\n using %sFTP from site %s",
2541 e->eb_flags ? "anonymous " : "", e->eb_site);
2542 len = strlen (bp);
2543 bp += len;
2544 buflen -= len;
2545
2546 if (e->eb_size > 0) {
2547 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2548 len = strlen (bp);
2549 bp += len;
2550 buflen -= len;
2551 }
2552 snprintf (bp, buflen, "? ");
2553
2554 /*
2555 * Now, check the answer
2556 */
2557 if (!getanswer (buffer))
2558 return NOTOK;
2559
2560 if (e->eb_flags) {
2561 user = "anonymous";
2562 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2563 pass = buffer;
2564 } else {
2565 ruserpass (e->eb_site, &username, &password);
2566 user = username;
2567 pass = password;
2568 }
2569
2570 ce->ce_unlink = (*file == NULL);
2571 caching = 0;
2572 cachefile[0] = '\0';
2573 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2574 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2575 cachefile, sizeof(cachefile)) != NOTOK) {
2576 if (*file == NULL) {
2577 ce->ce_unlink = 0;
2578 caching = 1;
2579 }
2580 }
2581
2582 if (*file)
2583 ce->ce_file = add (*file, NULL);
2584 else if (caching)
2585 ce->ce_file = add (cachefile, NULL);
2586 else
2587 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2588
2589 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2590 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2591 return NOTOK;
2592 }
2593
2594 #ifdef BUILTIN_FTP
2595 if (ftp)
2596 #endif
2597 {
2598 int child_id, i, vecp;
2599 char *vec[9];
2600
2601 vecp = 0;
2602 vec[vecp++] = r1bindex (ftp, '/');
2603 vec[vecp++] = e->eb_site;
2604 vec[vecp++] = user;
2605 vec[vecp++] = pass;
2606 vec[vecp++] = e->eb_dir;
2607 vec[vecp++] = e->eb_name;
2608 vec[vecp++] = ce->ce_file,
2609 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2610 ? "ascii" : "binary";
2611 vec[vecp] = NULL;
2612
2613 fflush (stdout);
2614
2615 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2616 sleep (5);
2617 switch (child_id) {
2618 case NOTOK:
2619 adios ("fork", "unable to");
2620 /* NOTREACHED */
2621
2622 case OK:
2623 close (fileno (ce->ce_fp));
2624 execvp (ftp, vec);
2625 fprintf (stderr, "unable to exec ");
2626 perror (ftp);
2627 _exit (-1);
2628 /* NOTREACHED */
2629
2630 default:
2631 if (pidXwait (child_id, NULL)) {
2632 #ifdef BUILTIN_FTP
2633 losing_ftp:
2634 #endif
2635 username = password = NULL;
2636 ce->ce_unlink = 1;
2637 return NOTOK;
2638 }
2639 break;
2640 }
2641 }
2642 #ifdef BUILTIN_FTP
2643 else
2644 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2645 ce->ce_file,
2646 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2647 == NOTOK)
2648 goto losing_ftp;
2649 #endif
2650
2651 if (cachefile[0]) {
2652 if (caching)
2653 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2654 else {
2655 int mask;
2656 FILE *fp;
2657
2658 mask = umask (cachetype ? ~m_gmprot () : 0222);
2659 if ((fp = fopen (cachefile, "w"))) {
2660 int cc;
2661 FILE *gp = ce->ce_fp;
2662
2663 fseek (gp, 0L, SEEK_SET);
2664
2665 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2666 > 0)
2667 fwrite (buffer, sizeof(*buffer), cc, fp);
2668 fflush (fp);
2669
2670 if (ferror (gp)) {
2671 admonish (ce->ce_file, "error reading");
2672 unlink (cachefile);
2673 }
2674 else
2675 if (ferror (fp)) {
2676 admonish (cachefile, "error writing");
2677 unlink (cachefile);
2678 }
2679 fclose (fp);
2680 }
2681 umask (mask);
2682 }
2683 }
2684
2685 fseek (ce->ce_fp, 0L, SEEK_SET);
2686 *file = ce->ce_file;
2687 return fileno (ce->ce_fp);
2688 }
2689
2690
2691 /*
2692 * Mail
2693 */
2694
2695 static int
2696 InitMail (CT ct)
2697 {
2698 return init_encoding (ct, openMail);
2699 }
2700
2701
2702 static int
2703 openMail (CT ct, char **file)
2704 {
2705 int child_id, fd, i, vecp;
2706 int len, buflen;
2707 char *bp, buffer[BUFSIZ], *vec[7];
2708 struct exbody *e = ct->c_ctexbody;
2709 CE ce = ct->c_cefile;
2710
2711 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2712 case NOTOK:
2713 return NOTOK;
2714
2715 case OK:
2716 break;
2717
2718 case DONE:
2719 return fd;
2720 }
2721
2722 if (!e->eb_server) {
2723 content_error (NULL, ct, "missing server parameter");
2724 return NOTOK;
2725 }
2726
2727 if (xpid) {
2728 if (xpid < 0)
2729 xpid = -xpid;
2730 pidcheck (pidwait (xpid, NOTOK));
2731 xpid = 0;
2732 }
2733
2734 /* Get buffer ready to go */
2735 bp = buffer;
2736 buflen = sizeof(buffer);
2737
2738 /* Now, construct query message */
2739 snprintf (bp, buflen, "Retrieve content");
2740 len = strlen (bp);
2741 bp += len;
2742 buflen -= len;
2743
2744 if (e->eb_partno) {
2745 snprintf (bp, buflen, " %s", e->eb_partno);
2746 len = strlen (bp);
2747 bp += len;
2748 buflen -= len;
2749 }
2750
2751 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2752 e->eb_server,
2753 e->eb_subject ? e->eb_subject : e->eb_body);
2754
2755 /* Now, check answer */
2756 if (!getanswer (buffer))
2757 return NOTOK;
2758
2759 vecp = 0;
2760 vec[vecp++] = r1bindex (mailproc, '/');
2761 vec[vecp++] = e->eb_server;
2762 vec[vecp++] = "-subject";
2763 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2764 vec[vecp++] = "-body";
2765 vec[vecp++] = e->eb_body;
2766 vec[vecp] = NULL;
2767
2768 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2769 sleep (5);
2770 switch (child_id) {
2771 case NOTOK:
2772 advise ("fork", "unable to");
2773 return NOTOK;
2774
2775 case OK:
2776 execvp (mailproc, vec);
2777 fprintf (stderr, "unable to exec ");
2778 perror (mailproc);
2779 _exit (-1);
2780 /* NOTREACHED */
2781
2782 default:
2783 if (pidXwait (child_id, NULL) == OK)
2784 advise (NULL, "request sent");
2785 break;
2786 }
2787
2788 if (*file == NULL) {
2789 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2790 ce->ce_unlink = 1;
2791 } else {
2792 ce->ce_file = add (*file, NULL);
2793 ce->ce_unlink = 0;
2794 }
2795
2796 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2797 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2798 return NOTOK;
2799 }
2800
2801 /* showproc is for mhshow and mhstore, though mhlist -debug
2802 * prints it, too. */
2803 if (ct->c_showproc)
2804 free (ct->c_showproc);
2805 ct->c_showproc = add ("true", NULL);
2806
2807 fseek (ce->ce_fp, 0L, SEEK_SET);
2808 *file = ce->ce_file;
2809 return fileno (ce->ce_fp);
2810 }
2811
2812
2813 static int
2814 readDigest (CT ct, char *cp)
2815 {
2816 int bitno, skip;
2817 unsigned long bits;
2818 char *bp = cp;
2819 unsigned char *dp, value, *ep;
2820 unsigned char *b, *b1, *b2, *b3;
2821
2822 b = (unsigned char *) &bits,
2823 b1 = &b[endian > 0 ? 1 : 2],
2824 b2 = &b[endian > 0 ? 2 : 1],
2825 b3 = &b[endian > 0 ? 3 : 0];
2826 bitno = 18;
2827 bits = 0L;
2828 skip = 0;
2829
2830 for (ep = (dp = ct->c_digest)
2831 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2832 switch (*cp) {
2833 default:
2834 if (skip
2835 || (*cp & 0x80)
2836 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2837 if (debugsw)
2838 fprintf (stderr, "invalid BASE64 encoding\n");
2839 return NOTOK;
2840 }
2841
2842 bits |= value << bitno;
2843 test_end:
2844 if ((bitno -= 6) < 0) {
2845 if (dp + (3 - skip) > ep)
2846 goto invalid_digest;
2847 *dp++ = *b1;
2848 if (skip < 2) {
2849 *dp++ = *b2;
2850 if (skip < 1)
2851 *dp++ = *b3;
2852 }
2853 bitno = 18;
2854 bits = 0L;
2855 skip = 0;
2856 }
2857 break;
2858
2859 case '=':
2860 if (++skip > 3)
2861 goto self_delimiting;
2862 goto test_end;
2863 }
2864 if (bitno != 18) {
2865 if (debugsw)
2866 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2867
2868 return NOTOK;
2869 }
2870 self_delimiting:
2871 if (dp != ep) {
2872 invalid_digest:
2873 if (debugsw) {
2874 while (*cp)
2875 cp++;
2876 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2877 (int)(cp - bp));
2878 }
2879
2880 return NOTOK;
2881 }
2882
2883 if (debugsw) {
2884 fprintf (stderr, "MD5 digest=");
2885 for (dp = ct->c_digest; dp < ep; dp++)
2886 fprintf (stderr, "%02x", *dp & 0xff);
2887 fprintf (stderr, "\n");
2888 }
2889
2890 return OK;
2891 }