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>
17 #include <h/mhparse.h>
21 #endif /* HAVE_ICONV */
27 extern int rcachesw
; /* mhcachesbr.c */
28 extern int wcachesw
; /* mhcachesbr.c */
30 int checksw
= 0; /* check Content-MD5 field */
33 * These are for mhfixmsg to:
34 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
36 * 2) Suppress the warning about bogus multipart content, and report it.
38 int skip_mp_cte_check
;
39 int suppress_bogus_mp_content_warning
;
43 * Structures for TEXT messages
45 struct k2v SubText
[] = {
46 { "plain", TEXT_PLAIN
},
47 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
48 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
49 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
52 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
55 * Structures for MULTIPART messages
57 struct k2v SubMultiPart
[] = {
58 { "mixed", MULTI_MIXED
},
59 { "alternative", MULTI_ALTERNATE
},
60 { "digest", MULTI_DIGEST
},
61 { "parallel", MULTI_PARALLEL
},
62 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
66 * Structures for MESSAGE messages
68 struct k2v SubMessage
[] = {
69 { "rfc822", MESSAGE_RFC822
},
70 { "partial", MESSAGE_PARTIAL
},
71 { "external-body", MESSAGE_EXTERNAL
},
72 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
76 * Structure for APPLICATION messages
78 struct k2v SubApplication
[] = {
79 { "octet-stream", APPLICATION_OCTETS
},
80 { "postscript", APPLICATION_POSTSCRIPT
},
81 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
85 * Mapping of names of CTE types in mhbuild directives
87 static struct k2v EncodingType
[] = {
91 { "quoted-printable", CE_QUOTED
},
93 { "base64", CE_BASE64
},
99 int find_cache (CT
, int, int *, char *, char *, int);
102 int part_ok (CT
, int);
103 int type_ok (CT
, int);
104 void content_error (char *, CT
, char *, ...);
107 void free_encoding (CT
, int);
112 static CT
get_content (FILE *, char *, int);
113 static int get_comment (const char *, const char *, char **, char **);
115 static int InitGeneric (CT
);
116 static int InitText (CT
);
117 static int InitMultiPart (CT
);
118 static void reverse_parts (CT
);
119 static int InitMessage (CT
);
120 static int InitApplication (CT
);
121 static int init_encoding (CT
, OpenCEFunc
);
122 static unsigned long size_encoding (CT
);
123 static int InitBase64 (CT
);
124 static int openBase64 (CT
, char **);
125 static int InitQuoted (CT
);
126 static int openQuoted (CT
, char **);
127 static int Init7Bit (CT
);
128 static int openExternal (CT
, CT
, CE
, char **, int *);
129 static int InitFile (CT
);
130 static int openFile (CT
, char **);
131 static int InitFTP (CT
);
132 static int openFTP (CT
, char **);
133 static int InitMail (CT
);
134 static int openMail (CT
, char **);
135 static int readDigest (CT
, char *);
136 static int get_leftover_mp_content (CT
, int);
137 static int InitURL (CT
);
138 static int openURL (CT
, char **);
139 static int parse_header_attrs (const char *, const char *, char **, PM
*,
141 static size_t param_len(PM
, int, size_t, int *, int *, size_t *);
142 static size_t encode_param(PM
, char *, size_t, size_t, size_t, int);
143 static size_t normal_param(PM
, char *, size_t, size_t, size_t);
144 static int get_dispo (char *, CT
, int);
146 struct str2init str2cts
[] = {
147 { "application", CT_APPLICATION
, InitApplication
},
148 { "audio", CT_AUDIO
, InitGeneric
},
149 { "image", CT_IMAGE
, InitGeneric
},
150 { "message", CT_MESSAGE
, InitMessage
},
151 { "multipart", CT_MULTIPART
, InitMultiPart
},
152 { "text", CT_TEXT
, InitText
},
153 { "video", CT_VIDEO
, InitGeneric
},
154 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
155 { NULL
, CT_UNKNOWN
, NULL
},
158 struct str2init str2ces
[] = {
159 { "base64", CE_BASE64
, InitBase64
},
160 { "quoted-printable", CE_QUOTED
, InitQuoted
},
161 { "8bit", CE_8BIT
, Init7Bit
},
162 { "7bit", CE_7BIT
, Init7Bit
},
163 { "binary", CE_BINARY
, Init7Bit
},
164 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
165 { NULL
, CE_UNKNOWN
, NULL
},
169 * NOTE WELL: si_key MUST NOT have value of NOTOK
171 * si_key is 1 if access method is anonymous.
173 struct str2init str2methods
[] = {
174 { "afs", 1, InitFile
},
175 { "anon-ftp", 1, InitFTP
},
176 { "ftp", 0, InitFTP
},
177 { "local-file", 0, InitFile
},
178 { "mail-server", 0, InitMail
},
179 { "url", 0, InitURL
},
185 * Main entry point for parsing a MIME message or file.
186 * It returns the Content structure for the top level
187 * entity in the file.
191 parse_mime (char *file
)
200 * Check if file is actually standard input
202 if ((is_stdin
= !(strcmp (file
, "-")))) {
203 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
205 advise("mhparse", "unable to create temporary file in %s",
209 file
= add (tfile
, NULL
);
211 while ((n
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0) {
212 if (fwrite(buffer
, 1, n
, fp
) != n
) {
213 (void) m_unlink (file
);
214 advise (file
, "error copying to temporary file");
220 if (ferror (stdin
)) {
221 (void) m_unlink (file
);
222 advise ("stdin", "error reading");
226 (void) m_unlink (file
);
227 advise (file
, "error writing");
230 fseek (fp
, 0L, SEEK_SET
);
231 } else if ((fp
= fopen (file
, "r")) == NULL
) {
232 advise (file
, "unable to read");
236 if (!(ct
= get_content (fp
, file
, 1))) {
238 (void) m_unlink (file
);
239 advise (NULL
, "unable to decode %s", file
);
244 ct
->c_unlink
= 1; /* temp file to remove */
248 if (ct
->c_end
== 0L) {
249 fseek (fp
, 0L, SEEK_END
);
250 ct
->c_end
= ftell (fp
);
253 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
265 * Main routine for reading/parsing the headers
266 * of a message content.
268 * toplevel = 1 # we are at the top level of the message
269 * toplevel = 0 # we are inside message type or multipart type
270 * # other than multipart/digest
271 * toplevel = -1 # we are inside multipart/digest
272 * NB: on failure we will fclose(in)!
276 get_content (FILE *in
, char *file
, int toplevel
)
279 char buf
[BUFSIZ
], name
[NAMESZ
];
283 m_getfld_state_t gstate
= 0;
285 /* allocate the content structure */
286 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
287 adios (NULL
, "out of memory");
290 ct
->c_file
= add (file
, NULL
);
291 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
294 * Parse the header fields for this
295 * content into a linked list.
297 m_getfld_track_filepos (&gstate
, in
);
298 for (compnum
= 1;;) {
299 int bufsz
= sizeof buf
;
300 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
305 /* get copies of the buffers */
306 np
= add (name
, NULL
);
307 vp
= add (buf
, NULL
);
309 /* if necessary, get rest of field */
310 while (state
== FLDPLUS
) {
312 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
313 vp
= add (buf
, vp
); /* add to previous value */
316 /* Now add the header data to the list */
317 add_header (ct
, np
, vp
);
319 /* continue, to see if this isn't the last header field */
320 ct
->c_begin
= ftell (in
) + 1;
324 ct
->c_begin
= ftell (in
) - strlen (buf
);
328 ct
->c_begin
= ftell (in
);
333 adios (NULL
, "message format error in component #%d", compnum
);
336 adios (NULL
, "getfld() returned %d", state
);
339 /* break out of the loop */
342 m_getfld_state_destroy (&gstate
);
345 * Read the content headers. We will parse the
346 * MIME related header fields into their various
347 * structures and set internal flags related to
348 * content type/subtype, etc.
351 hp
= ct
->c_first_hf
; /* start at first header field */
353 /* Get MIME-Version field */
354 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
359 advise (NULL
, "message %s has multiple %s: fields",
360 ct
->c_file
, VRSN_FIELD
);
363 ct
->c_vrsn
= add (hp
->value
, NULL
);
365 /* Now, cleanup this field */
368 while (isspace ((unsigned char) *cp
))
370 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
372 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
373 if (!isspace ((unsigned char) *dp
))
377 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
380 get_comment (ct
->c_file
, VRSN_FIELD
, &cp
, NULL
) == NOTOK
)
383 for (dp
= cp
; istoken (*dp
); dp
++)
387 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
390 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
391 ct
->c_file
, VRSN_FIELD
, cp
);
394 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
395 /* Get Content-Type field */
396 struct str2init
*s2i
;
397 CI ci
= &ct
->c_ctinfo
;
399 /* Check if we've already seen a Content-Type header */
401 advise (NULL
, "message %s has multiple %s: fields",
402 ct
->c_file
, TYPE_FIELD
);
406 /* Parse the Content-Type field */
407 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
411 * Set the Init function and the internal
412 * flag for this content type.
414 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
415 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
417 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
419 ct
->c_type
= s2i
->si_val
;
420 ct
->c_ctinitfnx
= s2i
->si_init
;
422 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
423 /* Get Content-Transfer-Encoding field */
425 struct str2init
*s2i
;
428 * Check if we've already seen the
429 * Content-Transfer-Encoding field
432 advise (NULL
, "message %s has multiple %s: fields",
433 ct
->c_file
, ENCODING_FIELD
);
437 /* get copy of this field */
438 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
440 while (isspace ((unsigned char) *cp
))
442 for (dp
= cp
; istoken (*dp
); dp
++)
448 * Find the internal flag and Init function
449 * for this transfer encoding.
451 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
452 if (!strcasecmp (cp
, s2i
->si_key
))
454 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
457 ct
->c_encoding
= s2i
->si_val
;
459 /* Call the Init function for this encoding */
460 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
463 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
464 /* Get Content-MD5 field */
470 if (ct
->c_digested
) {
471 advise (NULL
, "message %s has multiple %s: fields",
472 ct
->c_file
, MD5_FIELD
);
476 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
478 while (isspace ((unsigned char) *cp
))
480 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
482 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
483 if (!isspace ((unsigned char) *dp
))
487 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
490 get_comment (ct
->c_file
, MD5_FIELD
, &cp
, NULL
) == NOTOK
) {
495 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
503 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
504 /* Get Content-ID field */
505 ct
->c_id
= add (hp
->value
, ct
->c_id
);
507 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
508 /* Get Content-Description field */
509 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
511 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
512 /* Get Content-Disposition field */
513 if (get_dispo(hp
->value
, ct
, 0) == NOTOK
)
518 hp
= hp
->next
; /* next header field */
522 * Check if we saw a Content-Type field.
523 * If not, then assign a default value for
524 * it, and the Init function.
528 * If we are inside a multipart/digest message,
529 * so default type is message/rfc822
532 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
534 ct
->c_type
= CT_MESSAGE
;
535 ct
->c_ctinitfnx
= InitMessage
;
538 * Else default type is text/plain
540 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
542 ct
->c_type
= CT_TEXT
;
543 ct
->c_ctinitfnx
= InitText
;
547 /* Use default Transfer-Encoding, if necessary */
549 ct
->c_encoding
= CE_7BIT
;
562 * small routine to add header field to list
566 add_header (CT ct
, char *name
, char *value
)
570 /* allocate header field structure */
571 hp
= mh_xmalloc (sizeof(*hp
));
573 /* link data into header structure */
578 /* link header structure into the list */
579 if (ct
->c_first_hf
== NULL
) {
580 ct
->c_first_hf
= hp
; /* this is the first */
583 ct
->c_last_hf
->next
= hp
; /* add it to the end */
592 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
593 * directives. Fills in the information of the CTinfo structure.
596 get_ctinfo (char *cp
, CT ct
, int magic
)
605 /* store copy of Content-Type line */
606 cp
= ct
->c_ctline
= add (cp
, NULL
);
608 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
611 /* change newlines to spaces */
612 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
615 /* trim trailing spaces */
616 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
617 if (!isspace ((unsigned char) *dp
))
622 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
624 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
625 &ci
->ci_comment
) == NOTOK
)
628 for (dp
= cp
; istoken (*dp
); dp
++)
631 ci
->ci_type
= add (cp
, NULL
); /* store content type */
635 advise (NULL
, "invalid %s: field in message %s (empty type)",
636 TYPE_FIELD
, ct
->c_file
);
640 /* down case the content type string */
641 for (dp
= ci
->ci_type
; *dp
; dp
++)
642 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
643 *dp
= tolower ((unsigned char) *dp
);
645 while (isspace ((unsigned char) *cp
))
648 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
649 &ci
->ci_comment
) == NOTOK
)
654 ci
->ci_subtype
= add ("", NULL
);
659 while (isspace ((unsigned char) *cp
))
662 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
663 &ci
->ci_comment
) == NOTOK
)
666 for (dp
= cp
; istoken (*dp
); dp
++)
669 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
672 if (!*ci
->ci_subtype
) {
674 "invalid %s: field in message %s (empty subtype for \"%s\")",
675 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
679 /* down case the content subtype string */
680 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
681 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
682 *dp
= tolower ((unsigned char) *dp
);
685 while (isspace ((unsigned char) *cp
))
688 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
689 &ci
->ci_comment
) == NOTOK
)
692 if ((status
= parse_header_attrs (ct
->c_file
, TYPE_FIELD
, &cp
,
693 &ci
->ci_first_pm
, &ci
->ci_last_pm
,
694 &ci
->ci_comment
)) != OK
) {
695 return status
== NOTOK
? NOTOK
: OK
;
699 * Get any <Content-Id> given in buffer
701 if (magic
&& *cp
== '<') {
706 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
707 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
713 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
719 while (isspace ((unsigned char) *cp
))
724 * Get any [Content-Description] given in buffer.
726 if (magic
&& *cp
== '[') {
728 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
732 advise (NULL
, "invalid description in message %s", ct
->c_file
);
740 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
746 while (isspace ((unsigned char) *cp
))
751 * Get any {Content-Disposition} given in buffer.
753 if (magic
&& *cp
== '{') {
755 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
759 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
767 if (get_dispo(cp
, ct
, 1) != OK
)
773 while (isspace ((unsigned char) *cp
))
778 * Get any extension directives (right now just the content transfer
779 * encoding, but maybe others) that we care about.
782 if (magic
&& *cp
== '*') {
784 * See if it's a CTE we match on
789 while (*cp
!= '\0' && ! isspace((unsigned char) *cp
))
793 advise (NULL
, "invalid null transfer encoding specification");
800 ct
->c_reqencoding
= CE_UNKNOWN
;
802 for (kv
= EncodingType
; kv
->kv_key
; kv
++) {
803 if (strcasecmp(kv
->kv_key
, dp
) == 0) {
804 ct
->c_reqencoding
= kv
->kv_value
;
809 if (ct
->c_reqencoding
== CE_UNKNOWN
) {
810 advise (NULL
, "invalid CTE specification: \"%s\"", dp
);
814 while (isspace ((unsigned char) *cp
))
819 * Check if anything is left over
823 ci
->ci_magic
= add (cp
, NULL
);
825 /* If there is a Content-Disposition header and it doesn't
826 have a *filename=, extract it from the magic contents.
827 The r1bindex call skips any leading directory
829 if (ct
->c_dispo_type
&&
830 !get_param(ct
->c_dispo_first
, "filename", '_', 1)) {
831 add_param(&ct
->c_dispo_first
, &ct
->c_dispo_last
, "filename",
832 r1bindex(ci
->ci_magic
, '/'), 0);
837 "extraneous information in message %s's %s: field\n%*s(%s)",
838 ct
->c_file
, TYPE_FIELD
, strlen(invo_name
) + 2, "", cp
);
846 * Parse out a Content-Disposition header. A lot of this is cribbed from
850 get_dispo (char *cp
, CT ct
, int buildflag
)
852 char *dp
, *dispoheader
;
857 * Save the whole copy of the Content-Disposition header, unless we're
858 * processing a mhbuild directive. A NULL c_dispo will be a flag to
859 * mhbuild that the disposition header needs to be generated at that
863 dispoheader
= cp
= add(cp
, NULL
);
865 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
868 /* change newlines to spaces */
869 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
872 /* trim trailing spaces */
873 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
874 if (!isspace ((unsigned char) *dp
))
879 fprintf (stderr
, "%s: %s\n", DISPO_FIELD
, cp
);
881 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) ==
887 for (dp
= cp
; istoken (*dp
); dp
++)
890 ct
->c_dispo_type
= add (cp
, NULL
); /* store disposition type */
893 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) == NOTOK
)
896 if ((status
= parse_header_attrs (ct
->c_file
, DISPO_FIELD
, &cp
,
897 &ct
->c_dispo_first
, &ct
->c_dispo_last
,
899 if (status
== NOTOK
) {
905 "extraneous information in message %s's %s: field\n%*s(%s)",
906 ct
->c_file
, DISPO_FIELD
, strlen(invo_name
) + 2, "", cp
);
912 ct
->c_dispo
= dispoheader
;
919 get_comment (const char *filename
, const char *fieldname
, char **ap
,
924 char c
, buffer
[BUFSIZ
], *dp
;
934 advise (NULL
, "invalid comment in message %s's %s: field",
935 filename
, fieldname
);
940 if ((c
= *cp
++) == '\0')
963 if ((dp
= *commentp
)) {
964 *commentp
= concat (dp
, " ", buffer
, NULL
);
967 *commentp
= add (buffer
, NULL
);
971 while (isspace ((unsigned char) *cp
))
982 * Handles content types audio, image, and video.
983 * There's not much to do right here.
991 return OK
; /* not much to do here */
1002 char buffer
[BUFSIZ
];
1008 CI ci
= &ct
->c_ctinfo
;
1010 /* check for missing subtype */
1011 if (!*ci
->ci_subtype
)
1012 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1015 for (kv
= SubText
; kv
->kv_key
; kv
++)
1016 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1018 ct
->c_subtype
= kv
->kv_value
;
1020 /* allocate text character set structure */
1021 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1022 adios (NULL
, "out of memory");
1023 ct
->c_ctparams
= (void *) t
;
1025 /* scan for charset parameter */
1026 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
)
1027 if (!strcasecmp (pm
->pm_name
, "charset"))
1030 /* check if content specified a character set */
1032 chset
= pm
->pm_value
;
1033 t
->tx_charset
= CHARSET_SPECIFIED
;
1035 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1039 * If we can not handle character set natively,
1040 * then check profile for string to modify the
1041 * terminal or display method.
1043 * termproc is for mhshow, though mhlist -debug prints it, too.
1045 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1046 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1047 if ((cp
= context_find (buffer
)))
1048 ct
->c_termproc
= getcpy (cp
);
1060 InitMultiPart (CT ct
)
1070 struct multipart
*m
;
1072 struct part
*part
, **next
;
1073 CI ci
= &ct
->c_ctinfo
;
1078 * The encoding for multipart messages must be either
1079 * 7bit, 8bit, or binary (per RFC2045).
1081 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1082 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1083 /* Copy the Content-Transfer-Encoding header field body so we can
1084 remove any trailing whitespace and leading blanks from it. */
1085 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1087 bp
= cte
+ strlen (cte
) - 1;
1088 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1089 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1092 "\"%s/%s\" type in message %s must be encoded in\n"
1093 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1094 "is to\nmanually edit the file and change the \"%s\"\n"
1095 "Content-Transfer-Encoding to one of those. For now",
1096 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1103 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1104 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1106 ct
->c_subtype
= kv
->kv_value
;
1109 * Check for "boundary" parameter, which is
1110 * required for multipart messages.
1113 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1114 if (!strcasecmp (pm
->pm_name
, "boundary")) {
1120 /* complain if boundary parameter is missing */
1123 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1124 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1128 /* allocate primary structure for multipart info */
1129 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1130 adios (NULL
, "out of memory");
1131 ct
->c_ctparams
= (void *) m
;
1133 /* check if boundary parameter contains only whitespace characters */
1134 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1137 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1138 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1142 /* remove trailing whitespace from boundary parameter */
1143 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1144 if (!isspace ((unsigned char) *dp
))
1148 /* record boundary separators */
1149 m
->mp_start
= concat (bp
, "\n", NULL
);
1150 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1152 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1153 advise (ct
->c_file
, "unable to open for reading");
1157 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1159 next
= &m
->mp_parts
;
1163 while ((gotlen
= getline(&bufp
, &buflen
, fp
)) != -1) {
1168 if (bufp
[0] != '-' || bufp
[1] != '-')
1171 if (strcmp (bufp
+ 2, m
->mp_start
))
1174 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1175 adios (NULL
, "out of memory");
1177 next
= &part
->mp_next
;
1179 if (!(p
= get_content (fp
, ct
->c_file
,
1180 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1189 fseek (fp
, pos
, SEEK_SET
);
1192 if (strcmp (bufp
+ 2, m
->mp_start
) == 0) {
1196 p
->c_end
= ftell(fp
) - (gotlen
+ 1);
1197 if (p
->c_end
< p
->c_begin
)
1198 p
->c_begin
= p
->c_end
;
1203 if (strcmp (bufp
+ 2, m
->mp_stop
) == 0)
1209 if (! suppress_bogus_mp_content_warning
) {
1210 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1212 bogus_mp_content
= 1;
1214 if (!inout
&& part
) {
1216 p
->c_end
= ct
->c_end
;
1218 if (p
->c_begin
>= p
->c_end
) {
1219 for (next
= &m
->mp_parts
; *next
!= part
;
1220 next
= &((*next
)->mp_next
))
1224 free ((char *) part
);
1229 /* reverse the order of the parts for multipart/alternative */
1230 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1234 * label all subparts with part number, and
1235 * then initialize the content of the subpart.
1240 char partnam
[BUFSIZ
];
1243 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1244 pp
= partnam
+ strlen (partnam
);
1249 for (part
= m
->mp_parts
, partnum
= 1; part
;
1250 part
= part
->mp_next
, partnum
++) {
1253 sprintf (pp
, "%d", partnum
);
1254 p
->c_partno
= add (partnam
, NULL
);
1256 /* initialize the content of the subparts */
1257 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1266 get_leftover_mp_content (ct
, 1);
1267 get_leftover_mp_content (ct
, 0);
1277 * reverse the order of the parts of a multipart/alternative
1281 reverse_parts (CT ct
)
1283 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1287 /* Reverse the order of its parts by walking the mp_parts list
1288 and pushing each node to the front. */
1289 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1290 next
= part
->mp_next
;
1291 part
->mp_next
= m
->mp_parts
;
1299 /* parse_mime() arranges alternates in reverse (priority) order. This
1300 function can be used to reverse them back. This will put, for
1301 example, a text/plain part before a text/html part in a
1302 multipart/alternative part, for example, where it belongs. */
1304 reverse_alternative_parts (CT ct
) {
1305 if (ct
->c_type
== CT_MULTIPART
) {
1306 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1309 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1313 /* And call recursively on each part of a multipart. */
1314 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1315 reverse_alternative_parts (part
->mp_part
);
1329 CI ci
= &ct
->c_ctinfo
;
1331 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1333 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1334 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1338 /* check for missing subtype */
1339 if (!*ci
->ci_subtype
)
1340 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1343 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1344 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1346 ct
->c_subtype
= kv
->kv_value
;
1348 switch (ct
->c_subtype
) {
1349 case MESSAGE_RFC822
:
1352 case MESSAGE_PARTIAL
:
1357 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1358 adios (NULL
, "out of memory");
1359 ct
->c_ctparams
= (void *) p
;
1361 /* scan for parameters "id", "number", and "total" */
1362 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1363 if (!strcasecmp (pm
->pm_name
, "id")) {
1364 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1367 if (!strcasecmp (pm
->pm_name
, "number")) {
1368 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1369 || p
->pm_partno
< 1) {
1372 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1373 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1374 ct
->c_file
, TYPE_FIELD
);
1379 if (!strcasecmp (pm
->pm_name
, "total")) {
1380 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1389 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1391 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1392 ci
->ci_type
, ci
->ci_subtype
,
1393 ct
->c_file
, TYPE_FIELD
);
1399 case MESSAGE_EXTERNAL
:
1406 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1407 adios (NULL
, "out of memory");
1408 ct
->c_ctparams
= (void *) e
;
1411 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1412 advise (ct
->c_file
, "unable to open for reading");
1416 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1418 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1426 p
->c_ceopenfnx
= NULL
;
1427 if ((exresult
= params_external (ct
, 0)) != NOTOK
1428 && p
->c_ceopenfnx
== openMail
) {
1432 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1434 content_error (NULL
, ct
,
1435 "empty body for access-type=mail-server");
1439 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1440 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1442 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1444 adios ("failed", "fread");
1447 adios (NULL
, "unexpected EOF from fread");
1450 bp
+= cc
, size
-= cc
;
1457 p
->c_end
= p
->c_begin
;
1462 if (exresult
== NOTOK
)
1464 if (e
->eb_flags
== NOTOK
)
1467 switch (p
->c_type
) {
1472 if (p
->c_subtype
!= MESSAGE_RFC822
)
1476 e
->eb_partno
= ct
->c_partno
;
1478 (*p
->c_ctinitfnx
) (p
);
1493 params_external (CT ct
, int composing
)
1496 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1497 CI ci
= &ct
->c_ctinfo
;
1499 ct
->c_ceopenfnx
= NULL
;
1500 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1501 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1502 struct str2init
*s2i
;
1503 CT p
= e
->eb_content
;
1505 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1506 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1509 e
->eb_access
= pm
->pm_value
;
1510 e
->eb_flags
= NOTOK
;
1511 p
->c_encoding
= CE_EXTERNAL
;
1514 e
->eb_access
= s2i
->si_key
;
1515 e
->eb_flags
= s2i
->si_val
;
1516 p
->c_encoding
= CE_EXTERNAL
;
1518 /* Call the Init function for this external type */
1519 if ((*s2i
->si_init
)(p
) == NOTOK
)
1523 if (!strcasecmp (pm
->pm_name
, "name")) {
1524 e
->eb_name
= pm
->pm_value
;
1527 if (!strcasecmp (pm
->pm_name
, "permission")) {
1528 e
->eb_permission
= pm
->pm_value
;
1531 if (!strcasecmp (pm
->pm_name
, "site")) {
1532 e
->eb_site
= pm
->pm_value
;
1535 if (!strcasecmp (pm
->pm_name
, "directory")) {
1536 e
->eb_dir
= pm
->pm_value
;
1539 if (!strcasecmp (pm
->pm_name
, "mode")) {
1540 e
->eb_mode
= pm
->pm_value
;
1543 if (!strcasecmp (pm
->pm_name
, "size")) {
1544 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1547 if (!strcasecmp (pm
->pm_name
, "server")) {
1548 e
->eb_server
= pm
->pm_value
;
1551 if (!strcasecmp (pm
->pm_name
, "subject")) {
1552 e
->eb_subject
= pm
->pm_value
;
1555 if (!strcasecmp (pm
->pm_name
, "url")) {
1557 * According to RFC 2017, we have to remove all whitespace from
1561 char *u
, *p
= pm
->pm_value
;
1562 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1564 for (; *p
!= '\0'; p
++) {
1565 if (! isspace((unsigned char) *p
))
1572 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1573 e
->eb_body
= getcpy (pm
->pm_value
);
1578 if (!e
->eb_access
) {
1580 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1581 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1594 InitApplication (CT ct
)
1597 CI ci
= &ct
->c_ctinfo
;
1600 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1601 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1603 ct
->c_subtype
= kv
->kv_value
;
1610 * TRANSFER ENCODINGS
1614 init_encoding (CT ct
, OpenCEFunc openfnx
)
1616 ct
->c_ceopenfnx
= openfnx
;
1617 ct
->c_ceclosefnx
= close_encoding
;
1618 ct
->c_cesizefnx
= size_encoding
;
1625 close_encoding (CT ct
)
1627 CE ce
= &ct
->c_cefile
;
1636 static unsigned long
1637 size_encoding (CT ct
)
1642 CE ce
= &ct
->c_cefile
;
1645 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1646 return (long) st
.st_size
;
1649 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1650 return (long) st
.st_size
;
1655 if (ct
->c_encoding
== CE_EXTERNAL
)
1656 return (ct
->c_end
- ct
->c_begin
);
1659 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1660 return (ct
->c_end
- ct
->c_begin
);
1662 if (fstat (fd
, &st
) != NOTOK
)
1663 size
= (long) st
.st_size
;
1667 (*ct
->c_ceclosefnx
) (ct
);
1676 static unsigned char b642nib
[0x80] = {
1677 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1678 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1679 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1680 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1681 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1682 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1683 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1684 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1685 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1686 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1687 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1688 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1689 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1690 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1691 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1692 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1699 return init_encoding (ct
, openBase64
);
1704 openBase64 (CT ct
, char **file
)
1706 int bitno
, cc
, digested
;
1707 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1709 unsigned char value
, b
;
1710 char *cp
, *ep
, buffer
[BUFSIZ
];
1711 /* sbeck -- handle suffixes */
1713 CE ce
= &ct
->c_cefile
;
1717 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1722 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1723 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1729 if (*file
== NULL
) {
1732 ce
->ce_file
= add (*file
, NULL
);
1736 /* sbeck@cise.ufl.edu -- handle suffixes */
1738 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1739 if (ce
->ce_unlink
) {
1740 /* Create temporary file with filename extension. */
1741 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1742 adios(NULL
, "unable to create temporary file in %s",
1746 ce
->ce_file
= add (cp
, ce
->ce_file
);
1748 } else if (*file
== NULL
) {
1750 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1751 adios(NULL
, "unable to create temporary file in %s",
1754 ce
->ce_file
= add (tempfile
, NULL
);
1757 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1758 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1762 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1763 adios (NULL
, "internal error(1)");
1766 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1767 content_error (ct
->c_file
, ct
, "unable to open for reading");
1773 if ((digested
= ct
->c_digested
))
1774 MD5Init (&mdContext
);
1780 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1782 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1784 content_error (ct
->c_file
, ct
, "error reading from");
1788 content_error (NULL
, ct
, "premature eof");
1796 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1799 if (isspace ((unsigned char) *cp
))
1801 if (skip
|| (((unsigned char) *cp
) & 0x80)
1802 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1804 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1805 (unsigned char) *cp
,
1806 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1809 content_error (NULL
, ct
,
1810 "invalid BASE64 encoding -- continuing");
1814 bits
|= value
<< bitno
;
1816 if ((bitno
-= 6) < 0) {
1817 b
= (bits
>> 16) & 0xff;
1818 if (!text
|| b
!= '\r')
1819 putc ((char) b
, ce
->ce_fp
);
1821 MD5Update (&mdContext
, &b
, 1);
1823 b
= (bits
>> 8) & 0xff;
1824 if (! text
|| b
!= '\r')
1825 putc ((char) b
, ce
->ce_fp
);
1827 MD5Update (&mdContext
, &b
, 1);
1830 if (! text
|| b
!= '\r')
1831 putc ((char) b
, ce
->ce_fp
);
1833 MD5Update (&mdContext
, &b
, 1);
1837 if (ferror (ce
->ce_fp
)) {
1838 content_error (ce
->ce_file
, ct
,
1839 "error writing to");
1842 bitno
= 18, bits
= 0L, skip
= 0;
1848 goto self_delimiting
;
1857 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1859 content_error (NULL
, ct
, "invalid BASE64 encoding");
1864 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1866 if (fflush (ce
->ce_fp
)) {
1867 content_error (ce
->ce_file
, ct
, "error writing to");
1872 unsigned char digest
[16];
1874 MD5Final (digest
, &mdContext
);
1875 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1876 sizeof(digest
) / sizeof(digest
[0])))
1877 content_error (NULL
, ct
,
1878 "content integrity suspect (digest mismatch) -- continuing");
1881 fprintf (stderr
, "content integrity confirmed\n");
1884 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1887 *file
= ce
->ce_file
;
1892 return fileno (ce
->ce_fp
);
1899 free_encoding (ct
, 0);
1908 static char hex2nib
[0x80] = {
1909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1911 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1912 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1913 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1915 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1916 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1917 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1921 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 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
1931 return init_encoding (ct
, openQuoted
);
1936 openQuoted (CT ct
, char **file
)
1938 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1944 CE ce
= &ct
->c_cefile
;
1945 /* sbeck -- handle suffixes */
1950 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1955 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1956 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1962 if (*file
== NULL
) {
1965 ce
->ce_file
= add (*file
, NULL
);
1969 /* sbeck@cise.ufl.edu -- handle suffixes */
1971 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1972 if (ce
->ce_unlink
) {
1973 /* Create temporary file with filename extension. */
1974 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1975 adios(NULL
, "unable to create temporary file in %s",
1979 ce
->ce_file
= add (cp
, ce
->ce_file
);
1981 } else if (*file
== NULL
) {
1983 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1984 adios(NULL
, "unable to create temporary file in %s",
1987 ce
->ce_file
= add (tempfile
, NULL
);
1990 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1991 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1995 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1996 adios (NULL
, "internal error(2)");
1999 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2000 content_error (ct
->c_file
, ct
, "unable to open for reading");
2006 if ((digested
= ct
->c_digested
))
2007 MD5Init (&mdContext
);
2014 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2016 if ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) == -1) {
2017 content_error (NULL
, ct
, "premature eof");
2021 if ((cc
= gotlen
) > len
)
2025 for (ep
= (cp
= bufp
) + cc
- 1; cp
<= ep
; ep
--)
2026 if (!isspace ((unsigned char) *ep
))
2030 for (; cp
< ep
; cp
++) {
2032 /* in an escape sequence */
2034 /* at byte 1 of an escape sequence */
2035 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2036 /* next is byte 2 */
2039 /* at byte 2 of an escape sequence */
2041 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2042 putc (mask
, ce
->ce_fp
);
2044 MD5Update (&mdContext
, &mask
, 1);
2045 if (ferror (ce
->ce_fp
)) {
2046 content_error (ce
->ce_file
, ct
, "error writing to");
2049 /* finished escape sequence; next may be literal or a new
2050 * escape sequence */
2053 /* on to next byte */
2057 /* not in an escape sequence */
2059 /* starting an escape sequence, or invalid '='? */
2060 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2061 /* "=\n" soft line break, eat the \n */
2065 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2066 /* We don't have 2 bytes left, so this is an invalid
2067 * escape sequence; just show the raw bytes (below). */
2068 } else if (isxdigit ((unsigned char) cp
[1]) &&
2069 isxdigit ((unsigned char) cp
[2])) {
2070 /* Next 2 bytes are hex digits, making this a valid escape
2071 * sequence; let's decode it (above). */
2075 /* One or both of the next 2 is out of range, making this
2076 * an invalid escape sequence; just show the raw bytes
2081 /* Just show the raw byte. */
2082 putc (*cp
, ce
->ce_fp
);
2085 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2087 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2090 if (ferror (ce
->ce_fp
)) {
2091 content_error (ce
->ce_file
, ct
, "error writing to");
2097 content_error (NULL
, ct
,
2098 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2102 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2104 if (fflush (ce
->ce_fp
)) {
2105 content_error (ce
->ce_file
, ct
, "error writing to");
2110 unsigned char digest
[16];
2112 MD5Final (digest
, &mdContext
);
2113 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2114 sizeof(digest
) / sizeof(digest
[0])))
2115 content_error (NULL
, ct
,
2116 "content integrity suspect (digest mismatch) -- continuing");
2119 fprintf (stderr
, "content integrity confirmed\n");
2122 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2125 *file
= ce
->ce_file
;
2130 return fileno (ce
->ce_fp
);
2133 free_encoding (ct
, 0);
2149 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2152 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2158 open7Bit (CT ct
, char **file
)
2160 int cc
, fd
, len
, own_ct_fp
= 0;
2161 char buffer
[BUFSIZ
];
2162 /* sbeck -- handle suffixes */
2165 CE ce
= &ct
->c_cefile
;
2168 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2173 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2174 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2180 if (*file
== NULL
) {
2183 ce
->ce_file
= add (*file
, NULL
);
2187 /* sbeck@cise.ufl.edu -- handle suffixes */
2189 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2190 if (ce
->ce_unlink
) {
2191 /* Create temporary file with filename extension. */
2192 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2193 adios(NULL
, "unable to create temporary file in %s",
2197 ce
->ce_file
= add (cp
, ce
->ce_file
);
2199 } else if (*file
== NULL
) {
2201 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2202 adios(NULL
, "unable to create temporary file in %s",
2205 ce
->ce_file
= add (tempfile
, NULL
);
2208 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2209 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2213 if (ct
->c_type
== CT_MULTIPART
) {
2214 CI ci
= &ct
->c_ctinfo
;
2218 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2219 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2220 + 1 + strlen (ci
->ci_subtype
);
2221 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2224 fputs (buffer
, ce
->ce_fp
);
2228 if (ci
->ci_comment
) {
2229 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2230 fputs ("\n\t", ce
->ce_fp
);
2234 putc (' ', ce
->ce_fp
);
2237 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2240 fprintf (ce
->ce_fp
, "\n");
2242 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2244 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2246 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2247 fprintf (ce
->ce_fp
, "\n");
2250 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2251 adios (NULL
, "internal error(3)");
2254 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2255 content_error (ct
->c_file
, ct
, "unable to open for reading");
2261 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2263 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2265 content_error (ct
->c_file
, ct
, "error reading from");
2269 content_error (NULL
, ct
, "premature eof");
2277 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2278 if (ferror (ce
->ce_fp
)) {
2279 content_error (ce
->ce_file
, ct
, "error writing to");
2284 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2286 if (fflush (ce
->ce_fp
)) {
2287 content_error (ce
->ce_file
, ct
, "error writing to");
2291 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2294 *file
= ce
->ce_file
;
2299 return fileno (ce
->ce_fp
);
2302 free_encoding (ct
, 0);
2316 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2318 char cachefile
[BUFSIZ
];
2321 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2326 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2327 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2333 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2334 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2335 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2336 ce
->ce_file
= getcpy (cachefile
);
2340 admonish (cachefile
, "unable to fopen for reading");
2347 *file
= ce
->ce_file
;
2348 *fd
= fileno (ce
->ce_fp
);
2359 return init_encoding (ct
, openFile
);
2364 openFile (CT ct
, char **file
)
2367 char cachefile
[BUFSIZ
];
2368 struct exbody
*e
= ct
->c_ctexbody
;
2369 CE ce
= &ct
->c_cefile
;
2371 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2383 content_error (NULL
, ct
, "missing name parameter");
2387 ce
->ce_file
= getcpy (e
->eb_name
);
2390 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2391 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2395 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2396 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2397 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2401 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2402 if ((fp
= fopen (cachefile
, "w"))) {
2404 char buffer
[BUFSIZ
];
2405 FILE *gp
= ce
->ce_fp
;
2407 fseek (gp
, 0L, SEEK_SET
);
2409 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2411 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2415 admonish (ce
->ce_file
, "error reading");
2416 (void) m_unlink (cachefile
);
2420 admonish (cachefile
, "error writing");
2421 (void) m_unlink (cachefile
);
2428 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2429 *file
= ce
->ce_file
;
2430 return fileno (ce
->ce_fp
);
2440 return init_encoding (ct
, openFTP
);
2445 openFTP (CT ct
, char **file
)
2447 int cachetype
, caching
, fd
;
2449 char *bp
, *ftp
, *user
, *pass
;
2450 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2452 CE ce
= &ct
->c_cefile
;
2453 static char *username
= NULL
;
2454 static char *password
= NULL
;
2458 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2464 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2475 if (!e
->eb_name
|| !e
->eb_site
) {
2476 content_error (NULL
, ct
, "missing %s parameter",
2477 e
->eb_name
? "site": "name");
2481 /* Get the buffer ready to go */
2483 buflen
= sizeof(buffer
);
2486 * Construct the query message for user
2488 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2494 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2500 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2501 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2506 if (e
->eb_size
> 0) {
2507 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2512 snprintf (bp
, buflen
, "? ");
2515 * Now, check the answer
2517 if (!getanswer (buffer
))
2522 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2526 ruserpass (e
->eb_site
, &username
, &password
);
2531 ce
->ce_unlink
= (*file
== NULL
);
2533 cachefile
[0] = '\0';
2534 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2535 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2536 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2537 if (*file
== NULL
) {
2544 ce
->ce_file
= add (*file
, NULL
);
2546 ce
->ce_file
= add (cachefile
, NULL
);
2549 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2550 adios(NULL
, "unable to create temporary file in %s",
2553 ce
->ce_file
= add (tempfile
, NULL
);
2556 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2557 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2562 int child_id
, i
, vecp
;
2566 vec
[vecp
++] = r1bindex (ftp
, '/');
2567 vec
[vecp
++] = e
->eb_site
;
2570 vec
[vecp
++] = e
->eb_dir
;
2571 vec
[vecp
++] = e
->eb_name
;
2572 vec
[vecp
++] = ce
->ce_file
,
2573 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2574 ? "ascii" : "binary";
2579 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2583 adios ("fork", "unable to");
2587 close (fileno (ce
->ce_fp
));
2589 fprintf (stderr
, "unable to exec ");
2595 if (pidXwait (child_id
, NULL
)) {
2596 username
= password
= NULL
;
2606 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2611 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2612 if ((fp
= fopen (cachefile
, "w"))) {
2614 FILE *gp
= ce
->ce_fp
;
2616 fseek (gp
, 0L, SEEK_SET
);
2618 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2620 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2624 admonish (ce
->ce_file
, "error reading");
2625 (void) m_unlink (cachefile
);
2629 admonish (cachefile
, "error writing");
2630 (void) m_unlink (cachefile
);
2638 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2639 *file
= ce
->ce_file
;
2640 return fileno (ce
->ce_fp
);
2651 return init_encoding (ct
, openMail
);
2656 openMail (CT ct
, char **file
)
2658 int child_id
, fd
, i
, vecp
;
2660 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2661 struct exbody
*e
= ct
->c_ctexbody
;
2662 CE ce
= &ct
->c_cefile
;
2664 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2675 if (!e
->eb_server
) {
2676 content_error (NULL
, ct
, "missing server parameter");
2680 /* Get buffer ready to go */
2682 buflen
= sizeof(buffer
);
2684 /* Now, construct query message */
2685 snprintf (bp
, buflen
, "Retrieve content");
2691 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2697 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2699 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2701 /* Now, check answer */
2702 if (!getanswer (buffer
))
2706 vec
[vecp
++] = r1bindex (mailproc
, '/');
2707 vec
[vecp
++] = e
->eb_server
;
2708 vec
[vecp
++] = "-subject";
2709 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2710 vec
[vecp
++] = "-body";
2711 vec
[vecp
++] = e
->eb_body
;
2714 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2718 advise ("fork", "unable to");
2722 execvp (mailproc
, vec
);
2723 fprintf (stderr
, "unable to exec ");
2729 if (pidXwait (child_id
, NULL
) == OK
)
2730 advise (NULL
, "request sent");
2734 if (*file
== NULL
) {
2736 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2737 adios(NULL
, "unable to create temporary file in %s",
2740 ce
->ce_file
= add (tempfile
, NULL
);
2743 ce
->ce_file
= add (*file
, NULL
);
2747 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2748 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2752 /* showproc is for mhshow and mhstore, though mhlist -debug
2753 * prints it, too. */
2755 free (ct
->c_showproc
);
2756 ct
->c_showproc
= add ("true", NULL
);
2758 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2759 *file
= ce
->ce_file
;
2760 return fileno (ce
->ce_fp
);
2771 return init_encoding (ct
, openURL
);
2776 openURL (CT ct
, char **file
)
2778 struct exbody
*e
= ct
->c_ctexbody
;
2779 CE ce
= &ct
->c_cefile
;
2780 char *urlprog
, *program
;
2781 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2782 int fd
, caching
, cachetype
;
2783 struct msgs_array args
= { 0, 0, NULL
};
2786 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2790 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2794 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2806 content_error(NULL
, ct
, "missing url parameter");
2810 ce
->ce_unlink
= (*file
== NULL
);
2812 cachefile
[0] = '\0';
2814 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2815 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2816 if (*file
== NULL
) {
2823 ce
->ce_file
= add(*file
, NULL
);
2825 ce
->ce_file
= add(cachefile
, NULL
);
2828 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2829 adios(NULL
, "unable to create temporary file in %s",
2832 ce
->ce_file
= add (tempfile
, NULL
);
2835 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2836 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2840 switch (child_id
= fork()) {
2842 adios ("fork", "unable to");
2846 argsplit_msgarg(&args
, urlprog
, &program
);
2847 app_msgarg(&args
, e
->eb_url
);
2848 app_msgarg(&args
, NULL
);
2849 dup2(fileno(ce
->ce_fp
), 1);
2850 close(fileno(ce
->ce_fp
));
2851 execvp(program
, args
.msgs
);
2852 fprintf(stderr
, "Unable to exec ");
2858 if (pidXwait(child_id
, NULL
)) {
2866 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2871 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2872 if ((fp
= fopen(cachefile
, "w"))) {
2874 FILE *gp
= ce
->ce_fp
;
2876 fseeko(gp
, 0, SEEK_SET
);
2878 while ((cc
= fread(buffer
, sizeof(*buffer
),
2879 sizeof(buffer
), gp
)) > 0)
2880 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2885 admonish(ce
->ce_file
, "error reading");
2886 (void) m_unlink (cachefile
);
2893 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2894 *file
= ce
->ce_file
;
2899 readDigest (CT ct
, char *cp
)
2904 unsigned char *dp
, value
, *ep
;
2910 for (ep
= (dp
= ct
->c_digest
)
2911 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2916 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2918 fprintf (stderr
, "invalid BASE64 encoding\n");
2922 bits
|= value
<< bitno
;
2924 if ((bitno
-= 6) < 0) {
2925 if (dp
+ (3 - skip
) > ep
)
2926 goto invalid_digest
;
2927 *dp
++ = (bits
>> 16) & 0xff;
2929 *dp
++ = (bits
>> 8) & 0xff;
2931 *dp
++ = bits
& 0xff;
2941 goto self_delimiting
;
2946 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2956 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2964 fprintf (stderr
, "MD5 digest=");
2965 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2966 fprintf (stderr
, "%02x", *dp
& 0xff);
2967 fprintf (stderr
, "\n");
2974 /* Multipart parts might have content before the first subpart and/or
2975 after the last subpart that hasn't been stored anywhere else, so do
2978 get_leftover_mp_content (CT ct
, int before
/* or after */)
2980 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2982 int found_boundary
= 0;
2988 char *content
= NULL
;
2990 if (! m
) return NOTOK
;
2993 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2995 /* Isolate the beginning of this part to the beginning of the
2996 first subpart and save any content between them. */
2997 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2998 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2999 boundary
= concat ("--", m
->mp_start
, NULL
);
3001 struct part
*last_subpart
= NULL
;
3002 struct part
*subpart
;
3004 /* Go to the last subpart to get its end position. */
3005 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3006 last_subpart
= subpart
;
3009 if (last_subpart
== NULL
) return NOTOK
;
3011 /* Isolate the end of the last subpart to the end of this part
3012 and save any content between them. */
3013 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3014 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3015 boundary
= concat ("--", m
->mp_stop
, NULL
);
3018 /* Back up by 1 to pick up the newline. */
3019 while ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) != -1) {
3021 /* Don't look beyond beginning of first subpart (before) or
3022 next part (after). */
3023 if (read
> max
) bufp
[read
-max
] = '\0';
3026 if (! strcmp (bufp
, boundary
)) {
3030 if (! found_boundary
&& ! strcmp (bufp
, boundary
)) {
3036 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3038 char *old_content
= content
;
3039 content
= concat (content
, bufp
, NULL
);
3043 ? concat ("\n", bufp
, NULL
)
3044 : concat (bufp
, NULL
);
3049 if (found_boundary
|| read
> max
) break;
3051 if (read
> max
) break;
3055 /* Skip the newline if that's all there is. */
3059 /* Remove trailing newline, except at EOF. */
3060 if ((before
|| ! feof (ct
->c_fp
)) &&
3061 (cp
= content
+ strlen (content
)) > content
&&
3066 if (strlen (content
) > 1) {
3068 m
->mp_content_before
= content
;
3070 m
->mp_content_after
= content
;
3085 ct_type_str (int type
) {
3087 case CT_APPLICATION
:
3088 return "application";
3104 return "unknown_type";
3110 ct_subtype_str (int type
, int subtype
) {
3112 case CT_APPLICATION
:
3114 case APPLICATION_OCTETS
:
3116 case APPLICATION_POSTSCRIPT
:
3117 return "postscript";
3119 return "unknown_app_subtype";
3123 case MESSAGE_RFC822
:
3125 case MESSAGE_PARTIAL
:
3127 case MESSAGE_EXTERNAL
:
3130 return "unknown_msg_subtype";
3136 case MULTI_ALTERNATE
:
3137 return "alternative";
3140 case MULTI_PARALLEL
:
3143 return "unknown_multipart_subtype";
3154 return "unknown_text_subtype";
3157 return "unknown_type";
3162 /* Find the content type and InitFunc for the CT. */
3163 const struct str2init
*
3164 get_ct_init (int type
) {
3165 const struct str2init
*sp
;
3167 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3168 if (type
== sp
->si_val
) {
3177 ce_str (int encoding
) {
3182 return "quoted-printable";
3198 /* Find the content type and InitFunc for the content encoding method. */
3199 const struct str2init
*
3200 get_ce_method (const char *method
) {
3201 struct str2init
*sp
;
3203 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3204 if (! strcasecmp (method
, sp
->si_key
)) {
3213 * Parse a series of MIME attributes (or parameters) given a header as
3216 * Arguments include:
3218 * filename - Name of input file (for error messages)
3219 * fieldname - Name of field being processed
3220 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3221 * Updated to point to end of attributes when finished.
3222 * param_head - Pointer to head of parameter list
3223 * param_tail - Pointer to tail of parameter list
3224 * commentp - Pointer to header comment pointer (may be NULL)
3226 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3227 * DONE to indicate a benign error (minor parsing error, but the program
3232 parse_header_attrs (const char *filename
, const char *fieldname
,
3233 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3236 char *cp
= *header_attrp
;
3242 struct sectlist
*next
;
3248 struct sectlist
*sechead
;
3249 struct parmlist
*next
;
3250 } *pp
, *pp2
, *phead
= NULL
;
3252 while (*cp
== ';') {
3253 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3254 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3257 while (isspace ((unsigned char) *cp
))
3261 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3267 "extraneous trailing ';' in message %s's %s: "
3269 filename
, fieldname
);
3273 /* down case the attribute name */
3274 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3275 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3276 *dp
= tolower ((unsigned char) *dp
);
3278 for (up
= dp
; isspace ((unsigned char) *dp
);)
3280 if (dp
== cp
|| *dp
!= '=') {
3282 "invalid parameter in message %s's %s: "
3283 "field\n%*sparameter %s (error detected at offset %d)",
3284 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3289 * To handle RFC 2231, we have to deal with the following extensions:
3291 * name*=encoded-value
3292 * name*<N>=part-N-of-a-parameter-value
3293 * name*<N>*=encoded-part-N-of-a-parameter-value
3296 * If there's a * right before the equal sign, it's encoded.
3297 * If there's a * and one or more digits, then it's section N.
3299 * Remember we can have one or the other, or both. cp points to
3300 * beginning of name, up points past the last character in the
3304 for (vp
= cp
; vp
< up
; vp
++) {
3305 if (*vp
== '*' && vp
< up
- 1) {
3308 } else if (*vp
== '*' && vp
== up
- 1) {
3310 } else if (partial
) {
3311 if (isdigit((unsigned char) *vp
))
3312 index
= *vp
- '0' + index
* 10;
3314 advise (NULL
, "invalid parameter index in message %s's "
3315 "%s: field\n%*s(parameter %s)", filename
,
3316 fieldname
, strlen(invo_name
) + 2, "", cp
);
3325 * Break out the parameter name and value sections and allocate
3329 nameptr
= mh_xmalloc(len
+ 1);
3330 strncpy(nameptr
, cp
, len
);
3331 nameptr
[len
] = '\0';
3333 for (dp
++; isspace ((unsigned char) *dp
);)
3338 * Single quotes delimit the character set and language tag.
3339 * They are required on the first section (or a complete
3344 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3350 charset
= mh_xmalloc(len
+ 1);
3351 strncpy(charset
, dp
, len
);
3352 charset
[len
] = '\0';
3358 advise(NULL
, "missing charset in message %s's %s: "
3359 "field\n%*s(parameter %s)", filename
, fieldname
,
3360 strlen(invo_name
) + 2, "", nameptr
);
3366 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3373 lang
= mh_xmalloc(len
+ 1);
3374 strncpy(lang
, dp
, len
);
3381 advise(NULL
, "missing language tag in message %s's %s: "
3382 "field\n%*s(parameter %s)", filename
, fieldname
,
3383 strlen(invo_name
) + 2, "", nameptr
);
3394 * At this point vp should be pointing at the beginning
3395 * of the encoded value/section. Continue until we reach
3396 * the end or get whitespace. But first, calculate the
3397 * length so we can allocate the correct buffer size.
3400 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3402 if (*(vp
+ 1) == '\0' ||
3403 !isxdigit((unsigned char) *(vp
+ 1)) ||
3404 *(vp
+ 2) == '\0' ||
3405 !isxdigit((unsigned char) *(vp
+ 2))) {
3406 advise(NULL
, "invalid encoded sequence in message "
3407 "%s's %s: field\n%*s(parameter %s)",
3408 filename
, fieldname
, strlen(invo_name
) + 2,
3422 up
= valptr
= mh_xmalloc(len
+ 1);
3424 for (vp
= dp
; istoken(*vp
); vp
++) {
3426 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3437 * A "normal" string. If it's got a leading quote, then we
3438 * strip the quotes out. Otherwise go until we reach the end
3439 * or get whitespace. Note we scan it twice; once to get the
3440 * length, then the second time copies it into the destination
3447 for (cp
= dp
+ 1;;) {
3452 "invalid quoted-string in message %s's %s: "
3453 "field\n%*s(parameter %s)",
3454 filename
, fieldname
, strlen(invo_name
) + 2, "",
3477 for (cp
= dp
; istoken (*cp
); cp
++) {
3482 valptr
= mh_xmalloc(len
+ 1);
3486 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3494 strncpy(valptr
, cp
= dp
, len
);
3502 * If 'partial' is set, we don't allocate a parameter now. We
3503 * put it on the parameter linked list to be reassembled later.
3505 * "phead" points to a list of all parameters we need to reassemble.
3506 * Each parameter has a list of sections. We insert the sections in
3511 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3512 if (strcasecmp(nameptr
, pp
->name
) == 0)
3517 pp
= mh_xmalloc(sizeof(*pp
));
3518 memset(pp
, 0, sizeof(*pp
));
3525 * Insert this into the section linked list
3528 sp
= mh_xmalloc(sizeof(*sp
));
3529 memset(sp
, 0, sizeof(*sp
));
3534 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3535 sp
->next
= pp
->sechead
;
3538 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3539 if (sp2
->index
== sp
->index
) {
3540 advise (NULL
, "duplicate index (%d) in message "
3541 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3542 filename
, fieldname
, strlen(invo_name
) + 2, "",
3546 if (sp2
->index
< sp
->index
&&
3547 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3548 sp
->next
= sp2
->next
;
3555 advise(NULL
, "Internal error: cannot insert partial "
3556 "param in message %s's %s: field\n%*s(parameter %s)",
3557 filename
, fieldname
, strlen(invo_name
) + 2, "",
3564 * Save our charset and lang tags.
3567 if (index
== 0 && encoded
) {
3570 pp
->charset
= charset
;
3576 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3577 pm
->pm_charset
= charset
;
3581 while (isspace ((unsigned char) *cp
))
3585 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3591 * Now that we're done, reassemble all of the partial parameters.
3594 for (pp
= phead
; pp
!= NULL
; ) {
3598 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3599 if (sp
->index
!= pindex
++) {
3600 advise(NULL
, "missing section %d for parameter in "
3601 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3602 filename
, fieldname
, strlen(invo_name
) + 2, "",
3609 p
= q
= mh_xmalloc(tlen
+ 1);
3610 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3611 memcpy(q
, sp
->value
, sp
->len
);
3621 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3622 pm
->pm_charset
= pp
->charset
;
3623 pm
->pm_lang
= pp
->lang
;
3634 * Return the charset for a particular content type. Return pointer is
3635 * only valid until the next call to content_charset().
3639 content_charset (CT ct
) {
3640 static char *ret_charset
= NULL
;
3642 if (ret_charset
!= NULL
) {
3646 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3648 return ret_charset
? ret_charset
: "US-ASCII";
3653 * Create a string based on a list of output parameters. Assume that this
3654 * parameter string will be appended to an existing header, so start out
3655 * with the separator (;). Perform RFC 2231 encoding when necessary.
3659 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3661 char *paramout
= NULL
;
3662 char line
[CPERLIN
* 2], *q
;
3663 int curlen
, index
, cont
, encode
, i
;
3664 size_t valoff
, numchars
;
3666 while (params
!= NULL
) {
3672 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3675 if (strlen(params
->pm_name
) > CPERLIN
) {
3676 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3682 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3685 * Loop until we get a parameter that fits within a line. We
3686 * assume new lines start with a tab, so check our overflow based
3696 * At this point we're definitely continuing the line, so
3697 * be sure to include the parameter name and section index.
3700 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3701 params
->pm_name
, index
);
3704 * Both of these functions do a NUL termination
3708 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3709 numchars
, valoff
, index
);
3711 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3722 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3727 * "line" starts with a ;\n\t, so that doesn't count against
3728 * the length. But add 8 since it starts with a tab; that's
3729 * how we end up with 5.
3732 initialwidth
= strlen(line
) + 5;
3735 * At this point the line should be built, so add it to our
3736 * current output buffer.
3739 paramout
= add(line
, paramout
);
3743 * If this won't fit on the line, start a new one. Save room in
3744 * case we need a semicolon on the end
3747 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3759 * At this point, we're either finishing a contined parameter, or
3760 * we're working on a new one.
3764 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3765 params
->pm_name
, index
);
3767 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3772 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3773 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3775 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3776 strlen(params
->pm_value
+ valoff
), valoff
);
3784 paramout
= add(line
, paramout
);
3785 initialwidth
+= strlen(line
);
3787 params
= params
->pm_next
;
3791 *offsetout
= initialwidth
;
3797 * Calculate the size of a parameter.
3801 * pm - The parameter being output
3802 * index - If continuing the parameter, the index of the section
3804 * valueoff - The current offset into the parameter value that we're
3805 * working on (previous sections have consumed valueoff bytes).
3806 * encode - Set if we should perform encoding on this parameter section
3807 * (given that we're consuming bytesfit bytes).
3808 * cont - Set if the remaining data in value will not fit on a single
3809 * line and will need to be continued.
3810 * bytesfit - The number of bytes that we can consume from the parameter
3811 * value and still fit on a completely new line. The
3812 * calculation assumes the new line starts with a tab,
3813 * includes the parameter name and any encoding, and fits
3814 * within CPERLIN bytes. Will always be at least 1.
3818 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3821 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3822 size_t len
= 0, fit
= 0;
3823 int fitlimit
= 0, eightbit
, maxfit
;
3828 * Add up the length. First, start with the parameter name.
3831 len
= strlen(pm
->pm_name
);
3834 * Scan the parameter value and see if we need to do encoding for this
3838 eightbit
= contains8bit(start
, NULL
);
3841 * Determine if we need to encode this section. Encoding is necessary if:
3843 * - There are any 8-bit characters at all and we're on the first
3845 * - There are 8-bit characters within N bytes of our section start.
3846 * N is calculated based on the number of bytes it would take to
3847 * reach CPERLIN. Specifically:
3848 * 8 (starting tab) +
3849 * strlen(param name) +
3850 * 4 ('* for section marker, '=', opening/closing '"')
3852 * is the number of bytes used by everything that isn't part of the
3853 * value. So that gets subtracted from CPERLIN.
3856 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3857 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3858 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3862 len
++; /* Add in equal sign */
3866 * We're using maxfit as a marker for how many characters we can
3867 * fit into the line. Bump it by two because we're not using quotes
3874 * If we don't have a charset or language tag in this parameter,
3878 if (! pm
->pm_charset
) {
3879 pm
->pm_charset
= getcpy(write_charset_8bit());
3880 if (strcasecmp(pm
->pm_charset
, "US-ASCII") == 0)
3881 adios(NULL
, "8-bit characters in parameter \"%s\", but "
3882 "local character set is US-ASCII", pm
->pm_name
);
3885 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
3887 len
++; /* For the encoding marker */
3890 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
3895 * We know we definitely need to include an index. maxfit already
3896 * includes the section marker.
3898 len
+= strlen(indexchar
);
3900 for (p
= start
; *p
!= '\0'; p
++) {
3901 if (isparamencode(*p
)) {
3909 * Just so there's no confusion: maxfit is counting OUTPUT
3910 * characters (post-encoding). fit is counting INPUT characters.
3912 if (! fitlimit
&& maxfit
>= 0)
3914 else if (! fitlimit
)
3919 * Calculate the string length, but add room for quoting \
3920 * and " if necessary. Also account for quotes at beginning
3923 for (p
= start
; *p
!= '\0'; p
++) {
3934 if (! fitlimit
&& maxfit
>= 0)
3936 else if (! fitlimit
)
3953 * Output an encoded parameter string.
3957 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
3958 size_t valueoff
, int index
)
3960 size_t outlen
= 0, n
;
3961 char *endptr
= output
+ len
, *p
;
3964 * First, output the marker for an encoded string.
3972 * If the index is 0, output the character set and language tag.
3973 * If theses were NULL, they should have already been filled in
3978 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
3982 if (output
> endptr
) {
3983 advise(NULL
, "Internal error: parameter buffer overflow");
3989 * Copy over the value, encoding if necessary
3992 p
= pm
->pm_value
+ valueoff
;
3993 while (valuelen
-- > 0) {
3994 if (isparamencode(*p
)) {
3995 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
4002 if (output
> endptr
) {
4003 advise(NULL
, "Internal error: parameter buffer overflow");
4014 * Output a "normal" parameter, without encoding. Be sure to escape
4015 * quotes and backslashes if necessary.
4019 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4023 char *endptr
= output
+ len
, *p
;
4029 p
= pm
->pm_value
+ valueoff
;
4031 while (valuelen
-- > 0) {
4041 if (output
> endptr
) {
4042 advise(NULL
, "Internal error: parameter buffer overflow");
4047 if (output
- 2 > endptr
) {
4048 advise(NULL
, "Internal error: parameter buffer overflow");
4059 * Add a parameter to the parameter linked list
4063 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4065 PM pm
= mh_xmalloc(sizeof(*pm
));
4067 memset(pm
, 0, sizeof(*pm
));
4069 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4070 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4073 (*last
)->pm_next
= pm
;
4084 * Either replace a current parameter with a new value, or add the parameter
4085 * to the parameter linked list.
4089 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4093 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4094 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4096 * If nocopy is set, it's assumed that we own both name
4097 * and value. We don't need name, so we discard it now.
4102 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4107 return add_param(first
, last
, name
, value
, nocopy
);
4111 * Retrieve a parameter value from a parameter linked list. If the parameter
4112 * value needs converted to the local character set, do that now.
4116 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4118 while (first
!= NULL
) {
4119 if (strcasecmp(name
, first
->pm_name
) == 0) {
4121 return first
->pm_value
;
4123 return getcpy(get_param_value(first
, replace
));
4125 first
= first
->pm_next
;
4132 * Return a parameter value, converting to the local character set if
4136 char *get_param_value(PM pm
, char replace
)
4138 static char buffer
[4096]; /* I hope no parameters are larger */
4139 size_t bufsize
= sizeof(buffer
);
4144 ICONV_CONST
char *p
;
4145 #else /* HAVE_ICONV */
4147 #endif /* HAVE_ICONV */
4152 * If we don't have a character set indicated, it's assumed to be
4153 * US-ASCII. If it matches our character set, we don't need to convert
4157 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4158 strlen(pm
->pm_charset
))) {
4159 return pm
->pm_value
;
4163 * In this case, we need to convert. If we have iconv support, use
4164 * that. Otherwise, go through and simply replace every non-ASCII
4165 * character with the substitution character.
4170 bufsize
= sizeof(buffer
);
4171 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4173 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4174 if (cd
== (iconv_t
) -1) {
4178 inbytes
= strlen(pm
->pm_value
);
4182 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4183 if (errno
!= EILSEQ
) {
4188 * Reset shift state, substitute our character,
4189 * try to restart conversion.
4192 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4205 for (++p
, --inbytes
;
4206 inbytes
> 0 && (((unsigned char) *q
) & 0xc0) == 0x80;
4225 #endif /* HAVE_ICONV */
4228 * Take everything non-ASCII and substituite the replacement character
4232 bufsize
= sizeof(buffer
);
4233 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4234 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))