]> diplodocus.org Git - nmh/blob - uip/mhparse.c
Bring these changes over from the branch.
[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 errno;
30 extern int debugsw;
31
32 extern int endian; /* mhmisc.c */
33
34 extern pid_t xpid; /* mhshowsbr.c */
35
36 /* cache policies */
37 extern int rcachesw; /* mhcachesbr.c */
38 extern int wcachesw; /* mhcachesbr.c */
39
40 int checksw = 0; /* check Content-MD5 field */
41
42 /*
43 * Directory to place temp files. This must
44 * be set before these routines are called.
45 */
46 char *tmp;
47
48 /*
49 * Structure for mapping types to their internal flags
50 */
51 struct k2v {
52 char *kv_key;
53 int kv_value;
54 };
55
56 /*
57 * Structures for TEXT messages
58 */
59 static struct k2v SubText[] = {
60 { "plain", TEXT_PLAIN },
61 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
62 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
63 { NULL, TEXT_UNKNOWN } /* this one must be last! */
64 };
65
66 static struct k2v Charset[] = {
67 { "us-ascii", CHARSET_USASCII },
68 { "iso-8859-1", CHARSET_LATIN },
69 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
70 };
71
72 /*
73 * Structures for MULTIPART messages
74 */
75 static struct k2v SubMultiPart[] = {
76 { "mixed", MULTI_MIXED },
77 { "alternative", MULTI_ALTERNATE },
78 { "digest", MULTI_DIGEST },
79 { "parallel", MULTI_PARALLEL },
80 { NULL, MULTI_UNKNOWN } /* this one must be last! */
81 };
82
83 /*
84 * Structures for MESSAGE messages
85 */
86 static struct k2v SubMessage[] = {
87 { "rfc822", MESSAGE_RFC822 },
88 { "partial", MESSAGE_PARTIAL },
89 { "external-body", MESSAGE_EXTERNAL },
90 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
91 };
92
93 /*
94 * Structure for APPLICATION messages
95 */
96 static struct k2v SubApplication[] = {
97 { "octet-stream", APPLICATION_OCTETS },
98 { "postscript", APPLICATION_POSTSCRIPT },
99 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
100 };
101
102
103 /* ftpsbr.c */
104 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
105
106 /* mhcachesbr.c */
107 int find_cache (CT, int, int *, char *, char *, int);
108
109 /* mhmisc.c */
110 int part_ok (CT, int);
111 int type_ok (CT, int);
112 int make_intermediates (char *);
113 void content_error (char *, CT, char *, ...);
114
115 /* mhfree.c */
116 void free_content (CT);
117 void free_encoding (CT, int);
118
119 /*
120 * prototypes
121 */
122 int pidcheck (int);
123 CT parse_mime (char *);
124
125 /*
126 * static prototypes
127 */
128 static CT get_content (FILE *, char *, int);
129 static int add_header (CT, char *, char *);
130 static int get_ctinfo (char *, CT);
131 static int get_comment (CT, char **, int);
132 static int InitGeneric (CT);
133 static int InitText (CT);
134 static int InitMultiPart (CT);
135 static void reverse_parts (CT);
136 static int InitMessage (CT);
137 static int params_external (CT, int);
138 static int InitApplication (CT);
139 static int init_encoding (CT, OpenCEFunc);
140 static void close_encoding (CT);
141 static unsigned long size_encoding (CT);
142 static int InitBase64 (CT);
143 static int openBase64 (CT, char **);
144 static int InitQuoted (CT);
145 static int openQuoted (CT, char **);
146 static int Init7Bit (CT);
147 static int open7Bit (CT, char **);
148 static int openExternal (CT, CT, CE, char **, int *);
149 static int InitFile (CT);
150 static int openFile (CT, char **);
151 static int InitFTP (CT);
152 static int openFTP (CT, char **);
153 static int InitMail (CT);
154 static int openMail (CT, char **);
155 static int readDigest (CT, char *);
156
157 /*
158 * Structures for mapping (content) types to
159 * the functions to handle them.
160 */
161 struct str2init {
162 char *si_key;
163 int si_val;
164 InitFunc si_init;
165 };
166
167 static struct str2init str2cts[] = {
168 { "application", CT_APPLICATION, InitApplication },
169 { "audio", CT_AUDIO, InitGeneric },
170 { "image", CT_IMAGE, InitGeneric },
171 { "message", CT_MESSAGE, InitMessage },
172 { "multipart", CT_MULTIPART, InitMultiPart },
173 { "text", CT_TEXT, InitText },
174 { "video", CT_VIDEO, InitGeneric },
175 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
176 { NULL, CT_UNKNOWN, NULL },
177 };
178
179 static struct str2init str2ces[] = {
180 { "base64", CE_BASE64, InitBase64 },
181 { "quoted-printable", CE_QUOTED, InitQuoted },
182 { "8bit", CE_8BIT, Init7Bit },
183 { "7bit", CE_7BIT, Init7Bit },
184 { "binary", CE_BINARY, NULL },
185 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
186 { NULL, CE_UNKNOWN, NULL },
187 };
188
189 /*
190 * NOTE WELL: si_key MUST NOT have value of NOTOK
191 *
192 * si_key is 1 if access method is anonymous.
193 */
194 static struct str2init str2methods[] = {
195 { "afs", 1, InitFile },
196 { "anon-ftp", 1, InitFTP },
197 { "ftp", 0, InitFTP },
198 { "local-file", 0, InitFile },
199 { "mail-server", 0, InitMail },
200 { NULL, 0, NULL }
201 };
202
203
204 int
205 pidcheck (int status)
206 {
207 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
208 return status;
209
210 fflush (stdout);
211 fflush (stderr);
212 return done (1);
213 }
214
215
216 /*
217 * Main entry point for parsing a MIME message or file.
218 * It returns the Content structure for the top level
219 * entity in the file.
220 */
221
222 CT
223 parse_mime (char *file)
224 {
225 int is_stdin;
226 char buffer[BUFSIZ];
227 FILE *fp;
228 CT ct;
229
230 /*
231 * Check if file is actually standard input
232 */
233 if ((is_stdin = !(strcmp (file, "-")))) {
234 file = add (m_tmpfil (invo_name), NULL);
235 if ((fp = fopen (file, "w+")) == NULL) {
236 advise (file, "unable to fopen for writing and reading");
237 return NULL;
238 }
239 chmod (file, 0600);
240 while (fgets (buffer, sizeof(buffer), stdin))
241 fputs (buffer, fp);
242 fflush (fp);
243
244 if (ferror (stdin)) {
245 unlink (file);
246 advise ("stdin", "error reading");
247 return NULL;
248 }
249 if (ferror (fp)) {
250 unlink (file);
251 advise (file, "error writing");
252 return NULL;
253 }
254 fseek (fp, 0L, SEEK_SET);
255 } else if ((fp = fopen (file, "r")) == NULL) {
256 advise (file, "unable to read");
257 return NULL;
258 }
259
260 if (!(ct = get_content (fp, file, 1))) {
261 if (is_stdin)
262 unlink (file);
263 fclose (fp);
264 advise (NULL, "unable to decode %s", file);
265 return NULL;
266 }
267
268 if (is_stdin)
269 ct->c_unlink = 1; /* temp file to remove */
270
271 ct->c_fp = NULL;
272
273 if (ct->c_end == 0L) {
274 fseek (fp, 0L, SEEK_END);
275 ct->c_end = ftell (fp);
276 }
277
278 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
279 fclose (fp);
280 free_content (ct);
281 return NULL;
282 }
283
284 fclose (fp);
285 return ct;
286 }
287
288
289 /*
290 * Main routine for reading/parsing the headers
291 * of a message content.
292 *
293 * toplevel = 1 # we are at the top level of the message
294 * toplevel = 0 # we are inside message type or multipart type
295 * # other than multipart/digest
296 * toplevel = -1 # we are inside multipart/digest
297 */
298
299 static CT
300 get_content (FILE *in, char *file, int toplevel)
301 {
302 int compnum, state;
303 char buf[BUFSIZ], name[NAMESZ];
304 char *np, *vp;
305 CT ct;
306 HF hp;
307
308 /* allocate the content structure */
309 if (!(ct = (CT) calloc (1, sizeof(*ct))))
310 adios (NULL, "out of memory");
311
312 ct->c_fp = in;
313 ct->c_file = add (file, NULL);
314 ct->c_begin = ftell (ct->c_fp) + 1;
315
316 /*
317 * Parse the header fields for this
318 * content into a linked list.
319 */
320 for (compnum = 1, state = FLD;;) {
321 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
322 case FLD:
323 case FLDPLUS:
324 case FLDEOF:
325 compnum++;
326
327 /* get copies of the buffers */
328 np = add (name, NULL);
329 vp = add (buf, NULL);
330
331 /* if necessary, get rest of field */
332 while (state == FLDPLUS) {
333 state = m_getfld (state, name, buf, sizeof(buf), in);
334 vp = add (buf, vp); /* add to previous value */
335 }
336
337 /* Now add the header data to the list */
338 add_header (ct, np, vp);
339
340 /* continue, if this isn't the last header field */
341 if (state != FLDEOF) {
342 ct->c_begin = ftell (in) + 1;
343 continue;
344 }
345 /* else fall... */
346
347 case BODY:
348 case BODYEOF:
349 ct->c_begin = ftell (in) - strlen (buf);
350 break;
351
352 case FILEEOF:
353 ct->c_begin = ftell (in);
354 break;
355
356 case LENERR:
357 case FMTERR:
358 adios (NULL, "message format error in component #%d", compnum);
359
360 default:
361 adios (NULL, "getfld() returned %d", state);
362 }
363
364 /* break out of the loop */
365 break;
366 }
367
368 /*
369 * Read the content headers. We will parse the
370 * MIME related header fields into their various
371 * structures and set internal flags related to
372 * content type/subtype, etc.
373 */
374
375 hp = ct->c_first_hf; /* start at first header field */
376 while (hp) {
377 /* Get MIME-Version field */
378 if (!strcasecmp (hp->name, VRSN_FIELD)) {
379 int ucmp;
380 char c, *cp, *dp;
381
382 if (ct->c_vrsn) {
383 advise (NULL, "message %s has multiple %s: fields",
384 ct->c_file, VRSN_FIELD);
385 goto next_header;
386 }
387 ct->c_vrsn = add (hp->value, NULL);
388
389 /* Now, cleanup this field */
390 cp = ct->c_vrsn;
391
392 while (isspace (*cp))
393 cp++;
394 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
395 *dp++ = ' ';
396 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
397 if (!isspace (*dp))
398 break;
399 *++dp = '\0';
400 if (debugsw)
401 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
402
403 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
404 goto out;
405
406 for (dp = cp; istoken (*dp); dp++)
407 continue;
408 c = *dp;
409 *dp = '\0';
410 ucmp = !strcasecmp (cp, VRSN_VALUE);
411 *dp = c;
412 if (!ucmp) {
413 admonish (NULL, "message %s has unknown value for %s: field (%s)",
414 ct->c_file, VRSN_FIELD, cp);
415 }
416 }
417 else if (!strcasecmp (hp->name, TYPE_FIELD)) {
418 /* Get Content-Type field */
419 struct str2init *s2i;
420 CI ci = &ct->c_ctinfo;
421
422 /* Check if we've already seen a Content-Type header */
423 if (ct->c_ctline) {
424 advise (NULL, "message %s has multiple %s: fields",
425 ct->c_file, TYPE_FIELD);
426 goto next_header;
427 }
428
429 /* Parse the Content-Type field */
430 if (get_ctinfo (hp->value, ct) == NOTOK)
431 goto out;
432
433 /*
434 * Set the Init function and the internal
435 * flag for this content type.
436 */
437 for (s2i = str2cts; s2i->si_key; s2i++)
438 if (!strcasecmp (ci->ci_type, s2i->si_key))
439 break;
440 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
441 s2i++;
442 ct->c_type = s2i->si_val;
443 ct->c_ctinitfnx = s2i->si_init;
444 }
445 else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
446 /* Get Content-Transfer-Encoding field */
447 char c, *cp, *dp;
448 struct str2init *s2i;
449
450 /*
451 * Check if we've already seen the
452 * Content-Transfer-Encoding field
453 */
454 if (ct->c_celine) {
455 advise (NULL, "message %s has multiple %s: fields",
456 ct->c_file, ENCODING_FIELD);
457 goto next_header;
458 }
459
460 /* get copy of this field */
461 ct->c_celine = cp = add (hp->value, NULL);
462
463 while (isspace (*cp))
464 cp++;
465 for (dp = cp; istoken (*dp); dp++)
466 continue;
467 c = *dp;
468 *dp = '\0';
469
470 /*
471 * Find the internal flag and Init function
472 * for this transfer encoding.
473 */
474 for (s2i = str2ces; s2i->si_key; s2i++)
475 if (!strcasecmp (cp, s2i->si_key))
476 break;
477 if (!s2i->si_key && !uprf (cp, "X-"))
478 s2i++;
479 *dp = c;
480 ct->c_encoding = s2i->si_val;
481
482 /* Call the Init function for this encoding */
483 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
484 goto out;
485 }
486 else if (!strcasecmp (hp->name, MD5_FIELD)) {
487 /* Get Content-MD5 field */
488 char *cp, *dp, *ep;
489
490 if (!checksw)
491 goto next_header;
492
493 if (ct->c_digested) {
494 advise (NULL, "message %s has multiple %s: fields",
495 ct->c_file, MD5_FIELD);
496 goto next_header;
497 }
498
499 ep = cp = add (hp->value, NULL); /* get a copy */
500
501 while (isspace (*cp))
502 cp++;
503 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
504 *dp++ = ' ';
505 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
506 if (!isspace (*dp))
507 break;
508 *++dp = '\0';
509 if (debugsw)
510 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
511
512 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
513 free (ep);
514 goto out;
515 }
516
517 for (dp = cp; *dp && !isspace (*dp); dp++)
518 continue;
519 *dp = '\0';
520
521 readDigest (ct, cp);
522 free (ep);
523 ct->c_digested++;
524 }
525 else if (!strcasecmp (hp->name, ID_FIELD)) {
526 /* Get Content-ID field */
527 ct->c_id = add (hp->value, ct->c_id);
528 }
529 else if (!strcasecmp (hp->name, DESCR_FIELD)) {
530 /* Get Content-Description field */
531 ct->c_descr = add (hp->value, ct->c_descr);
532 }
533
534 next_header:
535 hp = hp->next; /* next header field */
536 }
537
538 /*
539 * Check if we saw a Content-Type field.
540 * If not, then assign a default value for
541 * it, and the Init function.
542 */
543 if (!ct->c_ctline) {
544 /*
545 * If we are inside a multipart/digest message,
546 * so default type is message/rfc822
547 */
548 if (toplevel < 0) {
549 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
550 goto out;
551 ct->c_type = CT_MESSAGE;
552 ct->c_ctinitfnx = InitMessage;
553 } else {
554 /*
555 * Else default type is text/plain
556 */
557 if (get_ctinfo ("text/plain", ct) == NOTOK)
558 goto out;
559 ct->c_type = CT_TEXT;
560 ct->c_ctinitfnx = InitText;
561 }
562 }
563
564 /* Use default Transfer-Encoding, if necessary */
565 if (!ct->c_celine) {
566 ct->c_encoding = CE_7BIT;
567 Init7Bit (ct);
568 }
569
570 return ct;
571
572 out:
573 free_content (ct);
574 return NULL;
575 }
576
577
578 /*
579 * small routine to add header field to list
580 */
581
582 static int
583 add_header (CT ct, char *name, char *value)
584 {
585 HF hp;
586
587 /* allocate header field structure */
588 if (!(hp = malloc (sizeof(*hp))))
589 adios (NULL, "out of memory");
590
591 /* link data into header structure */
592 hp->name = name;
593 hp->value = value;
594 hp->next = NULL;
595
596 /* link header structure into the list */
597 if (ct->c_first_hf == NULL) {
598 ct->c_first_hf = hp; /* this is the first */
599 ct->c_last_hf = hp;
600 } else {
601 ct->c_last_hf->next = hp; /* add it to the end */
602 ct->c_last_hf = hp;
603 }
604
605 return 0;
606 }
607
608
609 /*
610 * Parse Content-Type line and fill in the
611 * information of the CTinfo structure.
612 */
613
614 static int
615 get_ctinfo (char *cp, CT ct)
616 {
617 int i;
618 char *dp, **ap, **ep;
619 char c;
620 CI ci;
621
622 ci = &ct->c_ctinfo;
623 i = strlen (invo_name) + 2;
624
625 /* store copy of Content-Type line */
626 cp = ct->c_ctline = add (cp, NULL);
627
628 while (isspace (*cp)) /* trim leading spaces */
629 cp++;
630
631 /* change newlines to spaces */
632 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
633 *dp++ = ' ';
634
635 /* trim trailing spaces */
636 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
637 if (!isspace (*dp))
638 break;
639 *++dp = '\0';
640
641 if (debugsw)
642 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
643
644 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
645 return NOTOK;
646
647 for (dp = cp; istoken (*dp); dp++)
648 continue;
649 c = *dp, *dp = '\0';
650 ci->ci_type = add (cp, NULL); /* store content type */
651 *dp = c, cp = dp;
652
653 if (!*ci->ci_type) {
654 advise (NULL, "invalid %s: field in message %s (empty type)",
655 TYPE_FIELD, ct->c_file);
656 return NOTOK;
657 }
658
659 /* down case the content type string */
660 for (dp = ci->ci_type; *dp; dp++)
661 if (isalpha(*dp) && isupper (*dp))
662 *dp = tolower (*dp);
663
664 while (isspace (*cp))
665 cp++;
666
667 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
668 return NOTOK;
669
670 if (*cp != '/') {
671 ci->ci_subtype = add ("", NULL);
672 goto magic_skip;
673 }
674
675 cp++;
676 while (isspace (*cp))
677 cp++;
678
679 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
680 return NOTOK;
681
682 for (dp = cp; istoken (*dp); dp++)
683 continue;
684 c = *dp, *dp = '\0';
685 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
686 *dp = c, cp = dp;
687
688 if (!*ci->ci_subtype) {
689 advise (NULL,
690 "invalid %s: field in message %s (empty subtype for \"%s\")",
691 TYPE_FIELD, ct->c_file, ci->ci_type);
692 return NOTOK;
693 }
694
695 /* down case the content subtype string */
696 for (dp = ci->ci_subtype; *dp; dp++)
697 if (isalpha(*dp) && isupper (*dp))
698 *dp = tolower (*dp);
699
700 magic_skip:
701 while (isspace (*cp))
702 cp++;
703
704 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
705 return NOTOK;
706
707 /*
708 * Parse attribute/value pairs given with Content-Type
709 */
710 ep = (ap = ci->ci_attrs) + NPARMS;
711 while (*cp == ';') {
712 char *vp, *up;
713
714 if (ap >= ep) {
715 advise (NULL,
716 "too many parameters in message %s's %s: field (%d max)",
717 ct->c_file, TYPE_FIELD, NPARMS);
718 return NOTOK;
719 }
720
721 cp++;
722 while (isspace (*cp))
723 cp++;
724
725 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
726 return NOTOK;
727
728 if (*cp == 0) {
729 advise (NULL,
730 "extraneous trailing ';' in message %s's %s: parameter list",
731 ct->c_file, TYPE_FIELD);
732 return OK;
733 }
734
735 /* down case the attribute name */
736 for (dp = cp; istoken (*dp); dp++)
737 if (isalpha(*dp) && isupper (*dp))
738 *dp = tolower (*dp);
739
740 for (up = dp; isspace (*dp);)
741 dp++;
742 if (dp == cp || *dp != '=') {
743 advise (NULL,
744 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
745 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
746 return NOTOK;
747 }
748
749 vp = (*ap = add (cp, NULL)) + (up - cp);
750 *vp = '\0';
751 for (dp++; isspace (*dp);)
752 dp++;
753
754 /* now add the attribute value */
755 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
756
757 if (*dp == '"') {
758 for (cp = ++dp, dp = vp;;) {
759 switch (c = *cp++) {
760 case '\0':
761 bad_quote:
762 advise (NULL,
763 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
764 ct->c_file, TYPE_FIELD, i, i, "", *ap);
765 return NOTOK;
766
767 case '\\':
768 *dp++ = c;
769 if ((c = *cp++) == '\0')
770 goto bad_quote;
771 /* else fall... */
772
773 default:
774 *dp++ = c;
775 continue;
776
777 case '"':
778 *dp = '\0';
779 break;
780 }
781 break;
782 }
783 } else {
784 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
785 continue;
786 *dp = '\0';
787 }
788 if (!*vp) {
789 advise (NULL,
790 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
791 ct->c_file, TYPE_FIELD, i, i, "", *ap);
792 return NOTOK;
793 }
794 ap++;
795
796 while (isspace (*cp))
797 cp++;
798
799 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
800 return NOTOK;
801 }
802
803 /*
804 * Check if anything is left over
805 */
806 if (*cp) {
807 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
808 ct->c_file, TYPE_FIELD, i, i, "", cp);
809 }
810
811 return OK;
812 }
813
814
815 static int
816 get_comment (CT ct, char **ap, int istype)
817 {
818 int i;
819 char *bp, *cp;
820 char c, buffer[BUFSIZ], *dp;
821 CI ci;
822
823 ci = &ct->c_ctinfo;
824 cp = *ap;
825 bp = buffer;
826 cp++;
827
828 for (i = 0;;) {
829 switch (c = *cp++) {
830 case '\0':
831 invalid:
832 advise (NULL, "invalid comment in message %s's %s: field",
833 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
834 return NOTOK;
835
836 case '\\':
837 *bp++ = c;
838 if ((c = *cp++) == '\0')
839 goto invalid;
840 *bp++ = c;
841 continue;
842
843 case '(':
844 i++;
845 /* and fall... */
846 default:
847 *bp++ = c;
848 continue;
849
850 case ')':
851 if (--i < 0)
852 break;
853 *bp++ = c;
854 continue;
855 }
856 break;
857 }
858 *bp = '\0';
859
860 if (istype) {
861 if ((dp = ci->ci_comment)) {
862 ci->ci_comment = concat (dp, " ", buffer, NULL);
863 free (dp);
864 } else {
865 ci->ci_comment = add (buffer, NULL);
866 }
867 }
868
869 while (isspace (*cp))
870 cp++;
871
872 *ap = cp;
873 return OK;
874 }
875
876
877 /*
878 * CONTENTS
879 *
880 * Handles content types audio, image, and video.
881 * There's not much to do right here.
882 */
883
884 static int
885 InitGeneric (CT ct)
886 {
887 return OK; /* not much to do here */
888 }
889
890
891 /*
892 * TEXT
893 */
894
895 static int
896 InitText (CT ct)
897 {
898 char buffer[BUFSIZ];
899 char *chset;
900 char **ap, **ep, *cp;
901 struct k2v *kv;
902 struct text *t;
903 CI ci = &ct->c_ctinfo;
904
905 /* check for missing subtype */
906 if (!*ci->ci_subtype)
907 ci->ci_subtype = add ("plain", ci->ci_subtype);
908
909 /* match subtype */
910 for (kv = SubText; kv->kv_key; kv++)
911 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
912 break;
913 ct->c_subtype = kv->kv_value;
914
915 /* allocate text structure */
916 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
917 adios (NULL, "out of memory");
918 ct->c_ctparams = (void *) t;
919
920 /* scan for charset parameter */
921 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
922 if (!strcasecmp (*ap, "charset"))
923 break;
924
925 if (*ap)
926 chset = *ep;
927 else
928 chset = "US-ASCII"; /* default for text */
929
930 /* match character set, or set to unknown */
931 for (kv = Charset; kv->kv_key; kv++)
932 if (!strcasecmp (chset, kv->kv_key))
933 break;
934 t->tx_charset = kv->kv_value;
935
936 /*
937 * If we can not handle character set natively,
938 * then check profile for string to modify the
939 * terminal or display method.
940 */
941 if (!check_charset (chset, strlen (chset))) {
942 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
943 if ((cp = context_find (buffer)))
944 ct->c_termproc = getcpy (cp);
945 }
946
947 return OK;
948 }
949
950
951 /*
952 * MULTIPART
953 */
954
955 static int
956 InitMultiPart (CT ct)
957 {
958 int inout;
959 long last, pos;
960 char *cp, *dp, **ap, **ep;
961 char *bp, buffer[BUFSIZ];
962 struct multipart *m;
963 struct k2v *kv;
964 struct part *part, **next;
965 CI ci = &ct->c_ctinfo;
966 CT p;
967 FILE *fp;
968
969 /*
970 * The encoding for multipart messages must be either
971 * 7bit, 8bit, or binary (per RFC2045).
972 */
973 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
974 && ct->c_encoding != CE_BINARY) {
975 admonish (NULL,
976 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
977 ci->ci_type, ci->ci_subtype, ct->c_file);
978 return NOTOK;
979 }
980
981 /* match subtype */
982 for (kv = SubMultiPart; kv->kv_key; kv++)
983 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
984 break;
985 ct->c_subtype = kv->kv_value;
986
987 /*
988 * Check for "boundary" parameter, which is
989 * required for multipart messages.
990 */
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 }