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