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). One workaround "
1105 "is to\nmanually edit the file and change the \"%s\"\n"
1106 "Content-Transfer-Encoding to one of those. For now",
1107 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1114 ct
->c_subtype
= ct_str_subtype (CT_MULTIPART
, ci
->ci_subtype
);
1117 * Check for "boundary" parameter, which is
1118 * required for multipart messages.
1121 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1122 if (!strcasecmp (pm
->pm_name
, "boundary")) {
1128 /* complain if boundary parameter is missing */
1131 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1132 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1136 /* allocate primary structure for multipart info */
1137 if ((m
= (struct multipart
*) mh_xcalloc (1, sizeof(*m
))) == NULL
)
1138 adios (NULL
, "out of memory");
1139 ct
->c_ctparams
= (void *) m
;
1141 /* check if boundary parameter contains only whitespace characters */
1142 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1145 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1146 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1150 /* remove trailing whitespace from boundary parameter */
1151 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1152 if (!isspace ((unsigned char) *dp
))
1156 /* record boundary separators */
1157 m
->mp_start
= concat (bp
, "\n", NULL
);
1158 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1160 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1161 advise (ct
->c_file
, "unable to open for reading");
1165 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1167 next
= &m
->mp_parts
;
1171 while ((gotlen
= getline(&bufp
, &buflen
, fp
)) != -1) {
1176 if (bufp
[0] != '-' || bufp
[1] != '-')
1179 if (strcmp (bufp
+ 2, m
->mp_start
))
1182 if ((part
= (struct part
*) mh_xcalloc (1, sizeof(*part
))) == NULL
)
1183 adios (NULL
, "out of memory");
1185 next
= &part
->mp_next
;
1187 if (!(p
= get_content (fp
, ct
->c_file
,
1188 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1196 fseek (fp
, pos
, SEEK_SET
);
1199 if (strcmp (bufp
+ 2, m
->mp_start
) == 0) {
1203 p
->c_end
= ftell(fp
) - (gotlen
+ 1);
1204 if (p
->c_end
< p
->c_begin
)
1205 p
->c_begin
= p
->c_end
;
1210 if (strcmp (bufp
+ 2, m
->mp_stop
) == 0)
1216 if (! suppress_bogus_mp_content_warning
) {
1217 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1219 bogus_mp_content
= 1;
1221 if (!inout
&& part
) {
1223 p
->c_end
= ct
->c_end
;
1225 if (p
->c_begin
>= p
->c_end
) {
1226 for (next
= &m
->mp_parts
; *next
!= part
;
1227 next
= &((*next
)->mp_next
))
1231 free ((char *) part
);
1236 /* reverse the order of the parts for multipart/alternative */
1237 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1243 * label all subparts with part number, and
1244 * then initialize the content of the subpart.
1249 char partnam
[BUFSIZ
];
1252 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1253 pp
= partnam
+ strlen (partnam
);
1258 for (part
= m
->mp_parts
, partnum
= 1; part
;
1259 part
= part
->mp_next
, partnum
++) {
1262 sprintf (pp
, "%d", partnum
);
1263 p
->c_partno
= add (partnam
, NULL
);
1265 /* initialize the content of the subparts */
1266 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1275 get_leftover_mp_content (ct
, 1);
1276 get_leftover_mp_content (ct
, 0);
1286 * reverse the order of the parts of a multipart/alternative,
1287 * presumably to put the "most favored" alternative first, for
1288 * ease of choosing/displaying it later on. from a mail message on
1289 * nmh-workers, from kenh:
1290 * "Stock" MH 6.8.5 did not have a reverse_parts() function, but I
1291 * see code in mhn that did the same thing... Acccording to the RCS
1292 * logs, that code was around from the initial checkin of mhn.c by
1293 * John Romine in 1992, which is as far back as we have."
1296 reverse_parts (CT ct
)
1298 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1302 /* Reverse the order of its parts by walking the mp_parts list
1303 and pushing each node to the front. */
1304 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1305 next
= part
->mp_next
;
1306 part
->mp_next
= m
->mp_parts
;
1312 move_preferred_part (CT ct
, char *type
, char *subtype
)
1314 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1315 struct part
*part
, *prev
, *head
, *nhead
, *ntail
;
1319 /* move the matching part(s) to the head of the list: walk the
1320 * list of parts, move matching parts to a new list (maintaining
1321 * their order), and finally, concatenate the old list onto the
1328 head
->mp_next
= m
->mp_parts
;
1329 nhead
->mp_next
= NULL
;
1333 part
= head
->mp_next
;
1334 while (part
!= NULL
) {
1335 ci
= &part
->mp_part
->c_ctinfo
;
1336 if (!strcasecmp(ci
->ci_type
, type
) &&
1337 (!subtype
|| !strcasecmp(ci
->ci_subtype
, subtype
))) {
1338 prev
->mp_next
= part
->mp_next
;
1339 part
->mp_next
= NULL
;
1340 ntail
->mp_next
= part
;
1342 part
= prev
->mp_next
;
1345 part
= prev
->mp_next
;
1348 ntail
->mp_next
= head
->mp_next
;
1349 m
->mp_parts
= nhead
->mp_next
;
1354 * move parts that match the user's preferences (-prefer) to the head
1355 * of the line. process preferences in reverse so first one given
1356 * ends up first in line
1362 for (i
= npreferred
-1; i
>= 0; i
--)
1363 move_preferred_part(ct
, preferred_types
[i
], preferred_subtypes
[i
]);
1368 /* parse_mime() arranges alternates in reverse (priority) order. This
1369 function can be used to reverse them back. This will put, for
1370 example, a text/plain part before a text/html part in a
1371 multipart/alternative part, for example, where it belongs. */
1373 reverse_alternative_parts (CT ct
) {
1374 if (ct
->c_type
== CT_MULTIPART
) {
1375 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1378 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1382 /* And call recursively on each part of a multipart. */
1383 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1384 reverse_alternative_parts (part
->mp_part
);
1397 CI ci
= &ct
->c_ctinfo
;
1399 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1401 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1402 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1406 /* check for missing subtype */
1407 if (!*ci
->ci_subtype
)
1408 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1411 ct
->c_subtype
= ct_str_subtype (CT_MESSAGE
, ci
->ci_subtype
);
1413 switch (ct
->c_subtype
) {
1414 case MESSAGE_RFC822
:
1417 case MESSAGE_PARTIAL
:
1422 if ((p
= (struct partial
*) mh_xcalloc (1, sizeof(*p
))) == NULL
)
1423 adios (NULL
, "out of memory");
1424 ct
->c_ctparams
= (void *) p
;
1426 /* scan for parameters "id", "number", and "total" */
1427 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1428 if (!strcasecmp (pm
->pm_name
, "id")) {
1429 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1432 if (!strcasecmp (pm
->pm_name
, "number")) {
1433 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1434 || p
->pm_partno
< 1) {
1437 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1438 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1439 ct
->c_file
, TYPE_FIELD
);
1444 if (!strcasecmp (pm
->pm_name
, "total")) {
1445 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1454 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1456 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1457 ci
->ci_type
, ci
->ci_subtype
,
1458 ct
->c_file
, TYPE_FIELD
);
1464 case MESSAGE_EXTERNAL
:
1471 if ((e
= (struct exbody
*) mh_xcalloc (1, sizeof(*e
))) == NULL
)
1472 adios (NULL
, "out of memory");
1473 ct
->c_ctparams
= (void *) e
;
1476 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1477 advise (ct
->c_file
, "unable to open for reading");
1481 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1483 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1491 p
->c_ceopenfnx
= NULL
;
1492 if ((exresult
= params_external (ct
, 0)) != NOTOK
1493 && p
->c_ceopenfnx
== openMail
) {
1497 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1499 content_error (NULL
, ct
,
1500 "empty body for access-type=mail-server");
1504 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1505 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1507 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1509 adios ("failed", "fread");
1512 adios (NULL
, "unexpected EOF from fread");
1515 bp
+= cc
, size
-= cc
;
1522 p
->c_end
= p
->c_begin
;
1527 if (exresult
== NOTOK
)
1529 if (e
->eb_flags
== NOTOK
)
1532 switch (p
->c_type
) {
1537 if (p
->c_subtype
!= MESSAGE_RFC822
)
1541 e
->eb_partno
= ct
->c_partno
;
1543 (*p
->c_ctinitfnx
) (p
);
1558 params_external (CT ct
, int composing
)
1561 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1562 CI ci
= &ct
->c_ctinfo
;
1564 ct
->c_ceopenfnx
= NULL
;
1565 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1566 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1567 struct str2init
*s2i
;
1568 CT p
= e
->eb_content
;
1570 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1571 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1574 e
->eb_access
= pm
->pm_value
;
1575 e
->eb_flags
= NOTOK
;
1576 p
->c_encoding
= CE_EXTERNAL
;
1579 e
->eb_access
= s2i
->si_key
;
1580 e
->eb_flags
= s2i
->si_val
;
1581 p
->c_encoding
= CE_EXTERNAL
;
1583 /* Call the Init function for this external type */
1584 if ((*s2i
->si_init
)(p
) == NOTOK
)
1588 if (!strcasecmp (pm
->pm_name
, "name")) {
1589 e
->eb_name
= pm
->pm_value
;
1592 if (!strcasecmp (pm
->pm_name
, "permission")) {
1593 e
->eb_permission
= pm
->pm_value
;
1596 if (!strcasecmp (pm
->pm_name
, "site")) {
1597 e
->eb_site
= pm
->pm_value
;
1600 if (!strcasecmp (pm
->pm_name
, "directory")) {
1601 e
->eb_dir
= pm
->pm_value
;
1604 if (!strcasecmp (pm
->pm_name
, "mode")) {
1605 e
->eb_mode
= pm
->pm_value
;
1608 if (!strcasecmp (pm
->pm_name
, "size")) {
1609 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1612 if (!strcasecmp (pm
->pm_name
, "server")) {
1613 e
->eb_server
= pm
->pm_value
;
1616 if (!strcasecmp (pm
->pm_name
, "subject")) {
1617 e
->eb_subject
= pm
->pm_value
;
1620 if (!strcasecmp (pm
->pm_name
, "url")) {
1622 * According to RFC 2017, we have to remove all whitespace from
1626 char *u
, *p
= pm
->pm_value
;
1627 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1629 for (; *p
!= '\0'; p
++) {
1630 if (! isspace((unsigned char) *p
))
1637 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1638 e
->eb_body
= getcpy (pm
->pm_value
);
1643 if (!e
->eb_access
) {
1645 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1646 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1659 InitApplication (CT ct
)
1661 CI ci
= &ct
->c_ctinfo
;
1664 ct
->c_subtype
= ct_str_subtype (CT_APPLICATION
, ci
->ci_subtype
);
1671 * TRANSFER ENCODINGS
1675 init_encoding (CT ct
, OpenCEFunc openfnx
)
1677 ct
->c_ceopenfnx
= openfnx
;
1678 ct
->c_ceclosefnx
= close_encoding
;
1679 ct
->c_cesizefnx
= size_encoding
;
1686 close_encoding (CT ct
)
1688 CE ce
= &ct
->c_cefile
;
1697 static unsigned long
1698 size_encoding (CT ct
)
1703 CE ce
= &ct
->c_cefile
;
1706 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1707 return (long) st
.st_size
;
1710 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1711 return (long) st
.st_size
;
1716 if (ct
->c_encoding
== CE_EXTERNAL
)
1717 return (ct
->c_end
- ct
->c_begin
);
1720 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1721 return (ct
->c_end
- ct
->c_begin
);
1723 if (fstat (fd
, &st
) != NOTOK
)
1724 size
= (long) st
.st_size
;
1728 (*ct
->c_ceclosefnx
) (ct
);
1737 static unsigned char b642nib
[0x80] = {
1738 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
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, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1744 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1745 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1746 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1747 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1748 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1749 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1750 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1751 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1752 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1753 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1760 return init_encoding (ct
, openBase64
);
1765 openBase64 (CT ct
, char **file
)
1767 int bitno
, cc
, digested
;
1768 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1770 unsigned char value
, b
;
1771 char *cp
, *ep
, buffer
[BUFSIZ
];
1772 /* sbeck -- handle suffixes */
1774 CE ce
= &ct
->c_cefile
;
1778 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1783 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1784 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1790 if (*file
== NULL
) {
1793 ce
->ce_file
= add (*file
, NULL
);
1797 /* sbeck@cise.ufl.edu -- handle suffixes */
1799 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1800 if (ce
->ce_unlink
) {
1801 /* Create temporary file with filename extension. */
1802 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1803 adios(NULL
, "unable to create temporary file in %s",
1807 ce
->ce_file
= add (cp
, ce
->ce_file
);
1809 } else if (*file
== NULL
) {
1811 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1812 adios(NULL
, "unable to create temporary file in %s",
1815 ce
->ce_file
= add (tempfile
, NULL
);
1818 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1819 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1823 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1824 adios (NULL
, "internal error(1)");
1827 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1828 content_error (ct
->c_file
, ct
, "unable to open for reading");
1834 if ((digested
= ct
->c_digested
))
1835 MD5Init (&mdContext
);
1841 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1843 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1845 content_error (ct
->c_file
, ct
, "error reading from");
1849 content_error (NULL
, ct
, "premature eof");
1857 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1860 if (isspace ((unsigned char) *cp
))
1862 if (skip
|| (((unsigned char) *cp
) & 0x80)
1863 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1865 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1866 (unsigned char) *cp
,
1867 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1870 content_error (NULL
, ct
,
1871 "invalid BASE64 encoding -- continuing");
1875 bits
|= value
<< bitno
;
1877 if ((bitno
-= 6) < 0) {
1878 b
= (bits
>> 16) & 0xff;
1879 if (!text
|| b
!= '\r')
1880 putc ((char) b
, ce
->ce_fp
);
1882 MD5Update (&mdContext
, &b
, 1);
1884 b
= (bits
>> 8) & 0xff;
1885 if (! text
|| b
!= '\r')
1886 putc ((char) b
, ce
->ce_fp
);
1888 MD5Update (&mdContext
, &b
, 1);
1891 if (! text
|| b
!= '\r')
1892 putc ((char) b
, ce
->ce_fp
);
1894 MD5Update (&mdContext
, &b
, 1);
1898 if (ferror (ce
->ce_fp
)) {
1899 content_error (ce
->ce_file
, ct
,
1900 "error writing to");
1903 bitno
= 18, bits
= 0L, skip
= 0;
1909 goto self_delimiting
;
1918 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1920 content_error (NULL
, ct
, "invalid BASE64 encoding");
1925 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1927 if (fflush (ce
->ce_fp
)) {
1928 content_error (ce
->ce_file
, ct
, "error writing to");
1933 unsigned char digest
[16];
1935 MD5Final (digest
, &mdContext
);
1936 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1937 sizeof(digest
) / sizeof(digest
[0])))
1938 content_error (NULL
, ct
,
1939 "content integrity suspect (digest mismatch) -- continuing");
1942 fprintf (stderr
, "content integrity confirmed\n");
1945 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1948 *file
= ce
->ce_file
;
1953 return fileno (ce
->ce_fp
);
1960 free_encoding (ct
, 0);
1969 static char hex2nib
[0x80] = {
1970 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1977 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1978 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1979 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1980 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1981 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1982 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1983 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1984 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1985 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1992 return init_encoding (ct
, openQuoted
);
1997 openQuoted (CT ct
, char **file
)
1999 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
2005 CE ce
= &ct
->c_cefile
;
2006 /* sbeck -- handle suffixes */
2011 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2016 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2017 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2023 if (*file
== NULL
) {
2026 ce
->ce_file
= add (*file
, NULL
);
2030 /* sbeck@cise.ufl.edu -- handle suffixes */
2032 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2033 if (ce
->ce_unlink
) {
2034 /* Create temporary file with filename extension. */
2035 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2036 adios(NULL
, "unable to create temporary file in %s",
2040 ce
->ce_file
= add (cp
, ce
->ce_file
);
2042 } else if (*file
== NULL
) {
2044 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2045 adios(NULL
, "unable to create temporary file in %s",
2048 ce
->ce_file
= add (tempfile
, NULL
);
2051 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2052 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2056 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2057 adios (NULL
, "internal error(2)");
2060 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2061 content_error (ct
->c_file
, ct
, "unable to open for reading");
2067 if ((digested
= ct
->c_digested
))
2068 MD5Init (&mdContext
);
2075 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2077 if ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) == -1) {
2078 content_error (NULL
, ct
, "premature eof");
2082 if ((cc
= gotlen
) > len
)
2086 for (ep
= (cp
= bufp
) + cc
- 1; cp
<= ep
; ep
--)
2087 if (!isspace ((unsigned char) *ep
))
2091 for (; cp
< ep
; cp
++) {
2093 /* in an escape sequence */
2095 /* at byte 1 of an escape sequence */
2096 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2097 /* next is byte 2 */
2100 /* at byte 2 of an escape sequence */
2102 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2103 putc (mask
, ce
->ce_fp
);
2105 MD5Update (&mdContext
, &mask
, 1);
2106 if (ferror (ce
->ce_fp
)) {
2107 content_error (ce
->ce_file
, ct
, "error writing to");
2110 /* finished escape sequence; next may be literal or a new
2111 * escape sequence */
2114 /* on to next byte */
2118 /* not in an escape sequence */
2120 /* starting an escape sequence, or invalid '='? */
2121 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2122 /* "=\n" soft line break, eat the \n */
2126 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2127 /* We don't have 2 bytes left, so this is an invalid
2128 * escape sequence; just show the raw bytes (below). */
2129 } else if (isxdigit ((unsigned char) cp
[1]) &&
2130 isxdigit ((unsigned char) cp
[2])) {
2131 /* Next 2 bytes are hex digits, making this a valid escape
2132 * sequence; let's decode it (above). */
2136 /* One or both of the next 2 is out of range, making this
2137 * an invalid escape sequence; just show the raw bytes
2142 /* Just show the raw byte. */
2143 putc (*cp
, ce
->ce_fp
);
2146 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2148 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2151 if (ferror (ce
->ce_fp
)) {
2152 content_error (ce
->ce_file
, ct
, "error writing to");
2158 content_error (NULL
, ct
,
2159 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2163 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2165 if (fflush (ce
->ce_fp
)) {
2166 content_error (ce
->ce_file
, ct
, "error writing to");
2171 unsigned char digest
[16];
2173 MD5Final (digest
, &mdContext
);
2174 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2175 sizeof(digest
) / sizeof(digest
[0])))
2176 content_error (NULL
, ct
,
2177 "content integrity suspect (digest mismatch) -- continuing");
2180 fprintf (stderr
, "content integrity confirmed\n");
2183 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2186 *file
= ce
->ce_file
;
2192 return fileno (ce
->ce_fp
);
2195 free_encoding (ct
, 0);
2212 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2215 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2221 open7Bit (CT ct
, char **file
)
2223 int cc
, fd
, len
, own_ct_fp
= 0;
2224 char buffer
[BUFSIZ
];
2225 /* sbeck -- handle suffixes */
2228 CE ce
= &ct
->c_cefile
;
2231 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2236 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2237 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2243 if (*file
== NULL
) {
2246 ce
->ce_file
= add (*file
, NULL
);
2250 /* sbeck@cise.ufl.edu -- handle suffixes */
2252 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2253 if (ce
->ce_unlink
) {
2254 /* Create temporary file with filename extension. */
2255 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2256 adios(NULL
, "unable to create temporary file in %s",
2260 ce
->ce_file
= add (cp
, ce
->ce_file
);
2262 } else if (*file
== NULL
) {
2264 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2265 adios(NULL
, "unable to create temporary file in %s",
2268 ce
->ce_file
= add (tempfile
, NULL
);
2271 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2272 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2276 if (ct
->c_type
== CT_MULTIPART
) {
2277 CI ci
= &ct
->c_ctinfo
;
2281 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2282 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2283 + 1 + strlen (ci
->ci_subtype
);
2284 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2287 fputs (buffer
, ce
->ce_fp
);
2291 if (ci
->ci_comment
) {
2292 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2293 fputs ("\n\t", ce
->ce_fp
);
2297 putc (' ', ce
->ce_fp
);
2300 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2303 fprintf (ce
->ce_fp
, "\n");
2305 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2307 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2309 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2310 fprintf (ce
->ce_fp
, "\n");
2313 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2314 adios (NULL
, "internal error(3)");
2317 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2318 content_error (ct
->c_file
, ct
, "unable to open for reading");
2324 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2326 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2328 content_error (ct
->c_file
, ct
, "error reading from");
2332 content_error (NULL
, ct
, "premature eof");
2340 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
) < cc
) {
2341 advise ("open7Bit", "fwrite");
2343 if (ferror (ce
->ce_fp
)) {
2344 content_error (ce
->ce_file
, ct
, "error writing to");
2349 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2351 if (fflush (ce
->ce_fp
)) {
2352 content_error (ce
->ce_file
, ct
, "error writing to");
2356 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2359 *file
= ce
->ce_file
;
2364 return fileno (ce
->ce_fp
);
2367 free_encoding (ct
, 0);
2381 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2383 char cachefile
[BUFSIZ
];
2386 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2391 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2392 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2398 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2399 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2400 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2401 ce
->ce_file
= getcpy (cachefile
);
2405 admonish (cachefile
, "unable to fopen for reading");
2409 *fd
= fileno (ce
->ce_fp
);
2413 *file
= ce
->ce_file
;
2414 *fd
= fileno (ce
->ce_fp
);
2425 return init_encoding (ct
, openFile
);
2430 openFile (CT ct
, char **file
)
2433 char cachefile
[BUFSIZ
];
2434 struct exbody
*e
= ct
->c_ctexbody
;
2435 CE ce
= &ct
->c_cefile
;
2437 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2449 content_error (NULL
, ct
, "missing name parameter");
2453 ce
->ce_file
= getcpy (e
->eb_name
);
2456 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2457 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2461 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2462 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2463 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2467 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2468 if ((fp
= fopen (cachefile
, "w"))) {
2470 char buffer
[BUFSIZ
];
2471 FILE *gp
= ce
->ce_fp
;
2473 fseek (gp
, 0L, SEEK_SET
);
2475 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2477 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2478 advise ("openFile", "fwrite");
2483 admonish (ce
->ce_file
, "error reading");
2484 (void) m_unlink (cachefile
);
2488 admonish (cachefile
, "error writing");
2489 (void) m_unlink (cachefile
);
2496 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2497 *file
= ce
->ce_file
;
2498 return fileno (ce
->ce_fp
);
2508 return init_encoding (ct
, openFTP
);
2513 openFTP (CT ct
, char **file
)
2515 int cachetype
, caching
, fd
;
2517 char *bp
, *ftp
, *user
, *pass
;
2518 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2520 CE ce
= &ct
->c_cefile
;
2521 static char *username
= NULL
;
2522 static char *password
= NULL
;
2526 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2532 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2543 if (!e
->eb_name
|| !e
->eb_site
) {
2544 content_error (NULL
, ct
, "missing %s parameter",
2545 e
->eb_name
? "site": "name");
2549 /* Get the buffer ready to go */
2551 buflen
= sizeof(buffer
);
2554 * Construct the query message for user
2556 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2562 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2568 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2569 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2574 if (e
->eb_size
> 0) {
2575 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2580 snprintf (bp
, buflen
, "? ");
2583 * Now, check the answer
2585 if (!getanswer (buffer
))
2590 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2594 ruserpass (e
->eb_site
, &username
, &password
);
2599 ce
->ce_unlink
= (*file
== NULL
);
2601 cachefile
[0] = '\0';
2602 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2603 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2604 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2605 if (*file
== NULL
) {
2612 ce
->ce_file
= add (*file
, NULL
);
2614 ce
->ce_file
= add (cachefile
, NULL
);
2617 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2618 adios(NULL
, "unable to create temporary file in %s",
2621 ce
->ce_file
= add (tempfile
, NULL
);
2624 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2625 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2630 int child_id
, i
, vecp
;
2634 vec
[vecp
++] = r1bindex (ftp
, '/');
2635 vec
[vecp
++] = e
->eb_site
;
2638 vec
[vecp
++] = e
->eb_dir
;
2639 vec
[vecp
++] = e
->eb_name
;
2640 vec
[vecp
++] = ce
->ce_file
,
2641 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2642 ? "ascii" : "binary";
2647 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2651 adios ("fork", "unable to");
2655 close (fileno (ce
->ce_fp
));
2657 fprintf (stderr
, "unable to exec ");
2663 if (pidXwait (child_id
, NULL
)) {
2664 username
= password
= NULL
;
2674 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2679 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2680 if ((fp
= fopen (cachefile
, "w"))) {
2682 FILE *gp
= ce
->ce_fp
;
2684 fseek (gp
, 0L, SEEK_SET
);
2686 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2688 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2689 advise ("openFTP", "fwrite");
2694 admonish (ce
->ce_file
, "error reading");
2695 (void) m_unlink (cachefile
);
2699 admonish (cachefile
, "error writing");
2700 (void) m_unlink (cachefile
);
2708 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2709 *file
= ce
->ce_file
;
2710 return fileno (ce
->ce_fp
);
2721 return init_encoding (ct
, openMail
);
2726 openMail (CT ct
, char **file
)
2728 int child_id
, fd
, i
, vecp
;
2730 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2731 struct exbody
*e
= ct
->c_ctexbody
;
2732 CE ce
= &ct
->c_cefile
;
2734 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2745 if (!e
->eb_server
) {
2746 content_error (NULL
, ct
, "missing server parameter");
2750 /* Get buffer ready to go */
2752 buflen
= sizeof(buffer
);
2754 /* Now, construct query message */
2755 snprintf (bp
, buflen
, "Retrieve content");
2761 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2767 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2769 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2771 /* Now, check answer */
2772 if (!getanswer (buffer
))
2776 vec
[vecp
++] = r1bindex (mailproc
, '/');
2777 vec
[vecp
++] = e
->eb_server
;
2778 vec
[vecp
++] = "-subject";
2779 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2780 vec
[vecp
++] = "-body";
2781 vec
[vecp
++] = e
->eb_body
;
2784 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2788 advise ("fork", "unable to");
2792 execvp (mailproc
, vec
);
2793 fprintf (stderr
, "unable to exec ");
2799 if (pidXwait (child_id
, NULL
) == OK
)
2800 advise (NULL
, "request sent");
2804 if (*file
== NULL
) {
2806 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2807 adios(NULL
, "unable to create temporary file in %s",
2810 ce
->ce_file
= add (tempfile
, NULL
);
2813 ce
->ce_file
= add (*file
, NULL
);
2817 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2818 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2822 /* showproc is for mhshow and mhstore, though mhlist -debug
2823 * prints it, too. */
2825 free (ct
->c_showproc
);
2826 ct
->c_showproc
= add ("true", NULL
);
2828 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2829 *file
= ce
->ce_file
;
2830 return fileno (ce
->ce_fp
);
2841 return init_encoding (ct
, openURL
);
2846 openURL (CT ct
, char **file
)
2848 struct exbody
*e
= ct
->c_ctexbody
;
2849 CE ce
= &ct
->c_cefile
;
2850 char *urlprog
, *program
;
2851 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2852 int fd
, caching
, cachetype
;
2853 struct msgs_array args
= { 0, 0, NULL
};
2856 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2860 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2864 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2876 content_error(NULL
, ct
, "missing url parameter");
2880 ce
->ce_unlink
= (*file
== NULL
);
2882 cachefile
[0] = '\0';
2884 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2885 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2886 if (*file
== NULL
) {
2893 ce
->ce_file
= add(*file
, NULL
);
2895 ce
->ce_file
= add(cachefile
, NULL
);
2898 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2899 adios(NULL
, "unable to create temporary file in %s",
2902 ce
->ce_file
= add (tempfile
, NULL
);
2905 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2906 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2910 switch (child_id
= fork()) {
2912 adios ("fork", "unable to");
2916 argsplit_msgarg(&args
, urlprog
, &program
);
2917 app_msgarg(&args
, e
->eb_url
);
2918 app_msgarg(&args
, NULL
);
2919 dup2(fileno(ce
->ce_fp
), 1);
2920 close(fileno(ce
->ce_fp
));
2921 execvp(program
, args
.msgs
);
2922 fprintf(stderr
, "Unable to exec ");
2928 if (pidXwait(child_id
, NULL
)) {
2936 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2941 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2942 if ((fp
= fopen(cachefile
, "w"))) {
2944 FILE *gp
= ce
->ce_fp
;
2946 fseeko(gp
, 0, SEEK_SET
);
2948 while ((cc
= fread(buffer
, sizeof(*buffer
),
2949 sizeof(buffer
), gp
)) > 0)
2950 if ((int) fwrite(buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2951 advise ("openURL", "fwrite");
2957 admonish(ce
->ce_file
, "error reading");
2958 (void) m_unlink (cachefile
);
2965 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2966 *file
= ce
->ce_file
;
2971 readDigest (CT ct
, char *cp
)
2976 unsigned char *dp
, value
, *ep
;
2982 for (ep
= (dp
= ct
->c_digest
)
2983 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2988 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2990 fprintf (stderr
, "invalid BASE64 encoding\n");
2994 bits
|= value
<< bitno
;
2996 if ((bitno
-= 6) < 0) {
2997 if (dp
+ (3 - skip
) > ep
)
2998 goto invalid_digest
;
2999 *dp
++ = (bits
>> 16) & 0xff;
3001 *dp
++ = (bits
>> 8) & 0xff;
3003 *dp
++ = bits
& 0xff;
3013 goto self_delimiting
;
3018 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
3028 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
3036 fprintf (stderr
, "MD5 digest=");
3037 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
3038 fprintf (stderr
, "%02x", *dp
& 0xff);
3039 fprintf (stderr
, "\n");
3046 /* Multipart parts might have content before the first subpart and/or
3047 after the last subpart that hasn't been stored anywhere else, so do
3050 get_leftover_mp_content (CT ct
, int before
/* or after */)
3052 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
3054 int found_boundary
= 0;
3060 char *content
= NULL
;
3062 if (! m
) return NOTOK
;
3065 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
3067 /* Isolate the beginning of this part to the beginning of the
3068 first subpart and save any content between them. */
3069 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
3070 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3071 boundary
= concat ("--", m
->mp_start
, NULL
);
3073 struct part
*last_subpart
= NULL
;
3074 struct part
*subpart
;
3076 /* Go to the last subpart to get its end position. */
3077 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3078 last_subpart
= subpart
;
3081 if (last_subpart
== NULL
) return NOTOK
;
3083 /* Isolate the end of the last subpart to the end of this part
3084 and save any content between them. */
3085 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3086 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3087 boundary
= concat ("--", m
->mp_stop
, NULL
);
3090 /* Back up by 1 to pick up the newline. */
3091 while ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) != -1) {
3093 /* Don't look beyond beginning of first subpart (before) or
3094 next part (after). */
3095 if (read
> max
) bufp
[read
-max
] = '\0';
3098 if (! strcmp (bufp
, boundary
)) {
3102 if (! found_boundary
&& ! strcmp (bufp
, boundary
)) {
3108 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3110 char *old_content
= content
;
3111 content
= concat (content
, bufp
, NULL
);
3115 ? concat ("\n", bufp
, NULL
)
3116 : concat (bufp
, NULL
);
3121 if (found_boundary
|| read
> max
) break;
3123 if (read
> max
) break;
3127 /* Skip the newline if that's all there is. */
3131 /* Remove trailing newline, except at EOF. */
3132 if ((before
|| ! feof (ct
->c_fp
)) &&
3133 (cp
= content
+ strlen (content
)) > content
&&
3138 if (strlen (content
) > 1) {
3140 m
->mp_content_before
= content
;
3142 m
->mp_content_after
= content
;
3157 ct_type_str (int type
) {
3159 case CT_APPLICATION
:
3160 return "application";
3176 return "unknown_type";
3182 ct_subtype_str (int type
, int subtype
) {
3184 case CT_APPLICATION
:
3186 case APPLICATION_OCTETS
:
3188 case APPLICATION_POSTSCRIPT
:
3189 return "postscript";
3191 return "unknown_app_subtype";
3195 case MESSAGE_RFC822
:
3197 case MESSAGE_PARTIAL
:
3199 case MESSAGE_EXTERNAL
:
3202 return "unknown_msg_subtype";
3208 case MULTI_ALTERNATE
:
3209 return "alternative";
3212 case MULTI_PARALLEL
:
3217 return "unknown_multipart_subtype";
3228 return "unknown_text_subtype";
3231 return "unknown_type";
3237 ct_str_type (const char *type
) {
3238 struct str2init
*s2i
;
3240 for (s2i
= str2cts
; s2i
->si_key
; ++s2i
) {
3241 if (! strcasecmp (type
, s2i
->si_key
)) {
3245 if (! s2i
->si_key
&& ! uprf (type
, "X-")) {
3254 ct_str_subtype (int type
, const char *subtype
) {
3258 case CT_APPLICATION
:
3259 for (kv
= SubApplication
; kv
->kv_key
; ++kv
) {
3260 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3264 return kv
->kv_value
;
3266 for (kv
= SubMessage
; kv
->kv_key
; ++kv
) {
3267 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3271 return kv
->kv_value
;
3273 for (kv
= SubMultiPart
; kv
->kv_key
; ++kv
) {
3274 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3278 return kv
->kv_value
;
3280 for (kv
= SubText
; kv
->kv_key
; ++kv
) {
3281 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3285 return kv
->kv_value
;
3292 /* Find the content type and InitFunc for the CT. */
3293 const struct str2init
*
3294 get_ct_init (int type
) {
3295 const struct str2init
*sp
;
3297 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3298 if (type
== sp
->si_val
) {
3307 ce_str (int encoding
) {
3312 return "quoted-printable";
3328 /* Find the content type and InitFunc for the content encoding method. */
3329 const struct str2init
*
3330 get_ce_method (const char *method
) {
3331 struct str2init
*sp
;
3333 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3334 if (! strcasecmp (method
, sp
->si_key
)) {
3343 * Parse a series of MIME attributes (or parameters) given a header as
3346 * Arguments include:
3348 * filename - Name of input file (for error messages)
3349 * fieldname - Name of field being processed
3350 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3351 * Updated to point to end of attributes when finished.
3352 * param_head - Pointer to head of parameter list
3353 * param_tail - Pointer to tail of parameter list
3354 * commentp - Pointer to header comment pointer (may be NULL)
3356 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3357 * DONE to indicate a benign error (minor parsing error, but the program
3362 parse_header_attrs (const char *filename
, const char *fieldname
,
3363 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3366 char *cp
= *header_attrp
;
3372 struct sectlist
*next
;
3378 struct sectlist
*sechead
;
3379 struct parmlist
*next
;
3380 } *pp
, *pp2
, *phead
= NULL
;
3382 while (*cp
== ';') {
3383 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3384 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3387 while (isspace ((unsigned char) *cp
))
3391 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3396 if (! suppress_extraneous_trailing_semicolon_warning
) {
3398 "extraneous trailing ';' in message %s's %s: "
3400 filename
, fieldname
);
3402 extraneous_trailing_semicolon
= 1;
3406 /* down case the attribute name */
3407 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3408 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3409 *dp
= tolower ((unsigned char) *dp
);
3411 for (up
= dp
; isspace ((unsigned char) *dp
);)
3413 if (dp
== cp
|| *dp
!= '=') {
3415 "invalid parameter in message %s's %s: "
3416 "field\n%*sparameter %s (error detected at offset %d)",
3417 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3422 * To handle RFC 2231, we have to deal with the following extensions:
3424 * name*=encoded-value
3425 * name*<N>=part-N-of-a-parameter-value
3426 * name*<N>*=encoded-part-N-of-a-parameter-value
3429 * If there's a * right before the equal sign, it's encoded.
3430 * If there's a * and one or more digits, then it's section N.
3432 * Remember we can have one or the other, or both. cp points to
3433 * beginning of name, up points past the last character in the
3437 for (vp
= cp
; vp
< up
; vp
++) {
3438 if (*vp
== '*' && vp
< up
- 1) {
3441 } else if (*vp
== '*' && vp
== up
- 1) {
3443 } else if (partial
) {
3444 if (isdigit((unsigned char) *vp
))
3445 index
= *vp
- '0' + index
* 10;
3447 advise (NULL
, "invalid parameter index in message %s's "
3448 "%s: field\n%*s(parameter %s)", filename
,
3449 fieldname
, strlen(invo_name
) + 2, "", cp
);
3458 * Break out the parameter name and value sections and allocate
3462 nameptr
= mh_xmalloc(len
+ 1);
3463 strncpy(nameptr
, cp
, len
);
3464 nameptr
[len
] = '\0';
3466 for (dp
++; isspace ((unsigned char) *dp
);)
3471 * Single quotes delimit the character set and language tag.
3472 * They are required on the first section (or a complete
3477 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3483 charset
= mh_xmalloc(len
+ 1);
3484 strncpy(charset
, dp
, len
);
3485 charset
[len
] = '\0';
3491 advise(NULL
, "missing charset in message %s's %s: "
3492 "field\n%*s(parameter %s)", filename
, fieldname
,
3493 strlen(invo_name
) + 2, "", nameptr
);
3499 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3506 lang
= mh_xmalloc(len
+ 1);
3507 strncpy(lang
, dp
, len
);
3514 advise(NULL
, "missing language tag in message %s's %s: "
3515 "field\n%*s(parameter %s)", filename
, fieldname
,
3516 strlen(invo_name
) + 2, "", nameptr
);
3527 * At this point vp should be pointing at the beginning
3528 * of the encoded value/section. Continue until we reach
3529 * the end or get whitespace. But first, calculate the
3530 * length so we can allocate the correct buffer size.
3533 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3535 if (*(vp
+ 1) == '\0' ||
3536 !isxdigit((unsigned char) *(vp
+ 1)) ||
3537 *(vp
+ 2) == '\0' ||
3538 !isxdigit((unsigned char) *(vp
+ 2))) {
3539 advise(NULL
, "invalid encoded sequence in message "
3540 "%s's %s: field\n%*s(parameter %s)",
3541 filename
, fieldname
, strlen(invo_name
) + 2,
3555 up
= valptr
= mh_xmalloc(len
+ 1);
3557 for (vp
= dp
; istoken(*vp
); vp
++) {
3559 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3570 * A "normal" string. If it's got a leading quote, then we
3571 * strip the quotes out. Otherwise go until we reach the end
3572 * or get whitespace. Note we scan it twice; once to get the
3573 * length, then the second time copies it into the destination
3580 for (cp
= dp
+ 1;;) {
3585 "invalid quoted-string in message %s's %s: "
3586 "field\n%*s(parameter %s)",
3587 filename
, fieldname
, strlen(invo_name
) + 2, "",
3610 for (cp
= dp
; istoken (*cp
); cp
++) {
3615 valptr
= mh_xmalloc(len
+ 1);
3619 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3627 strncpy(valptr
, cp
= dp
, len
);
3635 * If 'partial' is set, we don't allocate a parameter now. We
3636 * put it on the parameter linked list to be reassembled later.
3638 * "phead" points to a list of all parameters we need to reassemble.
3639 * Each parameter has a list of sections. We insert the sections in
3644 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3645 if (strcasecmp(nameptr
, pp
->name
) == 0)
3650 pp
= mh_xmalloc(sizeof(*pp
));
3651 memset(pp
, 0, sizeof(*pp
));
3658 * Insert this into the section linked list
3661 sp
= mh_xmalloc(sizeof(*sp
));
3662 memset(sp
, 0, sizeof(*sp
));
3667 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3668 sp
->next
= pp
->sechead
;
3671 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3672 if (sp2
->index
== sp
->index
) {
3673 advise (NULL
, "duplicate index (%d) in message "
3674 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3675 filename
, fieldname
, strlen(invo_name
) + 2, "",
3680 if (sp2
->index
< sp
->index
&&
3681 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3682 sp
->next
= sp2
->next
;
3689 advise(NULL
, "Internal error: cannot insert partial "
3690 "param in message %s's %s: field\n%*s(parameter %s)",
3691 filename
, fieldname
, strlen(invo_name
) + 2, "",
3699 * Save our charset and lang tags.
3702 if (index
== 0 && encoded
) {
3705 pp
->charset
= charset
;
3711 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3712 pm
->pm_charset
= charset
;
3716 while (isspace ((unsigned char) *cp
))
3720 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3726 * Now that we're done, reassemble all of the partial parameters.
3729 for (pp
= phead
; pp
!= NULL
; ) {
3733 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3734 if (sp
->index
!= pindex
++) {
3735 advise(NULL
, "missing section %d for parameter in "
3736 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3737 filename
, fieldname
, strlen(invo_name
) + 2, "",
3744 p
= q
= mh_xmalloc(tlen
+ 1);
3745 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3746 memcpy(q
, sp
->value
, sp
->len
);
3756 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3757 pm
->pm_charset
= pp
->charset
;
3758 pm
->pm_lang
= pp
->lang
;
3769 * Return the charset for a particular content type.
3773 content_charset (CT ct
) {
3774 char *ret_charset
= NULL
;
3776 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3778 return ret_charset
? ret_charset
: getcpy ("US-ASCII");
3783 * Create a string based on a list of output parameters. Assume that this
3784 * parameter string will be appended to an existing header, so start out
3785 * with the separator (;). Perform RFC 2231 encoding when necessary.
3789 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3791 char *paramout
= NULL
;
3792 char line
[CPERLIN
* 2], *q
;
3793 int curlen
, index
, cont
, encode
, i
;
3794 size_t valoff
, numchars
;
3796 while (params
!= NULL
) {
3802 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3805 if (strlen(params
->pm_name
) > CPERLIN
) {
3806 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3812 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3815 * Loop until we get a parameter that fits within a line. We
3816 * assume new lines start with a tab, so check our overflow based
3826 * At this point we're definitely continuing the line, so
3827 * be sure to include the parameter name and section index.
3830 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3831 params
->pm_name
, index
);
3834 * Both of these functions do a NUL termination
3838 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3839 numchars
, valoff
, index
);
3841 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3852 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3857 * "line" starts with a ;\n\t, so that doesn't count against
3858 * the length. But add 8 since it starts with a tab; that's
3859 * how we end up with 5.
3862 initialwidth
= strlen(line
) + 5;
3865 * At this point the line should be built, so add it to our
3866 * current output buffer.
3869 paramout
= add(line
, paramout
);
3873 * If this won't fit on the line, start a new one. Save room in
3874 * case we need a semicolon on the end
3877 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3889 * At this point, we're either finishing a contined parameter, or
3890 * we're working on a new one.
3894 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3895 params
->pm_name
, index
);
3897 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3902 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3903 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3905 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3906 strlen(params
->pm_value
+ valoff
), valoff
);
3914 paramout
= add(line
, paramout
);
3915 initialwidth
+= strlen(line
);
3917 params
= params
->pm_next
;
3921 *offsetout
= initialwidth
;
3927 * Calculate the size of a parameter.
3931 * pm - The parameter being output
3932 * index - If continuing the parameter, the index of the section
3934 * valueoff - The current offset into the parameter value that we're
3935 * working on (previous sections have consumed valueoff bytes).
3936 * encode - Set if we should perform encoding on this parameter section
3937 * (given that we're consuming bytesfit bytes).
3938 * cont - Set if the remaining data in value will not fit on a single
3939 * line and will need to be continued.
3940 * bytesfit - The number of bytes that we can consume from the parameter
3941 * value and still fit on a completely new line. The
3942 * calculation assumes the new line starts with a tab,
3943 * includes the parameter name and any encoding, and fits
3944 * within CPERLIN bytes. Will always be at least 1.
3948 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3951 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3952 size_t len
= 0, fit
= 0;
3953 int fitlimit
= 0, eightbit
, maxfit
;
3958 * Add up the length. First, start with the parameter name.
3961 len
= strlen(pm
->pm_name
);
3964 * Scan the parameter value and see if we need to do encoding for this
3968 eightbit
= contains8bit(start
, NULL
);
3971 * Determine if we need to encode this section. Encoding is necessary if:
3973 * - There are any 8-bit characters at all and we're on the first
3975 * - There are 8-bit characters within N bytes of our section start.
3976 * N is calculated based on the number of bytes it would take to
3977 * reach CPERLIN. Specifically:
3978 * 8 (starting tab) +
3979 * strlen(param name) +
3980 * 4 ('* for section marker, '=', opening/closing '"')
3982 * is the number of bytes used by everything that isn't part of the
3983 * value. So that gets subtracted from CPERLIN.
3986 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3987 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3988 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3992 len
++; /* Add in equal sign */
3996 * We're using maxfit as a marker for how many characters we can
3997 * fit into the line. Bump it by two because we're not using quotes
4004 * If we don't have a charset or language tag in this parameter,
4008 if (! pm
->pm_charset
) {
4009 pm
->pm_charset
= getcpy(write_charset_8bit());
4010 if (strcasecmp(pm
->pm_charset
, "US-ASCII") == 0)
4011 adios(NULL
, "8-bit characters in parameter \"%s\", but "
4012 "local character set is US-ASCII", pm
->pm_name
);
4015 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
4017 len
++; /* For the encoding marker */
4020 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
4025 * We know we definitely need to include an index. maxfit already
4026 * includes the section marker.
4028 len
+= strlen(indexchar
);
4030 for (p
= start
; *p
!= '\0'; p
++) {
4031 if (isparamencode(*p
)) {
4039 * Just so there's no confusion: maxfit is counting OUTPUT
4040 * characters (post-encoding). fit is counting INPUT characters.
4042 if (! fitlimit
&& maxfit
>= 0)
4044 else if (! fitlimit
)
4049 * Calculate the string length, but add room for quoting \
4050 * and " if necessary. Also account for quotes at beginning
4053 for (p
= start
; *p
!= '\0'; p
++) {
4064 if (! fitlimit
&& maxfit
>= 0)
4066 else if (! fitlimit
)
4083 * Output an encoded parameter string.
4087 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4088 size_t valueoff
, int index
)
4090 size_t outlen
= 0, n
;
4091 char *endptr
= output
+ len
, *p
;
4094 * First, output the marker for an encoded string.
4102 * If the index is 0, output the character set and language tag.
4103 * If theses were NULL, they should have already been filled in
4108 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
4112 if (output
> endptr
) {
4113 advise(NULL
, "Internal error: parameter buffer overflow");
4119 * Copy over the value, encoding if necessary
4122 p
= pm
->pm_value
+ valueoff
;
4123 while (valuelen
-- > 0) {
4124 if (isparamencode(*p
)) {
4125 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
4132 if (output
> endptr
) {
4133 advise(NULL
, "Internal error: parameter buffer overflow");
4144 * Output a "normal" parameter, without encoding. Be sure to escape
4145 * quotes and backslashes if necessary.
4149 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4153 char *endptr
= output
+ len
, *p
;
4159 p
= pm
->pm_value
+ valueoff
;
4161 while (valuelen
-- > 0) {
4171 if (output
> endptr
) {
4172 advise(NULL
, "Internal error: parameter buffer overflow");
4177 if (output
- 2 > endptr
) {
4178 advise(NULL
, "Internal error: parameter buffer overflow");
4189 * Add a parameter to the parameter linked list
4193 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4195 PM pm
= mh_xmalloc(sizeof(*pm
));
4197 memset(pm
, 0, sizeof(*pm
));
4199 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4200 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4203 (*last
)->pm_next
= pm
;
4214 * Either replace a current parameter with a new value, or add the parameter
4215 * to the parameter linked list.
4219 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4223 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4224 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4226 * If nocopy is set, it's assumed that we own both name
4227 * and value. We don't need name, so we discard it now.
4232 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4237 return add_param(first
, last
, name
, value
, nocopy
);
4241 * Retrieve a parameter value from a parameter linked list. If the parameter
4242 * value needs converted to the local character set, do that now.
4246 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4248 while (first
!= NULL
) {
4249 if (strcasecmp(name
, first
->pm_name
) == 0) {
4251 return first
->pm_value
;
4253 return getcpy(get_param_value(first
, replace
));
4255 first
= first
->pm_next
;
4262 * Return a parameter value, converting to the local character set if
4266 char *get_param_value(PM pm
, char replace
)
4268 static char buffer
[4096]; /* I hope no parameters are larger */
4269 size_t bufsize
= sizeof(buffer
);
4274 ICONV_CONST
char *p
;
4275 #else /* HAVE_ICONV */
4277 #endif /* HAVE_ICONV */
4282 * If we don't have a character set indicated, it's assumed to be
4283 * US-ASCII. If it matches our character set, we don't need to convert
4287 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4288 strlen(pm
->pm_charset
))) {
4289 return pm
->pm_value
;
4293 * In this case, we need to convert. If we have iconv support, use
4294 * that. Otherwise, go through and simply replace every non-ASCII
4295 * character with the substitution character.
4300 bufsize
= sizeof(buffer
);
4301 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4303 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4304 if (cd
== (iconv_t
) -1) {
4308 inbytes
= strlen(pm
->pm_value
);
4312 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4313 if (errno
!= EILSEQ
) {
4318 * Reset shift state, substitute our character,
4319 * try to restart conversion.
4322 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4335 for (++p
, --inbytes
;
4336 inbytes
> 0 && (((unsigned char) *q
) & 0xc0) == 0x80;
4355 #endif /* HAVE_ICONV */
4358 * Take everything non-ASCII and substituite the replacement character
4362 bufsize
= sizeof(buffer
);
4363 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4364 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))