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