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.
16 #include <h/mhparse.h>
20 #endif /* HAVE_ICONV */
26 extern int rcachesw
; /* mhcachesbr.c */
27 extern int wcachesw
; /* mhcachesbr.c */
29 int checksw
= 0; /* check Content-MD5 field */
32 * These are for mhfixmsg to:
33 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
35 * 2) Suppress the warning about bogus multipart content, and report it.
36 * 3) Suppress the warning about extraneous trailing ';' in header parameter
37 * lists, and report it.
39 int skip_mp_cte_check
;
40 int suppress_bogus_mp_content_warning
;
42 int suppress_extraneous_trailing_semicolon_warning
;
43 int extraneous_trailing_semicolon
;
44 int suppress_multiple_mime_version_warning
= 1;
46 /* list of preferred type/subtype pairs, for -prefer */
47 char *preferred_types
[NPREFS
],
48 *preferred_subtypes
[NPREFS
];
53 * Structures for TEXT messages
55 struct k2v SubText
[] = {
56 { "plain", TEXT_PLAIN
},
57 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
58 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
59 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
62 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
65 * Structures for MULTIPART messages
67 struct k2v SubMultiPart
[] = {
68 { "mixed", MULTI_MIXED
},
69 { "alternative", MULTI_ALTERNATE
},
70 { "digest", MULTI_DIGEST
},
71 { "parallel", MULTI_PARALLEL
},
72 { "related", MULTI_RELATED
},
73 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
77 * Structures for MESSAGE messages
79 struct k2v SubMessage
[] = {
80 { "rfc822", MESSAGE_RFC822
},
81 { "partial", MESSAGE_PARTIAL
},
82 { "external-body", MESSAGE_EXTERNAL
},
83 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
87 * Structure for APPLICATION messages
89 struct k2v SubApplication
[] = {
90 { "octet-stream", APPLICATION_OCTETS
},
91 { "postscript", APPLICATION_POSTSCRIPT
},
92 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
96 * Mapping of names of CTE types in mhbuild directives
98 static struct k2v EncodingType
[] = {
101 { "q-p", CE_QUOTED
},
102 { "quoted-printable", CE_QUOTED
},
103 { "b64", CE_BASE64
},
104 { "base64", CE_BASE64
},
110 int find_cache (CT
, int, int *, char *, char *, int);
114 int type_ok (CT
, int);
115 void content_error (char *, CT
, char *, ...);
118 void free_encoding (CT
, int);
123 static CT
get_content (FILE *, char *, int);
124 static int get_comment (const char *, const char *, char **, char **);
126 static int InitGeneric (CT
);
127 static int InitText (CT
);
128 static int InitMultiPart (CT
);
129 static void reverse_parts (CT
);
130 static void prefer_parts(CT ct
);
131 static int InitMessage (CT
);
132 static int InitApplication (CT
);
133 static int init_encoding (CT
, OpenCEFunc
);
134 static unsigned long size_encoding (CT
);
135 static int InitBase64 (CT
);
136 static int openBase64 (CT
, char **);
137 static int InitQuoted (CT
);
138 static int openQuoted (CT
, char **);
139 static int Init7Bit (CT
);
140 static int openExternal (CT
, CT
, CE
, char **, int *);
141 static int InitFile (CT
);
142 static int openFile (CT
, char **);
143 static int InitFTP (CT
);
144 static int openFTP (CT
, char **);
145 static int InitMail (CT
);
146 static int openMail (CT
, char **);
147 static int readDigest (CT
, char *);
148 static int get_leftover_mp_content (CT
, int);
149 static int InitURL (CT
);
150 static int openURL (CT
, char **);
151 static int parse_header_attrs (const char *, const char *, char **, PM
*,
153 static size_t param_len(PM
, int, size_t, int *, int *, size_t *);
154 static size_t encode_param(PM
, char *, size_t, size_t, size_t, int);
155 static size_t normal_param(PM
, char *, size_t, size_t, size_t);
156 static int get_dispo (char *, CT
, int);
158 struct str2init str2cts
[] = {
159 { "application", CT_APPLICATION
, InitApplication
},
160 { "audio", CT_AUDIO
, InitGeneric
},
161 { "image", CT_IMAGE
, InitGeneric
},
162 { "message", CT_MESSAGE
, InitMessage
},
163 { "multipart", CT_MULTIPART
, InitMultiPart
},
164 { "text", CT_TEXT
, InitText
},
165 { "video", CT_VIDEO
, InitGeneric
},
166 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
167 { NULL
, CT_UNKNOWN
, NULL
},
170 struct str2init str2ces
[] = {
171 { "base64", CE_BASE64
, InitBase64
},
172 { "quoted-printable", CE_QUOTED
, InitQuoted
},
173 { "8bit", CE_8BIT
, Init7Bit
},
174 { "7bit", CE_7BIT
, Init7Bit
},
175 { "binary", CE_BINARY
, Init7Bit
},
176 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
177 { NULL
, CE_UNKNOWN
, NULL
},
181 * NOTE WELL: si_key MUST NOT have value of NOTOK
183 * si_key is 1 if access method is anonymous.
185 struct str2init str2methods
[] = {
186 { "afs", 1, InitFile
},
187 { "anon-ftp", 1, InitFTP
},
188 { "ftp", 0, InitFTP
},
189 { "local-file", 0, InitFile
},
190 { "mail-server", 0, InitMail
},
191 { "url", 0, InitURL
},
197 * Main entry point for parsing a MIME message or file.
198 * It returns the Content structure for the top level
199 * entity in the file.
203 parse_mime (char *file
)
212 * Check if file is actually standard input
214 if ((is_stdin
= !(strcmp (file
, "-")))) {
215 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
217 advise("mhparse", "unable to create temporary file in %s",
221 file
= add (tfile
, NULL
);
223 while ((n
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0) {
224 if (fwrite(buffer
, 1, n
, fp
) != n
) {
225 (void) m_unlink (file
);
226 advise (file
, "error copying to temporary file");
232 if (ferror (stdin
)) {
233 (void) m_unlink (file
);
234 advise ("stdin", "error reading");
238 (void) m_unlink (file
);
239 advise (file
, "error writing");
242 fseek (fp
, 0L, SEEK_SET
);
243 } else if ((fp
= fopen (file
, "r")) == NULL
) {
244 advise (file
, "unable to read");
248 if (!(ct
= get_content (fp
, file
, 1))) {
250 (void) m_unlink (file
);
251 advise (NULL
, "unable to decode %s", file
);
256 ct
->c_unlink
= 1; /* temp file to remove */
260 if (ct
->c_end
== 0L) {
261 fseek (fp
, 0L, SEEK_END
);
262 ct
->c_end
= ftell (fp
);
265 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
277 * Main routine for reading/parsing the headers
278 * of a message content.
280 * toplevel = 1 # we are at the top level of the message
281 * toplevel = 0 # we are inside message type or multipart type
282 * # other than multipart/digest
283 * toplevel = -1 # we are inside multipart/digest
284 * NB: on failure we will fclose(in)!
288 get_content (FILE *in
, char *file
, int toplevel
)
291 char buf
[BUFSIZ
], name
[NAMESZ
];
295 m_getfld_state_t gstate
= 0;
297 /* allocate the content structure */
298 if (!(ct
= (CT
) mh_xcalloc (1, sizeof(*ct
))))
299 adios (NULL
, "out of memory");
302 ct
->c_file
= add (file
, NULL
);
303 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
306 * Parse the header fields for this
307 * content into a linked list.
309 m_getfld_track_filepos (&gstate
, in
);
310 for (compnum
= 1;;) {
311 int bufsz
= sizeof buf
;
312 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
317 /* get copies of the buffers */
318 np
= add (name
, NULL
);
319 vp
= add (buf
, NULL
);
321 /* if necessary, get rest of field */
322 while (state
== FLDPLUS
) {
324 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
325 vp
= add (buf
, vp
); /* add to previous value */
328 /* Now add the header data to the list */
329 add_header (ct
, np
, vp
);
331 /* continue, to see if this isn't the last header field */
332 ct
->c_begin
= ftell (in
) + 1;
336 ct
->c_begin
= ftell (in
) - strlen (buf
);
340 ct
->c_begin
= ftell (in
);
345 adios (NULL
, "message format error in component #%d", compnum
);
348 adios (NULL
, "getfld() returned %d", state
);
351 /* break out of the loop */
354 m_getfld_state_destroy (&gstate
);
357 * Read the content headers. We will parse the
358 * MIME related header fields into their various
359 * structures and set internal flags related to
360 * content type/subtype, etc.
363 hp
= ct
->c_first_hf
; /* start at first header field */
365 /* Get MIME-Version field */
366 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
371 vrsn
= add (hp
->value
, NULL
);
373 /* Now, cleanup this field */
376 while (isspace ((unsigned char) *cp
))
378 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
380 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
381 if (!isspace ((unsigned char) *dp
))
385 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
388 get_comment (ct
->c_file
, VRSN_FIELD
, &cp
, NULL
) == NOTOK
)
391 for (dp
= cp
; istoken (*dp
); dp
++)
395 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
398 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
399 ct
->c_file
, VRSN_FIELD
, cp
);
404 if (! suppress_multiple_mime_version_warning
)
405 advise (NULL
, "message %s has multiple %s: fields",
406 ct
->c_file
, VRSN_FIELD
);
410 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
411 /* Get Content-Type field */
412 struct str2init
*s2i
;
413 CI ci
= &ct
->c_ctinfo
;
415 /* Check if we've already seen a Content-Type header */
417 advise (NULL
, "message %s has multiple %s: fields",
418 ct
->c_file
, TYPE_FIELD
);
422 /* Parse the Content-Type field */
423 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
427 * Set the Init function and the internal
428 * flag for this content type.
430 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
431 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
433 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
435 ct
->c_type
= s2i
->si_val
;
436 ct
->c_ctinitfnx
= s2i
->si_init
;
438 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
439 /* Get Content-Transfer-Encoding field */
441 struct str2init
*s2i
;
444 * Check if we've already seen the
445 * Content-Transfer-Encoding field
448 advise (NULL
, "message %s has multiple %s: fields",
449 ct
->c_file
, ENCODING_FIELD
);
453 /* get copy of this field */
454 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
456 while (isspace ((unsigned char) *cp
))
458 for (dp
= cp
; istoken (*dp
); dp
++)
464 * Find the internal flag and Init function
465 * for this transfer encoding.
467 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
468 if (!strcasecmp (cp
, s2i
->si_key
))
470 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
473 ct
->c_encoding
= s2i
->si_val
;
475 /* Call the Init function for this encoding */
476 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
479 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
480 /* Get Content-MD5 field */
486 if (ct
->c_digested
) {
487 advise (NULL
, "message %s has multiple %s: fields",
488 ct
->c_file
, MD5_FIELD
);
492 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
494 while (isspace ((unsigned char) *cp
))
496 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
498 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
499 if (!isspace ((unsigned char) *dp
))
503 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
506 get_comment (ct
->c_file
, MD5_FIELD
, &cp
, NULL
) == NOTOK
) {
511 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
519 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
520 /* Get Content-ID field */
521 ct
->c_id
= add (hp
->value
, ct
->c_id
);
523 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
524 /* Get Content-Description field */
525 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
527 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
528 /* Get Content-Disposition field */
529 if (get_dispo(hp
->value
, ct
, 0) == NOTOK
)
534 hp
= hp
->next
; /* next header field */
538 * Check if we saw a Content-Type field.
539 * If not, then assign a default value for
540 * it, and the Init function.
544 * If we are inside a multipart/digest message,
545 * so default type is message/rfc822
548 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
550 ct
->c_type
= CT_MESSAGE
;
551 ct
->c_ctinitfnx
= InitMessage
;
554 * Else default type is text/plain
556 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
558 ct
->c_type
= CT_TEXT
;
559 ct
->c_ctinitfnx
= InitText
;
563 /* Use default Transfer-Encoding, if necessary */
565 ct
->c_encoding
= CE_7BIT
;
578 * small routine to add header field to list
582 add_header (CT ct
, char *name
, char *value
)
586 /* allocate header field structure */
587 hp
= mh_xmalloc (sizeof(*hp
));
589 /* link data into header structure */
594 /* link header structure into the list */
595 if (ct
->c_first_hf
== NULL
) {
596 ct
->c_first_hf
= hp
; /* this is the first */
599 ct
->c_last_hf
->next
= hp
; /* add it to the end */
608 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
609 * directives. Fills in the information of the CTinfo structure.
612 get_ctinfo (char *cp
, CT ct
, int magic
)
621 /* store copy of Content-Type line */
622 cp
= ct
->c_ctline
= add (cp
, NULL
);
624 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
627 /* change newlines to spaces */
628 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
631 /* trim trailing spaces */
632 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
633 if (!isspace ((unsigned char) *dp
))
638 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
640 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
641 &ci
->ci_comment
) == NOTOK
)
644 for (dp
= cp
; istoken (*dp
); dp
++)
647 ci
->ci_type
= add (cp
, NULL
); /* store content type */
651 advise (NULL
, "invalid %s: field in message %s (empty type)",
652 TYPE_FIELD
, ct
->c_file
);
656 /* down case the content type string */
657 for (dp
= ci
->ci_type
; *dp
; dp
++)
658 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
659 *dp
= tolower ((unsigned char) *dp
);
661 while (isspace ((unsigned char) *cp
))
664 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
665 &ci
->ci_comment
) == NOTOK
)
670 ci
->ci_subtype
= add ("", NULL
);
675 while (isspace ((unsigned char) *cp
))
678 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
679 &ci
->ci_comment
) == NOTOK
)
682 for (dp
= cp
; istoken (*dp
); dp
++)
685 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
688 if (!*ci
->ci_subtype
) {
690 "invalid %s: field in message %s (empty subtype for \"%s\")",
691 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
695 /* down case the content subtype string */
696 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
697 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
698 *dp
= tolower ((unsigned char) *dp
);
701 while (isspace ((unsigned char) *cp
))
704 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
705 &ci
->ci_comment
) == NOTOK
)
708 if ((status
= parse_header_attrs (ct
->c_file
, TYPE_FIELD
, &cp
,
709 &ci
->ci_first_pm
, &ci
->ci_last_pm
,
710 &ci
->ci_comment
)) != OK
) {
711 return status
== NOTOK
? NOTOK
: OK
;
715 * Get any <Content-Id> given in buffer
717 if (magic
&& *cp
== '<') {
722 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
723 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
729 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
735 while (isspace ((unsigned char) *cp
))
740 * Get any [Content-Description] given in buffer.
742 if (magic
&& *cp
== '[') {
744 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
748 advise (NULL
, "invalid description in message %s", ct
->c_file
);
756 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
762 while (isspace ((unsigned char) *cp
))
767 * Get any {Content-Disposition} given in buffer.
769 if (magic
&& *cp
== '{') {
771 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
775 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
783 if (get_dispo(cp
, ct
, 1) != OK
)
789 while (isspace ((unsigned char) *cp
))
794 * Get any extension directives (right now just the content transfer
795 * encoding, but maybe others) that we care about.
798 if (magic
&& *cp
== '*') {
800 * See if it's a CTE we match on
805 while (*cp
!= '\0' && ! isspace((unsigned char) *cp
))
809 advise (NULL
, "invalid null transfer encoding specification");
816 ct
->c_reqencoding
= CE_UNKNOWN
;
818 for (kv
= EncodingType
; kv
->kv_key
; kv
++) {
819 if (strcasecmp(kv
->kv_key
, dp
) == 0) {
820 ct
->c_reqencoding
= kv
->kv_value
;
825 if (ct
->c_reqencoding
== CE_UNKNOWN
) {
826 advise (NULL
, "invalid CTE specification: \"%s\"", dp
);
830 while (isspace ((unsigned char) *cp
))
835 * Check if anything is left over
839 ci
->ci_magic
= add (cp
, NULL
);
841 /* If there is a Content-Disposition header and it doesn't
842 have a *filename=, extract it from the magic contents.
843 The r1bindex call skips any leading directory
845 if (ct
->c_dispo_type
&&
846 !get_param(ct
->c_dispo_first
, "filename", '_', 1)) {
847 add_param(&ct
->c_dispo_first
, &ct
->c_dispo_last
, "filename",
848 r1bindex(ci
->ci_magic
, '/'), 0);
853 "extraneous information in message %s's %s: field\n%*s(%s)",
854 ct
->c_file
, TYPE_FIELD
, strlen(invo_name
) + 2, "", cp
);
862 * Parse out a Content-Disposition header. A lot of this is cribbed from
866 get_dispo (char *cp
, CT ct
, int buildflag
)
868 char *dp
, *dispoheader
;
873 * Save the whole copy of the Content-Disposition header, unless we're
874 * processing a mhbuild directive. A NULL c_dispo will be a flag to
875 * mhbuild that the disposition header needs to be generated at that
879 dispoheader
= cp
= add(cp
, NULL
);
881 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
884 /* change newlines to spaces */
885 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
888 /* trim trailing spaces */
889 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
890 if (!isspace ((unsigned char) *dp
))
895 fprintf (stderr
, "%s: %s\n", DISPO_FIELD
, cp
);
897 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) ==
903 for (dp
= cp
; istoken (*dp
); dp
++)
906 ct
->c_dispo_type
= add (cp
, NULL
); /* store disposition type */
909 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) == NOTOK
)
912 if ((status
= parse_header_attrs (ct
->c_file
, DISPO_FIELD
, &cp
,
913 &ct
->c_dispo_first
, &ct
->c_dispo_last
,
915 if (status
== NOTOK
) {
921 "extraneous information in message %s's %s: field\n%*s(%s)",
922 ct
->c_file
, DISPO_FIELD
, strlen(invo_name
) + 2, "", cp
);
928 ct
->c_dispo
= dispoheader
;
935 get_comment (const char *filename
, const char *fieldname
, char **ap
,
940 char c
, buffer
[BUFSIZ
], *dp
;
950 advise (NULL
, "invalid comment in message %s's %s: field",
951 filename
, fieldname
);
956 if ((c
= *cp
++) == '\0')
979 if ((dp
= *commentp
)) {
980 *commentp
= concat (dp
, " ", buffer
, NULL
);
983 *commentp
= add (buffer
, NULL
);
987 while (isspace ((unsigned char) *cp
))
998 * Handles content types audio, image, and video.
999 * There's not much to do right here.
1007 return OK
; /* not much to do here */
1018 char buffer
[BUFSIZ
];
1023 CI ci
= &ct
->c_ctinfo
;
1025 /* check for missing subtype */
1026 if (!*ci
->ci_subtype
)
1027 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1030 ct
->c_subtype
= ct_str_subtype (CT_TEXT
, ci
->ci_subtype
);
1032 /* allocate text character set structure */
1033 if ((t
= (struct text
*) mh_xcalloc (1, sizeof(*t
))) == NULL
)
1034 adios (NULL
, "out of memory");
1035 ct
->c_ctparams
= (void *) t
;
1037 /* scan for charset parameter */
1038 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
)
1039 if (!strcasecmp (pm
->pm_name
, "charset"))
1042 /* check if content specified a character set */
1044 chset
= pm
->pm_value
;
1045 t
->tx_charset
= CHARSET_SPECIFIED
;
1047 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1051 * If we can not handle character set natively,
1052 * then check profile for string to modify the
1053 * terminal or display method.
1055 * termproc is for mhshow, though mhlist -debug prints it, too.
1057 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1058 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1059 if ((cp
= context_find (buffer
)))
1060 ct
->c_termproc
= getcpy (cp
);
1072 InitMultiPart (CT ct
)
1082 struct multipart
*m
;
1083 struct part
*part
, **next
;
1084 CI ci
= &ct
->c_ctinfo
;
1089 * The encoding for multipart messages must be either
1090 * 7bit, 8bit, or binary (per RFC2045).
1092 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1093 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1094 /* Copy the Content-Transfer-Encoding header field body so we can
1095 remove any trailing whitespace and leading blanks from it. */
1096 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1098 bp
= cte
+ strlen (cte
) - 1;
1099 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1100 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1103 "\"%s/%s\" type in message %s must be encoded in\n"
1104 "7bit, 8bit, or binary, per RFC 2045 (6.4). "
1105 "mhfixmsg -fixcte can fix it, or\n"
1106 "manually edit the file and change the \"%s\"\n"
1107 "Content-Transfer-Encoding to one of those. For now",
1108 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1115 ct
->c_subtype
= ct_str_subtype (CT_MULTIPART
, ci
->ci_subtype
);
1118 * Check for "boundary" parameter, which is
1119 * required for multipart messages.
1122 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1123 if (!strcasecmp (pm
->pm_name
, "boundary")) {
1129 /* complain if boundary parameter is missing */
1132 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1133 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1137 /* allocate primary structure for multipart info */
1138 if ((m
= (struct multipart
*) mh_xcalloc (1, sizeof(*m
))) == NULL
)
1139 adios (NULL
, "out of memory");
1140 ct
->c_ctparams
= (void *) m
;
1142 /* check if boundary parameter contains only whitespace characters */
1143 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1146 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1147 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1151 /* remove trailing whitespace from boundary parameter */
1152 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1153 if (!isspace ((unsigned char) *dp
))
1157 /* record boundary separators */
1158 m
->mp_start
= concat (bp
, "\n", NULL
);
1159 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1161 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1162 advise (ct
->c_file
, "unable to open for reading");
1166 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1168 next
= &m
->mp_parts
;
1172 while ((gotlen
= getline(&bufp
, &buflen
, fp
)) != -1) {
1177 if (bufp
[0] != '-' || bufp
[1] != '-')
1180 if (strcmp (bufp
+ 2, m
->mp_start
))
1183 if ((part
= (struct part
*) mh_xcalloc (1, sizeof(*part
))) == NULL
)
1184 adios (NULL
, "out of memory");
1186 next
= &part
->mp_next
;
1188 if (!(p
= get_content (fp
, ct
->c_file
,
1189 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1197 fseek (fp
, pos
, SEEK_SET
);
1200 if (strcmp (bufp
+ 2, m
->mp_start
) == 0) {
1204 p
->c_end
= ftell(fp
) - (gotlen
+ 1);
1205 if (p
->c_end
< p
->c_begin
)
1206 p
->c_begin
= p
->c_end
;
1211 if (strcmp (bufp
+ 2, m
->mp_stop
) == 0)
1217 if (! suppress_bogus_mp_content_warning
) {
1218 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1220 bogus_mp_content
= 1;
1222 if (!inout
&& part
) {
1224 p
->c_end
= ct
->c_end
;
1226 if (p
->c_begin
>= p
->c_end
) {
1227 for (next
= &m
->mp_parts
; *next
!= part
;
1228 next
= &((*next
)->mp_next
))
1232 free ((char *) part
);
1237 /* reverse the order of the parts for multipart/alternative */
1238 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1244 * label all subparts with part number, and
1245 * then initialize the content of the subpart.
1250 char partnam
[BUFSIZ
];
1253 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1254 pp
= partnam
+ strlen (partnam
);
1259 for (part
= m
->mp_parts
, partnum
= 1; part
;
1260 part
= part
->mp_next
, partnum
++) {
1263 sprintf (pp
, "%d", partnum
);
1264 p
->c_partno
= add (partnam
, NULL
);
1266 /* initialize the content of the subparts */
1267 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1276 get_leftover_mp_content (ct
, 1);
1277 get_leftover_mp_content (ct
, 0);
1287 * reverse the order of the parts of a multipart/alternative,
1288 * presumably to put the "most favored" alternative first, for
1289 * ease of choosing/displaying it later on. from a mail message on
1290 * nmh-workers, from kenh:
1291 * "Stock" MH 6.8.5 did not have a reverse_parts() function, but I
1292 * see code in mhn that did the same thing... Acccording to the RCS
1293 * logs, that code was around from the initial checkin of mhn.c by
1294 * John Romine in 1992, which is as far back as we have."
1297 reverse_parts (CT ct
)
1299 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1303 /* Reverse the order of its parts by walking the mp_parts list
1304 and pushing each node to the front. */
1305 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1306 next
= part
->mp_next
;
1307 part
->mp_next
= m
->mp_parts
;
1313 move_preferred_part (CT ct
, char *type
, char *subtype
)
1315 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1316 struct part
*part
, *prev
, *head
, *nhead
, *ntail
;
1320 /* move the matching part(s) to the head of the list: walk the
1321 * list of parts, move matching parts to a new list (maintaining
1322 * their order), and finally, concatenate the old list onto the
1329 head
->mp_next
= m
->mp_parts
;
1330 nhead
->mp_next
= NULL
;
1334 part
= head
->mp_next
;
1335 while (part
!= NULL
) {
1336 ci
= &part
->mp_part
->c_ctinfo
;
1337 if (!strcasecmp(ci
->ci_type
, type
) &&
1338 (!subtype
|| !strcasecmp(ci
->ci_subtype
, subtype
))) {
1339 prev
->mp_next
= part
->mp_next
;
1340 part
->mp_next
= NULL
;
1341 ntail
->mp_next
= part
;
1343 part
= prev
->mp_next
;
1346 part
= prev
->mp_next
;
1349 ntail
->mp_next
= head
->mp_next
;
1350 m
->mp_parts
= nhead
->mp_next
;
1355 * move parts that match the user's preferences (-prefer) to the head
1356 * of the line. process preferences in reverse so first one given
1357 * ends up first in line
1363 for (i
= npreferred
-1; i
>= 0; i
--)
1364 move_preferred_part(ct
, preferred_types
[i
], preferred_subtypes
[i
]);
1369 /* parse_mime() arranges alternates in reverse (priority) order. This
1370 function can be used to reverse them back. This will put, for
1371 example, a text/plain part before a text/html part in a
1372 multipart/alternative part, for example, where it belongs. */
1374 reverse_alternative_parts (CT ct
) {
1375 if (ct
->c_type
== CT_MULTIPART
) {
1376 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1379 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1383 /* And call recursively on each part of a multipart. */
1384 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1385 reverse_alternative_parts (part
->mp_part
);
1398 CI ci
= &ct
->c_ctinfo
;
1400 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1402 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1403 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1407 /* check for missing subtype */
1408 if (!*ci
->ci_subtype
)
1409 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1412 ct
->c_subtype
= ct_str_subtype (CT_MESSAGE
, ci
->ci_subtype
);
1414 switch (ct
->c_subtype
) {
1415 case MESSAGE_RFC822
:
1418 case MESSAGE_PARTIAL
:
1423 if ((p
= (struct partial
*) mh_xcalloc (1, sizeof(*p
))) == NULL
)
1424 adios (NULL
, "out of memory");
1425 ct
->c_ctparams
= (void *) p
;
1427 /* scan for parameters "id", "number", and "total" */
1428 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1429 if (!strcasecmp (pm
->pm_name
, "id")) {
1430 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1433 if (!strcasecmp (pm
->pm_name
, "number")) {
1434 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1435 || p
->pm_partno
< 1) {
1438 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1439 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1440 ct
->c_file
, TYPE_FIELD
);
1445 if (!strcasecmp (pm
->pm_name
, "total")) {
1446 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1455 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1457 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1458 ci
->ci_type
, ci
->ci_subtype
,
1459 ct
->c_file
, TYPE_FIELD
);
1465 case MESSAGE_EXTERNAL
:
1472 if ((e
= (struct exbody
*) mh_xcalloc (1, sizeof(*e
))) == NULL
)
1473 adios (NULL
, "out of memory");
1474 ct
->c_ctparams
= (void *) e
;
1477 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1478 advise (ct
->c_file
, "unable to open for reading");
1482 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1484 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1492 p
->c_ceopenfnx
= NULL
;
1493 if ((exresult
= params_external (ct
, 0)) != NOTOK
1494 && p
->c_ceopenfnx
== openMail
) {
1498 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1500 content_error (NULL
, ct
,
1501 "empty body for access-type=mail-server");
1505 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1506 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1508 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1510 adios ("failed", "fread");
1513 adios (NULL
, "unexpected EOF from fread");
1516 bp
+= cc
, size
-= cc
;
1523 p
->c_end
= p
->c_begin
;
1528 if (exresult
== NOTOK
)
1530 if (e
->eb_flags
== NOTOK
)
1533 switch (p
->c_type
) {
1538 if (p
->c_subtype
!= MESSAGE_RFC822
)
1542 e
->eb_partno
= ct
->c_partno
;
1544 (*p
->c_ctinitfnx
) (p
);
1559 params_external (CT ct
, int composing
)
1562 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1563 CI ci
= &ct
->c_ctinfo
;
1565 ct
->c_ceopenfnx
= NULL
;
1566 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1567 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1568 struct str2init
*s2i
;
1569 CT p
= e
->eb_content
;
1571 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1572 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1575 e
->eb_access
= pm
->pm_value
;
1576 e
->eb_flags
= NOTOK
;
1577 p
->c_encoding
= CE_EXTERNAL
;
1580 e
->eb_access
= s2i
->si_key
;
1581 e
->eb_flags
= s2i
->si_val
;
1582 p
->c_encoding
= CE_EXTERNAL
;
1584 /* Call the Init function for this external type */
1585 if ((*s2i
->si_init
)(p
) == NOTOK
)
1589 if (!strcasecmp (pm
->pm_name
, "name")) {
1590 e
->eb_name
= pm
->pm_value
;
1593 if (!strcasecmp (pm
->pm_name
, "permission")) {
1594 e
->eb_permission
= pm
->pm_value
;
1597 if (!strcasecmp (pm
->pm_name
, "site")) {
1598 e
->eb_site
= pm
->pm_value
;
1601 if (!strcasecmp (pm
->pm_name
, "directory")) {
1602 e
->eb_dir
= pm
->pm_value
;
1605 if (!strcasecmp (pm
->pm_name
, "mode")) {
1606 e
->eb_mode
= pm
->pm_value
;
1609 if (!strcasecmp (pm
->pm_name
, "size")) {
1610 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1613 if (!strcasecmp (pm
->pm_name
, "server")) {
1614 e
->eb_server
= pm
->pm_value
;
1617 if (!strcasecmp (pm
->pm_name
, "subject")) {
1618 e
->eb_subject
= pm
->pm_value
;
1621 if (!strcasecmp (pm
->pm_name
, "url")) {
1623 * According to RFC 2017, we have to remove all whitespace from
1627 char *u
, *p
= pm
->pm_value
;
1628 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1630 for (; *p
!= '\0'; p
++) {
1631 if (! isspace((unsigned char) *p
))
1638 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1639 e
->eb_body
= getcpy (pm
->pm_value
);
1644 if (!e
->eb_access
) {
1646 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1647 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1660 InitApplication (CT ct
)
1662 CI ci
= &ct
->c_ctinfo
;
1665 ct
->c_subtype
= ct_str_subtype (CT_APPLICATION
, ci
->ci_subtype
);
1672 * TRANSFER ENCODINGS
1676 init_encoding (CT ct
, OpenCEFunc openfnx
)
1678 ct
->c_ceopenfnx
= openfnx
;
1679 ct
->c_ceclosefnx
= close_encoding
;
1680 ct
->c_cesizefnx
= size_encoding
;
1687 close_encoding (CT ct
)
1689 CE ce
= &ct
->c_cefile
;
1698 static unsigned long
1699 size_encoding (CT ct
)
1704 CE ce
= &ct
->c_cefile
;
1707 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1708 return (long) st
.st_size
;
1711 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1712 return (long) st
.st_size
;
1717 if (ct
->c_encoding
== CE_EXTERNAL
)
1718 return (ct
->c_end
- ct
->c_begin
);
1721 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1722 return (ct
->c_end
- ct
->c_begin
);
1724 if (fstat (fd
, &st
) != NOTOK
)
1725 size
= (long) st
.st_size
;
1729 (*ct
->c_ceclosefnx
) (ct
);
1738 static unsigned char b642nib
[0x80] = {
1739 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1740 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1741 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1742 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1743 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1744 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1745 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1746 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1747 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1748 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1749 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1750 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1751 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1752 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1753 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1754 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1761 return init_encoding (ct
, openBase64
);
1766 openBase64 (CT ct
, char **file
)
1768 int bitno
, cc
, digested
;
1769 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1771 unsigned char value
, b
;
1772 char *cp
, *ep
, buffer
[BUFSIZ
];
1773 /* sbeck -- handle suffixes */
1775 CE ce
= &ct
->c_cefile
;
1779 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1784 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1785 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1791 if (*file
== NULL
) {
1794 ce
->ce_file
= add (*file
, NULL
);
1798 /* sbeck@cise.ufl.edu -- handle suffixes */
1800 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1801 if (ce
->ce_unlink
) {
1802 /* Create temporary file with filename extension. */
1803 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1804 adios(NULL
, "unable to create temporary file in %s",
1808 ce
->ce_file
= add (cp
, ce
->ce_file
);
1810 } else if (*file
== NULL
) {
1812 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1813 adios(NULL
, "unable to create temporary file in %s",
1816 ce
->ce_file
= add (tempfile
, NULL
);
1819 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1820 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1824 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1825 adios (NULL
, "internal error(1)");
1828 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1829 content_error (ct
->c_file
, ct
, "unable to open for reading");
1835 if ((digested
= ct
->c_digested
))
1836 MD5Init (&mdContext
);
1842 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1844 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1846 content_error (ct
->c_file
, ct
, "error reading from");
1850 content_error (NULL
, ct
, "premature eof");
1858 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1861 if (isspace ((unsigned char) *cp
))
1863 if (skip
|| (((unsigned char) *cp
) & 0x80)
1864 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1866 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1867 (unsigned char) *cp
,
1868 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1871 content_error (NULL
, ct
,
1872 "invalid BASE64 encoding -- continuing");
1876 bits
|= value
<< bitno
;
1878 if ((bitno
-= 6) < 0) {
1879 b
= (bits
>> 16) & 0xff;
1880 if (!text
|| b
!= '\r')
1881 putc ((char) b
, ce
->ce_fp
);
1883 MD5Update (&mdContext
, &b
, 1);
1885 b
= (bits
>> 8) & 0xff;
1886 if (! text
|| b
!= '\r')
1887 putc ((char) b
, ce
->ce_fp
);
1889 MD5Update (&mdContext
, &b
, 1);
1892 if (! text
|| b
!= '\r')
1893 putc ((char) b
, ce
->ce_fp
);
1895 MD5Update (&mdContext
, &b
, 1);
1899 if (ferror (ce
->ce_fp
)) {
1900 content_error (ce
->ce_file
, ct
,
1901 "error writing to");
1904 bitno
= 18, bits
= 0L, skip
= 0;
1910 goto self_delimiting
;
1919 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1921 content_error (NULL
, ct
, "invalid BASE64 encoding");
1926 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1928 if (fflush (ce
->ce_fp
)) {
1929 content_error (ce
->ce_file
, ct
, "error writing to");
1934 unsigned char digest
[16];
1936 MD5Final (digest
, &mdContext
);
1937 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1938 sizeof(digest
) / sizeof(digest
[0])))
1939 content_error (NULL
, ct
,
1940 "content integrity suspect (digest mismatch) -- continuing");
1943 fprintf (stderr
, "content integrity confirmed\n");
1946 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1949 *file
= ce
->ce_file
;
1954 return fileno (ce
->ce_fp
);
1961 free_encoding (ct
, 0);
1970 static char hex2nib
[0x80] = {
1971 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1972 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1973 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1974 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1975 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1976 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1977 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1978 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1979 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1980 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1981 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1982 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1983 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1984 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1985 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1986 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1993 return init_encoding (ct
, openQuoted
);
1998 openQuoted (CT ct
, char **file
)
2000 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
2006 CE ce
= &ct
->c_cefile
;
2007 /* sbeck -- handle suffixes */
2012 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2017 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2018 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2024 if (*file
== NULL
) {
2027 ce
->ce_file
= add (*file
, NULL
);
2031 /* sbeck@cise.ufl.edu -- handle suffixes */
2033 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2034 if (ce
->ce_unlink
) {
2035 /* Create temporary file with filename extension. */
2036 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2037 adios(NULL
, "unable to create temporary file in %s",
2041 ce
->ce_file
= add (cp
, ce
->ce_file
);
2043 } else if (*file
== NULL
) {
2045 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2046 adios(NULL
, "unable to create temporary file in %s",
2049 ce
->ce_file
= add (tempfile
, NULL
);
2052 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2053 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2057 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2058 adios (NULL
, "internal error(2)");
2061 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2062 content_error (ct
->c_file
, ct
, "unable to open for reading");
2068 if ((digested
= ct
->c_digested
))
2069 MD5Init (&mdContext
);
2076 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2078 if ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) == -1) {
2079 content_error (NULL
, ct
, "premature eof");
2083 if ((cc
= gotlen
) > len
)
2087 for (ep
= (cp
= bufp
) + cc
- 1; cp
<= ep
; ep
--)
2088 if (!isspace ((unsigned char) *ep
))
2092 for (; cp
< ep
; cp
++) {
2094 /* in an escape sequence */
2096 /* at byte 1 of an escape sequence */
2097 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2098 /* next is byte 2 */
2101 /* at byte 2 of an escape sequence */
2103 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2104 putc (mask
, ce
->ce_fp
);
2106 MD5Update (&mdContext
, &mask
, 1);
2107 if (ferror (ce
->ce_fp
)) {
2108 content_error (ce
->ce_file
, ct
, "error writing to");
2111 /* finished escape sequence; next may be literal or a new
2112 * escape sequence */
2115 /* on to next byte */
2119 /* not in an escape sequence */
2121 /* starting an escape sequence, or invalid '='? */
2122 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2123 /* "=\n" soft line break, eat the \n */
2127 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2128 /* We don't have 2 bytes left, so this is an invalid
2129 * escape sequence; just show the raw bytes (below). */
2130 } else if (isxdigit ((unsigned char) cp
[1]) &&
2131 isxdigit ((unsigned char) cp
[2])) {
2132 /* Next 2 bytes are hex digits, making this a valid escape
2133 * sequence; let's decode it (above). */
2137 /* One or both of the next 2 is out of range, making this
2138 * an invalid escape sequence; just show the raw bytes
2143 /* Just show the raw byte. */
2144 putc (*cp
, ce
->ce_fp
);
2147 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2149 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2152 if (ferror (ce
->ce_fp
)) {
2153 content_error (ce
->ce_file
, ct
, "error writing to");
2159 content_error (NULL
, ct
,
2160 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2164 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2166 if (fflush (ce
->ce_fp
)) {
2167 content_error (ce
->ce_file
, ct
, "error writing to");
2172 unsigned char digest
[16];
2174 MD5Final (digest
, &mdContext
);
2175 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2176 sizeof(digest
) / sizeof(digest
[0])))
2177 content_error (NULL
, ct
,
2178 "content integrity suspect (digest mismatch) -- continuing");
2181 fprintf (stderr
, "content integrity confirmed\n");
2184 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2187 *file
= ce
->ce_file
;
2193 return fileno (ce
->ce_fp
);
2196 free_encoding (ct
, 0);
2213 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2216 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2222 open7Bit (CT ct
, char **file
)
2224 int cc
, fd
, len
, own_ct_fp
= 0;
2225 char buffer
[BUFSIZ
];
2226 /* sbeck -- handle suffixes */
2229 CE ce
= &ct
->c_cefile
;
2232 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2237 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2238 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2244 if (*file
== NULL
) {
2247 ce
->ce_file
= add (*file
, NULL
);
2251 /* sbeck@cise.ufl.edu -- handle suffixes */
2253 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2254 if (ce
->ce_unlink
) {
2255 /* Create temporary file with filename extension. */
2256 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2257 adios(NULL
, "unable to create temporary file in %s",
2261 ce
->ce_file
= add (cp
, ce
->ce_file
);
2263 } else if (*file
== NULL
) {
2265 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2266 adios(NULL
, "unable to create temporary file in %s",
2269 ce
->ce_file
= add (tempfile
, NULL
);
2272 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2273 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2277 if (ct
->c_type
== CT_MULTIPART
) {
2278 CI ci
= &ct
->c_ctinfo
;
2282 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2283 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2284 + 1 + strlen (ci
->ci_subtype
);
2285 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2288 fputs (buffer
, ce
->ce_fp
);
2292 if (ci
->ci_comment
) {
2293 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2294 fputs ("\n\t", ce
->ce_fp
);
2298 putc (' ', ce
->ce_fp
);
2301 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2304 fprintf (ce
->ce_fp
, "\n");
2306 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2308 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2310 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2311 fprintf (ce
->ce_fp
, "\n");
2314 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2315 adios (NULL
, "internal error(3)");
2318 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2319 content_error (ct
->c_file
, ct
, "unable to open for reading");
2325 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2327 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2329 content_error (ct
->c_file
, ct
, "error reading from");
2333 content_error (NULL
, ct
, "premature eof");
2341 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
) < cc
) {
2342 advise ("open7Bit", "fwrite");
2344 if (ferror (ce
->ce_fp
)) {
2345 content_error (ce
->ce_file
, ct
, "error writing to");
2350 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2352 if (fflush (ce
->ce_fp
)) {
2353 content_error (ce
->ce_file
, ct
, "error writing to");
2357 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2360 *file
= ce
->ce_file
;
2365 return fileno (ce
->ce_fp
);
2368 free_encoding (ct
, 0);
2382 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2384 char cachefile
[BUFSIZ
];
2387 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2392 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2393 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2399 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2400 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2401 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2402 ce
->ce_file
= getcpy (cachefile
);
2406 admonish (cachefile
, "unable to fopen for reading");
2410 *fd
= fileno (ce
->ce_fp
);
2414 *file
= ce
->ce_file
;
2415 *fd
= fileno (ce
->ce_fp
);
2426 return init_encoding (ct
, openFile
);
2431 openFile (CT ct
, char **file
)
2434 char cachefile
[BUFSIZ
];
2435 struct exbody
*e
= ct
->c_ctexbody
;
2436 CE ce
= &ct
->c_cefile
;
2438 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2450 content_error (NULL
, ct
, "missing name parameter");
2454 ce
->ce_file
= getcpy (e
->eb_name
);
2457 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2458 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2462 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2463 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2464 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2468 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2469 if ((fp
= fopen (cachefile
, "w"))) {
2471 char buffer
[BUFSIZ
];
2472 FILE *gp
= ce
->ce_fp
;
2474 fseek (gp
, 0L, SEEK_SET
);
2476 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2478 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2479 advise ("openFile", "fwrite");
2484 admonish (ce
->ce_file
, "error reading");
2485 (void) m_unlink (cachefile
);
2489 admonish (cachefile
, "error writing");
2490 (void) m_unlink (cachefile
);
2497 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2498 *file
= ce
->ce_file
;
2499 return fileno (ce
->ce_fp
);
2509 return init_encoding (ct
, openFTP
);
2514 openFTP (CT ct
, char **file
)
2516 int cachetype
, caching
, fd
;
2518 char *bp
, *ftp
, *user
, *pass
;
2519 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2521 CE ce
= &ct
->c_cefile
;
2522 static char *username
= NULL
;
2523 static char *password
= NULL
;
2527 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2533 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2544 if (!e
->eb_name
|| !e
->eb_site
) {
2545 content_error (NULL
, ct
, "missing %s parameter",
2546 e
->eb_name
? "site": "name");
2550 /* Get the buffer ready to go */
2552 buflen
= sizeof(buffer
);
2555 * Construct the query message for user
2557 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2563 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2569 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2570 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2575 if (e
->eb_size
> 0) {
2576 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2581 snprintf (bp
, buflen
, "? ");
2584 * Now, check the answer
2586 if (!read_yes_or_no_if_tty (buffer
))
2591 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2595 ruserpass (e
->eb_site
, &username
, &password
);
2600 ce
->ce_unlink
= (*file
== NULL
);
2602 cachefile
[0] = '\0';
2603 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2604 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2605 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2606 if (*file
== NULL
) {
2613 ce
->ce_file
= add (*file
, NULL
);
2615 ce
->ce_file
= add (cachefile
, NULL
);
2618 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2619 adios(NULL
, "unable to create temporary file in %s",
2622 ce
->ce_file
= add (tempfile
, NULL
);
2625 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2626 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2631 int child_id
, i
, vecp
;
2635 vec
[vecp
++] = r1bindex (ftp
, '/');
2636 vec
[vecp
++] = e
->eb_site
;
2639 vec
[vecp
++] = e
->eb_dir
;
2640 vec
[vecp
++] = e
->eb_name
;
2641 vec
[vecp
++] = ce
->ce_file
,
2642 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2643 ? "ascii" : "binary";
2648 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2652 adios ("fork", "unable to");
2656 close (fileno (ce
->ce_fp
));
2658 fprintf (stderr
, "unable to exec ");
2664 if (pidXwait (child_id
, NULL
)) {
2665 username
= password
= NULL
;
2675 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2680 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2681 if ((fp
= fopen (cachefile
, "w"))) {
2683 FILE *gp
= ce
->ce_fp
;
2685 fseek (gp
, 0L, SEEK_SET
);
2687 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2689 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2690 advise ("openFTP", "fwrite");
2695 admonish (ce
->ce_file
, "error reading");
2696 (void) m_unlink (cachefile
);
2700 admonish (cachefile
, "error writing");
2701 (void) m_unlink (cachefile
);
2709 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2710 *file
= ce
->ce_file
;
2711 return fileno (ce
->ce_fp
);
2722 return init_encoding (ct
, openMail
);
2727 openMail (CT ct
, char **file
)
2729 int child_id
, fd
, i
, vecp
;
2731 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2732 struct exbody
*e
= ct
->c_ctexbody
;
2733 CE ce
= &ct
->c_cefile
;
2735 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2746 if (!e
->eb_server
) {
2747 content_error (NULL
, ct
, "missing server parameter");
2751 /* Get buffer ready to go */
2753 buflen
= sizeof(buffer
);
2755 /* Now, construct query message */
2756 snprintf (bp
, buflen
, "Retrieve content");
2762 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2768 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2770 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2772 /* Now, check answer */
2773 if (!read_yes_or_no_if_tty (buffer
))
2777 vec
[vecp
++] = r1bindex (mailproc
, '/');
2778 vec
[vecp
++] = e
->eb_server
;
2779 vec
[vecp
++] = "-subject";
2780 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2781 vec
[vecp
++] = "-body";
2782 vec
[vecp
++] = e
->eb_body
;
2785 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2789 advise ("fork", "unable to");
2793 execvp (mailproc
, vec
);
2794 fprintf (stderr
, "unable to exec ");
2800 if (pidXwait (child_id
, NULL
) == OK
)
2801 advise (NULL
, "request sent");
2805 if (*file
== NULL
) {
2807 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2808 adios(NULL
, "unable to create temporary file in %s",
2811 ce
->ce_file
= add (tempfile
, NULL
);
2814 ce
->ce_file
= add (*file
, NULL
);
2818 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2819 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2823 /* showproc is for mhshow and mhstore, though mhlist -debug
2824 * prints it, too. */
2826 free (ct
->c_showproc
);
2827 ct
->c_showproc
= add ("true", NULL
);
2829 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2830 *file
= ce
->ce_file
;
2831 return fileno (ce
->ce_fp
);
2842 return init_encoding (ct
, openURL
);
2847 openURL (CT ct
, char **file
)
2849 struct exbody
*e
= ct
->c_ctexbody
;
2850 CE ce
= &ct
->c_cefile
;
2851 char *urlprog
, *program
;
2852 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2853 int fd
, caching
, cachetype
;
2854 struct msgs_array args
= { 0, 0, NULL
};
2857 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2861 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2865 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2877 content_error(NULL
, ct
, "missing url parameter");
2881 ce
->ce_unlink
= (*file
== NULL
);
2883 cachefile
[0] = '\0';
2885 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2886 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2887 if (*file
== NULL
) {
2894 ce
->ce_file
= add(*file
, NULL
);
2896 ce
->ce_file
= add(cachefile
, NULL
);
2899 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2900 adios(NULL
, "unable to create temporary file in %s",
2903 ce
->ce_file
= add (tempfile
, NULL
);
2906 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2907 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2911 switch (child_id
= fork()) {
2913 adios ("fork", "unable to");
2917 argsplit_msgarg(&args
, urlprog
, &program
);
2918 app_msgarg(&args
, e
->eb_url
);
2919 app_msgarg(&args
, NULL
);
2920 dup2(fileno(ce
->ce_fp
), 1);
2921 close(fileno(ce
->ce_fp
));
2922 execvp(program
, args
.msgs
);
2923 fprintf(stderr
, "Unable to exec ");
2929 if (pidXwait(child_id
, NULL
)) {
2937 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2942 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2943 if ((fp
= fopen(cachefile
, "w"))) {
2945 FILE *gp
= ce
->ce_fp
;
2947 fseeko(gp
, 0, SEEK_SET
);
2949 while ((cc
= fread(buffer
, sizeof(*buffer
),
2950 sizeof(buffer
), gp
)) > 0)
2951 if ((int) fwrite(buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2952 advise ("openURL", "fwrite");
2958 admonish(ce
->ce_file
, "error reading");
2959 (void) m_unlink (cachefile
);
2966 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2967 *file
= ce
->ce_file
;
2972 readDigest (CT ct
, char *cp
)
2977 unsigned char *dp
, value
, *ep
;
2983 for (ep
= (dp
= ct
->c_digest
)
2984 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2989 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2991 fprintf (stderr
, "invalid BASE64 encoding\n");
2995 bits
|= value
<< bitno
;
2997 if ((bitno
-= 6) < 0) {
2998 if (dp
+ (3 - skip
) > ep
)
2999 goto invalid_digest
;
3000 *dp
++ = (bits
>> 16) & 0xff;
3002 *dp
++ = (bits
>> 8) & 0xff;
3004 *dp
++ = bits
& 0xff;
3014 goto self_delimiting
;
3019 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
3029 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
3037 fprintf (stderr
, "MD5 digest=");
3038 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
3039 fprintf (stderr
, "%02x", *dp
& 0xff);
3040 fprintf (stderr
, "\n");
3047 /* Multipart parts might have content before the first subpart and/or
3048 after the last subpart that hasn't been stored anywhere else, so do
3051 get_leftover_mp_content (CT ct
, int before
/* or after */)
3053 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
3055 int found_boundary
= 0;
3061 char *content
= NULL
;
3063 if (! m
) return NOTOK
;
3066 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
3068 /* Isolate the beginning of this part to the beginning of the
3069 first subpart and save any content between them. */
3070 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
3071 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3072 boundary
= concat ("--", m
->mp_start
, NULL
);
3074 struct part
*last_subpart
= NULL
;
3075 struct part
*subpart
;
3077 /* Go to the last subpart to get its end position. */
3078 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3079 last_subpart
= subpart
;
3082 if (last_subpart
== NULL
) return NOTOK
;
3084 /* Isolate the end of the last subpart to the end of this part
3085 and save any content between them. */
3086 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3087 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3088 boundary
= concat ("--", m
->mp_stop
, NULL
);
3091 /* Back up by 1 to pick up the newline. */
3092 while ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) != -1) {
3094 /* Don't look beyond beginning of first subpart (before) or
3095 next part (after). */
3096 if (read
> max
) bufp
[read
-max
] = '\0';
3099 if (! strcmp (bufp
, boundary
)) {
3103 if (! found_boundary
&& ! strcmp (bufp
, boundary
)) {
3109 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3111 char *old_content
= content
;
3112 content
= concat (content
, bufp
, NULL
);
3116 ? concat ("\n", bufp
, NULL
)
3117 : concat (bufp
, NULL
);
3122 if (found_boundary
|| read
> max
) break;
3124 if (read
> max
) break;
3128 /* Skip the newline if that's all there is. */
3132 /* Remove trailing newline, except at EOF. */
3133 if ((before
|| ! feof (ct
->c_fp
)) &&
3134 (cp
= content
+ strlen (content
)) > content
&&
3139 if (strlen (content
) > 1) {
3141 m
->mp_content_before
= content
;
3143 m
->mp_content_after
= content
;
3158 ct_type_str (int type
) {
3160 case CT_APPLICATION
:
3161 return "application";
3177 return "unknown_type";
3183 ct_subtype_str (int type
, int subtype
) {
3185 case CT_APPLICATION
:
3187 case APPLICATION_OCTETS
:
3189 case APPLICATION_POSTSCRIPT
:
3190 return "postscript";
3192 return "unknown_app_subtype";
3196 case MESSAGE_RFC822
:
3198 case MESSAGE_PARTIAL
:
3200 case MESSAGE_EXTERNAL
:
3203 return "unknown_msg_subtype";
3209 case MULTI_ALTERNATE
:
3210 return "alternative";
3213 case MULTI_PARALLEL
:
3218 return "unknown_multipart_subtype";
3229 return "unknown_text_subtype";
3232 return "unknown_type";
3238 ct_str_type (const char *type
) {
3239 struct str2init
*s2i
;
3241 for (s2i
= str2cts
; s2i
->si_key
; ++s2i
) {
3242 if (! strcasecmp (type
, s2i
->si_key
)) {
3246 if (! s2i
->si_key
&& ! uprf (type
, "X-")) {
3255 ct_str_subtype (int type
, const char *subtype
) {
3259 case CT_APPLICATION
:
3260 for (kv
= SubApplication
; kv
->kv_key
; ++kv
) {
3261 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3265 return kv
->kv_value
;
3267 for (kv
= SubMessage
; kv
->kv_key
; ++kv
) {
3268 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3272 return kv
->kv_value
;
3274 for (kv
= SubMultiPart
; kv
->kv_key
; ++kv
) {
3275 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3279 return kv
->kv_value
;
3281 for (kv
= SubText
; kv
->kv_key
; ++kv
) {
3282 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3286 return kv
->kv_value
;
3293 /* Find the content type and InitFunc for the CT. */
3294 const struct str2init
*
3295 get_ct_init (int type
) {
3296 const struct str2init
*sp
;
3298 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3299 if (type
== sp
->si_val
) {
3308 ce_str (int encoding
) {
3313 return "quoted-printable";
3329 /* Find the content type and InitFunc for the content encoding method. */
3330 const struct str2init
*
3331 get_ce_method (const char *method
) {
3332 struct str2init
*sp
;
3334 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3335 if (! strcasecmp (method
, sp
->si_key
)) {
3344 * Parse a series of MIME attributes (or parameters) given a header as
3347 * Arguments include:
3349 * filename - Name of input file (for error messages)
3350 * fieldname - Name of field being processed
3351 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3352 * Updated to point to end of attributes when finished.
3353 * param_head - Pointer to head of parameter list
3354 * param_tail - Pointer to tail of parameter list
3355 * commentp - Pointer to header comment pointer (may be NULL)
3357 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3358 * DONE to indicate a benign error (minor parsing error, but the program
3363 parse_header_attrs (const char *filename
, const char *fieldname
,
3364 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3367 char *cp
= *header_attrp
;
3373 struct sectlist
*next
;
3379 struct sectlist
*sechead
;
3380 struct parmlist
*next
;
3381 } *pp
, *pp2
, *phead
= NULL
;
3383 while (*cp
== ';') {
3384 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3385 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3388 while (isspace ((unsigned char) *cp
))
3392 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3397 if (! suppress_extraneous_trailing_semicolon_warning
) {
3399 "extraneous trailing ';' in message %s's %s: "
3401 filename
, fieldname
);
3403 extraneous_trailing_semicolon
= 1;
3407 /* down case the attribute name */
3408 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3409 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3410 *dp
= tolower ((unsigned char) *dp
);
3412 for (up
= dp
; isspace ((unsigned char) *dp
);)
3414 if (dp
== cp
|| *dp
!= '=') {
3416 "invalid parameter in message %s's %s: "
3417 "field\n%*sparameter %s (error detected at offset %d)",
3418 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3423 * To handle RFC 2231, we have to deal with the following extensions:
3425 * name*=encoded-value
3426 * name*<N>=part-N-of-a-parameter-value
3427 * name*<N>*=encoded-part-N-of-a-parameter-value
3430 * If there's a * right before the equal sign, it's encoded.
3431 * If there's a * and one or more digits, then it's section N.
3433 * Remember we can have one or the other, or both. cp points to
3434 * beginning of name, up points past the last character in the
3438 for (vp
= cp
; vp
< up
; vp
++) {
3439 if (*vp
== '*' && vp
< up
- 1) {
3442 } else if (*vp
== '*' && vp
== up
- 1) {
3444 } else if (partial
) {
3445 if (isdigit((unsigned char) *vp
))
3446 index
= *vp
- '0' + index
* 10;
3448 advise (NULL
, "invalid parameter index in message %s's "
3449 "%s: field\n%*s(parameter %s)", filename
,
3450 fieldname
, strlen(invo_name
) + 2, "", cp
);
3459 * Break out the parameter name and value sections and allocate
3463 nameptr
= mh_xmalloc(len
+ 1);
3464 strncpy(nameptr
, cp
, len
);
3465 nameptr
[len
] = '\0';
3467 for (dp
++; isspace ((unsigned char) *dp
);)
3472 * Single quotes delimit the character set and language tag.
3473 * They are required on the first section (or a complete
3478 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3484 charset
= mh_xmalloc(len
+ 1);
3485 strncpy(charset
, dp
, len
);
3486 charset
[len
] = '\0';
3492 advise(NULL
, "missing charset in message %s's %s: "
3493 "field\n%*s(parameter %s)", filename
, fieldname
,
3494 strlen(invo_name
) + 2, "", nameptr
);
3500 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3507 lang
= mh_xmalloc(len
+ 1);
3508 strncpy(lang
, dp
, len
);
3515 advise(NULL
, "missing language tag in message %s's %s: "
3516 "field\n%*s(parameter %s)", filename
, fieldname
,
3517 strlen(invo_name
) + 2, "", nameptr
);
3528 * At this point vp should be pointing at the beginning
3529 * of the encoded value/section. Continue until we reach
3530 * the end or get whitespace. But first, calculate the
3531 * length so we can allocate the correct buffer size.
3534 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3536 if (*(vp
+ 1) == '\0' ||
3537 !isxdigit((unsigned char) *(vp
+ 1)) ||
3538 *(vp
+ 2) == '\0' ||
3539 !isxdigit((unsigned char) *(vp
+ 2))) {
3540 advise(NULL
, "invalid encoded sequence in message "
3541 "%s's %s: field\n%*s(parameter %s)",
3542 filename
, fieldname
, strlen(invo_name
) + 2,
3556 up
= valptr
= mh_xmalloc(len
+ 1);
3558 for (vp
= dp
; istoken(*vp
); vp
++) {
3560 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3571 * A "normal" string. If it's got a leading quote, then we
3572 * strip the quotes out. Otherwise go until we reach the end
3573 * or get whitespace. Note we scan it twice; once to get the
3574 * length, then the second time copies it into the destination
3581 for (cp
= dp
+ 1;;) {
3586 "invalid quoted-string in message %s's %s: "
3587 "field\n%*s(parameter %s)",
3588 filename
, fieldname
, strlen(invo_name
) + 2, "",
3611 for (cp
= dp
; istoken (*cp
); cp
++) {
3616 valptr
= mh_xmalloc(len
+ 1);
3620 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3628 strncpy(valptr
, cp
= dp
, len
);
3636 * If 'partial' is set, we don't allocate a parameter now. We
3637 * put it on the parameter linked list to be reassembled later.
3639 * "phead" points to a list of all parameters we need to reassemble.
3640 * Each parameter has a list of sections. We insert the sections in
3645 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3646 if (strcasecmp(nameptr
, pp
->name
) == 0)
3651 pp
= mh_xmalloc(sizeof(*pp
));
3652 memset(pp
, 0, sizeof(*pp
));
3659 * Insert this into the section linked list
3662 sp
= mh_xmalloc(sizeof(*sp
));
3663 memset(sp
, 0, sizeof(*sp
));
3668 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3669 sp
->next
= pp
->sechead
;
3672 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3673 if (sp2
->index
== sp
->index
) {
3674 advise (NULL
, "duplicate index (%d) in message "
3675 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3676 filename
, fieldname
, strlen(invo_name
) + 2, "",
3681 if (sp2
->index
< sp
->index
&&
3682 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3683 sp
->next
= sp2
->next
;
3690 advise(NULL
, "Internal error: cannot insert partial "
3691 "param in message %s's %s: field\n%*s(parameter %s)",
3692 filename
, fieldname
, strlen(invo_name
) + 2, "",
3700 * Save our charset and lang tags.
3703 if (index
== 0 && encoded
) {
3706 pp
->charset
= charset
;
3712 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3713 pm
->pm_charset
= charset
;
3717 while (isspace ((unsigned char) *cp
))
3721 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3727 * Now that we're done, reassemble all of the partial parameters.
3730 for (pp
= phead
; pp
!= NULL
; ) {
3734 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3735 if (sp
->index
!= pindex
++) {
3736 advise(NULL
, "missing section %d for parameter in "
3737 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3738 filename
, fieldname
, strlen(invo_name
) + 2, "",
3745 p
= q
= mh_xmalloc(tlen
+ 1);
3746 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3747 memcpy(q
, sp
->value
, sp
->len
);
3757 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3758 pm
->pm_charset
= pp
->charset
;
3759 pm
->pm_lang
= pp
->lang
;
3770 * Return the charset for a particular content type.
3774 content_charset (CT ct
) {
3775 char *ret_charset
= NULL
;
3777 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3779 return ret_charset
? ret_charset
: getcpy ("US-ASCII");
3784 * Create a string based on a list of output parameters. Assume that this
3785 * parameter string will be appended to an existing header, so start out
3786 * with the separator (;). Perform RFC 2231 encoding when necessary.
3790 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3792 char *paramout
= NULL
;
3793 char line
[CPERLIN
* 2], *q
;
3794 int curlen
, index
, cont
, encode
, i
;
3795 size_t valoff
, numchars
;
3797 while (params
!= NULL
) {
3803 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3806 if (strlen(params
->pm_name
) > CPERLIN
) {
3807 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3813 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3816 * Loop until we get a parameter that fits within a line. We
3817 * assume new lines start with a tab, so check our overflow based
3827 * At this point we're definitely continuing the line, so
3828 * be sure to include the parameter name and section index.
3831 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3832 params
->pm_name
, index
);
3835 * Both of these functions do a NUL termination
3839 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3840 numchars
, valoff
, index
);
3842 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3853 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3858 * "line" starts with a ;\n\t, so that doesn't count against
3859 * the length. But add 8 since it starts with a tab; that's
3860 * how we end up with 5.
3863 initialwidth
= strlen(line
) + 5;
3866 * At this point the line should be built, so add it to our
3867 * current output buffer.
3870 paramout
= add(line
, paramout
);
3874 * If this won't fit on the line, start a new one. Save room in
3875 * case we need a semicolon on the end
3878 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3890 * At this point, we're either finishing a contined parameter, or
3891 * we're working on a new one.
3895 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3896 params
->pm_name
, index
);
3898 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3903 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3904 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3906 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3907 strlen(params
->pm_value
+ valoff
), valoff
);
3915 paramout
= add(line
, paramout
);
3916 initialwidth
+= strlen(line
);
3918 params
= params
->pm_next
;
3922 *offsetout
= initialwidth
;
3928 * Calculate the size of a parameter.
3932 * pm - The parameter being output
3933 * index - If continuing the parameter, the index of the section
3935 * valueoff - The current offset into the parameter value that we're
3936 * working on (previous sections have consumed valueoff bytes).
3937 * encode - Set if we should perform encoding on this parameter section
3938 * (given that we're consuming bytesfit bytes).
3939 * cont - Set if the remaining data in value will not fit on a single
3940 * line and will need to be continued.
3941 * bytesfit - The number of bytes that we can consume from the parameter
3942 * value and still fit on a completely new line. The
3943 * calculation assumes the new line starts with a tab,
3944 * includes the parameter name and any encoding, and fits
3945 * within CPERLIN bytes. Will always be at least 1.
3949 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3952 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3953 size_t len
= 0, fit
= 0;
3954 int fitlimit
= 0, eightbit
, maxfit
;
3959 * Add up the length. First, start with the parameter name.
3962 len
= strlen(pm
->pm_name
);
3965 * Scan the parameter value and see if we need to do encoding for this
3969 eightbit
= contains8bit(start
, NULL
);
3972 * Determine if we need to encode this section. Encoding is necessary if:
3974 * - There are any 8-bit characters at all and we're on the first
3976 * - There are 8-bit characters within N bytes of our section start.
3977 * N is calculated based on the number of bytes it would take to
3978 * reach CPERLIN. Specifically:
3979 * 8 (starting tab) +
3980 * strlen(param name) +
3981 * 4 ('* for section marker, '=', opening/closing '"')
3983 * is the number of bytes used by everything that isn't part of the
3984 * value. So that gets subtracted from CPERLIN.
3987 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3988 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3989 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3993 len
++; /* Add in equal sign */
3997 * We're using maxfit as a marker for how many characters we can
3998 * fit into the line. Bump it by two because we're not using quotes
4005 * If we don't have a charset or language tag in this parameter,
4009 if (! pm
->pm_charset
) {
4010 pm
->pm_charset
= getcpy(write_charset_8bit());
4011 if (strcasecmp(pm
->pm_charset
, "US-ASCII") == 0)
4012 adios(NULL
, "8-bit characters in parameter \"%s\", but "
4013 "local character set is US-ASCII", pm
->pm_name
);
4016 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
4018 len
++; /* For the encoding marker */
4021 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
4026 * We know we definitely need to include an index. maxfit already
4027 * includes the section marker.
4029 len
+= strlen(indexchar
);
4031 for (p
= start
; *p
!= '\0'; p
++) {
4032 if (isparamencode(*p
)) {
4040 * Just so there's no confusion: maxfit is counting OUTPUT
4041 * characters (post-encoding). fit is counting INPUT characters.
4043 if (! fitlimit
&& maxfit
>= 0)
4045 else if (! fitlimit
)
4050 * Calculate the string length, but add room for quoting \
4051 * and " if necessary. Also account for quotes at beginning
4054 for (p
= start
; *p
!= '\0'; p
++) {
4065 if (! fitlimit
&& maxfit
>= 0)
4067 else if (! fitlimit
)
4084 * Output an encoded parameter string.
4088 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4089 size_t valueoff
, int index
)
4091 size_t outlen
= 0, n
;
4092 char *endptr
= output
+ len
, *p
;
4095 * First, output the marker for an encoded string.
4103 * If the index is 0, output the character set and language tag.
4104 * If theses were NULL, they should have already been filled in
4109 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
4113 if (output
> endptr
) {
4114 advise(NULL
, "Internal error: parameter buffer overflow");
4120 * Copy over the value, encoding if necessary
4123 p
= pm
->pm_value
+ valueoff
;
4124 while (valuelen
-- > 0) {
4125 if (isparamencode(*p
)) {
4126 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
4133 if (output
> endptr
) {
4134 advise(NULL
, "Internal error: parameter buffer overflow");
4145 * Output a "normal" parameter, without encoding. Be sure to escape
4146 * quotes and backslashes if necessary.
4150 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4154 char *endptr
= output
+ len
, *p
;
4160 p
= pm
->pm_value
+ valueoff
;
4162 while (valuelen
-- > 0) {
4172 if (output
> endptr
) {
4173 advise(NULL
, "Internal error: parameter buffer overflow");
4178 if (output
- 2 > endptr
) {
4179 advise(NULL
, "Internal error: parameter buffer overflow");
4190 * Add a parameter to the parameter linked list
4194 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4196 PM pm
= mh_xmalloc(sizeof(*pm
));
4198 memset(pm
, 0, sizeof(*pm
));
4200 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4201 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4204 (*last
)->pm_next
= pm
;
4215 * Either replace a current parameter with a new value, or add the parameter
4216 * to the parameter linked list.
4220 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4224 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4225 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4227 * If nocopy is set, it's assumed that we own both name
4228 * and value. We don't need name, so we discard it now.
4233 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4238 return add_param(first
, last
, name
, value
, nocopy
);
4242 * Retrieve a parameter value from a parameter linked list. If the parameter
4243 * value needs converted to the local character set, do that now.
4247 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4249 while (first
!= NULL
) {
4250 if (strcasecmp(name
, first
->pm_name
) == 0) {
4252 return first
->pm_value
;
4254 return getcpy(get_param_value(first
, replace
));
4256 first
= first
->pm_next
;
4263 * Return a parameter value, converting to the local character set if
4267 char *get_param_value(PM pm
, char replace
)
4269 static char buffer
[4096]; /* I hope no parameters are larger */
4270 size_t bufsize
= sizeof(buffer
);
4275 ICONV_CONST
char *p
;
4276 #else /* HAVE_ICONV */
4278 #endif /* HAVE_ICONV */
4283 * If we don't have a character set indicated, it's assumed to be
4284 * US-ASCII. If it matches our character set, we don't need to convert
4288 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4289 strlen(pm
->pm_charset
))) {
4290 return pm
->pm_value
;
4294 * In this case, we need to convert. If we have iconv support, use
4295 * that. Otherwise, go through and simply replace every non-ASCII
4296 * character with the substitution character.
4301 bufsize
= sizeof(buffer
);
4302 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4304 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4305 if (cd
== (iconv_t
) -1) {
4309 inbytes
= strlen(pm
->pm_value
);
4313 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4314 if (errno
!= EILSEQ
) {
4319 * Reset shift state, substitute our character,
4320 * try to restart conversion.
4323 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4336 for (++p
, --inbytes
;
4337 inbytes
> 0 && (((unsigned char) *p
) & 0xc0) == 0x80;
4356 #endif /* HAVE_ICONV */
4359 * Take everything non-ASCII and substituite the replacement character
4363 bufsize
= sizeof(buffer
);
4364 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4365 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))