]> diplodocus.org Git - nmh/blob - uip/mhparse.c
* I had alphabetized the --configure options in the --help output
[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 /* sbeck -- handle prefixes */
1572 CI ci;
1573 CE ce;
1574 MD5_CTX mdContext;
1575
1576 b = (unsigned char *) &bits;
1577 b1 = &b[endian > 0 ? 1 : 2];
1578 b2 = &b[endian > 0 ? 2 : 1];
1579 b3 = &b[endian > 0 ? 3 : 0];
1580
1581 ce = ct->c_cefile;
1582 if (ce->ce_fp) {
1583 fseek (ce->ce_fp, 0L, SEEK_SET);
1584 goto ready_to_go;
1585 }
1586
1587 if (ce->ce_file) {
1588 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1589 content_error (ce->ce_file, ct, "unable to fopen for reading");
1590 return NOTOK;
1591 }
1592 goto ready_to_go;
1593 }
1594
1595 if (*file == NULL) {
1596 ce->ce_file = add (m_scratch ("", tmp), NULL);
1597 ce->ce_unlink = 1;
1598 } else {
1599 ce->ce_file = add (*file, NULL);
1600 ce->ce_unlink = 0;
1601 }
1602
1603 /* sbeck@cise.ufl.edu -- handle suffixes */
1604 ci = &ct->c_ctinfo;
1605 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1606 invo_name, ci->ci_type, ci->ci_subtype);
1607 cp = context_find (buffer);
1608 if (cp == NULL || *cp == '\0') {
1609 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1610 ci->ci_type);
1611 cp = context_find (buffer);
1612 }
1613 if (cp != NULL && *cp != '\0')
1614 ce->ce_file = add (cp, ce->ce_file);
1615
1616 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1617 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1618 return NOTOK;
1619 }
1620
1621 if ((len = ct->c_end - ct->c_begin) < 0)
1622 adios (NULL, "internal error(1)");
1623
1624 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1625 content_error (ct->c_file, ct, "unable to open for reading");
1626 return NOTOK;
1627 }
1628
1629 if ((digested = ct->c_digested))
1630 MD5Init (&mdContext);
1631
1632 bitno = 18;
1633 bits = 0L;
1634 skip = 0;
1635
1636 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1637 while (len > 0) {
1638 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1639 case NOTOK:
1640 content_error (ct->c_file, ct, "error reading from");
1641 goto clean_up;
1642
1643 case OK:
1644 content_error (NULL, ct, "premature eof");
1645 goto clean_up;
1646
1647 default:
1648 if (cc > len)
1649 cc = len;
1650 len -= cc;
1651
1652 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1653 switch (*cp) {
1654 default:
1655 if (isspace (*cp))
1656 break;
1657 if (skip || (*cp & 0x80)
1658 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1659 if (debugsw) {
1660 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1661 *cp,
1662 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1663 skip);
1664 }
1665 content_error (NULL, ct,
1666 "invalid BASE64 encoding -- continuing");
1667 continue;
1668 }
1669
1670 bits |= value << bitno;
1671 test_end:
1672 if ((bitno -= 6) < 0) {
1673 putc ((char) *b1, ce->ce_fp);
1674 if (digested)
1675 MD5Update (&mdContext, b1, 1);
1676 if (skip < 2) {
1677 putc ((char) *b2, ce->ce_fp);
1678 if (digested)
1679 MD5Update (&mdContext, b2, 1);
1680 if (skip < 1) {
1681 putc ((char) *b3, ce->ce_fp);
1682 if (digested)
1683 MD5Update (&mdContext, b3, 1);
1684 }
1685 }
1686
1687 if (ferror (ce->ce_fp)) {
1688 content_error (ce->ce_file, ct,
1689 "error writing to");
1690 goto clean_up;
1691 }
1692 bitno = 18, bits = 0L, skip = 0;
1693 }
1694 break;
1695
1696 case '=':
1697 if (++skip > 3)
1698 goto self_delimiting;
1699 goto test_end;
1700 }
1701 }
1702 }
1703 }
1704
1705 if (bitno != 18) {
1706 if (debugsw)
1707 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1708
1709 content_error (NULL, ct, "invalid BASE64 encoding");
1710 goto clean_up;
1711 }
1712
1713 self_delimiting:
1714 fseek (ct->c_fp, 0L, SEEK_SET);
1715
1716 if (fflush (ce->ce_fp)) {
1717 content_error (ce->ce_file, ct, "error writing to");
1718 goto clean_up;
1719 }
1720
1721 if (digested) {
1722 unsigned char digest[16];
1723
1724 MD5Final (digest, &mdContext);
1725 if (memcmp((char *) digest, (char *) ct->c_digest,
1726 sizeof(digest) / sizeof(digest[0])))
1727 content_error (NULL, ct,
1728 "content integrity suspect (digest mismatch) -- continuing");
1729 else
1730 if (debugsw)
1731 fprintf (stderr, "content integrity confirmed\n");
1732 }
1733
1734 fseek (ce->ce_fp, 0L, SEEK_SET);
1735
1736 ready_to_go:
1737 *file = ce->ce_file;
1738 return fileno (ce->ce_fp);
1739
1740 clean_up:
1741 free_encoding (ct, 0);
1742 return NOTOK;
1743 }
1744
1745
1746 /*
1747 * QUOTED PRINTABLE
1748 */
1749
1750 static char hex2nib[0x80] = {
1751 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1752 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1753 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1754 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1755 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1756 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1757 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1758 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1759 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1760 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1761 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1762 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1763 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1767 };
1768
1769
1770 static int
1771 InitQuoted (CT ct)
1772 {
1773 return init_encoding (ct, openQuoted);
1774 }
1775
1776
1777 static int
1778 openQuoted (CT ct, char **file)
1779 {
1780 int cc, digested, len, quoted;
1781 char *cp, *ep;
1782 char buffer[BUFSIZ];
1783 unsigned char mask;
1784 CE ce;
1785 /* sbeck -- handle prefixes */
1786 CI ci;
1787 MD5_CTX mdContext;
1788
1789 ce = ct->c_cefile;
1790 if (ce->ce_fp) {
1791 fseek (ce->ce_fp, 0L, SEEK_SET);
1792 goto ready_to_go;
1793 }
1794
1795 if (ce->ce_file) {
1796 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1797 content_error (ce->ce_file, ct, "unable to fopen for reading");
1798 return NOTOK;
1799 }
1800 goto ready_to_go;
1801 }
1802
1803 if (*file == NULL) {
1804 ce->ce_file = add (m_scratch ("", tmp), NULL);
1805 ce->ce_unlink = 1;
1806 } else {
1807 ce->ce_file = add (*file, NULL);
1808 ce->ce_unlink = 0;
1809 }
1810
1811 /* sbeck@cise.ufl.edu -- handle suffixes */
1812 ci = &ct->c_ctinfo;
1813 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1814 invo_name, ci->ci_type, ci->ci_subtype);
1815 cp = context_find (buffer);
1816 if (cp == NULL || *cp == '\0') {
1817 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1818 ci->ci_type);
1819 cp = context_find (buffer);
1820 }
1821 if (cp != NULL && *cp != '\0')
1822 ce->ce_file = add (cp, ce->ce_file);
1823
1824 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1825 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1826 return NOTOK;
1827 }
1828
1829 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1830 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1831 return NOTOK;
1832 }
1833
1834 if ((len = ct->c_end - ct->c_begin) < 0)
1835 adios (NULL, "internal error(2)");
1836
1837 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1838 content_error (ct->c_file, ct, "unable to open for reading");
1839 return NOTOK;
1840 }
1841
1842 if ((digested = ct->c_digested))
1843 MD5Init (&mdContext);
1844
1845 quoted = 0;
1846 #ifdef lint
1847 mask = 0;
1848 #endif
1849
1850 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1851 while (len > 0) {
1852 char *dp;
1853
1854 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1855 content_error (NULL, ct, "premature eof");
1856 goto clean_up;
1857 }
1858
1859 if ((cc = strlen (buffer)) > len)
1860 cc = len;
1861 len -= cc;
1862
1863 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1864 if (!isspace (*ep))
1865 break;
1866 *++ep = '\n', ep++;
1867
1868 for (; cp < ep; cp++) {
1869 if (quoted) {
1870 if (quoted > 1) {
1871 if (!isxdigit (*cp)) {
1872 invalid_hex:
1873 dp = "expecting hexidecimal-digit";
1874 goto invalid_encoding;
1875 }
1876 mask <<= 4;
1877 mask |= hex2nib[*cp & 0x7f];
1878 putc (mask, ce->ce_fp);
1879 if (digested)
1880 MD5Update (&mdContext, &mask, 1);
1881 } else {
1882 switch (*cp) {
1883 case ':':
1884 putc (*cp, ce->ce_fp);
1885 if (digested)
1886 MD5Update (&mdContext, (unsigned char *) ":", 1);
1887 break;
1888
1889 default:
1890 if (!isxdigit (*cp))
1891 goto invalid_hex;
1892 mask = hex2nib[*cp & 0x7f];
1893 quoted = 2;
1894 continue;
1895 }
1896 }
1897
1898 if (ferror (ce->ce_fp)) {
1899 content_error (ce->ce_file, ct, "error writing to");
1900 goto clean_up;
1901 }
1902 quoted = 0;
1903 continue;
1904 }
1905
1906 switch (*cp) {
1907 default:
1908 if (*cp < '!' || *cp > '~') {
1909 int i;
1910 dp = "expecting character in range [!..~]";
1911
1912 invalid_encoding:
1913 i = strlen (invo_name) + 2;
1914 content_error (NULL, ct,
1915 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1916 dp, i, i, "", *cp);
1917 goto clean_up;
1918 }
1919 /* and fall...*/
1920 case ' ':
1921 case '\t':
1922 case '\n':
1923 putc (*cp, ce->ce_fp);
1924 if (digested) {
1925 if (*cp == '\n')
1926 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1927 else
1928 MD5Update (&mdContext, (unsigned char *) cp, 1);
1929 }
1930 if (ferror (ce->ce_fp)) {
1931 content_error (ce->ce_file, ct, "error writing to");
1932 goto clean_up;
1933 }
1934 break;
1935
1936 case '=':
1937 if (*++cp != '\n') {
1938 quoted = 1;
1939 cp--;
1940 }
1941 break;
1942 }
1943 }
1944 }
1945 if (quoted) {
1946 content_error (NULL, ct,
1947 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1948 goto clean_up;
1949 }
1950
1951 fseek (ct->c_fp, 0L, SEEK_SET);
1952
1953 if (fflush (ce->ce_fp)) {
1954 content_error (ce->ce_file, ct, "error writing to");
1955 goto clean_up;
1956 }
1957
1958 if (digested) {
1959 unsigned char digest[16];
1960
1961 MD5Final (digest, &mdContext);
1962 if (memcmp((char *) digest, (char *) ct->c_digest,
1963 sizeof(digest) / sizeof(digest[0])))
1964 content_error (NULL, ct,
1965 "content integrity suspect (digest mismatch) -- continuing");
1966 else
1967 if (debugsw)
1968 fprintf (stderr, "content integrity confirmed\n");
1969 }
1970
1971 fseek (ce->ce_fp, 0L, SEEK_SET);
1972
1973 ready_to_go:
1974 *file = ce->ce_file;
1975 return fileno (ce->ce_fp);
1976
1977 clean_up:
1978 free_encoding (ct, 0);
1979 return NOTOK;
1980 }
1981
1982
1983 /*
1984 * 7BIT
1985 */
1986
1987 static int
1988 Init7Bit (CT ct)
1989 {
1990 if (init_encoding (ct, open7Bit) == NOTOK)
1991 return NOTOK;
1992
1993 ct->c_cesizefnx = NULL; /* no need to decode for real size */
1994 return OK;
1995 }
1996
1997
1998 static int
1999 open7Bit (CT ct, char **file)
2000 {
2001 int cc, fd, len;
2002 char buffer[BUFSIZ];
2003 /* sbeck -- handle prefixes */
2004 char *cp;
2005 CI ci;
2006 CE ce;
2007
2008 ce = ct->c_cefile;
2009 if (ce->ce_fp) {
2010 fseek (ce->ce_fp, 0L, SEEK_SET);
2011 goto ready_to_go;
2012 }
2013
2014 if (ce->ce_file) {
2015 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2016 content_error (ce->ce_file, ct, "unable to fopen for reading");
2017 return NOTOK;
2018 }
2019 goto ready_to_go;
2020 }
2021
2022 if (*file == NULL) {
2023 ce->ce_file = add (m_scratch ("", tmp), NULL);
2024 ce->ce_unlink = 1;
2025 } else {
2026 ce->ce_file = add (*file, NULL);
2027 ce->ce_unlink = 0;
2028 }
2029
2030 /* sbeck@cise.ufl.edu -- handle suffixes */
2031 ci = &ct->c_ctinfo;
2032 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2033 invo_name, ci->ci_type, ci->ci_subtype);
2034 cp = context_find (buffer);
2035 if (cp == NULL || *cp == '\0') {
2036 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2037 ci->ci_type);
2038 cp = context_find (buffer);
2039 }
2040 if (cp != NULL && *cp != '\0')
2041 ce->ce_file = add (cp, ce->ce_file);
2042
2043 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2044 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2045 return NOTOK;
2046 }
2047
2048 if (ct->c_type == CT_MULTIPART) {
2049 char **ap, **ep;
2050 CI ci = &ct->c_ctinfo;
2051
2052 len = 0;
2053 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2054 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2055 + 1 + strlen (ci->ci_subtype);
2056 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2057 putc (';', ce->ce_fp);
2058 len++;
2059
2060 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2061
2062 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2063 fputs ("\n\t", ce->ce_fp);
2064 len = 8;
2065 } else {
2066 putc (' ', ce->ce_fp);
2067 len++;
2068 }
2069 fprintf (ce->ce_fp, "%s", buffer);
2070 len += cc;
2071 }
2072
2073 if (ci->ci_comment) {
2074 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2075 fputs ("\n\t", ce->ce_fp);
2076 len = 8;
2077 }
2078 else {
2079 putc (' ', ce->ce_fp);
2080 len++;
2081 }
2082 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2083 len += cc;
2084 }
2085 fprintf (ce->ce_fp, "\n");
2086 if (ct->c_id)
2087 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2088 if (ct->c_descr)
2089 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2090 fprintf (ce->ce_fp, "\n");
2091 }
2092
2093 if ((len = ct->c_end - ct->c_begin) < 0)
2094 adios (NULL, "internal error(3)");
2095
2096 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2097 content_error (ct->c_file, ct, "unable to open for reading");
2098 return NOTOK;
2099 }
2100
2101 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2102 while (len > 0)
2103 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2104 case NOTOK:
2105 content_error (ct->c_file, ct, "error reading from");
2106 goto clean_up;
2107
2108 case OK:
2109 content_error (NULL, ct, "premature eof");
2110 goto clean_up;
2111
2112 default:
2113 if (cc > len)
2114 cc = len;
2115 len -= cc;
2116
2117 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2118 if (ferror (ce->ce_fp)) {
2119 content_error (ce->ce_file, ct, "error writing to");
2120 goto clean_up;
2121 }
2122 }
2123
2124 fseek (ct->c_fp, 0L, SEEK_SET);
2125
2126 if (fflush (ce->ce_fp)) {
2127 content_error (ce->ce_file, ct, "error writing to");
2128 goto clean_up;
2129 }
2130
2131 fseek (ce->ce_fp, 0L, SEEK_SET);
2132
2133 ready_to_go:
2134 *file = ce->ce_file;
2135 return fileno (ce->ce_fp);
2136
2137 clean_up:
2138 free_encoding (ct, 0);
2139 return NOTOK;
2140 }
2141
2142
2143 /*
2144 * External
2145 */
2146
2147 static int
2148 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2149 {
2150 char cachefile[BUFSIZ];
2151
2152 if (ce->ce_fp) {
2153 fseek (ce->ce_fp, 0L, SEEK_SET);
2154 goto ready_already;
2155 }
2156
2157 if (ce->ce_file) {
2158 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2159 content_error (ce->ce_file, ct, "unable to fopen for reading");
2160 return NOTOK;
2161 }
2162 goto ready_already;
2163 }
2164
2165 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2166 cachefile, sizeof(cachefile)) != NOTOK) {
2167 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2168 ce->ce_file = getcpy (cachefile);
2169 ce->ce_unlink = 0;
2170 goto ready_already;
2171 } else {
2172 admonish (cachefile, "unable to fopen for reading");
2173 }
2174 }
2175
2176 return OK;
2177
2178 ready_already:
2179 *file = ce->ce_file;
2180 *fd = fileno (ce->ce_fp);
2181 return DONE;
2182 }
2183
2184 /*
2185 * File
2186 */
2187
2188 static int
2189 InitFile (CT ct)
2190 {
2191 return init_encoding (ct, openFile);
2192 }
2193
2194
2195 static int
2196 openFile (CT ct, char **file)
2197 {
2198 int fd, cachetype;
2199 char cachefile[BUFSIZ];
2200 struct exbody *e = ct->c_ctexbody;
2201 CE ce = ct->c_cefile;
2202
2203 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2204 case NOTOK:
2205 return NOTOK;
2206
2207 case OK:
2208 break;
2209
2210 case DONE:
2211 return fd;
2212 }
2213
2214 if (!e->eb_name) {
2215 content_error (NULL, ct, "missing name parameter");
2216 return NOTOK;
2217 }
2218
2219 ce->ce_file = getcpy (e->eb_name);
2220 ce->ce_unlink = 0;
2221
2222 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2223 content_error (ce->ce_file, ct, "unable to fopen for reading");
2224 return NOTOK;
2225 }
2226
2227 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2228 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2229 cachefile, sizeof(cachefile)) != NOTOK) {
2230 int mask;
2231 FILE *fp;
2232
2233 mask = umask (cachetype ? ~m_gmprot () : 0222);
2234 if ((fp = fopen (cachefile, "w"))) {
2235 int cc;
2236 char buffer[BUFSIZ];
2237 FILE *gp = ce->ce_fp;
2238
2239 fseek (gp, 0L, SEEK_SET);
2240
2241 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2242 > 0)
2243 fwrite (buffer, sizeof(*buffer), cc, fp);
2244 fflush (fp);
2245
2246 if (ferror (gp)) {
2247 admonish (ce->ce_file, "error reading");
2248 unlink (cachefile);
2249 }
2250 else
2251 if (ferror (fp)) {
2252 admonish (cachefile, "error writing");
2253 unlink (cachefile);
2254 }
2255 fclose (fp);
2256 }
2257 umask (mask);
2258 }
2259
2260 fseek (ce->ce_fp, 0L, SEEK_SET);
2261 *file = ce->ce_file;
2262 return fileno (ce->ce_fp);
2263 }
2264
2265 /*
2266 * FTP
2267 */
2268
2269 static int
2270 InitFTP (CT ct)
2271 {
2272 return init_encoding (ct, openFTP);
2273 }
2274
2275
2276 static int
2277 openFTP (CT ct, char **file)
2278 {
2279 int cachetype, caching, fd;
2280 int len, buflen;
2281 char *bp, *ftp, *user, *pass;
2282 char buffer[BUFSIZ], cachefile[BUFSIZ];
2283 struct exbody *e;
2284 CE ce;
2285 static char *username = NULL;
2286 static char *password = NULL;
2287
2288 e = ct->c_ctexbody;
2289 ce = ct->c_cefile;
2290
2291 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2292 ftp = NULL;
2293
2294 #ifndef BUILTIN_FTP
2295 if (!ftp)
2296 return NOTOK;
2297 #endif
2298
2299 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2300 case NOTOK:
2301 return NOTOK;
2302
2303 case OK:
2304 break;
2305
2306 case DONE:
2307 return fd;
2308 }
2309
2310 if (!e->eb_name || !e->eb_site) {
2311 content_error (NULL, ct, "missing %s parameter",
2312 e->eb_name ? "site": "name");
2313 return NOTOK;
2314 }
2315
2316 if (xpid) {
2317 if (xpid < 0)
2318 xpid = -xpid;
2319 pidcheck (pidwait (xpid, NOTOK));
2320 xpid = 0;
2321 }
2322
2323 /* Get the buffer ready to go */
2324 bp = buffer;
2325 buflen = sizeof(buffer);
2326
2327 /*
2328 * Construct the query message for user
2329 */
2330 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2331 len = strlen (bp);
2332 bp += len;
2333 buflen -= len;
2334
2335 if (e->eb_partno) {
2336 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2337 len = strlen (bp);
2338 bp += len;
2339 buflen -= len;
2340 }
2341
2342 snprintf (bp, buflen, "\n using %sFTP from site %s",
2343 e->eb_flags ? "anonymous " : "", e->eb_site);
2344 len = strlen (bp);
2345 bp += len;
2346 buflen -= len;
2347
2348 if (e->eb_size > 0) {
2349 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2350 len = strlen (bp);
2351 bp += len;
2352 buflen -= len;
2353 }
2354 snprintf (bp, buflen, "? ");
2355
2356 /*
2357 * Now, check the answer
2358 */
2359 if (!getanswer (buffer))
2360 return NOTOK;
2361
2362 if (e->eb_flags) {
2363 user = "anonymous";
2364 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2365 pass = buffer;
2366 } else {
2367 ruserpass (e->eb_site, &username, &password);
2368 user = username;
2369 pass = password;
2370 }
2371
2372 ce->ce_unlink = (*file == NULL);
2373 caching = 0;
2374 cachefile[0] = '\0';
2375 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2376 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2377 cachefile, sizeof(cachefile)) != NOTOK) {
2378 if (*file == NULL) {
2379 ce->ce_unlink = 0;
2380 caching = 1;
2381 }
2382 }
2383
2384 if (*file)
2385 ce->ce_file = add (*file, NULL);
2386 else if (caching)
2387 ce->ce_file = add (cachefile, NULL);
2388 else
2389 ce->ce_file = add (m_scratch ("", tmp), NULL);
2390
2391 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2392 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2393 return NOTOK;
2394 }
2395
2396 #ifdef BUILTIN_FTP
2397 if (ftp)
2398 #endif
2399 {
2400 int child_id, i, vecp;
2401 char *vec[9];
2402
2403 vecp = 0;
2404 vec[vecp++] = r1bindex (ftp, '/');
2405 vec[vecp++] = e->eb_site;
2406 vec[vecp++] = user;
2407 vec[vecp++] = pass;
2408 vec[vecp++] = e->eb_dir;
2409 vec[vecp++] = e->eb_name;
2410 vec[vecp++] = ce->ce_file,
2411 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2412 ? "ascii" : "binary";
2413 vec[vecp] = NULL;
2414
2415 fflush (stdout);
2416
2417 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2418 sleep (5);
2419 switch (child_id) {
2420 case NOTOK:
2421 adios ("fork", "unable to");
2422 /* NOTREACHED */
2423
2424 case OK:
2425 close (fileno (ce->ce_fp));
2426 execvp (ftp, vec);
2427 fprintf (stderr, "unable to exec ");
2428 perror (ftp);
2429 _exit (-1);
2430 /* NOTREACHED */
2431
2432 default:
2433 if (pidXwait (child_id, NULL)) {
2434 #ifdef BUILTIN_FTP
2435 losing_ftp:
2436 #endif
2437 username = password = NULL;
2438 ce->ce_unlink = 1;
2439 return NOTOK;
2440 }
2441 break;
2442 }
2443 }
2444 #ifdef BUILTIN_FTP
2445 else
2446 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2447 ce->ce_file,
2448 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2449 == NOTOK)
2450 goto losing_ftp;
2451 #endif
2452
2453 if (cachefile[0]) {
2454 if (caching)
2455 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2456 else {
2457 int mask;
2458 FILE *fp;
2459
2460 mask = umask (cachetype ? ~m_gmprot () : 0222);
2461 if ((fp = fopen (cachefile, "w"))) {
2462 int cc;
2463 FILE *gp = ce->ce_fp;
2464
2465 fseek (gp, 0L, SEEK_SET);
2466
2467 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2468 > 0)
2469 fwrite (buffer, sizeof(*buffer), cc, fp);
2470 fflush (fp);
2471
2472 if (ferror (gp)) {
2473 admonish (ce->ce_file, "error reading");
2474 unlink (cachefile);
2475 }
2476 else
2477 if (ferror (fp)) {
2478 admonish (cachefile, "error writing");
2479 unlink (cachefile);
2480 }
2481 fclose (fp);
2482 }
2483 umask (mask);
2484 }
2485 }
2486
2487 fseek (ce->ce_fp, 0L, SEEK_SET);
2488 *file = ce->ce_file;
2489 return fileno (ce->ce_fp);
2490 }
2491
2492
2493 /*
2494 * Mail
2495 */
2496
2497 static int
2498 InitMail (CT ct)
2499 {
2500 return init_encoding (ct, openMail);
2501 }
2502
2503
2504 static int
2505 openMail (CT ct, char **file)
2506 {
2507 int child_id, fd, i, vecp;
2508 int len, buflen;
2509 char *bp, buffer[BUFSIZ], *vec[7];
2510 struct exbody *e = ct->c_ctexbody;
2511 CE ce = ct->c_cefile;
2512
2513 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2514 case NOTOK:
2515 return NOTOK;
2516
2517 case OK:
2518 break;
2519
2520 case DONE:
2521 return fd;
2522 }
2523
2524 if (!e->eb_server) {
2525 content_error (NULL, ct, "missing server parameter");
2526 return NOTOK;
2527 }
2528
2529 if (xpid) {
2530 if (xpid < 0)
2531 xpid = -xpid;
2532 pidcheck (pidwait (xpid, NOTOK));
2533 xpid = 0;
2534 }
2535
2536 /* Get buffer ready to go */
2537 bp = buffer;
2538 buflen = sizeof(buffer);
2539
2540 /* Now, construct query message */
2541 snprintf (bp, buflen, "Retrieve content");
2542 len = strlen (bp);
2543 bp += len;
2544 buflen -= len;
2545
2546 if (e->eb_partno) {
2547 snprintf (bp, buflen, " %s", e->eb_partno);
2548 len = strlen (bp);
2549 bp += len;
2550 buflen -= len;
2551 }
2552
2553 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2554 e->eb_server,
2555 e->eb_subject ? e->eb_subject : e->eb_body);
2556
2557 /* Now, check answer */
2558 if (!getanswer (buffer))
2559 return NOTOK;
2560
2561 vecp = 0;
2562 vec[vecp++] = r1bindex (mailproc, '/');
2563 vec[vecp++] = e->eb_server;
2564 vec[vecp++] = "-subject";
2565 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2566 vec[vecp++] = "-body";
2567 vec[vecp++] = e->eb_body;
2568 vec[vecp] = NULL;
2569
2570 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2571 sleep (5);
2572 switch (child_id) {
2573 case NOTOK:
2574 advise ("fork", "unable to");
2575 return NOTOK;
2576
2577 case OK:
2578 execvp (mailproc, vec);
2579 fprintf (stderr, "unable to exec ");
2580 perror (mailproc);
2581 _exit (-1);
2582 /* NOTREACHED */
2583
2584 default:
2585 if (pidXwait (child_id, NULL) == OK)
2586 advise (NULL, "request sent");
2587 break;
2588 }
2589
2590 if (*file == NULL) {
2591 ce->ce_file = add (m_scratch ("", tmp), NULL);
2592 ce->ce_unlink = 1;
2593 } else {
2594 ce->ce_file = add (*file, NULL);
2595 ce->ce_unlink = 0;
2596 }
2597
2598 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2599 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2600 return NOTOK;
2601 }
2602
2603 if (ct->c_showproc)
2604 free (ct->c_showproc);
2605 ct->c_showproc = add ("true", NULL);
2606
2607 fseek (ce->ce_fp, 0L, SEEK_SET);
2608 *file = ce->ce_file;
2609 return fileno (ce->ce_fp);
2610 }
2611
2612
2613 static int
2614 readDigest (CT ct, char *cp)
2615 {
2616 int bitno, skip;
2617 unsigned long bits;
2618 char *bp = cp;
2619 unsigned char *dp, value, *ep;
2620 unsigned char *b, *b1, *b2, *b3;
2621
2622 b = (unsigned char *) &bits,
2623 b1 = &b[endian > 0 ? 1 : 2],
2624 b2 = &b[endian > 0 ? 2 : 1],
2625 b3 = &b[endian > 0 ? 3 : 0];
2626 bitno = 18;
2627 bits = 0L;
2628 skip = 0;
2629
2630 for (ep = (dp = ct->c_digest)
2631 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2632 switch (*cp) {
2633 default:
2634 if (skip
2635 || (*cp & 0x80)
2636 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2637 if (debugsw)
2638 fprintf (stderr, "invalid BASE64 encoding\n");
2639 return NOTOK;
2640 }
2641
2642 bits |= value << bitno;
2643 test_end:
2644 if ((bitno -= 6) < 0) {
2645 if (dp + (3 - skip) > ep)
2646 goto invalid_digest;
2647 *dp++ = *b1;
2648 if (skip < 2) {
2649 *dp++ = *b2;
2650 if (skip < 1)
2651 *dp++ = *b3;
2652 }
2653 bitno = 18;
2654 bits = 0L;
2655 skip = 0;
2656 }
2657 break;
2658
2659 case '=':
2660 if (++skip > 3)
2661 goto self_delimiting;
2662 goto test_end;
2663 }
2664 if (bitno != 18) {
2665 if (debugsw)
2666 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2667
2668 return NOTOK;
2669 }
2670 self_delimiting:
2671 if (dp != ep) {
2672 invalid_digest:
2673 if (debugsw) {
2674 while (*cp)
2675 cp++;
2676 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2677 cp - bp);
2678 }
2679
2680 return NOTOK;
2681 }
2682
2683 if (debugsw) {
2684 fprintf (stderr, "MD5 digest=");
2685 for (dp = ct->c_digest; dp < ep; dp++)
2686 fprintf (stderr, "%02x", *dp & 0xff);
2687 fprintf (stderr, "\n");
2688 }
2689
2690 return OK;
2691 }