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