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 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1715 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1716 cp
= context_find (buffer
);
1717 if (cp
== NULL
|| *cp
== '\0') {
1718 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1720 cp
= context_find (buffer
);
1722 if (cp
!= NULL
&& *cp
!= '\0') {
1723 if (ce
->ce_unlink
) {
1724 /* Create temporary file with filename extension. */
1725 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1726 adios(NULL
, "unable to create temporary file in %s",
1730 ce
->ce_file
= add (cp
, ce
->ce_file
);
1732 } else if (*file
== NULL
) {
1734 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1735 adios(NULL
, "unable to create temporary file in %s",
1738 ce
->ce_file
= add (tempfile
, NULL
);
1741 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1742 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1746 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1747 adios (NULL
, "internal error(1)");
1750 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1751 content_error (ct
->c_file
, ct
, "unable to open for reading");
1757 if ((digested
= ct
->c_digested
))
1758 MD5Init (&mdContext
);
1764 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1766 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1768 content_error (ct
->c_file
, ct
, "error reading from");
1772 content_error (NULL
, ct
, "premature eof");
1780 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1783 if (isspace ((unsigned char) *cp
))
1785 if (skip
|| (((unsigned char) *cp
) & 0x80)
1786 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1788 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1789 (unsigned char) *cp
,
1790 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1793 content_error (NULL
, ct
,
1794 "invalid BASE64 encoding -- continuing");
1798 bits
|= value
<< bitno
;
1800 if ((bitno
-= 6) < 0) {
1801 b
= (bits
>> 16) & 0xff;
1802 if (!text
|| b
!= '\r')
1803 putc ((char) b
, ce
->ce_fp
);
1805 MD5Update (&mdContext
, &b
, 1);
1807 b
= (bits
>> 8) & 0xff;
1808 if (! text
|| b
!= '\r')
1809 putc ((char) b
, ce
->ce_fp
);
1811 MD5Update (&mdContext
, &b
, 1);
1814 if (! text
|| b
!= '\r')
1815 putc ((char) b
, ce
->ce_fp
);
1817 MD5Update (&mdContext
, &b
, 1);
1821 if (ferror (ce
->ce_fp
)) {
1822 content_error (ce
->ce_file
, ct
,
1823 "error writing to");
1826 bitno
= 18, bits
= 0L, skip
= 0;
1832 goto self_delimiting
;
1841 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1843 content_error (NULL
, ct
, "invalid BASE64 encoding");
1848 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1850 if (fflush (ce
->ce_fp
)) {
1851 content_error (ce
->ce_file
, ct
, "error writing to");
1856 unsigned char digest
[16];
1858 MD5Final (digest
, &mdContext
);
1859 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1860 sizeof(digest
) / sizeof(digest
[0])))
1861 content_error (NULL
, ct
,
1862 "content integrity suspect (digest mismatch) -- continuing");
1865 fprintf (stderr
, "content integrity confirmed\n");
1868 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1871 *file
= ce
->ce_file
;
1876 return fileno (ce
->ce_fp
);
1883 free_encoding (ct
, 0);
1892 static char hex2nib
[0x80] = {
1893 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1898 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1899 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1900 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1901 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1902 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1905 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1915 return init_encoding (ct
, openQuoted
);
1920 openQuoted (CT ct
, char **file
)
1922 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1924 char buffer
[BUFSIZ
];
1929 CE ce
= &ct
->c_cefile
;
1930 /* sbeck -- handle suffixes */
1935 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1940 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1941 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1947 if (*file
== NULL
) {
1950 ce
->ce_file
= add (*file
, NULL
);
1954 /* sbeck@cise.ufl.edu -- handle suffixes */
1956 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1957 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1958 cp
= context_find (buffer
);
1959 if (cp
== NULL
|| *cp
== '\0') {
1960 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1962 cp
= context_find (buffer
);
1964 if (cp
!= NULL
&& *cp
!= '\0') {
1965 if (ce
->ce_unlink
) {
1966 /* Create temporary file with filename extension. */
1967 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1968 adios(NULL
, "unable to create temporary file in %s",
1972 ce
->ce_file
= add (cp
, ce
->ce_file
);
1974 } else if (*file
== NULL
) {
1976 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1977 adios(NULL
, "unable to create temporary file in %s",
1980 ce
->ce_file
= add (tempfile
, NULL
);
1983 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1984 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1988 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1989 adios (NULL
, "internal error(2)");
1992 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1993 content_error (ct
->c_file
, ct
, "unable to open for reading");
1999 if ((digested
= ct
->c_digested
))
2000 MD5Init (&mdContext
);
2007 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2009 if ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) == -1) {
2010 content_error (NULL
, ct
, "premature eof");
2014 if ((cc
= gotlen
) > len
)
2018 for (ep
= (cp
= bufp
) + cc
- 1; cp
<= ep
; ep
--)
2019 if (!isspace ((unsigned char) *ep
))
2023 for (; cp
< ep
; cp
++) {
2025 /* in an escape sequence */
2027 /* at byte 1 of an escape sequence */
2028 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2029 /* next is byte 2 */
2032 /* at byte 2 of an escape sequence */
2034 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2035 putc (mask
, ce
->ce_fp
);
2037 MD5Update (&mdContext
, &mask
, 1);
2038 if (ferror (ce
->ce_fp
)) {
2039 content_error (ce
->ce_file
, ct
, "error writing to");
2042 /* finished escape sequence; next may be literal or a new
2043 * escape sequence */
2046 /* on to next byte */
2050 /* not in an escape sequence */
2052 /* starting an escape sequence, or invalid '='? */
2053 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2054 /* "=\n" soft line break, eat the \n */
2058 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2059 /* We don't have 2 bytes left, so this is an invalid
2060 * escape sequence; just show the raw bytes (below). */
2061 } else if (isxdigit ((unsigned char) cp
[1]) &&
2062 isxdigit ((unsigned char) cp
[2])) {
2063 /* Next 2 bytes are hex digits, making this a valid escape
2064 * sequence; let's decode it (above). */
2068 /* One or both of the next 2 is out of range, making this
2069 * an invalid escape sequence; just show the raw bytes
2074 /* Just show the raw byte. */
2075 putc (*cp
, ce
->ce_fp
);
2078 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2080 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2083 if (ferror (ce
->ce_fp
)) {
2084 content_error (ce
->ce_file
, ct
, "error writing to");
2090 content_error (NULL
, ct
,
2091 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2095 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2097 if (fflush (ce
->ce_fp
)) {
2098 content_error (ce
->ce_file
, ct
, "error writing to");
2103 unsigned char digest
[16];
2105 MD5Final (digest
, &mdContext
);
2106 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2107 sizeof(digest
) / sizeof(digest
[0])))
2108 content_error (NULL
, ct
,
2109 "content integrity suspect (digest mismatch) -- continuing");
2112 fprintf (stderr
, "content integrity confirmed\n");
2115 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2118 *file
= ce
->ce_file
;
2123 return fileno (ce
->ce_fp
);
2126 free_encoding (ct
, 0);
2142 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2145 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2151 open7Bit (CT ct
, char **file
)
2153 int cc
, fd
, len
, own_ct_fp
= 0;
2154 char buffer
[BUFSIZ
];
2155 /* sbeck -- handle suffixes */
2158 CE ce
= &ct
->c_cefile
;
2161 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2166 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2167 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2173 if (*file
== NULL
) {
2176 ce
->ce_file
= add (*file
, NULL
);
2180 /* sbeck@cise.ufl.edu -- handle suffixes */
2182 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2183 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2184 cp
= context_find (buffer
);
2185 if (cp
== NULL
|| *cp
== '\0') {
2186 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2188 cp
= context_find (buffer
);
2190 if (cp
!= NULL
&& *cp
!= '\0') {
2191 if (ce
->ce_unlink
) {
2192 /* Create temporary file with filename extension. */
2193 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2194 adios(NULL
, "unable to create temporary file in %s",
2198 ce
->ce_file
= add (cp
, ce
->ce_file
);
2200 } else if (*file
== NULL
) {
2202 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2203 adios(NULL
, "unable to create temporary file in %s",
2206 ce
->ce_file
= add (tempfile
, NULL
);
2209 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2210 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2214 if (ct
->c_type
== CT_MULTIPART
) {
2215 CI ci
= &ct
->c_ctinfo
;
2219 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2220 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2221 + 1 + strlen (ci
->ci_subtype
);
2222 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2225 fputs (buffer
, ce
->ce_fp
);
2229 if (ci
->ci_comment
) {
2230 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2231 fputs ("\n\t", ce
->ce_fp
);
2235 putc (' ', ce
->ce_fp
);
2238 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2241 fprintf (ce
->ce_fp
, "\n");
2243 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2245 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2247 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2248 fprintf (ce
->ce_fp
, "\n");
2251 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2252 adios (NULL
, "internal error(3)");
2255 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2256 content_error (ct
->c_file
, ct
, "unable to open for reading");
2262 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2264 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2266 content_error (ct
->c_file
, ct
, "error reading from");
2270 content_error (NULL
, ct
, "premature eof");
2278 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2279 if (ferror (ce
->ce_fp
)) {
2280 content_error (ce
->ce_file
, ct
, "error writing to");
2285 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2287 if (fflush (ce
->ce_fp
)) {
2288 content_error (ce
->ce_file
, ct
, "error writing to");
2292 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2295 *file
= ce
->ce_file
;
2300 return fileno (ce
->ce_fp
);
2303 free_encoding (ct
, 0);
2317 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2319 char cachefile
[BUFSIZ
];
2322 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2327 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2328 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2334 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2335 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2336 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2337 ce
->ce_file
= getcpy (cachefile
);
2341 admonish (cachefile
, "unable to fopen for reading");
2348 *file
= ce
->ce_file
;
2349 *fd
= fileno (ce
->ce_fp
);
2360 return init_encoding (ct
, openFile
);
2365 openFile (CT ct
, char **file
)
2368 char cachefile
[BUFSIZ
];
2369 struct exbody
*e
= ct
->c_ctexbody
;
2370 CE ce
= &ct
->c_cefile
;
2372 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2384 content_error (NULL
, ct
, "missing name parameter");
2388 ce
->ce_file
= getcpy (e
->eb_name
);
2391 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2392 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2396 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2397 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2398 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2402 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2403 if ((fp
= fopen (cachefile
, "w"))) {
2405 char buffer
[BUFSIZ
];
2406 FILE *gp
= ce
->ce_fp
;
2408 fseek (gp
, 0L, SEEK_SET
);
2410 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2412 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2416 admonish (ce
->ce_file
, "error reading");
2417 (void) m_unlink (cachefile
);
2421 admonish (cachefile
, "error writing");
2422 (void) m_unlink (cachefile
);
2429 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2430 *file
= ce
->ce_file
;
2431 return fileno (ce
->ce_fp
);
2441 return init_encoding (ct
, openFTP
);
2446 openFTP (CT ct
, char **file
)
2448 int cachetype
, caching
, fd
;
2450 char *bp
, *ftp
, *user
, *pass
;
2451 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2453 CE ce
= &ct
->c_cefile
;
2454 static char *username
= NULL
;
2455 static char *password
= NULL
;
2459 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2465 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2476 if (!e
->eb_name
|| !e
->eb_site
) {
2477 content_error (NULL
, ct
, "missing %s parameter",
2478 e
->eb_name
? "site": "name");
2482 /* Get the buffer ready to go */
2484 buflen
= sizeof(buffer
);
2487 * Construct the query message for user
2489 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2495 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2501 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2502 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2507 if (e
->eb_size
> 0) {
2508 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2513 snprintf (bp
, buflen
, "? ");
2516 * Now, check the answer
2518 if (!getanswer (buffer
))
2523 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2527 ruserpass (e
->eb_site
, &username
, &password
);
2532 ce
->ce_unlink
= (*file
== NULL
);
2534 cachefile
[0] = '\0';
2535 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2536 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2537 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2538 if (*file
== NULL
) {
2545 ce
->ce_file
= add (*file
, NULL
);
2547 ce
->ce_file
= add (cachefile
, NULL
);
2550 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2551 adios(NULL
, "unable to create temporary file in %s",
2554 ce
->ce_file
= add (tempfile
, NULL
);
2557 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2558 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2563 int child_id
, i
, vecp
;
2567 vec
[vecp
++] = r1bindex (ftp
, '/');
2568 vec
[vecp
++] = e
->eb_site
;
2571 vec
[vecp
++] = e
->eb_dir
;
2572 vec
[vecp
++] = e
->eb_name
;
2573 vec
[vecp
++] = ce
->ce_file
,
2574 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2575 ? "ascii" : "binary";
2580 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2584 adios ("fork", "unable to");
2588 close (fileno (ce
->ce_fp
));
2590 fprintf (stderr
, "unable to exec ");
2596 if (pidXwait (child_id
, NULL
)) {
2597 username
= password
= NULL
;
2607 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2612 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2613 if ((fp
= fopen (cachefile
, "w"))) {
2615 FILE *gp
= ce
->ce_fp
;
2617 fseek (gp
, 0L, SEEK_SET
);
2619 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2621 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2625 admonish (ce
->ce_file
, "error reading");
2626 (void) m_unlink (cachefile
);
2630 admonish (cachefile
, "error writing");
2631 (void) m_unlink (cachefile
);
2639 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2640 *file
= ce
->ce_file
;
2641 return fileno (ce
->ce_fp
);
2652 return init_encoding (ct
, openMail
);
2657 openMail (CT ct
, char **file
)
2659 int child_id
, fd
, i
, vecp
;
2661 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2662 struct exbody
*e
= ct
->c_ctexbody
;
2663 CE ce
= &ct
->c_cefile
;
2665 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2676 if (!e
->eb_server
) {
2677 content_error (NULL
, ct
, "missing server parameter");
2681 /* Get buffer ready to go */
2683 buflen
= sizeof(buffer
);
2685 /* Now, construct query message */
2686 snprintf (bp
, buflen
, "Retrieve content");
2692 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2698 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2700 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2702 /* Now, check answer */
2703 if (!getanswer (buffer
))
2707 vec
[vecp
++] = r1bindex (mailproc
, '/');
2708 vec
[vecp
++] = e
->eb_server
;
2709 vec
[vecp
++] = "-subject";
2710 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2711 vec
[vecp
++] = "-body";
2712 vec
[vecp
++] = e
->eb_body
;
2715 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2719 advise ("fork", "unable to");
2723 execvp (mailproc
, vec
);
2724 fprintf (stderr
, "unable to exec ");
2730 if (pidXwait (child_id
, NULL
) == OK
)
2731 advise (NULL
, "request sent");
2735 if (*file
== NULL
) {
2737 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2738 adios(NULL
, "unable to create temporary file in %s",
2741 ce
->ce_file
= add (tempfile
, NULL
);
2744 ce
->ce_file
= add (*file
, NULL
);
2748 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2749 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2753 /* showproc is for mhshow and mhstore, though mhlist -debug
2754 * prints it, too. */
2756 free (ct
->c_showproc
);
2757 ct
->c_showproc
= add ("true", NULL
);
2759 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2760 *file
= ce
->ce_file
;
2761 return fileno (ce
->ce_fp
);
2772 return init_encoding (ct
, openURL
);
2777 openURL (CT ct
, char **file
)
2779 struct exbody
*e
= ct
->c_ctexbody
;
2780 CE ce
= &ct
->c_cefile
;
2781 char *urlprog
, *program
;
2782 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2783 int fd
, caching
, cachetype
;
2784 struct msgs_array args
= { 0, 0, NULL
};
2787 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2791 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2795 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2807 content_error(NULL
, ct
, "missing url parameter");
2811 ce
->ce_unlink
= (*file
== NULL
);
2813 cachefile
[0] = '\0';
2815 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2816 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2817 if (*file
== NULL
) {
2824 ce
->ce_file
= add(*file
, NULL
);
2826 ce
->ce_file
= add(cachefile
, NULL
);
2829 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2830 adios(NULL
, "unable to create temporary file in %s",
2833 ce
->ce_file
= add (tempfile
, NULL
);
2836 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2837 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2841 switch (child_id
= fork()) {
2843 adios ("fork", "unable to");
2847 argsplit_msgarg(&args
, urlprog
, &program
);
2848 app_msgarg(&args
, e
->eb_url
);
2849 app_msgarg(&args
, NULL
);
2850 dup2(fileno(ce
->ce_fp
), 1);
2851 close(fileno(ce
->ce_fp
));
2852 execvp(program
, args
.msgs
);
2853 fprintf(stderr
, "Unable to exec ");
2859 if (pidXwait(child_id
, NULL
)) {
2867 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2872 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2873 if ((fp
= fopen(cachefile
, "w"))) {
2875 FILE *gp
= ce
->ce_fp
;
2877 fseeko(gp
, 0, SEEK_SET
);
2879 while ((cc
= fread(buffer
, sizeof(*buffer
),
2880 sizeof(buffer
), gp
)) > 0)
2881 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2886 admonish(ce
->ce_file
, "error reading");
2887 (void) m_unlink (cachefile
);
2894 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2895 *file
= ce
->ce_file
;
2900 readDigest (CT ct
, char *cp
)
2905 unsigned char *dp
, value
, *ep
;
2911 for (ep
= (dp
= ct
->c_digest
)
2912 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2917 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2919 fprintf (stderr
, "invalid BASE64 encoding\n");
2923 bits
|= value
<< bitno
;
2925 if ((bitno
-= 6) < 0) {
2926 if (dp
+ (3 - skip
) > ep
)
2927 goto invalid_digest
;
2928 *dp
++ = (bits
>> 16) & 0xff;
2930 *dp
++ = (bits
>> 8) & 0xff;
2932 *dp
++ = bits
& 0xff;
2942 goto self_delimiting
;
2947 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2957 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2965 fprintf (stderr
, "MD5 digest=");
2966 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2967 fprintf (stderr
, "%02x", *dp
& 0xff);
2968 fprintf (stderr
, "\n");
2975 /* Multipart parts might have content before the first subpart and/or
2976 after the last subpart that hasn't been stored anywhere else, so do
2979 get_leftover_mp_content (CT ct
, int before
/* or after */)
2981 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2983 int found_boundary
= 0;
2989 char *content
= NULL
;
2991 if (! m
) return NOTOK
;
2994 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2996 /* Isolate the beginning of this part to the beginning of the
2997 first subpart and save any content between them. */
2998 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2999 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3000 boundary
= concat ("--", m
->mp_start
, NULL
);
3002 struct part
*last_subpart
= NULL
;
3003 struct part
*subpart
;
3005 /* Go to the last subpart to get its end position. */
3006 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3007 last_subpart
= subpart
;
3010 if (last_subpart
== NULL
) return NOTOK
;
3012 /* Isolate the end of the last subpart to the end of this part
3013 and save any content between them. */
3014 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3015 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3016 boundary
= concat ("--", m
->mp_stop
, NULL
);
3019 /* Back up by 1 to pick up the newline. */
3020 while ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) != -1) {
3022 /* Don't look beyond beginning of first subpart (before) or
3023 next part (after). */
3024 if (read
> max
) bufp
[read
-max
] = '\0';
3027 if (! strcmp (bufp
, boundary
)) {
3031 if (! found_boundary
&& ! strcmp (bufp
, boundary
)) {
3037 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3039 char *old_content
= content
;
3040 content
= concat (content
, bufp
, NULL
);
3044 ? concat ("\n", bufp
, NULL
)
3045 : concat (bufp
, NULL
);
3050 if (found_boundary
|| read
> max
) break;
3052 if (read
> max
) break;
3056 /* Skip the newline if that's all there is. */
3060 /* Remove trailing newline, except at EOF. */
3061 if ((before
|| ! feof (ct
->c_fp
)) &&
3062 (cp
= content
+ strlen (content
)) > content
&&
3067 if (strlen (content
) > 1) {
3069 m
->mp_content_before
= content
;
3071 m
->mp_content_after
= content
;
3086 ct_type_str (int type
) {
3088 case CT_APPLICATION
:
3089 return "application";
3105 return "unknown_type";
3111 ct_subtype_str (int type
, int subtype
) {
3113 case CT_APPLICATION
:
3115 case APPLICATION_OCTETS
:
3117 case APPLICATION_POSTSCRIPT
:
3118 return "postscript";
3120 return "unknown_app_subtype";
3124 case MESSAGE_RFC822
:
3126 case MESSAGE_PARTIAL
:
3128 case MESSAGE_EXTERNAL
:
3131 return "unknown_msg_subtype";
3137 case MULTI_ALTERNATE
:
3138 return "alternative";
3141 case MULTI_PARALLEL
:
3144 return "unknown_multipart_subtype";
3155 return "unknown_text_subtype";
3158 return "unknown_type";
3163 /* Find the content type and InitFunc for the CT. */
3164 const struct str2init
*
3165 get_ct_init (int type
) {
3166 const struct str2init
*sp
;
3168 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3169 if (type
== sp
->si_val
) {
3178 ce_str (int encoding
) {
3183 return "quoted-printable";
3199 /* Find the content type and InitFunc for the content encoding method. */
3200 const struct str2init
*
3201 get_ce_method (const char *method
) {
3202 struct str2init
*sp
;
3204 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3205 if (! strcasecmp (method
, sp
->si_key
)) {
3214 * Parse a series of MIME attributes (or parameters) given a header as
3217 * Arguments include:
3219 * filename - Name of input file (for error messages)
3220 * fieldname - Name of field being processed
3221 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3222 * Updated to point to end of attributes when finished.
3223 * param_head - Pointer to head of parameter list
3224 * param_tail - Pointer to tail of parameter list
3225 * commentp - Pointer to header comment pointer (may be NULL)
3227 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3228 * DONE to indicate a benign error (minor parsing error, but the program
3233 parse_header_attrs (const char *filename
, const char *fieldname
,
3234 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3237 char *cp
= *header_attrp
;
3243 struct sectlist
*next
;
3249 struct sectlist
*sechead
;
3250 struct parmlist
*next
;
3251 } *pp
, *pp2
, *phead
= NULL
;
3253 while (*cp
== ';') {
3254 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3255 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3258 while (isspace ((unsigned char) *cp
))
3262 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3268 "extraneous trailing ';' in message %s's %s: "
3270 filename
, fieldname
);
3274 /* down case the attribute name */
3275 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3276 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3277 *dp
= tolower ((unsigned char) *dp
);
3279 for (up
= dp
; isspace ((unsigned char) *dp
);)
3281 if (dp
== cp
|| *dp
!= '=') {
3283 "invalid parameter in message %s's %s: "
3284 "field\n%*sparameter %s (error detected at offset %d)",
3285 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3290 * To handle RFC 2231, we have to deal with the following extensions:
3292 * name*=encoded-value
3293 * name*<N>=part-N-of-a-parameter-value
3294 * name*<N>*=encoded-part-N-of-a-parameter-value
3297 * If there's a * right before the equal sign, it's encoded.
3298 * If there's a * and one or more digits, then it's section N.
3300 * Remember we can have one or the other, or both. cp points to
3301 * beginning of name, up points past the last character in the
3305 for (vp
= cp
; vp
< up
; vp
++) {
3306 if (*vp
== '*' && vp
< up
- 1) {
3309 } else if (*vp
== '*' && vp
== up
- 1) {
3311 } else if (partial
) {
3312 if (isdigit((unsigned char) *vp
))
3313 index
= *vp
- '0' + index
* 10;
3315 advise (NULL
, "invalid parameter index in message %s's "
3316 "%s: field\n%*s(parameter %s)", filename
,
3317 fieldname
, strlen(invo_name
) + 2, "", cp
);
3326 * Break out the parameter name and value sections and allocate
3330 nameptr
= mh_xmalloc(len
+ 1);
3331 strncpy(nameptr
, cp
, len
);
3332 nameptr
[len
] = '\0';
3334 for (dp
++; isspace ((unsigned char) *dp
);)
3339 * Single quotes delimit the character set and language tag.
3340 * They are required on the first section (or a complete
3345 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3351 charset
= mh_xmalloc(len
+ 1);
3352 strncpy(charset
, dp
, len
);
3353 charset
[len
] = '\0';
3359 advise(NULL
, "missing charset in message %s's %s: "
3360 "field\n%*s(parameter %s)", filename
, fieldname
,
3361 strlen(invo_name
) + 2, "", nameptr
);
3367 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3374 lang
= mh_xmalloc(len
+ 1);
3375 strncpy(lang
, dp
, len
);
3382 advise(NULL
, "missing language tag in message %s's %s: "
3383 "field\n%*s(parameter %s)", filename
, fieldname
,
3384 strlen(invo_name
) + 2, "", nameptr
);
3395 * At this point vp should be pointing at the beginning
3396 * of the encoded value/section. Continue until we reach
3397 * the end or get whitespace. But first, calculate the
3398 * length so we can allocate the correct buffer size.
3401 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3403 if (*(vp
+ 1) == '\0' ||
3404 !isxdigit((unsigned char) *(vp
+ 1)) ||
3405 *(vp
+ 2) == '\0' ||
3406 !isxdigit((unsigned char) *(vp
+ 2))) {
3407 advise(NULL
, "invalid encoded sequence in message "
3408 "%s's %s: field\n%*s(parameter %s)",
3409 filename
, fieldname
, strlen(invo_name
) + 2,
3423 up
= valptr
= mh_xmalloc(len
+ 1);
3425 for (vp
= dp
; istoken(*vp
); vp
++) {
3427 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3438 * A "normal" string. If it's got a leading quote, then we
3439 * strip the quotes out. Otherwise go until we reach the end
3440 * or get whitespace. Note we scan it twice; once to get the
3441 * length, then the second time copies it into the destination
3448 for (cp
= dp
+ 1;;) {
3453 "invalid quoted-string in message %s's %s: "
3454 "field\n%*s(parameter %s)",
3455 filename
, fieldname
, strlen(invo_name
) + 2, "",
3478 for (cp
= dp
; istoken (*cp
); cp
++) {
3483 valptr
= mh_xmalloc(len
+ 1);
3487 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3495 strncpy(valptr
, cp
= dp
, len
);
3503 * If 'partial' is set, we don't allocate a parameter now. We
3504 * put it on the parameter linked list to be reassembled later.
3506 * "phead" points to a list of all parameters we need to reassemble.
3507 * Each parameter has a list of sections. We insert the sections in
3512 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3513 if (strcasecmp(nameptr
, pp
->name
) == 0)
3518 pp
= mh_xmalloc(sizeof(*pp
));
3519 memset(pp
, 0, sizeof(*pp
));
3526 * Insert this into the section linked list
3529 sp
= mh_xmalloc(sizeof(*sp
));
3530 memset(sp
, 0, sizeof(*sp
));
3535 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3536 sp
->next
= pp
->sechead
;
3539 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3540 if (sp2
->index
== sp
->index
) {
3541 advise (NULL
, "duplicate index (%d) in message "
3542 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3543 filename
, fieldname
, strlen(invo_name
) + 2, "",
3547 if (sp2
->index
< sp
->index
&&
3548 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3549 sp
->next
= sp2
->next
;
3556 advise(NULL
, "Internal error: cannot insert partial "
3557 "param in message %s's %s: field\n%*s(parameter %s)",
3558 filename
, fieldname
, strlen(invo_name
) + 2, "",
3565 * Save our charset and lang tags.
3568 if (index
== 0 && encoded
) {
3571 pp
->charset
= charset
;
3577 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3578 pm
->pm_charset
= charset
;
3582 while (isspace ((unsigned char) *cp
))
3586 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3592 * Now that we're done, reassemble all of the partial parameters.
3595 for (pp
= phead
; pp
!= NULL
; ) {
3599 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3600 if (sp
->index
!= pindex
++) {
3601 advise(NULL
, "missing section %d for parameter in "
3602 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3603 filename
, fieldname
, strlen(invo_name
) + 2, "",
3610 p
= q
= mh_xmalloc(tlen
+ 1);
3611 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3612 memcpy(q
, sp
->value
, sp
->len
);
3622 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3623 pm
->pm_charset
= pp
->charset
;
3624 pm
->pm_lang
= pp
->lang
;
3635 * Return the charset for a particular content type. Return pointer is
3636 * only valid until the next call to content_charset().
3640 content_charset (CT ct
) {
3641 static char *ret_charset
= NULL
;
3643 if (ret_charset
!= NULL
) {
3647 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3649 return ret_charset
? ret_charset
: "US-ASCII";
3654 * Create a string based on a list of output parameters. Assume that this
3655 * parameter string will be appended to an existing header, so start out
3656 * with the separator (;). Perform RFC 2231 encoding when necessary.
3660 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3662 char *paramout
= NULL
;
3663 char line
[CPERLIN
* 2], *q
;
3664 int curlen
, index
, cont
, encode
, i
;
3665 size_t valoff
, numchars
;
3667 while (params
!= NULL
) {
3673 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3676 if (strlen(params
->pm_name
) > CPERLIN
) {
3677 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3683 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3686 * Loop until we get a parameter that fits within a line. We
3687 * assume new lines start with a tab, so check our overflow based
3697 * At this point we're definitely continuing the line, so
3698 * be sure to include the parameter name and section index.
3701 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3702 params
->pm_name
, index
);
3705 * Both of these functions do a NUL termination
3709 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3710 numchars
, valoff
, index
);
3712 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3723 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3728 * "line" starts with a ;\n\t, so that doesn't count against
3729 * the length. But add 8 since it starts with a tab; that's
3730 * how we end up with 5.
3733 initialwidth
= strlen(line
) + 5;
3736 * At this point the line should be built, so add it to our
3737 * current output buffer.
3740 paramout
= add(line
, paramout
);
3744 * If this won't fit on the line, start a new one. Save room in
3745 * case we need a semicolon on the end
3748 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3760 * At this point, we're either finishing a contined parameter, or
3761 * we're working on a new one.
3765 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3766 params
->pm_name
, index
);
3768 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3773 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3774 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3776 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3777 strlen(params
->pm_value
+ valoff
), valoff
);
3785 paramout
= add(line
, paramout
);
3786 initialwidth
+= strlen(line
);
3788 params
= params
->pm_next
;
3792 *offsetout
= initialwidth
;
3798 * Calculate the size of a parameter.
3802 * pm - The parameter being output
3803 * index - If continuing the parameter, the index of the section
3805 * valueoff - The current offset into the parameter value that we're
3806 * working on (previous sections have consumed valueoff bytes).
3807 * encode - Set if we should perform encoding on this parameter section
3808 * (given that we're consuming bytesfit bytes).
3809 * cont - Set if the remaining data in value will not fit on a single
3810 * line and will need to be continued.
3811 * bytesfit - The number of bytes that we can consume from the parameter
3812 * value and still fit on a completely new line. The
3813 * calculation assumes the new line starts with a tab,
3814 * includes the parameter name and any encoding, and fits
3815 * within CPERLIN bytes. Will always be at least 1.
3819 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3822 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3823 size_t len
= 0, fit
= 0;
3824 int fitlimit
= 0, eightbit
, maxfit
;
3829 * Add up the length. First, start with the parameter name.
3832 len
= strlen(pm
->pm_name
);
3835 * Scan the parameter value and see if we need to do encoding for this
3839 eightbit
= contains8bit(start
, NULL
);
3842 * Determine if we need to encode this section. Encoding is necessary if:
3844 * - There are any 8-bit characters at all and we're on the first
3846 * - There are 8-bit characters within N bytes of our section start.
3847 * N is calculated based on the number of bytes it would take to
3848 * reach CPERLIN. Specifically:
3849 * 8 (starting tab) +
3850 * strlen(param name) +
3851 * 4 ('* for section marker, '=', opening/closing '"')
3853 * is the number of bytes used by everything that isn't part of the
3854 * value. So that gets subtracted from CPERLIN.
3857 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3858 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3859 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3863 len
++; /* Add in equal sign */
3867 * We're using maxfit as a marker for how many characters we can
3868 * fit into the line. Bump it by two because we're not using quotes
3875 * If we don't have a charset or language tag in this parameter,
3879 if (! pm
->pm_charset
)
3880 pm
->pm_charset
= getcpy(write_charset_8bit());
3882 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
3884 len
++; /* For the encoding marker */
3887 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
3892 * We know we definitely need to include an index. maxfit already
3893 * includes the section marker.
3895 len
+= strlen(indexchar
);
3897 for (p
= start
; *p
!= '\0'; p
++) {
3898 if (isparamencode(*p
)) {
3906 * Just so there's no confusion: maxfit is counting OUTPUT
3907 * characters (post-encoding). fit is counting INPUT characters.
3909 if (! fitlimit
&& maxfit
>= 0)
3911 else if (! fitlimit
)
3916 * Calculate the string length, but add room for quoting \
3917 * and " if necessary. Also account for quotes at beginning
3920 for (p
= start
; *p
!= '\0'; p
++) {
3931 if (! fitlimit
&& maxfit
>= 0)
3933 else if (! fitlimit
)
3950 * Output an encoded parameter string.
3954 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
3955 size_t valueoff
, int index
)
3957 size_t outlen
= 0, n
;
3958 char *endptr
= output
+ len
, *p
;
3961 * First, output the marker for an encoded string.
3969 * If the index is 0, output the character set and language tag.
3970 * If theses were NULL, they should have already been filled in
3975 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
3979 if (output
> endptr
) {
3980 advise(NULL
, "Internal error: parameter buffer overflow");
3986 * Copy over the value, encoding if necessary
3989 p
= pm
->pm_value
+ valueoff
;
3990 while (valuelen
-- > 0) {
3991 if (isparamencode(*p
)) {
3992 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
3999 if (output
> endptr
) {
4000 advise(NULL
, "Internal error: parameter buffer overflow");
4011 * Output a "normal" parameter, without encoding. Be sure to escape
4012 * quotes and backslashes if necessary.
4016 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4020 char *endptr
= output
+ len
, *p
;
4026 p
= pm
->pm_value
+ valueoff
;
4028 while (valuelen
-- > 0) {
4038 if (output
> endptr
) {
4039 advise(NULL
, "Internal error: parameter buffer overflow");
4044 if (output
- 2 > endptr
) {
4045 advise(NULL
, "Internal error: parameter buffer overflow");
4056 * Add a parameter to the parameter linked list
4060 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4062 PM pm
= mh_xmalloc(sizeof(*pm
));
4064 memset(pm
, 0, sizeof(*pm
));
4066 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4067 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4070 (*last
)->pm_next
= pm
;
4081 * Either replace a current parameter with a new value, or add the parameter
4082 * to the parameter linked list.
4086 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4090 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4091 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4093 * If nocopy is set, it's assumed that we own both name
4094 * and value. We don't need name, so we discard it now.
4099 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4104 return add_param(first
, last
, name
, value
, nocopy
);
4108 * Retrieve a parameter value from a parameter linked list. If the parameter
4109 * value needs converted to the local character set, do that now.
4113 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4115 while (first
!= NULL
) {
4116 if (strcasecmp(name
, first
->pm_name
) == 0) {
4118 return first
->pm_value
;
4120 return getcpy(get_param_value(first
, replace
));
4122 first
= first
->pm_next
;
4129 * Return a parameter value, converting to the local character set if
4133 char *get_param_value(PM pm
, char replace
)
4135 static char buffer
[4096]; /* I hope no parameters are larger */
4136 size_t bufsize
= sizeof(buffer
);
4141 ICONV_CONST
char *p
;
4142 #else /* HAVE_ICONV */
4144 #endif /* HAVE_ICONV */
4149 * If we don't have a character set indicated, it's assumed to be
4150 * US-ASCII. If it matches our character set, we don't need to convert
4154 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4155 strlen(pm
->pm_charset
))) {
4156 return pm
->pm_value
;
4160 * In this case, we need to convert. If we have iconv support, use
4161 * that. Otherwise, go through and simply replace every non-ASCII
4162 * character with the substitution character.
4167 bufsize
= sizeof(buffer
);
4168 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4170 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4171 if (cd
== (iconv_t
) -1) {
4175 inbytes
= strlen(pm
->pm_value
);
4179 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4180 if (errno
!= EILSEQ
) {
4185 * Reset shift state, substitute our character,
4186 * try to restart conversion.
4189 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4202 for (++p
, --inbytes
;
4203 inbytes
> 0 && (((unsigned char) *q
) & 0xc0) == 0x80;
4222 #endif /* HAVE_ICONV */
4225 * Take everything non-ASCII and substituite the replacement character
4229 bufsize
= sizeof(buffer
);
4230 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4231 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))