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