3 * mhparse.c -- routines to parse the contents of MIME messages
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.
12 #include <h/signals.h>
19 #include <h/mhparse.h>
25 extern pid_t xpid
; /* mhshowsbr.c */
28 extern int rcachesw
; /* mhcachesbr.c */
29 extern int wcachesw
; /* mhcachesbr.c */
31 int checksw
= 0; /* check Content-MD5 field */
34 * Directory to place temp files. This must
35 * be set before these routines are called.
40 * These are for mhfixmsg to:
41 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
43 * 2) Suppress the warning about bogus multipart content, and report it.
45 int skip_mp_cte_check
;
46 int suppress_bogus_mp_content_warning
;
50 * Structures for TEXT messages
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! */
59 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
62 * Structures for MULTIPART messages
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! */
73 * Structures for MESSAGE messages
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! */
83 * Structure for APPLICATION messages
85 struct k2v SubApplication
[] = {
86 { "octet-stream", APPLICATION_OCTETS
},
87 { "postscript", APPLICATION_POSTSCRIPT
},
88 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
93 int find_cache (CT
, int, int *, char *, char *, int);
96 int part_ok (CT
, int);
97 int type_ok (CT
, int);
98 void content_error (char *, CT
, char *, ...);
101 void free_encoding (CT
, int);
106 static CT
get_content (FILE *, char *, int);
107 static int get_comment (CT
, char **, int);
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);
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
},
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
},
155 * NOTE WELL: si_key MUST NOT have value of NOTOK
157 * si_key is 1 if access method is anonymous.
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
},
170 pidcheck (int status
)
172 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
183 * Main entry point for parsing a MIME message or file.
184 * It returns the Content structure for the top level
185 * entity in the file.
189 parse_mime (char *file
)
197 * Check if file is actually standard input
199 if ((is_stdin
= !(strcmp (file
, "-")))) {
200 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
202 advise("mhparse", "unable to create temporary file");
205 file
= add (tfile
, NULL
);
208 while (fgets (buffer
, sizeof(buffer
), stdin
))
212 if (ferror (stdin
)) {
214 advise ("stdin", "error reading");
219 advise (file
, "error writing");
222 fseek (fp
, 0L, SEEK_SET
);
223 } else if ((fp
= fopen (file
, "r")) == NULL
) {
224 advise (file
, "unable to read");
228 if (!(ct
= get_content (fp
, file
, 1))) {
231 advise (NULL
, "unable to decode %s", file
);
236 ct
->c_unlink
= 1; /* temp file to remove */
240 if (ct
->c_end
== 0L) {
241 fseek (fp
, 0L, SEEK_END
);
242 ct
->c_end
= ftell (fp
);
245 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
257 * Main routine for reading/parsing the headers
258 * of a message content.
260 * toplevel = 1 # we are at the top level of the message
261 * toplevel = 0 # we are inside message type or multipart type
262 * # other than multipart/digest
263 * toplevel = -1 # we are inside multipart/digest
264 * NB: on failure we will fclose(in)!
268 get_content (FILE *in
, char *file
, int toplevel
)
271 char buf
[BUFSIZ
], name
[NAMESZ
];
275 m_getfld_state_t gstate
= 0;
277 /* allocate the content structure */
278 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
279 adios (NULL
, "out of memory");
282 ct
->c_file
= add (file
, NULL
);
283 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
286 * Parse the header fields for this
287 * content into a linked list.
289 m_getfld_track_filepos (&gstate
, in
);
290 for (compnum
= 1;;) {
291 int bufsz
= sizeof buf
;
292 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
297 /* get copies of the buffers */
298 np
= add (name
, NULL
);
299 vp
= add (buf
, NULL
);
301 /* if necessary, get rest of field */
302 while (state
== FLDPLUS
) {
304 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
305 vp
= add (buf
, vp
); /* add to previous value */
308 /* Now add the header data to the list */
309 add_header (ct
, np
, vp
);
311 /* continue, to see if this isn't the last header field */
312 ct
->c_begin
= ftell (in
) + 1;
316 ct
->c_begin
= ftell (in
) - strlen (buf
);
320 ct
->c_begin
= ftell (in
);
325 adios (NULL
, "message format error in component #%d", compnum
);
328 adios (NULL
, "getfld() returned %d", state
);
331 /* break out of the loop */
334 m_getfld_state_destroy (&gstate
);
337 * Read the content headers. We will parse the
338 * MIME related header fields into their various
339 * structures and set internal flags related to
340 * content type/subtype, etc.
343 hp
= ct
->c_first_hf
; /* start at first header field */
345 /* Get MIME-Version field */
346 if (!mh_strcasecmp (hp
->name
, VRSN_FIELD
)) {
351 advise (NULL
, "message %s has multiple %s: fields",
352 ct
->c_file
, VRSN_FIELD
);
355 ct
->c_vrsn
= add (hp
->value
, NULL
);
357 /* Now, cleanup this field */
360 while (isspace ((unsigned char) *cp
))
362 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
364 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
365 if (!isspace ((unsigned char) *dp
))
369 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
371 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
)
374 for (dp
= cp
; istoken (*dp
); dp
++)
378 ucmp
= !mh_strcasecmp (cp
, VRSN_VALUE
);
381 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
382 ct
->c_file
, VRSN_FIELD
, cp
);
385 else if (!mh_strcasecmp (hp
->name
, TYPE_FIELD
)) {
386 /* Get Content-Type field */
387 struct str2init
*s2i
;
388 CI ci
= &ct
->c_ctinfo
;
390 /* Check if we've already seen a Content-Type header */
392 advise (NULL
, "message %s has multiple %s: fields",
393 ct
->c_file
, TYPE_FIELD
);
397 /* Parse the Content-Type field */
398 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
402 * Set the Init function and the internal
403 * flag for this content type.
405 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
406 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
408 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
410 ct
->c_type
= s2i
->si_val
;
411 ct
->c_ctinitfnx
= s2i
->si_init
;
413 else if (!mh_strcasecmp (hp
->name
, ENCODING_FIELD
)) {
414 /* Get Content-Transfer-Encoding field */
416 struct str2init
*s2i
;
419 * Check if we've already seen the
420 * Content-Transfer-Encoding field
423 advise (NULL
, "message %s has multiple %s: fields",
424 ct
->c_file
, ENCODING_FIELD
);
428 /* get copy of this field */
429 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
431 while (isspace ((unsigned char) *cp
))
433 for (dp
= cp
; istoken (*dp
); dp
++)
439 * Find the internal flag and Init function
440 * for this transfer encoding.
442 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
443 if (!mh_strcasecmp (cp
, s2i
->si_key
))
445 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
448 ct
->c_encoding
= s2i
->si_val
;
450 /* Call the Init function for this encoding */
451 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
454 else if (!mh_strcasecmp (hp
->name
, MD5_FIELD
)) {
455 /* Get Content-MD5 field */
461 if (ct
->c_digested
) {
462 advise (NULL
, "message %s has multiple %s: fields",
463 ct
->c_file
, MD5_FIELD
);
467 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
469 while (isspace ((unsigned char) *cp
))
471 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
473 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
474 if (!isspace ((unsigned char) *dp
))
478 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
480 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
) {
485 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
493 else if (!mh_strcasecmp (hp
->name
, ID_FIELD
)) {
494 /* Get Content-ID field */
495 ct
->c_id
= add (hp
->value
, ct
->c_id
);
497 else if (!mh_strcasecmp (hp
->name
, DESCR_FIELD
)) {
498 /* Get Content-Description field */
499 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
501 else if (!mh_strcasecmp (hp
->name
, DISPO_FIELD
)) {
502 /* Get Content-Disposition field */
503 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
507 hp
= hp
->next
; /* next header field */
511 * Check if we saw a Content-Type field.
512 * If not, then assign a default value for
513 * it, and the Init function.
517 * If we are inside a multipart/digest message,
518 * so default type is message/rfc822
521 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
523 ct
->c_type
= CT_MESSAGE
;
524 ct
->c_ctinitfnx
= InitMessage
;
527 * Else default type is text/plain
529 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
531 ct
->c_type
= CT_TEXT
;
532 ct
->c_ctinitfnx
= InitText
;
536 /* Use default Transfer-Encoding, if necessary */
538 ct
->c_encoding
= CE_7BIT
;
551 * small routine to add header field to list
555 add_header (CT ct
, char *name
, char *value
)
559 /* allocate header field structure */
560 hp
= mh_xmalloc (sizeof(*hp
));
562 /* link data into header structure */
567 /* link header structure into the list */
568 if (ct
->c_first_hf
== NULL
) {
569 ct
->c_first_hf
= hp
; /* this is the first */
572 ct
->c_last_hf
->next
= hp
; /* add it to the end */
580 /* Make sure that buf contains at least one appearance of name,
581 followed by =. If not, insert both name and value, just after
582 first semicolon, if any. Note that name should not contain a
583 trailing =. And quotes will be added around the value. Typical
584 usage: make sure that a Content-Disposition header contains
585 filename="foo". If it doesn't and value does, use value from
588 incl_name_value (char *buf
, char *name
, char *value
) {
591 /* Assume that name is non-null. */
593 char *name_plus_equal
= concat (name
, "=", NULL
);
595 if (! strstr (buf
, name_plus_equal
)) {
597 char *cp
, *prefix
, *suffix
;
599 /* Trim trailing space, esp. newline. */
600 for (cp
= &buf
[strlen (buf
) - 1];
601 cp
>= buf
&& isspace ((unsigned char) *cp
);
606 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
608 /* Insert at first semicolon, if any. If none, append to
610 prefix
= add (buf
, NULL
);
611 if ((cp
= strchr (prefix
, ';'))) {
612 suffix
= concat (cp
, NULL
);
614 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
618 newbuf
= concat (buf
, insertion
, "\n", NULL
);
626 free (name_plus_equal
);
632 /* Extract just name_suffix="foo", if any, from value. If there isn't
633 one, return the entire value. Note that, for example, a name_suffix
634 of name will match filename="foo", and return foo. */
636 extract_name_value (char *name_suffix
, char *value
) {
637 char *extracted_name_value
= value
;
638 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
639 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
642 free (name_suffix_plus_quote
);
643 if (name_suffix_equals
) {
644 char *name_suffix_begin
;
647 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
648 name_suffix_begin
= ++cp
;
649 /* Find second \". */
650 for (; *cp
!= '"'; ++cp
) /* empty */;
652 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
653 memcpy (extracted_name_value
,
655 cp
- name_suffix_begin
);
656 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
659 return extracted_name_value
;
663 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
664 * directives. Fills in the information of the CTinfo structure.
667 get_ctinfo (char *cp
, CT ct
, int magic
)
670 char *dp
, **ap
, **ep
;
675 i
= strlen (invo_name
) + 2;
677 /* store copy of Content-Type line */
678 cp
= ct
->c_ctline
= add (cp
, NULL
);
680 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
683 /* change newlines to spaces */
684 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
687 /* trim trailing spaces */
688 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
689 if (!isspace ((unsigned char) *dp
))
694 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
696 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
699 for (dp
= cp
; istoken (*dp
); dp
++)
702 ci
->ci_type
= add (cp
, NULL
); /* store content type */
706 advise (NULL
, "invalid %s: field in message %s (empty type)",
707 TYPE_FIELD
, ct
->c_file
);
711 /* down case the content type string */
712 for (dp
= ci
->ci_type
; *dp
; dp
++)
713 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
714 *dp
= tolower ((unsigned char) *dp
);
716 while (isspace ((unsigned char) *cp
))
719 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
724 ci
->ci_subtype
= add ("", NULL
);
729 while (isspace ((unsigned char) *cp
))
732 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
735 for (dp
= cp
; istoken (*dp
); dp
++)
738 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
741 if (!*ci
->ci_subtype
) {
743 "invalid %s: field in message %s (empty subtype for \"%s\")",
744 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
748 /* down case the content subtype string */
749 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
750 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
751 *dp
= tolower ((unsigned char) *dp
);
754 while (isspace ((unsigned char) *cp
))
757 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
761 * Parse attribute/value pairs given with Content-Type
763 ep
= (ap
= ci
->ci_attrs
) + NPARMS
;
769 "too many parameters in message %s's %s: field (%d max)",
770 ct
->c_file
, TYPE_FIELD
, NPARMS
);
775 while (isspace ((unsigned char) *cp
))
778 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
783 "extraneous trailing ';' in message %s's %s: parameter list",
784 ct
->c_file
, TYPE_FIELD
);
788 /* down case the attribute name */
789 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
790 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
791 *dp
= tolower ((unsigned char) *dp
);
793 for (up
= dp
; isspace ((unsigned char) *dp
);)
795 if (dp
== cp
|| *dp
!= '=') {
797 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
798 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
, dp
- cp
);
802 vp
= (*ap
= add (cp
, NULL
)) + (up
- cp
);
804 for (dp
++; isspace ((unsigned char) *dp
);)
807 /* now add the attribute value */
808 ci
->ci_values
[ap
- ci
->ci_attrs
] = vp
= *ap
+ (dp
- cp
);
811 for (cp
= ++dp
, dp
= vp
;;) {
816 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
817 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
822 if ((c
= *cp
++) == '\0')
837 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
843 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
844 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
849 while (isspace ((unsigned char) *cp
))
852 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
857 * Get any <Content-Id> given in buffer
859 if (magic
&& *cp
== '<') {
864 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
865 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
871 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
877 while (isspace ((unsigned char) *cp
))
882 * Get any [Content-Description] given in buffer.
884 if (magic
&& *cp
== '[') {
886 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
890 advise (NULL
, "invalid description in message %s", ct
->c_file
);
898 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
904 while (isspace ((unsigned char) *cp
))
909 * Get any {Content-Disposition} given in buffer.
911 if (magic
&& *cp
== '{') {
913 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
917 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
925 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
931 while (isspace ((unsigned char) *cp
))
936 * Check if anything is left over
940 ci
->ci_magic
= add (cp
, NULL
);
942 /* If there is a Content-Disposition header and it doesn't
943 have a *filename=, extract it from the magic contents.
944 The r1bindex call skips any leading directory
948 incl_name_value (ct
->c_dispo
,
950 r1bindex (extract_name_value ("name",
957 "extraneous information in message %s's %s: field\n%*.*s(%s)",
958 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
966 get_comment (CT ct
, char **ap
, int istype
)
970 char c
, buffer
[BUFSIZ
], *dp
;
982 advise (NULL
, "invalid comment in message %s's %s: field",
983 ct
->c_file
, istype
? TYPE_FIELD
: VRSN_FIELD
);
988 if ((c
= *cp
++) == '\0')
1011 if ((dp
= ci
->ci_comment
)) {
1012 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
1015 ci
->ci_comment
= add (buffer
, NULL
);
1019 while (isspace ((unsigned char) *cp
))
1030 * Handles content types audio, image, and video.
1031 * There's not much to do right here.
1039 return OK
; /* not much to do here */
1050 char buffer
[BUFSIZ
];
1052 char **ap
, **ep
, *cp
;
1055 CI ci
= &ct
->c_ctinfo
;
1057 /* check for missing subtype */
1058 if (!*ci
->ci_subtype
)
1059 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1062 for (kv
= SubText
; kv
->kv_key
; kv
++)
1063 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1065 ct
->c_subtype
= kv
->kv_value
;
1067 /* allocate text character set structure */
1068 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1069 adios (NULL
, "out of memory");
1070 ct
->c_ctparams
= (void *) t
;
1072 /* scan for charset parameter */
1073 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1074 if (!mh_strcasecmp (*ap
, "charset"))
1077 /* check if content specified a character set */
1080 t
->tx_charset
= CHARSET_SPECIFIED
;
1082 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1086 * If we can not handle character set natively,
1087 * then check profile for string to modify the
1088 * terminal or display method.
1090 * termproc is for mhshow, though mhlist -debug prints it, too.
1092 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1093 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1094 if ((cp
= context_find (buffer
)))
1095 ct
->c_termproc
= getcpy (cp
);
1107 InitMultiPart (CT ct
)
1111 char *cp
, *dp
, **ap
, **ep
;
1112 char *bp
, buffer
[BUFSIZ
];
1113 struct multipart
*m
;
1115 struct part
*part
, **next
;
1116 CI ci
= &ct
->c_ctinfo
;
1121 * The encoding for multipart messages must be either
1122 * 7bit, 8bit, or binary (per RFC2045).
1124 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1125 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1126 /* Copy the Content-Transfer-Encoding header field body so we can
1127 remove any trailing whitespace and leading blanks from it. */
1128 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1130 bp
= cte
+ strlen (cte
) - 1;
1131 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1132 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1135 "\"%s/%s\" type in message %s must be encoded in\n"
1136 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1137 "is to\nmanually edit the file and change the \"%s\"\n"
1138 "Content-Transfer-Encoding to one of those. For now",
1139 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1146 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1147 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1149 ct
->c_subtype
= kv
->kv_value
;
1152 * Check for "boundary" parameter, which is
1153 * required for multipart messages.
1156 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1157 if (!mh_strcasecmp (*ap
, "boundary")) {
1163 /* complain if boundary parameter is missing */
1166 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1167 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1171 /* allocate primary structure for multipart info */
1172 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1173 adios (NULL
, "out of memory");
1174 ct
->c_ctparams
= (void *) m
;
1176 /* check if boundary parameter contains only whitespace characters */
1177 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1180 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1181 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1185 /* remove trailing whitespace from boundary parameter */
1186 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1187 if (!isspace ((unsigned char) *dp
))
1191 /* record boundary separators */
1192 m
->mp_start
= concat (bp
, "\n", NULL
);
1193 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1195 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1196 advise (ct
->c_file
, "unable to open for reading");
1200 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1202 next
= &m
->mp_parts
;
1206 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1210 pos
+= strlen (buffer
);
1211 if (buffer
[0] != '-' || buffer
[1] != '-')
1214 if (strcmp (buffer
+ 2, m
->mp_start
))
1217 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1218 adios (NULL
, "out of memory");
1220 next
= &part
->mp_next
;
1222 if (!(p
= get_content (fp
, ct
->c_file
,
1223 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1230 fseek (fp
, pos
, SEEK_SET
);
1233 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1237 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1238 if (p
->c_end
< p
->c_begin
)
1239 p
->c_begin
= p
->c_end
;
1244 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1250 if (! suppress_bogus_mp_content_warning
) {
1251 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1253 bogus_mp_content
= 1;
1255 if (!inout
&& part
) {
1257 p
->c_end
= ct
->c_end
;
1259 if (p
->c_begin
>= p
->c_end
) {
1260 for (next
= &m
->mp_parts
; *next
!= part
;
1261 next
= &((*next
)->mp_next
))
1265 free ((char *) part
);
1270 /* reverse the order of the parts for multipart/alternative */
1271 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1275 * label all subparts with part number, and
1276 * then initialize the content of the subpart.
1281 char partnam
[BUFSIZ
];
1284 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1285 pp
= partnam
+ strlen (partnam
);
1290 for (part
= m
->mp_parts
, partnum
= 1; part
;
1291 part
= part
->mp_next
, partnum
++) {
1294 sprintf (pp
, "%d", partnum
);
1295 p
->c_partno
= add (partnam
, NULL
);
1297 /* initialize the content of the subparts */
1298 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1306 get_leftover_mp_content (ct
, 1);
1307 get_leftover_mp_content (ct
, 0);
1316 * reverse the order of the parts of a multipart/alternative
1320 reverse_parts (CT ct
)
1322 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1326 /* Reverse the order of its parts by walking the mp_parts list
1327 and pushing each node to the front. */
1328 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1329 next
= part
->mp_next
;
1330 part
->mp_next
= m
->mp_parts
;
1344 CI ci
= &ct
->c_ctinfo
;
1346 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1348 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1349 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1353 /* check for missing subtype */
1354 if (!*ci
->ci_subtype
)
1355 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1358 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1359 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1361 ct
->c_subtype
= kv
->kv_value
;
1363 switch (ct
->c_subtype
) {
1364 case MESSAGE_RFC822
:
1367 case MESSAGE_PARTIAL
:
1372 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1373 adios (NULL
, "out of memory");
1374 ct
->c_ctparams
= (void *) p
;
1376 /* scan for parameters "id", "number", and "total" */
1377 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1378 if (!mh_strcasecmp (*ap
, "id")) {
1379 p
->pm_partid
= add (*ep
, NULL
);
1382 if (!mh_strcasecmp (*ap
, "number")) {
1383 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1384 || p
->pm_partno
< 1) {
1387 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1388 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1389 ct
->c_file
, TYPE_FIELD
);
1394 if (!mh_strcasecmp (*ap
, "total")) {
1395 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1404 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1406 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1407 ci
->ci_type
, ci
->ci_subtype
,
1408 ct
->c_file
, TYPE_FIELD
);
1414 case MESSAGE_EXTERNAL
:
1421 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1422 adios (NULL
, "out of memory");
1423 ct
->c_ctparams
= (void *) e
;
1426 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1427 advise (ct
->c_file
, "unable to open for reading");
1431 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1433 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1441 if ((exresult
= params_external (ct
, 0)) != NOTOK
1442 && p
->c_ceopenfnx
== openMail
) {
1446 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1448 content_error (NULL
, ct
,
1449 "empty body for access-type=mail-server");
1453 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1454 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1456 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1458 adios ("failed", "fread");
1461 adios (NULL
, "unexpected EOF from fread");
1464 bp
+= cc
, size
-= cc
;
1471 p
->c_end
= p
->c_begin
;
1476 if (exresult
== NOTOK
)
1478 if (e
->eb_flags
== NOTOK
)
1481 switch (p
->c_type
) {
1486 if (p
->c_subtype
!= MESSAGE_RFC822
)
1490 e
->eb_partno
= ct
->c_partno
;
1492 (*p
->c_ctinitfnx
) (p
);
1507 params_external (CT ct
, int composing
)
1510 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1511 CI ci
= &ct
->c_ctinfo
;
1513 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1514 if (!mh_strcasecmp (*ap
, "access-type")) {
1515 struct str2init
*s2i
;
1516 CT p
= e
->eb_content
;
1518 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1519 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1523 e
->eb_flags
= NOTOK
;
1524 p
->c_encoding
= CE_EXTERNAL
;
1527 e
->eb_access
= s2i
->si_key
;
1528 e
->eb_flags
= s2i
->si_val
;
1529 p
->c_encoding
= CE_EXTERNAL
;
1531 /* Call the Init function for this external type */
1532 if ((*s2i
->si_init
)(p
) == NOTOK
)
1536 if (!mh_strcasecmp (*ap
, "name")) {
1540 if (!mh_strcasecmp (*ap
, "permission")) {
1541 e
->eb_permission
= *ep
;
1544 if (!mh_strcasecmp (*ap
, "site")) {
1548 if (!mh_strcasecmp (*ap
, "directory")) {
1552 if (!mh_strcasecmp (*ap
, "mode")) {
1556 if (!mh_strcasecmp (*ap
, "size")) {
1557 sscanf (*ep
, "%lu", &e
->eb_size
);
1560 if (!mh_strcasecmp (*ap
, "server")) {
1564 if (!mh_strcasecmp (*ap
, "subject")) {
1565 e
->eb_subject
= *ep
;
1568 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1569 e
->eb_body
= getcpy (*ep
);
1574 if (!e
->eb_access
) {
1576 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1577 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1590 InitApplication (CT ct
)
1593 CI ci
= &ct
->c_ctinfo
;
1596 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1597 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1599 ct
->c_subtype
= kv
->kv_value
;
1606 * TRANSFER ENCODINGS
1610 init_encoding (CT ct
, OpenCEFunc openfnx
)
1614 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
1615 adios (NULL
, "out of memory");
1618 ct
->c_ceopenfnx
= openfnx
;
1619 ct
->c_ceclosefnx
= close_encoding
;
1620 ct
->c_cesizefnx
= size_encoding
;
1627 close_encoding (CT ct
)
1631 if (!(ce
= ct
->c_cefile
))
1641 static unsigned long
1642 size_encoding (CT ct
)
1650 if (!(ce
= ct
->c_cefile
))
1651 return (ct
->c_end
- ct
->c_begin
);
1653 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1654 return (long) st
.st_size
;
1657 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1658 return (long) st
.st_size
;
1663 if (ct
->c_encoding
== CE_EXTERNAL
)
1664 return (ct
->c_end
- ct
->c_begin
);
1667 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1668 return (ct
->c_end
- ct
->c_begin
);
1670 if (fstat (fd
, &st
) != NOTOK
)
1671 size
= (long) st
.st_size
;
1675 (*ct
->c_ceclosefnx
) (ct
);
1684 static unsigned char b642nib
[0x80] = {
1685 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1686 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1687 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1688 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1689 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1690 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1691 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1692 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1693 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1694 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1695 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1696 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1697 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1698 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1699 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1700 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1707 return init_encoding (ct
, openBase64
);
1712 openBase64 (CT ct
, char **file
)
1714 int bitno
, cc
, digested
;
1715 int fd
, len
, skip
, own_ct_fp
= 0;
1717 unsigned char value
, b
;
1718 char *cp
, *ep
, buffer
[BUFSIZ
];
1719 /* sbeck -- handle suffixes */
1726 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1731 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1732 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1738 if (*file
== NULL
) {
1739 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1742 ce
->ce_file
= add (*file
, NULL
);
1746 /* sbeck@cise.ufl.edu -- handle suffixes */
1748 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1749 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1750 cp
= context_find (buffer
);
1751 if (cp
== NULL
|| *cp
== '\0') {
1752 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1754 cp
= context_find (buffer
);
1756 if (cp
!= NULL
&& *cp
!= '\0') {
1757 if (ce
->ce_unlink
) {
1758 /* Temporary file already exists, so we rename to
1759 version with extension. */
1760 char *file_org
= strdup(ce
->ce_file
);
1761 ce
->ce_file
= add (cp
, ce
->ce_file
);
1762 if (rename(file_org
, ce
->ce_file
)) {
1763 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1768 ce
->ce_file
= add (cp
, ce
->ce_file
);
1772 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1773 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1777 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1778 adios (NULL
, "internal error(1)");
1781 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1782 content_error (ct
->c_file
, ct
, "unable to open for reading");
1788 if ((digested
= ct
->c_digested
))
1789 MD5Init (&mdContext
);
1795 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1797 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1799 content_error (ct
->c_file
, ct
, "error reading from");
1803 content_error (NULL
, ct
, "premature eof");
1811 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1814 if (isspace ((unsigned char) *cp
))
1816 if (skip
|| (((unsigned char) *cp
) & 0x80)
1817 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1819 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1820 (unsigned char) *cp
,
1821 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1824 content_error (NULL
, ct
,
1825 "invalid BASE64 encoding -- continuing");
1829 bits
|= value
<< bitno
;
1831 if ((bitno
-= 6) < 0) {
1832 b
= (bits
>> 16) & 0xff;
1833 putc ((char) b
, ce
->ce_fp
);
1835 MD5Update (&mdContext
, &b
, 1);
1837 b
= (bits
>> 8) & 0xff;
1838 putc ((char) b
, ce
->ce_fp
);
1840 MD5Update (&mdContext
, &b
, 1);
1843 putc ((char) b
, ce
->ce_fp
);
1845 MD5Update (&mdContext
, &b
, 1);
1849 if (ferror (ce
->ce_fp
)) {
1850 content_error (ce
->ce_file
, ct
,
1851 "error writing to");
1854 bitno
= 18, bits
= 0L, skip
= 0;
1860 goto self_delimiting
;
1869 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1871 content_error (NULL
, ct
, "invalid BASE64 encoding");
1876 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1878 if (fflush (ce
->ce_fp
)) {
1879 content_error (ce
->ce_file
, ct
, "error writing to");
1884 unsigned char digest
[16];
1886 MD5Final (digest
, &mdContext
);
1887 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1888 sizeof(digest
) / sizeof(digest
[0])))
1889 content_error (NULL
, ct
,
1890 "content integrity suspect (digest mismatch) -- continuing");
1893 fprintf (stderr
, "content integrity confirmed\n");
1896 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1899 *file
= ce
->ce_file
;
1904 return fileno (ce
->ce_fp
);
1911 free_encoding (ct
, 0);
1920 static char hex2nib
[0x80] = {
1921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1924 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1925 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1926 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1927 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1928 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1929 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1943 return init_encoding (ct
, openQuoted
);
1948 openQuoted (CT ct
, char **file
)
1950 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1952 char buffer
[BUFSIZ
];
1955 /* sbeck -- handle suffixes */
1961 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1966 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1967 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1973 if (*file
== NULL
) {
1974 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1977 ce
->ce_file
= add (*file
, NULL
);
1981 /* sbeck@cise.ufl.edu -- handle suffixes */
1983 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1984 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1985 cp
= context_find (buffer
);
1986 if (cp
== NULL
|| *cp
== '\0') {
1987 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1989 cp
= context_find (buffer
);
1991 if (cp
!= NULL
&& *cp
!= '\0') {
1992 if (ce
->ce_unlink
) {
1993 /* Temporary file already exists, so we rename to
1994 version with extension. */
1995 char *file_org
= strdup(ce
->ce_file
);
1996 ce
->ce_file
= add (cp
, ce
->ce_file
);
1997 if (rename(file_org
, ce
->ce_file
)) {
1998 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2003 ce
->ce_file
= add (cp
, ce
->ce_file
);
2007 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2008 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2012 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2013 adios (NULL
, "internal error(2)");
2016 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2017 content_error (ct
->c_file
, ct
, "unable to open for reading");
2023 if ((digested
= ct
->c_digested
))
2024 MD5Init (&mdContext
);
2031 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2033 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2034 content_error (NULL
, ct
, "premature eof");
2038 if ((cc
= strlen (buffer
)) > len
)
2042 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2043 if (!isspace ((unsigned char) *ep
))
2047 for (; cp
< ep
; cp
++) {
2049 /* in an escape sequence */
2051 /* at byte 1 of an escape sequence */
2052 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2053 /* next is byte 2 */
2056 /* at byte 2 of an escape sequence */
2058 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2059 putc (mask
, ce
->ce_fp
);
2061 MD5Update (&mdContext
, &mask
, 1);
2062 if (ferror (ce
->ce_fp
)) {
2063 content_error (ce
->ce_file
, ct
, "error writing to");
2066 /* finished escape sequence; next may be literal or a new
2067 * escape sequence */
2070 /* on to next byte */
2074 /* not in an escape sequence */
2076 /* starting an escape sequence, or invalid '='? */
2077 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2078 /* "=\n" soft line break, eat the \n */
2082 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2083 /* We don't have 2 bytes left, so this is an invalid
2084 * escape sequence; just show the raw bytes (below). */
2085 } else if (isxdigit ((unsigned char) cp
[1]) &&
2086 isxdigit ((unsigned char) cp
[2])) {
2087 /* Next 2 bytes are hex digits, making this a valid escape
2088 * sequence; let's decode it (above). */
2092 /* One or both of the next 2 is out of range, making this
2093 * an invalid escape sequence; just show the raw bytes
2098 /* Just show the raw byte. */
2099 putc (*cp
, ce
->ce_fp
);
2102 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2104 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2107 if (ferror (ce
->ce_fp
)) {
2108 content_error (ce
->ce_file
, ct
, "error writing to");
2114 content_error (NULL
, ct
,
2115 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2119 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2121 if (fflush (ce
->ce_fp
)) {
2122 content_error (ce
->ce_file
, ct
, "error writing to");
2127 unsigned char digest
[16];
2129 MD5Final (digest
, &mdContext
);
2130 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2131 sizeof(digest
) / sizeof(digest
[0])))
2132 content_error (NULL
, ct
,
2133 "content integrity suspect (digest mismatch) -- continuing");
2136 fprintf (stderr
, "content integrity confirmed\n");
2139 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2142 *file
= ce
->ce_file
;
2147 return fileno (ce
->ce_fp
);
2150 free_encoding (ct
, 0);
2166 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2169 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2175 open7Bit (CT ct
, char **file
)
2177 int cc
, fd
, len
, own_ct_fp
= 0;
2178 char buffer
[BUFSIZ
];
2179 /* sbeck -- handle suffixes */
2186 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2191 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2192 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2198 if (*file
== NULL
) {
2199 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2202 ce
->ce_file
= add (*file
, NULL
);
2206 /* sbeck@cise.ufl.edu -- handle suffixes */
2208 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2209 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2210 cp
= context_find (buffer
);
2211 if (cp
== NULL
|| *cp
== '\0') {
2212 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2214 cp
= context_find (buffer
);
2216 if (cp
!= NULL
&& *cp
!= '\0') {
2217 if (ce
->ce_unlink
) {
2218 /* Temporary file already exists, so we rename to
2219 version with extension. */
2220 char *file_org
= strdup(ce
->ce_file
);
2221 ce
->ce_file
= add (cp
, ce
->ce_file
);
2222 if (rename(file_org
, ce
->ce_file
)) {
2223 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2228 ce
->ce_file
= add (cp
, ce
->ce_file
);
2232 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2233 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2237 if (ct
->c_type
== CT_MULTIPART
) {
2239 CI ci
= &ct
->c_ctinfo
;
2242 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2243 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2244 + 1 + strlen (ci
->ci_subtype
);
2245 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2246 putc (';', ce
->ce_fp
);
2249 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2251 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2252 fputs ("\n\t", ce
->ce_fp
);
2255 putc (' ', ce
->ce_fp
);
2258 fprintf (ce
->ce_fp
, "%s", buffer
);
2262 if (ci
->ci_comment
) {
2263 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2264 fputs ("\n\t", ce
->ce_fp
);
2268 putc (' ', ce
->ce_fp
);
2271 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2274 fprintf (ce
->ce_fp
, "\n");
2276 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2278 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2280 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2281 fprintf (ce
->ce_fp
, "\n");
2284 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2285 adios (NULL
, "internal error(3)");
2288 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2289 content_error (ct
->c_file
, ct
, "unable to open for reading");
2295 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2297 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2299 content_error (ct
->c_file
, ct
, "error reading from");
2303 content_error (NULL
, ct
, "premature eof");
2311 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2312 if (ferror (ce
->ce_fp
)) {
2313 content_error (ce
->ce_file
, ct
, "error writing to");
2318 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2320 if (fflush (ce
->ce_fp
)) {
2321 content_error (ce
->ce_file
, ct
, "error writing to");
2325 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2328 *file
= ce
->ce_file
;
2333 return fileno (ce
->ce_fp
);
2336 free_encoding (ct
, 0);
2350 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2352 char cachefile
[BUFSIZ
];
2355 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2360 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2361 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2367 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2368 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2369 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2370 ce
->ce_file
= getcpy (cachefile
);
2374 admonish (cachefile
, "unable to fopen for reading");
2381 *file
= ce
->ce_file
;
2382 *fd
= fileno (ce
->ce_fp
);
2393 return init_encoding (ct
, openFile
);
2398 openFile (CT ct
, char **file
)
2401 char cachefile
[BUFSIZ
];
2402 struct exbody
*e
= ct
->c_ctexbody
;
2403 CE ce
= ct
->c_cefile
;
2405 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2417 content_error (NULL
, ct
, "missing name parameter");
2421 ce
->ce_file
= getcpy (e
->eb_name
);
2424 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2425 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2429 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2430 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2431 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2435 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2436 if ((fp
= fopen (cachefile
, "w"))) {
2438 char buffer
[BUFSIZ
];
2439 FILE *gp
= ce
->ce_fp
;
2441 fseek (gp
, 0L, SEEK_SET
);
2443 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2445 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2449 admonish (ce
->ce_file
, "error reading");
2454 admonish (cachefile
, "error writing");
2462 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2463 *file
= ce
->ce_file
;
2464 return fileno (ce
->ce_fp
);
2474 return init_encoding (ct
, openFTP
);
2479 openFTP (CT ct
, char **file
)
2481 int cachetype
, caching
, fd
;
2483 char *bp
, *ftp
, *user
, *pass
;
2484 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2487 static char *username
= NULL
;
2488 static char *password
= NULL
;
2493 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2499 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2510 if (!e
->eb_name
|| !e
->eb_site
) {
2511 content_error (NULL
, ct
, "missing %s parameter",
2512 e
->eb_name
? "site": "name");
2519 pidcheck (pidwait (xpid
, NOTOK
));
2523 /* Get the buffer ready to go */
2525 buflen
= sizeof(buffer
);
2528 * Construct the query message for user
2530 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2536 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2542 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2543 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2548 if (e
->eb_size
> 0) {
2549 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2554 snprintf (bp
, buflen
, "? ");
2557 * Now, check the answer
2559 if (!getanswer (buffer
))
2564 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2568 ruserpass (e
->eb_site
, &username
, &password
);
2573 ce
->ce_unlink
= (*file
== NULL
);
2575 cachefile
[0] = '\0';
2576 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2577 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2578 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2579 if (*file
== NULL
) {
2586 ce
->ce_file
= add (*file
, NULL
);
2588 ce
->ce_file
= add (cachefile
, NULL
);
2590 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2592 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2593 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2598 int child_id
, i
, vecp
;
2602 vec
[vecp
++] = r1bindex (ftp
, '/');
2603 vec
[vecp
++] = e
->eb_site
;
2606 vec
[vecp
++] = e
->eb_dir
;
2607 vec
[vecp
++] = e
->eb_name
;
2608 vec
[vecp
++] = ce
->ce_file
,
2609 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2610 ? "ascii" : "binary";
2615 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2619 adios ("fork", "unable to");
2623 close (fileno (ce
->ce_fp
));
2625 fprintf (stderr
, "unable to exec ");
2631 if (pidXwait (child_id
, NULL
)) {
2632 username
= password
= NULL
;
2642 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2647 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2648 if ((fp
= fopen (cachefile
, "w"))) {
2650 FILE *gp
= ce
->ce_fp
;
2652 fseek (gp
, 0L, SEEK_SET
);
2654 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2656 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2660 admonish (ce
->ce_file
, "error reading");
2665 admonish (cachefile
, "error writing");
2674 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2675 *file
= ce
->ce_file
;
2676 return fileno (ce
->ce_fp
);
2687 return init_encoding (ct
, openMail
);
2692 openMail (CT ct
, char **file
)
2694 int child_id
, fd
, i
, vecp
;
2696 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2697 struct exbody
*e
= ct
->c_ctexbody
;
2698 CE ce
= ct
->c_cefile
;
2700 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2711 if (!e
->eb_server
) {
2712 content_error (NULL
, ct
, "missing server parameter");
2719 pidcheck (pidwait (xpid
, NOTOK
));
2723 /* Get buffer ready to go */
2725 buflen
= sizeof(buffer
);
2727 /* Now, construct query message */
2728 snprintf (bp
, buflen
, "Retrieve content");
2734 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2740 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2742 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2744 /* Now, check answer */
2745 if (!getanswer (buffer
))
2749 vec
[vecp
++] = r1bindex (mailproc
, '/');
2750 vec
[vecp
++] = e
->eb_server
;
2751 vec
[vecp
++] = "-subject";
2752 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2753 vec
[vecp
++] = "-body";
2754 vec
[vecp
++] = e
->eb_body
;
2757 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2761 advise ("fork", "unable to");
2765 execvp (mailproc
, vec
);
2766 fprintf (stderr
, "unable to exec ");
2772 if (pidXwait (child_id
, NULL
) == OK
)
2773 advise (NULL
, "request sent");
2777 if (*file
== NULL
) {
2778 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2781 ce
->ce_file
= add (*file
, NULL
);
2785 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2786 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2790 /* showproc is for mhshow and mhstore, though mhlist -debug
2791 * prints it, too. */
2793 free (ct
->c_showproc
);
2794 ct
->c_showproc
= add ("true", NULL
);
2796 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2797 *file
= ce
->ce_file
;
2798 return fileno (ce
->ce_fp
);
2803 readDigest (CT ct
, char *cp
)
2808 unsigned char *dp
, value
, *ep
;
2814 for (ep
= (dp
= ct
->c_digest
)
2815 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2820 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2822 fprintf (stderr
, "invalid BASE64 encoding\n");
2826 bits
|= value
<< bitno
;
2828 if ((bitno
-= 6) < 0) {
2829 if (dp
+ (3 - skip
) > ep
)
2830 goto invalid_digest
;
2831 *dp
++ = (bits
>> 16) & 0xff;
2833 *dp
++ = (bits
>> 8) & 0xff;
2835 *dp
++ = bits
& 0xff;
2845 goto self_delimiting
;
2850 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2860 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2868 fprintf (stderr
, "MD5 digest=");
2869 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2870 fprintf (stderr
, "%02x", *dp
& 0xff);
2871 fprintf (stderr
, "\n");
2878 /* Multipart parts might have content before the first subpart and/or
2879 after the last subpart that hasn't been stored anywhere else, so do
2882 get_leftover_mp_content (CT ct
, int before
/* or after */) {
2883 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2885 int found_boundary
= 0;
2886 char buffer
[BUFSIZ
];
2889 char *content
= NULL
;
2891 if (! m
) return NOTOK
;
2894 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2896 /* Isolate the beginning of this part to the beginning of the
2897 first subpart and save any content between them. */
2898 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2899 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2900 boundary
= concat ("--", m
->mp_start
, NULL
);
2902 struct part
*last_subpart
= NULL
;
2903 struct part
*subpart
;
2905 /* Go to the last subpart to get its end position. */
2906 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
2907 last_subpart
= subpart
;
2910 if (last_subpart
== NULL
) return NOTOK
;
2912 /* Isolate the end of the last subpart to the end of this part
2913 and save any content between them. */
2914 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
2915 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
2916 boundary
= concat ("--", m
->mp_stop
, NULL
);
2919 /* Back up by 1 to pick up the newline. */
2920 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
2921 read
+= strlen (buffer
);
2922 /* Don't look beyond beginning of first subpart (before) or
2923 next part (after). */
2924 if (read
> max
) buffer
[read
-max
] = '\0';
2927 if (! strcmp (buffer
, boundary
)) {
2931 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
2937 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
2939 char *old_content
= content
;
2940 content
= concat (content
, buffer
, NULL
);
2944 ? concat ("\n", buffer
, NULL
)
2945 : concat (buffer
, NULL
);
2950 if (found_boundary
|| read
> max
) break;
2952 if (read
> max
) break;
2956 /* Skip the newline if that's all there is. */
2960 /* Remove trailing newline, except at EOF. */
2961 if ((before
|| ! feof (ct
->c_fp
)) &&
2962 (cp
= content
+ strlen (content
)) > content
&&
2967 if (strlen (content
) > 1) {
2969 m
->mp_content_before
= content
;
2971 m
->mp_content_after
= content
;
2985 ct_type_str (int type
) {
2987 case CT_APPLICATION
:
2988 return "application";
3004 return "unknown_type";
3010 ct_subtype_str (int type
, int subtype
) {
3012 case CT_APPLICATION
:
3014 case APPLICATION_OCTETS
:
3016 case APPLICATION_POSTSCRIPT
:
3017 return "postscript";
3019 return "unknown_app_subtype";
3023 case MESSAGE_RFC822
:
3025 case MESSAGE_PARTIAL
:
3027 case MESSAGE_EXTERNAL
:
3030 return "unknown_msg_subtype";
3036 case MULTI_ALTERNATE
:
3037 return "alternative";
3040 case MULTI_PARALLEL
:
3043 return "unknown_multipart_subtype";
3054 return "unknown_text_subtype";
3057 return "unknown_type";
3062 /* Find the content type and InitFunc for the CT. */
3063 const struct str2init
*
3064 get_ct_init (int type
) {
3065 const struct str2init
*sp
;
3067 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3068 if (type
== sp
->si_val
) {
3077 ce_str (int encoding
) {
3098 /* Find the content type and InitFunc for the content encoding method. */
3099 const struct str2init
*
3100 get_ce_method (const char *method
) {
3101 struct str2init
*sp
;
3103 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3104 if (! strcasecmp (method
, sp
->si_key
)) {