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
;
45 /* list of preferred type/subtype pairs, for -prefer */
46 char *preferred_types
[NPREFS
],
47 *preferred_subtypes
[NPREFS
];
52 * Structures for TEXT messages
54 struct k2v SubText
[] = {
55 { "plain", TEXT_PLAIN
},
56 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
57 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
58 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
61 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
64 * Structures for MULTIPART messages
66 struct k2v SubMultiPart
[] = {
67 { "mixed", MULTI_MIXED
},
68 { "alternative", MULTI_ALTERNATE
},
69 { "digest", MULTI_DIGEST
},
70 { "parallel", MULTI_PARALLEL
},
71 { "related", MULTI_RELATED
},
72 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
76 * Structures for MESSAGE messages
78 struct k2v SubMessage
[] = {
79 { "rfc822", MESSAGE_RFC822
},
80 { "partial", MESSAGE_PARTIAL
},
81 { "external-body", MESSAGE_EXTERNAL
},
82 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
86 * Structure for APPLICATION messages
88 struct k2v SubApplication
[] = {
89 { "octet-stream", APPLICATION_OCTETS
},
90 { "postscript", APPLICATION_POSTSCRIPT
},
91 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
95 * Mapping of names of CTE types in mhbuild directives
97 static struct k2v EncodingType
[] = {
100 { "q-p", CE_QUOTED
},
101 { "quoted-printable", CE_QUOTED
},
102 { "b64", CE_BASE64
},
103 { "base64", CE_BASE64
},
109 int find_cache (CT
, int, int *, char *, char *, int);
113 int type_ok (CT
, int);
114 void content_error (char *, CT
, char *, ...);
117 void free_encoding (CT
, int);
122 static CT
get_content (FILE *, char *, int);
123 static int get_comment (const char *, const char *, char **, char **);
125 static int InitGeneric (CT
);
126 static int InitText (CT
);
127 static int InitMultiPart (CT
);
128 static void reverse_parts (CT
);
129 static void prefer_parts(CT ct
);
130 static int InitMessage (CT
);
131 static int InitApplication (CT
);
132 static int init_encoding (CT
, OpenCEFunc
);
133 static unsigned long size_encoding (CT
);
134 static int InitBase64 (CT
);
135 static int openBase64 (CT
, char **);
136 static int InitQuoted (CT
);
137 static int openQuoted (CT
, char **);
138 static int Init7Bit (CT
);
139 static int openExternal (CT
, CT
, CE
, char **, int *);
140 static int InitFile (CT
);
141 static int openFile (CT
, char **);
142 static int InitFTP (CT
);
143 static int openFTP (CT
, char **);
144 static int InitMail (CT
);
145 static int openMail (CT
, char **);
146 static int readDigest (CT
, char *);
147 static int get_leftover_mp_content (CT
, int);
148 static int InitURL (CT
);
149 static int openURL (CT
, char **);
150 static int parse_header_attrs (const char *, const char *, char **, PM
*,
152 static size_t param_len(PM
, int, size_t, int *, int *, size_t *);
153 static size_t encode_param(PM
, char *, size_t, size_t, size_t, int);
154 static size_t normal_param(PM
, char *, size_t, size_t, size_t);
155 static int get_dispo (char *, CT
, int);
157 struct str2init str2cts
[] = {
158 { "application", CT_APPLICATION
, InitApplication
},
159 { "audio", CT_AUDIO
, InitGeneric
},
160 { "image", CT_IMAGE
, InitGeneric
},
161 { "message", CT_MESSAGE
, InitMessage
},
162 { "multipart", CT_MULTIPART
, InitMultiPart
},
163 { "text", CT_TEXT
, InitText
},
164 { "video", CT_VIDEO
, InitGeneric
},
165 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
166 { NULL
, CT_UNKNOWN
, NULL
},
169 struct str2init str2ces
[] = {
170 { "base64", CE_BASE64
, InitBase64
},
171 { "quoted-printable", CE_QUOTED
, InitQuoted
},
172 { "8bit", CE_8BIT
, Init7Bit
},
173 { "7bit", CE_7BIT
, Init7Bit
},
174 { "binary", CE_BINARY
, Init7Bit
},
175 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
176 { NULL
, CE_UNKNOWN
, NULL
},
180 * NOTE WELL: si_key MUST NOT have value of NOTOK
182 * si_key is 1 if access method is anonymous.
184 struct str2init str2methods
[] = {
185 { "afs", 1, InitFile
},
186 { "anon-ftp", 1, InitFTP
},
187 { "ftp", 0, InitFTP
},
188 { "local-file", 0, InitFile
},
189 { "mail-server", 0, InitMail
},
190 { "url", 0, InitURL
},
196 * Main entry point for parsing a MIME message or file.
197 * It returns the Content structure for the top level
198 * entity in the file.
202 parse_mime (char *file
)
211 * Check if file is actually standard input
213 if ((is_stdin
= !(strcmp (file
, "-")))) {
214 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
216 advise("mhparse", "unable to create temporary file in %s",
220 file
= add (tfile
, NULL
);
222 while ((n
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0) {
223 if (fwrite(buffer
, 1, n
, fp
) != n
) {
224 (void) m_unlink (file
);
225 advise (file
, "error copying to temporary file");
231 if (ferror (stdin
)) {
232 (void) m_unlink (file
);
233 advise ("stdin", "error reading");
237 (void) m_unlink (file
);
238 advise (file
, "error writing");
241 fseek (fp
, 0L, SEEK_SET
);
242 } else if ((fp
= fopen (file
, "r")) == NULL
) {
243 advise (file
, "unable to read");
247 if (!(ct
= get_content (fp
, file
, 1))) {
249 (void) m_unlink (file
);
250 advise (NULL
, "unable to decode %s", file
);
255 ct
->c_unlink
= 1; /* temp file to remove */
259 if (ct
->c_end
== 0L) {
260 fseek (fp
, 0L, SEEK_END
);
261 ct
->c_end
= ftell (fp
);
264 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
276 * Main routine for reading/parsing the headers
277 * of a message content.
279 * toplevel = 1 # we are at the top level of the message
280 * toplevel = 0 # we are inside message type or multipart type
281 * # other than multipart/digest
282 * toplevel = -1 # we are inside multipart/digest
283 * NB: on failure we will fclose(in)!
287 get_content (FILE *in
, char *file
, int toplevel
)
290 char buf
[BUFSIZ
], name
[NAMESZ
];
294 m_getfld_state_t gstate
= 0;
296 /* allocate the content structure */
297 if (!(ct
= (CT
) mh_xcalloc (1, sizeof(*ct
))))
298 adios (NULL
, "out of memory");
301 ct
->c_file
= add (file
, NULL
);
302 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
305 * Parse the header fields for this
306 * content into a linked list.
308 m_getfld_track_filepos (&gstate
, in
);
309 for (compnum
= 1;;) {
310 int bufsz
= sizeof buf
;
311 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
316 /* get copies of the buffers */
317 np
= add (name
, NULL
);
318 vp
= add (buf
, NULL
);
320 /* if necessary, get rest of field */
321 while (state
== FLDPLUS
) {
323 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
324 vp
= add (buf
, vp
); /* add to previous value */
327 /* Now add the header data to the list */
328 add_header (ct
, np
, vp
);
330 /* continue, to see if this isn't the last header field */
331 ct
->c_begin
= ftell (in
) + 1;
335 ct
->c_begin
= ftell (in
) - strlen (buf
);
339 ct
->c_begin
= ftell (in
);
344 adios (NULL
, "message format error in component #%d", compnum
);
347 adios (NULL
, "getfld() returned %d", state
);
350 /* break out of the loop */
353 m_getfld_state_destroy (&gstate
);
356 * Read the content headers. We will parse the
357 * MIME related header fields into their various
358 * structures and set internal flags related to
359 * content type/subtype, etc.
362 hp
= ct
->c_first_hf
; /* start at first header field */
364 /* Get MIME-Version field */
365 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
370 advise (NULL
, "message %s has multiple %s: fields",
371 ct
->c_file
, VRSN_FIELD
);
374 ct
->c_vrsn
= add (hp
->value
, NULL
);
376 /* Now, cleanup this field */
379 while (isspace ((unsigned char) *cp
))
381 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
383 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
384 if (!isspace ((unsigned char) *dp
))
388 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
391 get_comment (ct
->c_file
, VRSN_FIELD
, &cp
, NULL
) == NOTOK
)
394 for (dp
= cp
; istoken (*dp
); dp
++)
398 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
401 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
402 ct
->c_file
, VRSN_FIELD
, cp
);
405 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
406 /* Get Content-Type field */
407 struct str2init
*s2i
;
408 CI ci
= &ct
->c_ctinfo
;
410 /* Check if we've already seen a Content-Type header */
412 advise (NULL
, "message %s has multiple %s: fields",
413 ct
->c_file
, TYPE_FIELD
);
417 /* Parse the Content-Type field */
418 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
422 * Set the Init function and the internal
423 * flag for this content type.
425 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
426 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
428 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
430 ct
->c_type
= s2i
->si_val
;
431 ct
->c_ctinitfnx
= s2i
->si_init
;
433 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
434 /* Get Content-Transfer-Encoding field */
436 struct str2init
*s2i
;
439 * Check if we've already seen the
440 * Content-Transfer-Encoding field
443 advise (NULL
, "message %s has multiple %s: fields",
444 ct
->c_file
, ENCODING_FIELD
);
448 /* get copy of this field */
449 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
451 while (isspace ((unsigned char) *cp
))
453 for (dp
= cp
; istoken (*dp
); dp
++)
459 * Find the internal flag and Init function
460 * for this transfer encoding.
462 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
463 if (!strcasecmp (cp
, s2i
->si_key
))
465 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
468 ct
->c_encoding
= s2i
->si_val
;
470 /* Call the Init function for this encoding */
471 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
474 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
475 /* Get Content-MD5 field */
481 if (ct
->c_digested
) {
482 advise (NULL
, "message %s has multiple %s: fields",
483 ct
->c_file
, MD5_FIELD
);
487 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
489 while (isspace ((unsigned char) *cp
))
491 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
493 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
494 if (!isspace ((unsigned char) *dp
))
498 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
501 get_comment (ct
->c_file
, MD5_FIELD
, &cp
, NULL
) == NOTOK
) {
506 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
514 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
515 /* Get Content-ID field */
516 ct
->c_id
= add (hp
->value
, ct
->c_id
);
518 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
519 /* Get Content-Description field */
520 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
522 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
523 /* Get Content-Disposition field */
524 if (get_dispo(hp
->value
, ct
, 0) == NOTOK
)
529 hp
= hp
->next
; /* next header field */
533 * Check if we saw a Content-Type field.
534 * If not, then assign a default value for
535 * it, and the Init function.
539 * If we are inside a multipart/digest message,
540 * so default type is message/rfc822
543 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
545 ct
->c_type
= CT_MESSAGE
;
546 ct
->c_ctinitfnx
= InitMessage
;
549 * Else default type is text/plain
551 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
553 ct
->c_type
= CT_TEXT
;
554 ct
->c_ctinitfnx
= InitText
;
558 /* Use default Transfer-Encoding, if necessary */
560 ct
->c_encoding
= CE_7BIT
;
573 * small routine to add header field to list
577 add_header (CT ct
, char *name
, char *value
)
581 /* allocate header field structure */
582 hp
= mh_xmalloc (sizeof(*hp
));
584 /* link data into header structure */
589 /* link header structure into the list */
590 if (ct
->c_first_hf
== NULL
) {
591 ct
->c_first_hf
= hp
; /* this is the first */
594 ct
->c_last_hf
->next
= hp
; /* add it to the end */
603 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
604 * directives. Fills in the information of the CTinfo structure.
607 get_ctinfo (char *cp
, CT ct
, int magic
)
616 /* store copy of Content-Type line */
617 cp
= ct
->c_ctline
= add (cp
, NULL
);
619 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
622 /* change newlines to spaces */
623 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
626 /* trim trailing spaces */
627 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
628 if (!isspace ((unsigned char) *dp
))
633 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
635 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
636 &ci
->ci_comment
) == NOTOK
)
639 for (dp
= cp
; istoken (*dp
); dp
++)
642 ci
->ci_type
= add (cp
, NULL
); /* store content type */
646 advise (NULL
, "invalid %s: field in message %s (empty type)",
647 TYPE_FIELD
, ct
->c_file
);
651 /* down case the content type string */
652 for (dp
= ci
->ci_type
; *dp
; dp
++)
653 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
654 *dp
= tolower ((unsigned char) *dp
);
656 while (isspace ((unsigned char) *cp
))
659 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
660 &ci
->ci_comment
) == NOTOK
)
665 ci
->ci_subtype
= add ("", NULL
);
670 while (isspace ((unsigned char) *cp
))
673 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
674 &ci
->ci_comment
) == NOTOK
)
677 for (dp
= cp
; istoken (*dp
); dp
++)
680 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
683 if (!*ci
->ci_subtype
) {
685 "invalid %s: field in message %s (empty subtype for \"%s\")",
686 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
690 /* down case the content subtype string */
691 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
692 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
693 *dp
= tolower ((unsigned char) *dp
);
696 while (isspace ((unsigned char) *cp
))
699 if (*cp
== '(' && get_comment (ct
->c_file
, TYPE_FIELD
, &cp
,
700 &ci
->ci_comment
) == NOTOK
)
703 if ((status
= parse_header_attrs (ct
->c_file
, TYPE_FIELD
, &cp
,
704 &ci
->ci_first_pm
, &ci
->ci_last_pm
,
705 &ci
->ci_comment
)) != OK
) {
706 return status
== NOTOK
? NOTOK
: OK
;
710 * Get any <Content-Id> given in buffer
712 if (magic
&& *cp
== '<') {
717 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
718 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
724 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
730 while (isspace ((unsigned char) *cp
))
735 * Get any [Content-Description] given in buffer.
737 if (magic
&& *cp
== '[') {
739 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
743 advise (NULL
, "invalid description in message %s", ct
->c_file
);
751 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
757 while (isspace ((unsigned char) *cp
))
762 * Get any {Content-Disposition} given in buffer.
764 if (magic
&& *cp
== '{') {
766 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
770 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
778 if (get_dispo(cp
, ct
, 1) != OK
)
784 while (isspace ((unsigned char) *cp
))
789 * Get any extension directives (right now just the content transfer
790 * encoding, but maybe others) that we care about.
793 if (magic
&& *cp
== '*') {
795 * See if it's a CTE we match on
800 while (*cp
!= '\0' && ! isspace((unsigned char) *cp
))
804 advise (NULL
, "invalid null transfer encoding specification");
811 ct
->c_reqencoding
= CE_UNKNOWN
;
813 for (kv
= EncodingType
; kv
->kv_key
; kv
++) {
814 if (strcasecmp(kv
->kv_key
, dp
) == 0) {
815 ct
->c_reqencoding
= kv
->kv_value
;
820 if (ct
->c_reqencoding
== CE_UNKNOWN
) {
821 advise (NULL
, "invalid CTE specification: \"%s\"", dp
);
825 while (isspace ((unsigned char) *cp
))
830 * Check if anything is left over
834 ci
->ci_magic
= add (cp
, NULL
);
836 /* If there is a Content-Disposition header and it doesn't
837 have a *filename=, extract it from the magic contents.
838 The r1bindex call skips any leading directory
840 if (ct
->c_dispo_type
&&
841 !get_param(ct
->c_dispo_first
, "filename", '_', 1)) {
842 add_param(&ct
->c_dispo_first
, &ct
->c_dispo_last
, "filename",
843 r1bindex(ci
->ci_magic
, '/'), 0);
848 "extraneous information in message %s's %s: field\n%*s(%s)",
849 ct
->c_file
, TYPE_FIELD
, strlen(invo_name
) + 2, "", cp
);
857 * Parse out a Content-Disposition header. A lot of this is cribbed from
861 get_dispo (char *cp
, CT ct
, int buildflag
)
863 char *dp
, *dispoheader
;
868 * Save the whole copy of the Content-Disposition header, unless we're
869 * processing a mhbuild directive. A NULL c_dispo will be a flag to
870 * mhbuild that the disposition header needs to be generated at that
874 dispoheader
= cp
= add(cp
, NULL
);
876 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
879 /* change newlines to spaces */
880 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
883 /* trim trailing spaces */
884 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
885 if (!isspace ((unsigned char) *dp
))
890 fprintf (stderr
, "%s: %s\n", DISPO_FIELD
, cp
);
892 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) ==
898 for (dp
= cp
; istoken (*dp
); dp
++)
901 ct
->c_dispo_type
= add (cp
, NULL
); /* store disposition type */
904 if (*cp
== '(' && get_comment (ct
->c_file
, DISPO_FIELD
, &cp
, NULL
) == NOTOK
)
907 if ((status
= parse_header_attrs (ct
->c_file
, DISPO_FIELD
, &cp
,
908 &ct
->c_dispo_first
, &ct
->c_dispo_last
,
910 if (status
== NOTOK
) {
916 "extraneous information in message %s's %s: field\n%*s(%s)",
917 ct
->c_file
, DISPO_FIELD
, strlen(invo_name
) + 2, "", cp
);
923 ct
->c_dispo
= dispoheader
;
930 get_comment (const char *filename
, const char *fieldname
, char **ap
,
935 char c
, buffer
[BUFSIZ
], *dp
;
945 advise (NULL
, "invalid comment in message %s's %s: field",
946 filename
, fieldname
);
951 if ((c
= *cp
++) == '\0')
974 if ((dp
= *commentp
)) {
975 *commentp
= concat (dp
, " ", buffer
, NULL
);
978 *commentp
= add (buffer
, NULL
);
982 while (isspace ((unsigned char) *cp
))
993 * Handles content types audio, image, and video.
994 * There's not much to do right here.
1002 return OK
; /* not much to do here */
1013 char buffer
[BUFSIZ
];
1018 CI ci
= &ct
->c_ctinfo
;
1020 /* check for missing subtype */
1021 if (!*ci
->ci_subtype
)
1022 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1025 ct
->c_subtype
= ct_str_subtype (CT_TEXT
, ci
->ci_subtype
);
1027 /* allocate text character set structure */
1028 if ((t
= (struct text
*) mh_xcalloc (1, sizeof(*t
))) == NULL
)
1029 adios (NULL
, "out of memory");
1030 ct
->c_ctparams
= (void *) t
;
1032 /* scan for charset parameter */
1033 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
)
1034 if (!strcasecmp (pm
->pm_name
, "charset"))
1037 /* check if content specified a character set */
1039 chset
= pm
->pm_value
;
1040 t
->tx_charset
= CHARSET_SPECIFIED
;
1042 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1046 * If we can not handle character set natively,
1047 * then check profile for string to modify the
1048 * terminal or display method.
1050 * termproc is for mhshow, though mhlist -debug prints it, too.
1052 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1053 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1054 if ((cp
= context_find (buffer
)))
1055 ct
->c_termproc
= getcpy (cp
);
1067 InitMultiPart (CT ct
)
1077 struct multipart
*m
;
1078 struct part
*part
, **next
;
1079 CI ci
= &ct
->c_ctinfo
;
1084 * The encoding for multipart messages must be either
1085 * 7bit, 8bit, or binary (per RFC2045).
1087 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1088 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1089 /* Copy the Content-Transfer-Encoding header field body so we can
1090 remove any trailing whitespace and leading blanks from it. */
1091 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1093 bp
= cte
+ strlen (cte
) - 1;
1094 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1095 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1098 "\"%s/%s\" type in message %s must be encoded in\n"
1099 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1100 "is to\nmanually edit the file and change the \"%s\"\n"
1101 "Content-Transfer-Encoding to one of those. For now",
1102 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1109 ct
->c_subtype
= ct_str_subtype (CT_MULTIPART
, ci
->ci_subtype
);
1112 * Check for "boundary" parameter, which is
1113 * required for multipart messages.
1116 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1117 if (!strcasecmp (pm
->pm_name
, "boundary")) {
1123 /* complain if boundary parameter is missing */
1126 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1127 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1131 /* allocate primary structure for multipart info */
1132 if ((m
= (struct multipart
*) mh_xcalloc (1, sizeof(*m
))) == NULL
)
1133 adios (NULL
, "out of memory");
1134 ct
->c_ctparams
= (void *) m
;
1136 /* check if boundary parameter contains only whitespace characters */
1137 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1140 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1141 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1145 /* remove trailing whitespace from boundary parameter */
1146 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1147 if (!isspace ((unsigned char) *dp
))
1151 /* record boundary separators */
1152 m
->mp_start
= concat (bp
, "\n", NULL
);
1153 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1155 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1156 advise (ct
->c_file
, "unable to open for reading");
1160 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1162 next
= &m
->mp_parts
;
1166 while ((gotlen
= getline(&bufp
, &buflen
, fp
)) != -1) {
1171 if (bufp
[0] != '-' || bufp
[1] != '-')
1174 if (strcmp (bufp
+ 2, m
->mp_start
))
1177 if ((part
= (struct part
*) mh_xcalloc (1, sizeof(*part
))) == NULL
)
1178 adios (NULL
, "out of memory");
1180 next
= &part
->mp_next
;
1182 if (!(p
= get_content (fp
, ct
->c_file
,
1183 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1191 fseek (fp
, pos
, SEEK_SET
);
1194 if (strcmp (bufp
+ 2, m
->mp_start
) == 0) {
1198 p
->c_end
= ftell(fp
) - (gotlen
+ 1);
1199 if (p
->c_end
< p
->c_begin
)
1200 p
->c_begin
= p
->c_end
;
1205 if (strcmp (bufp
+ 2, m
->mp_stop
) == 0)
1211 if (! suppress_bogus_mp_content_warning
) {
1212 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1214 bogus_mp_content
= 1;
1216 if (!inout
&& part
) {
1218 p
->c_end
= ct
->c_end
;
1220 if (p
->c_begin
>= p
->c_end
) {
1221 for (next
= &m
->mp_parts
; *next
!= part
;
1222 next
= &((*next
)->mp_next
))
1226 free ((char *) part
);
1231 /* reverse the order of the parts for multipart/alternative */
1232 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1238 * label all subparts with part number, and
1239 * then initialize the content of the subpart.
1244 char partnam
[BUFSIZ
];
1247 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1248 pp
= partnam
+ strlen (partnam
);
1253 for (part
= m
->mp_parts
, partnum
= 1; part
;
1254 part
= part
->mp_next
, partnum
++) {
1257 sprintf (pp
, "%d", partnum
);
1258 p
->c_partno
= add (partnam
, NULL
);
1260 /* initialize the content of the subparts */
1261 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1270 get_leftover_mp_content (ct
, 1);
1271 get_leftover_mp_content (ct
, 0);
1281 * reverse the order of the parts of a multipart/alternative,
1282 * presumably to put the "most favored" alternative first, for
1283 * ease of choosing/displaying it later on. from a mail message on
1284 * nmh-workers, from kenh:
1285 * "Stock" MH 6.8.5 did not have a reverse_parts() function, but I
1286 * see code in mhn that did the same thing... Acccording to the RCS
1287 * logs, that code was around from the initial checkin of mhn.c by
1288 * John Romine in 1992, which is as far back as we have."
1291 reverse_parts (CT ct
)
1293 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1297 /* Reverse the order of its parts by walking the mp_parts list
1298 and pushing each node to the front. */
1299 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1300 next
= part
->mp_next
;
1301 part
->mp_next
= m
->mp_parts
;
1307 move_preferred_part (CT ct
, char *type
, char *subtype
)
1309 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1310 struct part
*part
, *prev
, *head
, *nhead
, *ntail
;
1314 /* move the matching part(s) to the head of the list: walk the
1315 * list of parts, move matching parts to a new list (maintaining
1316 * their order), and finally, concatenate the old list onto the
1323 head
->mp_next
= m
->mp_parts
;
1324 nhead
->mp_next
= NULL
;
1328 part
= head
->mp_next
;
1329 while (part
!= NULL
) {
1330 ci
= &part
->mp_part
->c_ctinfo
;
1331 if (!strcasecmp(ci
->ci_type
, type
) &&
1332 (!subtype
|| !strcasecmp(ci
->ci_subtype
, subtype
))) {
1333 prev
->mp_next
= part
->mp_next
;
1334 part
->mp_next
= NULL
;
1335 ntail
->mp_next
= part
;
1337 part
= prev
->mp_next
;
1340 part
= prev
->mp_next
;
1343 ntail
->mp_next
= head
->mp_next
;
1344 m
->mp_parts
= nhead
->mp_next
;
1349 * move parts that match the user's preferences (-prefer) to the head
1350 * of the line. process preferences in reverse so first one given
1351 * ends up first in line
1357 for (i
= npreferred
-1; i
>= 0; i
--)
1358 move_preferred_part(ct
, preferred_types
[i
], preferred_subtypes
[i
]);
1363 /* parse_mime() arranges alternates in reverse (priority) order. This
1364 function can be used to reverse them back. This will put, for
1365 example, a text/plain part before a text/html part in a
1366 multipart/alternative part, for example, where it belongs. */
1368 reverse_alternative_parts (CT ct
) {
1369 if (ct
->c_type
== CT_MULTIPART
) {
1370 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1373 if (ct
->c_subtype
== MULTI_ALTERNATE
) {
1377 /* And call recursively on each part of a multipart. */
1378 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1379 reverse_alternative_parts (part
->mp_part
);
1392 CI ci
= &ct
->c_ctinfo
;
1394 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1396 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1397 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1401 /* check for missing subtype */
1402 if (!*ci
->ci_subtype
)
1403 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1406 ct
->c_subtype
= ct_str_subtype (CT_MESSAGE
, ci
->ci_subtype
);
1408 switch (ct
->c_subtype
) {
1409 case MESSAGE_RFC822
:
1412 case MESSAGE_PARTIAL
:
1417 if ((p
= (struct partial
*) mh_xcalloc (1, sizeof(*p
))) == NULL
)
1418 adios (NULL
, "out of memory");
1419 ct
->c_ctparams
= (void *) p
;
1421 /* scan for parameters "id", "number", and "total" */
1422 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1423 if (!strcasecmp (pm
->pm_name
, "id")) {
1424 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1427 if (!strcasecmp (pm
->pm_name
, "number")) {
1428 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1429 || p
->pm_partno
< 1) {
1432 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1433 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1434 ct
->c_file
, TYPE_FIELD
);
1439 if (!strcasecmp (pm
->pm_name
, "total")) {
1440 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1449 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1451 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1452 ci
->ci_type
, ci
->ci_subtype
,
1453 ct
->c_file
, TYPE_FIELD
);
1459 case MESSAGE_EXTERNAL
:
1466 if ((e
= (struct exbody
*) mh_xcalloc (1, sizeof(*e
))) == NULL
)
1467 adios (NULL
, "out of memory");
1468 ct
->c_ctparams
= (void *) e
;
1471 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1472 advise (ct
->c_file
, "unable to open for reading");
1476 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1478 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1486 p
->c_ceopenfnx
= NULL
;
1487 if ((exresult
= params_external (ct
, 0)) != NOTOK
1488 && p
->c_ceopenfnx
== openMail
) {
1492 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1494 content_error (NULL
, ct
,
1495 "empty body for access-type=mail-server");
1499 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1500 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1502 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1504 adios ("failed", "fread");
1507 adios (NULL
, "unexpected EOF from fread");
1510 bp
+= cc
, size
-= cc
;
1517 p
->c_end
= p
->c_begin
;
1522 if (exresult
== NOTOK
)
1524 if (e
->eb_flags
== NOTOK
)
1527 switch (p
->c_type
) {
1532 if (p
->c_subtype
!= MESSAGE_RFC822
)
1536 e
->eb_partno
= ct
->c_partno
;
1538 (*p
->c_ctinitfnx
) (p
);
1553 params_external (CT ct
, int composing
)
1556 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1557 CI ci
= &ct
->c_ctinfo
;
1559 ct
->c_ceopenfnx
= NULL
;
1560 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1561 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1562 struct str2init
*s2i
;
1563 CT p
= e
->eb_content
;
1565 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1566 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1569 e
->eb_access
= pm
->pm_value
;
1570 e
->eb_flags
= NOTOK
;
1571 p
->c_encoding
= CE_EXTERNAL
;
1574 e
->eb_access
= s2i
->si_key
;
1575 e
->eb_flags
= s2i
->si_val
;
1576 p
->c_encoding
= CE_EXTERNAL
;
1578 /* Call the Init function for this external type */
1579 if ((*s2i
->si_init
)(p
) == NOTOK
)
1583 if (!strcasecmp (pm
->pm_name
, "name")) {
1584 e
->eb_name
= pm
->pm_value
;
1587 if (!strcasecmp (pm
->pm_name
, "permission")) {
1588 e
->eb_permission
= pm
->pm_value
;
1591 if (!strcasecmp (pm
->pm_name
, "site")) {
1592 e
->eb_site
= pm
->pm_value
;
1595 if (!strcasecmp (pm
->pm_name
, "directory")) {
1596 e
->eb_dir
= pm
->pm_value
;
1599 if (!strcasecmp (pm
->pm_name
, "mode")) {
1600 e
->eb_mode
= pm
->pm_value
;
1603 if (!strcasecmp (pm
->pm_name
, "size")) {
1604 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1607 if (!strcasecmp (pm
->pm_name
, "server")) {
1608 e
->eb_server
= pm
->pm_value
;
1611 if (!strcasecmp (pm
->pm_name
, "subject")) {
1612 e
->eb_subject
= pm
->pm_value
;
1615 if (!strcasecmp (pm
->pm_name
, "url")) {
1617 * According to RFC 2017, we have to remove all whitespace from
1621 char *u
, *p
= pm
->pm_value
;
1622 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1624 for (; *p
!= '\0'; p
++) {
1625 if (! isspace((unsigned char) *p
))
1632 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1633 e
->eb_body
= getcpy (pm
->pm_value
);
1638 if (!e
->eb_access
) {
1640 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1641 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1654 InitApplication (CT ct
)
1656 CI ci
= &ct
->c_ctinfo
;
1659 ct
->c_subtype
= ct_str_subtype (CT_APPLICATION
, ci
->ci_subtype
);
1666 * TRANSFER ENCODINGS
1670 init_encoding (CT ct
, OpenCEFunc openfnx
)
1672 ct
->c_ceopenfnx
= openfnx
;
1673 ct
->c_ceclosefnx
= close_encoding
;
1674 ct
->c_cesizefnx
= size_encoding
;
1681 close_encoding (CT ct
)
1683 CE ce
= &ct
->c_cefile
;
1692 static unsigned long
1693 size_encoding (CT ct
)
1698 CE ce
= &ct
->c_cefile
;
1701 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1702 return (long) st
.st_size
;
1705 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1706 return (long) st
.st_size
;
1711 if (ct
->c_encoding
== CE_EXTERNAL
)
1712 return (ct
->c_end
- ct
->c_begin
);
1715 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1716 return (ct
->c_end
- ct
->c_begin
);
1718 if (fstat (fd
, &st
) != NOTOK
)
1719 size
= (long) st
.st_size
;
1723 (*ct
->c_ceclosefnx
) (ct
);
1732 static unsigned char b642nib
[0x80] = {
1733 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
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, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1739 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1740 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1741 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1742 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1743 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1744 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1745 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1746 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1747 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1748 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1755 return init_encoding (ct
, openBase64
);
1760 openBase64 (CT ct
, char **file
)
1762 int bitno
, cc
, digested
;
1763 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1765 unsigned char value
, b
;
1766 char *cp
, *ep
, buffer
[BUFSIZ
];
1767 /* sbeck -- handle suffixes */
1769 CE ce
= &ct
->c_cefile
;
1773 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1778 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1779 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1785 if (*file
== NULL
) {
1788 ce
->ce_file
= add (*file
, NULL
);
1792 /* sbeck@cise.ufl.edu -- handle suffixes */
1794 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
1795 if (ce
->ce_unlink
) {
1796 /* Create temporary file with filename extension. */
1797 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1798 adios(NULL
, "unable to create temporary file in %s",
1802 ce
->ce_file
= add (cp
, ce
->ce_file
);
1804 } else if (*file
== NULL
) {
1806 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1807 adios(NULL
, "unable to create temporary file in %s",
1810 ce
->ce_file
= add (tempfile
, NULL
);
1813 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1814 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1818 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1819 adios (NULL
, "internal error(1)");
1822 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1823 content_error (ct
->c_file
, ct
, "unable to open for reading");
1829 if ((digested
= ct
->c_digested
))
1830 MD5Init (&mdContext
);
1836 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1838 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1840 content_error (ct
->c_file
, ct
, "error reading from");
1844 content_error (NULL
, ct
, "premature eof");
1852 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1855 if (isspace ((unsigned char) *cp
))
1857 if (skip
|| (((unsigned char) *cp
) & 0x80)
1858 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1860 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1861 (unsigned char) *cp
,
1862 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1865 content_error (NULL
, ct
,
1866 "invalid BASE64 encoding -- continuing");
1870 bits
|= value
<< bitno
;
1872 if ((bitno
-= 6) < 0) {
1873 b
= (bits
>> 16) & 0xff;
1874 if (!text
|| b
!= '\r')
1875 putc ((char) b
, ce
->ce_fp
);
1877 MD5Update (&mdContext
, &b
, 1);
1879 b
= (bits
>> 8) & 0xff;
1880 if (! text
|| b
!= '\r')
1881 putc ((char) b
, ce
->ce_fp
);
1883 MD5Update (&mdContext
, &b
, 1);
1886 if (! text
|| b
!= '\r')
1887 putc ((char) b
, ce
->ce_fp
);
1889 MD5Update (&mdContext
, &b
, 1);
1893 if (ferror (ce
->ce_fp
)) {
1894 content_error (ce
->ce_file
, ct
,
1895 "error writing to");
1898 bitno
= 18, bits
= 0L, skip
= 0;
1904 goto self_delimiting
;
1913 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1915 content_error (NULL
, ct
, "invalid BASE64 encoding");
1920 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1922 if (fflush (ce
->ce_fp
)) {
1923 content_error (ce
->ce_file
, ct
, "error writing to");
1928 unsigned char digest
[16];
1930 MD5Final (digest
, &mdContext
);
1931 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1932 sizeof(digest
) / sizeof(digest
[0])))
1933 content_error (NULL
, ct
,
1934 "content integrity suspect (digest mismatch) -- continuing");
1937 fprintf (stderr
, "content integrity confirmed\n");
1940 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1943 *file
= ce
->ce_file
;
1948 return fileno (ce
->ce_fp
);
1955 free_encoding (ct
, 0);
1964 static char hex2nib
[0x80] = {
1965 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1972 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1973 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1974 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1975 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1976 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1977 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1978 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1979 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1980 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1987 return init_encoding (ct
, openQuoted
);
1992 openQuoted (CT ct
, char **file
)
1994 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
2000 CE ce
= &ct
->c_cefile
;
2001 /* sbeck -- handle suffixes */
2006 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2011 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2012 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2018 if (*file
== NULL
) {
2021 ce
->ce_file
= add (*file
, NULL
);
2025 /* sbeck@cise.ufl.edu -- handle suffixes */
2027 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2028 if (ce
->ce_unlink
) {
2029 /* Create temporary file with filename extension. */
2030 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2031 adios(NULL
, "unable to create temporary file in %s",
2035 ce
->ce_file
= add (cp
, ce
->ce_file
);
2037 } else if (*file
== NULL
) {
2039 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2040 adios(NULL
, "unable to create temporary file in %s",
2043 ce
->ce_file
= add (tempfile
, NULL
);
2046 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2047 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2051 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2052 adios (NULL
, "internal error(2)");
2055 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2056 content_error (ct
->c_file
, ct
, "unable to open for reading");
2062 if ((digested
= ct
->c_digested
))
2063 MD5Init (&mdContext
);
2070 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2072 if ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) == -1) {
2073 content_error (NULL
, ct
, "premature eof");
2077 if ((cc
= gotlen
) > len
)
2081 for (ep
= (cp
= bufp
) + cc
- 1; cp
<= ep
; ep
--)
2082 if (!isspace ((unsigned char) *ep
))
2086 for (; cp
< ep
; cp
++) {
2088 /* in an escape sequence */
2090 /* at byte 1 of an escape sequence */
2091 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2092 /* next is byte 2 */
2095 /* at byte 2 of an escape sequence */
2097 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2098 putc (mask
, ce
->ce_fp
);
2100 MD5Update (&mdContext
, &mask
, 1);
2101 if (ferror (ce
->ce_fp
)) {
2102 content_error (ce
->ce_file
, ct
, "error writing to");
2105 /* finished escape sequence; next may be literal or a new
2106 * escape sequence */
2109 /* on to next byte */
2113 /* not in an escape sequence */
2115 /* starting an escape sequence, or invalid '='? */
2116 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2117 /* "=\n" soft line break, eat the \n */
2121 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2122 /* We don't have 2 bytes left, so this is an invalid
2123 * escape sequence; just show the raw bytes (below). */
2124 } else if (isxdigit ((unsigned char) cp
[1]) &&
2125 isxdigit ((unsigned char) cp
[2])) {
2126 /* Next 2 bytes are hex digits, making this a valid escape
2127 * sequence; let's decode it (above). */
2131 /* One or both of the next 2 is out of range, making this
2132 * an invalid escape sequence; just show the raw bytes
2137 /* Just show the raw byte. */
2138 putc (*cp
, ce
->ce_fp
);
2141 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2143 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2146 if (ferror (ce
->ce_fp
)) {
2147 content_error (ce
->ce_file
, ct
, "error writing to");
2153 content_error (NULL
, ct
,
2154 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2158 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2160 if (fflush (ce
->ce_fp
)) {
2161 content_error (ce
->ce_file
, ct
, "error writing to");
2166 unsigned char digest
[16];
2168 MD5Final (digest
, &mdContext
);
2169 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2170 sizeof(digest
) / sizeof(digest
[0])))
2171 content_error (NULL
, ct
,
2172 "content integrity suspect (digest mismatch) -- continuing");
2175 fprintf (stderr
, "content integrity confirmed\n");
2178 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2181 *file
= ce
->ce_file
;
2187 return fileno (ce
->ce_fp
);
2190 free_encoding (ct
, 0);
2207 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2210 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2216 open7Bit (CT ct
, char **file
)
2218 int cc
, fd
, len
, own_ct_fp
= 0;
2219 char buffer
[BUFSIZ
];
2220 /* sbeck -- handle suffixes */
2223 CE ce
= &ct
->c_cefile
;
2226 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2231 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2232 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2238 if (*file
== NULL
) {
2241 ce
->ce_file
= add (*file
, NULL
);
2245 /* sbeck@cise.ufl.edu -- handle suffixes */
2247 if ((cp
= context_find_by_type ("suffix", ci
->ci_type
, ci
->ci_subtype
))) {
2248 if (ce
->ce_unlink
) {
2249 /* Create temporary file with filename extension. */
2250 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2251 adios(NULL
, "unable to create temporary file in %s",
2255 ce
->ce_file
= add (cp
, ce
->ce_file
);
2257 } else if (*file
== NULL
) {
2259 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2260 adios(NULL
, "unable to create temporary file in %s",
2263 ce
->ce_file
= add (tempfile
, NULL
);
2266 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2267 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2271 if (ct
->c_type
== CT_MULTIPART
) {
2272 CI ci
= &ct
->c_ctinfo
;
2276 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2277 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2278 + 1 + strlen (ci
->ci_subtype
);
2279 buffer
= output_params(len
, ci
->ci_first_pm
, &len
, 0);
2282 fputs (buffer
, ce
->ce_fp
);
2286 if (ci
->ci_comment
) {
2287 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2288 fputs ("\n\t", ce
->ce_fp
);
2292 putc (' ', ce
->ce_fp
);
2295 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2298 fprintf (ce
->ce_fp
, "\n");
2300 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2302 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2304 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2305 fprintf (ce
->ce_fp
, "\n");
2308 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2309 adios (NULL
, "internal error(3)");
2312 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2313 content_error (ct
->c_file
, ct
, "unable to open for reading");
2319 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2321 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2323 content_error (ct
->c_file
, ct
, "error reading from");
2327 content_error (NULL
, ct
, "premature eof");
2335 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
) < cc
) {
2336 advise ("open7Bit", "fwrite");
2338 if (ferror (ce
->ce_fp
)) {
2339 content_error (ce
->ce_file
, ct
, "error writing to");
2344 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2346 if (fflush (ce
->ce_fp
)) {
2347 content_error (ce
->ce_file
, ct
, "error writing to");
2351 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2354 *file
= ce
->ce_file
;
2359 return fileno (ce
->ce_fp
);
2362 free_encoding (ct
, 0);
2376 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2378 char cachefile
[BUFSIZ
];
2381 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2386 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2387 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2393 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2394 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2395 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2396 ce
->ce_file
= getcpy (cachefile
);
2400 admonish (cachefile
, "unable to fopen for reading");
2404 *fd
= fileno (ce
->ce_fp
);
2408 *file
= ce
->ce_file
;
2409 *fd
= fileno (ce
->ce_fp
);
2420 return init_encoding (ct
, openFile
);
2425 openFile (CT ct
, char **file
)
2428 char cachefile
[BUFSIZ
];
2429 struct exbody
*e
= ct
->c_ctexbody
;
2430 CE ce
= &ct
->c_cefile
;
2432 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2444 content_error (NULL
, ct
, "missing name parameter");
2448 ce
->ce_file
= getcpy (e
->eb_name
);
2451 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2452 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2456 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2457 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2458 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2462 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2463 if ((fp
= fopen (cachefile
, "w"))) {
2465 char buffer
[BUFSIZ
];
2466 FILE *gp
= ce
->ce_fp
;
2468 fseek (gp
, 0L, SEEK_SET
);
2470 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2472 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2473 advise ("openFile", "fwrite");
2478 admonish (ce
->ce_file
, "error reading");
2479 (void) m_unlink (cachefile
);
2483 admonish (cachefile
, "error writing");
2484 (void) m_unlink (cachefile
);
2491 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2492 *file
= ce
->ce_file
;
2493 return fileno (ce
->ce_fp
);
2503 return init_encoding (ct
, openFTP
);
2508 openFTP (CT ct
, char **file
)
2510 int cachetype
, caching
, fd
;
2512 char *bp
, *ftp
, *user
, *pass
;
2513 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2515 CE ce
= &ct
->c_cefile
;
2516 static char *username
= NULL
;
2517 static char *password
= NULL
;
2521 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2527 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2538 if (!e
->eb_name
|| !e
->eb_site
) {
2539 content_error (NULL
, ct
, "missing %s parameter",
2540 e
->eb_name
? "site": "name");
2544 /* Get the buffer ready to go */
2546 buflen
= sizeof(buffer
);
2549 * Construct the query message for user
2551 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2557 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2563 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2564 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2569 if (e
->eb_size
> 0) {
2570 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2575 snprintf (bp
, buflen
, "? ");
2578 * Now, check the answer
2580 if (!getanswer (buffer
))
2585 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2589 ruserpass (e
->eb_site
, &username
, &password
);
2594 ce
->ce_unlink
= (*file
== NULL
);
2596 cachefile
[0] = '\0';
2597 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2598 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2599 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2600 if (*file
== NULL
) {
2607 ce
->ce_file
= add (*file
, NULL
);
2609 ce
->ce_file
= add (cachefile
, NULL
);
2612 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2613 adios(NULL
, "unable to create temporary file in %s",
2616 ce
->ce_file
= add (tempfile
, NULL
);
2619 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2620 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2625 int child_id
, i
, vecp
;
2629 vec
[vecp
++] = r1bindex (ftp
, '/');
2630 vec
[vecp
++] = e
->eb_site
;
2633 vec
[vecp
++] = e
->eb_dir
;
2634 vec
[vecp
++] = e
->eb_name
;
2635 vec
[vecp
++] = ce
->ce_file
,
2636 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2637 ? "ascii" : "binary";
2642 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2646 adios ("fork", "unable to");
2650 close (fileno (ce
->ce_fp
));
2652 fprintf (stderr
, "unable to exec ");
2658 if (pidXwait (child_id
, NULL
)) {
2659 username
= password
= NULL
;
2669 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2674 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2675 if ((fp
= fopen (cachefile
, "w"))) {
2677 FILE *gp
= ce
->ce_fp
;
2679 fseek (gp
, 0L, SEEK_SET
);
2681 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2683 if ((int) fwrite (buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2684 advise ("openFTP", "fwrite");
2689 admonish (ce
->ce_file
, "error reading");
2690 (void) m_unlink (cachefile
);
2694 admonish (cachefile
, "error writing");
2695 (void) m_unlink (cachefile
);
2703 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2704 *file
= ce
->ce_file
;
2705 return fileno (ce
->ce_fp
);
2716 return init_encoding (ct
, openMail
);
2721 openMail (CT ct
, char **file
)
2723 int child_id
, fd
, i
, vecp
;
2725 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2726 struct exbody
*e
= ct
->c_ctexbody
;
2727 CE ce
= &ct
->c_cefile
;
2729 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2740 if (!e
->eb_server
) {
2741 content_error (NULL
, ct
, "missing server parameter");
2745 /* Get buffer ready to go */
2747 buflen
= sizeof(buffer
);
2749 /* Now, construct query message */
2750 snprintf (bp
, buflen
, "Retrieve content");
2756 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2762 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2764 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2766 /* Now, check answer */
2767 if (!getanswer (buffer
))
2771 vec
[vecp
++] = r1bindex (mailproc
, '/');
2772 vec
[vecp
++] = e
->eb_server
;
2773 vec
[vecp
++] = "-subject";
2774 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2775 vec
[vecp
++] = "-body";
2776 vec
[vecp
++] = e
->eb_body
;
2779 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2783 advise ("fork", "unable to");
2787 execvp (mailproc
, vec
);
2788 fprintf (stderr
, "unable to exec ");
2794 if (pidXwait (child_id
, NULL
) == OK
)
2795 advise (NULL
, "request sent");
2799 if (*file
== NULL
) {
2801 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2802 adios(NULL
, "unable to create temporary file in %s",
2805 ce
->ce_file
= add (tempfile
, NULL
);
2808 ce
->ce_file
= add (*file
, NULL
);
2812 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2813 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2817 /* showproc is for mhshow and mhstore, though mhlist -debug
2818 * prints it, too. */
2820 free (ct
->c_showproc
);
2821 ct
->c_showproc
= add ("true", NULL
);
2823 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2824 *file
= ce
->ce_file
;
2825 return fileno (ce
->ce_fp
);
2836 return init_encoding (ct
, openURL
);
2841 openURL (CT ct
, char **file
)
2843 struct exbody
*e
= ct
->c_ctexbody
;
2844 CE ce
= &ct
->c_cefile
;
2845 char *urlprog
, *program
;
2846 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2847 int fd
, caching
, cachetype
;
2848 struct msgs_array args
= { 0, 0, NULL
};
2851 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2855 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2859 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2871 content_error(NULL
, ct
, "missing url parameter");
2875 ce
->ce_unlink
= (*file
== NULL
);
2877 cachefile
[0] = '\0';
2879 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2880 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2881 if (*file
== NULL
) {
2888 ce
->ce_file
= add(*file
, NULL
);
2890 ce
->ce_file
= add(cachefile
, NULL
);
2893 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2894 adios(NULL
, "unable to create temporary file in %s",
2897 ce
->ce_file
= add (tempfile
, NULL
);
2900 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2901 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2905 switch (child_id
= fork()) {
2907 adios ("fork", "unable to");
2911 argsplit_msgarg(&args
, urlprog
, &program
);
2912 app_msgarg(&args
, e
->eb_url
);
2913 app_msgarg(&args
, NULL
);
2914 dup2(fileno(ce
->ce_fp
), 1);
2915 close(fileno(ce
->ce_fp
));
2916 execvp(program
, args
.msgs
);
2917 fprintf(stderr
, "Unable to exec ");
2923 if (pidXwait(child_id
, NULL
)) {
2931 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2936 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2937 if ((fp
= fopen(cachefile
, "w"))) {
2939 FILE *gp
= ce
->ce_fp
;
2941 fseeko(gp
, 0, SEEK_SET
);
2943 while ((cc
= fread(buffer
, sizeof(*buffer
),
2944 sizeof(buffer
), gp
)) > 0)
2945 if ((int) fwrite(buffer
, sizeof(*buffer
), cc
, fp
) < cc
) {
2946 advise ("openURL", "fwrite");
2952 admonish(ce
->ce_file
, "error reading");
2953 (void) m_unlink (cachefile
);
2960 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2961 *file
= ce
->ce_file
;
2966 readDigest (CT ct
, char *cp
)
2971 unsigned char *dp
, value
, *ep
;
2977 for (ep
= (dp
= ct
->c_digest
)
2978 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2983 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2985 fprintf (stderr
, "invalid BASE64 encoding\n");
2989 bits
|= value
<< bitno
;
2991 if ((bitno
-= 6) < 0) {
2992 if (dp
+ (3 - skip
) > ep
)
2993 goto invalid_digest
;
2994 *dp
++ = (bits
>> 16) & 0xff;
2996 *dp
++ = (bits
>> 8) & 0xff;
2998 *dp
++ = bits
& 0xff;
3008 goto self_delimiting
;
3013 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
3023 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
3031 fprintf (stderr
, "MD5 digest=");
3032 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
3033 fprintf (stderr
, "%02x", *dp
& 0xff);
3034 fprintf (stderr
, "\n");
3041 /* Multipart parts might have content before the first subpart and/or
3042 after the last subpart that hasn't been stored anywhere else, so do
3045 get_leftover_mp_content (CT ct
, int before
/* or after */)
3047 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
3049 int found_boundary
= 0;
3055 char *content
= NULL
;
3057 if (! m
) return NOTOK
;
3060 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
3062 /* Isolate the beginning of this part to the beginning of the
3063 first subpart and save any content between them. */
3064 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
3065 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3066 boundary
= concat ("--", m
->mp_start
, NULL
);
3068 struct part
*last_subpart
= NULL
;
3069 struct part
*subpart
;
3071 /* Go to the last subpart to get its end position. */
3072 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3073 last_subpart
= subpart
;
3076 if (last_subpart
== NULL
) return NOTOK
;
3078 /* Isolate the end of the last subpart to the end of this part
3079 and save any content between them. */
3080 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3081 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3082 boundary
= concat ("--", m
->mp_stop
, NULL
);
3085 /* Back up by 1 to pick up the newline. */
3086 while ((gotlen
= getline(&bufp
, &buflen
, ct
->c_fp
)) != -1) {
3088 /* Don't look beyond beginning of first subpart (before) or
3089 next part (after). */
3090 if (read
> max
) bufp
[read
-max
] = '\0';
3093 if (! strcmp (bufp
, boundary
)) {
3097 if (! found_boundary
&& ! strcmp (bufp
, boundary
)) {
3103 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3105 char *old_content
= content
;
3106 content
= concat (content
, bufp
, NULL
);
3110 ? concat ("\n", bufp
, NULL
)
3111 : concat (bufp
, NULL
);
3116 if (found_boundary
|| read
> max
) break;
3118 if (read
> max
) break;
3122 /* Skip the newline if that's all there is. */
3126 /* Remove trailing newline, except at EOF. */
3127 if ((before
|| ! feof (ct
->c_fp
)) &&
3128 (cp
= content
+ strlen (content
)) > content
&&
3133 if (strlen (content
) > 1) {
3135 m
->mp_content_before
= content
;
3137 m
->mp_content_after
= content
;
3152 ct_type_str (int type
) {
3154 case CT_APPLICATION
:
3155 return "application";
3171 return "unknown_type";
3177 ct_subtype_str (int type
, int subtype
) {
3179 case CT_APPLICATION
:
3181 case APPLICATION_OCTETS
:
3183 case APPLICATION_POSTSCRIPT
:
3184 return "postscript";
3186 return "unknown_app_subtype";
3190 case MESSAGE_RFC822
:
3192 case MESSAGE_PARTIAL
:
3194 case MESSAGE_EXTERNAL
:
3197 return "unknown_msg_subtype";
3203 case MULTI_ALTERNATE
:
3204 return "alternative";
3207 case MULTI_PARALLEL
:
3212 return "unknown_multipart_subtype";
3223 return "unknown_text_subtype";
3226 return "unknown_type";
3232 ct_str_type (const char *type
) {
3233 struct str2init
*s2i
;
3235 for (s2i
= str2cts
; s2i
->si_key
; ++s2i
) {
3236 if (! strcasecmp (type
, s2i
->si_key
)) {
3240 if (! s2i
->si_key
&& ! uprf (type
, "X-")) {
3249 ct_str_subtype (int type
, const char *subtype
) {
3253 case CT_APPLICATION
:
3254 for (kv
= SubApplication
; kv
->kv_key
; ++kv
) {
3255 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3259 return kv
->kv_value
;
3261 for (kv
= SubMessage
; kv
->kv_key
; ++kv
) {
3262 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3266 return kv
->kv_value
;
3268 for (kv
= SubMultiPart
; kv
->kv_key
; ++kv
) {
3269 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3273 return kv
->kv_value
;
3275 for (kv
= SubText
; kv
->kv_key
; ++kv
) {
3276 if (! strcasecmp (subtype
, kv
->kv_key
)) {
3280 return kv
->kv_value
;
3287 /* Find the content type and InitFunc for the CT. */
3288 const struct str2init
*
3289 get_ct_init (int type
) {
3290 const struct str2init
*sp
;
3292 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3293 if (type
== sp
->si_val
) {
3302 ce_str (int encoding
) {
3307 return "quoted-printable";
3323 /* Find the content type and InitFunc for the content encoding method. */
3324 const struct str2init
*
3325 get_ce_method (const char *method
) {
3326 struct str2init
*sp
;
3328 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3329 if (! strcasecmp (method
, sp
->si_key
)) {
3338 * Parse a series of MIME attributes (or parameters) given a header as
3341 * Arguments include:
3343 * filename - Name of input file (for error messages)
3344 * fieldname - Name of field being processed
3345 * headerp - Pointer to pointer of the beginning of the MIME attributes.
3346 * Updated to point to end of attributes when finished.
3347 * param_head - Pointer to head of parameter list
3348 * param_tail - Pointer to tail of parameter list
3349 * commentp - Pointer to header comment pointer (may be NULL)
3351 * Returns OK if parsing was successful, NOTOK if parsing failed, and
3352 * DONE to indicate a benign error (minor parsing error, but the program
3357 parse_header_attrs (const char *filename
, const char *fieldname
,
3358 char **header_attrp
, PM
*param_head
, PM
*param_tail
,
3361 char *cp
= *header_attrp
;
3367 struct sectlist
*next
;
3373 struct sectlist
*sechead
;
3374 struct parmlist
*next
;
3375 } *pp
, *pp2
, *phead
= NULL
;
3377 while (*cp
== ';') {
3378 char *dp
, *vp
, *up
, *nameptr
, *valptr
, *charset
= NULL
, *lang
= NULL
;
3379 int encoded
= 0, partial
= 0, len
= 0, index
= 0;
3382 while (isspace ((unsigned char) *cp
))
3386 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3391 if (! suppress_extraneous_trailing_semicolon_warning
) {
3393 "extraneous trailing ';' in message %s's %s: "
3395 filename
, fieldname
);
3397 extraneous_trailing_semicolon
= 1;
3401 /* down case the attribute name */
3402 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3403 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3404 *dp
= tolower ((unsigned char) *dp
);
3406 for (up
= dp
; isspace ((unsigned char) *dp
);)
3408 if (dp
== cp
|| *dp
!= '=') {
3410 "invalid parameter in message %s's %s: "
3411 "field\n%*sparameter %s (error detected at offset %d)",
3412 filename
, fieldname
, strlen(invo_name
) + 2, "",cp
, dp
- cp
);
3417 * To handle RFC 2231, we have to deal with the following extensions:
3419 * name*=encoded-value
3420 * name*<N>=part-N-of-a-parameter-value
3421 * name*<N>*=encoded-part-N-of-a-parameter-value
3424 * If there's a * right before the equal sign, it's encoded.
3425 * If there's a * and one or more digits, then it's section N.
3427 * Remember we can have one or the other, or both. cp points to
3428 * beginning of name, up points past the last character in the
3432 for (vp
= cp
; vp
< up
; vp
++) {
3433 if (*vp
== '*' && vp
< up
- 1) {
3436 } else if (*vp
== '*' && vp
== up
- 1) {
3438 } else if (partial
) {
3439 if (isdigit((unsigned char) *vp
))
3440 index
= *vp
- '0' + index
* 10;
3442 advise (NULL
, "invalid parameter index in message %s's "
3443 "%s: field\n%*s(parameter %s)", filename
,
3444 fieldname
, strlen(invo_name
) + 2, "", cp
);
3453 * Break out the parameter name and value sections and allocate
3457 nameptr
= mh_xmalloc(len
+ 1);
3458 strncpy(nameptr
, cp
, len
);
3459 nameptr
[len
] = '\0';
3461 for (dp
++; isspace ((unsigned char) *dp
);)
3466 * Single quotes delimit the character set and language tag.
3467 * They are required on the first section (or a complete
3472 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3478 charset
= mh_xmalloc(len
+ 1);
3479 strncpy(charset
, dp
, len
);
3480 charset
[len
] = '\0';
3486 advise(NULL
, "missing charset in message %s's %s: "
3487 "field\n%*s(parameter %s)", filename
, fieldname
,
3488 strlen(invo_name
) + 2, "", nameptr
);
3494 while (*vp
!= '\'' && !isspace((unsigned char) *vp
) &&
3501 lang
= mh_xmalloc(len
+ 1);
3502 strncpy(lang
, dp
, len
);
3509 advise(NULL
, "missing language tag in message %s's %s: "
3510 "field\n%*s(parameter %s)", filename
, fieldname
,
3511 strlen(invo_name
) + 2, "", nameptr
);
3522 * At this point vp should be pointing at the beginning
3523 * of the encoded value/section. Continue until we reach
3524 * the end or get whitespace. But first, calculate the
3525 * length so we can allocate the correct buffer size.
3528 for (vp
= dp
, len
= 0; istoken(*vp
); vp
++) {
3530 if (*(vp
+ 1) == '\0' ||
3531 !isxdigit((unsigned char) *(vp
+ 1)) ||
3532 *(vp
+ 2) == '\0' ||
3533 !isxdigit((unsigned char) *(vp
+ 2))) {
3534 advise(NULL
, "invalid encoded sequence in message "
3535 "%s's %s: field\n%*s(parameter %s)",
3536 filename
, fieldname
, strlen(invo_name
) + 2,
3550 up
= valptr
= mh_xmalloc(len
+ 1);
3552 for (vp
= dp
; istoken(*vp
); vp
++) {
3554 *up
++ = decode_qp(*(vp
+ 1), *(vp
+ 2));
3565 * A "normal" string. If it's got a leading quote, then we
3566 * strip the quotes out. Otherwise go until we reach the end
3567 * or get whitespace. Note we scan it twice; once to get the
3568 * length, then the second time copies it into the destination
3575 for (cp
= dp
+ 1;;) {
3580 "invalid quoted-string in message %s's %s: "
3581 "field\n%*s(parameter %s)",
3582 filename
, fieldname
, strlen(invo_name
) + 2, "",
3605 for (cp
= dp
; istoken (*cp
); cp
++) {
3610 valptr
= mh_xmalloc(len
+ 1);
3614 for (cp
= dp
+ 1, vp
= valptr
, i
= 0; i
< len
; i
++) {
3622 strncpy(valptr
, cp
= dp
, len
);
3630 * If 'partial' is set, we don't allocate a parameter now. We
3631 * put it on the parameter linked list to be reassembled later.
3633 * "phead" points to a list of all parameters we need to reassemble.
3634 * Each parameter has a list of sections. We insert the sections in
3639 for (pp
= phead
; pp
!= NULL
; pp
= pp
->next
) {
3640 if (strcasecmp(nameptr
, pp
->name
) == 0)
3645 pp
= mh_xmalloc(sizeof(*pp
));
3646 memset(pp
, 0, sizeof(*pp
));
3653 * Insert this into the section linked list
3656 sp
= mh_xmalloc(sizeof(*sp
));
3657 memset(sp
, 0, sizeof(*sp
));
3662 if (pp
->sechead
== NULL
|| pp
->sechead
->index
> index
) {
3663 sp
->next
= pp
->sechead
;
3666 for (sp2
= pp
->sechead
; sp2
!= NULL
; sp2
= sp2
->next
) {
3667 if (sp2
->index
== sp
->index
) {
3668 advise (NULL
, "duplicate index (%d) in message "
3669 "%s's %s: field\n%*s(parameter %s)", sp
->index
,
3670 filename
, fieldname
, strlen(invo_name
) + 2, "",
3675 if (sp2
->index
< sp
->index
&&
3676 (sp2
->next
== NULL
|| sp2
->next
->index
> sp
->index
)) {
3677 sp
->next
= sp2
->next
;
3684 advise(NULL
, "Internal error: cannot insert partial "
3685 "param in message %s's %s: field\n%*s(parameter %s)",
3686 filename
, fieldname
, strlen(invo_name
) + 2, "",
3694 * Save our charset and lang tags.
3697 if (index
== 0 && encoded
) {
3700 pp
->charset
= charset
;
3706 pm
= add_param(param_head
, param_tail
, nameptr
, valptr
, 1);
3707 pm
->pm_charset
= charset
;
3711 while (isspace ((unsigned char) *cp
))
3715 get_comment (filename
, fieldname
, &cp
, commentp
) == NOTOK
) {
3721 * Now that we're done, reassemble all of the partial parameters.
3724 for (pp
= phead
; pp
!= NULL
; ) {
3728 for (sp
= pp
->sechead
; sp
!= NULL
; sp
= sp
->next
) {
3729 if (sp
->index
!= pindex
++) {
3730 advise(NULL
, "missing section %d for parameter in "
3731 "message %s's %s: field\n%*s(parameter %s)", pindex
- 1,
3732 filename
, fieldname
, strlen(invo_name
) + 2, "",
3739 p
= q
= mh_xmalloc(tlen
+ 1);
3740 for (sp
= pp
->sechead
; sp
!= NULL
; ) {
3741 memcpy(q
, sp
->value
, sp
->len
);
3751 pm
= add_param(param_head
, param_tail
, pp
->name
, p
, 1);
3752 pm
->pm_charset
= pp
->charset
;
3753 pm
->pm_lang
= pp
->lang
;
3764 * Return the charset for a particular content type.
3768 content_charset (CT ct
) {
3769 char *ret_charset
= NULL
;
3771 ret_charset
= get_param(ct
->c_ctinfo
.ci_first_pm
, "charset", '?', 0);
3773 return ret_charset
? ret_charset
: getcpy ("US-ASCII");
3778 * Create a string based on a list of output parameters. Assume that this
3779 * parameter string will be appended to an existing header, so start out
3780 * with the separator (;). Perform RFC 2231 encoding when necessary.
3784 output_params(size_t initialwidth
, PM params
, int *offsetout
, int external
)
3786 char *paramout
= NULL
;
3787 char line
[CPERLIN
* 2], *q
;
3788 int curlen
, index
, cont
, encode
, i
;
3789 size_t valoff
, numchars
;
3791 while (params
!= NULL
) {
3797 if (external
&& strcasecmp(params
->pm_name
, "body") == 0)
3800 if (strlen(params
->pm_name
) > CPERLIN
) {
3801 advise(NULL
, "Parameter name \"%s\" is too long", params
->pm_name
);
3807 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
, &numchars
);
3810 * Loop until we get a parameter that fits within a line. We
3811 * assume new lines start with a tab, so check our overflow based
3821 * At this point we're definitely continuing the line, so
3822 * be sure to include the parameter name and section index.
3825 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3826 params
->pm_name
, index
);
3829 * Both of these functions do a NUL termination
3833 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3834 numchars
, valoff
, index
);
3836 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3847 curlen
= param_len(params
, index
, valoff
, &encode
, &cont
,
3852 * "line" starts with a ;\n\t, so that doesn't count against
3853 * the length. But add 8 since it starts with a tab; that's
3854 * how we end up with 5.
3857 initialwidth
= strlen(line
) + 5;
3860 * At this point the line should be built, so add it to our
3861 * current output buffer.
3864 paramout
= add(line
, paramout
);
3868 * If this won't fit on the line, start a new one. Save room in
3869 * case we need a semicolon on the end
3872 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3884 * At this point, we're either finishing a contined parameter, or
3885 * we're working on a new one.
3889 q
+= snprintf(q
, sizeof(line
) - (q
- line
), "%s*%d",
3890 params
->pm_name
, index
);
3892 strncpy(q
, params
->pm_name
, sizeof(line
) - (q
- line
));
3897 i
= encode_param(params
, q
, sizeof(line
) - (q
- line
),
3898 strlen(params
->pm_value
+ valoff
), valoff
, index
);
3900 i
= normal_param(params
, q
, sizeof(line
) - (q
- line
),
3901 strlen(params
->pm_value
+ valoff
), valoff
);
3909 paramout
= add(line
, paramout
);
3910 initialwidth
+= strlen(line
);
3912 params
= params
->pm_next
;
3916 *offsetout
= initialwidth
;
3922 * Calculate the size of a parameter.
3926 * pm - The parameter being output
3927 * index - If continuing the parameter, the index of the section
3929 * valueoff - The current offset into the parameter value that we're
3930 * working on (previous sections have consumed valueoff bytes).
3931 * encode - Set if we should perform encoding on this parameter section
3932 * (given that we're consuming bytesfit bytes).
3933 * cont - Set if the remaining data in value will not fit on a single
3934 * line and will need to be continued.
3935 * bytesfit - The number of bytes that we can consume from the parameter
3936 * value and still fit on a completely new line. The
3937 * calculation assumes the new line starts with a tab,
3938 * includes the parameter name and any encoding, and fits
3939 * within CPERLIN bytes. Will always be at least 1.
3943 param_len(PM pm
, int index
, size_t valueoff
, int *encode
, int *cont
,
3946 char *start
= pm
->pm_value
+ valueoff
, *p
, indexchar
[32];
3947 size_t len
= 0, fit
= 0;
3948 int fitlimit
= 0, eightbit
, maxfit
;
3953 * Add up the length. First, start with the parameter name.
3956 len
= strlen(pm
->pm_name
);
3959 * Scan the parameter value and see if we need to do encoding for this
3963 eightbit
= contains8bit(start
, NULL
);
3966 * Determine if we need to encode this section. Encoding is necessary if:
3968 * - There are any 8-bit characters at all and we're on the first
3970 * - There are 8-bit characters within N bytes of our section start.
3971 * N is calculated based on the number of bytes it would take to
3972 * reach CPERLIN. Specifically:
3973 * 8 (starting tab) +
3974 * strlen(param name) +
3975 * 4 ('* for section marker, '=', opening/closing '"')
3977 * is the number of bytes used by everything that isn't part of the
3978 * value. So that gets subtracted from CPERLIN.
3981 snprintf(indexchar
, sizeof(indexchar
), "%d", index
);
3982 maxfit
= CPERLIN
- (12 + len
+ strlen(indexchar
));
3983 if ((eightbit
&& index
== 0) || contains8bit(start
, start
+ maxfit
)) {
3987 len
++; /* Add in equal sign */
3991 * We're using maxfit as a marker for how many characters we can
3992 * fit into the line. Bump it by two because we're not using quotes
3999 * If we don't have a charset or language tag in this parameter,
4003 if (! pm
->pm_charset
) {
4004 pm
->pm_charset
= getcpy(write_charset_8bit());
4005 if (strcasecmp(pm
->pm_charset
, "US-ASCII") == 0)
4006 adios(NULL
, "8-bit characters in parameter \"%s\", but "
4007 "local character set is US-ASCII", pm
->pm_name
);
4010 pm
->pm_lang
= getcpy(NULL
); /* Default to a blank lang tag */
4012 len
++; /* For the encoding marker */
4015 int enclen
= strlen(pm
->pm_charset
) + strlen(pm
->pm_lang
) + 2;
4020 * We know we definitely need to include an index. maxfit already
4021 * includes the section marker.
4023 len
+= strlen(indexchar
);
4025 for (p
= start
; *p
!= '\0'; p
++) {
4026 if (isparamencode(*p
)) {
4034 * Just so there's no confusion: maxfit is counting OUTPUT
4035 * characters (post-encoding). fit is counting INPUT characters.
4037 if (! fitlimit
&& maxfit
>= 0)
4039 else if (! fitlimit
)
4044 * Calculate the string length, but add room for quoting \
4045 * and " if necessary. Also account for quotes at beginning
4048 for (p
= start
; *p
!= '\0'; p
++) {
4059 if (! fitlimit
&& maxfit
>= 0)
4061 else if (! fitlimit
)
4078 * Output an encoded parameter string.
4082 encode_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4083 size_t valueoff
, int index
)
4085 size_t outlen
= 0, n
;
4086 char *endptr
= output
+ len
, *p
;
4089 * First, output the marker for an encoded string.
4097 * If the index is 0, output the character set and language tag.
4098 * If theses were NULL, they should have already been filled in
4103 n
= snprintf(output
, len
- outlen
, "%s'%s'", pm
->pm_charset
,
4107 if (output
> endptr
) {
4108 advise(NULL
, "Internal error: parameter buffer overflow");
4114 * Copy over the value, encoding if necessary
4117 p
= pm
->pm_value
+ valueoff
;
4118 while (valuelen
-- > 0) {
4119 if (isparamencode(*p
)) {
4120 n
= snprintf(output
, len
- outlen
, "%%%02X", (unsigned char) *p
++);
4127 if (output
> endptr
) {
4128 advise(NULL
, "Internal error: parameter buffer overflow");
4139 * Output a "normal" parameter, without encoding. Be sure to escape
4140 * quotes and backslashes if necessary.
4144 normal_param(PM pm
, char *output
, size_t len
, size_t valuelen
,
4148 char *endptr
= output
+ len
, *p
;
4154 p
= pm
->pm_value
+ valueoff
;
4156 while (valuelen
-- > 0) {
4166 if (output
> endptr
) {
4167 advise(NULL
, "Internal error: parameter buffer overflow");
4172 if (output
- 2 > endptr
) {
4173 advise(NULL
, "Internal error: parameter buffer overflow");
4184 * Add a parameter to the parameter linked list
4188 add_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4190 PM pm
= mh_xmalloc(sizeof(*pm
));
4192 memset(pm
, 0, sizeof(*pm
));
4194 pm
->pm_name
= nocopy
? name
: getcpy(name
);
4195 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4198 (*last
)->pm_next
= pm
;
4209 * Either replace a current parameter with a new value, or add the parameter
4210 * to the parameter linked list.
4214 replace_param(PM
*first
, PM
*last
, char *name
, char *value
, int nocopy
)
4218 for (pm
= *first
; pm
!= NULL
; pm
= pm
->pm_next
) {
4219 if (strcasecmp(name
, pm
->pm_name
) == 0) {
4221 * If nocopy is set, it's assumed that we own both name
4222 * and value. We don't need name, so we discard it now.
4227 pm
->pm_value
= nocopy
? value
: getcpy(value
);
4232 return add_param(first
, last
, name
, value
, nocopy
);
4236 * Retrieve a parameter value from a parameter linked list. If the parameter
4237 * value needs converted to the local character set, do that now.
4241 get_param(PM first
, const char *name
, char replace
, int fetchonly
)
4243 while (first
!= NULL
) {
4244 if (strcasecmp(name
, first
->pm_name
) == 0) {
4246 return first
->pm_value
;
4248 return getcpy(get_param_value(first
, replace
));
4250 first
= first
->pm_next
;
4257 * Return a parameter value, converting to the local character set if
4261 char *get_param_value(PM pm
, char replace
)
4263 static char buffer
[4096]; /* I hope no parameters are larger */
4264 size_t bufsize
= sizeof(buffer
);
4269 ICONV_CONST
char *p
;
4270 #else /* HAVE_ICONV */
4272 #endif /* HAVE_ICONV */
4277 * If we don't have a character set indicated, it's assumed to be
4278 * US-ASCII. If it matches our character set, we don't need to convert
4282 if (!pm
->pm_charset
|| check_charset(pm
->pm_charset
,
4283 strlen(pm
->pm_charset
))) {
4284 return pm
->pm_value
;
4288 * In this case, we need to convert. If we have iconv support, use
4289 * that. Otherwise, go through and simply replace every non-ASCII
4290 * character with the substitution character.
4295 bufsize
= sizeof(buffer
);
4296 utf8
= strcasecmp(pm
->pm_charset
, "UTF-8") == 0;
4298 cd
= iconv_open(get_charset(), pm
->pm_charset
);
4299 if (cd
== (iconv_t
) -1) {
4303 inbytes
= strlen(pm
->pm_value
);
4307 if (iconv(cd
, &p
, &inbytes
, &q
, &bufsize
) == (size_t)-1) {
4308 if (errno
!= EILSEQ
) {
4313 * Reset shift state, substitute our character,
4314 * try to restart conversion.
4317 iconv(cd
, NULL
, NULL
, &q
, &bufsize
);
4330 for (++p
, --inbytes
;
4331 inbytes
> 0 && (((unsigned char) *q
) & 0xc0) == 0x80;
4350 #endif /* HAVE_ICONV */
4353 * Take everything non-ASCII and substituite the replacement character
4357 bufsize
= sizeof(buffer
);
4358 for (p
= pm
->pm_value
; *p
!= '\0' && bufsize
> 1; p
++, q
++, bufsize
--) {
4359 if (isascii((unsigned char) *p
) && !iscntrl((unsigned char) *p
))