]> diplodocus.org Git - nmh/blob - uip/mhparse.c
3ff5fd7ecaf7c3e1fb6f2f09642612283f4cf9cb
[nmh] / uip / mhparse.c
1
2 /*
3 * mhparse.c -- routines to parse the contents of MIME messages
4 *
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
8 */
9
10 #include <h/mh.h>
11 #include <fcntl.h>
12 #include <h/signals.h>
13 #include <h/md5.h>
14 #include <signal.h>
15 #include <h/mts.h>
16 #include <h/tws.h>
17 #include <h/mime.h>
18 #include <h/mhparse.h>
19 #include <h/utils.h>
20
21
22 extern int debugsw;
23
24 extern pid_t xpid; /* mhshowsbr.c */
25
26 /* cache policies */
27 extern int rcachesw; /* mhcachesbr.c */
28 extern int wcachesw; /* mhcachesbr.c */
29
30 int checksw = 0; /* check Content-MD5 field */
31
32 /*
33 * Directory to place temp files. This must
34 * be set before these routines are called.
35 */
36 char *tmp;
37
38 /*
39 * These are for mhfixmsg to:
40 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
41 * in a multipart.
42 * 2) Suppress the warning about bogus multipart content, and report it.
43 */
44 int skip_mp_cte_check;
45 int suppress_bogus_mp_content_warning;
46 int bogus_mp_content;
47
48 /*
49 * Structures for TEXT messages
50 */
51 struct k2v SubText[] = {
52 { "plain", TEXT_PLAIN },
53 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
54 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
55 { NULL, TEXT_UNKNOWN } /* this one must be last! */
56 };
57
58 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
59
60 /*
61 * Structures for MULTIPART messages
62 */
63 struct k2v SubMultiPart[] = {
64 { "mixed", MULTI_MIXED },
65 { "alternative", MULTI_ALTERNATE },
66 { "digest", MULTI_DIGEST },
67 { "parallel", MULTI_PARALLEL },
68 { NULL, MULTI_UNKNOWN } /* this one must be last! */
69 };
70
71 /*
72 * Structures for MESSAGE messages
73 */
74 struct k2v SubMessage[] = {
75 { "rfc822", MESSAGE_RFC822 },
76 { "partial", MESSAGE_PARTIAL },
77 { "external-body", MESSAGE_EXTERNAL },
78 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
79 };
80
81 /*
82 * Structure for APPLICATION messages
83 */
84 struct k2v SubApplication[] = {
85 { "octet-stream", APPLICATION_OCTETS },
86 { "postscript", APPLICATION_POSTSCRIPT },
87 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
88 };
89
90
91 /* mhcachesbr.c */
92 int find_cache (CT, int, int *, char *, char *, int);
93
94 /* mhmisc.c */
95 int part_ok (CT, int);
96 int type_ok (CT, int);
97 void content_error (char *, CT, char *, ...);
98
99 /* mhfree.c */
100 void free_encoding (CT, int);
101
102 /*
103 * static prototypes
104 */
105 static CT get_content (FILE *, char *, int);
106 static int get_comment (const char *, CI, char **, int);
107
108 static int InitGeneric (CT);
109 static int InitText (CT);
110 static int InitMultiPart (CT);
111 void reverse_parts (CT);
112 static int InitMessage (CT);
113 static int InitApplication (CT);
114 static int init_encoding (CT, OpenCEFunc);
115 static unsigned long size_encoding (CT);
116 static int InitBase64 (CT);
117 static int openBase64 (CT, char **);
118 static int InitQuoted (CT);
119 static int openQuoted (CT, char **);
120 static int Init7Bit (CT);
121 static int openExternal (CT, CT, CE, char **, int *);
122 static int InitFile (CT);
123 static int openFile (CT, char **);
124 static int InitFTP (CT);
125 static int openFTP (CT, char **);
126 static int InitMail (CT);
127 static int openMail (CT, char **);
128 static int readDigest (CT, char *);
129 static int get_leftover_mp_content (CT, int);
130 static int InitURL (CT);
131 static int openURL (CT, char **);
132
133 struct str2init str2cts[] = {
134 { "application", CT_APPLICATION, InitApplication },
135 { "audio", CT_AUDIO, InitGeneric },
136 { "image", CT_IMAGE, InitGeneric },
137 { "message", CT_MESSAGE, InitMessage },
138 { "multipart", CT_MULTIPART, InitMultiPart },
139 { "text", CT_TEXT, InitText },
140 { "video", CT_VIDEO, InitGeneric },
141 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
142 { NULL, CT_UNKNOWN, NULL },
143 };
144
145 struct str2init str2ces[] = {
146 { "base64", CE_BASE64, InitBase64 },
147 { "quoted-printable", CE_QUOTED, InitQuoted },
148 { "8bit", CE_8BIT, Init7Bit },
149 { "7bit", CE_7BIT, Init7Bit },
150 { "binary", CE_BINARY, Init7Bit },
151 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
152 { NULL, CE_UNKNOWN, NULL },
153 };
154
155 /*
156 * NOTE WELL: si_key MUST NOT have value of NOTOK
157 *
158 * si_key is 1 if access method is anonymous.
159 */
160 struct str2init str2methods[] = {
161 { "afs", 1, InitFile },
162 { "anon-ftp", 1, InitFTP },
163 { "ftp", 0, InitFTP },
164 { "local-file", 0, InitFile },
165 { "mail-server", 0, InitMail },
166 { "url", 0, InitURL },
167 { NULL, 0, NULL }
168 };
169
170
171 int
172 pidcheck (int status)
173 {
174 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
175 return status;
176
177 fflush (stdout);
178 fflush (stderr);
179 done (1);
180 return 1;
181 }
182
183
184 /*
185 * Main entry point for parsing a MIME message or file.
186 * It returns the Content structure for the top level
187 * entity in the file.
188 */
189
190 CT
191 parse_mime (char *file)
192 {
193 int is_stdin;
194 char buffer[BUFSIZ];
195 FILE *fp;
196 CT ct;
197
198 /*
199 * Check if file is actually standard input
200 */
201 if ((is_stdin = !(strcmp (file, "-")))) {
202 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
203 if (tfile == NULL) {
204 advise("mhparse", "unable to create temporary file");
205 return NULL;
206 }
207 file = add (tfile, NULL);
208 chmod (file, 0600);
209
210 while (fgets (buffer, sizeof(buffer), stdin))
211 fputs (buffer, fp);
212 fflush (fp);
213
214 if (ferror (stdin)) {
215 unlink (file);
216 advise ("stdin", "error reading");
217 return NULL;
218 }
219 if (ferror (fp)) {
220 unlink (file);
221 advise (file, "error writing");
222 return NULL;
223 }
224 fseek (fp, 0L, SEEK_SET);
225 } else if ((fp = fopen (file, "r")) == NULL) {
226 advise (file, "unable to read");
227 return NULL;
228 }
229
230 if (!(ct = get_content (fp, file, 1))) {
231 if (is_stdin)
232 unlink (file);
233 advise (NULL, "unable to decode %s", file);
234 return NULL;
235 }
236
237 if (is_stdin)
238 ct->c_unlink = 1; /* temp file to remove */
239
240 ct->c_fp = NULL;
241
242 if (ct->c_end == 0L) {
243 fseek (fp, 0L, SEEK_END);
244 ct->c_end = ftell (fp);
245 }
246
247 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
248 fclose (fp);
249 free_content (ct);
250 return NULL;
251 }
252
253 fclose (fp);
254 return ct;
255 }
256
257
258 /*
259 * Main routine for reading/parsing the headers
260 * of a message content.
261 *
262 * toplevel = 1 # we are at the top level of the message
263 * toplevel = 0 # we are inside message type or multipart type
264 * # other than multipart/digest
265 * toplevel = -1 # we are inside multipart/digest
266 * NB: on failure we will fclose(in)!
267 */
268
269 static CT
270 get_content (FILE *in, char *file, int toplevel)
271 {
272 int compnum, state;
273 char buf[BUFSIZ], name[NAMESZ];
274 char *np, *vp;
275 CT ct;
276 HF hp;
277 m_getfld_state_t gstate = 0;
278
279 /* allocate the content structure */
280 if (!(ct = (CT) calloc (1, sizeof(*ct))))
281 adios (NULL, "out of memory");
282
283 ct->c_fp = in;
284 ct->c_file = add (file, NULL);
285 ct->c_begin = ftell (ct->c_fp) + 1;
286
287 /*
288 * Parse the header fields for this
289 * content into a linked list.
290 */
291 m_getfld_track_filepos (&gstate, in);
292 for (compnum = 1;;) {
293 int bufsz = sizeof buf;
294 switch (state = m_getfld (&gstate, name, buf, &bufsz, in)) {
295 case FLD:
296 case FLDPLUS:
297 compnum++;
298
299 /* get copies of the buffers */
300 np = add (name, NULL);
301 vp = add (buf, NULL);
302
303 /* if necessary, get rest of field */
304 while (state == FLDPLUS) {
305 bufsz = sizeof buf;
306 state = m_getfld (&gstate, name, buf, &bufsz, in);
307 vp = add (buf, vp); /* add to previous value */
308 }
309
310 /* Now add the header data to the list */
311 add_header (ct, np, vp);
312
313 /* continue, to see if this isn't the last header field */
314 ct->c_begin = ftell (in) + 1;
315 continue;
316
317 case BODY:
318 ct->c_begin = ftell (in) - strlen (buf);
319 break;
320
321 case FILEEOF:
322 ct->c_begin = ftell (in);
323 break;
324
325 case LENERR:
326 case FMTERR:
327 adios (NULL, "message format error in component #%d", compnum);
328
329 default:
330 adios (NULL, "getfld() returned %d", state);
331 }
332
333 /* break out of the loop */
334 break;
335 }
336 m_getfld_state_destroy (&gstate);
337
338 /*
339 * Read the content headers. We will parse the
340 * MIME related header fields into their various
341 * structures and set internal flags related to
342 * content type/subtype, etc.
343 */
344
345 hp = ct->c_first_hf; /* start at first header field */
346 while (hp) {
347 /* Get MIME-Version field */
348 if (!strcasecmp (hp->name, VRSN_FIELD)) {
349 int ucmp;
350 char c, *cp, *dp;
351
352 if (ct->c_vrsn) {
353 advise (NULL, "message %s has multiple %s: fields",
354 ct->c_file, VRSN_FIELD);
355 goto next_header;
356 }
357 ct->c_vrsn = add (hp->value, NULL);
358
359 /* Now, cleanup this field */
360 cp = ct->c_vrsn;
361
362 while (isspace ((unsigned char) *cp))
363 cp++;
364 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
365 *dp++ = ' ';
366 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
367 if (!isspace ((unsigned char) *dp))
368 break;
369 *++dp = '\0';
370 if (debugsw)
371 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
372
373 if (*cp == '(' &&
374 get_comment (ct->c_file, &ct->c_ctinfo, &cp, 0) == NOTOK)
375 goto out;
376
377 for (dp = cp; istoken (*dp); dp++)
378 continue;
379 c = *dp;
380 *dp = '\0';
381 ucmp = !strcasecmp (cp, VRSN_VALUE);
382 *dp = c;
383 if (!ucmp) {
384 admonish (NULL, "message %s has unknown value for %s: field (%s)",
385 ct->c_file, VRSN_FIELD, cp);
386 }
387 }
388 else if (!strcasecmp (hp->name, TYPE_FIELD)) {
389 /* Get Content-Type field */
390 struct str2init *s2i;
391 CI ci = &ct->c_ctinfo;
392
393 /* Check if we've already seen a Content-Type header */
394 if (ct->c_ctline) {
395 advise (NULL, "message %s has multiple %s: fields",
396 ct->c_file, TYPE_FIELD);
397 goto next_header;
398 }
399
400 /* Parse the Content-Type field */
401 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
402 goto out;
403
404 /*
405 * Set the Init function and the internal
406 * flag for this content type.
407 */
408 for (s2i = str2cts; s2i->si_key; s2i++)
409 if (!strcasecmp (ci->ci_type, s2i->si_key))
410 break;
411 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
412 s2i++;
413 ct->c_type = s2i->si_val;
414 ct->c_ctinitfnx = s2i->si_init;
415 }
416 else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
417 /* Get Content-Transfer-Encoding field */
418 char c, *cp, *dp;
419 struct str2init *s2i;
420
421 /*
422 * Check if we've already seen the
423 * Content-Transfer-Encoding field
424 */
425 if (ct->c_celine) {
426 advise (NULL, "message %s has multiple %s: fields",
427 ct->c_file, ENCODING_FIELD);
428 goto next_header;
429 }
430
431 /* get copy of this field */
432 ct->c_celine = cp = add (hp->value, NULL);
433
434 while (isspace ((unsigned char) *cp))
435 cp++;
436 for (dp = cp; istoken (*dp); dp++)
437 continue;
438 c = *dp;
439 *dp = '\0';
440
441 /*
442 * Find the internal flag and Init function
443 * for this transfer encoding.
444 */
445 for (s2i = str2ces; s2i->si_key; s2i++)
446 if (!strcasecmp (cp, s2i->si_key))
447 break;
448 if (!s2i->si_key && !uprf (cp, "X-"))
449 s2i++;
450 *dp = c;
451 ct->c_encoding = s2i->si_val;
452
453 /* Call the Init function for this encoding */
454 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
455 goto out;
456 }
457 else if (!strcasecmp (hp->name, MD5_FIELD)) {
458 /* Get Content-MD5 field */
459 char *cp, *dp, *ep;
460
461 if (!checksw)
462 goto next_header;
463
464 if (ct->c_digested) {
465 advise (NULL, "message %s has multiple %s: fields",
466 ct->c_file, MD5_FIELD);
467 goto next_header;
468 }
469
470 ep = cp = add (hp->value, NULL); /* get a copy */
471
472 while (isspace ((unsigned char) *cp))
473 cp++;
474 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
475 *dp++ = ' ';
476 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
477 if (!isspace ((unsigned char) *dp))
478 break;
479 *++dp = '\0';
480 if (debugsw)
481 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
482
483 if (*cp == '(' &&
484 get_comment (ct->c_file, &ct->c_ctinfo, &cp, 0) == NOTOK) {
485 free (ep);
486 goto out;
487 }
488
489 for (dp = cp; *dp && !isspace ((unsigned char) *dp); dp++)
490 continue;
491 *dp = '\0';
492
493 readDigest (ct, cp);
494 free (ep);
495 ct->c_digested++;
496 }
497 else if (!strcasecmp (hp->name, ID_FIELD)) {
498 /* Get Content-ID field */
499 ct->c_id = add (hp->value, ct->c_id);
500 }
501 else if (!strcasecmp (hp->name, DESCR_FIELD)) {
502 /* Get Content-Description field */
503 ct->c_descr = add (hp->value, ct->c_descr);
504 }
505 else if (!strcasecmp (hp->name, DISPO_FIELD)) {
506 /* Get Content-Disposition field */
507 ct->c_dispo = add (hp->value, ct->c_dispo);
508 }
509
510 next_header:
511 hp = hp->next; /* next header field */
512 }
513
514 /*
515 * Check if we saw a Content-Type field.
516 * If not, then assign a default value for
517 * it, and the Init function.
518 */
519 if (!ct->c_ctline) {
520 /*
521 * If we are inside a multipart/digest message,
522 * so default type is message/rfc822
523 */
524 if (toplevel < 0) {
525 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
526 goto out;
527 ct->c_type = CT_MESSAGE;
528 ct->c_ctinitfnx = InitMessage;
529 } else {
530 /*
531 * Else default type is text/plain
532 */
533 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
534 goto out;
535 ct->c_type = CT_TEXT;
536 ct->c_ctinitfnx = InitText;
537 }
538 }
539
540 /* Use default Transfer-Encoding, if necessary */
541 if (!ct->c_celine) {
542 ct->c_encoding = CE_7BIT;
543 Init7Bit (ct);
544 }
545
546 return ct;
547
548 out:
549 free_content (ct);
550 return NULL;
551 }
552
553
554 /*
555 * small routine to add header field to list
556 */
557
558 int
559 add_header (CT ct, char *name, char *value)
560 {
561 HF hp;
562
563 /* allocate header field structure */
564 hp = mh_xmalloc (sizeof(*hp));
565
566 /* link data into header structure */
567 hp->name = name;
568 hp->value = value;
569 hp->next = NULL;
570
571 /* link header structure into the list */
572 if (ct->c_first_hf == NULL) {
573 ct->c_first_hf = hp; /* this is the first */
574 ct->c_last_hf = hp;
575 } else {
576 ct->c_last_hf->next = hp; /* add it to the end */
577 ct->c_last_hf = hp;
578 }
579
580 return 0;
581 }
582
583
584 /* Make sure that buf contains at least one appearance of name,
585 followed by =. If not, insert both name and value, just after
586 first semicolon, if any. Note that name should not contain a
587 trailing =. And quotes will be added around the value. Typical
588 usage: make sure that a Content-Disposition header contains
589 filename="foo". If it doesn't and value does, use value from
590 that. */
591 static char *
592 incl_name_value (char *buf, char *name, char *value) {
593 char *newbuf = buf;
594
595 /* Assume that name is non-null. */
596 if (buf && value) {
597 char *name_plus_equal = concat (name, "=", NULL);
598
599 if (! strstr (buf, name_plus_equal)) {
600 char *insertion;
601 char *cp, *prefix, *suffix;
602
603 /* Trim trailing space, esp. newline. */
604 for (cp = &buf[strlen (buf) - 1];
605 cp >= buf && isspace ((unsigned char) *cp);
606 --cp) {
607 *cp = '\0';
608 }
609
610 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
611
612 /* Insert at first semicolon, if any. If none, append to
613 end. */
614 prefix = add (buf, NULL);
615 if ((cp = strchr (prefix, ';'))) {
616 suffix = concat (cp, NULL);
617 *cp = '\0';
618 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
619 free (suffix);
620 } else {
621 /* Append to end. */
622 newbuf = concat (buf, insertion, "\n", NULL);
623 }
624
625 free (prefix);
626 free (insertion);
627 free (buf);
628 }
629
630 free (name_plus_equal);
631 }
632
633 return newbuf;
634 }
635
636 /* Extract just name_suffix="foo", if any, from value. If there isn't
637 one, return the entire value. Note that, for example, a name_suffix
638 of name will match filename="foo", and return foo. */
639 static char *
640 extract_name_value (char *name_suffix, char *value) {
641 char *extracted_name_value = value;
642 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
643 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
644 char *cp;
645
646 free (name_suffix_plus_quote);
647 if (name_suffix_equals) {
648 char *name_suffix_begin;
649
650 /* Find first \". */
651 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
652 name_suffix_begin = ++cp;
653 /* Find second \". */
654 for (; *cp != '"'; ++cp) /* empty */;
655
656 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
657 memcpy (extracted_name_value,
658 name_suffix_begin,
659 cp - name_suffix_begin);
660 extracted_name_value[cp - name_suffix_begin] = '\0';
661 }
662
663 return extracted_name_value;
664 }
665
666 /*
667 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
668 * directives. Fills in the information of the CTinfo structure.
669 */
670 int
671 get_ctinfo (char *cp, CT ct, int magic)
672 {
673 int i;
674 char *dp;
675 char c;
676 CI ci;
677 int status;
678
679 ci = &ct->c_ctinfo;
680 i = strlen (invo_name) + 2;
681
682 /* store copy of Content-Type line */
683 cp = ct->c_ctline = add (cp, NULL);
684
685 while (isspace ((unsigned char) *cp)) /* trim leading spaces */
686 cp++;
687
688 /* change newlines to spaces */
689 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
690 *dp++ = ' ';
691
692 /* trim trailing spaces */
693 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
694 if (!isspace ((unsigned char) *dp))
695 break;
696 *++dp = '\0';
697
698 if (debugsw)
699 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
700
701 if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
702 return NOTOK;
703
704 for (dp = cp; istoken (*dp); dp++)
705 continue;
706 c = *dp, *dp = '\0';
707 ci->ci_type = add (cp, NULL); /* store content type */
708 *dp = c, cp = dp;
709
710 if (!*ci->ci_type) {
711 advise (NULL, "invalid %s: field in message %s (empty type)",
712 TYPE_FIELD, ct->c_file);
713 return NOTOK;
714 }
715
716 /* down case the content type string */
717 for (dp = ci->ci_type; *dp; dp++)
718 if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp))
719 *dp = tolower ((unsigned char) *dp);
720
721 while (isspace ((unsigned char) *cp))
722 cp++;
723
724 if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
725 return NOTOK;
726
727 if (*cp != '/') {
728 if (!magic)
729 ci->ci_subtype = add ("", NULL);
730 goto magic_skip;
731 }
732
733 cp++;
734 while (isspace ((unsigned char) *cp))
735 cp++;
736
737 if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
738 return NOTOK;
739
740 for (dp = cp; istoken (*dp); dp++)
741 continue;
742 c = *dp, *dp = '\0';
743 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
744 *dp = c, cp = dp;
745
746 if (!*ci->ci_subtype) {
747 advise (NULL,
748 "invalid %s: field in message %s (empty subtype for \"%s\")",
749 TYPE_FIELD, ct->c_file, ci->ci_type);
750 return NOTOK;
751 }
752
753 /* down case the content subtype string */
754 for (dp = ci->ci_subtype; *dp; dp++)
755 if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp))
756 *dp = tolower ((unsigned char) *dp);
757
758 magic_skip:
759 while (isspace ((unsigned char) *cp))
760 cp++;
761
762 if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
763 return NOTOK;
764
765 if (parse_header_attrs (ct->c_file, i, &cp, ci, &status) == NOTOK) {
766 return status;
767 }
768
769 /*
770 * Get any <Content-Id> given in buffer
771 */
772 if (magic && *cp == '<') {
773 if (ct->c_id) {
774 free (ct->c_id);
775 ct->c_id = NULL;
776 }
777 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
778 advise (NULL, "invalid ID in message %s", ct->c_file);
779 return NOTOK;
780 }
781 c = *dp;
782 *dp = '\0';
783 if (*ct->c_id)
784 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
785 else
786 ct->c_id = NULL;
787 *dp++ = c;
788 cp = dp;
789
790 while (isspace ((unsigned char) *cp))
791 cp++;
792 }
793
794 /*
795 * Get any [Content-Description] given in buffer.
796 */
797 if (magic && *cp == '[') {
798 ct->c_descr = ++cp;
799 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
800 if (*dp == ']')
801 break;
802 if (dp < cp) {
803 advise (NULL, "invalid description in message %s", ct->c_file);
804 ct->c_descr = NULL;
805 return NOTOK;
806 }
807
808 c = *dp;
809 *dp = '\0';
810 if (*ct->c_descr)
811 ct->c_descr = concat (ct->c_descr, "\n", NULL);
812 else
813 ct->c_descr = NULL;
814 *dp++ = c;
815 cp = dp;
816
817 while (isspace ((unsigned char) *cp))
818 cp++;
819 }
820
821 /*
822 * Get any {Content-Disposition} given in buffer.
823 */
824 if (magic && *cp == '{') {
825 ct->c_dispo = ++cp;
826 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
827 if (*dp == '}')
828 break;
829 if (dp < cp) {
830 advise (NULL, "invalid disposition in message %s", ct->c_file);
831 ct->c_dispo = NULL;
832 return NOTOK;
833 }
834
835 c = *dp;
836 *dp = '\0';
837 if (*ct->c_dispo)
838 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
839 else
840 ct->c_dispo = NULL;
841 *dp++ = c;
842 cp = dp;
843
844 while (isspace ((unsigned char) *cp))
845 cp++;
846 }
847
848 /*
849 * Check if anything is left over
850 */
851 if (*cp) {
852 if (magic) {
853 ci->ci_magic = add (cp, NULL);
854
855 /* If there is a Content-Disposition header and it doesn't
856 have a *filename=, extract it from the magic contents.
857 The r1bindex call skips any leading directory
858 components. */
859 if (ct->c_dispo)
860 ct->c_dispo =
861 incl_name_value (ct->c_dispo,
862 "filename",
863 r1bindex (extract_name_value ("name",
864 ci->
865 ci_magic),
866 '/'));
867 }
868 else
869 advise (NULL,
870 "extraneous information in message %s's %s: field\n%*.*s(%s)",
871 ct->c_file, TYPE_FIELD, i, i, "", cp);
872 }
873
874 return OK;
875 }
876
877
878 static int
879 get_comment (const char *filename, CI ci, char **ap, int istype)
880 {
881 int i;
882 char *bp, *cp;
883 char c, buffer[BUFSIZ], *dp;
884
885 cp = *ap;
886 bp = buffer;
887 cp++;
888
889 for (i = 0;;) {
890 switch (c = *cp++) {
891 case '\0':
892 invalid:
893 advise (NULL, "invalid comment in message %s's %s: field",
894 filename, istype ? TYPE_FIELD : VRSN_FIELD);
895 return NOTOK;
896
897 case '\\':
898 *bp++ = c;
899 if ((c = *cp++) == '\0')
900 goto invalid;
901 *bp++ = c;
902 continue;
903
904 case '(':
905 i++;
906 /* and fall... */
907 default:
908 *bp++ = c;
909 continue;
910
911 case ')':
912 if (--i < 0)
913 break;
914 *bp++ = c;
915 continue;
916 }
917 break;
918 }
919 *bp = '\0';
920
921 if (istype) {
922 if ((dp = ci->ci_comment)) {
923 ci->ci_comment = concat (dp, " ", buffer, NULL);
924 free (dp);
925 } else {
926 ci->ci_comment = add (buffer, NULL);
927 }
928 }
929
930 while (isspace ((unsigned char) *cp))
931 cp++;
932
933 *ap = cp;
934 return OK;
935 }
936
937
938 /*
939 * CONTENTS
940 *
941 * Handles content types audio, image, and video.
942 * There's not much to do right here.
943 */
944
945 static int
946 InitGeneric (CT ct)
947 {
948 NMH_UNUSED (ct);
949
950 return OK; /* not much to do here */
951 }
952
953
954 /*
955 * TEXT
956 */
957
958 static int
959 InitText (CT ct)
960 {
961 char buffer[BUFSIZ];
962 char *chset = NULL;
963 char **ap, **ep, *cp;
964 struct k2v *kv;
965 struct text *t;
966 CI ci = &ct->c_ctinfo;
967
968 /* check for missing subtype */
969 if (!*ci->ci_subtype)
970 ci->ci_subtype = add ("plain", ci->ci_subtype);
971
972 /* match subtype */
973 for (kv = SubText; kv->kv_key; kv++)
974 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
975 break;
976 ct->c_subtype = kv->kv_value;
977
978 /* allocate text character set structure */
979 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
980 adios (NULL, "out of memory");
981 ct->c_ctparams = (void *) t;
982
983 /* scan for charset parameter */
984 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
985 if (!strcasecmp (*ap, "charset"))
986 break;
987
988 /* check if content specified a character set */
989 if (*ap) {
990 chset = *ep;
991 t->tx_charset = CHARSET_SPECIFIED;
992 } else {
993 t->tx_charset = CHARSET_UNSPECIFIED;
994 }
995
996 /*
997 * If we can not handle character set natively,
998 * then check profile for string to modify the
999 * terminal or display method.
1000 *
1001 * termproc is for mhshow, though mhlist -debug prints it, too.
1002 */
1003 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1004 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1005 if ((cp = context_find (buffer)))
1006 ct->c_termproc = getcpy (cp);
1007 }
1008
1009 return OK;
1010 }
1011
1012
1013 /*
1014 * MULTIPART
1015 */
1016
1017 static int
1018 InitMultiPart (CT ct)
1019 {
1020 int inout;
1021 long last, pos;
1022 char *cp, *dp, **ap, **ep;
1023 char *bp, buffer[BUFSIZ];
1024 struct multipart *m;
1025 struct k2v *kv;
1026 struct part *part, **next;
1027 CI ci = &ct->c_ctinfo;
1028 CT p;
1029 FILE *fp;
1030
1031 /*
1032 * The encoding for multipart messages must be either
1033 * 7bit, 8bit, or binary (per RFC2045).
1034 */
1035 if (! skip_mp_cte_check && ct->c_encoding != CE_7BIT &&
1036 ct->c_encoding != CE_8BIT && ct->c_encoding != CE_BINARY) {
1037 /* Copy the Content-Transfer-Encoding header field body so we can
1038 remove any trailing whitespace and leading blanks from it. */
1039 char *cte = add (ct->c_celine ? ct->c_celine : "(null)", NULL);
1040
1041 bp = cte + strlen (cte) - 1;
1042 while (bp >= cte && isspace ((unsigned char) *bp)) *bp-- = '\0';
1043 for (bp = cte; *bp && isblank ((unsigned char) *bp); ++bp) continue;
1044
1045 admonish (NULL,
1046 "\"%s/%s\" type in message %s must be encoded in\n"
1047 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1048 "is to\nmanually edit the file and change the \"%s\"\n"
1049 "Content-Transfer-Encoding to one of those. For now",
1050 ci->ci_type, ci->ci_subtype, ct->c_file, bp);
1051 free (cte);
1052
1053 return NOTOK;
1054 }
1055
1056 /* match subtype */
1057 for (kv = SubMultiPart; kv->kv_key; kv++)
1058 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1059 break;
1060 ct->c_subtype = kv->kv_value;
1061
1062 /*
1063 * Check for "boundary" parameter, which is
1064 * required for multipart messages.
1065 */
1066 bp = 0;
1067 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1068 if (!strcasecmp (*ap, "boundary")) {
1069 bp = *ep;
1070 break;
1071 }
1072 }
1073
1074 /* complain if boundary parameter is missing */
1075 if (!*ap) {
1076 advise (NULL,
1077 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1078 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1079 return NOTOK;
1080 }
1081
1082 /* allocate primary structure for multipart info */
1083 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1084 adios (NULL, "out of memory");
1085 ct->c_ctparams = (void *) m;
1086
1087 /* check if boundary parameter contains only whitespace characters */
1088 for (cp = bp; isspace ((unsigned char) *cp); cp++)
1089 continue;
1090 if (!*cp) {
1091 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1092 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1093 return NOTOK;
1094 }
1095
1096 /* remove trailing whitespace from boundary parameter */
1097 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1098 if (!isspace ((unsigned char) *dp))
1099 break;
1100 *++dp = '\0';
1101
1102 /* record boundary separators */
1103 m->mp_start = concat (bp, "\n", NULL);
1104 m->mp_stop = concat (bp, "--\n", NULL);
1105
1106 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1107 advise (ct->c_file, "unable to open for reading");
1108 return NOTOK;
1109 }
1110
1111 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1112 last = ct->c_end;
1113 next = &m->mp_parts;
1114 part = NULL;
1115 inout = 1;
1116
1117 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1118 if (pos > last)
1119 break;
1120
1121 pos += strlen (buffer);
1122 if (buffer[0] != '-' || buffer[1] != '-')
1123 continue;
1124 if (inout) {
1125 if (strcmp (buffer + 2, m->mp_start))
1126 continue;
1127 next_part:
1128 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1129 adios (NULL, "out of memory");
1130 *next = part;
1131 next = &part->mp_next;
1132
1133 if (!(p = get_content (fp, ct->c_file,
1134 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1135 ct->c_fp = NULL;
1136 return NOTOK;
1137 }
1138 p->c_fp = NULL;
1139 part->mp_part = p;
1140 pos = p->c_begin;
1141 fseek (fp, pos, SEEK_SET);
1142 inout = 0;
1143 } else {
1144 if (strcmp (buffer + 2, m->mp_start) == 0) {
1145 inout = 1;
1146 end_part:
1147 p = part->mp_part;
1148 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1149 if (p->c_end < p->c_begin)
1150 p->c_begin = p->c_end;
1151 if (inout)
1152 goto next_part;
1153 goto last_part;
1154 } else {
1155 if (strcmp (buffer + 2, m->mp_stop) == 0)
1156 goto end_part;
1157 }
1158 }
1159 }
1160
1161 if (! suppress_bogus_mp_content_warning) {
1162 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1163 }
1164 bogus_mp_content = 1;
1165
1166 if (!inout && part) {
1167 p = part->mp_part;
1168 p->c_end = ct->c_end;
1169
1170 if (p->c_begin >= p->c_end) {
1171 for (next = &m->mp_parts; *next != part;
1172 next = &((*next)->mp_next))
1173 continue;
1174 *next = NULL;
1175 free_content (p);
1176 free ((char *) part);
1177 }
1178 }
1179
1180 last_part:
1181 /* reverse the order of the parts for multipart/alternative */
1182 if (ct->c_subtype == MULTI_ALTERNATE)
1183 reverse_parts (ct);
1184
1185 /*
1186 * label all subparts with part number, and
1187 * then initialize the content of the subpart.
1188 */
1189 {
1190 int partnum;
1191 char *pp;
1192 char partnam[BUFSIZ];
1193
1194 if (ct->c_partno) {
1195 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1196 pp = partnam + strlen (partnam);
1197 } else {
1198 pp = partnam;
1199 }
1200
1201 for (part = m->mp_parts, partnum = 1; part;
1202 part = part->mp_next, partnum++) {
1203 p = part->mp_part;
1204
1205 sprintf (pp, "%d", partnum);
1206 p->c_partno = add (partnam, NULL);
1207
1208 /* initialize the content of the subparts */
1209 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1210 fclose (ct->c_fp);
1211 ct->c_fp = NULL;
1212 return NOTOK;
1213 }
1214 }
1215 }
1216
1217 get_leftover_mp_content (ct, 1);
1218 get_leftover_mp_content (ct, 0);
1219
1220 fclose (ct->c_fp);
1221 ct->c_fp = NULL;
1222 return OK;
1223 }
1224
1225
1226 /*
1227 * reverse the order of the parts of a multipart/alternative
1228 */
1229
1230 void
1231 reverse_parts (CT ct)
1232 {
1233 struct multipart *m = (struct multipart *) ct->c_ctparams;
1234 struct part *part;
1235 struct part *next;
1236
1237 /* Reverse the order of its parts by walking the mp_parts list
1238 and pushing each node to the front. */
1239 for (part = m->mp_parts, m->mp_parts = NULL; part; part = next) {
1240 next = part->mp_next;
1241 part->mp_next = m->mp_parts;
1242 m->mp_parts = part;
1243 }
1244 }
1245
1246
1247 /*
1248 * MESSAGE
1249 */
1250
1251 static int
1252 InitMessage (CT ct)
1253 {
1254 struct k2v *kv;
1255 CI ci = &ct->c_ctinfo;
1256
1257 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1258 admonish (NULL,
1259 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1260 ci->ci_type, ci->ci_subtype, ct->c_file);
1261 return NOTOK;
1262 }
1263
1264 /* check for missing subtype */
1265 if (!*ci->ci_subtype)
1266 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1267
1268 /* match subtype */
1269 for (kv = SubMessage; kv->kv_key; kv++)
1270 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1271 break;
1272 ct->c_subtype = kv->kv_value;
1273
1274 switch (ct->c_subtype) {
1275 case MESSAGE_RFC822:
1276 break;
1277
1278 case MESSAGE_PARTIAL:
1279 {
1280 char **ap, **ep;
1281 struct partial *p;
1282
1283 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1284 adios (NULL, "out of memory");
1285 ct->c_ctparams = (void *) p;
1286
1287 /* scan for parameters "id", "number", and "total" */
1288 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1289 if (!strcasecmp (*ap, "id")) {
1290 p->pm_partid = add (*ep, NULL);
1291 continue;
1292 }
1293 if (!strcasecmp (*ap, "number")) {
1294 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1295 || p->pm_partno < 1) {
1296 invalid_param:
1297 advise (NULL,
1298 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1299 *ap, ci->ci_type, ci->ci_subtype,
1300 ct->c_file, TYPE_FIELD);
1301 return NOTOK;
1302 }
1303 continue;
1304 }
1305 if (!strcasecmp (*ap, "total")) {
1306 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1307 || p->pm_maxno < 1)
1308 goto invalid_param;
1309 continue;
1310 }
1311 }
1312
1313 if (!p->pm_partid
1314 || !p->pm_partno
1315 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1316 advise (NULL,
1317 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1318 ci->ci_type, ci->ci_subtype,
1319 ct->c_file, TYPE_FIELD);
1320 return NOTOK;
1321 }
1322 }
1323 break;
1324
1325 case MESSAGE_EXTERNAL:
1326 {
1327 int exresult;
1328 struct exbody *e;
1329 CT p;
1330 FILE *fp;
1331
1332 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1333 adios (NULL, "out of memory");
1334 ct->c_ctparams = (void *) e;
1335
1336 if (!ct->c_fp
1337 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1338 advise (ct->c_file, "unable to open for reading");
1339 return NOTOK;
1340 }
1341
1342 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1343
1344 if (!(p = get_content (fp, ct->c_file, 0))) {
1345 ct->c_fp = NULL;
1346 return NOTOK;
1347 }
1348
1349 e->eb_parent = ct;
1350 e->eb_content = p;
1351 p->c_ctexbody = e;
1352 p->c_ceopenfnx = NULL;
1353 if ((exresult = params_external (ct, 0)) != NOTOK
1354 && p->c_ceopenfnx == openMail) {
1355 int cc, size;
1356 char *bp;
1357
1358 if ((size = ct->c_end - p->c_begin) <= 0) {
1359 if (!e->eb_subject)
1360 content_error (NULL, ct,
1361 "empty body for access-type=mail-server");
1362 goto no_body;
1363 }
1364
1365 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1366 fseek (p->c_fp, p->c_begin, SEEK_SET);
1367 while (size > 0)
1368 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1369 case NOTOK:
1370 adios ("failed", "fread");
1371
1372 case OK:
1373 adios (NULL, "unexpected EOF from fread");
1374
1375 default:
1376 bp += cc, size -= cc;
1377 break;
1378 }
1379 *bp = 0;
1380 }
1381 no_body:
1382 p->c_fp = NULL;
1383 p->c_end = p->c_begin;
1384
1385 fclose (ct->c_fp);
1386 ct->c_fp = NULL;
1387
1388 if (exresult == NOTOK)
1389 return NOTOK;
1390 if (e->eb_flags == NOTOK)
1391 return OK;
1392
1393 switch (p->c_type) {
1394 case CT_MULTIPART:
1395 break;
1396
1397 case CT_MESSAGE:
1398 if (p->c_subtype != MESSAGE_RFC822)
1399 break;
1400 /* else fall... */
1401 default:
1402 e->eb_partno = ct->c_partno;
1403 if (p->c_ctinitfnx)
1404 (*p->c_ctinitfnx) (p);
1405 break;
1406 }
1407 }
1408 break;
1409
1410 default:
1411 break;
1412 }
1413
1414 return OK;
1415 }
1416
1417
1418 int
1419 params_external (CT ct, int composing)
1420 {
1421 char **ap, **ep;
1422 struct exbody *e = (struct exbody *) ct->c_ctparams;
1423 CI ci = &ct->c_ctinfo;
1424
1425 ct->c_ceopenfnx = NULL;
1426 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1427 if (!strcasecmp (*ap, "access-type")) {
1428 struct str2init *s2i;
1429 CT p = e->eb_content;
1430
1431 for (s2i = str2methods; s2i->si_key; s2i++)
1432 if (!strcasecmp (*ep, s2i->si_key))
1433 break;
1434 if (!s2i->si_key) {
1435 e->eb_access = *ep;
1436 e->eb_flags = NOTOK;
1437 p->c_encoding = CE_EXTERNAL;
1438 continue;
1439 }
1440 e->eb_access = s2i->si_key;
1441 e->eb_flags = s2i->si_val;
1442 p->c_encoding = CE_EXTERNAL;
1443
1444 /* Call the Init function for this external type */
1445 if ((*s2i->si_init)(p) == NOTOK)
1446 return NOTOK;
1447 continue;
1448 }
1449 if (!strcasecmp (*ap, "name")) {
1450 e->eb_name = *ep;
1451 continue;
1452 }
1453 if (!strcasecmp (*ap, "permission")) {
1454 e->eb_permission = *ep;
1455 continue;
1456 }
1457 if (!strcasecmp (*ap, "site")) {
1458 e->eb_site = *ep;
1459 continue;
1460 }
1461 if (!strcasecmp (*ap, "directory")) {
1462 e->eb_dir = *ep;
1463 continue;
1464 }
1465 if (!strcasecmp (*ap, "mode")) {
1466 e->eb_mode = *ep;
1467 continue;
1468 }
1469 if (!strcasecmp (*ap, "size")) {
1470 sscanf (*ep, "%lu", &e->eb_size);
1471 continue;
1472 }
1473 if (!strcasecmp (*ap, "server")) {
1474 e->eb_server = *ep;
1475 continue;
1476 }
1477 if (!strcasecmp (*ap, "subject")) {
1478 e->eb_subject = *ep;
1479 continue;
1480 }
1481 if (!strcasecmp (*ap, "url")) {
1482 /*
1483 * According to RFC 2017, we have to remove all whitespace from
1484 * the URL
1485 */
1486
1487 char *u, *p = *ep;
1488 e->eb_url = u = mh_xmalloc(strlen(*ep) + 1);
1489
1490 for (; *p != '\0'; p++) {
1491 if (! isspace((unsigned char) *p))
1492 *u++ = *p;
1493 }
1494
1495 *u = '\0';
1496 continue;
1497 }
1498 if (composing && !strcasecmp (*ap, "body")) {
1499 e->eb_body = getcpy (*ep);
1500 continue;
1501 }
1502 }
1503
1504 if (!e->eb_access) {
1505 advise (NULL,
1506 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1507 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1508 return NOTOK;
1509 }
1510
1511 return OK;
1512 }
1513
1514
1515 /*
1516 * APPLICATION
1517 */
1518
1519 static int
1520 InitApplication (CT ct)
1521 {
1522 struct k2v *kv;
1523 CI ci = &ct->c_ctinfo;
1524
1525 /* match subtype */
1526 for (kv = SubApplication; kv->kv_key; kv++)
1527 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1528 break;
1529 ct->c_subtype = kv->kv_value;
1530
1531 return OK;
1532 }
1533
1534
1535 /*
1536 * TRANSFER ENCODINGS
1537 */
1538
1539 static int
1540 init_encoding (CT ct, OpenCEFunc openfnx)
1541 {
1542 ct->c_ceopenfnx = openfnx;
1543 ct->c_ceclosefnx = close_encoding;
1544 ct->c_cesizefnx = size_encoding;
1545
1546 return OK;
1547 }
1548
1549
1550 void
1551 close_encoding (CT ct)
1552 {
1553 CE ce = &ct->c_cefile;
1554
1555 if (ce->ce_fp) {
1556 fclose (ce->ce_fp);
1557 ce->ce_fp = NULL;
1558 }
1559 }
1560
1561
1562 static unsigned long
1563 size_encoding (CT ct)
1564 {
1565 int fd;
1566 unsigned long size;
1567 char *file;
1568 CE ce = &ct->c_cefile;
1569 struct stat st;
1570
1571 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1572 return (long) st.st_size;
1573
1574 if (ce->ce_file) {
1575 if (stat (ce->ce_file, &st) != NOTOK)
1576 return (long) st.st_size;
1577 else
1578 return 0L;
1579 }
1580
1581 if (ct->c_encoding == CE_EXTERNAL)
1582 return (ct->c_end - ct->c_begin);
1583
1584 file = NULL;
1585 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1586 return (ct->c_end - ct->c_begin);
1587
1588 if (fstat (fd, &st) != NOTOK)
1589 size = (long) st.st_size;
1590 else
1591 size = 0L;
1592
1593 (*ct->c_ceclosefnx) (ct);
1594 return size;
1595 }
1596
1597
1598 /*
1599 * BASE64
1600 */
1601
1602 static unsigned char b642nib[0x80] = {
1603 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1604 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1605 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1606 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1607 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1608 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1609 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1610 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1611 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1612 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1613 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1614 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1615 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1616 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1617 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1618 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1619 };
1620
1621
1622 static int
1623 InitBase64 (CT ct)
1624 {
1625 return init_encoding (ct, openBase64);
1626 }
1627
1628
1629 static int
1630 openBase64 (CT ct, char **file)
1631 {
1632 int bitno, cc, digested;
1633 int fd, len, skip, own_ct_fp = 0;
1634 uint32_t bits;
1635 unsigned char value, b;
1636 char *cp, *ep, buffer[BUFSIZ];
1637 /* sbeck -- handle suffixes */
1638 CI ci;
1639 CE ce = &ct->c_cefile;
1640 MD5_CTX mdContext;
1641
1642 if (ce->ce_fp) {
1643 fseek (ce->ce_fp, 0L, SEEK_SET);
1644 goto ready_to_go;
1645 }
1646
1647 if (ce->ce_file) {
1648 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1649 content_error (ce->ce_file, ct, "unable to fopen for reading");
1650 return NOTOK;
1651 }
1652 goto ready_to_go;
1653 }
1654
1655 if (*file == NULL) {
1656 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1657 ce->ce_unlink = 1;
1658 } else {
1659 ce->ce_file = add (*file, NULL);
1660 ce->ce_unlink = 0;
1661 }
1662
1663 /* sbeck@cise.ufl.edu -- handle suffixes */
1664 ci = &ct->c_ctinfo;
1665 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1666 invo_name, ci->ci_type, ci->ci_subtype);
1667 cp = context_find (buffer);
1668 if (cp == NULL || *cp == '\0') {
1669 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1670 ci->ci_type);
1671 cp = context_find (buffer);
1672 }
1673 if (cp != NULL && *cp != '\0') {
1674 if (ce->ce_unlink) {
1675 /* Temporary file already exists, so we rename to
1676 version with extension. */
1677 char *file_org = strdup(ce->ce_file);
1678 ce->ce_file = add (cp, ce->ce_file);
1679 if (rename(file_org, ce->ce_file)) {
1680 adios (ce->ce_file, "unable to rename %s to ", file_org);
1681 }
1682 free(file_org);
1683
1684 } else {
1685 ce->ce_file = add (cp, ce->ce_file);
1686 }
1687 }
1688
1689 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1690 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1691 return NOTOK;
1692 }
1693
1694 if ((len = ct->c_end - ct->c_begin) < 0)
1695 adios (NULL, "internal error(1)");
1696
1697 if (! ct->c_fp) {
1698 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1699 content_error (ct->c_file, ct, "unable to open for reading");
1700 return NOTOK;
1701 }
1702 own_ct_fp = 1;
1703 }
1704
1705 if ((digested = ct->c_digested))
1706 MD5Init (&mdContext);
1707
1708 bitno = 18;
1709 bits = 0L;
1710 skip = 0;
1711
1712 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1713 while (len > 0) {
1714 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1715 case NOTOK:
1716 content_error (ct->c_file, ct, "error reading from");
1717 goto clean_up;
1718
1719 case OK:
1720 content_error (NULL, ct, "premature eof");
1721 goto clean_up;
1722
1723 default:
1724 if (cc > len)
1725 cc = len;
1726 len -= cc;
1727
1728 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1729 switch (*cp) {
1730 default:
1731 if (isspace ((unsigned char) *cp))
1732 break;
1733 if (skip || (((unsigned char) *cp) & 0x80)
1734 || (value = b642nib[((unsigned char) *cp) & 0x7f]) > 0x3f) {
1735 if (debugsw) {
1736 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1737 (unsigned char) *cp,
1738 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1739 skip);
1740 }
1741 content_error (NULL, ct,
1742 "invalid BASE64 encoding -- continuing");
1743 continue;
1744 }
1745
1746 bits |= value << bitno;
1747 test_end:
1748 if ((bitno -= 6) < 0) {
1749 b = (bits >> 16) & 0xff;
1750 putc ((char) b, ce->ce_fp);
1751 if (digested)
1752 MD5Update (&mdContext, &b, 1);
1753 if (skip < 2) {
1754 b = (bits >> 8) & 0xff;
1755 putc ((char) b, ce->ce_fp);
1756 if (digested)
1757 MD5Update (&mdContext, &b, 1);
1758 if (skip < 1) {
1759 b = bits & 0xff;
1760 putc ((char) b, ce->ce_fp);
1761 if (digested)
1762 MD5Update (&mdContext, &b, 1);
1763 }
1764 }
1765
1766 if (ferror (ce->ce_fp)) {
1767 content_error (ce->ce_file, ct,
1768 "error writing to");
1769 goto clean_up;
1770 }
1771 bitno = 18, bits = 0L, skip = 0;
1772 }
1773 break;
1774
1775 case '=':
1776 if (++skip > 3)
1777 goto self_delimiting;
1778 goto test_end;
1779 }
1780 }
1781 }
1782 }
1783
1784 if (bitno != 18) {
1785 if (debugsw)
1786 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1787
1788 content_error (NULL, ct, "invalid BASE64 encoding");
1789 goto clean_up;
1790 }
1791
1792 self_delimiting:
1793 fseek (ct->c_fp, 0L, SEEK_SET);
1794
1795 if (fflush (ce->ce_fp)) {
1796 content_error (ce->ce_file, ct, "error writing to");
1797 goto clean_up;
1798 }
1799
1800 if (digested) {
1801 unsigned char digest[16];
1802
1803 MD5Final (digest, &mdContext);
1804 if (memcmp((char *) digest, (char *) ct->c_digest,
1805 sizeof(digest) / sizeof(digest[0])))
1806 content_error (NULL, ct,
1807 "content integrity suspect (digest mismatch) -- continuing");
1808 else
1809 if (debugsw)
1810 fprintf (stderr, "content integrity confirmed\n");
1811 }
1812
1813 fseek (ce->ce_fp, 0L, SEEK_SET);
1814
1815 ready_to_go:
1816 *file = ce->ce_file;
1817 if (own_ct_fp) {
1818 fclose (ct->c_fp);
1819 ct->c_fp = NULL;
1820 }
1821 return fileno (ce->ce_fp);
1822
1823 clean_up:
1824 if (own_ct_fp) {
1825 fclose (ct->c_fp);
1826 ct->c_fp = NULL;
1827 }
1828 free_encoding (ct, 0);
1829 return NOTOK;
1830 }
1831
1832
1833 /*
1834 * QUOTED PRINTABLE
1835 */
1836
1837 static char hex2nib[0x80] = {
1838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1839 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1840 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1841 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1842 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1843 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1844 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1845 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1846 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1847 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1848 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1849 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1850 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1851 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1852 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1853 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1854 };
1855
1856
1857 static int
1858 InitQuoted (CT ct)
1859 {
1860 return init_encoding (ct, openQuoted);
1861 }
1862
1863
1864 static int
1865 openQuoted (CT ct, char **file)
1866 {
1867 int cc, digested, len, quoted, own_ct_fp = 0;
1868 char *cp, *ep;
1869 char buffer[BUFSIZ];
1870 unsigned char mask;
1871 CE ce = &ct->c_cefile;
1872 /* sbeck -- handle suffixes */
1873 CI ci;
1874 MD5_CTX mdContext;
1875
1876 if (ce->ce_fp) {
1877 fseek (ce->ce_fp, 0L, SEEK_SET);
1878 goto ready_to_go;
1879 }
1880
1881 if (ce->ce_file) {
1882 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1883 content_error (ce->ce_file, ct, "unable to fopen for reading");
1884 return NOTOK;
1885 }
1886 goto ready_to_go;
1887 }
1888
1889 if (*file == NULL) {
1890 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1891 ce->ce_unlink = 1;
1892 } else {
1893 ce->ce_file = add (*file, NULL);
1894 ce->ce_unlink = 0;
1895 }
1896
1897 /* sbeck@cise.ufl.edu -- handle suffixes */
1898 ci = &ct->c_ctinfo;
1899 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1900 invo_name, ci->ci_type, ci->ci_subtype);
1901 cp = context_find (buffer);
1902 if (cp == NULL || *cp == '\0') {
1903 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1904 ci->ci_type);
1905 cp = context_find (buffer);
1906 }
1907 if (cp != NULL && *cp != '\0') {
1908 if (ce->ce_unlink) {
1909 /* Temporary file already exists, so we rename to
1910 version with extension. */
1911 char *file_org = strdup(ce->ce_file);
1912 ce->ce_file = add (cp, ce->ce_file);
1913 if (rename(file_org, ce->ce_file)) {
1914 adios (ce->ce_file, "unable to rename %s to ", file_org);
1915 }
1916 free(file_org);
1917
1918 } else {
1919 ce->ce_file = add (cp, ce->ce_file);
1920 }
1921 }
1922
1923 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1924 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1925 return NOTOK;
1926 }
1927
1928 if ((len = ct->c_end - ct->c_begin) < 0)
1929 adios (NULL, "internal error(2)");
1930
1931 if (! ct->c_fp) {
1932 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1933 content_error (ct->c_file, ct, "unable to open for reading");
1934 return NOTOK;
1935 }
1936 own_ct_fp = 1;
1937 }
1938
1939 if ((digested = ct->c_digested))
1940 MD5Init (&mdContext);
1941
1942 quoted = 0;
1943 #ifdef lint
1944 mask = 0;
1945 #endif
1946
1947 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1948 while (len > 0) {
1949 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1950 content_error (NULL, ct, "premature eof");
1951 goto clean_up;
1952 }
1953
1954 if ((cc = strlen (buffer)) > len)
1955 cc = len;
1956 len -= cc;
1957
1958 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1959 if (!isspace ((unsigned char) *ep))
1960 break;
1961 *++ep = '\n', ep++;
1962
1963 for (; cp < ep; cp++) {
1964 if (quoted > 0) {
1965 /* in an escape sequence */
1966 if (quoted == 1) {
1967 /* at byte 1 of an escape sequence */
1968 mask = hex2nib[((unsigned char) *cp) & 0x7f];
1969 /* next is byte 2 */
1970 quoted = 2;
1971 } else {
1972 /* at byte 2 of an escape sequence */
1973 mask <<= 4;
1974 mask |= hex2nib[((unsigned char) *cp) & 0x7f];
1975 putc (mask, ce->ce_fp);
1976 if (digested)
1977 MD5Update (&mdContext, &mask, 1);
1978 if (ferror (ce->ce_fp)) {
1979 content_error (ce->ce_file, ct, "error writing to");
1980 goto clean_up;
1981 }
1982 /* finished escape sequence; next may be literal or a new
1983 * escape sequence */
1984 quoted = 0;
1985 }
1986 /* on to next byte */
1987 continue;
1988 }
1989
1990 /* not in an escape sequence */
1991 if (*cp == '=') {
1992 /* starting an escape sequence, or invalid '='? */
1993 if (cp + 1 < ep && cp[1] == '\n') {
1994 /* "=\n" soft line break, eat the \n */
1995 cp++;
1996 continue;
1997 }
1998 if (cp + 1 >= ep || cp + 2 >= ep) {
1999 /* We don't have 2 bytes left, so this is an invalid
2000 * escape sequence; just show the raw bytes (below). */
2001 } else if (isxdigit ((unsigned char) cp[1]) &&
2002 isxdigit ((unsigned char) cp[2])) {
2003 /* Next 2 bytes are hex digits, making this a valid escape
2004 * sequence; let's decode it (above). */
2005 quoted = 1;
2006 continue;
2007 } else {
2008 /* One or both of the next 2 is out of range, making this
2009 * an invalid escape sequence; just show the raw bytes
2010 * (below). */
2011 }
2012 }
2013
2014 /* Just show the raw byte. */
2015 putc (*cp, ce->ce_fp);
2016 if (digested) {
2017 if (*cp == '\n') {
2018 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2019 } else {
2020 MD5Update (&mdContext, (unsigned char *) cp, 1);
2021 }
2022 }
2023 if (ferror (ce->ce_fp)) {
2024 content_error (ce->ce_file, ct, "error writing to");
2025 goto clean_up;
2026 }
2027 }
2028 }
2029 if (quoted) {
2030 content_error (NULL, ct,
2031 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2032 goto clean_up;
2033 }
2034
2035 fseek (ct->c_fp, 0L, SEEK_SET);
2036
2037 if (fflush (ce->ce_fp)) {
2038 content_error (ce->ce_file, ct, "error writing to");
2039 goto clean_up;
2040 }
2041
2042 if (digested) {
2043 unsigned char digest[16];
2044
2045 MD5Final (digest, &mdContext);
2046 if (memcmp((char *) digest, (char *) ct->c_digest,
2047 sizeof(digest) / sizeof(digest[0])))
2048 content_error (NULL, ct,
2049 "content integrity suspect (digest mismatch) -- continuing");
2050 else
2051 if (debugsw)
2052 fprintf (stderr, "content integrity confirmed\n");
2053 }
2054
2055 fseek (ce->ce_fp, 0L, SEEK_SET);
2056
2057 ready_to_go:
2058 *file = ce->ce_file;
2059 if (own_ct_fp) {
2060 fclose (ct->c_fp);
2061 ct->c_fp = NULL;
2062 }
2063 return fileno (ce->ce_fp);
2064
2065 clean_up:
2066 free_encoding (ct, 0);
2067 if (own_ct_fp) {
2068 fclose (ct->c_fp);
2069 ct->c_fp = NULL;
2070 }
2071 return NOTOK;
2072 }
2073
2074
2075 /*
2076 * 7BIT
2077 */
2078
2079 static int
2080 Init7Bit (CT ct)
2081 {
2082 if (init_encoding (ct, open7Bit) == NOTOK)
2083 return NOTOK;
2084
2085 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2086 return OK;
2087 }
2088
2089
2090 int
2091 open7Bit (CT ct, char **file)
2092 {
2093 int cc, fd, len, own_ct_fp = 0;
2094 char buffer[BUFSIZ];
2095 /* sbeck -- handle suffixes */
2096 char *cp;
2097 CI ci;
2098 CE ce = &ct->c_cefile;
2099
2100 if (ce->ce_fp) {
2101 fseek (ce->ce_fp, 0L, SEEK_SET);
2102 goto ready_to_go;
2103 }
2104
2105 if (ce->ce_file) {
2106 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2107 content_error (ce->ce_file, ct, "unable to fopen for reading");
2108 return NOTOK;
2109 }
2110 goto ready_to_go;
2111 }
2112
2113 if (*file == NULL) {
2114 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2115 ce->ce_unlink = 1;
2116 } else {
2117 ce->ce_file = add (*file, NULL);
2118 ce->ce_unlink = 0;
2119 }
2120
2121 /* sbeck@cise.ufl.edu -- handle suffixes */
2122 ci = &ct->c_ctinfo;
2123 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2124 invo_name, ci->ci_type, ci->ci_subtype);
2125 cp = context_find (buffer);
2126 if (cp == NULL || *cp == '\0') {
2127 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2128 ci->ci_type);
2129 cp = context_find (buffer);
2130 }
2131 if (cp != NULL && *cp != '\0') {
2132 if (ce->ce_unlink) {
2133 /* Temporary file already exists, so we rename to
2134 version with extension. */
2135 char *file_org = strdup(ce->ce_file);
2136 ce->ce_file = add (cp, ce->ce_file);
2137 if (rename(file_org, ce->ce_file)) {
2138 adios (ce->ce_file, "unable to rename %s to ", file_org);
2139 }
2140 free(file_org);
2141
2142 } else {
2143 ce->ce_file = add (cp, ce->ce_file);
2144 }
2145 }
2146
2147 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2148 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2149 return NOTOK;
2150 }
2151
2152 if (ct->c_type == CT_MULTIPART) {
2153 char **ap, **ep;
2154 CI ci = &ct->c_ctinfo;
2155
2156 len = 0;
2157 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2158 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2159 + 1 + strlen (ci->ci_subtype);
2160 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2161 putc (';', ce->ce_fp);
2162 len++;
2163
2164 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2165
2166 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2167 fputs ("\n\t", ce->ce_fp);
2168 len = 8;
2169 } else {
2170 putc (' ', ce->ce_fp);
2171 len++;
2172 }
2173 fprintf (ce->ce_fp, "%s", buffer);
2174 len += cc;
2175 }
2176
2177 if (ci->ci_comment) {
2178 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2179 fputs ("\n\t", ce->ce_fp);
2180 len = 8;
2181 }
2182 else {
2183 putc (' ', ce->ce_fp);
2184 len++;
2185 }
2186 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2187 len += cc;
2188 }
2189 fprintf (ce->ce_fp, "\n");
2190 if (ct->c_id)
2191 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2192 if (ct->c_descr)
2193 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2194 if (ct->c_dispo)
2195 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2196 fprintf (ce->ce_fp, "\n");
2197 }
2198
2199 if ((len = ct->c_end - ct->c_begin) < 0)
2200 adios (NULL, "internal error(3)");
2201
2202 if (! ct->c_fp) {
2203 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2204 content_error (ct->c_file, ct, "unable to open for reading");
2205 return NOTOK;
2206 }
2207 own_ct_fp = 1;
2208 }
2209
2210 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2211 while (len > 0)
2212 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2213 case NOTOK:
2214 content_error (ct->c_file, ct, "error reading from");
2215 goto clean_up;
2216
2217 case OK:
2218 content_error (NULL, ct, "premature eof");
2219 goto clean_up;
2220
2221 default:
2222 if (cc > len)
2223 cc = len;
2224 len -= cc;
2225
2226 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2227 if (ferror (ce->ce_fp)) {
2228 content_error (ce->ce_file, ct, "error writing to");
2229 goto clean_up;
2230 }
2231 }
2232
2233 fseek (ct->c_fp, 0L, SEEK_SET);
2234
2235 if (fflush (ce->ce_fp)) {
2236 content_error (ce->ce_file, ct, "error writing to");
2237 goto clean_up;
2238 }
2239
2240 fseek (ce->ce_fp, 0L, SEEK_SET);
2241
2242 ready_to_go:
2243 *file = ce->ce_file;
2244 if (own_ct_fp) {
2245 fclose (ct->c_fp);
2246 ct->c_fp = NULL;
2247 }
2248 return fileno (ce->ce_fp);
2249
2250 clean_up:
2251 free_encoding (ct, 0);
2252 if (own_ct_fp) {
2253 fclose (ct->c_fp);
2254 ct->c_fp = NULL;
2255 }
2256 return NOTOK;
2257 }
2258
2259
2260 /*
2261 * External
2262 */
2263
2264 static int
2265 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2266 {
2267 char cachefile[BUFSIZ];
2268
2269 if (ce->ce_fp) {
2270 fseek (ce->ce_fp, 0L, SEEK_SET);
2271 goto ready_already;
2272 }
2273
2274 if (ce->ce_file) {
2275 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2276 content_error (ce->ce_file, ct, "unable to fopen for reading");
2277 return NOTOK;
2278 }
2279 goto ready_already;
2280 }
2281
2282 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2283 cachefile, sizeof(cachefile)) != NOTOK) {
2284 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2285 ce->ce_file = getcpy (cachefile);
2286 ce->ce_unlink = 0;
2287 goto ready_already;
2288 } else {
2289 admonish (cachefile, "unable to fopen for reading");
2290 }
2291 }
2292
2293 return OK;
2294
2295 ready_already:
2296 *file = ce->ce_file;
2297 *fd = fileno (ce->ce_fp);
2298 return DONE;
2299 }
2300
2301 /*
2302 * File
2303 */
2304
2305 static int
2306 InitFile (CT ct)
2307 {
2308 return init_encoding (ct, openFile);
2309 }
2310
2311
2312 static int
2313 openFile (CT ct, char **file)
2314 {
2315 int fd, cachetype;
2316 char cachefile[BUFSIZ];
2317 struct exbody *e = ct->c_ctexbody;
2318 CE ce = &ct->c_cefile;
2319
2320 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2321 case NOTOK:
2322 return NOTOK;
2323
2324 case OK:
2325 break;
2326
2327 case DONE:
2328 return fd;
2329 }
2330
2331 if (!e->eb_name) {
2332 content_error (NULL, ct, "missing name parameter");
2333 return NOTOK;
2334 }
2335
2336 ce->ce_file = getcpy (e->eb_name);
2337 ce->ce_unlink = 0;
2338
2339 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2340 content_error (ce->ce_file, ct, "unable to fopen for reading");
2341 return NOTOK;
2342 }
2343
2344 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2345 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2346 cachefile, sizeof(cachefile)) != NOTOK) {
2347 int mask;
2348 FILE *fp;
2349
2350 mask = umask (cachetype ? ~m_gmprot () : 0222);
2351 if ((fp = fopen (cachefile, "w"))) {
2352 int cc;
2353 char buffer[BUFSIZ];
2354 FILE *gp = ce->ce_fp;
2355
2356 fseek (gp, 0L, SEEK_SET);
2357
2358 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2359 > 0)
2360 fwrite (buffer, sizeof(*buffer), cc, fp);
2361 fflush (fp);
2362
2363 if (ferror (gp)) {
2364 admonish (ce->ce_file, "error reading");
2365 unlink (cachefile);
2366 }
2367 else
2368 if (ferror (fp)) {
2369 admonish (cachefile, "error writing");
2370 unlink (cachefile);
2371 }
2372 fclose (fp);
2373 }
2374 umask (mask);
2375 }
2376
2377 fseek (ce->ce_fp, 0L, SEEK_SET);
2378 *file = ce->ce_file;
2379 return fileno (ce->ce_fp);
2380 }
2381
2382 /*
2383 * FTP
2384 */
2385
2386 static int
2387 InitFTP (CT ct)
2388 {
2389 return init_encoding (ct, openFTP);
2390 }
2391
2392
2393 static int
2394 openFTP (CT ct, char **file)
2395 {
2396 int cachetype, caching, fd;
2397 int len, buflen;
2398 char *bp, *ftp, *user, *pass;
2399 char buffer[BUFSIZ], cachefile[BUFSIZ];
2400 struct exbody *e;
2401 CE ce = &ct->c_cefile;
2402 static char *username = NULL;
2403 static char *password = NULL;
2404
2405 e = ct->c_ctexbody;
2406
2407 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2408 ftp = NULL;
2409
2410 if (!ftp)
2411 return NOTOK;
2412
2413 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2414 case NOTOK:
2415 return NOTOK;
2416
2417 case OK:
2418 break;
2419
2420 case DONE:
2421 return fd;
2422 }
2423
2424 if (!e->eb_name || !e->eb_site) {
2425 content_error (NULL, ct, "missing %s parameter",
2426 e->eb_name ? "site": "name");
2427 return NOTOK;
2428 }
2429
2430 if (xpid) {
2431 if (xpid < 0)
2432 xpid = -xpid;
2433 pidcheck (pidwait (xpid, NOTOK));
2434 xpid = 0;
2435 }
2436
2437 /* Get the buffer ready to go */
2438 bp = buffer;
2439 buflen = sizeof(buffer);
2440
2441 /*
2442 * Construct the query message for user
2443 */
2444 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2445 len = strlen (bp);
2446 bp += len;
2447 buflen -= len;
2448
2449 if (e->eb_partno) {
2450 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2451 len = strlen (bp);
2452 bp += len;
2453 buflen -= len;
2454 }
2455
2456 snprintf (bp, buflen, "\n using %sFTP from site %s",
2457 e->eb_flags ? "anonymous " : "", e->eb_site);
2458 len = strlen (bp);
2459 bp += len;
2460 buflen -= len;
2461
2462 if (e->eb_size > 0) {
2463 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2464 len = strlen (bp);
2465 bp += len;
2466 buflen -= len;
2467 }
2468 snprintf (bp, buflen, "? ");
2469
2470 /*
2471 * Now, check the answer
2472 */
2473 if (!getanswer (buffer))
2474 return NOTOK;
2475
2476 if (e->eb_flags) {
2477 user = "anonymous";
2478 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2479 LocalName (1));
2480 pass = buffer;
2481 } else {
2482 ruserpass (e->eb_site, &username, &password);
2483 user = username;
2484 pass = password;
2485 }
2486
2487 ce->ce_unlink = (*file == NULL);
2488 caching = 0;
2489 cachefile[0] = '\0';
2490 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2491 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2492 cachefile, sizeof(cachefile)) != NOTOK) {
2493 if (*file == NULL) {
2494 ce->ce_unlink = 0;
2495 caching = 1;
2496 }
2497 }
2498
2499 if (*file)
2500 ce->ce_file = add (*file, NULL);
2501 else if (caching)
2502 ce->ce_file = add (cachefile, NULL);
2503 else
2504 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2505
2506 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2507 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2508 return NOTOK;
2509 }
2510
2511 {
2512 int child_id, i, vecp;
2513 char *vec[9];
2514
2515 vecp = 0;
2516 vec[vecp++] = r1bindex (ftp, '/');
2517 vec[vecp++] = e->eb_site;
2518 vec[vecp++] = user;
2519 vec[vecp++] = pass;
2520 vec[vecp++] = e->eb_dir;
2521 vec[vecp++] = e->eb_name;
2522 vec[vecp++] = ce->ce_file,
2523 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2524 ? "ascii" : "binary";
2525 vec[vecp] = NULL;
2526
2527 fflush (stdout);
2528
2529 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2530 sleep (5);
2531 switch (child_id) {
2532 case NOTOK:
2533 adios ("fork", "unable to");
2534 /* NOTREACHED */
2535
2536 case OK:
2537 close (fileno (ce->ce_fp));
2538 execvp (ftp, vec);
2539 fprintf (stderr, "unable to exec ");
2540 perror (ftp);
2541 _exit (-1);
2542 /* NOTREACHED */
2543
2544 default:
2545 if (pidXwait (child_id, NULL)) {
2546 username = password = NULL;
2547 ce->ce_unlink = 1;
2548 return NOTOK;
2549 }
2550 break;
2551 }
2552 }
2553
2554 if (cachefile[0]) {
2555 if (caching)
2556 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2557 else {
2558 int mask;
2559 FILE *fp;
2560
2561 mask = umask (cachetype ? ~m_gmprot () : 0222);
2562 if ((fp = fopen (cachefile, "w"))) {
2563 int cc;
2564 FILE *gp = ce->ce_fp;
2565
2566 fseek (gp, 0L, SEEK_SET);
2567
2568 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2569 > 0)
2570 fwrite (buffer, sizeof(*buffer), cc, fp);
2571 fflush (fp);
2572
2573 if (ferror (gp)) {
2574 admonish (ce->ce_file, "error reading");
2575 unlink (cachefile);
2576 }
2577 else
2578 if (ferror (fp)) {
2579 admonish (cachefile, "error writing");
2580 unlink (cachefile);
2581 }
2582 fclose (fp);
2583 }
2584 umask (mask);
2585 }
2586 }
2587
2588 fseek (ce->ce_fp, 0L, SEEK_SET);
2589 *file = ce->ce_file;
2590 return fileno (ce->ce_fp);
2591 }
2592
2593
2594 /*
2595 * Mail
2596 */
2597
2598 static int
2599 InitMail (CT ct)
2600 {
2601 return init_encoding (ct, openMail);
2602 }
2603
2604
2605 static int
2606 openMail (CT ct, char **file)
2607 {
2608 int child_id, fd, i, vecp;
2609 int len, buflen;
2610 char *bp, buffer[BUFSIZ], *vec[7];
2611 struct exbody *e = ct->c_ctexbody;
2612 CE ce = &ct->c_cefile;
2613
2614 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2615 case NOTOK:
2616 return NOTOK;
2617
2618 case OK:
2619 break;
2620
2621 case DONE:
2622 return fd;
2623 }
2624
2625 if (!e->eb_server) {
2626 content_error (NULL, ct, "missing server parameter");
2627 return NOTOK;
2628 }
2629
2630 if (xpid) {
2631 if (xpid < 0)
2632 xpid = -xpid;
2633 pidcheck (pidwait (xpid, NOTOK));
2634 xpid = 0;
2635 }
2636
2637 /* Get buffer ready to go */
2638 bp = buffer;
2639 buflen = sizeof(buffer);
2640
2641 /* Now, construct query message */
2642 snprintf (bp, buflen, "Retrieve content");
2643 len = strlen (bp);
2644 bp += len;
2645 buflen -= len;
2646
2647 if (e->eb_partno) {
2648 snprintf (bp, buflen, " %s", e->eb_partno);
2649 len = strlen (bp);
2650 bp += len;
2651 buflen -= len;
2652 }
2653
2654 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2655 e->eb_server,
2656 e->eb_subject ? e->eb_subject : e->eb_body);
2657
2658 /* Now, check answer */
2659 if (!getanswer (buffer))
2660 return NOTOK;
2661
2662 vecp = 0;
2663 vec[vecp++] = r1bindex (mailproc, '/');
2664 vec[vecp++] = e->eb_server;
2665 vec[vecp++] = "-subject";
2666 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2667 vec[vecp++] = "-body";
2668 vec[vecp++] = e->eb_body;
2669 vec[vecp] = NULL;
2670
2671 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2672 sleep (5);
2673 switch (child_id) {
2674 case NOTOK:
2675 advise ("fork", "unable to");
2676 return NOTOK;
2677
2678 case OK:
2679 execvp (mailproc, vec);
2680 fprintf (stderr, "unable to exec ");
2681 perror (mailproc);
2682 _exit (-1);
2683 /* NOTREACHED */
2684
2685 default:
2686 if (pidXwait (child_id, NULL) == OK)
2687 advise (NULL, "request sent");
2688 break;
2689 }
2690
2691 if (*file == NULL) {
2692 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2693 ce->ce_unlink = 1;
2694 } else {
2695 ce->ce_file = add (*file, NULL);
2696 ce->ce_unlink = 0;
2697 }
2698
2699 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2700 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2701 return NOTOK;
2702 }
2703
2704 /* showproc is for mhshow and mhstore, though mhlist -debug
2705 * prints it, too. */
2706 if (ct->c_showproc)
2707 free (ct->c_showproc);
2708 ct->c_showproc = add ("true", NULL);
2709
2710 fseek (ce->ce_fp, 0L, SEEK_SET);
2711 *file = ce->ce_file;
2712 return fileno (ce->ce_fp);
2713 }
2714
2715
2716 /*
2717 * URL
2718 */
2719
2720 static int
2721 InitURL (CT ct)
2722 {
2723 return init_encoding (ct, openURL);
2724 }
2725
2726
2727 static int
2728 openURL (CT ct, char **file)
2729 {
2730 struct exbody *e = ct->c_ctexbody;
2731 CE ce = &ct->c_cefile;
2732 char *urlprog, *program;
2733 char buffer[BUFSIZ], cachefile[BUFSIZ];
2734 int fd, caching, cachetype;
2735 struct msgs_array args = { 0, 0, NULL};
2736 pid_t child_id;
2737
2738 if ((urlprog = context_find(nmhaccessurl)) && *urlprog == '\0')
2739 urlprog = NULL;
2740
2741 if (! urlprog) {
2742 content_error(NULL, ct, "No entry for nmh-access-url in profile");
2743 return NOTOK;
2744 }
2745
2746 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2747 case NOTOK:
2748 return NOTOK;
2749
2750 case OK:
2751 break;
2752
2753 case DONE:
2754 return fd;
2755 }
2756
2757 if (!e->eb_url) {
2758 content_error(NULL, ct, "missing url parameter");
2759 return NOTOK;
2760 }
2761
2762 if (xpid) {
2763 if (xpid < 0)
2764 xpid = -xpid;
2765 pidcheck (pidwait (xpid, NOTOK));
2766 xpid = 0;
2767 }
2768
2769 ce->ce_unlink = (*file == NULL);
2770 caching = 0;
2771 cachefile[0] = '\0';
2772
2773 if (find_cache(NULL, wcachesw, &cachetype, e->eb_content->c_id,
2774 cachefile, sizeof(cachefile)) != NOTOK) {
2775 if (*file == NULL) {
2776 ce->ce_unlink = 0;
2777 caching = 1;
2778 }
2779 }
2780
2781 if (*file)
2782 ce->ce_file = add(*file, NULL);
2783 else if (caching)
2784 ce->ce_file = add(cachefile, NULL);
2785 else
2786 ce->ce_file = add(m_mktemp(tmp, NULL, NULL), NULL);
2787
2788 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2789 content_error(ce->ce_file, ct, "unable to fopen for read/writing");
2790 return NOTOK;
2791 }
2792
2793 switch (child_id = fork()) {
2794 case NOTOK:
2795 adios ("fork", "unable to");
2796 /* NOTREACHED */
2797
2798 case OK:
2799 argsplit_msgarg(&args, urlprog, &program);
2800 app_msgarg(&args, e->eb_url);
2801 app_msgarg(&args, NULL);
2802 dup2(fileno(ce->ce_fp), 1);
2803 close(fileno(ce->ce_fp));
2804 execvp(program, args.msgs);
2805 fprintf(stderr, "Unable to exec ");
2806 perror(program);
2807 _exit(-1);
2808 /* NOTREACHED */
2809
2810 default:
2811 if (pidXwait(child_id, NULL)) {
2812 ce->ce_unlink = 1;
2813 return NOTOK;
2814 }
2815 }
2816
2817 if (cachefile[0]) {
2818 if (caching)
2819 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2820 else {
2821 int mask;
2822 FILE *fp;
2823
2824 mask = umask (cachetype ? ~m_gmprot() : 0222);
2825 if ((fp = fopen(cachefile, "w"))) {
2826 int cc;
2827 FILE *gp = ce->ce_fp;
2828
2829 fseeko(gp, 0, SEEK_SET);
2830
2831 while ((cc = fread(buffer, sizeof(*buffer),
2832 sizeof(buffer), gp)) > 0)
2833 fwrite(buffer, sizeof(*buffer), cc, fp);
2834
2835 fflush(fp);
2836
2837 if (ferror(gp)) {
2838 admonish(ce->ce_file, "error reading");
2839 unlink(cachefile);
2840 }
2841 }
2842 umask(mask);
2843 }
2844 }
2845
2846 fseeko(ce->ce_fp, 0, SEEK_SET);
2847 *file = ce->ce_file;
2848 return fd;
2849 }
2850
2851 static int
2852 readDigest (CT ct, char *cp)
2853 {
2854 int bitno, skip;
2855 uint32_t bits;
2856 char *bp = cp;
2857 unsigned char *dp, value, *ep;
2858
2859 bitno = 18;
2860 bits = 0L;
2861 skip = 0;
2862
2863 for (ep = (dp = ct->c_digest)
2864 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2865 switch (*cp) {
2866 default:
2867 if (skip
2868 || (*cp & 0x80)
2869 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2870 if (debugsw)
2871 fprintf (stderr, "invalid BASE64 encoding\n");
2872 return NOTOK;
2873 }
2874
2875 bits |= value << bitno;
2876 test_end:
2877 if ((bitno -= 6) < 0) {
2878 if (dp + (3 - skip) > ep)
2879 goto invalid_digest;
2880 *dp++ = (bits >> 16) & 0xff;
2881 if (skip < 2) {
2882 *dp++ = (bits >> 8) & 0xff;
2883 if (skip < 1)
2884 *dp++ = bits & 0xff;
2885 }
2886 bitno = 18;
2887 bits = 0L;
2888 skip = 0;
2889 }
2890 break;
2891
2892 case '=':
2893 if (++skip > 3)
2894 goto self_delimiting;
2895 goto test_end;
2896 }
2897 if (bitno != 18) {
2898 if (debugsw)
2899 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2900
2901 return NOTOK;
2902 }
2903 self_delimiting:
2904 if (dp != ep) {
2905 invalid_digest:
2906 if (debugsw) {
2907 while (*cp)
2908 cp++;
2909 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2910 (int)(cp - bp));
2911 }
2912
2913 return NOTOK;
2914 }
2915
2916 if (debugsw) {
2917 fprintf (stderr, "MD5 digest=");
2918 for (dp = ct->c_digest; dp < ep; dp++)
2919 fprintf (stderr, "%02x", *dp & 0xff);
2920 fprintf (stderr, "\n");
2921 }
2922
2923 return OK;
2924 }
2925
2926
2927 /* Multipart parts might have content before the first subpart and/or
2928 after the last subpart that hasn't been stored anywhere else, so do
2929 that. */
2930 int
2931 get_leftover_mp_content (CT ct, int before /* or after */) {
2932 struct multipart *m = (struct multipart *) ct->c_ctparams;
2933 char *boundary;
2934 int found_boundary = 0;
2935 char buffer[BUFSIZ];
2936 int max = BUFSIZ;
2937 int read = 0;
2938 char *content = NULL;
2939
2940 if (! m) return NOTOK;
2941
2942 if (before) {
2943 if (! m->mp_parts || ! m->mp_parts->mp_part) return NOTOK;
2944
2945 /* Isolate the beginning of this part to the beginning of the
2946 first subpart and save any content between them. */
2947 fseeko (ct->c_fp, ct->c_begin, SEEK_SET);
2948 max = m->mp_parts->mp_part->c_begin - ct->c_begin;
2949 boundary = concat ("--", m->mp_start, NULL);
2950 } else {
2951 struct part *last_subpart = NULL;
2952 struct part *subpart;
2953
2954 /* Go to the last subpart to get its end position. */
2955 for (subpart = m->mp_parts; subpart; subpart = subpart->mp_next) {
2956 last_subpart = subpart;
2957 }
2958
2959 if (last_subpart == NULL) return NOTOK;
2960
2961 /* Isolate the end of the last subpart to the end of this part
2962 and save any content between them. */
2963 fseeko (ct->c_fp, last_subpart->mp_part->c_end, SEEK_SET);
2964 max = ct->c_end - last_subpart->mp_part->c_end;
2965 boundary = concat ("--", m->mp_stop, NULL);
2966 }
2967
2968 /* Back up by 1 to pick up the newline. */
2969 while (fgets (buffer, sizeof(buffer) - 1, ct->c_fp)) {
2970 read += strlen (buffer);
2971 /* Don't look beyond beginning of first subpart (before) or
2972 next part (after). */
2973 if (read > max) buffer[read-max] = '\0';
2974
2975 if (before) {
2976 if (! strcmp (buffer, boundary)) {
2977 found_boundary = 1;
2978 }
2979 } else {
2980 if (! found_boundary && ! strcmp (buffer, boundary)) {
2981 found_boundary = 1;
2982 continue;
2983 }
2984 }
2985
2986 if ((before && ! found_boundary) || (! before && found_boundary)) {
2987 if (content) {
2988 char *old_content = content;
2989 content = concat (content, buffer, NULL);
2990 free (old_content);
2991 } else {
2992 content = before
2993 ? concat ("\n", buffer, NULL)
2994 : concat (buffer, NULL);
2995 }
2996 }
2997
2998 if (before) {
2999 if (found_boundary || read > max) break;
3000 } else {
3001 if (read > max) break;
3002 }
3003 }
3004
3005 /* Skip the newline if that's all there is. */
3006 if (content) {
3007 char *cp;
3008
3009 /* Remove trailing newline, except at EOF. */
3010 if ((before || ! feof (ct->c_fp)) &&
3011 (cp = content + strlen (content)) > content &&
3012 *--cp == '\n') {
3013 *cp = '\0';
3014 }
3015
3016 if (strlen (content) > 1) {
3017 if (before) {
3018 m->mp_content_before = content;
3019 } else {
3020 m->mp_content_after = content;
3021 }
3022 } else {
3023 free (content);
3024 }
3025 }
3026
3027 free (boundary);
3028
3029 return OK;
3030 }
3031
3032
3033 char *
3034 ct_type_str (int type) {
3035 switch (type) {
3036 case CT_APPLICATION:
3037 return "application";
3038 case CT_AUDIO:
3039 return "audio";
3040 case CT_IMAGE:
3041 return "image";
3042 case CT_MESSAGE:
3043 return "message";
3044 case CT_MULTIPART:
3045 return "multipart";
3046 case CT_TEXT:
3047 return "text";
3048 case CT_VIDEO:
3049 return "video";
3050 case CT_EXTENSION:
3051 return "extension";
3052 default:
3053 return "unknown_type";
3054 }
3055 }
3056
3057
3058 char *
3059 ct_subtype_str (int type, int subtype) {
3060 switch (type) {
3061 case CT_APPLICATION:
3062 switch (subtype) {
3063 case APPLICATION_OCTETS:
3064 return "octets";
3065 case APPLICATION_POSTSCRIPT:
3066 return "postscript";
3067 default:
3068 return "unknown_app_subtype";
3069 }
3070 case CT_MESSAGE:
3071 switch (subtype) {
3072 case MESSAGE_RFC822:
3073 return "rfc822";
3074 case MESSAGE_PARTIAL:
3075 return "partial";
3076 case MESSAGE_EXTERNAL:
3077 return "external";
3078 default:
3079 return "unknown_msg_subtype";
3080 }
3081 case CT_MULTIPART:
3082 switch (subtype) {
3083 case MULTI_MIXED:
3084 return "mixed";
3085 case MULTI_ALTERNATE:
3086 return "alternative";
3087 case MULTI_DIGEST:
3088 return "digest";
3089 case MULTI_PARALLEL:
3090 return "parallel";
3091 default:
3092 return "unknown_multipart_subtype";
3093 }
3094 case CT_TEXT:
3095 switch (subtype) {
3096 case TEXT_PLAIN:
3097 return "plain";
3098 case TEXT_RICHTEXT:
3099 return "richtext";
3100 case TEXT_ENRICHED:
3101 return "enriched";
3102 default:
3103 return "unknown_text_subtype";
3104 }
3105 default:
3106 return "unknown_type";
3107 }
3108 }
3109
3110
3111 /* Find the content type and InitFunc for the CT. */
3112 const struct str2init *
3113 get_ct_init (int type) {
3114 const struct str2init *sp;
3115
3116 for (sp = str2cts; sp->si_key; ++sp) {
3117 if (type == sp->si_val) {
3118 return sp;
3119 }
3120 }
3121
3122 return NULL;
3123 }
3124
3125 const char *
3126 ce_str (int encoding) {
3127 switch (encoding) {
3128 case CE_BASE64:
3129 return "base64";
3130 case CE_QUOTED:
3131 return "quoted-printable";
3132 case CE_8BIT:
3133 return "8bit";
3134 case CE_7BIT:
3135 return "7bit";
3136 case CE_BINARY:
3137 return "binary";
3138 case CE_EXTENSION:
3139 return "extension";
3140 case CE_EXTERNAL:
3141 return "external";
3142 default:
3143 return "unknown";
3144 }
3145 }
3146
3147 /* Find the content type and InitFunc for the content encoding method. */
3148 const struct str2init *
3149 get_ce_method (const char *method) {
3150 struct str2init *sp;
3151
3152 for (sp = str2ces; sp->si_key; ++sp) {
3153 if (! strcasecmp (method, sp->si_key)) {
3154 return sp;
3155 }
3156 }
3157
3158 return NULL;
3159 }
3160
3161 int
3162 parse_header_attrs (const char *filename, int len, char **header_attrp, CI ci,
3163 int *status) {
3164 char **attr = ci->ci_attrs;
3165 char *cp = *header_attrp;
3166
3167 while (*cp == ';') {
3168 char *dp, *vp, *up, c;
3169
3170 /* Relies on knowledge of this declaration:
3171 * char *ci_attrs[NPARMS + 2];
3172 */
3173 if (attr >= ci->ci_attrs + sizeof ci->ci_attrs/sizeof (char *) - 2) {
3174 advise (NULL,
3175 "too many parameters in message %s's %s: field (%d max)",
3176 filename, TYPE_FIELD, NPARMS);
3177 *status = NOTOK;
3178 return NOTOK;
3179 }
3180
3181 cp++;
3182 while (isspace ((unsigned char) *cp))
3183 cp++;
3184
3185 if (*cp == '(' &&
3186 get_comment (filename, ci, &cp, 1) == NOTOK) {
3187 *status = NOTOK;
3188 return NOTOK;
3189 }
3190
3191 if (*cp == 0) {
3192 advise (NULL,
3193 "extraneous trailing ';' in message %s's %s: "
3194 "parameter list",
3195 filename, TYPE_FIELD);
3196 *status = OK;
3197 return NOTOK;
3198 }
3199
3200 /* down case the attribute name */
3201 for (dp = cp; istoken ((unsigned char) *dp); dp++)
3202 if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp))
3203 *dp = tolower ((unsigned char) *dp);
3204
3205 for (up = dp; isspace ((unsigned char) *dp);)
3206 dp++;
3207 if (dp == cp || *dp != '=') {
3208 advise (NULL,
3209 "invalid parameter in message %s's %s: "
3210 "field\n%*.*sparameter %s (error detected at offset %d)",
3211 filename, TYPE_FIELD, len, len, "", cp, dp - cp);
3212 *status = NOTOK;
3213 return NOTOK;
3214 }
3215
3216 vp = (*attr = add (cp, NULL)) + (up - cp);
3217 *vp = '\0';
3218 for (dp++; isspace ((unsigned char) *dp);)
3219 dp++;
3220
3221 /* Now store the attribute value. */
3222 ci->ci_values[attr - ci->ci_attrs] = vp = *attr + (dp - cp);
3223
3224 if (*dp == '"') {
3225 for (cp = ++dp, dp = vp;;) {
3226 switch (c = *cp++) {
3227 case '\0':
3228 bad_quote:
3229 advise (NULL,
3230 "invalid quoted-string in message %s's %s: "
3231 "field\n%*.*s(parameter %s)",
3232 filename, TYPE_FIELD, len, len, "", *attr);
3233 *status = NOTOK;
3234 return NOTOK;
3235
3236 case '\\':
3237 *dp++ = c;
3238 if ((c = *cp++) == '\0')
3239 goto bad_quote;
3240 /* else fall... */
3241
3242 default:
3243 *dp++ = c;
3244 continue;
3245
3246 case '"':
3247 *dp = '\0';
3248 break;
3249 }
3250 break;
3251 }
3252 } else {
3253 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
3254 continue;
3255 *dp = '\0';
3256 }
3257 if (!*vp) {
3258 advise (NULL,
3259 "invalid parameter in message %s's %s: "
3260 "field\n%*.*s(parameter %s)",
3261 filename, TYPE_FIELD, len, len, "", *attr);
3262 *status = NOTOK;
3263 return NOTOK;
3264 }
3265
3266 while (isspace ((unsigned char) *cp))
3267 cp++;
3268
3269 if (*cp == '(' &&
3270 get_comment (filename, ci, &cp, 1) == NOTOK) {
3271 *status = NOTOK;
3272 return NOTOK;
3273 }
3274
3275 ++attr;
3276 }
3277
3278 *header_attrp = cp;
3279 return OK;
3280 }