3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
17 #include <h/mhparse.h>
21 #endif /* HAVE_ICONV */
27 extern int rcachesw
; /* mhcachesbr.c */
28 extern int wcachesw
; /* mhcachesbr.c */
30 int checksw
= 0; /* check Content-MD5 field */
33 * These are for mhfixmsg to:
34 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
36 * 2) Suppress the warning about bogus multipart content, and report it.
37 * 3) Suppress the warning about extraneous trailing ';' in header parameter
38 * lists, and report it.
40 int skip_mp_cte_check
;
41 int suppress_bogus_mp_content_warning
;
43 int suppress_extraneous_trailing_semicolon_warning
;
44 int extraneous_trailing_semicolon
;
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 advise (NULL
, "message %s has multiple %s: fields",
372 ct
->c_file
, VRSN_FIELD
);
375 ct
->c_vrsn
= add (hp
->value
, NULL
);
377 /* Now, cleanup this field */
380 while (isspace ((unsigned char) *cp
))
382 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
384 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
385 if (!isspace ((unsigned char) *dp
))
389 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
392 get_comment (ct
->c_file
, VRSN_FIELD
, &cp
, NULL
) == NOTOK
)
395 for (dp
= cp
; istoken (*dp
); dp
++)
399 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
402 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
403 ct
->c_file
, VRSN_FIELD
, cp
);
406 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
407 /* Get Content-Type field */
408 struct str2init
*s2i
;
409 CI ci
= &ct
->c_ctinfo
;
411 /* Check if we've already seen a Content-Type header */
413 advise (NULL
, "message %s has multiple %s: fields",
414 ct
->c_file
, TYPE_FIELD
);
418 /* Parse the Content-Type field */
419 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
423 * Set the Init function and the internal
424 * flag for this content type.
426 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
427 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
429 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
431 ct
->c_type
= s2i
->si_val
;
432 ct
->c_ctinitfnx
= s2i
->si_init
;
434 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
435 /* Get Content-Transfer-Encoding field */
437 struct str2init
*s2i
;
440 * Check if we've already seen the
441 * Content-Transfer-Encoding field
444 advise (NULL
, "message %s has multiple %s: fields",
445 ct
->c_file
, ENCODING_FIELD
);
449 /* get copy of this field */
450 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
452 while (isspace ((unsigned char) *cp
))
454 for (dp
= cp
; istoken (*dp
); dp
++)
460 * Find the internal flag and Init function
461 * for this transfer encoding.
463 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
464 if (!strcasecmp (cp
, s2i
->si_key
))
466 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
469 ct
->c_encoding
= s2i
->si_val
;
471 /* Call the Init function for this encoding */
472 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
475 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
476 /* Get Content-MD5 field */
482 if (ct
->c_digested
) {
483 advise (NULL
, "message %s has multiple %s: fields",
484 ct
->c_file
, MD5_FIELD
);
488 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
490 while (isspace ((unsigned char) *cp
))
492 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
494 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
495 if (!isspace ((unsigned char) *dp
))
499 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
502 get_comment (ct
->c_file
, MD5_FIELD
, &cp
, NULL
) == NOTOK
) {
507 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
515 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
516 /* Get Content-ID field */
517 ct
->c_id
= add (hp
->value
, ct
->c_id
);
519 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
520 /* Get Content-Description field */
521 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
523 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
524 /* Get Content-Disposition field */
525 if (get_dispo(hp
->value
, ct
, 0) == NOTOK
)
530 hp
= hp
->next
; /* next header field */
534 * Check if we saw a Content-Type field.
535 * If not, then assign a default value for
536 * it, and the Init function.
540 * If we are inside a multipart/digest message,
541 * so default type is message/rfc822
544 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
546 ct
->c_type
= CT_MESSAGE
;
547 ct
->c_ctinitfnx
= InitMessage
;
550 * Else default type is text/plain
552 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
554 ct
->c_type
= CT_TEXT
;
555 ct
->c_ctinitfnx
= InitText
;
559 /* Use default Transfer-Encoding, if necessary */
561 ct
->c_encoding
= CE_7BIT
;
574 * small routine to add header field to list
578 add_header (CT ct
, char *name
, char *value
)
582 /* allocate header field structure */
583 hp
= mh_xmalloc (sizeof(*hp
));
585 /* link data into header structure */
590 /* link header structure into the list */
591 if (ct
->c_first_hf
== NULL
) {
592 ct
->c_first_hf
= hp
; /* this is the first */
595 ct
->c_last_hf
->next
= hp
; /* add it to the end */
604 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
605 * directives. Fills in the information of the CTinfo structure.
608 get_ctinfo (char *cp
, CT ct
, int magic
)
617 /* store copy of Content-Type line */
618 cp
= ct
->c_ctline
= add (cp
, NULL
);
620 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
623 /* change newlines to spaces */
624 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
627 /* trim trailing spaces */
628 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
629 if (!isspace ((unsigned char) *dp
))
634 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
636 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
637 &ci
->ci_comment
) == NOTOK
)
640 for (dp
= cp
; istoken (*dp
); dp
++)
643 ci
->ci_type
= add (cp
, NULL
); /* store content type */
647 advise (NULL
, "invalid %s: field in message %s (empty type)",
648 TYPE_FIELD
, ct
->c_file
);
652 /* down case the content type string */
653 for (dp
= ci
->ci_type
; *dp
; dp
++)
654 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
655 *dp
= tolower ((unsigned char) *dp
);
657 while (isspace ((unsigned char) *cp
))
660 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
661 &ci
->ci_comment
) == NOTOK
)
666 ci
->ci_subtype
= add ("", NULL
);
671 while (isspace ((unsigned char) *cp
))
674 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
675 &ci
->ci_comment
) == NOTOK
)
678 for (dp
= cp
; istoken (*dp
); dp
++)
681 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
684 if (!*ci
->ci_subtype
) {
686 "invalid %s: field in message %s (empty subtype for \"%s\")",
687 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
691 /* down case the content subtype string */
692 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
693 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
694 *dp
= tolower ((unsigned char) *dp
);
697 while (isspace ((unsigned char) *cp
))
700 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
701 &ci
->ci_comment
) == NOTOK
)
704 if ((status
= parse_header_attrs (ct
->c_file
, TYPE_FIELD
, &cp
,
705 &ci
->ci_first_pm
, &ci
->ci_last_pm
,
706 &ci
->ci_comment
)) != OK
) {
707 return status
== NOTOK
? NOTOK
: OK
;
711 * Get any <Content-Id> given in buffer
713 if (magic
&& *cp
== '<') {
718 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
719 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
725 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
731 while (isspace ((unsigned char) *cp
))
736 * Get any [Content-Description] given in buffer.
738 if (magic
&& *cp
== '[') {
740 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
744 advise (NULL
, "invalid description in message %s", ct
->c_file
);
752 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
758 while (isspace ((unsigned char) *cp
))
763 * Get any {Content-Disposition} given in buffer.
765 if (magic
&& *cp
== '{') {
767 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
771 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
779 if (get_dispo(cp
, ct
, 1) != OK
)
785 while (isspace ((unsigned char) *cp
))
790 * Get any extension directives (right now just the content transfer
791 * encoding, but maybe others) that we care about.
794 if (magic
&& *cp
== '*') {
796 * See if it's a CTE we match on
801 while (*cp
!= '\0' && ! isspace((unsigned char) *cp
))
805 advise (NULL
, "invalid null transfer encoding specification");
812 ct
->c_reqencoding
= CE_UNKNOWN
;
814 for (kv
= EncodingType
; kv
->kv_key
; kv
++) {
815 if (strcasecmp(kv
->kv_key
, dp
) == 0) {
816 ct
->c_reqencoding
= kv
->kv_value
;
821 if (ct
->c_reqencoding
== CE_UNKNOWN
) {
822 advise (NULL
, "invalid CTE specification: \"%s\"", dp
);
826 while (isspace ((unsigned char) *cp
))
831 * Check if anything is left over
835 ci
->ci_magic
= add (cp
, NULL
);
837 /* If there is a Content-Disposition header and it doesn't
838 have a *filename=, extract it from the magic contents.
839 The r1bindex call skips any leading directory
841 if (ct
->c_dispo_type
&&
842 !get_param(ct
->c_dispo_first
, "filename", '_', 1)) {
843 add_param(&ct
->c_dispo_first
, &ct
->c_dispo_last
, "filename",
844 r1bindex(ci
->ci_magic
, '/'), 0);
849 "extraneous information in message %s's %s: field\n%*s(%s)",
850 ct
->c_file
, TYPE_FIELD
, strlen(invo_name
) + 2, "", cp
);
858 * Parse out a Content-Disposition header. A lot of this is cribbed from
862 get_dispo (char *cp
, CT ct
, int buildflag
)
864 char *dp
, *dispoheader
;
869 * Save the whole copy of the Content-Disposition header, unless we're
870 * processing a mhbuild directive. A NULL c_dispo will be a flag to
871 * mhbuild that the disposition header needs to be generated at that
875 dispoheader
= cp
= add(cp
, NULL
);
877 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
880 /* change newlines to spaces */
881 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
884 /* trim trailing spaces */
885 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
886 if (!isspace ((unsigned char) *dp
))
891 fprintf (stderr
, "%s: %s\n", DISPO_FIELD
, cp
);
893 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) ==
899 for (dp
= cp
; istoken (*dp
); dp
++)
902 ct
->c_dispo_type
= add (cp
, NULL
); /* store disposition type */
905 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) == NOTOK
)
908 if ((status
= parse_header_attrs (ct
->c_file
, DISPO_FIELD
, &cp
,
909 &ct
->c_dispo_first
, &ct
->c_dispo_last
,
911 if (status
== NOTOK
) {
917 "extraneous information in message %s's %s: field\n%*s(%s)",
918 ct
->c_file
, DISPO_FIELD
, strlen(invo_name
) + 2, "", cp
);
924 ct
->c_dispo
= dispoheader
;
931 get_comment (const char *filename
, const char *fieldname
, char **ap
,
936 char c
, buffer
[BUFSIZ
], *dp
;
946 advise (NULL
, "invalid comment in message %s's %s: field",
947 filename
, fieldname
);
952 if ((c
= *cp
++) == '\0')
975 if ((dp
= *commentp
)) {
976 *commentp
= concat (dp
, " ", buffer
, NULL
);
979 *commentp
= add (buffer
, NULL
);
983 while (isspace ((unsigned char) *cp
))
994 * Handles content types audio, image, and video.
995 * There's not much to do right here.
1003 return OK
; /* not much to do here */
1014 char buffer
[BUFSIZ
];
1019 CI ci
= &ct
->c_ctinfo
;
1021 /* check for missing subtype */
1022 if (!*ci
->ci_subtype
)
1023 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1026 ct
->c_subtype
= ct_str_subtype (CT_TEXT
, ci
->ci_subtype
);
1028 /* allocate text character set structure */
1029 if ((t
= (struct text
*) mh_xcalloc (1, sizeof(*t
))) == NULL
)
1030 adios (NULL
, "out of memory");
1031 ct
->c_ctparams
= (void *) t
;
1033 /* scan for charset parameter */
1034 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
)
1035 if (!strcasecmp (pm
->pm_name
, "charset"))
1038 /* check if content specified a character set */
1040 chset
= pm
->pm_value
;
1041 t
->tx_charset
= CHARSET_SPECIFIED
;
1043 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1047 * If we can not handle character set natively,
1048 * then check profile for string to modify the
1049 * terminal or display method.
1051 * termproc is for mhshow, though mhlist -debug prints it, too.
1053 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1054 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1055 if ((cp
= context_find (buffer
)))
1056 ct
->c_termproc
= getcpy (cp
);
1068 InitMultiPart (CT ct
)
1078 struct multipart
*m
;
1079 struct part
*part
, **next
;
1080 CI ci
= &ct
->c_ctinfo
;
1085 * The encoding for multipart messages must be either
1086 * 7bit, 8bit, or binary (per RFC2045).
1088 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1089 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1090 /* Copy the Content-Transfer-Encoding header field body so we can
1091 remove any trailing whitespace and leading blanks from it. */
1092 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1094 bp
= cte
+ strlen (cte
) - 1;
1095 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1096 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1099 "\"%s/%s\" type in message %s must be encoded in\n"
1100 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1101 "is to\nmanually edit the file and change the \"%s\"\n"
1102 "Content-Transfer-Encoding to one of those. For now",
1103 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1110 ct
->c_subtype
= ct_str_subtype (CT_MULTIPART
, ci
->ci_subtype
);
1113 * Check for "boundary" parameter, which is
1114 * required for multipart messages.
1117 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1118 if (!strcasecmp (pm
->pm_name
, "boundary")) {
1124 /* complain if boundary parameter is missing */
1127 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1128 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1132 /* allocate primary structure for multipart info */
1133 if ((m
= (struct multipart
*) mh_xcalloc (1, sizeof(*m
))) == NULL
)
1134 adios (NULL
, "out of memory");
1135 ct
->c_ctparams
= (void *) m
;
1137 /* check if boundary parameter contains only whitespace characters */
1138 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1141 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1142 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1146 /* remove trailing whitespace from boundary parameter */
1147 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1148 if (!isspace ((unsigned char) *dp
))
1152 /* record boundary separators */
1153 m
->mp_start
= concat (bp
, "\n", NULL
);
1154 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1156 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1157 advise (ct
->c_file
, "unable to open for reading");
1161 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1163 next
= &m
->mp_parts
;
1167 while ((gotlen
= getline(&bufp
, &buflen
, fp
)) != -1) {
1172 if (bufp
[0] != '-' || bufp
[1] != '-')
1175 if (strcmp (bufp
+ 2, m
->mp_start
))
1178 if ((part
= (struct part
*) mh_xcalloc (1, sizeof(*part
))) == NULL
)
1179 adios (NULL
, "out of memory");
1181 next
= &part
->mp_next
;
1183 if (!(p
= get_content (fp
, ct
->c_file
,
1184 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1192 fseek (fp
, pos
, SEEK_SET
);
1195 if (strcmp (bufp
+ 2, m
->mp_start
) == 0) {
1199 p
->c_end
= ftell(fp
) - (gotlen
+ 1);
1200 if (p
->c_end
< p
->c_begin
)
1201 p
->c_begin
= p
->c_end
;
1206 if (strcmp (bufp
+ 2, m
->mp_stop
) == 0)
1212 if (! suppress_bogus_mp_content_warning
) {
1213 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1215 bogus_mp_content
= 1;
1217 if (!inout
&& part
) {
1219 p
->c_end
= ct
->c_end
;
1221 if (p
->c_begin
>= p
->c_end
) {
1222 for (next
= &m
->mp_parts
; *next
!= part
;
1223 next
= &((*next
)->mp_next
))
1227 free ((char *) part
);
1232 /* reverse the order of the parts for multipart/alternative */
1233 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1239 * label all subparts with part number, and
1240 * then initialize the content of the subpart.
1245 char partnam
[BUFSIZ
];
1248 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1249 pp
= partnam
+ strlen (partnam
);
1254 for (part
= m
->mp_parts
, partnum
= 1; part
;
1255 part
= part
->mp_next
, partnum
++) {
1258 sprintf (pp
, "%d", partnum
);
1259 p
->c_partno
= add (partnam
, NULL
);
1261 /* initialize the content of the subparts */
1262 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1271 get_leftover_mp_content (ct
, 1);
1272 get_leftover_mp_content (ct
, 0);
1282 * reverse the order of the parts of a multipart/alternative,
1283 * presumably to put the "most favored" alternative first, for
1284 * ease of choosing/displaying it later on. from a mail message on
1285 * nmh-workers, from kenh:
1286 * "Stock" MH 6.8.5 did not have a reverse_parts() function, but I
1287 * see code in mhn that did the same thing... Acccording to the RCS
1288 * logs, that code was around from the initial checkin of mhn.c by
1289 * John Romine in 1992, which is as far back as we have."
1292 reverse_parts (CT ct
)
1294 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1298 /* Reverse the order of its parts by walking the mp_parts list
1299 and pushing each node to the front. */
1300 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1301 next
= part
->mp_next
;
1302 part
->mp_next
= m
->mp_parts
;
1308 move_preferred_part (CT ct
, char *type
, char *subtype
)
1310 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1311 struct part
*part
, *next
, *prev
, *head
, *nhead
, *ntail
;
1315 /* move the matching part(s) to the head of the list: walk the
1316 * list of parts, move matching parts to a new list (maintaining
1317 * their order), and finally, concatenate the old list onto the
1324 head
->mp_next
= m
->mp_parts
;
1325 nhead
->mp_next
= NULL
;
1329 part
= head
->mp_next
;
1330 while (part
!= NULL
) {
1331 ci
= &part
->mp_part
->c_ctinfo
;
1332 if (!strcasecmp(ci
->ci_type
, type
) &&
1333 (!subtype
|| !strcasecmp(ci
->ci_subtype
, subtype
))) {
1334 prev
->mp_next
= part
->mp_next
;
1335 part
->mp_next
= NULL
;
1336 ntail
->mp_next
= part
;
1338 part
= prev
->mp_next
;
1341 part
= prev
->mp_next
;
1344 ntail
->mp_next
= head
->mp_next
;
1345 m
->mp_parts
= nhead
->mp_next
;
1350 * move parts that match the user's preferences (-prefer) to the head
1351 * of the line. process preferences in reverse so first one given
1352 * ends up first in line
1358 for (i
= npreferred
-1; i
>= 0; i
--)
1359 move_preferred_part(ct
, preferred_types
[i
], preferred_subtypes
[i
]);
1364 /* parse_mime() arranges alternates in reverse (priority) order. This
1365 function can be used to reverse them back. This will put, for
1366 example, a text/plain part before a text/html part in a
1367 multipart/alternative part, for example, where it belongs. */
1369 reverse_alternative_parts (CT ct
) {
1370 if (ct
->c_type
== CT_MULTIPART
) {
1371 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1374 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1378 /* And call recursively on each part of a multipart. */
1379 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1380 reverse_alternative_parts (part
->mp_part
);
1393 CI ci
= &ct
->c_ctinfo
;
1395 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1397 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1398 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1402 /* check for missing subtype */
1403 if (!*ci
->ci_subtype
)
1404 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1407 ct
->c_subtype
= ct_str_subtype (CT_MESSAGE
, ci
->ci_subtype
);
1409 switch (ct
->c_subtype
) {
1410 case MESSAGE_RFC822
:
1413 case MESSAGE_PARTIAL
:
1418 if ((p
= (struct partial
*) mh_xcalloc (1, sizeof(*p
))) == NULL
)
1419 adios (NULL
, "out of memory");
1420 ct
->c_ctparams
= (void *) p
;
1422 /* scan for parameters "id", "number", and "total" */
1423 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1424 if (!strcasecmp (pm
->pm_name
, "id")) {
1425 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1428 if (!strcasecmp (pm
->pm_name
, "number")) {
1429 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1430 || p
->pm_partno
< 1) {
1433 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1434 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1435 ct
->c_file
, TYPE_FIELD
);
1440 if (!strcasecmp (pm
->pm_name
, "total")) {
1441 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1450 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1452 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1453 ci
->ci_type
, ci
->ci_subtype
,
1454 ct
->c_file
, TYPE_FIELD
);
1460 case MESSAGE_EXTERNAL
:
1467 if ((e
= (struct exbody
*) mh_xcalloc (1, sizeof(*e
))) == NULL
)
1468 adios (NULL
, "out of memory");
1469 ct
->c_ctparams
= (void *) e
;
1472 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1473 advise (ct
->c_file
, "unable to open for reading");
1477 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1479 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1487 p
->c_ceopenfnx
= NULL
;
1488 if ((exresult
= params_external (ct
, 0)) != NOTOK
1489 && p
->c_ceopenfnx
== openMail
) {
1493 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1495 content_error (NULL
, ct
,
1496 "empty body for access-type=mail-server");
1500 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1501 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1503 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1505 adios ("failed", "fread");
1508 adios (NULL
, "unexpected EOF from fread");
1511 bp
+= cc
, size
-= cc
;
1518 p
->c_end
= p
->c_begin
;
1523 if (exresult
== NOTOK
)
1525 if (e
->eb_flags
== NOTOK
)
1528 switch (p
->c_type
) {
1533 if (p
->c_subtype
!= MESSAGE_RFC822
)
1537 e
->eb_partno
= ct
->c_partno
;
1539 (*p
->c_ctinitfnx
) (p
);
1554 params_external (CT ct
, int composing
)
1557 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1558 CI ci
= &ct
->c_ctinfo
;
1560 ct
->c_ceopenfnx
= NULL
;
1561 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1562 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1563 struct str2init
*s2i
;
1564 CT p
= e
->eb_content
;
1566 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1567 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1570 e
->eb_access
= pm
->pm_value
;
1571 e
->eb_flags
= NOTOK
;
1572 p
->c_encoding
= CE_EXTERNAL
;
1575 e
->eb_access
= s2i
->si_key
;
1576 e
->eb_flags
= s2i
->si_val
;
1577 p
->c_encoding
= CE_EXTERNAL
;
1579 /* Call the Init function for this external type */
1580 if ((*s2i
->si_init
)(p
) == NOTOK
)
1584 if (!strcasecmp (pm
->pm_name
, "name")) {
1585 e
->eb_name
= pm
->pm_value
;
1588 if (!strcasecmp (pm
->pm_name
, "permission")) {
1589 e
->eb_permission
= pm
->pm_value
;
1592 if (!strcasecmp (pm
->pm_name
, "site")) {
1593 e
->eb_site
= pm
->pm_value
;
1596 if (!strcasecmp (pm
->pm_name
, "directory")) {
1597 e
->eb_dir
= pm
->pm_value
;
1600 if (!strcasecmp (pm
->pm_name
, "mode")) {
1601 e
->eb_mode
= pm
->pm_value
;
1604 if (!strcasecmp (pm
->pm_name
, "size")) {
1605 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1608 if (!strcasecmp (pm
->pm_name
, "server")) {
1609 e
->eb_server
= pm
->pm_value
;
1612 if (!strcasecmp (pm
->pm_name
, "subject")) {
1613 e
->eb_subject
= pm
->pm_value
;
1616 if (!strcasecmp (pm
->pm_name
, "url")) {
1618 * According to RFC 2017, we have to remove all whitespace from
1622 char *u
, *p
= pm
->pm_value
;
1623 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1625 for (; *p
!= '\0'; p
++) {
1626 if (! isspace((unsigned char) *p
))
1633 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1634 e
->eb_body
= getcpy (pm
->pm_value
);
1639 if (!e
->eb_access
) {
1641 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1642 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1655 InitApplication (CT ct
)
1657 CI ci
= &ct
->c_ctinfo
;
1660 ct
->c_subtype
= ct_str_subtype (CT_APPLICATION
, ci
->ci_subtype
);
1667 * TRANSFER ENCODINGS
1671 init_encoding (CT ct
, OpenCEFunc openfnx
)
1673 ct
->c_ceopenfnx
= openfnx
;
1674 ct
->c_ceclosefnx
= close_encoding
;
1675 ct
->c_cesizefnx
= size_encoding
;
1682 close_encoding (CT ct
)
1684 CE ce
= &ct
->c_cefile
;
1693 static unsigned long
1694 size_encoding (CT ct
)
1699 CE ce
= &ct
->c_cefile
;
1702 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1703 return (long) st
.st_size
;
1706 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1707 return (long) st
.st_size
;
1712 if (ct
->c_encoding
== CE_EXTERNAL
)
1713 return (ct
->c_end
- ct
->c_begin
);
1716 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1717 return (ct
->c_end
- ct
->c_begin
);
1719 if (fstat (fd
, &st
) != NOTOK
)
1720 size
= (long) st
.st_size
;
1724 (*ct
->c_ceclosefnx
) (ct
);
1733 static unsigned char b642nib
[0x80] = {
1734 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1735 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1736 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1737 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1738 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1739 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1740 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1741 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1742 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1743 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1744 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1745 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1746 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1747 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1748 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1749 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1756 return init_encoding (ct
, openBase64
);
1761 openBase64 (CT ct
, char **file
)
1763 int bitno
, cc
, digested
;
1764 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1766 unsigned char value
, b
;
1767 char *cp
, *ep
, buffer
[BUFSIZ
];
1768 /* sbeck -- handle suffixes */
1770 CE ce
= &ct
->c_cefile
;
1774 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1779 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1780 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1786 if (*file
== NULL
) {
1789 ce
->ce_file
= add (*file
, NULL
);
1793 /* sbeck@cise.ufl.edu -- handle suffixes */
1795 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1796 if (ce
->ce_unlink
) {
1797 /* Create temporary file with filename extension. */
1798 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1799 adios(NULL
, "unable to create temporary file in %s",
1803 ce
->ce_file
= add (cp
, ce
->ce_file
);
1805 } else if (*file
== NULL
) {
1807 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1808 adios(NULL
, "unable to create temporary file in %s",
1811 ce
->ce_file
= add (tempfile
, NULL
);
1814 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1815 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1819 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1820 adios (NULL
, "internal error(1)");
1823 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1824 content_error (ct
->c_file
, ct
, "unable to open for reading");
1830 if ((digested
= ct
->c_digested
))
1831 MD5Init (&mdContext
);
1837 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1839 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1841 content_error (ct
->c_file
, ct
, "error reading from");
1845 content_error (NULL
, ct
, "premature eof");
1853 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1856 if (isspace ((unsigned char) *cp
))
1858 if (skip
|| (((unsigned char) *cp
) & 0x80)
1859 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1861 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1862 (unsigned char) *cp
,
1863 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1866 content_error (NULL
, ct
,
1867 "invalid BASE64 encoding -- continuing");
1871 bits
|= value
<< bitno
;
1873 if ((bitno
-= 6) < 0) {
1874 b
= (bits
>> 16) & 0xff;
1875 if (!text
|| b
!= '\r')
1876 putc ((char) b
, ce
->ce_fp
);
1878 MD5Update (&mdContext
, &b
, 1);
1880 b
= (bits
>> 8) & 0xff;
1881 if (! text
|| b
!= '\r')
1882 putc ((char) b
, ce
->ce_fp
);
1884 MD5Update (&mdContext
, &b
, 1);
1887 if (! text
|| b
!= '\r')
1888 putc ((char) b
, ce
->ce_fp
);
1890 MD5Update (&mdContext
, &b
, 1);
1894 if (ferror (ce
->ce_fp
)) {
1895 content_error (ce
->ce_file
, ct
,
1896 "error writing to");
1899 bitno
= 18, bits
= 0L, skip
= 0;
1905 goto self_delimiting
;
1914 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1916 content_error (NULL
, ct
, "invalid BASE64 encoding");
1921 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1923 if (fflush (ce
->ce_fp
)) {
1924 content_error (ce
->ce_file
, ct
, "error writing to");
1929 unsigned char digest
[16];
1931 MD5Final (digest
, &mdContext
);
1932 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1933 sizeof(digest
) / sizeof(digest
[0])))
1934 content_error (NULL
, ct
,
1935 "content integrity suspect (digest mismatch) -- continuing");
1938 fprintf (stderr
, "content integrity confirmed\n");
1941 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1944 *file
= ce
->ce_file
;
1949 return fileno (ce
->ce_fp
);
1956 free_encoding (ct
, 0);
1965 static char hex2nib
[0x80] = {
1966 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1967 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1968 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1969 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1970 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1971 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1972 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1973 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1974 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1975 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1976 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1977 0x00, 0x00, 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
1988 return init_encoding (ct
, openQuoted
);
1993 openQuoted (CT ct
, char **file
)
1995 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
2001 CE ce
= &ct
->c_cefile
;
2002 /* sbeck -- handle suffixes */
2007 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2012 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2013 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2019 if (*file
== NULL
) {
2022 ce
->ce_file
= add (*file
, NULL
);
2026 /* sbeck@cise.ufl.edu -- handle suffixes */
2028 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2029 if (ce
->ce_unlink
) {
2030 /* Create temporary file with filename extension. */
2031 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2032 adios(NULL
, "unable to create temporary file in %s",
2036 ce
->ce_file
= add (cp
, ce
->ce_file
);
2038 } else if (*file
== NULL
) {
2040 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2041 adios(NULL
, "unable to create temporary file in %s",
2044 ce
->ce_file
= add (tempfile
, NULL
);
2047 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2048 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2052 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2053 adios (NULL
, "internal error(2)");
2056 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2057 content_error (ct
->c_file
, ct
, "unable to open for reading");
2063 if ((digested
= ct
->c_digested
))
2064 MD5Init (&mdContext
);
2071 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2073 if ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) == -1) {
2074 content_error (NULL
, ct
, "premature eof");
2078 if ((cc
= gotlen
) > len
)
2082 for (ep
= (cp
= bufp
) + cc
- 1; cp
<= ep
; ep
--)
2083 if (!isspace ((unsigned char) *ep
))
2087 for (; cp
< ep
; cp
++) {
2089 /* in an escape sequence */
2091 /* at byte 1 of an escape sequence */
2092 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2093 /* next is byte 2 */
2096 /* at byte 2 of an escape sequence */
2098 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2099 putc (mask
, ce
->ce_fp
);
2101 MD5Update (&mdContext
, &mask
, 1);
2102 if (ferror (ce
->ce_fp
)) {
2103 content_error (ce
->ce_file
, ct
, "error writing to");
2106 /* finished escape sequence; next may be literal or a new
2107 * escape sequence */
2110 /* on to next byte */
2114 /* not in an escape sequence */
2116 /* starting an escape sequence, or invalid '='? */
2117 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2118 /* "=\n" soft line break, eat the \n */
2122 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2123 /* We don't have 2 bytes left, so this is an invalid
2124 * escape sequence; just show the raw bytes (below). */
2125 } else if (isxdigit ((unsigned char) cp
[1]) &&
2126 isxdigit ((unsigned char) cp
[2])) {
2127 /* Next 2 bytes are hex digits, making this a valid escape
2128 * sequence; let's decode it (above). */
2132 /* One or both of the next 2 is out of range, making this
2133 * an invalid escape sequence; just show the raw bytes
2138 /* Just show the raw byte. */
2139 putc (*cp
, ce
->ce_fp
);
2142 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2144 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2147 if (ferror (ce
->ce_fp
)) {
2148 content_error (ce
->ce_file
, ct
, "error writing to");
2154 content_error (NULL
, ct
,
2155 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2159 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2161 if (fflush (ce
->ce_fp
)) {
2162 content_error (ce
->ce_file
, ct
, "error writing to");
2167 unsigned char digest
[16];
2169 MD5Final (digest
, &mdContext
);
2170 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2171 sizeof(digest
) / sizeof(digest
[0])))
2172 content_error (NULL
, ct
,
2173 "content integrity suspect (digest mismatch) -- continuing");
2176 fprintf (stderr
, "content integrity confirmed\n");
2179 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2182 *file
= ce
->ce_file
;
2188 return fileno (ce
->ce_fp
);
2191 free_encoding (ct
, 0);
2208 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2211 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2217 open7Bit (CT ct
, char **file
)
2219 int cc
, fd
, len
, own_ct_fp
= 0;
2220 char buffer
[BUFSIZ
];
2221 /* sbeck -- handle suffixes */
2224 CE ce
= &ct
->c_cefile
;
2227 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2232 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2233 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2239 if (*file
== NULL
) {
2242 ce
->ce_file
= add (*file
, NULL
);
2246 /* sbeck@cise.ufl.edu -- handle suffixes */
2248 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2249 if (ce
->ce_unlink
) {
2250 /* Create temporary file with filename extension. */
2251 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2252 adios(NULL
, "unable to create temporary file in %s",
2256 ce
->ce_file
= add (cp
, ce
->ce_file
);
2258 } else if (*file
== NULL
) {
2260 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2261 adios(NULL
, "unable to create temporary file in %s",
2264 ce
->ce_file
= add (tempfile
, NULL
);
2267 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2268 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2272 if (ct
->c_type
== CT_MULTIPART
) {
2273 CI ci
= &ct
->c_ctinfo
;
2277 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2278 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2279 + 1 + strlen (ci
->ci_subtype
);
2280 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2283 fputs (buffer
, ce
->ce_fp
);
2287 if (ci
->ci_comment
) {
2288 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2289 fputs ("\n\t", ce
->ce_fp
);
2293 putc (' ', ce
->ce_fp
);
2296 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2299 fprintf (ce
->ce_fp
, "\n");
2301 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2303 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2305 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2306 fprintf (ce
->ce_fp
, "\n");
2309 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2310 adios (NULL
, "internal error(3)");
2313 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2314 content_error (ct
->c_file
, ct
, "unable to open for reading");
2320 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2322 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2324 content_error (ct
->c_file
, ct
, "error reading from");
2328 content_error (NULL
, ct
, "premature eof");
2336 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
) < cc
) {
2337 advise ("open7Bit", "fwrite");
2339 if (ferror (ce
->ce_fp
)) {
2340 content_error (ce
->ce_file
, ct
, "error writing to");
2345 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2347 if (fflush (ce
->ce_fp
)) {
2348 content_error (ce
->ce_file
, ct
, "error writing to");
2352 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2355 *file
= ce
->ce_file
;
2360 return fileno (ce
->ce_fp
);
2363 free_encoding (ct
, 0);
2377 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2379 char cachefile
[BUFSIZ
];
2382 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2387 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2388 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2394 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2395 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2396 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2397 ce
->ce_file
= getcpy (cachefile
);
2401 admonish (cachefile
, "unable to fopen for reading");
2405 *fd
= fileno (ce
->ce_fp
);
2409 *file
= ce
->ce_file
;
2410 *fd
= fileno (ce
->ce_fp
);
2421 return init_encoding (ct
, openFile
);
2426 openFile (CT ct
, char **file
)
2429 char cachefile
[BUFSIZ
];
2430 struct exbody
*e
= ct
->c_ctexbody
;
2431 CE ce
= &ct
->c_cefile
;
2433 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2445 content_error (NULL
, ct
, "missing name parameter");
2449 ce
->ce_file
= getcpy (e
->eb_name
);
2452 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2453 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2457 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2458 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2459 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2463 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2464 if ((fp
= fopen (cachefile
, "w"))) {
2466 char buffer
[BUFSIZ
];
2467 FILE *gp
= ce
->ce_fp
;
2469 fseek (gp
, 0L, SEEK_SET
);
2471 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2473 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2474 advise ("openFile", "fwrite");
2479 admonish (ce
->ce_file
, "error reading");
2480 (void) m_unlink (cachefile
);
2484 admonish (cachefile
, "error writing");
2485 (void) m_unlink (cachefile
);
2492 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2493 *file
= ce
->ce_file
;
2494 return fileno (ce
->ce_fp
);
2504 return init_encoding (ct
, openFTP
);
2509 openFTP (CT ct
, char **file
)
2511 int cachetype
, caching
, fd
;
2513 char *bp
, *ftp
, *user
, *pass
;
2514 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2516 CE ce
= &ct
->c_cefile
;
2517 static char *username
= NULL
;
2518 static char *password
= NULL
;
2522 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2528 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2539 if (!e
->eb_name
|| !e
->eb_site
) {
2540 content_error (NULL
, ct
, "missing %s parameter",
2541 e
->eb_name
? "site": "name");
2545 /* Get the buffer ready to go */
2547 buflen
= sizeof(buffer
);
2550 * Construct the query message for user
2552 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2558 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2564 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2565 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2570 if (e
->eb_size
> 0) {
2571 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2576 snprintf (bp
, buflen
, "? ");
2579 * Now, check the answer
2581 if (!getanswer (buffer
))
2586 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2590 ruserpass (e
->eb_site
, &username
, &password
);
2595 ce
->ce_unlink
= (*file
== NULL
);
2597 cachefile
[0] = '\0';
2598 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2599 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2600 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2601 if (*file
== NULL
) {
2608 ce
->ce_file
= add (*file
, NULL
);
2610 ce
->ce_file
= add (cachefile
, NULL
);
2613 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2614 adios(NULL
, "unable to create temporary file in %s",
2617 ce
->ce_file
= add (tempfile
, NULL
);
2620 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2621 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2626 int child_id
, i
, vecp
;
2630 vec
[vecp
++] = r1bindex (ftp
, '/');
2631 vec
[vecp
++] = e
->eb_site
;
2634 vec
[vecp
++] = e
->eb_dir
;
2635 vec
[vecp
++] = e
->eb_name
;
2636 vec
[vecp
++] = ce
->ce_file
,
2637 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2638 ? "ascii" : "binary";
2643 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2647 adios ("fork", "unable to");
2651 close (fileno (ce
->ce_fp
));
2653 fprintf (stderr
, "unable to exec ");
2659 if (pidXwait (child_id
, NULL
)) {
2660 username
= password
= NULL
;
2670 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2675 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2676 if ((fp
= fopen (cachefile
, "w"))) {
2678 FILE *gp
= ce
->ce_fp
;
2680 fseek (gp
, 0L, SEEK_SET
);
2682 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2684 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2685 advise ("openFTP", "fwrite");
2690 admonish (ce
->ce_file
, "error reading");
2691 (void) m_unlink (cachefile
);
2695 admonish (cachefile
, "error writing");
2696 (void) m_unlink (cachefile
);
2704 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2705 *file
= ce
->ce_file
;
2706 return fileno (ce
->ce_fp
);
2717 return init_encoding (ct
, openMail
);
2722 openMail (CT ct
, char **file
)
2724 int child_id
, fd
, i
, vecp
;
2726 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2727 struct exbody
*e
= ct
->c_ctexbody
;
2728 CE ce
= &ct
->c_cefile
;
2730 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2741 if (!e
->eb_server
) {
2742 content_error (NULL
, ct
, "missing server parameter");
2746 /* Get buffer ready to go */
2748 buflen
= sizeof(buffer
);
2750 /* Now, construct query message */
2751 snprintf (bp
, buflen
, "Retrieve content");
2757 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2763 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2765 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2767 /* Now, check answer */
2768 if (!getanswer (buffer
))
2772 vec
[vecp
++] = r1bindex (mailproc
, '/');
2773 vec
[vecp
++] = e
->eb_server
;
2774 vec
[vecp
++] = "-subject";
2775 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2776 vec
[vecp
++] = "-body";
2777 vec
[vecp
++] = e
->eb_body
;
2780 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2784 advise ("fork", "unable to");
2788 execvp (mailproc
, vec
);
2789 fprintf (stderr
, "unable to exec ");
2795 if (pidXwait (child_id
, NULL
) == OK
)
2796 advise (NULL
, "request sent");
2800 if (*file
== NULL
) {
2802 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2803 adios(NULL
, "unable to create temporary file in %s",
2806 ce
->ce_file
= add (tempfile
, NULL
);
2809 ce
->ce_file
= add (*file
, NULL
);
2813 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2814 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2818 /* showproc is for mhshow and mhstore, though mhlist -debug
2819 * prints it, too. */
2821 free (ct
->c_showproc
);
2822 ct
->c_showproc
= add ("true", NULL
);
2824 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2825 *file
= ce
->ce_file
;
2826 return fileno (ce
->ce_fp
);
2837 return init_encoding (ct
, openURL
);
2842 openURL (CT ct
, char **file
)
2844 struct exbody
*e
= ct
->c_ctexbody
;
2845 CE ce
= &ct
->c_cefile
;
2846 char *urlprog
, *program
;
2847 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2848 int fd
, caching
, cachetype
;
2849 struct msgs_array args
= { 0, 0, NULL
};
2852 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2856 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2860 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2872 content_error(NULL
, ct
, "missing url parameter");
2876 ce
->ce_unlink
= (*file
== NULL
);
2878 cachefile
[0] = '\0';
2880 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2881 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2882 if (*file
== NULL
) {
2889 ce
->ce_file
= add(*file
, NULL
);
2891 ce
->ce_file
= add(cachefile
, NULL
);
2894 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2895 adios(NULL
, "unable to create temporary file in %s",
2898 ce
->ce_file
= add (tempfile
, NULL
);
2901 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2902 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2906 switch (child_id
= fork()) {
2908 adios ("fork", "unable to");
2912 argsplit_msgarg(&args
, urlprog
, &program
);
2913 app_msgarg(&args
, e
->eb_url
);
2914 app_msgarg(&args
, NULL
);
2915 dup2(fileno(ce
->ce_fp
), 1);
2916 close(fileno(ce
->ce_fp
));
2917 execvp(program
, args
.msgs
);
2918 fprintf(stderr
, "Unable to exec ");
2924 if (pidXwait(child_id
, NULL
)) {
2932 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2937 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2938 if ((fp
= fopen(cachefile
, "w"))) {
2940 FILE *gp
= ce
->ce_fp
;
2942 fseeko(gp
, 0, SEEK_SET
);
2944 while ((cc
= fread(buffer
, sizeof(*buffer
),
2945 sizeof(buffer
), gp
)) > 0)
2946 if ((int) fwrite(buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2947 advise ("openURL", "fwrite");
2953 admonish(ce
->ce_file
, "error reading");
2954 (void) m_unlink (cachefile
);
2961 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2962 *file
= ce
->ce_file
;
2967 readDigest (CT ct
, char *cp
)
2972 unsigned char *dp
, value
, *ep
;
2978 for (ep
= (dp
= ct
->c_digest
)
2979 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2984 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2986 fprintf (stderr
, "invalid BASE64 encoding\n");
2990 bits
|= value
<< bitno
;
2992 if ((bitno
-= 6) < 0) {
2993 if (dp
+ (3 - skip
) > ep
)
2994 goto invalid_digest
;
2995 *dp
++ = (bits
>> 16) & 0xff;
2997 *dp
++ = (bits
>> 8) & 0xff;
2999 *dp
++ = bits
& 0xff;
3009 goto self_delimiting
;
3014 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
3024 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
3032 fprintf (stderr
, "MD5 digest=");
3033 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
3034 fprintf (stderr
, "%02x", *dp
& 0xff);
3035 fprintf (stderr
, "\n");
3042 /* Multipart parts might have content before the first subpart and/or
3043 after the last subpart that hasn't been stored anywhere else, so do
3046 get_leftover_mp_content (CT ct
, int before
/* or after */)
3048 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
3050 int found_boundary
= 0;
3056 char *content
= NULL
;
3058 if (! m
) return NOTOK
;
3061 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
3063 /* Isolate the beginning of this part to the beginning of the
3064 first subpart and save any content between them. */
3065 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
3066 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3067 boundary
= concat ("--", m
->mp_start
, NULL
);
3069 struct part
*last_subpart
= NULL
;
3070 struct part
*subpart
;
3072 /* Go to the last subpart to get its end position. */
3073 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3074 last_subpart
= subpart
;
3077 if (last_subpart
== NULL
) return NOTOK
;
3079 /* Isolate the end of the last subpart to the end of this part
3080 and save any content between them. */
3081 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3082 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3083 boundary
= concat ("--", m
->mp_stop
, NULL
);
3086 /* Back up by 1 to pick up the newline. */
3087 while ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) != -1) {
3089 /* Don't look beyond beginning of first subpart (before) or
3090 next part (after). */
3091 if (read
> max
) bufp
[read
-max
] = '\0';
3094 if (! strcmp (bufp
, boundary
)) {
3098 if (! found_boundary
&& ! strcmp (bufp
, boundary
)) {
3104 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3106 char *old_content
= content
;
3107 content
= concat (content
, bufp
, NULL
);
3111 ? concat ("\n", bufp
, NULL
)
3112 : concat (bufp
, NULL
);
3117 if (found_boundary
|| read
> max
) break;
3119 if (read
> max
) break;
3123 /* Skip the newline if that's all there is. */
3127 /* Remove trailing newline, except at EOF. */
3128 if ((before
|| ! feof (ct
->c_fp
)) &&
3129 (cp
= content
+ strlen (content
)) > content
&&
3134 if (strlen (content
) > 1) {
3136 m
->mp_content_before
= content
;
3138 m
->mp_content_after
= content
;
3153 ct_type_str (int type
) {
3155 case CT_APPLICATION
:
3156 return "application";
3172 return "unknown_type";
3178 ct_subtype_str (int type
, int subtype
) {
3180 case CT_APPLICATION
:
3182 case APPLICATION_OCTETS
:
3184 case APPLICATION_POSTSCRIPT
:
3185 return "postscript";
3187 return "unknown_app_subtype";
3191 case MESSAGE_RFC822
:
3193 case MESSAGE_PARTIAL
:
3195 case MESSAGE_EXTERNAL
:
3198 return "unknown_msg_subtype";
3204 case MULTI_ALTERNATE
:
3205 return "alternative";
3208 case MULTI_PARALLEL
:
3213 return "unknown_multipart_subtype";
3224 return "unknown_text_subtype";
3227 return "unknown_type";
3233 ct_str_type (const char *type
) {
3234 struct str2init
*s2i
;
3236 for (s2i
= str2cts
; s2i
->si_key
; ++s2i
) {
3237 if (! strcasecmp (type
, s2i
->si_key
)) {
3241 if (! s2i
->si_key
&& ! uprf (type
, "X-")) {
3250 ct_str_subtype (int type
, const char *subtype
) {
3254 case CT_APPLICATION
:
3255 for (kv
= SubApplication
; kv
->kv_key
; ++kv
) {
3256 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3260 return kv
->kv_value
;
3262 for (kv
= SubMessage
; kv
->kv_key
; ++kv
) {
3263 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3267 return kv
->kv_value
;
3269 for (kv
= SubMultiPart
; kv
->kv_key
; ++kv
) {
3270 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3274 return kv
->kv_value
;
3276 for (kv
= SubText
; kv
->kv_key
; ++kv
) {
3277 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3281 return kv
->kv_value
;
3288 /* Find the content type and InitFunc for the CT. */
3289 const struct str2init
*
3290 get_ct_init (int type
) {
3291 const struct str2init
*sp
;
3293 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3294 if (type
== sp
->si_val
) {
3303 ce_str (int encoding
) {
3308 return "quoted-printable";
3324 /* Find the content type and InitFunc for the content encoding method. */
3325 const struct str2init
*
3326 get_ce_method (const char *method
) {
3327 struct str2init
*sp
;
3329 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3330 if (! strcasecmp (method
, sp
->si_key
)) {
3339 * Parse a series of MIME attributes (or parameters) given a header as
3342 * Arguments include:
3344 * filename - Name of input file (for error messages)
3345 * fieldname - Name of field being processed
3346 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3347 * Updated to point to end of attributes when finished.
3348 * param_head - Pointer to head of parameter list
3349 * param_tail - Pointer to tail of parameter list
3350 * commentp - Pointer to header comment pointer (may be NULL)
3352 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3353 * DONE to indicate a benign error (minor parsing error, but the program
3358 parse_header_attrs (const char *filename
, const char *fieldname
,
3359 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3362 char *cp
= *header_attrp
;
3368 struct sectlist
*next
;
3374 struct sectlist
*sechead
;
3375 struct parmlist
*next
;
3376 } *pp
, *pp2
, *phead
= NULL
;
3378 while (*cp
== ';') {
3379 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3380 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3383 while (isspace ((unsigned char) *cp
))
3387 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3392 if (! suppress_extraneous_trailing_semicolon_warning
) {
3394 "extraneous trailing ';' in message %s's %s: "
3396 filename
, fieldname
);
3398 extraneous_trailing_semicolon
= 1;
3402 /* down case the attribute name */
3403 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3404 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3405 *dp
= tolower ((unsigned char) *dp
);
3407 for (up
= dp
; isspace ((unsigned char) *dp
);)
3409 if (dp
== cp
|| *dp
!= '=') {
3411 "invalid parameter in message %s's %s: "
3412 "field\n%*sparameter %s (error detected at offset %d)",
3413 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3418 * To handle RFC 2231, we have to deal with the following extensions:
3420 * name*=encoded-value
3421 * name*<N>=part-N-of-a-parameter-value
3422 * name*<N>*=encoded-part-N-of-a-parameter-value
3425 * If there's a * right before the equal sign, it's encoded.
3426 * If there's a * and one or more digits, then it's section N.
3428 * Remember we can have one or the other, or both. cp points to
3429 * beginning of name, up points past the last character in the
3433 for (vp
= cp
; vp
< up
; vp
++) {
3434 if (*vp
== '*' && vp
< up
- 1) {
3437 } else if (*vp
== '*' && vp
== up
- 1) {
3439 } else if (partial
) {
3440 if (isdigit((unsigned char) *vp
))
3441 index
= *vp
- '0' + index
* 10;
3443 advise (NULL
, "invalid parameter index in message %s's "
3444 "%s: field\n%*s(parameter %s)", filename
,
3445 fieldname
, strlen(invo_name
) + 2, "", cp
);
3454 * Break out the parameter name and value sections and allocate
3458 nameptr
= mh_xmalloc(len
+ 1);
3459 strncpy(nameptr
, cp
, len
);
3460 nameptr
[len
] = '\0';
3462 for (dp
++; isspace ((unsigned char) *dp
);)
3467 * Single quotes delimit the character set and language tag.
3468 * They are required on the first section (or a complete
3473 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3479 charset
= mh_xmalloc(len
+ 1);
3480 strncpy(charset
, dp
, len
);
3481 charset
[len
] = '\0';
3487 advise(NULL
, "missing charset in message %s's %s: "
3488 "field\n%*s(parameter %s)", filename
, fieldname
,
3489 strlen(invo_name
) + 2, "", nameptr
);
3495 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3502 lang
= mh_xmalloc(len
+ 1);
3503 strncpy(lang
, dp
, len
);
3510 advise(NULL
, "missing language tag in message %s's %s: "
3511 "field\n%*s(parameter %s)", filename
, fieldname
,
3512 strlen(invo_name
) + 2, "", nameptr
);
3523 * At this point vp should be pointing at the beginning
3524 * of the encoded value/section. Continue until we reach
3525 * the end or get whitespace. But first, calculate the
3526 * length so we can allocate the correct buffer size.
3529 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3531 if (*(vp
+ 1) == '\0' ||
3532 !isxdigit((unsigned char) *(vp
+ 1)) ||
3533 *(vp
+ 2) == '\0' ||
3534 !isxdigit((unsigned char) *(vp
+ 2))) {
3535 advise(NULL
, "invalid encoded sequence in message "
3536 "%s's %s: field\n%*s(parameter %s)",
3537 filename
, fieldname
, strlen(invo_name
) + 2,
3551 up
= valptr
= mh_xmalloc(len
+ 1);
3553 for (vp
= dp
; istoken(*vp
); vp
++) {
3555 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3566 * A "normal" string. If it's got a leading quote, then we
3567 * strip the quotes out. Otherwise go until we reach the end
3568 * or get whitespace. Note we scan it twice; once to get the
3569 * length, then the second time copies it into the destination
3576 for (cp
= dp
+ 1;;) {
3581 "invalid quoted-string in message %s's %s: "
3582 "field\n%*s(parameter %s)",
3583 filename
, fieldname
, strlen(invo_name
) + 2, "",
3606 for (cp
= dp
; istoken (*cp
); cp
++) {
3611 valptr
= mh_xmalloc(len
+ 1);
3615 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3623 strncpy(valptr
, cp
= dp
, len
);
3631 * If 'partial' is set, we don't allocate a parameter now. We
3632 * put it on the parameter linked list to be reassembled later.
3634 * "phead" points to a list of all parameters we need to reassemble.
3635 * Each parameter has a list of sections. We insert the sections in
3640 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3641 if (strcasecmp(nameptr
, pp
->name
) == 0)
3646 pp
= mh_xmalloc(sizeof(*pp
));
3647 memset(pp
, 0, sizeof(*pp
));
3654 * Insert this into the section linked list
3657 sp
= mh_xmalloc(sizeof(*sp
));
3658 memset(sp
, 0, sizeof(*sp
));
3663 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3664 sp
->next
= pp
->sechead
;
3667 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3668 if (sp2
->index
== sp
->index
) {
3669 advise (NULL
, "duplicate index (%d) in message "
3670 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3671 filename
, fieldname
, strlen(invo_name
) + 2, "",
3676 if (sp2
->index
< sp
->index
&&
3677 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3678 sp
->next
= sp2
->next
;
3685 advise(NULL
, "Internal error: cannot insert partial "
3686 "param in message %s's %s: field\n%*s(parameter %s)",
3687 filename
, fieldname
, strlen(invo_name
) + 2, "",
3695 * Save our charset and lang tags.
3698 if (index
== 0 && encoded
) {
3701 pp
->charset
= charset
;
3707 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3708 pm
->pm_charset
= charset
;
3712 while (isspace ((unsigned char) *cp
))
3716 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3722 * Now that we're done, reassemble all of the partial parameters.
3725 for (pp
= phead
; pp
!= NULL
; ) {
3729 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3730 if (sp
->index
!= pindex
++) {
3731 advise(NULL
, "missing section %d for parameter in "
3732 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3733 filename
, fieldname
, strlen(invo_name
) + 2, "",
3740 p
= q
= mh_xmalloc(tlen
+ 1);
3741 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3742 memcpy(q
, sp
->value
, sp
->len
);
3752 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3753 pm
->pm_charset
= pp
->charset
;
3754 pm
->pm_lang
= pp
->lang
;
3765 * Return the charset for a particular content type.
3769 content_charset (CT ct
) {
3770 char *ret_charset
= NULL
;
3772 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3774 return ret_charset
? ret_charset
: getcpy ("US-ASCII");
3779 * Create a string based on a list of output parameters. Assume that this
3780 * parameter string will be appended to an existing header, so start out
3781 * with the separator (;). Perform RFC 2231 encoding when necessary.
3785 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3787 char *paramout
= NULL
;
3788 char line
[CPERLIN
* 2], *q
;
3789 int curlen
, index
, cont
, encode
, i
;
3790 size_t valoff
, numchars
;
3792 while (params
!= NULL
) {
3798 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3801 if (strlen(params
->pm_name
) > CPERLIN
) {
3802 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3808 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3811 * Loop until we get a parameter that fits within a line. We
3812 * assume new lines start with a tab, so check our overflow based
3822 * At this point we're definitely continuing the line, so
3823 * be sure to include the parameter name and section index.
3826 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3827 params
->pm_name
, index
);
3830 * Both of these functions do a NUL termination
3834 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3835 numchars
, valoff
, index
);
3837 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3848 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3853 * "line" starts with a ;\n\t, so that doesn't count against
3854 * the length. But add 8 since it starts with a tab; that's
3855 * how we end up with 5.
3858 initialwidth
= strlen(line
) + 5;
3861 * At this point the line should be built, so add it to our
3862 * current output buffer.
3865 paramout
= add(line
, paramout
);
3869 * If this won't fit on the line, start a new one. Save room in
3870 * case we need a semicolon on the end
3873 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3885 * At this point, we're either finishing a contined parameter, or
3886 * we're working on a new one.
3890 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3891 params
->pm_name
, index
);
3893 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3898 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3899 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3901 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3902 strlen(params
->pm_value
+ valoff
), valoff
);
3910 paramout
= add(line
, paramout
);
3911 initialwidth
+= strlen(line
);
3913 params
= params
->pm_next
;
3917 *offsetout
= initialwidth
;
3923 * Calculate the size of a parameter.
3927 * pm - The parameter being output
3928 * index - If continuing the parameter, the index of the section
3930 * valueoff - The current offset into the parameter value that we're
3931 * working on (previous sections have consumed valueoff bytes).
3932 * encode - Set if we should perform encoding on this parameter section
3933 * (given that we're consuming bytesfit bytes).
3934 * cont - Set if the remaining data in value will not fit on a single
3935 * line and will need to be continued.
3936 * bytesfit - The number of bytes that we can consume from the parameter
3937 * value and still fit on a completely new line. The
3938 * calculation assumes the new line starts with a tab,
3939 * includes the parameter name and any encoding, and fits
3940 * within CPERLIN bytes. Will always be at least 1.
3944 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3947 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3948 size_t len
= 0, fit
= 0;
3949 int fitlimit
= 0, eightbit
, maxfit
;
3954 * Add up the length. First, start with the parameter name.
3957 len
= strlen(pm
->pm_name
);
3960 * Scan the parameter value and see if we need to do encoding for this
3964 eightbit
= contains8bit(start
, NULL
);
3967 * Determine if we need to encode this section. Encoding is necessary if:
3969 * - There are any 8-bit characters at all and we're on the first
3971 * - There are 8-bit characters within N bytes of our section start.
3972 * N is calculated based on the number of bytes it would take to
3973 * reach CPERLIN. Specifically:
3974 * 8 (starting tab) +
3975 * strlen(param name) +
3976 * 4 ('* for section marker, '=', opening/closing '"')
3978 * is the number of bytes used by everything that isn't part of the
3979 * value. So that gets subtracted from CPERLIN.
3982 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3983 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3984 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3988 len
++; /* Add in equal sign */
3992 * We're using maxfit as a marker for how many characters we can
3993 * fit into the line. Bump it by two because we're not using quotes
4000 * If we don't have a charset or language tag in this parameter,
4004 if (! pm
->pm_charset
) {
4005 pm
->pm_charset
= getcpy(write_charset_8bit());
4006 if (strcasecmp(pm
->pm_charset
, "US-ASCII") == 0)
4007 adios(NULL
, "8-bit characters in parameter \"%s\", but "
4008 "local character set is US-ASCII", pm
->pm_name
);
4011 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
4013 len
++; /* For the encoding marker */
4016 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
4021 * We know we definitely need to include an index. maxfit already
4022 * includes the section marker.
4024 len
+= strlen(indexchar
);
4026 for (p
= start
; *p
!= '\0'; p
++) {
4027 if (isparamencode(*p
)) {
4035 * Just so there's no confusion: maxfit is counting OUTPUT
4036 * characters (post-encoding). fit is counting INPUT characters.
4038 if (! fitlimit
&& maxfit
>= 0)
4040 else if (! fitlimit
)
4045 * Calculate the string length, but add room for quoting \
4046 * and " if necessary. Also account for quotes at beginning
4049 for (p
= start
; *p
!= '\0'; p
++) {
4060 if (! fitlimit
&& maxfit
>= 0)
4062 else if (! fitlimit
)
4079 * Output an encoded parameter string.
4083 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4084 size_t valueoff
, int index
)
4086 size_t outlen
= 0, n
;
4087 char *endptr
= output
+ len
, *p
;
4090 * First, output the marker for an encoded string.
4098 * If the index is 0, output the character set and language tag.
4099 * If theses were NULL, they should have already been filled in
4104 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
4108 if (output
> endptr
) {
4109 advise(NULL
, "Internal error: parameter buffer overflow");
4115 * Copy over the value, encoding if necessary
4118 p
= pm
->pm_value
+ valueoff
;
4119 while (valuelen
-- > 0) {
4120 if (isparamencode(*p
)) {
4121 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
4128 if (output
> endptr
) {
4129 advise(NULL
, "Internal error: parameter buffer overflow");
4140 * Output a "normal" parameter, without encoding. Be sure to escape
4141 * quotes and backslashes if necessary.
4145 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4149 char *endptr
= output
+ len
, *p
;
4155 p
= pm
->pm_value
+ valueoff
;
4157 while (valuelen
-- > 0) {
4167 if (output
> endptr
) {
4168 advise(NULL
, "Internal error: parameter buffer overflow");
4173 if (output
- 2 > endptr
) {
4174 advise(NULL
, "Internal error: parameter buffer overflow");
4185 * Add a parameter to the parameter linked list
4189 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4191 PM pm
= mh_xmalloc(sizeof(*pm
));
4193 memset(pm
, 0, sizeof(*pm
));
4195 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4196 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4199 (*last
)->pm_next
= pm
;
4210 * Either replace a current parameter with a new value, or add the parameter
4211 * to the parameter linked list.
4215 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4219 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4220 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4222 * If nocopy is set, it's assumed that we own both name
4223 * and value. We don't need name, so we discard it now.
4228 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4233 return add_param(first
, last
, name
, value
, nocopy
);
4237 * Retrieve a parameter value from a parameter linked list. If the parameter
4238 * value needs converted to the local character set, do that now.
4242 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4244 while (first
!= NULL
) {
4245 if (strcasecmp(name
, first
->pm_name
) == 0) {
4247 return first
->pm_value
;
4249 return getcpy(get_param_value(first
, replace
));
4251 first
= first
->pm_next
;
4258 * Return a parameter value, converting to the local character set if
4262 char *get_param_value(PM pm
, char replace
)
4264 static char buffer
[4096]; /* I hope no parameters are larger */
4265 size_t bufsize
= sizeof(buffer
);
4270 ICONV_CONST
char *p
;
4271 #else /* HAVE_ICONV */
4273 #endif /* HAVE_ICONV */
4278 * If we don't have a character set indicated, it's assumed to be
4279 * US-ASCII. If it matches our character set, we don't need to convert
4283 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4284 strlen(pm
->pm_charset
))) {
4285 return pm
->pm_value
;
4289 * In this case, we need to convert. If we have iconv support, use
4290 * that. Otherwise, go through and simply replace every non-ASCII
4291 * character with the substitution character.
4296 bufsize
= sizeof(buffer
);
4297 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4299 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4300 if (cd
== (iconv_t
) -1) {
4304 inbytes
= strlen(pm
->pm_value
);
4308 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4309 if (errno
!= EILSEQ
) {
4314 * Reset shift state, substitute our character,
4315 * try to restart conversion.
4318 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4331 for (++p
, --inbytes
;
4332 inbytes
> 0 && (((unsigned char) *q
) & 0xc0) == 0x80;
4351 #endif /* HAVE_ICONV */
4354 * Take everything non-ASCII and substituite the replacement character
4358 bufsize
= sizeof(buffer
);
4359 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4360 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))