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