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