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 static 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
1320 reverse_parts (CT ct
)
1323 struct multipart
*m
;
1324 struct part
**base
, **bmp
, **next
, *part
;
1326 m
= (struct multipart
*) ct
->c_ctparams
;
1328 /* if only one part, just return */
1329 if (!m
->mp_parts
|| !m
->mp_parts
->mp_next
)
1332 /* count number of parts */
1334 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1337 /* allocate array of pointers to the parts */
1338 if (!(base
= (struct part
**) calloc ((size_t) (i
+ 1), sizeof(*base
))))
1339 adios (NULL
, "out of memory");
1342 /* point at all the parts */
1343 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1347 /* reverse the order of the parts */
1348 next
= &m
->mp_parts
;
1349 for (bmp
--; bmp
>= base
; bmp
--) {
1352 next
= &part
->mp_next
;
1356 /* free array of pointers */
1357 free ((char *) base
);
1369 CI ci
= &ct
->c_ctinfo
;
1371 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1373 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1374 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1378 /* check for missing subtype */
1379 if (!*ci
->ci_subtype
)
1380 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1383 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1384 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1386 ct
->c_subtype
= kv
->kv_value
;
1388 switch (ct
->c_subtype
) {
1389 case MESSAGE_RFC822
:
1392 case MESSAGE_PARTIAL
:
1397 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1398 adios (NULL
, "out of memory");
1399 ct
->c_ctparams
= (void *) p
;
1401 /* scan for parameters "id", "number", and "total" */
1402 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1403 if (!mh_strcasecmp (*ap
, "id")) {
1404 p
->pm_partid
= add (*ep
, NULL
);
1407 if (!mh_strcasecmp (*ap
, "number")) {
1408 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1409 || p
->pm_partno
< 1) {
1412 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1413 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1414 ct
->c_file
, TYPE_FIELD
);
1419 if (!mh_strcasecmp (*ap
, "total")) {
1420 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1429 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1431 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1432 ci
->ci_type
, ci
->ci_subtype
,
1433 ct
->c_file
, TYPE_FIELD
);
1439 case MESSAGE_EXTERNAL
:
1446 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1447 adios (NULL
, "out of memory");
1448 ct
->c_ctparams
= (void *) e
;
1451 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1452 advise (ct
->c_file
, "unable to open for reading");
1456 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1458 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1466 if ((exresult
= params_external (ct
, 0)) != NOTOK
1467 && p
->c_ceopenfnx
== openMail
) {
1471 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1473 content_error (NULL
, ct
,
1474 "empty body for access-type=mail-server");
1478 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1479 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1481 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1483 adios ("failed", "fread");
1486 adios (NULL
, "unexpected EOF from fread");
1489 bp
+= cc
, size
-= cc
;
1496 p
->c_end
= p
->c_begin
;
1501 if (exresult
== NOTOK
)
1503 if (e
->eb_flags
== NOTOK
)
1506 switch (p
->c_type
) {
1511 if (p
->c_subtype
!= MESSAGE_RFC822
)
1515 e
->eb_partno
= ct
->c_partno
;
1517 (*p
->c_ctinitfnx
) (p
);
1532 params_external (CT ct
, int composing
)
1535 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1536 CI ci
= &ct
->c_ctinfo
;
1538 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1539 if (!mh_strcasecmp (*ap
, "access-type")) {
1540 struct str2init
*s2i
;
1541 CT p
= e
->eb_content
;
1543 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1544 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1548 e
->eb_flags
= NOTOK
;
1549 p
->c_encoding
= CE_EXTERNAL
;
1552 e
->eb_access
= s2i
->si_key
;
1553 e
->eb_flags
= s2i
->si_val
;
1554 p
->c_encoding
= CE_EXTERNAL
;
1556 /* Call the Init function for this external type */
1557 if ((*s2i
->si_init
)(p
) == NOTOK
)
1561 if (!mh_strcasecmp (*ap
, "name")) {
1565 if (!mh_strcasecmp (*ap
, "permission")) {
1566 e
->eb_permission
= *ep
;
1569 if (!mh_strcasecmp (*ap
, "site")) {
1573 if (!mh_strcasecmp (*ap
, "directory")) {
1577 if (!mh_strcasecmp (*ap
, "mode")) {
1581 if (!mh_strcasecmp (*ap
, "size")) {
1582 sscanf (*ep
, "%lu", &e
->eb_size
);
1585 if (!mh_strcasecmp (*ap
, "server")) {
1589 if (!mh_strcasecmp (*ap
, "subject")) {
1590 e
->eb_subject
= *ep
;
1593 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1594 e
->eb_body
= getcpy (*ep
);
1599 if (!e
->eb_access
) {
1601 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1602 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1615 InitApplication (CT ct
)
1618 CI ci
= &ct
->c_ctinfo
;
1621 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1622 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1624 ct
->c_subtype
= kv
->kv_value
;
1631 * TRANSFER ENCODINGS
1635 init_encoding (CT ct
, OpenCEFunc openfnx
)
1639 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
1640 adios (NULL
, "out of memory");
1643 ct
->c_ceopenfnx
= openfnx
;
1644 ct
->c_ceclosefnx
= close_encoding
;
1645 ct
->c_cesizefnx
= size_encoding
;
1652 close_encoding (CT ct
)
1656 if (!(ce
= ct
->c_cefile
))
1666 static unsigned long
1667 size_encoding (CT ct
)
1675 if (!(ce
= ct
->c_cefile
))
1676 return (ct
->c_end
- ct
->c_begin
);
1678 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1679 return (long) st
.st_size
;
1682 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1683 return (long) st
.st_size
;
1688 if (ct
->c_encoding
== CE_EXTERNAL
)
1689 return (ct
->c_end
- ct
->c_begin
);
1692 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1693 return (ct
->c_end
- ct
->c_begin
);
1695 if (fstat (fd
, &st
) != NOTOK
)
1696 size
= (long) st
.st_size
;
1700 (*ct
->c_ceclosefnx
) (ct
);
1709 static unsigned char b642nib
[0x80] = {
1710 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1711 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1713 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1714 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1715 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1716 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1717 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1718 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1719 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1720 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1721 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1722 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1723 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1724 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1725 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1732 return init_encoding (ct
, openBase64
);
1737 openBase64 (CT ct
, char **file
)
1739 int bitno
, cc
, digested
;
1740 int fd
, len
, skip
, own_ct_fp
= 0;
1742 unsigned char value
, b
;
1743 char *cp
, *ep
, buffer
[BUFSIZ
];
1744 /* sbeck -- handle suffixes */
1751 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1756 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1757 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1763 if (*file
== NULL
) {
1764 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1767 ce
->ce_file
= add (*file
, NULL
);
1771 /* sbeck@cise.ufl.edu -- handle suffixes */
1773 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1774 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1775 cp
= context_find (buffer
);
1776 if (cp
== NULL
|| *cp
== '\0') {
1777 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1779 cp
= context_find (buffer
);
1781 if (cp
!= NULL
&& *cp
!= '\0') {
1782 if (ce
->ce_unlink
) {
1783 /* Temporary file already exists, so we rename to
1784 version with extension. */
1785 char *file_org
= strdup(ce
->ce_file
);
1786 ce
->ce_file
= add (cp
, ce
->ce_file
);
1787 if (rename(file_org
, ce
->ce_file
)) {
1788 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1793 ce
->ce_file
= add (cp
, ce
->ce_file
);
1797 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1798 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1802 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1803 adios (NULL
, "internal error(1)");
1806 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1807 content_error (ct
->c_file
, ct
, "unable to open for reading");
1813 if ((digested
= ct
->c_digested
))
1814 MD5Init (&mdContext
);
1820 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1822 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1824 content_error (ct
->c_file
, ct
, "error reading from");
1828 content_error (NULL
, ct
, "premature eof");
1836 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1839 if (isspace ((unsigned char) *cp
))
1841 if (skip
|| (((unsigned char) *cp
) & 0x80)
1842 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1844 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1845 (unsigned char) *cp
,
1846 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1849 content_error (NULL
, ct
,
1850 "invalid BASE64 encoding -- continuing");
1854 bits
|= value
<< bitno
;
1856 if ((bitno
-= 6) < 0) {
1857 b
= (bits
>> 16) & 0xff;
1858 putc ((char) b
, ce
->ce_fp
);
1860 MD5Update (&mdContext
, &b
, 1);
1862 b
= (bits
>> 8) & 0xff;
1863 putc ((char) b
, ce
->ce_fp
);
1865 MD5Update (&mdContext
, &b
, 1);
1868 putc ((char) b
, ce
->ce_fp
);
1870 MD5Update (&mdContext
, &b
, 1);
1874 if (ferror (ce
->ce_fp
)) {
1875 content_error (ce
->ce_file
, ct
,
1876 "error writing to");
1879 bitno
= 18, bits
= 0L, skip
= 0;
1885 goto self_delimiting
;
1894 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1896 content_error (NULL
, ct
, "invalid BASE64 encoding");
1901 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1903 if (fflush (ce
->ce_fp
)) {
1904 content_error (ce
->ce_file
, ct
, "error writing to");
1909 unsigned char digest
[16];
1911 MD5Final (digest
, &mdContext
);
1912 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1913 sizeof(digest
) / sizeof(digest
[0])))
1914 content_error (NULL
, ct
,
1915 "content integrity suspect (digest mismatch) -- continuing");
1918 fprintf (stderr
, "content integrity confirmed\n");
1921 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1924 *file
= ce
->ce_file
;
1929 return fileno (ce
->ce_fp
);
1936 free_encoding (ct
, 0);
1945 static char hex2nib
[0x80] = {
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1951 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1952 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1953 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1954 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1955 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1956 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1957 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1958 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1959 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1960 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1961 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1968 return init_encoding (ct
, openQuoted
);
1973 openQuoted (CT ct
, char **file
)
1975 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1977 char buffer
[BUFSIZ
];
1980 /* sbeck -- handle suffixes */
1986 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1991 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1992 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1998 if (*file
== NULL
) {
1999 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2002 ce
->ce_file
= add (*file
, NULL
);
2006 /* sbeck@cise.ufl.edu -- handle suffixes */
2008 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2009 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2010 cp
= context_find (buffer
);
2011 if (cp
== NULL
|| *cp
== '\0') {
2012 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2014 cp
= context_find (buffer
);
2016 if (cp
!= NULL
&& *cp
!= '\0') {
2017 if (ce
->ce_unlink
) {
2018 /* Temporary file already exists, so we rename to
2019 version with extension. */
2020 char *file_org
= strdup(ce
->ce_file
);
2021 ce
->ce_file
= add (cp
, ce
->ce_file
);
2022 if (rename(file_org
, ce
->ce_file
)) {
2023 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2028 ce
->ce_file
= add (cp
, ce
->ce_file
);
2032 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2033 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2037 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2038 adios (NULL
, "internal error(2)");
2041 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2042 content_error (ct
->c_file
, ct
, "unable to open for reading");
2048 if ((digested
= ct
->c_digested
))
2049 MD5Init (&mdContext
);
2056 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2058 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2059 content_error (NULL
, ct
, "premature eof");
2063 if ((cc
= strlen (buffer
)) > len
)
2067 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2068 if (!isspace ((unsigned char) *ep
))
2072 for (; cp
< ep
; cp
++) {
2074 /* in an escape sequence */
2076 /* at byte 1 of an escape sequence */
2077 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2078 /* next is byte 2 */
2081 /* at byte 2 of an escape sequence */
2083 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2084 putc (mask
, ce
->ce_fp
);
2086 MD5Update (&mdContext
, &mask
, 1);
2087 if (ferror (ce
->ce_fp
)) {
2088 content_error (ce
->ce_file
, ct
, "error writing to");
2091 /* finished escape sequence; next may be literal or a new
2092 * escape sequence */
2095 /* on to next byte */
2099 /* not in an escape sequence */
2101 /* starting an escape sequence, or invalid '='? */
2102 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2103 /* "=\n" soft line break, eat the \n */
2107 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2108 /* We don't have 2 bytes left, so this is an invalid
2109 * escape sequence; just show the raw bytes (below). */
2110 } else if (isxdigit ((unsigned char) cp
[1]) &&
2111 isxdigit ((unsigned char) cp
[2])) {
2112 /* Next 2 bytes are hex digits, making this a valid escape
2113 * sequence; let's decode it (above). */
2117 /* One or both of the next 2 is out of range, making this
2118 * an invalid escape sequence; just show the raw bytes
2123 /* Just show the raw byte. */
2124 putc (*cp
, ce
->ce_fp
);
2127 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2129 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2132 if (ferror (ce
->ce_fp
)) {
2133 content_error (ce
->ce_file
, ct
, "error writing to");
2139 content_error (NULL
, ct
,
2140 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2144 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2146 if (fflush (ce
->ce_fp
)) {
2147 content_error (ce
->ce_file
, ct
, "error writing to");
2152 unsigned char digest
[16];
2154 MD5Final (digest
, &mdContext
);
2155 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2156 sizeof(digest
) / sizeof(digest
[0])))
2157 content_error (NULL
, ct
,
2158 "content integrity suspect (digest mismatch) -- continuing");
2161 fprintf (stderr
, "content integrity confirmed\n");
2164 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2167 *file
= ce
->ce_file
;
2172 return fileno (ce
->ce_fp
);
2175 free_encoding (ct
, 0);
2191 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2194 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2200 open7Bit (CT ct
, char **file
)
2202 int cc
, fd
, len
, own_ct_fp
= 0;
2203 char buffer
[BUFSIZ
];
2204 /* sbeck -- handle suffixes */
2211 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2216 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2217 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2223 if (*file
== NULL
) {
2224 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2227 ce
->ce_file
= add (*file
, NULL
);
2231 /* sbeck@cise.ufl.edu -- handle suffixes */
2233 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2234 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2235 cp
= context_find (buffer
);
2236 if (cp
== NULL
|| *cp
== '\0') {
2237 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2239 cp
= context_find (buffer
);
2241 if (cp
!= NULL
&& *cp
!= '\0') {
2242 if (ce
->ce_unlink
) {
2243 /* Temporary file already exists, so we rename to
2244 version with extension. */
2245 char *file_org
= strdup(ce
->ce_file
);
2246 ce
->ce_file
= add (cp
, ce
->ce_file
);
2247 if (rename(file_org
, ce
->ce_file
)) {
2248 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2253 ce
->ce_file
= add (cp
, ce
->ce_file
);
2257 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2258 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2262 if (ct
->c_type
== CT_MULTIPART
) {
2264 CI ci
= &ct
->c_ctinfo
;
2267 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2268 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2269 + 1 + strlen (ci
->ci_subtype
);
2270 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2271 putc (';', ce
->ce_fp
);
2274 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2276 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2277 fputs ("\n\t", ce
->ce_fp
);
2280 putc (' ', ce
->ce_fp
);
2283 fprintf (ce
->ce_fp
, "%s", buffer
);
2287 if (ci
->ci_comment
) {
2288 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2289 fputs ("\n\t", ce
->ce_fp
);
2293 putc (' ', ce
->ce_fp
);
2296 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2299 fprintf (ce
->ce_fp
, "\n");
2301 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2303 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2305 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2306 fprintf (ce
->ce_fp
, "\n");
2309 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2310 adios (NULL
, "internal error(3)");
2313 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2314 content_error (ct
->c_file
, ct
, "unable to open for reading");
2320 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2322 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2324 content_error (ct
->c_file
, ct
, "error reading from");
2328 content_error (NULL
, ct
, "premature eof");
2336 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2337 if (ferror (ce
->ce_fp
)) {
2338 content_error (ce
->ce_file
, ct
, "error writing to");
2343 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2345 if (fflush (ce
->ce_fp
)) {
2346 content_error (ce
->ce_file
, ct
, "error writing to");
2350 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2353 *file
= ce
->ce_file
;
2358 return fileno (ce
->ce_fp
);
2361 free_encoding (ct
, 0);
2375 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2377 char cachefile
[BUFSIZ
];
2380 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2385 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2386 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2392 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2393 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2394 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2395 ce
->ce_file
= getcpy (cachefile
);
2399 admonish (cachefile
, "unable to fopen for reading");
2406 *file
= ce
->ce_file
;
2407 *fd
= fileno (ce
->ce_fp
);
2418 return init_encoding (ct
, openFile
);
2423 openFile (CT ct
, char **file
)
2426 char cachefile
[BUFSIZ
];
2427 struct exbody
*e
= ct
->c_ctexbody
;
2428 CE ce
= ct
->c_cefile
;
2430 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2442 content_error (NULL
, ct
, "missing name parameter");
2446 ce
->ce_file
= getcpy (e
->eb_name
);
2449 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2450 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2454 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2455 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2456 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2460 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2461 if ((fp
= fopen (cachefile
, "w"))) {
2463 char buffer
[BUFSIZ
];
2464 FILE *gp
= ce
->ce_fp
;
2466 fseek (gp
, 0L, SEEK_SET
);
2468 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2470 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2474 admonish (ce
->ce_file
, "error reading");
2479 admonish (cachefile
, "error writing");
2487 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2488 *file
= ce
->ce_file
;
2489 return fileno (ce
->ce_fp
);
2499 return init_encoding (ct
, openFTP
);
2504 openFTP (CT ct
, char **file
)
2506 int cachetype
, caching
, fd
;
2508 char *bp
, *ftp
, *user
, *pass
;
2509 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2512 static char *username
= NULL
;
2513 static char *password
= NULL
;
2518 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2524 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2535 if (!e
->eb_name
|| !e
->eb_site
) {
2536 content_error (NULL
, ct
, "missing %s parameter",
2537 e
->eb_name
? "site": "name");
2544 pidcheck (pidwait (xpid
, NOTOK
));
2548 /* Get the buffer ready to go */
2550 buflen
= sizeof(buffer
);
2553 * Construct the query message for user
2555 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2561 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2567 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2568 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2573 if (e
->eb_size
> 0) {
2574 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2579 snprintf (bp
, buflen
, "? ");
2582 * Now, check the answer
2584 if (!getanswer (buffer
))
2589 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2593 ruserpass (e
->eb_site
, &username
, &password
);
2598 ce
->ce_unlink
= (*file
== NULL
);
2600 cachefile
[0] = '\0';
2601 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2602 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2603 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2604 if (*file
== NULL
) {
2611 ce
->ce_file
= add (*file
, NULL
);
2613 ce
->ce_file
= add (cachefile
, NULL
);
2615 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2617 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2618 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2623 int child_id
, i
, vecp
;
2627 vec
[vecp
++] = r1bindex (ftp
, '/');
2628 vec
[vecp
++] = e
->eb_site
;
2631 vec
[vecp
++] = e
->eb_dir
;
2632 vec
[vecp
++] = e
->eb_name
;
2633 vec
[vecp
++] = ce
->ce_file
,
2634 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2635 ? "ascii" : "binary";
2640 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2644 adios ("fork", "unable to");
2648 close (fileno (ce
->ce_fp
));
2650 fprintf (stderr
, "unable to exec ");
2656 if (pidXwait (child_id
, NULL
)) {
2657 username
= password
= NULL
;
2667 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2672 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2673 if ((fp
= fopen (cachefile
, "w"))) {
2675 FILE *gp
= ce
->ce_fp
;
2677 fseek (gp
, 0L, SEEK_SET
);
2679 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2681 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2685 admonish (ce
->ce_file
, "error reading");
2690 admonish (cachefile
, "error writing");
2699 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2700 *file
= ce
->ce_file
;
2701 return fileno (ce
->ce_fp
);
2712 return init_encoding (ct
, openMail
);
2717 openMail (CT ct
, char **file
)
2719 int child_id
, fd
, i
, vecp
;
2721 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2722 struct exbody
*e
= ct
->c_ctexbody
;
2723 CE ce
= ct
->c_cefile
;
2725 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2736 if (!e
->eb_server
) {
2737 content_error (NULL
, ct
, "missing server parameter");
2744 pidcheck (pidwait (xpid
, NOTOK
));
2748 /* Get buffer ready to go */
2750 buflen
= sizeof(buffer
);
2752 /* Now, construct query message */
2753 snprintf (bp
, buflen
, "Retrieve content");
2759 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2765 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2767 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2769 /* Now, check answer */
2770 if (!getanswer (buffer
))
2774 vec
[vecp
++] = r1bindex (mailproc
, '/');
2775 vec
[vecp
++] = e
->eb_server
;
2776 vec
[vecp
++] = "-subject";
2777 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2778 vec
[vecp
++] = "-body";
2779 vec
[vecp
++] = e
->eb_body
;
2782 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2786 advise ("fork", "unable to");
2790 execvp (mailproc
, vec
);
2791 fprintf (stderr
, "unable to exec ");
2797 if (pidXwait (child_id
, NULL
) == OK
)
2798 advise (NULL
, "request sent");
2802 if (*file
== NULL
) {
2803 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2806 ce
->ce_file
= add (*file
, NULL
);
2810 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2811 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2815 /* showproc is for mhshow and mhstore, though mhlist -debug
2816 * prints it, too. */
2818 free (ct
->c_showproc
);
2819 ct
->c_showproc
= add ("true", NULL
);
2821 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2822 *file
= ce
->ce_file
;
2823 return fileno (ce
->ce_fp
);
2828 readDigest (CT ct
, char *cp
)
2833 unsigned char *dp
, value
, *ep
;
2839 for (ep
= (dp
= ct
->c_digest
)
2840 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2845 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2847 fprintf (stderr
, "invalid BASE64 encoding\n");
2851 bits
|= value
<< bitno
;
2853 if ((bitno
-= 6) < 0) {
2854 if (dp
+ (3 - skip
) > ep
)
2855 goto invalid_digest
;
2856 *dp
++ = (bits
>> 16) & 0xff;
2858 *dp
++ = (bits
>> 8) & 0xff;
2860 *dp
++ = bits
& 0xff;
2870 goto self_delimiting
;
2875 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2885 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2893 fprintf (stderr
, "MD5 digest=");
2894 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2895 fprintf (stderr
, "%02x", *dp
& 0xff);
2896 fprintf (stderr
, "\n");
2903 /* Multipart parts might have content before the first subpart and/or
2904 after the last subpart that hasn't been stored anywhere else, so do
2907 get_leftover_mp_content (CT ct
, int before
/* or after */) {
2908 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2910 int found_boundary
= 0;
2911 char buffer
[BUFSIZ
];
2914 char *content
= NULL
;
2916 if (! m
) return NOTOK
;
2919 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2921 /* Isolate the beginning of this part to the beginning of the
2922 first subpart and save any content between them. */
2923 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2924 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2925 boundary
= concat ("--", m
->mp_start
, NULL
);
2927 struct part
*last_subpart
= NULL
;
2928 struct part
*subpart
;
2930 /* Go to the last subpart to get its end position. */
2931 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
2932 last_subpart
= subpart
;
2935 if (last_subpart
== NULL
) return NOTOK
;
2937 /* Isolate the end of the last subpart to the end of this part
2938 and save any content between them. */
2939 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
2940 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
2941 boundary
= concat ("--", m
->mp_stop
, NULL
);
2944 /* Back up by 1 to pick up the newline. */
2945 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
2946 read
+= strlen (buffer
);
2947 /* Don't look beyond beginning of first subpart (before) or
2948 next part (after). */
2949 if (read
> max
) buffer
[read
-max
] = '\0';
2952 if (! strcmp (buffer
, boundary
)) {
2956 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
2962 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
2964 char *old_content
= content
;
2965 content
= concat (content
, buffer
, NULL
);
2969 ? concat ("\n", buffer
, NULL
)
2970 : concat (buffer
, NULL
);
2975 if (found_boundary
|| read
> max
) break;
2977 if (read
> max
) break;
2981 /* Skip the newline if that's all there is. */
2985 /* Remove trailing newline, except at EOF. */
2986 if ((before
|| ! feof (ct
->c_fp
)) &&
2987 (cp
= content
+ strlen (content
)) > content
&&
2992 if (strlen (content
) > 1) {
2994 m
->mp_content_before
= content
;
2996 m
->mp_content_after
= content
;