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 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
;
1305 CI ci
= &ct
->c_ctinfo
;
1307 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1309 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1310 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1314 /* check for missing subtype */
1315 if (!*ci
->ci_subtype
)
1316 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1319 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1320 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1322 ct
->c_subtype
= kv
->kv_value
;
1324 switch (ct
->c_subtype
) {
1325 case MESSAGE_RFC822
:
1328 case MESSAGE_PARTIAL
:
1333 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1334 adios (NULL
, "out of memory");
1335 ct
->c_ctparams
= (void *) p
;
1337 /* scan for parameters "id", "number", and "total" */
1338 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1339 if (!strcasecmp (pm
->pm_name
, "id")) {
1340 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1343 if (!strcasecmp (pm
->pm_name
, "number")) {
1344 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1345 || p
->pm_partno
< 1) {
1348 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1349 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1350 ct
->c_file
, TYPE_FIELD
);
1355 if (!strcasecmp (pm
->pm_name
, "total")) {
1356 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1365 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1367 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1368 ci
->ci_type
, ci
->ci_subtype
,
1369 ct
->c_file
, TYPE_FIELD
);
1375 case MESSAGE_EXTERNAL
:
1382 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1383 adios (NULL
, "out of memory");
1384 ct
->c_ctparams
= (void *) e
;
1387 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1388 advise (ct
->c_file
, "unable to open for reading");
1392 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1394 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1402 p
->c_ceopenfnx
= NULL
;
1403 if ((exresult
= params_external (ct
, 0)) != NOTOK
1404 && p
->c_ceopenfnx
== openMail
) {
1408 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1410 content_error (NULL
, ct
,
1411 "empty body for access-type=mail-server");
1415 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1416 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1418 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1420 adios ("failed", "fread");
1423 adios (NULL
, "unexpected EOF from fread");
1426 bp
+= cc
, size
-= cc
;
1433 p
->c_end
= p
->c_begin
;
1438 if (exresult
== NOTOK
)
1440 if (e
->eb_flags
== NOTOK
)
1443 switch (p
->c_type
) {
1448 if (p
->c_subtype
!= MESSAGE_RFC822
)
1452 e
->eb_partno
= ct
->c_partno
;
1454 (*p
->c_ctinitfnx
) (p
);
1469 params_external (CT ct
, int composing
)
1472 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1473 CI ci
= &ct
->c_ctinfo
;
1475 ct
->c_ceopenfnx
= NULL
;
1476 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1477 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1478 struct str2init
*s2i
;
1479 CT p
= e
->eb_content
;
1481 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1482 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1485 e
->eb_access
= pm
->pm_value
;
1486 e
->eb_flags
= NOTOK
;
1487 p
->c_encoding
= CE_EXTERNAL
;
1490 e
->eb_access
= s2i
->si_key
;
1491 e
->eb_flags
= s2i
->si_val
;
1492 p
->c_encoding
= CE_EXTERNAL
;
1494 /* Call the Init function for this external type */
1495 if ((*s2i
->si_init
)(p
) == NOTOK
)
1499 if (!strcasecmp (pm
->pm_name
, "name")) {
1500 e
->eb_name
= pm
->pm_value
;
1503 if (!strcasecmp (pm
->pm_name
, "permission")) {
1504 e
->eb_permission
= pm
->pm_value
;
1507 if (!strcasecmp (pm
->pm_name
, "site")) {
1508 e
->eb_site
= pm
->pm_value
;
1511 if (!strcasecmp (pm
->pm_name
, "directory")) {
1512 e
->eb_dir
= pm
->pm_value
;
1515 if (!strcasecmp (pm
->pm_name
, "mode")) {
1516 e
->eb_mode
= pm
->pm_value
;
1519 if (!strcasecmp (pm
->pm_name
, "size")) {
1520 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1523 if (!strcasecmp (pm
->pm_name
, "server")) {
1524 e
->eb_server
= pm
->pm_value
;
1527 if (!strcasecmp (pm
->pm_name
, "subject")) {
1528 e
->eb_subject
= pm
->pm_value
;
1531 if (!strcasecmp (pm
->pm_name
, "url")) {
1533 * According to RFC 2017, we have to remove all whitespace from
1537 char *u
, *p
= pm
->pm_value
;
1538 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1540 for (; *p
!= '\0'; p
++) {
1541 if (! isspace((unsigned char) *p
))
1548 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1549 e
->eb_body
= getcpy (pm
->pm_value
);
1554 if (!e
->eb_access
) {
1556 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1557 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1570 InitApplication (CT ct
)
1573 CI ci
= &ct
->c_ctinfo
;
1576 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1577 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1579 ct
->c_subtype
= kv
->kv_value
;
1586 * TRANSFER ENCODINGS
1590 init_encoding (CT ct
, OpenCEFunc openfnx
)
1592 ct
->c_ceopenfnx
= openfnx
;
1593 ct
->c_ceclosefnx
= close_encoding
;
1594 ct
->c_cesizefnx
= size_encoding
;
1601 close_encoding (CT ct
)
1603 CE ce
= &ct
->c_cefile
;
1612 static unsigned long
1613 size_encoding (CT ct
)
1618 CE ce
= &ct
->c_cefile
;
1621 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1622 return (long) st
.st_size
;
1625 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1626 return (long) st
.st_size
;
1631 if (ct
->c_encoding
== CE_EXTERNAL
)
1632 return (ct
->c_end
- ct
->c_begin
);
1635 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1636 return (ct
->c_end
- ct
->c_begin
);
1638 if (fstat (fd
, &st
) != NOTOK
)
1639 size
= (long) st
.st_size
;
1643 (*ct
->c_ceclosefnx
) (ct
);
1652 static unsigned char b642nib
[0x80] = {
1653 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1654 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1655 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1656 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1657 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1658 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1659 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1660 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1661 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1662 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1663 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1664 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1665 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1666 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1667 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1668 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1675 return init_encoding (ct
, openBase64
);
1680 openBase64 (CT ct
, char **file
)
1682 int bitno
, cc
, digested
;
1683 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1685 unsigned char value
, b
;
1686 char *cp
, *ep
, buffer
[BUFSIZ
];
1687 /* sbeck -- handle suffixes */
1689 CE ce
= &ct
->c_cefile
;
1693 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1698 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1699 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1705 if (*file
== NULL
) {
1708 ce
->ce_file
= add (*file
, NULL
);
1712 /* sbeck@cise.ufl.edu -- handle suffixes */
1714 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1715 if (ce
->ce_unlink
) {
1716 /* Create temporary file with filename extension. */
1717 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1718 adios(NULL
, "unable to create temporary file in %s",
1722 ce
->ce_file
= add (cp
, ce
->ce_file
);
1724 } else if (*file
== NULL
) {
1726 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1727 adios(NULL
, "unable to create temporary file in %s",
1730 ce
->ce_file
= add (tempfile
, NULL
);
1733 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1734 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1738 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1739 adios (NULL
, "internal error(1)");
1742 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1743 content_error (ct
->c_file
, ct
, "unable to open for reading");
1749 if ((digested
= ct
->c_digested
))
1750 MD5Init (&mdContext
);
1756 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1758 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1760 content_error (ct
->c_file
, ct
, "error reading from");
1764 content_error (NULL
, ct
, "premature eof");
1772 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1775 if (isspace ((unsigned char) *cp
))
1777 if (skip
|| (((unsigned char) *cp
) & 0x80)
1778 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1780 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1781 (unsigned char) *cp
,
1782 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1785 content_error (NULL
, ct
,
1786 "invalid BASE64 encoding -- continuing");
1790 bits
|= value
<< bitno
;
1792 if ((bitno
-= 6) < 0) {
1793 b
= (bits
>> 16) & 0xff;
1794 if (!text
|| b
!= '\r')
1795 putc ((char) b
, ce
->ce_fp
);
1797 MD5Update (&mdContext
, &b
, 1);
1799 b
= (bits
>> 8) & 0xff;
1800 if (! text
|| b
!= '\r')
1801 putc ((char) b
, ce
->ce_fp
);
1803 MD5Update (&mdContext
, &b
, 1);
1806 if (! text
|| b
!= '\r')
1807 putc ((char) b
, ce
->ce_fp
);
1809 MD5Update (&mdContext
, &b
, 1);
1813 if (ferror (ce
->ce_fp
)) {
1814 content_error (ce
->ce_file
, ct
,
1815 "error writing to");
1818 bitno
= 18, bits
= 0L, skip
= 0;
1824 goto self_delimiting
;
1833 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1835 content_error (NULL
, ct
, "invalid BASE64 encoding");
1840 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1842 if (fflush (ce
->ce_fp
)) {
1843 content_error (ce
->ce_file
, ct
, "error writing to");
1848 unsigned char digest
[16];
1850 MD5Final (digest
, &mdContext
);
1851 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1852 sizeof(digest
) / sizeof(digest
[0])))
1853 content_error (NULL
, ct
,
1854 "content integrity suspect (digest mismatch) -- continuing");
1857 fprintf (stderr
, "content integrity confirmed\n");
1860 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1863 *file
= ce
->ce_file
;
1868 return fileno (ce
->ce_fp
);
1875 free_encoding (ct
, 0);
1884 static char hex2nib
[0x80] = {
1885 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1886 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1887 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1888 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1889 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1891 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1892 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1893 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1894 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1895 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1896 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1897 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1898 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1899 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1900 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1907 return init_encoding (ct
, openQuoted
);
1912 openQuoted (CT ct
, char **file
)
1914 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1920 CE ce
= &ct
->c_cefile
;
1921 /* sbeck -- handle suffixes */
1926 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1931 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1932 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1938 if (*file
== NULL
) {
1941 ce
->ce_file
= add (*file
, NULL
);
1945 /* sbeck@cise.ufl.edu -- handle suffixes */
1947 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1948 if (ce
->ce_unlink
) {
1949 /* Create temporary file with filename extension. */
1950 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1951 adios(NULL
, "unable to create temporary file in %s",
1955 ce
->ce_file
= add (cp
, ce
->ce_file
);
1957 } else if (*file
== NULL
) {
1959 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1960 adios(NULL
, "unable to create temporary file in %s",
1963 ce
->ce_file
= add (tempfile
, NULL
);
1966 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1967 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1971 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1972 adios (NULL
, "internal error(2)");
1975 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1976 content_error (ct
->c_file
, ct
, "unable to open for reading");
1982 if ((digested
= ct
->c_digested
))
1983 MD5Init (&mdContext
);
1990 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1992 if ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) == -1) {
1993 content_error (NULL
, ct
, "premature eof");
1997 if ((cc
= gotlen
) > len
)
2001 for (ep
= (cp
= bufp
) + cc
- 1; cp
<= ep
; ep
--)
2002 if (!isspace ((unsigned char) *ep
))
2006 for (; cp
< ep
; cp
++) {
2008 /* in an escape sequence */
2010 /* at byte 1 of an escape sequence */
2011 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2012 /* next is byte 2 */
2015 /* at byte 2 of an escape sequence */
2017 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2018 putc (mask
, ce
->ce_fp
);
2020 MD5Update (&mdContext
, &mask
, 1);
2021 if (ferror (ce
->ce_fp
)) {
2022 content_error (ce
->ce_file
, ct
, "error writing to");
2025 /* finished escape sequence; next may be literal or a new
2026 * escape sequence */
2029 /* on to next byte */
2033 /* not in an escape sequence */
2035 /* starting an escape sequence, or invalid '='? */
2036 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2037 /* "=\n" soft line break, eat the \n */
2041 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2042 /* We don't have 2 bytes left, so this is an invalid
2043 * escape sequence; just show the raw bytes (below). */
2044 } else if (isxdigit ((unsigned char) cp
[1]) &&
2045 isxdigit ((unsigned char) cp
[2])) {
2046 /* Next 2 bytes are hex digits, making this a valid escape
2047 * sequence; let's decode it (above). */
2051 /* One or both of the next 2 is out of range, making this
2052 * an invalid escape sequence; just show the raw bytes
2057 /* Just show the raw byte. */
2058 putc (*cp
, ce
->ce_fp
);
2061 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2063 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2066 if (ferror (ce
->ce_fp
)) {
2067 content_error (ce
->ce_file
, ct
, "error writing to");
2073 content_error (NULL
, ct
,
2074 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2078 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2080 if (fflush (ce
->ce_fp
)) {
2081 content_error (ce
->ce_file
, ct
, "error writing to");
2086 unsigned char digest
[16];
2088 MD5Final (digest
, &mdContext
);
2089 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2090 sizeof(digest
) / sizeof(digest
[0])))
2091 content_error (NULL
, ct
,
2092 "content integrity suspect (digest mismatch) -- continuing");
2095 fprintf (stderr
, "content integrity confirmed\n");
2098 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2101 *file
= ce
->ce_file
;
2106 return fileno (ce
->ce_fp
);
2109 free_encoding (ct
, 0);
2125 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2128 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2134 open7Bit (CT ct
, char **file
)
2136 int cc
, fd
, len
, own_ct_fp
= 0;
2137 char buffer
[BUFSIZ
];
2138 /* sbeck -- handle suffixes */
2141 CE ce
= &ct
->c_cefile
;
2144 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2149 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2150 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2156 if (*file
== NULL
) {
2159 ce
->ce_file
= add (*file
, NULL
);
2163 /* sbeck@cise.ufl.edu -- handle suffixes */
2165 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2166 if (ce
->ce_unlink
) {
2167 /* Create temporary file with filename extension. */
2168 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2169 adios(NULL
, "unable to create temporary file in %s",
2173 ce
->ce_file
= add (cp
, ce
->ce_file
);
2175 } else if (*file
== NULL
) {
2177 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2178 adios(NULL
, "unable to create temporary file in %s",
2181 ce
->ce_file
= add (tempfile
, NULL
);
2184 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2185 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2189 if (ct
->c_type
== CT_MULTIPART
) {
2190 CI ci
= &ct
->c_ctinfo
;
2194 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2195 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2196 + 1 + strlen (ci
->ci_subtype
);
2197 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2200 fputs (buffer
, ce
->ce_fp
);
2204 if (ci
->ci_comment
) {
2205 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2206 fputs ("\n\t", ce
->ce_fp
);
2210 putc (' ', ce
->ce_fp
);
2213 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2216 fprintf (ce
->ce_fp
, "\n");
2218 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2220 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2222 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2223 fprintf (ce
->ce_fp
, "\n");
2226 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2227 adios (NULL
, "internal error(3)");
2230 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2231 content_error (ct
->c_file
, ct
, "unable to open for reading");
2237 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2239 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2241 content_error (ct
->c_file
, ct
, "error reading from");
2245 content_error (NULL
, ct
, "premature eof");
2253 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2254 if (ferror (ce
->ce_fp
)) {
2255 content_error (ce
->ce_file
, ct
, "error writing to");
2260 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2262 if (fflush (ce
->ce_fp
)) {
2263 content_error (ce
->ce_file
, ct
, "error writing to");
2267 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2270 *file
= ce
->ce_file
;
2275 return fileno (ce
->ce_fp
);
2278 free_encoding (ct
, 0);
2292 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2294 char cachefile
[BUFSIZ
];
2297 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2302 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2303 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2309 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2310 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2311 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2312 ce
->ce_file
= getcpy (cachefile
);
2316 admonish (cachefile
, "unable to fopen for reading");
2323 *file
= ce
->ce_file
;
2324 *fd
= fileno (ce
->ce_fp
);
2335 return init_encoding (ct
, openFile
);
2340 openFile (CT ct
, char **file
)
2343 char cachefile
[BUFSIZ
];
2344 struct exbody
*e
= ct
->c_ctexbody
;
2345 CE ce
= &ct
->c_cefile
;
2347 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2359 content_error (NULL
, ct
, "missing name parameter");
2363 ce
->ce_file
= getcpy (e
->eb_name
);
2366 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2367 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2371 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2372 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2373 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2377 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2378 if ((fp
= fopen (cachefile
, "w"))) {
2380 char buffer
[BUFSIZ
];
2381 FILE *gp
= ce
->ce_fp
;
2383 fseek (gp
, 0L, SEEK_SET
);
2385 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2387 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2391 admonish (ce
->ce_file
, "error reading");
2392 (void) m_unlink (cachefile
);
2396 admonish (cachefile
, "error writing");
2397 (void) m_unlink (cachefile
);
2404 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2405 *file
= ce
->ce_file
;
2406 return fileno (ce
->ce_fp
);
2416 return init_encoding (ct
, openFTP
);
2421 openFTP (CT ct
, char **file
)
2423 int cachetype
, caching
, fd
;
2425 char *bp
, *ftp
, *user
, *pass
;
2426 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2428 CE ce
= &ct
->c_cefile
;
2429 static char *username
= NULL
;
2430 static char *password
= NULL
;
2434 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2440 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2451 if (!e
->eb_name
|| !e
->eb_site
) {
2452 content_error (NULL
, ct
, "missing %s parameter",
2453 e
->eb_name
? "site": "name");
2457 /* Get the buffer ready to go */
2459 buflen
= sizeof(buffer
);
2462 * Construct the query message for user
2464 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2470 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2476 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2477 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2482 if (e
->eb_size
> 0) {
2483 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2488 snprintf (bp
, buflen
, "? ");
2491 * Now, check the answer
2493 if (!getanswer (buffer
))
2498 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2502 ruserpass (e
->eb_site
, &username
, &password
);
2507 ce
->ce_unlink
= (*file
== NULL
);
2509 cachefile
[0] = '\0';
2510 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2511 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2512 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2513 if (*file
== NULL
) {
2520 ce
->ce_file
= add (*file
, NULL
);
2522 ce
->ce_file
= add (cachefile
, NULL
);
2525 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2526 adios(NULL
, "unable to create temporary file in %s",
2529 ce
->ce_file
= add (tempfile
, NULL
);
2532 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2533 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2538 int child_id
, i
, vecp
;
2542 vec
[vecp
++] = r1bindex (ftp
, '/');
2543 vec
[vecp
++] = e
->eb_site
;
2546 vec
[vecp
++] = e
->eb_dir
;
2547 vec
[vecp
++] = e
->eb_name
;
2548 vec
[vecp
++] = ce
->ce_file
,
2549 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2550 ? "ascii" : "binary";
2555 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2559 adios ("fork", "unable to");
2563 close (fileno (ce
->ce_fp
));
2565 fprintf (stderr
, "unable to exec ");
2571 if (pidXwait (child_id
, NULL
)) {
2572 username
= password
= NULL
;
2582 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2587 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2588 if ((fp
= fopen (cachefile
, "w"))) {
2590 FILE *gp
= ce
->ce_fp
;
2592 fseek (gp
, 0L, SEEK_SET
);
2594 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2596 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2600 admonish (ce
->ce_file
, "error reading");
2601 (void) m_unlink (cachefile
);
2605 admonish (cachefile
, "error writing");
2606 (void) m_unlink (cachefile
);
2614 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2615 *file
= ce
->ce_file
;
2616 return fileno (ce
->ce_fp
);
2627 return init_encoding (ct
, openMail
);
2632 openMail (CT ct
, char **file
)
2634 int child_id
, fd
, i
, vecp
;
2636 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2637 struct exbody
*e
= ct
->c_ctexbody
;
2638 CE ce
= &ct
->c_cefile
;
2640 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2651 if (!e
->eb_server
) {
2652 content_error (NULL
, ct
, "missing server parameter");
2656 /* Get buffer ready to go */
2658 buflen
= sizeof(buffer
);
2660 /* Now, construct query message */
2661 snprintf (bp
, buflen
, "Retrieve content");
2667 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2673 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2675 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2677 /* Now, check answer */
2678 if (!getanswer (buffer
))
2682 vec
[vecp
++] = r1bindex (mailproc
, '/');
2683 vec
[vecp
++] = e
->eb_server
;
2684 vec
[vecp
++] = "-subject";
2685 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2686 vec
[vecp
++] = "-body";
2687 vec
[vecp
++] = e
->eb_body
;
2690 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2694 advise ("fork", "unable to");
2698 execvp (mailproc
, vec
);
2699 fprintf (stderr
, "unable to exec ");
2705 if (pidXwait (child_id
, NULL
) == OK
)
2706 advise (NULL
, "request sent");
2710 if (*file
== NULL
) {
2712 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2713 adios(NULL
, "unable to create temporary file in %s",
2716 ce
->ce_file
= add (tempfile
, NULL
);
2719 ce
->ce_file
= add (*file
, NULL
);
2723 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2724 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2728 /* showproc is for mhshow and mhstore, though mhlist -debug
2729 * prints it, too. */
2731 free (ct
->c_showproc
);
2732 ct
->c_showproc
= add ("true", NULL
);
2734 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2735 *file
= ce
->ce_file
;
2736 return fileno (ce
->ce_fp
);
2747 return init_encoding (ct
, openURL
);
2752 openURL (CT ct
, char **file
)
2754 struct exbody
*e
= ct
->c_ctexbody
;
2755 CE ce
= &ct
->c_cefile
;
2756 char *urlprog
, *program
;
2757 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2758 int fd
, caching
, cachetype
;
2759 struct msgs_array args
= { 0, 0, NULL
};
2762 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2766 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2770 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2782 content_error(NULL
, ct
, "missing url parameter");
2786 ce
->ce_unlink
= (*file
== NULL
);
2788 cachefile
[0] = '\0';
2790 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2791 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2792 if (*file
== NULL
) {
2799 ce
->ce_file
= add(*file
, NULL
);
2801 ce
->ce_file
= add(cachefile
, NULL
);
2804 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2805 adios(NULL
, "unable to create temporary file in %s",
2808 ce
->ce_file
= add (tempfile
, NULL
);
2811 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2812 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2816 switch (child_id
= fork()) {
2818 adios ("fork", "unable to");
2822 argsplit_msgarg(&args
, urlprog
, &program
);
2823 app_msgarg(&args
, e
->eb_url
);
2824 app_msgarg(&args
, NULL
);
2825 dup2(fileno(ce
->ce_fp
), 1);
2826 close(fileno(ce
->ce_fp
));
2827 execvp(program
, args
.msgs
);
2828 fprintf(stderr
, "Unable to exec ");
2834 if (pidXwait(child_id
, NULL
)) {
2842 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2847 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2848 if ((fp
= fopen(cachefile
, "w"))) {
2850 FILE *gp
= ce
->ce_fp
;
2852 fseeko(gp
, 0, SEEK_SET
);
2854 while ((cc
= fread(buffer
, sizeof(*buffer
),
2855 sizeof(buffer
), gp
)) > 0)
2856 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2861 admonish(ce
->ce_file
, "error reading");
2862 (void) m_unlink (cachefile
);
2869 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2870 *file
= ce
->ce_file
;
2875 readDigest (CT ct
, char *cp
)
2880 unsigned char *dp
, value
, *ep
;
2886 for (ep
= (dp
= ct
->c_digest
)
2887 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2892 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2894 fprintf (stderr
, "invalid BASE64 encoding\n");
2898 bits
|= value
<< bitno
;
2900 if ((bitno
-= 6) < 0) {
2901 if (dp
+ (3 - skip
) > ep
)
2902 goto invalid_digest
;
2903 *dp
++ = (bits
>> 16) & 0xff;
2905 *dp
++ = (bits
>> 8) & 0xff;
2907 *dp
++ = bits
& 0xff;
2917 goto self_delimiting
;
2922 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2932 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2940 fprintf (stderr
, "MD5 digest=");
2941 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2942 fprintf (stderr
, "%02x", *dp
& 0xff);
2943 fprintf (stderr
, "\n");
2950 /* Multipart parts might have content before the first subpart and/or
2951 after the last subpart that hasn't been stored anywhere else, so do
2954 get_leftover_mp_content (CT ct
, int before
/* or after */)
2956 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2958 int found_boundary
= 0;
2964 char *content
= NULL
;
2966 if (! m
) return NOTOK
;
2969 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2971 /* Isolate the beginning of this part to the beginning of the
2972 first subpart and save any content between them. */
2973 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2974 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2975 boundary
= concat ("--", m
->mp_start
, NULL
);
2977 struct part
*last_subpart
= NULL
;
2978 struct part
*subpart
;
2980 /* Go to the last subpart to get its end position. */
2981 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
2982 last_subpart
= subpart
;
2985 if (last_subpart
== NULL
) return NOTOK
;
2987 /* Isolate the end of the last subpart to the end of this part
2988 and save any content between them. */
2989 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
2990 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
2991 boundary
= concat ("--", m
->mp_stop
, NULL
);
2994 /* Back up by 1 to pick up the newline. */
2995 while ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) != -1) {
2997 /* Don't look beyond beginning of first subpart (before) or
2998 next part (after). */
2999 if (read
> max
) bufp
[read
-max
] = '\0';
3002 if (! strcmp (bufp
, boundary
)) {
3006 if (! found_boundary
&& ! strcmp (bufp
, boundary
)) {
3012 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3014 char *old_content
= content
;
3015 content
= concat (content
, bufp
, NULL
);
3019 ? concat ("\n", bufp
, NULL
)
3020 : concat (bufp
, NULL
);
3025 if (found_boundary
|| read
> max
) break;
3027 if (read
> max
) break;
3031 /* Skip the newline if that's all there is. */
3035 /* Remove trailing newline, except at EOF. */
3036 if ((before
|| ! feof (ct
->c_fp
)) &&
3037 (cp
= content
+ strlen (content
)) > content
&&
3042 if (strlen (content
) > 1) {
3044 m
->mp_content_before
= content
;
3046 m
->mp_content_after
= content
;
3061 ct_type_str (int type
) {
3063 case CT_APPLICATION
:
3064 return "application";
3080 return "unknown_type";
3086 ct_subtype_str (int type
, int subtype
) {
3088 case CT_APPLICATION
:
3090 case APPLICATION_OCTETS
:
3092 case APPLICATION_POSTSCRIPT
:
3093 return "postscript";
3095 return "unknown_app_subtype";
3099 case MESSAGE_RFC822
:
3101 case MESSAGE_PARTIAL
:
3103 case MESSAGE_EXTERNAL
:
3106 return "unknown_msg_subtype";
3112 case MULTI_ALTERNATE
:
3113 return "alternative";
3116 case MULTI_PARALLEL
:
3119 return "unknown_multipart_subtype";
3130 return "unknown_text_subtype";
3133 return "unknown_type";
3138 /* Find the content type and InitFunc for the CT. */
3139 const struct str2init
*
3140 get_ct_init (int type
) {
3141 const struct str2init
*sp
;
3143 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3144 if (type
== sp
->si_val
) {
3153 ce_str (int encoding
) {
3158 return "quoted-printable";
3174 /* Find the content type and InitFunc for the content encoding method. */
3175 const struct str2init
*
3176 get_ce_method (const char *method
) {
3177 struct str2init
*sp
;
3179 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3180 if (! strcasecmp (method
, sp
->si_key
)) {
3189 * Parse a series of MIME attributes (or parameters) given a header as
3192 * Arguments include:
3194 * filename - Name of input file (for error messages)
3195 * fieldname - Name of field being processed
3196 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3197 * Updated to point to end of attributes when finished.
3198 * param_head - Pointer to head of parameter list
3199 * param_tail - Pointer to tail of parameter list
3200 * commentp - Pointer to header comment pointer (may be NULL)
3202 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3203 * DONE to indicate a benign error (minor parsing error, but the program
3208 parse_header_attrs (const char *filename
, const char *fieldname
,
3209 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3212 char *cp
= *header_attrp
;
3218 struct sectlist
*next
;
3224 struct sectlist
*sechead
;
3225 struct parmlist
*next
;
3226 } *pp
, *pp2
, *phead
= NULL
;
3228 while (*cp
== ';') {
3229 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3230 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3233 while (isspace ((unsigned char) *cp
))
3237 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3243 "extraneous trailing ';' in message %s's %s: "
3245 filename
, fieldname
);
3249 /* down case the attribute name */
3250 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3251 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3252 *dp
= tolower ((unsigned char) *dp
);
3254 for (up
= dp
; isspace ((unsigned char) *dp
);)
3256 if (dp
== cp
|| *dp
!= '=') {
3258 "invalid parameter in message %s's %s: "
3259 "field\n%*sparameter %s (error detected at offset %d)",
3260 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3265 * To handle RFC 2231, we have to deal with the following extensions:
3267 * name*=encoded-value
3268 * name*<N>=part-N-of-a-parameter-value
3269 * name*<N>*=encoded-part-N-of-a-parameter-value
3272 * If there's a * right before the equal sign, it's encoded.
3273 * If there's a * and one or more digits, then it's section N.
3275 * Remember we can have one or the other, or both. cp points to
3276 * beginning of name, up points past the last character in the
3280 for (vp
= cp
; vp
< up
; vp
++) {
3281 if (*vp
== '*' && vp
< up
- 1) {
3284 } else if (*vp
== '*' && vp
== up
- 1) {
3286 } else if (partial
) {
3287 if (isdigit((unsigned char) *vp
))
3288 index
= *vp
- '0' + index
* 10;
3290 advise (NULL
, "invalid parameter index in message %s's "
3291 "%s: field\n%*s(parameter %s)", filename
,
3292 fieldname
, strlen(invo_name
) + 2, "", cp
);
3301 * Break out the parameter name and value sections and allocate
3305 nameptr
= mh_xmalloc(len
+ 1);
3306 strncpy(nameptr
, cp
, len
);
3307 nameptr
[len
] = '\0';
3309 for (dp
++; isspace ((unsigned char) *dp
);)
3314 * Single quotes delimit the character set and language tag.
3315 * They are required on the first section (or a complete
3320 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3326 charset
= mh_xmalloc(len
+ 1);
3327 strncpy(charset
, dp
, len
);
3328 charset
[len
] = '\0';
3334 advise(NULL
, "missing charset in message %s's %s: "
3335 "field\n%*s(parameter %s)", filename
, fieldname
,
3336 strlen(invo_name
) + 2, "", nameptr
);
3342 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3349 lang
= mh_xmalloc(len
+ 1);
3350 strncpy(lang
, dp
, len
);
3357 advise(NULL
, "missing language tag in message %s's %s: "
3358 "field\n%*s(parameter %s)", filename
, fieldname
,
3359 strlen(invo_name
) + 2, "", nameptr
);
3370 * At this point vp should be pointing at the beginning
3371 * of the encoded value/section. Continue until we reach
3372 * the end or get whitespace. But first, calculate the
3373 * length so we can allocate the correct buffer size.
3376 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3378 if (*(vp
+ 1) == '\0' ||
3379 !isxdigit((unsigned char) *(vp
+ 1)) ||
3380 *(vp
+ 2) == '\0' ||
3381 !isxdigit((unsigned char) *(vp
+ 2))) {
3382 advise(NULL
, "invalid encoded sequence in message "
3383 "%s's %s: field\n%*s(parameter %s)",
3384 filename
, fieldname
, strlen(invo_name
) + 2,
3398 up
= valptr
= mh_xmalloc(len
+ 1);
3400 for (vp
= dp
; istoken(*vp
); vp
++) {
3402 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3413 * A "normal" string. If it's got a leading quote, then we
3414 * strip the quotes out. Otherwise go until we reach the end
3415 * or get whitespace. Note we scan it twice; once to get the
3416 * length, then the second time copies it into the destination
3423 for (cp
= dp
+ 1;;) {
3428 "invalid quoted-string in message %s's %s: "
3429 "field\n%*s(parameter %s)",
3430 filename
, fieldname
, strlen(invo_name
) + 2, "",
3453 for (cp
= dp
; istoken (*cp
); cp
++) {
3458 valptr
= mh_xmalloc(len
+ 1);
3462 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3470 strncpy(valptr
, cp
= dp
, len
);
3478 * If 'partial' is set, we don't allocate a parameter now. We
3479 * put it on the parameter linked list to be reassembled later.
3481 * "phead" points to a list of all parameters we need to reassemble.
3482 * Each parameter has a list of sections. We insert the sections in
3487 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3488 if (strcasecmp(nameptr
, pp
->name
) == 0)
3493 pp
= mh_xmalloc(sizeof(*pp
));
3494 memset(pp
, 0, sizeof(*pp
));
3501 * Insert this into the section linked list
3504 sp
= mh_xmalloc(sizeof(*sp
));
3505 memset(sp
, 0, sizeof(*sp
));
3510 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3511 sp
->next
= pp
->sechead
;
3514 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3515 if (sp2
->index
== sp
->index
) {
3516 advise (NULL
, "duplicate index (%d) in message "
3517 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3518 filename
, fieldname
, strlen(invo_name
) + 2, "",
3522 if (sp2
->index
< sp
->index
&&
3523 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3524 sp
->next
= sp2
->next
;
3531 advise(NULL
, "Internal error: cannot insert partial "
3532 "param in message %s's %s: field\n%*s(parameter %s)",
3533 filename
, fieldname
, strlen(invo_name
) + 2, "",
3540 * Save our charset and lang tags.
3543 if (index
== 0 && encoded
) {
3546 pp
->charset
= charset
;
3552 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3553 pm
->pm_charset
= charset
;
3557 while (isspace ((unsigned char) *cp
))
3561 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3567 * Now that we're done, reassemble all of the partial parameters.
3570 for (pp
= phead
; pp
!= NULL
; ) {
3574 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3575 if (sp
->index
!= pindex
++) {
3576 advise(NULL
, "missing section %d for parameter in "
3577 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3578 filename
, fieldname
, strlen(invo_name
) + 2, "",
3585 p
= q
= mh_xmalloc(tlen
+ 1);
3586 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3587 memcpy(q
, sp
->value
, sp
->len
);
3597 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3598 pm
->pm_charset
= pp
->charset
;
3599 pm
->pm_lang
= pp
->lang
;
3610 * Return the charset for a particular content type. Return pointer is
3611 * only valid until the next call to content_charset().
3615 content_charset (CT ct
) {
3616 static char *ret_charset
= NULL
;
3618 if (ret_charset
!= NULL
) {
3622 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3624 return ret_charset
? ret_charset
: "US-ASCII";
3629 * Create a string based on a list of output parameters. Assume that this
3630 * parameter string will be appended to an existing header, so start out
3631 * with the separator (;). Perform RFC 2231 encoding when necessary.
3635 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3637 char *paramout
= NULL
;
3638 char line
[CPERLIN
* 2], *q
;
3639 int curlen
, index
, cont
, encode
, i
;
3640 size_t valoff
, numchars
;
3642 while (params
!= NULL
) {
3648 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3651 if (strlen(params
->pm_name
) > CPERLIN
) {
3652 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3658 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3661 * Loop until we get a parameter that fits within a line. We
3662 * assume new lines start with a tab, so check our overflow based
3672 * At this point we're definitely continuing the line, so
3673 * be sure to include the parameter name and section index.
3676 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3677 params
->pm_name
, index
);
3680 * Both of these functions do a NUL termination
3684 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3685 numchars
, valoff
, index
);
3687 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3698 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3703 * "line" starts with a ;\n\t, so that doesn't count against
3704 * the length. But add 8 since it starts with a tab; that's
3705 * how we end up with 5.
3708 initialwidth
= strlen(line
) + 5;
3711 * At this point the line should be built, so add it to our
3712 * current output buffer.
3715 paramout
= add(line
, paramout
);
3719 * If this won't fit on the line, start a new one. Save room in
3720 * case we need a semicolon on the end
3723 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3735 * At this point, we're either finishing a contined parameter, or
3736 * we're working on a new one.
3740 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3741 params
->pm_name
, index
);
3743 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3748 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3749 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3751 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3752 strlen(params
->pm_value
+ valoff
), valoff
);
3760 paramout
= add(line
, paramout
);
3761 initialwidth
+= strlen(line
);
3763 params
= params
->pm_next
;
3767 *offsetout
= initialwidth
;
3773 * Calculate the size of a parameter.
3777 * pm - The parameter being output
3778 * index - If continuing the parameter, the index of the section
3780 * valueoff - The current offset into the parameter value that we're
3781 * working on (previous sections have consumed valueoff bytes).
3782 * encode - Set if we should perform encoding on this parameter section
3783 * (given that we're consuming bytesfit bytes).
3784 * cont - Set if the remaining data in value will not fit on a single
3785 * line and will need to be continued.
3786 * bytesfit - The number of bytes that we can consume from the parameter
3787 * value and still fit on a completely new line. The
3788 * calculation assumes the new line starts with a tab,
3789 * includes the parameter name and any encoding, and fits
3790 * within CPERLIN bytes. Will always be at least 1.
3794 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3797 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3798 size_t len
= 0, fit
= 0;
3799 int fitlimit
= 0, eightbit
, maxfit
;
3804 * Add up the length. First, start with the parameter name.
3807 len
= strlen(pm
->pm_name
);
3810 * Scan the parameter value and see if we need to do encoding for this
3814 eightbit
= contains8bit(start
, NULL
);
3817 * Determine if we need to encode this section. Encoding is necessary if:
3819 * - There are any 8-bit characters at all and we're on the first
3821 * - There are 8-bit characters within N bytes of our section start.
3822 * N is calculated based on the number of bytes it would take to
3823 * reach CPERLIN. Specifically:
3824 * 8 (starting tab) +
3825 * strlen(param name) +
3826 * 4 ('* for section marker, '=', opening/closing '"')
3828 * is the number of bytes used by everything that isn't part of the
3829 * value. So that gets subtracted from CPERLIN.
3832 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3833 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3834 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3838 len
++; /* Add in equal sign */
3842 * We're using maxfit as a marker for how many characters we can
3843 * fit into the line. Bump it by two because we're not using quotes
3850 * If we don't have a charset or language tag in this parameter,
3854 if (! pm
->pm_charset
)
3855 pm
->pm_charset
= getcpy(write_charset_8bit());
3857 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
3859 len
++; /* For the encoding marker */
3862 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
3867 * We know we definitely need to include an index. maxfit already
3868 * includes the section marker.
3870 len
+= strlen(indexchar
);
3872 for (p
= start
; *p
!= '\0'; p
++) {
3873 if (isparamencode(*p
)) {
3881 * Just so there's no confusion: maxfit is counting OUTPUT
3882 * characters (post-encoding). fit is counting INPUT characters.
3884 if (! fitlimit
&& maxfit
>= 0)
3886 else if (! fitlimit
)
3891 * Calculate the string length, but add room for quoting \
3892 * and " if necessary. Also account for quotes at beginning
3895 for (p
= start
; *p
!= '\0'; p
++) {
3906 if (! fitlimit
&& maxfit
>= 0)
3908 else if (! fitlimit
)
3925 * Output an encoded parameter string.
3929 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
3930 size_t valueoff
, int index
)
3932 size_t outlen
= 0, n
;
3933 char *endptr
= output
+ len
, *p
;
3936 * First, output the marker for an encoded string.
3944 * If the index is 0, output the character set and language tag.
3945 * If theses were NULL, they should have already been filled in
3950 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
3954 if (output
> endptr
) {
3955 advise(NULL
, "Internal error: parameter buffer overflow");
3961 * Copy over the value, encoding if necessary
3964 p
= pm
->pm_value
+ valueoff
;
3965 while (valuelen
-- > 0) {
3966 if (isparamencode(*p
)) {
3967 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
3974 if (output
> endptr
) {
3975 advise(NULL
, "Internal error: parameter buffer overflow");
3986 * Output a "normal" parameter, without encoding. Be sure to escape
3987 * quotes and backslashes if necessary.
3991 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
3995 char *endptr
= output
+ len
, *p
;
4001 p
= pm
->pm_value
+ valueoff
;
4003 while (valuelen
-- > 0) {
4013 if (output
> endptr
) {
4014 advise(NULL
, "Internal error: parameter buffer overflow");
4019 if (output
- 2 > endptr
) {
4020 advise(NULL
, "Internal error: parameter buffer overflow");
4031 * Add a parameter to the parameter linked list
4035 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4037 PM pm
= mh_xmalloc(sizeof(*pm
));
4039 memset(pm
, 0, sizeof(*pm
));
4041 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4042 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4045 (*last
)->pm_next
= pm
;
4056 * Either replace a current parameter with a new value, or add the parameter
4057 * to the parameter linked list.
4061 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4065 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4066 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4068 * If nocopy is set, it's assumed that we own both name
4069 * and value. We don't need name, so we discard it now.
4074 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4079 return add_param(first
, last
, name
, value
, nocopy
);
4083 * Retrieve a parameter value from a parameter linked list. If the parameter
4084 * value needs converted to the local character set, do that now.
4088 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4090 while (first
!= NULL
) {
4091 if (strcasecmp(name
, first
->pm_name
) == 0) {
4093 return first
->pm_value
;
4095 return getcpy(get_param_value(first
, replace
));
4097 first
= first
->pm_next
;
4104 * Return a parameter value, converting to the local character set if
4108 char *get_param_value(PM pm
, char replace
)
4110 static char buffer
[4096]; /* I hope no parameters are larger */
4111 size_t bufsize
= sizeof(buffer
);
4116 ICONV_CONST
char *p
;
4117 #else /* HAVE_ICONV */
4119 #endif /* HAVE_ICONV */
4124 * If we don't have a character set indicated, it's assumed to be
4125 * US-ASCII. If it matches our character set, we don't need to convert
4129 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4130 strlen(pm
->pm_charset
))) {
4131 return pm
->pm_value
;
4135 * In this case, we need to convert. If we have iconv support, use
4136 * that. Otherwise, go through and simply replace every non-ASCII
4137 * character with the substitution character.
4142 bufsize
= sizeof(buffer
);
4143 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4145 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4146 if (cd
== (iconv_t
) -1) {
4150 inbytes
= strlen(pm
->pm_value
);
4154 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4155 if (errno
!= EILSEQ
) {
4160 * Reset shift state, substitute our character,
4161 * try to restart conversion.
4164 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4177 for (++p
, --inbytes
;
4178 inbytes
> 0 && (((unsigned char) *q
) & 0xc0) == 0x80;
4197 #endif /* HAVE_ICONV */
4200 * Take everything non-ASCII and substituite the replacement character
4204 bufsize
= sizeof(buffer
);
4205 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4206 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))