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