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