]> diplodocus.org Git - nmh/blob - uip/mhparse.c
Fix stupid accidental dependence on a bash quirk in previous
[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 advise (NULL, "unable to decode %s", file);
263 return NULL;
264 }
265
266 if (is_stdin)
267 ct->c_unlink = 1; /* temp file to remove */
268
269 ct->c_fp = NULL;
270
271 if (ct->c_end == 0L) {
272 fseek (fp, 0L, SEEK_END);
273 ct->c_end = ftell (fp);
274 }
275
276 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
277 fclose (fp);
278 free_content (ct);
279 return NULL;
280 }
281
282 fclose (fp);
283 return ct;
284 }
285
286
287 /*
288 * Main routine for reading/parsing the headers
289 * of a message content.
290 *
291 * toplevel = 1 # we are at the top level of the message
292 * toplevel = 0 # we are inside message type or multipart type
293 * # other than multipart/digest
294 * toplevel = -1 # we are inside multipart/digest
295 * NB: on failure we will fclose(in)!
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 ct->c_fp = NULL;
1060 return NOTOK;
1061 }
1062 p->c_fp = NULL;
1063 part->mp_part = p;
1064 pos = p->c_begin;
1065 fseek (fp, pos, SEEK_SET);
1066 inout = 0;
1067 } else {
1068 if (strcmp (buffer + 2, m->mp_start) == 0) {
1069 inout = 1;
1070 end_part:
1071 p = part->mp_part;
1072 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1073 if (p->c_end < p->c_begin)
1074 p->c_begin = p->c_end;
1075 if (inout)
1076 goto next_part;
1077 goto last_part;
1078 } else {
1079 if (strcmp (buffer + 2, m->mp_stop) == 0)
1080 goto end_part;
1081 }
1082 }
1083 }
1084
1085 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1086 if (!inout && part) {
1087 p = part->mp_part;
1088 p->c_end = ct->c_end;
1089
1090 if (p->c_begin >= p->c_end) {
1091 for (next = &m->mp_parts; *next != part;
1092 next = &((*next)->mp_next))
1093 continue;
1094 *next = NULL;
1095 free_content (p);
1096 free ((char *) part);
1097 }
1098 }
1099
1100 last_part:
1101 /* reverse the order of the parts for multipart/alternative */
1102 if (ct->c_subtype == MULTI_ALTERNATE)
1103 reverse_parts (ct);
1104
1105 /*
1106 * label all subparts with part number, and
1107 * then initialize the content of the subpart.
1108 */
1109 {
1110 int partnum;
1111 char *pp;
1112 char partnam[BUFSIZ];
1113
1114 if (ct->c_partno) {
1115 snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1116 pp = partnam + strlen (partnam);
1117 } else {
1118 pp = partnam;
1119 }
1120
1121 for (part = m->mp_parts, partnum = 1; part;
1122 part = part->mp_next, partnum++) {
1123 p = part->mp_part;
1124
1125 sprintf (pp, "%d", partnum);
1126 p->c_partno = add (partnam, NULL);
1127
1128 /* initialize the content of the subparts */
1129 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1130 fclose (ct->c_fp);
1131 ct->c_fp = NULL;
1132 return NOTOK;
1133 }
1134 }
1135 }
1136
1137 fclose (ct->c_fp);
1138 ct->c_fp = NULL;
1139 return OK;
1140 }
1141
1142
1143 /*
1144 * reverse the order of the parts of a multipart
1145 */
1146
1147 static void
1148 reverse_parts (CT ct)
1149 {
1150 int i;
1151 struct multipart *m;
1152 struct part **base, **bmp, **next, *part;
1153
1154 m = (struct multipart *) ct->c_ctparams;
1155
1156 /* if only one part, just return */
1157 if (!m->mp_parts || !m->mp_parts->mp_next)
1158 return;
1159
1160 /* count number of parts */
1161 i = 0;
1162 for (part = m->mp_parts; part; part = part->mp_next)
1163 i++;
1164
1165 /* allocate array of pointers to the parts */
1166 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1167 adios (NULL, "out of memory");
1168 bmp = base;
1169
1170 /* point at all the parts */
1171 for (part = m->mp_parts; part; part = part->mp_next)
1172 *bmp++ = part;
1173 *bmp = NULL;
1174
1175 /* reverse the order of the parts */
1176 next = &m->mp_parts;
1177 for (bmp--; bmp >= base; bmp--) {
1178 part = *bmp;
1179 *next = part;
1180 next = &part->mp_next;
1181 }
1182 *next = NULL;
1183
1184 /* free array of pointers */
1185 free ((char *) base);
1186 }
1187
1188
1189 /*
1190 * MESSAGE
1191 */
1192
1193 static int
1194 InitMessage (CT ct)
1195 {
1196 struct k2v *kv;
1197 CI ci = &ct->c_ctinfo;
1198
1199 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1200 admonish (NULL,
1201 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1202 ci->ci_type, ci->ci_subtype, ct->c_file);
1203 return NOTOK;
1204 }
1205
1206 /* check for missing subtype */
1207 if (!*ci->ci_subtype)
1208 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1209
1210 /* match subtype */
1211 for (kv = SubMessage; kv->kv_key; kv++)
1212 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1213 break;
1214 ct->c_subtype = kv->kv_value;
1215
1216 switch (ct->c_subtype) {
1217 case MESSAGE_RFC822:
1218 break;
1219
1220 case MESSAGE_PARTIAL:
1221 {
1222 char **ap, **ep;
1223 struct partial *p;
1224
1225 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1226 adios (NULL, "out of memory");
1227 ct->c_ctparams = (void *) p;
1228
1229 /* scan for parameters "id", "number", and "total" */
1230 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1231 if (!strcasecmp (*ap, "id")) {
1232 p->pm_partid = add (*ep, NULL);
1233 continue;
1234 }
1235 if (!strcasecmp (*ap, "number")) {
1236 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1237 || p->pm_partno < 1) {
1238 invalid_param:
1239 advise (NULL,
1240 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1241 *ap, ci->ci_type, ci->ci_subtype,
1242 ct->c_file, TYPE_FIELD);
1243 return NOTOK;
1244 }
1245 continue;
1246 }
1247 if (!strcasecmp (*ap, "total")) {
1248 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1249 || p->pm_maxno < 1)
1250 goto invalid_param;
1251 continue;
1252 }
1253 }
1254
1255 if (!p->pm_partid
1256 || !p->pm_partno
1257 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1258 advise (NULL,
1259 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1260 ci->ci_type, ci->ci_subtype,
1261 ct->c_file, TYPE_FIELD);
1262 return NOTOK;
1263 }
1264 }
1265 break;
1266
1267 case MESSAGE_EXTERNAL:
1268 {
1269 int exresult;
1270 struct exbody *e;
1271 CT p;
1272 FILE *fp;
1273
1274 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1275 adios (NULL, "out of memory");
1276 ct->c_ctparams = (void *) e;
1277
1278 if (!ct->c_fp
1279 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1280 advise (ct->c_file, "unable to open for reading");
1281 return NOTOK;
1282 }
1283
1284 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1285
1286 if (!(p = get_content (fp, ct->c_file, 0))) {
1287 ct->c_fp = NULL;
1288 return NOTOK;
1289 }
1290
1291 e->eb_parent = ct;
1292 e->eb_content = p;
1293 p->c_ctexbody = e;
1294 if ((exresult = params_external (ct, 0)) != NOTOK
1295 && p->c_ceopenfnx == openMail) {
1296 int cc, size;
1297 char *bp;
1298
1299 if ((size = ct->c_end - p->c_begin) <= 0) {
1300 if (!e->eb_subject)
1301 content_error (NULL, ct,
1302 "empty body for access-type=mail-server");
1303 goto no_body;
1304 }
1305
1306 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1307 adios (NULL, "out of memory");
1308 fseek (p->c_fp, p->c_begin, SEEK_SET);
1309 while (size > 0)
1310 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1311 case NOTOK:
1312 adios ("failed", "fread");
1313
1314 case OK:
1315 adios (NULL, "unexpected EOF from fread");
1316
1317 default:
1318 bp += cc, size -= cc;
1319 break;
1320 }
1321 *bp = 0;
1322 }
1323 no_body:
1324 p->c_fp = NULL;
1325 p->c_end = p->c_begin;
1326
1327 fclose (ct->c_fp);
1328 ct->c_fp = NULL;
1329
1330 if (exresult == NOTOK)
1331 return NOTOK;
1332 if (e->eb_flags == NOTOK)
1333 return OK;
1334
1335 switch (p->c_type) {
1336 case CT_MULTIPART:
1337 break;
1338
1339 case CT_MESSAGE:
1340 if (p->c_subtype != MESSAGE_RFC822)
1341 break;
1342 /* else fall... */
1343 default:
1344 e->eb_partno = ct->c_partno;
1345 if (p->c_ctinitfnx)
1346 (*p->c_ctinitfnx) (p);
1347 break;
1348 }
1349 }
1350 break;
1351
1352 default:
1353 break;
1354 }
1355
1356 return OK;
1357 }
1358
1359
1360 static int
1361 params_external (CT ct, int composing)
1362 {
1363 char **ap, **ep;
1364 struct exbody *e = (struct exbody *) ct->c_ctparams;
1365 CI ci = &ct->c_ctinfo;
1366
1367 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1368 if (!strcasecmp (*ap, "access-type")) {
1369 struct str2init *s2i;
1370 CT p = e->eb_content;
1371
1372 for (s2i = str2methods; s2i->si_key; s2i++)
1373 if (!strcasecmp (*ep, s2i->si_key))
1374 break;
1375 if (!s2i->si_key) {
1376 e->eb_access = *ep;
1377 e->eb_flags = NOTOK;
1378 p->c_encoding = CE_EXTERNAL;
1379 continue;
1380 }
1381 e->eb_access = s2i->si_key;
1382 e->eb_flags = s2i->si_val;
1383 p->c_encoding = CE_EXTERNAL;
1384
1385 /* Call the Init function for this external type */
1386 if ((*s2i->si_init)(p) == NOTOK)
1387 return NOTOK;
1388 continue;
1389 }
1390 if (!strcasecmp (*ap, "name")) {
1391 e->eb_name = *ep;
1392 continue;
1393 }
1394 if (!strcasecmp (*ap, "permission")) {
1395 e->eb_permission = *ep;
1396 continue;
1397 }
1398 if (!strcasecmp (*ap, "site")) {
1399 e->eb_site = *ep;
1400 continue;
1401 }
1402 if (!strcasecmp (*ap, "directory")) {
1403 e->eb_dir = *ep;
1404 continue;
1405 }
1406 if (!strcasecmp (*ap, "mode")) {
1407 e->eb_mode = *ep;
1408 continue;
1409 }
1410 if (!strcasecmp (*ap, "size")) {
1411 sscanf (*ep, "%lu", &e->eb_size);
1412 continue;
1413 }
1414 if (!strcasecmp (*ap, "server")) {
1415 e->eb_server = *ep;
1416 continue;
1417 }
1418 if (!strcasecmp (*ap, "subject")) {
1419 e->eb_subject = *ep;
1420 continue;
1421 }
1422 if (composing && !strcasecmp (*ap, "body")) {
1423 e->eb_body = getcpy (*ep);
1424 continue;
1425 }
1426 }
1427
1428 if (!e->eb_access) {
1429 advise (NULL,
1430 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1431 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1432 return NOTOK;
1433 }
1434
1435 return OK;
1436 }
1437
1438
1439 /*
1440 * APPLICATION
1441 */
1442
1443 static int
1444 InitApplication (CT ct)
1445 {
1446 struct k2v *kv;
1447 CI ci = &ct->c_ctinfo;
1448
1449 /* match subtype */
1450 for (kv = SubApplication; kv->kv_key; kv++)
1451 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1452 break;
1453 ct->c_subtype = kv->kv_value;
1454
1455 return OK;
1456 }
1457
1458
1459 /*
1460 * TRANSFER ENCODINGS
1461 */
1462
1463 static int
1464 init_encoding (CT ct, OpenCEFunc openfnx)
1465 {
1466 CE ce;
1467
1468 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1469 adios (NULL, "out of memory");
1470
1471 ct->c_cefile = ce;
1472 ct->c_ceopenfnx = openfnx;
1473 ct->c_ceclosefnx = close_encoding;
1474 ct->c_cesizefnx = size_encoding;
1475
1476 return OK;
1477 }
1478
1479
1480 static void
1481 close_encoding (CT ct)
1482 {
1483 CE ce;
1484
1485 if (!(ce = ct->c_cefile))
1486 return;
1487
1488 if (ce->ce_fp) {
1489 fclose (ce->ce_fp);
1490 ce->ce_fp = NULL;
1491 }
1492 }
1493
1494
1495 static unsigned long
1496 size_encoding (CT ct)
1497 {
1498 int fd;
1499 unsigned long size;
1500 char *file;
1501 CE ce;
1502 struct stat st;
1503
1504 if (!(ce = ct->c_cefile))
1505 return (ct->c_end - ct->c_begin);
1506
1507 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1508 return (long) st.st_size;
1509
1510 if (ce->ce_file) {
1511 if (stat (ce->ce_file, &st) != NOTOK)
1512 return (long) st.st_size;
1513 else
1514 return 0L;
1515 }
1516
1517 if (ct->c_encoding == CE_EXTERNAL)
1518 return (ct->c_end - ct->c_begin);
1519
1520 file = NULL;
1521 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1522 return (ct->c_end - ct->c_begin);
1523
1524 if (fstat (fd, &st) != NOTOK)
1525 size = (long) st.st_size;
1526 else
1527 size = 0L;
1528
1529 (*ct->c_ceclosefnx) (ct);
1530 return size;
1531 }
1532
1533
1534 /*
1535 * BASE64
1536 */
1537
1538 static unsigned char b642nib[0x80] = {
1539 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1540 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1541 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1542 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1543 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1544 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1545 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1546 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1547 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1548 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1549 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1550 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1551 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1552 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1553 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1554 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1555 };
1556
1557
1558 static int
1559 InitBase64 (CT ct)
1560 {
1561 return init_encoding (ct, openBase64);
1562 }
1563
1564
1565 static int
1566 openBase64 (CT ct, char **file)
1567 {
1568 int bitno, cc, digested;
1569 int fd, len, skip;
1570 unsigned long bits;
1571 unsigned char value, *b, *b1, *b2, *b3;
1572 char *cp, *ep, buffer[BUFSIZ];
1573 /* sbeck -- handle prefixes */
1574 CI ci;
1575 CE ce;
1576 MD5_CTX mdContext;
1577
1578 b = (unsigned char *) &bits;
1579 b1 = &b[endian > 0 ? 1 : 2];
1580 b2 = &b[endian > 0 ? 2 : 1];
1581 b3 = &b[endian > 0 ? 3 : 0];
1582
1583 ce = ct->c_cefile;
1584 if (ce->ce_fp) {
1585 fseek (ce->ce_fp, 0L, SEEK_SET);
1586 goto ready_to_go;
1587 }
1588
1589 if (ce->ce_file) {
1590 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1591 content_error (ce->ce_file, ct, "unable to fopen for reading");
1592 return NOTOK;
1593 }
1594 goto ready_to_go;
1595 }
1596
1597 if (*file == NULL) {
1598 ce->ce_file = add (m_scratch ("", tmp), NULL);
1599 ce->ce_unlink = 1;
1600 } else {
1601 ce->ce_file = add (*file, NULL);
1602 ce->ce_unlink = 0;
1603 }
1604
1605 /* sbeck@cise.ufl.edu -- handle suffixes */
1606 ci = &ct->c_ctinfo;
1607 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1608 invo_name, ci->ci_type, ci->ci_subtype);
1609 cp = context_find (buffer);
1610 if (cp == NULL || *cp == '\0') {
1611 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1612 ci->ci_type);
1613 cp = context_find (buffer);
1614 }
1615 if (cp != NULL && *cp != '\0')
1616 ce->ce_file = add (cp, ce->ce_file);
1617
1618 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1619 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1620 return NOTOK;
1621 }
1622
1623 if ((len = ct->c_end - ct->c_begin) < 0)
1624 adios (NULL, "internal error(1)");
1625
1626 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1627 content_error (ct->c_file, ct, "unable to open for reading");
1628 return NOTOK;
1629 }
1630
1631 if ((digested = ct->c_digested))
1632 MD5Init (&mdContext);
1633
1634 bitno = 18;
1635 bits = 0L;
1636 skip = 0;
1637
1638 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1639 while (len > 0) {
1640 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1641 case NOTOK:
1642 content_error (ct->c_file, ct, "error reading from");
1643 goto clean_up;
1644
1645 case OK:
1646 content_error (NULL, ct, "premature eof");
1647 goto clean_up;
1648
1649 default:
1650 if (cc > len)
1651 cc = len;
1652 len -= cc;
1653
1654 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1655 switch (*cp) {
1656 default:
1657 if (isspace (*cp))
1658 break;
1659 if (skip || (*cp & 0x80)
1660 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1661 if (debugsw) {
1662 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1663 *cp,
1664 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1665 skip);
1666 }
1667 content_error (NULL, ct,
1668 "invalid BASE64 encoding -- continuing");
1669 continue;
1670 }
1671
1672 bits |= value << bitno;
1673 test_end:
1674 if ((bitno -= 6) < 0) {
1675 putc ((char) *b1, ce->ce_fp);
1676 if (digested)
1677 MD5Update (&mdContext, b1, 1);
1678 if (skip < 2) {
1679 putc ((char) *b2, ce->ce_fp);
1680 if (digested)
1681 MD5Update (&mdContext, b2, 1);
1682 if (skip < 1) {
1683 putc ((char) *b3, ce->ce_fp);
1684 if (digested)
1685 MD5Update (&mdContext, b3, 1);
1686 }
1687 }
1688
1689 if (ferror (ce->ce_fp)) {
1690 content_error (ce->ce_file, ct,
1691 "error writing to");
1692 goto clean_up;
1693 }
1694 bitno = 18, bits = 0L, skip = 0;
1695 }
1696 break;
1697
1698 case '=':
1699 if (++skip > 3)
1700 goto self_delimiting;
1701 goto test_end;
1702 }
1703 }
1704 }
1705 }
1706
1707 if (bitno != 18) {
1708 if (debugsw)
1709 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1710
1711 content_error (NULL, ct, "invalid BASE64 encoding");
1712 goto clean_up;
1713 }
1714
1715 self_delimiting:
1716 fseek (ct->c_fp, 0L, SEEK_SET);
1717
1718 if (fflush (ce->ce_fp)) {
1719 content_error (ce->ce_file, ct, "error writing to");
1720 goto clean_up;
1721 }
1722
1723 if (digested) {
1724 unsigned char digest[16];
1725
1726 MD5Final (digest, &mdContext);
1727 if (memcmp((char *) digest, (char *) ct->c_digest,
1728 sizeof(digest) / sizeof(digest[0])))
1729 content_error (NULL, ct,
1730 "content integrity suspect (digest mismatch) -- continuing");
1731 else
1732 if (debugsw)
1733 fprintf (stderr, "content integrity confirmed\n");
1734 }
1735
1736 fseek (ce->ce_fp, 0L, SEEK_SET);
1737
1738 ready_to_go:
1739 *file = ce->ce_file;
1740 return fileno (ce->ce_fp);
1741
1742 clean_up:
1743 free_encoding (ct, 0);
1744 return NOTOK;
1745 }
1746
1747
1748 /*
1749 * QUOTED PRINTABLE
1750 */
1751
1752 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1758 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1759 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1760 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1761 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1762 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1763 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1767 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1768 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1769 };
1770
1771
1772 static int
1773 InitQuoted (CT ct)
1774 {
1775 return init_encoding (ct, openQuoted);
1776 }
1777
1778
1779 static int
1780 openQuoted (CT ct, char **file)
1781 {
1782 int cc, digested, len, quoted;
1783 char *cp, *ep;
1784 char buffer[BUFSIZ];
1785 unsigned char mask;
1786 CE ce;
1787 /* sbeck -- handle prefixes */
1788 CI ci;
1789 MD5_CTX mdContext;
1790
1791 ce = ct->c_cefile;
1792 if (ce->ce_fp) {
1793 fseek (ce->ce_fp, 0L, SEEK_SET);
1794 goto ready_to_go;
1795 }
1796
1797 if (ce->ce_file) {
1798 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1799 content_error (ce->ce_file, ct, "unable to fopen for reading");
1800 return NOTOK;
1801 }
1802 goto ready_to_go;
1803 }
1804
1805 if (*file == NULL) {
1806 ce->ce_file = add (m_scratch ("", tmp), NULL);
1807 ce->ce_unlink = 1;
1808 } else {
1809 ce->ce_file = add (*file, NULL);
1810 ce->ce_unlink = 0;
1811 }
1812
1813 /* sbeck@cise.ufl.edu -- handle suffixes */
1814 ci = &ct->c_ctinfo;
1815 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1816 invo_name, ci->ci_type, ci->ci_subtype);
1817 cp = context_find (buffer);
1818 if (cp == NULL || *cp == '\0') {
1819 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1820 ci->ci_type);
1821 cp = context_find (buffer);
1822 }
1823 if (cp != NULL && *cp != '\0')
1824 ce->ce_file = add (cp, ce->ce_file);
1825
1826 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1827 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1828 return NOTOK;
1829 }
1830
1831 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1832 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1833 return NOTOK;
1834 }
1835
1836 if ((len = ct->c_end - ct->c_begin) < 0)
1837 adios (NULL, "internal error(2)");
1838
1839 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1840 content_error (ct->c_file, ct, "unable to open for reading");
1841 return NOTOK;
1842 }
1843
1844 if ((digested = ct->c_digested))
1845 MD5Init (&mdContext);
1846
1847 quoted = 0;
1848 #ifdef lint
1849 mask = 0;
1850 #endif
1851
1852 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1853 while (len > 0) {
1854 char *dp;
1855
1856 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1857 content_error (NULL, ct, "premature eof");
1858 goto clean_up;
1859 }
1860
1861 if ((cc = strlen (buffer)) > len)
1862 cc = len;
1863 len -= cc;
1864
1865 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1866 if (!isspace (*ep))
1867 break;
1868 *++ep = '\n', ep++;
1869
1870 for (; cp < ep; cp++) {
1871 if (quoted) {
1872 if (quoted > 1) {
1873 if (!isxdigit (*cp)) {
1874 invalid_hex:
1875 dp = "expecting hexidecimal-digit";
1876 goto invalid_encoding;
1877 }
1878 mask <<= 4;
1879 mask |= hex2nib[*cp & 0x7f];
1880 putc (mask, ce->ce_fp);
1881 if (digested)
1882 MD5Update (&mdContext, &mask, 1);
1883 } else {
1884 switch (*cp) {
1885 case ':':
1886 putc (*cp, ce->ce_fp);
1887 if (digested)
1888 MD5Update (&mdContext, (unsigned char *) ":", 1);
1889 break;
1890
1891 default:
1892 if (!isxdigit (*cp))
1893 goto invalid_hex;
1894 mask = hex2nib[*cp & 0x7f];
1895 quoted = 2;
1896 continue;
1897 }
1898 }
1899
1900 if (ferror (ce->ce_fp)) {
1901 content_error (ce->ce_file, ct, "error writing to");
1902 goto clean_up;
1903 }
1904 quoted = 0;
1905 continue;
1906 }
1907
1908 switch (*cp) {
1909 default:
1910 if (*cp < '!' || *cp > '~') {
1911 int i;
1912 dp = "expecting character in range [!..~]";
1913
1914 invalid_encoding:
1915 i = strlen (invo_name) + 2;
1916 content_error (NULL, ct,
1917 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1918 dp, i, i, "", *cp);
1919 goto clean_up;
1920 }
1921 /* and fall...*/
1922 case ' ':
1923 case '\t':
1924 case '\n':
1925 putc (*cp, ce->ce_fp);
1926 if (digested) {
1927 if (*cp == '\n')
1928 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1929 else
1930 MD5Update (&mdContext, (unsigned char *) cp, 1);
1931 }
1932 if (ferror (ce->ce_fp)) {
1933 content_error (ce->ce_file, ct, "error writing to");
1934 goto clean_up;
1935 }
1936 break;
1937
1938 case '=':
1939 if (*++cp != '\n') {
1940 quoted = 1;
1941 cp--;
1942 }
1943 break;
1944 }
1945 }
1946 }
1947 if (quoted) {
1948 content_error (NULL, ct,
1949 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1950 goto clean_up;
1951 }
1952
1953 fseek (ct->c_fp, 0L, SEEK_SET);
1954
1955 if (fflush (ce->ce_fp)) {
1956 content_error (ce->ce_file, ct, "error writing to");
1957 goto clean_up;
1958 }
1959
1960 if (digested) {
1961 unsigned char digest[16];
1962
1963 MD5Final (digest, &mdContext);
1964 if (memcmp((char *) digest, (char *) ct->c_digest,
1965 sizeof(digest) / sizeof(digest[0])))
1966 content_error (NULL, ct,
1967 "content integrity suspect (digest mismatch) -- continuing");
1968 else
1969 if (debugsw)
1970 fprintf (stderr, "content integrity confirmed\n");
1971 }
1972
1973 fseek (ce->ce_fp, 0L, SEEK_SET);
1974
1975 ready_to_go:
1976 *file = ce->ce_file;
1977 return fileno (ce->ce_fp);
1978
1979 clean_up:
1980 free_encoding (ct, 0);
1981 return NOTOK;
1982 }
1983
1984
1985 /*
1986 * 7BIT
1987 */
1988
1989 static int
1990 Init7Bit (CT ct)
1991 {
1992 if (init_encoding (ct, open7Bit) == NOTOK)
1993 return NOTOK;
1994
1995 ct->c_cesizefnx = NULL; /* no need to decode for real size */
1996 return OK;
1997 }
1998
1999
2000 static int
2001 open7Bit (CT ct, char **file)
2002 {
2003 int cc, fd, len;
2004 char buffer[BUFSIZ];
2005 /* sbeck -- handle prefixes */
2006 char *cp;
2007 CI ci;
2008 CE ce;
2009
2010 ce = ct->c_cefile;
2011 if (ce->ce_fp) {
2012 fseek (ce->ce_fp, 0L, SEEK_SET);
2013 goto ready_to_go;
2014 }
2015
2016 if (ce->ce_file) {
2017 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2018 content_error (ce->ce_file, ct, "unable to fopen for reading");
2019 return NOTOK;
2020 }
2021 goto ready_to_go;
2022 }
2023
2024 if (*file == NULL) {
2025 ce->ce_file = add (m_scratch ("", tmp), NULL);
2026 ce->ce_unlink = 1;
2027 } else {
2028 ce->ce_file = add (*file, NULL);
2029 ce->ce_unlink = 0;
2030 }
2031
2032 /* sbeck@cise.ufl.edu -- handle suffixes */
2033 ci = &ct->c_ctinfo;
2034 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2035 invo_name, ci->ci_type, ci->ci_subtype);
2036 cp = context_find (buffer);
2037 if (cp == NULL || *cp == '\0') {
2038 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2039 ci->ci_type);
2040 cp = context_find (buffer);
2041 }
2042 if (cp != NULL && *cp != '\0')
2043 ce->ce_file = add (cp, ce->ce_file);
2044
2045 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2046 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2047 return NOTOK;
2048 }
2049
2050 if (ct->c_type == CT_MULTIPART) {
2051 char **ap, **ep;
2052 CI ci = &ct->c_ctinfo;
2053
2054 len = 0;
2055 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2056 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2057 + 1 + strlen (ci->ci_subtype);
2058 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2059 putc (';', ce->ce_fp);
2060 len++;
2061
2062 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2063
2064 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2065 fputs ("\n\t", ce->ce_fp);
2066 len = 8;
2067 } else {
2068 putc (' ', ce->ce_fp);
2069 len++;
2070 }
2071 fprintf (ce->ce_fp, "%s", buffer);
2072 len += cc;
2073 }
2074
2075 if (ci->ci_comment) {
2076 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2077 fputs ("\n\t", ce->ce_fp);
2078 len = 8;
2079 }
2080 else {
2081 putc (' ', ce->ce_fp);
2082 len++;
2083 }
2084 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2085 len += cc;
2086 }
2087 fprintf (ce->ce_fp, "\n");
2088 if (ct->c_id)
2089 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2090 if (ct->c_descr)
2091 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2092 fprintf (ce->ce_fp, "\n");
2093 }
2094
2095 if ((len = ct->c_end - ct->c_begin) < 0)
2096 adios (NULL, "internal error(3)");
2097
2098 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2099 content_error (ct->c_file, ct, "unable to open for reading");
2100 return NOTOK;
2101 }
2102
2103 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2104 while (len > 0)
2105 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2106 case NOTOK:
2107 content_error (ct->c_file, ct, "error reading from");
2108 goto clean_up;
2109
2110 case OK:
2111 content_error (NULL, ct, "premature eof");
2112 goto clean_up;
2113
2114 default:
2115 if (cc > len)
2116 cc = len;
2117 len -= cc;
2118
2119 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2120 if (ferror (ce->ce_fp)) {
2121 content_error (ce->ce_file, ct, "error writing to");
2122 goto clean_up;
2123 }
2124 }
2125
2126 fseek (ct->c_fp, 0L, SEEK_SET);
2127
2128 if (fflush (ce->ce_fp)) {
2129 content_error (ce->ce_file, ct, "error writing to");
2130 goto clean_up;
2131 }
2132
2133 fseek (ce->ce_fp, 0L, SEEK_SET);
2134
2135 ready_to_go:
2136 *file = ce->ce_file;
2137 return fileno (ce->ce_fp);
2138
2139 clean_up:
2140 free_encoding (ct, 0);
2141 return NOTOK;
2142 }
2143
2144
2145 /*
2146 * External
2147 */
2148
2149 static int
2150 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2151 {
2152 char cachefile[BUFSIZ];
2153
2154 if (ce->ce_fp) {
2155 fseek (ce->ce_fp, 0L, SEEK_SET);
2156 goto ready_already;
2157 }
2158
2159 if (ce->ce_file) {
2160 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2161 content_error (ce->ce_file, ct, "unable to fopen for reading");
2162 return NOTOK;
2163 }
2164 goto ready_already;
2165 }
2166
2167 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2168 cachefile, sizeof(cachefile)) != NOTOK) {
2169 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2170 ce->ce_file = getcpy (cachefile);
2171 ce->ce_unlink = 0;
2172 goto ready_already;
2173 } else {
2174 admonish (cachefile, "unable to fopen for reading");
2175 }
2176 }
2177
2178 return OK;
2179
2180 ready_already:
2181 *file = ce->ce_file;
2182 *fd = fileno (ce->ce_fp);
2183 return DONE;
2184 }
2185
2186 /*
2187 * File
2188 */
2189
2190 static int
2191 InitFile (CT ct)
2192 {
2193 return init_encoding (ct, openFile);
2194 }
2195
2196
2197 static int
2198 openFile (CT ct, char **file)
2199 {
2200 int fd, cachetype;
2201 char cachefile[BUFSIZ];
2202 struct exbody *e = ct->c_ctexbody;
2203 CE ce = ct->c_cefile;
2204
2205 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2206 case NOTOK:
2207 return NOTOK;
2208
2209 case OK:
2210 break;
2211
2212 case DONE:
2213 return fd;
2214 }
2215
2216 if (!e->eb_name) {
2217 content_error (NULL, ct, "missing name parameter");
2218 return NOTOK;
2219 }
2220
2221 ce->ce_file = getcpy (e->eb_name);
2222 ce->ce_unlink = 0;
2223
2224 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2225 content_error (ce->ce_file, ct, "unable to fopen for reading");
2226 return NOTOK;
2227 }
2228
2229 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2230 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2231 cachefile, sizeof(cachefile)) != NOTOK) {
2232 int mask;
2233 FILE *fp;
2234
2235 mask = umask (cachetype ? ~m_gmprot () : 0222);
2236 if ((fp = fopen (cachefile, "w"))) {
2237 int cc;
2238 char buffer[BUFSIZ];
2239 FILE *gp = ce->ce_fp;
2240
2241 fseek (gp, 0L, SEEK_SET);
2242
2243 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2244 > 0)
2245 fwrite (buffer, sizeof(*buffer), cc, fp);
2246 fflush (fp);
2247
2248 if (ferror (gp)) {
2249 admonish (ce->ce_file, "error reading");
2250 unlink (cachefile);
2251 }
2252 else
2253 if (ferror (fp)) {
2254 admonish (cachefile, "error writing");
2255 unlink (cachefile);
2256 }
2257 fclose (fp);
2258 }
2259 umask (mask);
2260 }
2261
2262 fseek (ce->ce_fp, 0L, SEEK_SET);
2263 *file = ce->ce_file;
2264 return fileno (ce->ce_fp);
2265 }
2266
2267 /*
2268 * FTP
2269 */
2270
2271 static int
2272 InitFTP (CT ct)
2273 {
2274 return init_encoding (ct, openFTP);
2275 }
2276
2277
2278 static int
2279 openFTP (CT ct, char **file)
2280 {
2281 int cachetype, caching, fd;
2282 int len, buflen;
2283 char *bp, *ftp, *user, *pass;
2284 char buffer[BUFSIZ], cachefile[BUFSIZ];
2285 struct exbody *e;
2286 CE ce;
2287 static char *username = NULL;
2288 static char *password = NULL;
2289
2290 e = ct->c_ctexbody;
2291 ce = ct->c_cefile;
2292
2293 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2294 ftp = NULL;
2295
2296 #ifndef BUILTIN_FTP
2297 if (!ftp)
2298 return NOTOK;
2299 #endif
2300
2301 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2302 case NOTOK:
2303 return NOTOK;
2304
2305 case OK:
2306 break;
2307
2308 case DONE:
2309 return fd;
2310 }
2311
2312 if (!e->eb_name || !e->eb_site) {
2313 content_error (NULL, ct, "missing %s parameter",
2314 e->eb_name ? "site": "name");
2315 return NOTOK;
2316 }
2317
2318 if (xpid) {
2319 if (xpid < 0)
2320 xpid = -xpid;
2321 pidcheck (pidwait (xpid, NOTOK));
2322 xpid = 0;
2323 }
2324
2325 /* Get the buffer ready to go */
2326 bp = buffer;
2327 buflen = sizeof(buffer);
2328
2329 /*
2330 * Construct the query message for user
2331 */
2332 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2333 len = strlen (bp);
2334 bp += len;
2335 buflen -= len;
2336
2337 if (e->eb_partno) {
2338 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2339 len = strlen (bp);
2340 bp += len;
2341 buflen -= len;
2342 }
2343
2344 snprintf (bp, buflen, "\n using %sFTP from site %s",
2345 e->eb_flags ? "anonymous " : "", e->eb_site);
2346 len = strlen (bp);
2347 bp += len;
2348 buflen -= len;
2349
2350 if (e->eb_size > 0) {
2351 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2352 len = strlen (bp);
2353 bp += len;
2354 buflen -= len;
2355 }
2356 snprintf (bp, buflen, "? ");
2357
2358 /*
2359 * Now, check the answer
2360 */
2361 if (!getanswer (buffer))
2362 return NOTOK;
2363
2364 if (e->eb_flags) {
2365 user = "anonymous";
2366 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2367 pass = buffer;
2368 } else {
2369 ruserpass (e->eb_site, &username, &password);
2370 user = username;
2371 pass = password;
2372 }
2373
2374 ce->ce_unlink = (*file == NULL);
2375 caching = 0;
2376 cachefile[0] = '\0';
2377 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2378 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2379 cachefile, sizeof(cachefile)) != NOTOK) {
2380 if (*file == NULL) {
2381 ce->ce_unlink = 0;
2382 caching = 1;
2383 }
2384 }
2385
2386 if (*file)
2387 ce->ce_file = add (*file, NULL);
2388 else if (caching)
2389 ce->ce_file = add (cachefile, NULL);
2390 else
2391 ce->ce_file = add (m_scratch ("", tmp), NULL);
2392
2393 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2394 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2395 return NOTOK;
2396 }
2397
2398 #ifdef BUILTIN_FTP
2399 if (ftp)
2400 #endif
2401 {
2402 int child_id, i, vecp;
2403 char *vec[9];
2404
2405 vecp = 0;
2406 vec[vecp++] = r1bindex (ftp, '/');
2407 vec[vecp++] = e->eb_site;
2408 vec[vecp++] = user;
2409 vec[vecp++] = pass;
2410 vec[vecp++] = e->eb_dir;
2411 vec[vecp++] = e->eb_name;
2412 vec[vecp++] = ce->ce_file,
2413 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2414 ? "ascii" : "binary";
2415 vec[vecp] = NULL;
2416
2417 fflush (stdout);
2418
2419 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2420 sleep (5);
2421 switch (child_id) {
2422 case NOTOK:
2423 adios ("fork", "unable to");
2424 /* NOTREACHED */
2425
2426 case OK:
2427 close (fileno (ce->ce_fp));
2428 execvp (ftp, vec);
2429 fprintf (stderr, "unable to exec ");
2430 perror (ftp);
2431 _exit (-1);
2432 /* NOTREACHED */
2433
2434 default:
2435 if (pidXwait (child_id, NULL)) {
2436 #ifdef BUILTIN_FTP
2437 losing_ftp:
2438 #endif
2439 username = password = NULL;
2440 ce->ce_unlink = 1;
2441 return NOTOK;
2442 }
2443 break;
2444 }
2445 }
2446 #ifdef BUILTIN_FTP
2447 else
2448 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2449 ce->ce_file,
2450 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2451 == NOTOK)
2452 goto losing_ftp;
2453 #endif
2454
2455 if (cachefile[0]) {
2456 if (caching)
2457 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2458 else {
2459 int mask;
2460 FILE *fp;
2461
2462 mask = umask (cachetype ? ~m_gmprot () : 0222);
2463 if ((fp = fopen (cachefile, "w"))) {
2464 int cc;
2465 FILE *gp = ce->ce_fp;
2466
2467 fseek (gp, 0L, SEEK_SET);
2468
2469 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2470 > 0)
2471 fwrite (buffer, sizeof(*buffer), cc, fp);
2472 fflush (fp);
2473
2474 if (ferror (gp)) {
2475 admonish (ce->ce_file, "error reading");
2476 unlink (cachefile);
2477 }
2478 else
2479 if (ferror (fp)) {
2480 admonish (cachefile, "error writing");
2481 unlink (cachefile);
2482 }
2483 fclose (fp);
2484 }
2485 umask (mask);
2486 }
2487 }
2488
2489 fseek (ce->ce_fp, 0L, SEEK_SET);
2490 *file = ce->ce_file;
2491 return fileno (ce->ce_fp);
2492 }
2493
2494
2495 /*
2496 * Mail
2497 */
2498
2499 static int
2500 InitMail (CT ct)
2501 {
2502 return init_encoding (ct, openMail);
2503 }
2504
2505
2506 static int
2507 openMail (CT ct, char **file)
2508 {
2509 int child_id, fd, i, vecp;
2510 int len, buflen;
2511 char *bp, buffer[BUFSIZ], *vec[7];
2512 struct exbody *e = ct->c_ctexbody;
2513 CE ce = ct->c_cefile;
2514
2515 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2516 case NOTOK:
2517 return NOTOK;
2518
2519 case OK:
2520 break;
2521
2522 case DONE:
2523 return fd;
2524 }
2525
2526 if (!e->eb_server) {
2527 content_error (NULL, ct, "missing server parameter");
2528 return NOTOK;
2529 }
2530
2531 if (xpid) {
2532 if (xpid < 0)
2533 xpid = -xpid;
2534 pidcheck (pidwait (xpid, NOTOK));
2535 xpid = 0;
2536 }
2537
2538 /* Get buffer ready to go */
2539 bp = buffer;
2540 buflen = sizeof(buffer);
2541
2542 /* Now, construct query message */
2543 snprintf (bp, buflen, "Retrieve content");
2544 len = strlen (bp);
2545 bp += len;
2546 buflen -= len;
2547
2548 if (e->eb_partno) {
2549 snprintf (bp, buflen, " %s", e->eb_partno);
2550 len = strlen (bp);
2551 bp += len;
2552 buflen -= len;
2553 }
2554
2555 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2556 e->eb_server,
2557 e->eb_subject ? e->eb_subject : e->eb_body);
2558
2559 /* Now, check answer */
2560 if (!getanswer (buffer))
2561 return NOTOK;
2562
2563 vecp = 0;
2564 vec[vecp++] = r1bindex (mailproc, '/');
2565 vec[vecp++] = e->eb_server;
2566 vec[vecp++] = "-subject";
2567 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2568 vec[vecp++] = "-body";
2569 vec[vecp++] = e->eb_body;
2570 vec[vecp] = NULL;
2571
2572 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2573 sleep (5);
2574 switch (child_id) {
2575 case NOTOK:
2576 advise ("fork", "unable to");
2577 return NOTOK;
2578
2579 case OK:
2580 execvp (mailproc, vec);
2581 fprintf (stderr, "unable to exec ");
2582 perror (mailproc);
2583 _exit (-1);
2584 /* NOTREACHED */
2585
2586 default:
2587 if (pidXwait (child_id, NULL) == OK)
2588 advise (NULL, "request sent");
2589 break;
2590 }
2591
2592 if (*file == NULL) {
2593 ce->ce_file = add (m_scratch ("", tmp), NULL);
2594 ce->ce_unlink = 1;
2595 } else {
2596 ce->ce_file = add (*file, NULL);
2597 ce->ce_unlink = 0;
2598 }
2599
2600 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2601 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2602 return NOTOK;
2603 }
2604
2605 if (ct->c_showproc)
2606 free (ct->c_showproc);
2607 ct->c_showproc = add ("true", NULL);
2608
2609 fseek (ce->ce_fp, 0L, SEEK_SET);
2610 *file = ce->ce_file;
2611 return fileno (ce->ce_fp);
2612 }
2613
2614
2615 static int
2616 readDigest (CT ct, char *cp)
2617 {
2618 int bitno, skip;
2619 unsigned long bits;
2620 char *bp = cp;
2621 unsigned char *dp, value, *ep;
2622 unsigned char *b, *b1, *b2, *b3;
2623
2624 b = (unsigned char *) &bits,
2625 b1 = &b[endian > 0 ? 1 : 2],
2626 b2 = &b[endian > 0 ? 2 : 1],
2627 b3 = &b[endian > 0 ? 3 : 0];
2628 bitno = 18;
2629 bits = 0L;
2630 skip = 0;
2631
2632 for (ep = (dp = ct->c_digest)
2633 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2634 switch (*cp) {
2635 default:
2636 if (skip
2637 || (*cp & 0x80)
2638 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2639 if (debugsw)
2640 fprintf (stderr, "invalid BASE64 encoding\n");
2641 return NOTOK;
2642 }
2643
2644 bits |= value << bitno;
2645 test_end:
2646 if ((bitno -= 6) < 0) {
2647 if (dp + (3 - skip) > ep)
2648 goto invalid_digest;
2649 *dp++ = *b1;
2650 if (skip < 2) {
2651 *dp++ = *b2;
2652 if (skip < 1)
2653 *dp++ = *b3;
2654 }
2655 bitno = 18;
2656 bits = 0L;
2657 skip = 0;
2658 }
2659 break;
2660
2661 case '=':
2662 if (++skip > 3)
2663 goto self_delimiting;
2664 goto test_end;
2665 }
2666 if (bitno != 18) {
2667 if (debugsw)
2668 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2669
2670 return NOTOK;
2671 }
2672 self_delimiting:
2673 if (dp != ep) {
2674 invalid_digest:
2675 if (debugsw) {
2676 while (*cp)
2677 cp++;
2678 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2679 cp - bp);
2680 }
2681
2682 return NOTOK;
2683 }
2684
2685 if (debugsw) {
2686 fprintf (stderr, "MD5 digest=");
2687 for (dp = ct->c_digest; dp < ep; dp++)
2688 fprintf (stderr, "%02x", *dp & 0xff);
2689 fprintf (stderr, "\n");
2690 }
2691
2692 return OK;
2693 }