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