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