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 */
26 extern pid_t xpid
; /* mhshowsbr.c */
29 extern int rcachesw
; /* mhcachesbr.c */
30 extern int wcachesw
; /* mhcachesbr.c */
32 int checksw
= 0; /* check Content-MD5 field */
35 * These are for mhfixmsg to:
36 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
38 * 2) Suppress the warning about bogus multipart content, and report it.
40 int skip_mp_cte_check
;
41 int suppress_bogus_mp_content_warning
;
45 * Structures for TEXT messages
47 struct k2v SubText
[] = {
48 { "plain", TEXT_PLAIN
},
49 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
50 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
51 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
54 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
57 * Structures for MULTIPART messages
59 struct k2v SubMultiPart
[] = {
60 { "mixed", MULTI_MIXED
},
61 { "alternative", MULTI_ALTERNATE
},
62 { "digest", MULTI_DIGEST
},
63 { "parallel", MULTI_PARALLEL
},
64 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
68 * Structures for MESSAGE messages
70 struct k2v SubMessage
[] = {
71 { "rfc822", MESSAGE_RFC822
},
72 { "partial", MESSAGE_PARTIAL
},
73 { "external-body", MESSAGE_EXTERNAL
},
74 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
78 * Structure for APPLICATION messages
80 struct k2v SubApplication
[] = {
81 { "octet-stream", APPLICATION_OCTETS
},
82 { "postscript", APPLICATION_POSTSCRIPT
},
83 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
87 * Mapping of names of CTE types in mhbuild directives
89 static struct k2v EncodingType
[] = {
93 { "quoted-printable", CE_QUOTED
},
95 { "base64", CE_BASE64
},
101 int find_cache (CT
, int, int *, char *, char *, int);
104 int part_ok (CT
, int);
105 int type_ok (CT
, int);
106 void content_error (char *, CT
, char *, ...);
109 void free_encoding (CT
, int);
114 static CT
get_content (FILE *, char *, int);
115 static int get_comment (const char *, const char *, char **, char **);
117 static int InitGeneric (CT
);
118 static int InitText (CT
);
119 static int InitMultiPart (CT
);
120 void reverse_parts (CT
);
121 static int InitMessage (CT
);
122 static int InitApplication (CT
);
123 static int init_encoding (CT
, OpenCEFunc
);
124 static unsigned long size_encoding (CT
);
125 static int InitBase64 (CT
);
126 static int openBase64 (CT
, char **);
127 static int InitQuoted (CT
);
128 static int openQuoted (CT
, char **);
129 static int Init7Bit (CT
);
130 static int openExternal (CT
, CT
, CE
, char **, int *);
131 static int InitFile (CT
);
132 static int openFile (CT
, char **);
133 static int InitFTP (CT
);
134 static int openFTP (CT
, char **);
135 static int InitMail (CT
);
136 static int openMail (CT
, char **);
137 static int readDigest (CT
, char *);
138 static int get_leftover_mp_content (CT
, int);
139 static int InitURL (CT
);
140 static int openURL (CT
, char **);
141 static int parse_header_attrs (const char *, const char *, char **, PM
*,
143 static size_t param_len(PM
, int, size_t, int *, int *, size_t *);
144 static size_t encode_param(PM
, char *, size_t, size_t, size_t, int);
145 static size_t normal_param(PM
, char *, size_t, size_t, size_t);
146 static int get_dispo (char *, CT
, int);
148 struct str2init str2cts
[] = {
149 { "application", CT_APPLICATION
, InitApplication
},
150 { "audio", CT_AUDIO
, InitGeneric
},
151 { "image", CT_IMAGE
, InitGeneric
},
152 { "message", CT_MESSAGE
, InitMessage
},
153 { "multipart", CT_MULTIPART
, InitMultiPart
},
154 { "text", CT_TEXT
, InitText
},
155 { "video", CT_VIDEO
, InitGeneric
},
156 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
157 { NULL
, CT_UNKNOWN
, NULL
},
160 struct str2init str2ces
[] = {
161 { "base64", CE_BASE64
, InitBase64
},
162 { "quoted-printable", CE_QUOTED
, InitQuoted
},
163 { "8bit", CE_8BIT
, Init7Bit
},
164 { "7bit", CE_7BIT
, Init7Bit
},
165 { "binary", CE_BINARY
, Init7Bit
},
166 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
167 { NULL
, CE_UNKNOWN
, NULL
},
171 * NOTE WELL: si_key MUST NOT have value of NOTOK
173 * si_key is 1 if access method is anonymous.
175 struct str2init str2methods
[] = {
176 { "afs", 1, InitFile
},
177 { "anon-ftp", 1, InitFTP
},
178 { "ftp", 0, InitFTP
},
179 { "local-file", 0, InitFile
},
180 { "mail-server", 0, InitMail
},
181 { "url", 0, InitURL
},
187 pidcheck (int status
)
189 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
200 * Main entry point for parsing a MIME message or file.
201 * It returns the Content structure for the top level
202 * entity in the file.
206 parse_mime (char *file
)
214 * Check if file is actually standard input
216 if ((is_stdin
= !(strcmp (file
, "-")))) {
217 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
219 advise("mhparse", "unable to create temporary file in %s",
223 file
= add (tfile
, NULL
);
225 while (fgets (buffer
, sizeof(buffer
), stdin
))
229 if (ferror (stdin
)) {
230 (void) m_unlink (file
);
231 advise ("stdin", "error reading");
235 (void) m_unlink (file
);
236 advise (file
, "error writing");
239 fseek (fp
, 0L, SEEK_SET
);
240 } else if ((fp
= fopen (file
, "r")) == NULL
) {
241 advise (file
, "unable to read");
245 if (!(ct
= get_content (fp
, file
, 1))) {
247 (void) m_unlink (file
);
248 advise (NULL
, "unable to decode %s", file
);
253 ct
->c_unlink
= 1; /* temp file to remove */
257 if (ct
->c_end
== 0L) {
258 fseek (fp
, 0L, SEEK_END
);
259 ct
->c_end
= ftell (fp
);
262 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
274 * Main routine for reading/parsing the headers
275 * of a message content.
277 * toplevel = 1 # we are at the top level of the message
278 * toplevel = 0 # we are inside message type or multipart type
279 * # other than multipart/digest
280 * toplevel = -1 # we are inside multipart/digest
281 * NB: on failure we will fclose(in)!
285 get_content (FILE *in
, char *file
, int toplevel
)
288 char buf
[BUFSIZ
], name
[NAMESZ
];
292 m_getfld_state_t gstate
= 0;
294 /* allocate the content structure */
295 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
296 adios (NULL
, "out of memory");
299 ct
->c_file
= add (file
, NULL
);
300 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
303 * Parse the header fields for this
304 * content into a linked list.
306 m_getfld_track_filepos (&gstate
, in
);
307 for (compnum
= 1;;) {
308 int bufsz
= sizeof buf
;
309 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
314 /* get copies of the buffers */
315 np
= add (name
, NULL
);
316 vp
= add (buf
, NULL
);
318 /* if necessary, get rest of field */
319 while (state
== FLDPLUS
) {
321 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
322 vp
= add (buf
, vp
); /* add to previous value */
325 /* Now add the header data to the list */
326 add_header (ct
, np
, vp
);
328 /* continue, to see if this isn't the last header field */
329 ct
->c_begin
= ftell (in
) + 1;
333 ct
->c_begin
= ftell (in
) - strlen (buf
);
337 ct
->c_begin
= ftell (in
);
342 adios (NULL
, "message format error in component #%d", compnum
);
345 adios (NULL
, "getfld() returned %d", state
);
348 /* break out of the loop */
351 m_getfld_state_destroy (&gstate
);
354 * Read the content headers. We will parse the
355 * MIME related header fields into their various
356 * structures and set internal flags related to
357 * content type/subtype, etc.
360 hp
= ct
->c_first_hf
; /* start at first header field */
362 /* Get MIME-Version field */
363 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
368 advise (NULL
, "message %s has multiple %s: fields",
369 ct
->c_file
, VRSN_FIELD
);
372 ct
->c_vrsn
= add (hp
->value
, NULL
);
374 /* Now, cleanup this field */
377 while (isspace ((unsigned char) *cp
))
379 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
381 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
382 if (!isspace ((unsigned char) *dp
))
386 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
389 get_comment (ct
->c_file
, VRSN_FIELD
, &cp
, NULL
) == NOTOK
)
392 for (dp
= cp
; istoken (*dp
); dp
++)
396 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
399 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
400 ct
->c_file
, VRSN_FIELD
, cp
);
403 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
404 /* Get Content-Type field */
405 struct str2init
*s2i
;
406 CI ci
= &ct
->c_ctinfo
;
408 /* Check if we've already seen a Content-Type header */
410 advise (NULL
, "message %s has multiple %s: fields",
411 ct
->c_file
, TYPE_FIELD
);
415 /* Parse the Content-Type field */
416 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
420 * Set the Init function and the internal
421 * flag for this content type.
423 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
424 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
426 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
428 ct
->c_type
= s2i
->si_val
;
429 ct
->c_ctinitfnx
= s2i
->si_init
;
431 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
432 /* Get Content-Transfer-Encoding field */
434 struct str2init
*s2i
;
437 * Check if we've already seen the
438 * Content-Transfer-Encoding field
441 advise (NULL
, "message %s has multiple %s: fields",
442 ct
->c_file
, ENCODING_FIELD
);
446 /* get copy of this field */
447 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
449 while (isspace ((unsigned char) *cp
))
451 for (dp
= cp
; istoken (*dp
); dp
++)
457 * Find the internal flag and Init function
458 * for this transfer encoding.
460 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
461 if (!strcasecmp (cp
, s2i
->si_key
))
463 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
466 ct
->c_encoding
= s2i
->si_val
;
468 /* Call the Init function for this encoding */
469 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
472 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
473 /* Get Content-MD5 field */
479 if (ct
->c_digested
) {
480 advise (NULL
, "message %s has multiple %s: fields",
481 ct
->c_file
, MD5_FIELD
);
485 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
487 while (isspace ((unsigned char) *cp
))
489 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
491 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
492 if (!isspace ((unsigned char) *dp
))
496 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
499 get_comment (ct
->c_file
, MD5_FIELD
, &cp
, NULL
) == NOTOK
) {
504 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
512 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
513 /* Get Content-ID field */
514 ct
->c_id
= add (hp
->value
, ct
->c_id
);
516 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
517 /* Get Content-Description field */
518 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
520 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
521 /* Get Content-Disposition field */
522 if (get_dispo(hp
->value
, ct
, 0) == NOTOK
)
527 hp
= hp
->next
; /* next header field */
531 * Check if we saw a Content-Type field.
532 * If not, then assign a default value for
533 * it, and the Init function.
537 * If we are inside a multipart/digest message,
538 * so default type is message/rfc822
541 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
543 ct
->c_type
= CT_MESSAGE
;
544 ct
->c_ctinitfnx
= InitMessage
;
547 * Else default type is text/plain
549 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
551 ct
->c_type
= CT_TEXT
;
552 ct
->c_ctinitfnx
= InitText
;
556 /* Use default Transfer-Encoding, if necessary */
558 ct
->c_encoding
= CE_7BIT
;
571 * small routine to add header field to list
575 add_header (CT ct
, char *name
, char *value
)
579 /* allocate header field structure */
580 hp
= mh_xmalloc (sizeof(*hp
));
582 /* link data into header structure */
587 /* link header structure into the list */
588 if (ct
->c_first_hf
== NULL
) {
589 ct
->c_first_hf
= hp
; /* this is the first */
592 ct
->c_last_hf
->next
= hp
; /* add it to the end */
601 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
602 * directives. Fills in the information of the CTinfo structure.
605 get_ctinfo (char *cp
, CT ct
, int magic
)
614 /* store copy of Content-Type line */
615 cp
= ct
->c_ctline
= add (cp
, NULL
);
617 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
620 /* change newlines to spaces */
621 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
624 /* trim trailing spaces */
625 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
626 if (!isspace ((unsigned char) *dp
))
631 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
633 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
634 &ci
->ci_comment
) == NOTOK
)
637 for (dp
= cp
; istoken (*dp
); dp
++)
640 ci
->ci_type
= add (cp
, NULL
); /* store content type */
644 advise (NULL
, "invalid %s: field in message %s (empty type)",
645 TYPE_FIELD
, ct
->c_file
);
649 /* down case the content type string */
650 for (dp
= ci
->ci_type
; *dp
; dp
++)
651 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
652 *dp
= tolower ((unsigned char) *dp
);
654 while (isspace ((unsigned char) *cp
))
657 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
658 &ci
->ci_comment
) == NOTOK
)
663 ci
->ci_subtype
= add ("", NULL
);
668 while (isspace ((unsigned char) *cp
))
671 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
672 &ci
->ci_comment
) == NOTOK
)
675 for (dp
= cp
; istoken (*dp
); dp
++)
678 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
681 if (!*ci
->ci_subtype
) {
683 "invalid %s: field in message %s (empty subtype for \"%s\")",
684 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
688 /* down case the content subtype string */
689 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
690 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
691 *dp
= tolower ((unsigned char) *dp
);
694 while (isspace ((unsigned char) *cp
))
697 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
698 &ci
->ci_comment
) == NOTOK
)
701 if ((status
= parse_header_attrs (ct
->c_file
, TYPE_FIELD
, &cp
,
702 &ci
->ci_first_pm
, &ci
->ci_last_pm
,
703 &ci
->ci_comment
)) != OK
) {
704 return status
== NOTOK
? NOTOK
: OK
;
708 * Get any <Content-Id> given in buffer
710 if (magic
&& *cp
== '<') {
715 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
716 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
722 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
728 while (isspace ((unsigned char) *cp
))
733 * Get any [Content-Description] given in buffer.
735 if (magic
&& *cp
== '[') {
737 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
741 advise (NULL
, "invalid description in message %s", ct
->c_file
);
749 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
755 while (isspace ((unsigned char) *cp
))
760 * Get any {Content-Disposition} given in buffer.
762 if (magic
&& *cp
== '{') {
764 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
768 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
776 if (get_dispo(cp
, ct
, 1) != OK
)
782 while (isspace ((unsigned char) *cp
))
787 * Get any extension directives (right now just the content transfer
788 * encoding, but maybe others) that we care about.
791 if (magic
&& *cp
== '*') {
793 * See if it's a CTE we match on
798 while (*cp
!= '\0' && ! isspace((unsigned char) *cp
))
802 advise (NULL
, "invalid null transfer encoding specification");
809 ct
->c_reqencoding
= CE_UNKNOWN
;
811 for (kv
= EncodingType
; kv
->kv_key
; kv
++) {
812 if (strcasecmp(kv
->kv_key
, dp
) == 0) {
813 ct
->c_reqencoding
= kv
->kv_value
;
818 if (ct
->c_reqencoding
== CE_UNKNOWN
) {
819 advise (NULL
, "invalid CTE specification: \"%s\"", dp
);
823 while (isspace ((unsigned char) *cp
))
828 * Check if anything is left over
832 ci
->ci_magic
= add (cp
, NULL
);
834 /* If there is a Content-Disposition header and it doesn't
835 have a *filename=, extract it from the magic contents.
836 The r1bindex call skips any leading directory
838 if (ct
->c_dispo_type
&&
839 !get_param(ct
->c_dispo_first
, "filename", '_', 1)) {
840 add_param(&ct
->c_dispo_first
, &ct
->c_dispo_last
, "filename",
841 r1bindex(ci
->ci_magic
, '/'), 0);
846 "extraneous information in message %s's %s: field\n%*s(%s)",
847 ct
->c_file
, TYPE_FIELD
, strlen(invo_name
) + 2, "", cp
);
855 * Parse out a Content-Disposition header. A lot of this is cribbed from
859 get_dispo (char *cp
, CT ct
, int buildflag
)
861 char *dp
, *dispoheader
;
866 * Save the whole copy of the Content-Disposition header, unless we're
867 * processing a mhbuild directive. A NULL c_dispo will be a flag to
868 * mhbuild that the disposition header needs to be generated at that
872 dispoheader
= cp
= add(cp
, NULL
);
874 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
877 /* change newlines to spaces */
878 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
881 /* trim trailing spaces */
882 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
883 if (!isspace ((unsigned char) *dp
))
888 fprintf (stderr
, "%s: %s\n", DISPO_FIELD
, cp
);
890 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) ==
896 for (dp
= cp
; istoken (*dp
); dp
++)
899 ct
->c_dispo_type
= add (cp
, NULL
); /* store disposition type */
902 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) == NOTOK
)
905 if ((status
= parse_header_attrs (ct
->c_file
, DISPO_FIELD
, &cp
,
906 &ct
->c_dispo_first
, &ct
->c_dispo_last
,
908 if (status
== NOTOK
) {
914 "extraneous information in message %s's %s: field\n%*s(%s)",
915 ct
->c_file
, DISPO_FIELD
, strlen(invo_name
) + 2, "", cp
);
921 ct
->c_dispo
= dispoheader
;
928 get_comment (const char *filename
, const char *fieldname
, char **ap
,
933 char c
, buffer
[BUFSIZ
], *dp
;
943 advise (NULL
, "invalid comment in message %s's %s: field",
944 filename
, fieldname
);
949 if ((c
= *cp
++) == '\0')
972 if ((dp
= *commentp
)) {
973 *commentp
= concat (dp
, " ", buffer
, NULL
);
976 *commentp
= add (buffer
, NULL
);
980 while (isspace ((unsigned char) *cp
))
991 * Handles content types audio, image, and video.
992 * There's not much to do right here.
1000 return OK
; /* not much to do here */
1011 char buffer
[BUFSIZ
];
1017 CI ci
= &ct
->c_ctinfo
;
1019 /* check for missing subtype */
1020 if (!*ci
->ci_subtype
)
1021 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1024 for (kv
= SubText
; kv
->kv_key
; kv
++)
1025 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1027 ct
->c_subtype
= kv
->kv_value
;
1029 /* allocate text character set structure */
1030 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1031 adios (NULL
, "out of memory");
1032 ct
->c_ctparams
= (void *) t
;
1034 /* scan for charset parameter */
1035 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
)
1036 if (!strcasecmp (pm
->pm_name
, "charset"))
1039 /* check if content specified a character set */
1041 chset
= pm
->pm_value
;
1042 t
->tx_charset
= CHARSET_SPECIFIED
;
1044 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1048 * If we can not handle character set natively,
1049 * then check profile for string to modify the
1050 * terminal or display method.
1052 * termproc is for mhshow, though mhlist -debug prints it, too.
1054 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1055 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1056 if ((cp
= context_find (buffer
)))
1057 ct
->c_termproc
= getcpy (cp
);
1069 InitMultiPart (CT ct
)
1075 char *bp
, buffer
[BUFSIZ
];
1076 struct multipart
*m
;
1078 struct part
*part
, **next
;
1079 CI ci
= &ct
->c_ctinfo
;
1084 * The encoding for multipart messages must be either
1085 * 7bit, 8bit, or binary (per RFC2045).
1087 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1088 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1089 /* Copy the Content-Transfer-Encoding header field body so we can
1090 remove any trailing whitespace and leading blanks from it. */
1091 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1093 bp
= cte
+ strlen (cte
) - 1;
1094 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1095 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1098 "\"%s/%s\" type in message %s must be encoded in\n"
1099 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1100 "is to\nmanually edit the file and change the \"%s\"\n"
1101 "Content-Transfer-Encoding to one of those. For now",
1102 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1109 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1110 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1112 ct
->c_subtype
= kv
->kv_value
;
1115 * Check for "boundary" parameter, which is
1116 * required for multipart messages.
1119 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1120 if (!strcasecmp (pm
->pm_name
, "boundary")) {
1126 /* complain if boundary parameter is missing */
1129 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1130 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1134 /* allocate primary structure for multipart info */
1135 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1136 adios (NULL
, "out of memory");
1137 ct
->c_ctparams
= (void *) m
;
1139 /* check if boundary parameter contains only whitespace characters */
1140 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1143 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1144 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1148 /* remove trailing whitespace from boundary parameter */
1149 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1150 if (!isspace ((unsigned char) *dp
))
1154 /* record boundary separators */
1155 m
->mp_start
= concat (bp
, "\n", NULL
);
1156 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1158 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1159 advise (ct
->c_file
, "unable to open for reading");
1163 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1165 next
= &m
->mp_parts
;
1169 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1173 pos
+= strlen (buffer
);
1174 if (buffer
[0] != '-' || buffer
[1] != '-')
1177 if (strcmp (buffer
+ 2, m
->mp_start
))
1180 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1181 adios (NULL
, "out of memory");
1183 next
= &part
->mp_next
;
1185 if (!(p
= get_content (fp
, ct
->c_file
,
1186 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1193 fseek (fp
, pos
, SEEK_SET
);
1196 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1200 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1201 if (p
->c_end
< p
->c_begin
)
1202 p
->c_begin
= p
->c_end
;
1207 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1213 if (! suppress_bogus_mp_content_warning
) {
1214 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1216 bogus_mp_content
= 1;
1218 if (!inout
&& part
) {
1220 p
->c_end
= ct
->c_end
;
1222 if (p
->c_begin
>= p
->c_end
) {
1223 for (next
= &m
->mp_parts
; *next
!= part
;
1224 next
= &((*next
)->mp_next
))
1228 free ((char *) part
);
1233 /* reverse the order of the parts for multipart/alternative */
1234 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1238 * label all subparts with part number, and
1239 * then initialize the content of the subpart.
1244 char partnam
[BUFSIZ
];
1247 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1248 pp
= partnam
+ strlen (partnam
);
1253 for (part
= m
->mp_parts
, partnum
= 1; part
;
1254 part
= part
->mp_next
, partnum
++) {
1257 sprintf (pp
, "%d", partnum
);
1258 p
->c_partno
= add (partnam
, NULL
);
1260 /* initialize the content of the subparts */
1261 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1269 get_leftover_mp_content (ct
, 1);
1270 get_leftover_mp_content (ct
, 0);
1279 * reverse the order of the parts of a multipart/alternative
1283 reverse_parts (CT ct
)
1285 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1289 /* Reverse the order of its parts by walking the mp_parts list
1290 and pushing each node to the front. */
1291 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1292 next
= part
->mp_next
;
1293 part
->mp_next
= m
->mp_parts
;
1307 CI ci
= &ct
->c_ctinfo
;
1309 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1311 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1312 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1316 /* check for missing subtype */
1317 if (!*ci
->ci_subtype
)
1318 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1321 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1322 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1324 ct
->c_subtype
= kv
->kv_value
;
1326 switch (ct
->c_subtype
) {
1327 case MESSAGE_RFC822
:
1330 case MESSAGE_PARTIAL
:
1335 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1336 adios (NULL
, "out of memory");
1337 ct
->c_ctparams
= (void *) p
;
1339 /* scan for parameters "id", "number", and "total" */
1340 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1341 if (!strcasecmp (pm
->pm_name
, "id")) {
1342 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1345 if (!strcasecmp (pm
->pm_name
, "number")) {
1346 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1347 || p
->pm_partno
< 1) {
1350 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1351 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1352 ct
->c_file
, TYPE_FIELD
);
1357 if (!strcasecmp (pm
->pm_name
, "total")) {
1358 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1367 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1369 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1370 ci
->ci_type
, ci
->ci_subtype
,
1371 ct
->c_file
, TYPE_FIELD
);
1377 case MESSAGE_EXTERNAL
:
1384 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1385 adios (NULL
, "out of memory");
1386 ct
->c_ctparams
= (void *) e
;
1389 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1390 advise (ct
->c_file
, "unable to open for reading");
1394 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1396 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1404 p
->c_ceopenfnx
= NULL
;
1405 if ((exresult
= params_external (ct
, 0)) != NOTOK
1406 && p
->c_ceopenfnx
== openMail
) {
1410 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1412 content_error (NULL
, ct
,
1413 "empty body for access-type=mail-server");
1417 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1418 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1420 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1422 adios ("failed", "fread");
1425 adios (NULL
, "unexpected EOF from fread");
1428 bp
+= cc
, size
-= cc
;
1435 p
->c_end
= p
->c_begin
;
1440 if (exresult
== NOTOK
)
1442 if (e
->eb_flags
== NOTOK
)
1445 switch (p
->c_type
) {
1450 if (p
->c_subtype
!= MESSAGE_RFC822
)
1454 e
->eb_partno
= ct
->c_partno
;
1456 (*p
->c_ctinitfnx
) (p
);
1471 params_external (CT ct
, int composing
)
1474 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1475 CI ci
= &ct
->c_ctinfo
;
1477 ct
->c_ceopenfnx
= NULL
;
1478 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1479 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1480 struct str2init
*s2i
;
1481 CT p
= e
->eb_content
;
1483 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1484 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1487 e
->eb_access
= pm
->pm_value
;
1488 e
->eb_flags
= NOTOK
;
1489 p
->c_encoding
= CE_EXTERNAL
;
1492 e
->eb_access
= s2i
->si_key
;
1493 e
->eb_flags
= s2i
->si_val
;
1494 p
->c_encoding
= CE_EXTERNAL
;
1496 /* Call the Init function for this external type */
1497 if ((*s2i
->si_init
)(p
) == NOTOK
)
1501 if (!strcasecmp (pm
->pm_name
, "name")) {
1502 e
->eb_name
= pm
->pm_value
;
1505 if (!strcasecmp (pm
->pm_name
, "permission")) {
1506 e
->eb_permission
= pm
->pm_value
;
1509 if (!strcasecmp (pm
->pm_name
, "site")) {
1510 e
->eb_site
= pm
->pm_value
;
1513 if (!strcasecmp (pm
->pm_name
, "directory")) {
1514 e
->eb_dir
= pm
->pm_value
;
1517 if (!strcasecmp (pm
->pm_name
, "mode")) {
1518 e
->eb_mode
= pm
->pm_value
;
1521 if (!strcasecmp (pm
->pm_name
, "size")) {
1522 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1525 if (!strcasecmp (pm
->pm_name
, "server")) {
1526 e
->eb_server
= pm
->pm_value
;
1529 if (!strcasecmp (pm
->pm_name
, "subject")) {
1530 e
->eb_subject
= pm
->pm_value
;
1533 if (!strcasecmp (pm
->pm_name
, "url")) {
1535 * According to RFC 2017, we have to remove all whitespace from
1539 char *u
, *p
= pm
->pm_value
;
1540 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1542 for (; *p
!= '\0'; p
++) {
1543 if (! isspace((unsigned char) *p
))
1550 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1551 e
->eb_body
= getcpy (pm
->pm_value
);
1556 if (!e
->eb_access
) {
1558 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1559 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1572 InitApplication (CT ct
)
1575 CI ci
= &ct
->c_ctinfo
;
1578 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1579 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1581 ct
->c_subtype
= kv
->kv_value
;
1588 * TRANSFER ENCODINGS
1592 init_encoding (CT ct
, OpenCEFunc openfnx
)
1594 ct
->c_ceopenfnx
= openfnx
;
1595 ct
->c_ceclosefnx
= close_encoding
;
1596 ct
->c_cesizefnx
= size_encoding
;
1603 close_encoding (CT ct
)
1605 CE ce
= &ct
->c_cefile
;
1614 static unsigned long
1615 size_encoding (CT ct
)
1620 CE ce
= &ct
->c_cefile
;
1623 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1624 return (long) st
.st_size
;
1627 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1628 return (long) st
.st_size
;
1633 if (ct
->c_encoding
== CE_EXTERNAL
)
1634 return (ct
->c_end
- ct
->c_begin
);
1637 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1638 return (ct
->c_end
- ct
->c_begin
);
1640 if (fstat (fd
, &st
) != NOTOK
)
1641 size
= (long) st
.st_size
;
1645 (*ct
->c_ceclosefnx
) (ct
);
1654 static unsigned char b642nib
[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1659 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1660 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1661 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1662 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1663 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1664 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1665 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1666 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1667 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1668 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1669 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1670 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1677 return init_encoding (ct
, openBase64
);
1682 openBase64 (CT ct
, char **file
)
1684 int bitno
, cc
, digested
;
1685 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1687 unsigned char value
, b
;
1688 char *cp
, *ep
, buffer
[BUFSIZ
];
1689 /* sbeck -- handle suffixes */
1691 CE ce
= &ct
->c_cefile
;
1695 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1700 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1701 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1707 if (*file
== NULL
) {
1710 ce
->ce_file
= add (*file
, NULL
);
1714 /* sbeck@cise.ufl.edu -- handle suffixes */
1716 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1717 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1718 cp
= context_find (buffer
);
1719 if (cp
== NULL
|| *cp
== '\0') {
1720 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1722 cp
= context_find (buffer
);
1724 if (cp
!= NULL
&& *cp
!= '\0') {
1725 if (ce
->ce_unlink
) {
1726 /* Create temporary file with filename extension. */
1727 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1728 adios(NULL
, "unable to create temporary file in %s",
1732 ce
->ce_file
= add (cp
, ce
->ce_file
);
1734 } else if (*file
== NULL
) {
1736 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1737 adios(NULL
, "unable to create temporary file in %s",
1740 ce
->ce_file
= add (tempfile
, NULL
);
1743 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1744 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1748 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1749 adios (NULL
, "internal error(1)");
1752 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1753 content_error (ct
->c_file
, ct
, "unable to open for reading");
1759 if ((digested
= ct
->c_digested
))
1760 MD5Init (&mdContext
);
1766 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1768 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1770 content_error (ct
->c_file
, ct
, "error reading from");
1774 content_error (NULL
, ct
, "premature eof");
1782 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1785 if (isspace ((unsigned char) *cp
))
1787 if (skip
|| (((unsigned char) *cp
) & 0x80)
1788 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1790 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1791 (unsigned char) *cp
,
1792 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1795 content_error (NULL
, ct
,
1796 "invalid BASE64 encoding -- continuing");
1800 bits
|= value
<< bitno
;
1802 if ((bitno
-= 6) < 0) {
1803 b
= (bits
>> 16) & 0xff;
1804 if (!text
|| b
!= '\r')
1805 putc ((char) b
, ce
->ce_fp
);
1807 MD5Update (&mdContext
, &b
, 1);
1809 b
= (bits
>> 8) & 0xff;
1810 if (! text
|| b
!= '\r')
1811 putc ((char) b
, ce
->ce_fp
);
1813 MD5Update (&mdContext
, &b
, 1);
1816 if (! text
|| b
!= '\r')
1817 putc ((char) b
, ce
->ce_fp
);
1819 MD5Update (&mdContext
, &b
, 1);
1823 if (ferror (ce
->ce_fp
)) {
1824 content_error (ce
->ce_file
, ct
,
1825 "error writing to");
1828 bitno
= 18, bits
= 0L, skip
= 0;
1834 goto self_delimiting
;
1843 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1845 content_error (NULL
, ct
, "invalid BASE64 encoding");
1850 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1852 if (fflush (ce
->ce_fp
)) {
1853 content_error (ce
->ce_file
, ct
, "error writing to");
1858 unsigned char digest
[16];
1860 MD5Final (digest
, &mdContext
);
1861 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1862 sizeof(digest
) / sizeof(digest
[0])))
1863 content_error (NULL
, ct
,
1864 "content integrity suspect (digest mismatch) -- continuing");
1867 fprintf (stderr
, "content integrity confirmed\n");
1870 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1873 *file
= ce
->ce_file
;
1878 return fileno (ce
->ce_fp
);
1885 free_encoding (ct
, 0);
1894 static char hex2nib
[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1900 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1901 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1902 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1903 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1907 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1917 return init_encoding (ct
, openQuoted
);
1922 openQuoted (CT ct
, char **file
)
1924 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1926 char buffer
[BUFSIZ
];
1928 CE ce
= &ct
->c_cefile
;
1929 /* sbeck -- handle suffixes */
1934 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1939 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1940 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1946 if (*file
== NULL
) {
1949 ce
->ce_file
= add (*file
, NULL
);
1953 /* sbeck@cise.ufl.edu -- handle suffixes */
1955 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1956 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1957 cp
= context_find (buffer
);
1958 if (cp
== NULL
|| *cp
== '\0') {
1959 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1961 cp
= context_find (buffer
);
1963 if (cp
!= NULL
&& *cp
!= '\0') {
1964 if (ce
->ce_unlink
) {
1965 /* Create temporary file with filename extension. */
1966 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1967 adios(NULL
, "unable to create temporary file in %s",
1971 ce
->ce_file
= add (cp
, ce
->ce_file
);
1973 } else if (*file
== NULL
) {
1975 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1976 adios(NULL
, "unable to create temporary file in %s",
1979 ce
->ce_file
= add (tempfile
, NULL
);
1982 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1983 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1987 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1988 adios (NULL
, "internal error(2)");
1991 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1992 content_error (ct
->c_file
, ct
, "unable to open for reading");
1998 if ((digested
= ct
->c_digested
))
1999 MD5Init (&mdContext
);
2006 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2008 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2009 content_error (NULL
, ct
, "premature eof");
2013 if ((cc
= strlen (buffer
)) > len
)
2017 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2018 if (!isspace ((unsigned char) *ep
))
2022 for (; cp
< ep
; cp
++) {
2024 /* in an escape sequence */
2026 /* at byte 1 of an escape sequence */
2027 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2028 /* next is byte 2 */
2031 /* at byte 2 of an escape sequence */
2033 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2034 putc (mask
, ce
->ce_fp
);
2036 MD5Update (&mdContext
, &mask
, 1);
2037 if (ferror (ce
->ce_fp
)) {
2038 content_error (ce
->ce_file
, ct
, "error writing to");
2041 /* finished escape sequence; next may be literal or a new
2042 * escape sequence */
2045 /* on to next byte */
2049 /* not in an escape sequence */
2051 /* starting an escape sequence, or invalid '='? */
2052 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2053 /* "=\n" soft line break, eat the \n */
2057 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2058 /* We don't have 2 bytes left, so this is an invalid
2059 * escape sequence; just show the raw bytes (below). */
2060 } else if (isxdigit ((unsigned char) cp
[1]) &&
2061 isxdigit ((unsigned char) cp
[2])) {
2062 /* Next 2 bytes are hex digits, making this a valid escape
2063 * sequence; let's decode it (above). */
2067 /* One or both of the next 2 is out of range, making this
2068 * an invalid escape sequence; just show the raw bytes
2073 /* Just show the raw byte. */
2074 putc (*cp
, ce
->ce_fp
);
2077 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2079 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2082 if (ferror (ce
->ce_fp
)) {
2083 content_error (ce
->ce_file
, ct
, "error writing to");
2089 content_error (NULL
, ct
,
2090 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2094 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2096 if (fflush (ce
->ce_fp
)) {
2097 content_error (ce
->ce_file
, ct
, "error writing to");
2102 unsigned char digest
[16];
2104 MD5Final (digest
, &mdContext
);
2105 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2106 sizeof(digest
) / sizeof(digest
[0])))
2107 content_error (NULL
, ct
,
2108 "content integrity suspect (digest mismatch) -- continuing");
2111 fprintf (stderr
, "content integrity confirmed\n");
2114 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2117 *file
= ce
->ce_file
;
2122 return fileno (ce
->ce_fp
);
2125 free_encoding (ct
, 0);
2141 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2144 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2150 open7Bit (CT ct
, char **file
)
2152 int cc
, fd
, len
, own_ct_fp
= 0;
2153 char buffer
[BUFSIZ
];
2154 /* sbeck -- handle suffixes */
2157 CE ce
= &ct
->c_cefile
;
2160 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2165 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2166 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2172 if (*file
== NULL
) {
2175 ce
->ce_file
= add (*file
, NULL
);
2179 /* sbeck@cise.ufl.edu -- handle suffixes */
2181 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2182 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2183 cp
= context_find (buffer
);
2184 if (cp
== NULL
|| *cp
== '\0') {
2185 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2187 cp
= context_find (buffer
);
2189 if (cp
!= NULL
&& *cp
!= '\0') {
2190 if (ce
->ce_unlink
) {
2191 /* Create temporary file with filename extension. */
2192 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2193 adios(NULL
, "unable to create temporary file in %s",
2197 ce
->ce_file
= add (cp
, ce
->ce_file
);
2199 } else if (*file
== NULL
) {
2201 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2202 adios(NULL
, "unable to create temporary file in %s",
2205 ce
->ce_file
= add (tempfile
, NULL
);
2208 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2209 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2213 if (ct
->c_type
== CT_MULTIPART
) {
2214 CI ci
= &ct
->c_ctinfo
;
2218 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2219 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2220 + 1 + strlen (ci
->ci_subtype
);
2221 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2224 fputs (buffer
, ce
->ce_fp
);
2228 if (ci
->ci_comment
) {
2229 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2230 fputs ("\n\t", ce
->ce_fp
);
2234 putc (' ', ce
->ce_fp
);
2237 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2240 fprintf (ce
->ce_fp
, "\n");
2242 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2244 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2246 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2247 fprintf (ce
->ce_fp
, "\n");
2250 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2251 adios (NULL
, "internal error(3)");
2254 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2255 content_error (ct
->c_file
, ct
, "unable to open for reading");
2261 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2263 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2265 content_error (ct
->c_file
, ct
, "error reading from");
2269 content_error (NULL
, ct
, "premature eof");
2277 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2278 if (ferror (ce
->ce_fp
)) {
2279 content_error (ce
->ce_file
, ct
, "error writing to");
2284 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2286 if (fflush (ce
->ce_fp
)) {
2287 content_error (ce
->ce_file
, ct
, "error writing to");
2291 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2294 *file
= ce
->ce_file
;
2299 return fileno (ce
->ce_fp
);
2302 free_encoding (ct
, 0);
2316 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2318 char cachefile
[BUFSIZ
];
2321 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2326 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2327 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2333 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2334 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2335 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2336 ce
->ce_file
= getcpy (cachefile
);
2340 admonish (cachefile
, "unable to fopen for reading");
2347 *file
= ce
->ce_file
;
2348 *fd
= fileno (ce
->ce_fp
);
2359 return init_encoding (ct
, openFile
);
2364 openFile (CT ct
, char **file
)
2367 char cachefile
[BUFSIZ
];
2368 struct exbody
*e
= ct
->c_ctexbody
;
2369 CE ce
= &ct
->c_cefile
;
2371 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2383 content_error (NULL
, ct
, "missing name parameter");
2387 ce
->ce_file
= getcpy (e
->eb_name
);
2390 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2391 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2395 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2396 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2397 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2401 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2402 if ((fp
= fopen (cachefile
, "w"))) {
2404 char buffer
[BUFSIZ
];
2405 FILE *gp
= ce
->ce_fp
;
2407 fseek (gp
, 0L, SEEK_SET
);
2409 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2411 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2415 admonish (ce
->ce_file
, "error reading");
2416 (void) m_unlink (cachefile
);
2420 admonish (cachefile
, "error writing");
2421 (void) m_unlink (cachefile
);
2428 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2429 *file
= ce
->ce_file
;
2430 return fileno (ce
->ce_fp
);
2440 return init_encoding (ct
, openFTP
);
2445 openFTP (CT ct
, char **file
)
2447 int cachetype
, caching
, fd
;
2449 char *bp
, *ftp
, *user
, *pass
;
2450 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2452 CE ce
= &ct
->c_cefile
;
2453 static char *username
= NULL
;
2454 static char *password
= NULL
;
2458 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2464 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2475 if (!e
->eb_name
|| !e
->eb_site
) {
2476 content_error (NULL
, ct
, "missing %s parameter",
2477 e
->eb_name
? "site": "name");
2484 pidcheck (pidwait (xpid
, NOTOK
));
2488 /* Get the buffer ready to go */
2490 buflen
= sizeof(buffer
);
2493 * Construct the query message for user
2495 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2501 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2507 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2508 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2513 if (e
->eb_size
> 0) {
2514 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2519 snprintf (bp
, buflen
, "? ");
2522 * Now, check the answer
2524 if (!getanswer (buffer
))
2529 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2533 ruserpass (e
->eb_site
, &username
, &password
);
2538 ce
->ce_unlink
= (*file
== NULL
);
2540 cachefile
[0] = '\0';
2541 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2542 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2543 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2544 if (*file
== NULL
) {
2551 ce
->ce_file
= add (*file
, NULL
);
2553 ce
->ce_file
= add (cachefile
, NULL
);
2556 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2557 adios(NULL
, "unable to create temporary file in %s",
2560 ce
->ce_file
= add (tempfile
, NULL
);
2563 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2564 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2569 int child_id
, i
, vecp
;
2573 vec
[vecp
++] = r1bindex (ftp
, '/');
2574 vec
[vecp
++] = e
->eb_site
;
2577 vec
[vecp
++] = e
->eb_dir
;
2578 vec
[vecp
++] = e
->eb_name
;
2579 vec
[vecp
++] = ce
->ce_file
,
2580 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2581 ? "ascii" : "binary";
2586 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2590 adios ("fork", "unable to");
2594 close (fileno (ce
->ce_fp
));
2596 fprintf (stderr
, "unable to exec ");
2602 if (pidXwait (child_id
, NULL
)) {
2603 username
= password
= NULL
;
2613 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2618 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2619 if ((fp
= fopen (cachefile
, "w"))) {
2621 FILE *gp
= ce
->ce_fp
;
2623 fseek (gp
, 0L, SEEK_SET
);
2625 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2627 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2631 admonish (ce
->ce_file
, "error reading");
2632 (void) m_unlink (cachefile
);
2636 admonish (cachefile
, "error writing");
2637 (void) m_unlink (cachefile
);
2645 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2646 *file
= ce
->ce_file
;
2647 return fileno (ce
->ce_fp
);
2658 return init_encoding (ct
, openMail
);
2663 openMail (CT ct
, char **file
)
2665 int child_id
, fd
, i
, vecp
;
2667 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2668 struct exbody
*e
= ct
->c_ctexbody
;
2669 CE ce
= &ct
->c_cefile
;
2671 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2682 if (!e
->eb_server
) {
2683 content_error (NULL
, ct
, "missing server parameter");
2690 pidcheck (pidwait (xpid
, NOTOK
));
2694 /* Get buffer ready to go */
2696 buflen
= sizeof(buffer
);
2698 /* Now, construct query message */
2699 snprintf (bp
, buflen
, "Retrieve content");
2705 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2711 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2713 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2715 /* Now, check answer */
2716 if (!getanswer (buffer
))
2720 vec
[vecp
++] = r1bindex (mailproc
, '/');
2721 vec
[vecp
++] = e
->eb_server
;
2722 vec
[vecp
++] = "-subject";
2723 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2724 vec
[vecp
++] = "-body";
2725 vec
[vecp
++] = e
->eb_body
;
2728 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2732 advise ("fork", "unable to");
2736 execvp (mailproc
, vec
);
2737 fprintf (stderr
, "unable to exec ");
2743 if (pidXwait (child_id
, NULL
) == OK
)
2744 advise (NULL
, "request sent");
2748 if (*file
== NULL
) {
2750 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2751 adios(NULL
, "unable to create temporary file in %s",
2754 ce
->ce_file
= add (tempfile
, NULL
);
2757 ce
->ce_file
= add (*file
, NULL
);
2761 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2762 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2766 /* showproc is for mhshow and mhstore, though mhlist -debug
2767 * prints it, too. */
2769 free (ct
->c_showproc
);
2770 ct
->c_showproc
= add ("true", NULL
);
2772 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2773 *file
= ce
->ce_file
;
2774 return fileno (ce
->ce_fp
);
2785 return init_encoding (ct
, openURL
);
2790 openURL (CT ct
, char **file
)
2792 struct exbody
*e
= ct
->c_ctexbody
;
2793 CE ce
= &ct
->c_cefile
;
2794 char *urlprog
, *program
;
2795 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2796 int fd
, caching
, cachetype
;
2797 struct msgs_array args
= { 0, 0, NULL
};
2800 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2804 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2808 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2820 content_error(NULL
, ct
, "missing url parameter");
2827 pidcheck (pidwait (xpid
, NOTOK
));
2831 ce
->ce_unlink
= (*file
== NULL
);
2833 cachefile
[0] = '\0';
2835 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2836 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2837 if (*file
== NULL
) {
2844 ce
->ce_file
= add(*file
, NULL
);
2846 ce
->ce_file
= add(cachefile
, NULL
);
2849 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2850 adios(NULL
, "unable to create temporary file in %s",
2853 ce
->ce_file
= add (tempfile
, NULL
);
2856 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2857 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2861 switch (child_id
= fork()) {
2863 adios ("fork", "unable to");
2867 argsplit_msgarg(&args
, urlprog
, &program
);
2868 app_msgarg(&args
, e
->eb_url
);
2869 app_msgarg(&args
, NULL
);
2870 dup2(fileno(ce
->ce_fp
), 1);
2871 close(fileno(ce
->ce_fp
));
2872 execvp(program
, args
.msgs
);
2873 fprintf(stderr
, "Unable to exec ");
2879 if (pidXwait(child_id
, NULL
)) {
2887 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2892 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2893 if ((fp
= fopen(cachefile
, "w"))) {
2895 FILE *gp
= ce
->ce_fp
;
2897 fseeko(gp
, 0, SEEK_SET
);
2899 while ((cc
= fread(buffer
, sizeof(*buffer
),
2900 sizeof(buffer
), gp
)) > 0)
2901 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2906 admonish(ce
->ce_file
, "error reading");
2907 (void) m_unlink (cachefile
);
2914 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2915 *file
= ce
->ce_file
;
2920 readDigest (CT ct
, char *cp
)
2925 unsigned char *dp
, value
, *ep
;
2931 for (ep
= (dp
= ct
->c_digest
)
2932 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2937 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2939 fprintf (stderr
, "invalid BASE64 encoding\n");
2943 bits
|= value
<< bitno
;
2945 if ((bitno
-= 6) < 0) {
2946 if (dp
+ (3 - skip
) > ep
)
2947 goto invalid_digest
;
2948 *dp
++ = (bits
>> 16) & 0xff;
2950 *dp
++ = (bits
>> 8) & 0xff;
2952 *dp
++ = bits
& 0xff;
2962 goto self_delimiting
;
2967 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2977 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2985 fprintf (stderr
, "MD5 digest=");
2986 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2987 fprintf (stderr
, "%02x", *dp
& 0xff);
2988 fprintf (stderr
, "\n");
2995 /* Multipart parts might have content before the first subpart and/or
2996 after the last subpart that hasn't been stored anywhere else, so do
2999 get_leftover_mp_content (CT ct
, int before
/* or after */) {
3000 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
3002 int found_boundary
= 0;
3003 char buffer
[BUFSIZ
];
3006 char *content
= NULL
;
3008 if (! m
) return NOTOK
;
3011 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
3013 /* Isolate the beginning of this part to the beginning of the
3014 first subpart and save any content between them. */
3015 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
3016 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3017 boundary
= concat ("--", m
->mp_start
, NULL
);
3019 struct part
*last_subpart
= NULL
;
3020 struct part
*subpart
;
3022 /* Go to the last subpart to get its end position. */
3023 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3024 last_subpart
= subpart
;
3027 if (last_subpart
== NULL
) return NOTOK
;
3029 /* Isolate the end of the last subpart to the end of this part
3030 and save any content between them. */
3031 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3032 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3033 boundary
= concat ("--", m
->mp_stop
, NULL
);
3036 /* Back up by 1 to pick up the newline. */
3037 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
3038 read
+= strlen (buffer
);
3039 /* Don't look beyond beginning of first subpart (before) or
3040 next part (after). */
3041 if (read
> max
) buffer
[read
-max
] = '\0';
3044 if (! strcmp (buffer
, boundary
)) {
3048 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
3054 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3056 char *old_content
= content
;
3057 content
= concat (content
, buffer
, NULL
);
3061 ? concat ("\n", buffer
, NULL
)
3062 : concat (buffer
, NULL
);
3067 if (found_boundary
|| read
> max
) break;
3069 if (read
> max
) break;
3073 /* Skip the newline if that's all there is. */
3077 /* Remove trailing newline, except at EOF. */
3078 if ((before
|| ! feof (ct
->c_fp
)) &&
3079 (cp
= content
+ strlen (content
)) > content
&&
3084 if (strlen (content
) > 1) {
3086 m
->mp_content_before
= content
;
3088 m
->mp_content_after
= content
;
3102 ct_type_str (int type
) {
3104 case CT_APPLICATION
:
3105 return "application";
3121 return "unknown_type";
3127 ct_subtype_str (int type
, int subtype
) {
3129 case CT_APPLICATION
:
3131 case APPLICATION_OCTETS
:
3133 case APPLICATION_POSTSCRIPT
:
3134 return "postscript";
3136 return "unknown_app_subtype";
3140 case MESSAGE_RFC822
:
3142 case MESSAGE_PARTIAL
:
3144 case MESSAGE_EXTERNAL
:
3147 return "unknown_msg_subtype";
3153 case MULTI_ALTERNATE
:
3154 return "alternative";
3157 case MULTI_PARALLEL
:
3160 return "unknown_multipart_subtype";
3171 return "unknown_text_subtype";
3174 return "unknown_type";
3179 /* Find the content type and InitFunc for the CT. */
3180 const struct str2init
*
3181 get_ct_init (int type
) {
3182 const struct str2init
*sp
;
3184 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3185 if (type
== sp
->si_val
) {
3194 ce_str (int encoding
) {
3199 return "quoted-printable";
3215 /* Find the content type and InitFunc for the content encoding method. */
3216 const struct str2init
*
3217 get_ce_method (const char *method
) {
3218 struct str2init
*sp
;
3220 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3221 if (! strcasecmp (method
, sp
->si_key
)) {
3230 * Parse a series of MIME attributes (or parameters) given a header as
3233 * Arguments include:
3235 * filename - Name of input file (for error messages)
3236 * fieldname - Name of field being processed
3237 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3238 * Updated to point to end of attributes when finished.
3239 * param_head - Pointer to head of parameter list
3240 * param_tail - Pointer to tail of parameter list
3241 * commentp - Pointer to header comment pointer (may be NULL)
3243 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3244 * DONE to indicate a benign error (minor parsing error, but the program
3249 parse_header_attrs (const char *filename
, const char *fieldname
,
3250 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3253 char *cp
= *header_attrp
;
3259 struct sectlist
*next
;
3265 struct sectlist
*sechead
;
3266 struct parmlist
*next
;
3267 } *pp
, *pp2
, *phead
= NULL
;
3269 while (*cp
== ';') {
3270 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3271 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3274 while (isspace ((unsigned char) *cp
))
3278 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3284 "extraneous trailing ';' in message %s's %s: "
3286 filename
, fieldname
);
3290 /* down case the attribute name */
3291 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3292 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3293 *dp
= tolower ((unsigned char) *dp
);
3295 for (up
= dp
; isspace ((unsigned char) *dp
);)
3297 if (dp
== cp
|| *dp
!= '=') {
3299 "invalid parameter in message %s's %s: "
3300 "field\n%*sparameter %s (error detected at offset %d)",
3301 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3306 * To handle RFC 2231, we have to deal with the following extensions:
3308 * name*=encoded-value
3309 * name*<N>=part-N-of-a-parameter-value
3310 * name*<N>*=encoded-part-N-of-a-parameter-value
3313 * If there's a * right before the equal sign, it's encoded.
3314 * If there's a * and one or more digits, then it's section N.
3316 * Remember we can have one or the other, or both. cp points to
3317 * beginning of name, up points past the last character in the
3321 for (vp
= cp
; vp
< up
; vp
++) {
3322 if (*vp
== '*' && vp
< up
- 1) {
3325 } else if (*vp
== '*' && vp
== up
- 1) {
3327 } else if (partial
) {
3328 if (isdigit((unsigned char) *vp
))
3329 index
= *vp
- '0' + index
* 10;
3331 advise (NULL
, "invalid parameter index in message %s's "
3332 "%s: field\n%*s(parameter %s)", filename
,
3333 fieldname
, strlen(invo_name
) + 2, "", cp
);
3342 * Break out the parameter name and value sections and allocate
3346 nameptr
= mh_xmalloc(len
+ 1);
3347 strncpy(nameptr
, cp
, len
);
3348 nameptr
[len
] = '\0';
3350 for (dp
++; isspace ((unsigned char) *dp
);)
3355 * Single quotes delimit the character set and language tag.
3356 * They are required on the first section (or a complete
3361 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3367 charset
= mh_xmalloc(len
+ 1);
3368 strncpy(charset
, dp
, len
);
3369 charset
[len
] = '\0';
3375 advise(NULL
, "missing charset in message %s's %s: "
3376 "field\n%*s(parameter %s)", filename
, fieldname
,
3377 strlen(invo_name
) + 2, "", nameptr
);
3383 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3390 lang
= mh_xmalloc(len
+ 1);
3391 strncpy(lang
, dp
, len
);
3398 advise(NULL
, "missing language tag in message %s's %s: "
3399 "field\n%*s(parameter %s)", filename
, fieldname
,
3400 strlen(invo_name
) + 2, "", nameptr
);
3411 * At this point vp should be pointing at the beginning
3412 * of the encoded value/section. Continue until we reach
3413 * the end or get whitespace. But first, calculate the
3414 * length so we can allocate the correct buffer size.
3417 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3419 if (*(vp
+ 1) == '\0' ||
3420 !isxdigit((unsigned char) *(vp
+ 1)) ||
3421 *(vp
+ 2) == '\0' ||
3422 !isxdigit((unsigned char) *(vp
+ 2))) {
3423 advise(NULL
, "invalid encoded sequence in message "
3424 "%s's %s: field\n%*s(parameter %s)",
3425 filename
, fieldname
, strlen(invo_name
) + 2,
3439 up
= valptr
= mh_xmalloc(len
+ 1);
3441 for (vp
= dp
; istoken(*vp
); vp
++) {
3443 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3454 * A "normal" string. If it's got a leading quote, then we
3455 * strip the quotes out. Otherwise go until we reach the end
3456 * or get whitespace. Note we scan it twice; once to get the
3457 * length, then the second time copies it into the destination
3464 for (cp
= dp
+ 1;;) {
3469 "invalid quoted-string in message %s's %s: "
3470 "field\n%*s(parameter %s)",
3471 filename
, fieldname
, strlen(invo_name
) + 2, "",
3494 for (cp
= dp
; istoken (*cp
); cp
++) {
3499 valptr
= mh_xmalloc(len
+ 1);
3503 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3511 strncpy(valptr
, cp
= dp
, len
);
3519 * If 'partial' is set, we don't allocate a parameter now. We
3520 * put it on the parameter linked list to be reassembled later.
3522 * "phead" points to a list of all parameters we need to reassemble.
3523 * Each parameter has a list of sections. We insert the sections in
3528 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3529 if (strcasecmp(nameptr
, pp
->name
) == 0)
3534 pp
= mh_xmalloc(sizeof(*pp
));
3535 memset(pp
, 0, sizeof(*pp
));
3542 * Insert this into the section linked list
3545 sp
= mh_xmalloc(sizeof(*sp
));
3546 memset(sp
, 0, sizeof(*sp
));
3551 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3552 sp
->next
= pp
->sechead
;
3555 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3556 if (sp2
->index
== sp
->index
) {
3557 advise (NULL
, "duplicate index (%d) in message "
3558 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3559 filename
, fieldname
, strlen(invo_name
) + 2, "",
3563 if (sp2
->index
< sp
->index
&&
3564 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3565 sp
->next
= sp2
->next
;
3572 advise(NULL
, "Internal error: cannot insert partial "
3573 "param in message %s's %s: field\n%*s(parameter %s)",
3574 filename
, fieldname
, strlen(invo_name
) + 2, "",
3581 * Save our charset and lang tags.
3584 if (index
== 0 && encoded
) {
3587 pp
->charset
= charset
;
3593 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3594 pm
->pm_charset
= charset
;
3598 while (isspace ((unsigned char) *cp
))
3602 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3608 * Now that we're done, reassemble all of the partial parameters.
3611 for (pp
= phead
; pp
!= NULL
; ) {
3615 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3616 if (sp
->index
!= pindex
++) {
3617 advise(NULL
, "missing section %d for parameter in "
3618 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3619 filename
, fieldname
, strlen(invo_name
) + 2, "",
3626 p
= q
= mh_xmalloc(tlen
+ 1);
3627 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3628 memcpy(q
, sp
->value
, sp
->len
);
3638 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3639 pm
->pm_charset
= pp
->charset
;
3640 pm
->pm_lang
= pp
->lang
;
3651 * Return the charset for a particular content type. Return pointer is
3652 * only valid until the next call to content_charset().
3656 content_charset (CT ct
) {
3657 static char *ret_charset
= NULL
;
3659 if (ret_charset
!= NULL
) {
3663 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3665 return ret_charset
? ret_charset
: "US-ASCII";
3670 * Create a string based on a list of output parameters. Assume that this
3671 * parameter string will be appended to an existing header, so start out
3672 * with the separator (;). Perform RFC 2231 encoding when necessary.
3676 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3678 char *paramout
= NULL
;
3679 char line
[CPERLIN
* 2], *q
;
3680 int curlen
, index
, cont
, encode
, i
;
3681 size_t valoff
, numchars
;
3683 while (params
!= NULL
) {
3689 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3692 if (strlen(params
->pm_name
) > CPERLIN
) {
3693 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3699 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3702 * Loop until we get a parameter that fits within a line. We
3703 * assume new lines start with a tab, so check our overflow based
3713 * At this point we're definitely continuing the line, so
3714 * be sure to include the parameter name and section index.
3717 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3718 params
->pm_name
, index
);
3721 * Both of these functions do a NUL termination
3725 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3726 numchars
, valoff
, index
);
3728 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3739 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3744 * "line" starts with a ;\n\t, so that doesn't count against
3745 * the length. But add 8 since it starts with a tab; that's
3746 * how we end up with 5.
3749 initialwidth
= strlen(line
) + 5;
3752 * At this point the line should be built, so add it to our
3753 * current output buffer.
3756 paramout
= add(line
, paramout
);
3760 * If this won't fit on the line, start a new one. Save room in
3761 * case we need a semicolon on the end
3764 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3776 * At this point, we're either finishing a contined parameter, or
3777 * we're working on a new one.
3781 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3782 params
->pm_name
, index
);
3784 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3789 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3790 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3792 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3793 strlen(params
->pm_value
+ valoff
), valoff
);
3801 paramout
= add(line
, paramout
);
3802 initialwidth
+= strlen(line
);
3804 params
= params
->pm_next
;
3808 *offsetout
= initialwidth
;
3814 * Calculate the size of a parameter.
3818 * pm - The parameter being output
3819 * index - If continuing the parameter, the index of the section
3821 * valueoff - The current offset into the parameter value that we're
3822 * working on (previous sections have consumed valueoff bytes).
3823 * encode - Set if we should perform encoding on this parameter section
3824 * (given that we're consuming bytesfit bytes).
3825 * cont - Set if the remaining data in value will not fit on a single
3826 * line and will need to be continued.
3827 * bytesfit - The number of bytes that we can consume from the parameter
3828 * value and still fit on a completely new line. The
3829 * calculation assumes the new line starts with a tab,
3830 * includes the parameter name and any encoding, and fits
3831 * within CPERLIN bytes. Will always be at least 1.
3835 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3838 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3839 size_t len
= 0, fit
= 0;
3840 int fitlimit
= 0, eightbit
, maxfit
;
3845 * Add up the length. First, start with the parameter name.
3848 len
= strlen(pm
->pm_name
);
3851 * Scan the parameter value and see if we need to do encoding for this
3855 eightbit
= contains8bit(start
, NULL
);
3858 * Determine if we need to encode this section. Encoding is necessary if:
3860 * - There are any 8-bit characters at all and we're on the first
3862 * - There are 8-bit characters within N bytes of our section start.
3863 * N is calculated based on the number of bytes it would take to
3864 * reach CPERLIN. Specifically:
3865 * 8 (starting tab) +
3866 * strlen(param name) +
3867 * 4 ('* for section marker, '=', opening/closing '"')
3869 * is the number of bytes used by everything that isn't part of the
3870 * value. So that gets subtracted from CPERLIN.
3873 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3874 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3875 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3879 len
++; /* Add in equal sign */
3883 * We're using maxfit as a marker for how many characters we can
3884 * fit into the line. Bump it by two because we're not using quotes
3891 * If we don't have a charset or language tag in this parameter,
3895 if (! pm
->pm_charset
)
3896 pm
->pm_charset
= getcpy(write_charset_8bit());
3898 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
3900 len
++; /* For the encoding marker */
3903 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
3908 * We know we definitely need to include an index. maxfit already
3909 * includes the section marker.
3911 len
+= strlen(indexchar
);
3913 for (p
= start
; *p
!= '\0'; p
++) {
3914 if (isparamencode(*p
)) {
3922 * Just so there's no confusion: maxfit is counting OUTPUT
3923 * characters (post-encoding). fit is counting INPUT characters.
3925 if (! fitlimit
&& maxfit
>= 0)
3927 else if (! fitlimit
)
3932 * Calculate the string length, but add room for quoting \
3933 * and " if necessary. Also account for quotes at beginning
3936 for (p
= start
; *p
!= '\0'; p
++) {
3947 if (! fitlimit
&& maxfit
>= 0)
3949 else if (! fitlimit
)
3966 * Output an encoded parameter string.
3970 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
3971 size_t valueoff
, int index
)
3973 size_t outlen
= 0, n
;
3974 char *endptr
= output
+ len
, *p
;
3977 * First, output the marker for an encoded string.
3985 * If the index is 0, output the character set and language tag.
3986 * If theses were NULL, they should have already been filled in
3991 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
3995 if (output
> endptr
) {
3996 advise(NULL
, "Internal error: parameter buffer overflow");
4002 * Copy over the value, encoding if necessary
4005 p
= pm
->pm_value
+ valueoff
;
4006 while (valuelen
-- > 0) {
4007 if (isparamencode(*p
)) {
4008 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
4015 if (output
> endptr
) {
4016 advise(NULL
, "Internal error: parameter buffer overflow");
4027 * Output a "normal" parameter, without encoding. Be sure to escape
4028 * quotes and backslashes if necessary.
4032 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4036 char *endptr
= output
+ len
, *p
;
4042 p
= pm
->pm_value
+ valueoff
;
4044 while (valuelen
-- > 0) {
4054 if (output
> endptr
) {
4055 advise(NULL
, "Internal error: parameter buffer overflow");
4060 if (output
- 2 > endptr
) {
4061 advise(NULL
, "Internal error: parameter buffer overflow");
4072 * Add a parameter to the parameter linked list
4076 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4078 PM pm
= mh_xmalloc(sizeof(*pm
));
4080 memset(pm
, 0, sizeof(*pm
));
4082 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4083 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4086 (*last
)->pm_next
= pm
;
4097 * Either replace a current parameter with a new value, or add the parameter
4098 * to the parameter linked list.
4102 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4106 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4107 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4109 * If nocopy is set, it's assumed that we own both name
4110 * and value. We don't need name, so we discard it now.
4115 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4120 return add_param(first
, last
, name
, value
, nocopy
);
4124 * Retrieve a parameter value from a parameter linked list. If the parameter
4125 * value needs converted to the local character set, do that now.
4129 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4131 while (first
!= NULL
) {
4132 if (strcasecmp(name
, first
->pm_name
) == 0) {
4134 return first
->pm_value
;
4136 return getcpy(get_param_value(first
, replace
));
4138 first
= first
->pm_next
;
4145 * Return a parameter value, converting to the local character set if
4149 char *get_param_value(PM pm
, char replace
)
4151 static char buffer
[4096]; /* I hope no parameters are larger */
4152 size_t bufsize
= sizeof(buffer
);
4157 ICONV_CONST
char *p
;
4158 #else /* HAVE_ICONV */
4160 #endif /* HAVE_ICONV */
4165 * If we don't have a character set indicated, it's assumed to be
4166 * US-ASCII. If it matches our character set, we don't need to convert
4170 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4171 strlen(pm
->pm_charset
))) {
4172 return pm
->pm_value
;
4176 * In this case, we need to convert. If we have iconv support, use
4177 * that. Otherwise, go through and simply replace every non-ASCII
4178 * character with the substitution character.
4183 bufsize
= sizeof(buffer
);
4184 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4186 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4187 if (cd
== (iconv_t
) -1) {
4191 inbytes
= strlen(pm
->pm_value
);
4195 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4196 if (errno
!= EILSEQ
) {
4201 * Reset shift state, substitute our character,
4202 * try to restart conversion.
4205 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4218 for (++p
, --inbytes
;
4219 inbytes
> 0 && (((unsigned char) *q
) & 0xc0) == 0x80;
4238 #endif /* HAVE_ICONV */
4241 * Take everything non-ASCII and substituite the replacement character
4245 bufsize
= sizeof(buffer
);
4246 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4247 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))