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