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>
23 extern pid_t xpid
; /* mhshowsbr.c */
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.
37 int skip_mp_cte_check
;
38 int suppress_bogus_mp_content_warning
;
42 * Structures for TEXT messages
44 struct k2v SubText
[] = {
45 { "plain", TEXT_PLAIN
},
46 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
47 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
48 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
51 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
54 * Structures for MULTIPART messages
56 struct k2v SubMultiPart
[] = {
57 { "mixed", MULTI_MIXED
},
58 { "alternative", MULTI_ALTERNATE
},
59 { "digest", MULTI_DIGEST
},
60 { "parallel", MULTI_PARALLEL
},
61 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
65 * Structures for MESSAGE messages
67 struct k2v SubMessage
[] = {
68 { "rfc822", MESSAGE_RFC822
},
69 { "partial", MESSAGE_PARTIAL
},
70 { "external-body", MESSAGE_EXTERNAL
},
71 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
75 * Structure for APPLICATION messages
77 struct k2v SubApplication
[] = {
78 { "octet-stream", APPLICATION_OCTETS
},
79 { "postscript", APPLICATION_POSTSCRIPT
},
80 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
84 * Mapping of names of CTE types in mhbuild directives
86 static struct k2v EncodingType
[] = {
90 { "quoted-printable", CE_QUOTED
},
92 { "base64", CE_BASE64
},
98 int find_cache (CT
, int, int *, char *, char *, int);
101 int part_ok (CT
, int);
102 int type_ok (CT
, int);
103 void content_error (char *, CT
, char *, ...);
106 void free_encoding (CT
, int);
111 static CT
get_content (FILE *, char *, int);
112 static int get_comment (const char *, CI
, char **, int);
114 static int InitGeneric (CT
);
115 static int InitText (CT
);
116 static int InitMultiPart (CT
);
117 void reverse_parts (CT
);
118 static int InitMessage (CT
);
119 static int InitApplication (CT
);
120 static int init_encoding (CT
, OpenCEFunc
);
121 static unsigned long size_encoding (CT
);
122 static int InitBase64 (CT
);
123 static int openBase64 (CT
, char **);
124 static int InitQuoted (CT
);
125 static int openQuoted (CT
, char **);
126 static int Init7Bit (CT
);
127 static int openExternal (CT
, CT
, CE
, char **, int *);
128 static int InitFile (CT
);
129 static int openFile (CT
, char **);
130 static int InitFTP (CT
);
131 static int openFTP (CT
, char **);
132 static int InitMail (CT
);
133 static int openMail (CT
, char **);
134 static int readDigest (CT
, char *);
135 static int get_leftover_mp_content (CT
, int);
136 static int InitURL (CT
);
137 static int openURL (CT
, char **);
139 struct str2init str2cts
[] = {
140 { "application", CT_APPLICATION
, InitApplication
},
141 { "audio", CT_AUDIO
, InitGeneric
},
142 { "image", CT_IMAGE
, InitGeneric
},
143 { "message", CT_MESSAGE
, InitMessage
},
144 { "multipart", CT_MULTIPART
, InitMultiPart
},
145 { "text", CT_TEXT
, InitText
},
146 { "video", CT_VIDEO
, InitGeneric
},
147 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
148 { NULL
, CT_UNKNOWN
, NULL
},
151 struct str2init str2ces
[] = {
152 { "base64", CE_BASE64
, InitBase64
},
153 { "quoted-printable", CE_QUOTED
, InitQuoted
},
154 { "8bit", CE_8BIT
, Init7Bit
},
155 { "7bit", CE_7BIT
, Init7Bit
},
156 { "binary", CE_BINARY
, Init7Bit
},
157 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
158 { NULL
, CE_UNKNOWN
, NULL
},
162 * NOTE WELL: si_key MUST NOT have value of NOTOK
164 * si_key is 1 if access method is anonymous.
166 struct str2init str2methods
[] = {
167 { "afs", 1, InitFile
},
168 { "anon-ftp", 1, InitFTP
},
169 { "ftp", 0, InitFTP
},
170 { "local-file", 0, InitFile
},
171 { "mail-server", 0, InitMail
},
172 { "url", 0, InitURL
},
178 pidcheck (int status
)
180 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
191 * Main entry point for parsing a MIME message or file.
192 * It returns the Content structure for the top level
193 * entity in the file.
197 parse_mime (char *file
)
205 * Check if file is actually standard input
207 if ((is_stdin
= !(strcmp (file
, "-")))) {
208 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
210 advise("mhparse", "unable to create temporary file in %s",
214 file
= add (tfile
, NULL
);
216 while (fgets (buffer
, sizeof(buffer
), stdin
))
220 if (ferror (stdin
)) {
221 (void) m_unlink (file
);
222 advise ("stdin", "error reading");
226 (void) m_unlink (file
);
227 advise (file
, "error writing");
230 fseek (fp
, 0L, SEEK_SET
);
231 } else if ((fp
= fopen (file
, "r")) == NULL
) {
232 advise (file
, "unable to read");
236 if (!(ct
= get_content (fp
, file
, 1))) {
238 (void) m_unlink (file
);
239 advise (NULL
, "unable to decode %s", file
);
244 ct
->c_unlink
= 1; /* temp file to remove */
248 if (ct
->c_end
== 0L) {
249 fseek (fp
, 0L, SEEK_END
);
250 ct
->c_end
= ftell (fp
);
253 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
265 * Main routine for reading/parsing the headers
266 * of a message content.
268 * toplevel = 1 # we are at the top level of the message
269 * toplevel = 0 # we are inside message type or multipart type
270 * # other than multipart/digest
271 * toplevel = -1 # we are inside multipart/digest
272 * NB: on failure we will fclose(in)!
276 get_content (FILE *in
, char *file
, int toplevel
)
279 char buf
[BUFSIZ
], name
[NAMESZ
];
283 m_getfld_state_t gstate
= 0;
285 /* allocate the content structure */
286 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
287 adios (NULL
, "out of memory");
290 ct
->c_file
= add (file
, NULL
);
291 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
294 * Parse the header fields for this
295 * content into a linked list.
297 m_getfld_track_filepos (&gstate
, in
);
298 for (compnum
= 1;;) {
299 int bufsz
= sizeof buf
;
300 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
305 /* get copies of the buffers */
306 np
= add (name
, NULL
);
307 vp
= add (buf
, NULL
);
309 /* if necessary, get rest of field */
310 while (state
== FLDPLUS
) {
312 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
313 vp
= add (buf
, vp
); /* add to previous value */
316 /* Now add the header data to the list */
317 add_header (ct
, np
, vp
);
319 /* continue, to see if this isn't the last header field */
320 ct
->c_begin
= ftell (in
) + 1;
324 ct
->c_begin
= ftell (in
) - strlen (buf
);
328 ct
->c_begin
= ftell (in
);
333 adios (NULL
, "message format error in component #%d", compnum
);
336 adios (NULL
, "getfld() returned %d", state
);
339 /* break out of the loop */
342 m_getfld_state_destroy (&gstate
);
345 * Read the content headers. We will parse the
346 * MIME related header fields into their various
347 * structures and set internal flags related to
348 * content type/subtype, etc.
351 hp
= ct
->c_first_hf
; /* start at first header field */
353 /* Get MIME-Version field */
354 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
359 advise (NULL
, "message %s has multiple %s: fields",
360 ct
->c_file
, VRSN_FIELD
);
363 ct
->c_vrsn
= add (hp
->value
, NULL
);
365 /* Now, cleanup this field */
368 while (isspace ((unsigned char) *cp
))
370 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
372 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
373 if (!isspace ((unsigned char) *dp
))
377 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
380 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
)
383 for (dp
= cp
; istoken (*dp
); dp
++)
387 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
390 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
391 ct
->c_file
, VRSN_FIELD
, cp
);
394 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
395 /* Get Content-Type field */
396 struct str2init
*s2i
;
397 CI ci
= &ct
->c_ctinfo
;
399 /* Check if we've already seen a Content-Type header */
401 advise (NULL
, "message %s has multiple %s: fields",
402 ct
->c_file
, TYPE_FIELD
);
406 /* Parse the Content-Type field */
407 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
411 * Set the Init function and the internal
412 * flag for this content type.
414 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
415 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
417 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
419 ct
->c_type
= s2i
->si_val
;
420 ct
->c_ctinitfnx
= s2i
->si_init
;
422 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
423 /* Get Content-Transfer-Encoding field */
425 struct str2init
*s2i
;
428 * Check if we've already seen the
429 * Content-Transfer-Encoding field
432 advise (NULL
, "message %s has multiple %s: fields",
433 ct
->c_file
, ENCODING_FIELD
);
437 /* get copy of this field */
438 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
440 while (isspace ((unsigned char) *cp
))
442 for (dp
= cp
; istoken (*dp
); dp
++)
448 * Find the internal flag and Init function
449 * for this transfer encoding.
451 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
452 if (!strcasecmp (cp
, s2i
->si_key
))
454 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
457 ct
->c_encoding
= s2i
->si_val
;
459 /* Call the Init function for this encoding */
460 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
463 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
464 /* Get Content-MD5 field */
470 if (ct
->c_digested
) {
471 advise (NULL
, "message %s has multiple %s: fields",
472 ct
->c_file
, MD5_FIELD
);
476 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
478 while (isspace ((unsigned char) *cp
))
480 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
482 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
483 if (!isspace ((unsigned char) *dp
))
487 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
490 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
) {
495 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
503 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
504 /* Get Content-ID field */
505 ct
->c_id
= add (hp
->value
, ct
->c_id
);
507 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
508 /* Get Content-Description field */
509 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
511 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
512 /* Get Content-Disposition field */
513 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
517 hp
= hp
->next
; /* next header field */
521 * Check if we saw a Content-Type field.
522 * If not, then assign a default value for
523 * it, and the Init function.
527 * If we are inside a multipart/digest message,
528 * so default type is message/rfc822
531 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
533 ct
->c_type
= CT_MESSAGE
;
534 ct
->c_ctinitfnx
= InitMessage
;
537 * Else default type is text/plain
539 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
541 ct
->c_type
= CT_TEXT
;
542 ct
->c_ctinitfnx
= InitText
;
546 /* Use default Transfer-Encoding, if necessary */
548 ct
->c_encoding
= CE_7BIT
;
561 * small routine to add header field to list
565 add_header (CT ct
, char *name
, char *value
)
569 /* allocate header field structure */
570 hp
= mh_xmalloc (sizeof(*hp
));
572 /* link data into header structure */
577 /* link header structure into the list */
578 if (ct
->c_first_hf
== NULL
) {
579 ct
->c_first_hf
= hp
; /* this is the first */
582 ct
->c_last_hf
->next
= hp
; /* add it to the end */
590 /* Make sure that buf contains at least one appearance of name,
591 followed by =. If not, insert both name and value, just after
592 first semicolon, if any. Note that name should not contain a
593 trailing =. And quotes will be added around the value. Typical
594 usage: make sure that a Content-Disposition header contains
595 filename="foo". If it doesn't and value does, use value from
598 incl_name_value (char *buf
, char *name
, char *value
) {
601 /* Assume that name is non-null. */
603 char *name_plus_equal
= concat (name
, "=", NULL
);
605 if (! strstr (buf
, name_plus_equal
)) {
607 char *cp
, *prefix
, *suffix
;
609 /* Trim trailing space, esp. newline. */
610 for (cp
= &buf
[strlen (buf
) - 1];
611 cp
>= buf
&& isspace ((unsigned char) *cp
);
616 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
618 /* Insert at first semicolon, if any. If none, append to
620 prefix
= add (buf
, NULL
);
621 if ((cp
= strchr (prefix
, ';'))) {
622 suffix
= concat (cp
, NULL
);
624 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
628 newbuf
= concat (buf
, insertion
, "\n", NULL
);
636 free (name_plus_equal
);
642 /* Extract just name_suffix="foo", if any, from value. If there isn't
643 one, return the entire value. Note that, for example, a name_suffix
644 of name will match filename="foo", and return foo. */
646 extract_name_value (char *name_suffix
, char *value
) {
647 char *extracted_name_value
= value
;
648 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
649 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
652 free (name_suffix_plus_quote
);
653 if (name_suffix_equals
) {
654 char *name_suffix_begin
;
657 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
658 name_suffix_begin
= ++cp
;
659 /* Find second \". */
660 for (; *cp
!= '"'; ++cp
) /* empty */;
662 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
663 memcpy (extracted_name_value
,
665 cp
- name_suffix_begin
);
666 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
669 return extracted_name_value
;
673 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
674 * directives. Fills in the information of the CTinfo structure.
677 get_ctinfo (char *cp
, CT ct
, int magic
)
686 i
= strlen (invo_name
) + 2;
688 /* store copy of Content-Type line */
689 cp
= ct
->c_ctline
= add (cp
, NULL
);
691 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
694 /* change newlines to spaces */
695 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
698 /* trim trailing spaces */
699 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
700 if (!isspace ((unsigned char) *dp
))
705 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
707 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
710 for (dp
= cp
; istoken (*dp
); dp
++)
713 ci
->ci_type
= add (cp
, NULL
); /* store content type */
717 advise (NULL
, "invalid %s: field in message %s (empty type)",
718 TYPE_FIELD
, ct
->c_file
);
722 /* down case the content type string */
723 for (dp
= ci
->ci_type
; *dp
; dp
++)
724 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
725 *dp
= tolower ((unsigned char) *dp
);
727 while (isspace ((unsigned char) *cp
))
730 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
735 ci
->ci_subtype
= add ("", NULL
);
740 while (isspace ((unsigned char) *cp
))
743 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
746 for (dp
= cp
; istoken (*dp
); dp
++)
749 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
752 if (!*ci
->ci_subtype
) {
754 "invalid %s: field in message %s (empty subtype for \"%s\")",
755 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
759 /* down case the content subtype string */
760 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
761 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
762 *dp
= tolower ((unsigned char) *dp
);
765 while (isspace ((unsigned char) *cp
))
768 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
771 if (parse_header_attrs (ct
->c_file
, i
, &cp
, ci
, &status
) == NOTOK
) {
776 * Get any <Content-Id> given in buffer
778 if (magic
&& *cp
== '<') {
783 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
784 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
790 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
796 while (isspace ((unsigned char) *cp
))
801 * Get any [Content-Description] given in buffer.
803 if (magic
&& *cp
== '[') {
805 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
809 advise (NULL
, "invalid description in message %s", ct
->c_file
);
817 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
823 while (isspace ((unsigned char) *cp
))
828 * Get any {Content-Disposition} given in buffer.
830 if (magic
&& *cp
== '{') {
832 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
836 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
844 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
850 while (isspace ((unsigned char) *cp
))
855 * Get any extension directives (right now just the content transfer
856 * encoding, but maybe others) that we care about.
859 if (magic
&& *cp
== '*') {
861 * See if it's a CTE we match on
866 while (*cp
!= '\0' && ! isspace((unsigned char) *cp
))
870 advise (NULL
, "invalid null transfer encoding specification");
877 ct
->c_reqencoding
= CE_UNKNOWN
;
879 for (kv
= EncodingType
; kv
->kv_key
; kv
++) {
880 if (strcasecmp(kv
->kv_key
, dp
) == 0) {
881 ct
->c_reqencoding
= kv
->kv_value
;
886 if (ct
->c_reqencoding
== CE_UNKNOWN
) {
887 advise (NULL
, "invalid CTE specification: \"%s\"", dp
);
891 while (isspace ((unsigned char) *cp
))
896 * Check if anything is left over
900 ci
->ci_magic
= add (cp
, NULL
);
902 /* If there is a Content-Disposition header and it doesn't
903 have a *filename=, extract it from the magic contents.
904 The r1bindex call skips any leading directory
908 incl_name_value (ct
->c_dispo
,
910 r1bindex (extract_name_value ("name",
917 "extraneous information in message %s's %s: field\n%*.*s(%s)",
918 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
926 get_comment (const char *filename
, CI ci
, char **ap
, int istype
)
930 char c
, buffer
[BUFSIZ
], *dp
;
940 advise (NULL
, "invalid comment in message %s's %s: field",
941 filename
, istype
? TYPE_FIELD
: VRSN_FIELD
);
946 if ((c
= *cp
++) == '\0')
969 if ((dp
= ci
->ci_comment
)) {
970 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
973 ci
->ci_comment
= add (buffer
, NULL
);
977 while (isspace ((unsigned char) *cp
))
988 * Handles content types audio, image, and video.
989 * There's not much to do right here.
997 return OK
; /* not much to do here */
1008 char buffer
[BUFSIZ
];
1010 char **ap
, **ep
, *cp
;
1013 CI ci
= &ct
->c_ctinfo
;
1015 /* check for missing subtype */
1016 if (!*ci
->ci_subtype
)
1017 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1020 for (kv
= SubText
; kv
->kv_key
; kv
++)
1021 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1023 ct
->c_subtype
= kv
->kv_value
;
1025 /* allocate text character set structure */
1026 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1027 adios (NULL
, "out of memory");
1028 ct
->c_ctparams
= (void *) t
;
1030 /* scan for charset parameter */
1031 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1032 if (!strcasecmp (*ap
, "charset"))
1035 /* check if content specified a character set */
1038 t
->tx_charset
= CHARSET_SPECIFIED
;
1040 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1044 * If we can not handle character set natively,
1045 * then check profile for string to modify the
1046 * terminal or display method.
1048 * termproc is for mhshow, though mhlist -debug prints it, too.
1050 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1051 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1052 if ((cp
= context_find (buffer
)))
1053 ct
->c_termproc
= getcpy (cp
);
1065 InitMultiPart (CT ct
)
1069 char *cp
, *dp
, **ap
, **ep
;
1070 char *bp
, buffer
[BUFSIZ
];
1071 struct multipart
*m
;
1073 struct part
*part
, **next
;
1074 CI ci
= &ct
->c_ctinfo
;
1079 * The encoding for multipart messages must be either
1080 * 7bit, 8bit, or binary (per RFC2045).
1082 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1083 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1084 /* Copy the Content-Transfer-Encoding header field body so we can
1085 remove any trailing whitespace and leading blanks from it. */
1086 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1088 bp
= cte
+ strlen (cte
) - 1;
1089 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1090 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1093 "\"%s/%s\" type in message %s must be encoded in\n"
1094 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1095 "is to\nmanually edit the file and change the \"%s\"\n"
1096 "Content-Transfer-Encoding to one of those. For now",
1097 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1104 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1105 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1107 ct
->c_subtype
= kv
->kv_value
;
1110 * Check for "boundary" parameter, which is
1111 * required for multipart messages.
1114 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1115 if (!strcasecmp (*ap
, "boundary")) {
1121 /* complain if boundary parameter is missing */
1124 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1125 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1129 /* allocate primary structure for multipart info */
1130 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1131 adios (NULL
, "out of memory");
1132 ct
->c_ctparams
= (void *) m
;
1134 /* check if boundary parameter contains only whitespace characters */
1135 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1138 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1139 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1143 /* remove trailing whitespace from boundary parameter */
1144 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1145 if (!isspace ((unsigned char) *dp
))
1149 /* record boundary separators */
1150 m
->mp_start
= concat (bp
, "\n", NULL
);
1151 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1153 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1154 advise (ct
->c_file
, "unable to open for reading");
1158 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1160 next
= &m
->mp_parts
;
1164 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1168 pos
+= strlen (buffer
);
1169 if (buffer
[0] != '-' || buffer
[1] != '-')
1172 if (strcmp (buffer
+ 2, m
->mp_start
))
1175 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1176 adios (NULL
, "out of memory");
1178 next
= &part
->mp_next
;
1180 if (!(p
= get_content (fp
, ct
->c_file
,
1181 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1188 fseek (fp
, pos
, SEEK_SET
);
1191 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1195 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1196 if (p
->c_end
< p
->c_begin
)
1197 p
->c_begin
= p
->c_end
;
1202 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1208 if (! suppress_bogus_mp_content_warning
) {
1209 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1211 bogus_mp_content
= 1;
1213 if (!inout
&& part
) {
1215 p
->c_end
= ct
->c_end
;
1217 if (p
->c_begin
>= p
->c_end
) {
1218 for (next
= &m
->mp_parts
; *next
!= part
;
1219 next
= &((*next
)->mp_next
))
1223 free ((char *) part
);
1228 /* reverse the order of the parts for multipart/alternative */
1229 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1233 * label all subparts with part number, and
1234 * then initialize the content of the subpart.
1239 char partnam
[BUFSIZ
];
1242 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1243 pp
= partnam
+ strlen (partnam
);
1248 for (part
= m
->mp_parts
, partnum
= 1; part
;
1249 part
= part
->mp_next
, partnum
++) {
1252 sprintf (pp
, "%d", partnum
);
1253 p
->c_partno
= add (partnam
, NULL
);
1255 /* initialize the content of the subparts */
1256 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1264 get_leftover_mp_content (ct
, 1);
1265 get_leftover_mp_content (ct
, 0);
1274 * reverse the order of the parts of a multipart/alternative
1278 reverse_parts (CT ct
)
1280 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1284 /* Reverse the order of its parts by walking the mp_parts list
1285 and pushing each node to the front. */
1286 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1287 next
= part
->mp_next
;
1288 part
->mp_next
= m
->mp_parts
;
1302 CI ci
= &ct
->c_ctinfo
;
1304 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1306 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1307 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1311 /* check for missing subtype */
1312 if (!*ci
->ci_subtype
)
1313 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1316 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1317 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1319 ct
->c_subtype
= kv
->kv_value
;
1321 switch (ct
->c_subtype
) {
1322 case MESSAGE_RFC822
:
1325 case MESSAGE_PARTIAL
:
1330 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1331 adios (NULL
, "out of memory");
1332 ct
->c_ctparams
= (void *) p
;
1334 /* scan for parameters "id", "number", and "total" */
1335 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1336 if (!strcasecmp (*ap
, "id")) {
1337 p
->pm_partid
= add (*ep
, NULL
);
1340 if (!strcasecmp (*ap
, "number")) {
1341 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1342 || p
->pm_partno
< 1) {
1345 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1346 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1347 ct
->c_file
, TYPE_FIELD
);
1352 if (!strcasecmp (*ap
, "total")) {
1353 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1362 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1364 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1365 ci
->ci_type
, ci
->ci_subtype
,
1366 ct
->c_file
, TYPE_FIELD
);
1372 case MESSAGE_EXTERNAL
:
1379 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1380 adios (NULL
, "out of memory");
1381 ct
->c_ctparams
= (void *) e
;
1384 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1385 advise (ct
->c_file
, "unable to open for reading");
1389 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1391 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1399 p
->c_ceopenfnx
= NULL
;
1400 if ((exresult
= params_external (ct
, 0)) != NOTOK
1401 && p
->c_ceopenfnx
== openMail
) {
1405 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1407 content_error (NULL
, ct
,
1408 "empty body for access-type=mail-server");
1412 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1413 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1415 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1417 adios ("failed", "fread");
1420 adios (NULL
, "unexpected EOF from fread");
1423 bp
+= cc
, size
-= cc
;
1430 p
->c_end
= p
->c_begin
;
1435 if (exresult
== NOTOK
)
1437 if (e
->eb_flags
== NOTOK
)
1440 switch (p
->c_type
) {
1445 if (p
->c_subtype
!= MESSAGE_RFC822
)
1449 e
->eb_partno
= ct
->c_partno
;
1451 (*p
->c_ctinitfnx
) (p
);
1466 params_external (CT ct
, int composing
)
1469 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1470 CI ci
= &ct
->c_ctinfo
;
1472 ct
->c_ceopenfnx
= NULL
;
1473 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1474 if (!strcasecmp (*ap
, "access-type")) {
1475 struct str2init
*s2i
;
1476 CT p
= e
->eb_content
;
1478 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1479 if (!strcasecmp (*ep
, s2i
->si_key
))
1483 e
->eb_flags
= NOTOK
;
1484 p
->c_encoding
= CE_EXTERNAL
;
1487 e
->eb_access
= s2i
->si_key
;
1488 e
->eb_flags
= s2i
->si_val
;
1489 p
->c_encoding
= CE_EXTERNAL
;
1491 /* Call the Init function for this external type */
1492 if ((*s2i
->si_init
)(p
) == NOTOK
)
1496 if (!strcasecmp (*ap
, "name")) {
1500 if (!strcasecmp (*ap
, "permission")) {
1501 e
->eb_permission
= *ep
;
1504 if (!strcasecmp (*ap
, "site")) {
1508 if (!strcasecmp (*ap
, "directory")) {
1512 if (!strcasecmp (*ap
, "mode")) {
1516 if (!strcasecmp (*ap
, "size")) {
1517 sscanf (*ep
, "%lu", &e
->eb_size
);
1520 if (!strcasecmp (*ap
, "server")) {
1524 if (!strcasecmp (*ap
, "subject")) {
1525 e
->eb_subject
= *ep
;
1528 if (!strcasecmp (*ap
, "url")) {
1530 * According to RFC 2017, we have to remove all whitespace from
1535 e
->eb_url
= u
= mh_xmalloc(strlen(*ep
) + 1);
1537 for (; *p
!= '\0'; p
++) {
1538 if (! isspace((unsigned char) *p
))
1545 if (composing
&& !strcasecmp (*ap
, "body")) {
1546 e
->eb_body
= getcpy (*ep
);
1551 if (!e
->eb_access
) {
1553 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1554 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1567 InitApplication (CT ct
)
1570 CI ci
= &ct
->c_ctinfo
;
1573 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1574 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1576 ct
->c_subtype
= kv
->kv_value
;
1583 * TRANSFER ENCODINGS
1587 init_encoding (CT ct
, OpenCEFunc openfnx
)
1589 ct
->c_ceopenfnx
= openfnx
;
1590 ct
->c_ceclosefnx
= close_encoding
;
1591 ct
->c_cesizefnx
= size_encoding
;
1598 close_encoding (CT ct
)
1600 CE ce
= &ct
->c_cefile
;
1609 static unsigned long
1610 size_encoding (CT ct
)
1615 CE ce
= &ct
->c_cefile
;
1618 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1619 return (long) st
.st_size
;
1622 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1623 return (long) st
.st_size
;
1628 if (ct
->c_encoding
== CE_EXTERNAL
)
1629 return (ct
->c_end
- ct
->c_begin
);
1632 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1633 return (ct
->c_end
- ct
->c_begin
);
1635 if (fstat (fd
, &st
) != NOTOK
)
1636 size
= (long) st
.st_size
;
1640 (*ct
->c_ceclosefnx
) (ct
);
1649 static unsigned char b642nib
[0x80] = {
1650 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1651 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1652 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1653 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1654 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1655 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1656 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1657 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1658 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1659 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1660 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1661 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1662 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1663 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1664 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1665 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1672 return init_encoding (ct
, openBase64
);
1677 openBase64 (CT ct
, char **file
)
1679 int bitno
, cc
, digested
;
1680 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1682 unsigned char value
, b
;
1683 char *cp
, *ep
, buffer
[BUFSIZ
];
1684 /* sbeck -- handle suffixes */
1686 CE ce
= &ct
->c_cefile
;
1690 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1695 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1696 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1702 if (*file
== NULL
) {
1705 ce
->ce_file
= add (*file
, NULL
);
1709 /* sbeck@cise.ufl.edu -- handle suffixes */
1711 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1712 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1713 cp
= context_find (buffer
);
1714 if (cp
== NULL
|| *cp
== '\0') {
1715 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1717 cp
= context_find (buffer
);
1719 if (cp
!= NULL
&& *cp
!= '\0') {
1720 if (ce
->ce_unlink
) {
1721 /* Create temporary file with filename extension. */
1722 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1723 adios(NULL
, "unable to create temporary file in %s",
1727 ce
->ce_file
= add (cp
, ce
->ce_file
);
1729 } else if (*file
== NULL
) {
1731 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1732 adios(NULL
, "unable to create temporary file in %s",
1735 ce
->ce_file
= add (tempfile
, NULL
);
1738 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1739 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1743 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1744 adios (NULL
, "internal error(1)");
1747 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1748 content_error (ct
->c_file
, ct
, "unable to open for reading");
1754 if ((digested
= ct
->c_digested
))
1755 MD5Init (&mdContext
);
1761 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1763 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1765 content_error (ct
->c_file
, ct
, "error reading from");
1769 content_error (NULL
, ct
, "premature eof");
1777 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1780 if (isspace ((unsigned char) *cp
))
1782 if (skip
|| (((unsigned char) *cp
) & 0x80)
1783 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1785 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1786 (unsigned char) *cp
,
1787 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1790 content_error (NULL
, ct
,
1791 "invalid BASE64 encoding -- continuing");
1795 bits
|= value
<< bitno
;
1797 if ((bitno
-= 6) < 0) {
1798 b
= (bits
>> 16) & 0xff;
1799 if (!text
|| b
!= '\r')
1800 putc ((char) b
, ce
->ce_fp
);
1802 MD5Update (&mdContext
, &b
, 1);
1804 b
= (bits
>> 8) & 0xff;
1805 if (! text
|| b
!= '\r')
1806 putc ((char) b
, ce
->ce_fp
);
1808 MD5Update (&mdContext
, &b
, 1);
1811 if (! text
|| b
!= '\r')
1812 putc ((char) b
, ce
->ce_fp
);
1814 MD5Update (&mdContext
, &b
, 1);
1818 if (ferror (ce
->ce_fp
)) {
1819 content_error (ce
->ce_file
, ct
,
1820 "error writing to");
1823 bitno
= 18, bits
= 0L, skip
= 0;
1829 goto self_delimiting
;
1838 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1840 content_error (NULL
, ct
, "invalid BASE64 encoding");
1845 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1847 if (fflush (ce
->ce_fp
)) {
1848 content_error (ce
->ce_file
, ct
, "error writing to");
1853 unsigned char digest
[16];
1855 MD5Final (digest
, &mdContext
);
1856 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1857 sizeof(digest
) / sizeof(digest
[0])))
1858 content_error (NULL
, ct
,
1859 "content integrity suspect (digest mismatch) -- continuing");
1862 fprintf (stderr
, "content integrity confirmed\n");
1865 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1868 *file
= ce
->ce_file
;
1873 return fileno (ce
->ce_fp
);
1880 free_encoding (ct
, 0);
1889 static char hex2nib
[0x80] = {
1890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1892 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1893 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1894 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1895 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1896 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1897 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1898 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1899 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1900 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1901 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1902 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1912 return init_encoding (ct
, openQuoted
);
1917 openQuoted (CT ct
, char **file
)
1919 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1921 char buffer
[BUFSIZ
];
1923 CE ce
= &ct
->c_cefile
;
1924 /* sbeck -- handle suffixes */
1929 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1934 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1935 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1941 if (*file
== NULL
) {
1944 ce
->ce_file
= add (*file
, NULL
);
1948 /* sbeck@cise.ufl.edu -- handle suffixes */
1950 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1951 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1952 cp
= context_find (buffer
);
1953 if (cp
== NULL
|| *cp
== '\0') {
1954 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1956 cp
= context_find (buffer
);
1958 if (cp
!= NULL
&& *cp
!= '\0') {
1959 if (ce
->ce_unlink
) {
1960 /* Create temporary file with filename extension. */
1961 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1962 adios(NULL
, "unable to create temporary file in %s",
1966 ce
->ce_file
= add (cp
, ce
->ce_file
);
1968 } else if (*file
== NULL
) {
1970 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1971 adios(NULL
, "unable to create temporary file in %s",
1974 ce
->ce_file
= add (tempfile
, NULL
);
1977 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1978 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1982 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1983 adios (NULL
, "internal error(2)");
1986 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1987 content_error (ct
->c_file
, ct
, "unable to open for reading");
1993 if ((digested
= ct
->c_digested
))
1994 MD5Init (&mdContext
);
2001 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2003 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2004 content_error (NULL
, ct
, "premature eof");
2008 if ((cc
= strlen (buffer
)) > len
)
2012 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2013 if (!isspace ((unsigned char) *ep
))
2017 for (; cp
< ep
; cp
++) {
2019 /* in an escape sequence */
2021 /* at byte 1 of an escape sequence */
2022 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2023 /* next is byte 2 */
2026 /* at byte 2 of an escape sequence */
2028 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2029 putc (mask
, ce
->ce_fp
);
2031 MD5Update (&mdContext
, &mask
, 1);
2032 if (ferror (ce
->ce_fp
)) {
2033 content_error (ce
->ce_file
, ct
, "error writing to");
2036 /* finished escape sequence; next may be literal or a new
2037 * escape sequence */
2040 /* on to next byte */
2044 /* not in an escape sequence */
2046 /* starting an escape sequence, or invalid '='? */
2047 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2048 /* "=\n" soft line break, eat the \n */
2052 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2053 /* We don't have 2 bytes left, so this is an invalid
2054 * escape sequence; just show the raw bytes (below). */
2055 } else if (isxdigit ((unsigned char) cp
[1]) &&
2056 isxdigit ((unsigned char) cp
[2])) {
2057 /* Next 2 bytes are hex digits, making this a valid escape
2058 * sequence; let's decode it (above). */
2062 /* One or both of the next 2 is out of range, making this
2063 * an invalid escape sequence; just show the raw bytes
2068 /* Just show the raw byte. */
2069 putc (*cp
, ce
->ce_fp
);
2072 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2074 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2077 if (ferror (ce
->ce_fp
)) {
2078 content_error (ce
->ce_file
, ct
, "error writing to");
2084 content_error (NULL
, ct
,
2085 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2089 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2091 if (fflush (ce
->ce_fp
)) {
2092 content_error (ce
->ce_file
, ct
, "error writing to");
2097 unsigned char digest
[16];
2099 MD5Final (digest
, &mdContext
);
2100 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2101 sizeof(digest
) / sizeof(digest
[0])))
2102 content_error (NULL
, ct
,
2103 "content integrity suspect (digest mismatch) -- continuing");
2106 fprintf (stderr
, "content integrity confirmed\n");
2109 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2112 *file
= ce
->ce_file
;
2117 return fileno (ce
->ce_fp
);
2120 free_encoding (ct
, 0);
2136 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2139 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2145 open7Bit (CT ct
, char **file
)
2147 int cc
, fd
, len
, own_ct_fp
= 0;
2148 char buffer
[BUFSIZ
];
2149 /* sbeck -- handle suffixes */
2152 CE ce
= &ct
->c_cefile
;
2155 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2160 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2161 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2167 if (*file
== NULL
) {
2170 ce
->ce_file
= add (*file
, NULL
);
2174 /* sbeck@cise.ufl.edu -- handle suffixes */
2176 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2177 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2178 cp
= context_find (buffer
);
2179 if (cp
== NULL
|| *cp
== '\0') {
2180 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2182 cp
= context_find (buffer
);
2184 if (cp
!= NULL
&& *cp
!= '\0') {
2185 if (ce
->ce_unlink
) {
2186 /* Create temporary file with filename extension. */
2187 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2188 adios(NULL
, "unable to create temporary file in %s",
2192 ce
->ce_file
= add (cp
, ce
->ce_file
);
2194 } else if (*file
== NULL
) {
2196 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2197 adios(NULL
, "unable to create temporary file in %s",
2200 ce
->ce_file
= add (tempfile
, NULL
);
2203 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2204 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2208 if (ct
->c_type
== CT_MULTIPART
) {
2210 CI ci
= &ct
->c_ctinfo
;
2213 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2214 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2215 + 1 + strlen (ci
->ci_subtype
);
2216 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2217 putc (';', ce
->ce_fp
);
2220 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2222 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2223 fputs ("\n\t", ce
->ce_fp
);
2226 putc (' ', ce
->ce_fp
);
2229 fprintf (ce
->ce_fp
, "%s", buffer
);
2233 if (ci
->ci_comment
) {
2234 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2235 fputs ("\n\t", ce
->ce_fp
);
2239 putc (' ', ce
->ce_fp
);
2242 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2245 fprintf (ce
->ce_fp
, "\n");
2247 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2249 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2251 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2252 fprintf (ce
->ce_fp
, "\n");
2255 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2256 adios (NULL
, "internal error(3)");
2259 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2260 content_error (ct
->c_file
, ct
, "unable to open for reading");
2266 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2268 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2270 content_error (ct
->c_file
, ct
, "error reading from");
2274 content_error (NULL
, ct
, "premature eof");
2282 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2283 if (ferror (ce
->ce_fp
)) {
2284 content_error (ce
->ce_file
, ct
, "error writing to");
2289 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2291 if (fflush (ce
->ce_fp
)) {
2292 content_error (ce
->ce_file
, ct
, "error writing to");
2296 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2299 *file
= ce
->ce_file
;
2304 return fileno (ce
->ce_fp
);
2307 free_encoding (ct
, 0);
2321 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2323 char cachefile
[BUFSIZ
];
2326 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2331 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2332 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2338 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2339 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2340 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2341 ce
->ce_file
= getcpy (cachefile
);
2345 admonish (cachefile
, "unable to fopen for reading");
2352 *file
= ce
->ce_file
;
2353 *fd
= fileno (ce
->ce_fp
);
2364 return init_encoding (ct
, openFile
);
2369 openFile (CT ct
, char **file
)
2372 char cachefile
[BUFSIZ
];
2373 struct exbody
*e
= ct
->c_ctexbody
;
2374 CE ce
= &ct
->c_cefile
;
2376 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2388 content_error (NULL
, ct
, "missing name parameter");
2392 ce
->ce_file
= getcpy (e
->eb_name
);
2395 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2396 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2400 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2401 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2402 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2406 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2407 if ((fp
= fopen (cachefile
, "w"))) {
2409 char buffer
[BUFSIZ
];
2410 FILE *gp
= ce
->ce_fp
;
2412 fseek (gp
, 0L, SEEK_SET
);
2414 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2416 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2420 admonish (ce
->ce_file
, "error reading");
2421 (void) m_unlink (cachefile
);
2425 admonish (cachefile
, "error writing");
2426 (void) m_unlink (cachefile
);
2433 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2434 *file
= ce
->ce_file
;
2435 return fileno (ce
->ce_fp
);
2445 return init_encoding (ct
, openFTP
);
2450 openFTP (CT ct
, char **file
)
2452 int cachetype
, caching
, fd
;
2454 char *bp
, *ftp
, *user
, *pass
;
2455 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2457 CE ce
= &ct
->c_cefile
;
2458 static char *username
= NULL
;
2459 static char *password
= NULL
;
2463 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2469 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2480 if (!e
->eb_name
|| !e
->eb_site
) {
2481 content_error (NULL
, ct
, "missing %s parameter",
2482 e
->eb_name
? "site": "name");
2489 pidcheck (pidwait (xpid
, NOTOK
));
2493 /* Get the buffer ready to go */
2495 buflen
= sizeof(buffer
);
2498 * Construct the query message for user
2500 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2506 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2512 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2513 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2518 if (e
->eb_size
> 0) {
2519 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2524 snprintf (bp
, buflen
, "? ");
2527 * Now, check the answer
2529 if (!getanswer (buffer
))
2534 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2538 ruserpass (e
->eb_site
, &username
, &password
);
2543 ce
->ce_unlink
= (*file
== NULL
);
2545 cachefile
[0] = '\0';
2546 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2547 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2548 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2549 if (*file
== NULL
) {
2556 ce
->ce_file
= add (*file
, NULL
);
2558 ce
->ce_file
= add (cachefile
, NULL
);
2561 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2562 adios(NULL
, "unable to create temporary file in %s",
2565 ce
->ce_file
= add (tempfile
, NULL
);
2568 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2569 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2574 int child_id
, i
, vecp
;
2578 vec
[vecp
++] = r1bindex (ftp
, '/');
2579 vec
[vecp
++] = e
->eb_site
;
2582 vec
[vecp
++] = e
->eb_dir
;
2583 vec
[vecp
++] = e
->eb_name
;
2584 vec
[vecp
++] = ce
->ce_file
,
2585 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2586 ? "ascii" : "binary";
2591 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2595 adios ("fork", "unable to");
2599 close (fileno (ce
->ce_fp
));
2601 fprintf (stderr
, "unable to exec ");
2607 if (pidXwait (child_id
, NULL
)) {
2608 username
= password
= NULL
;
2618 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2623 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2624 if ((fp
= fopen (cachefile
, "w"))) {
2626 FILE *gp
= ce
->ce_fp
;
2628 fseek (gp
, 0L, SEEK_SET
);
2630 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2632 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2636 admonish (ce
->ce_file
, "error reading");
2637 (void) m_unlink (cachefile
);
2641 admonish (cachefile
, "error writing");
2642 (void) m_unlink (cachefile
);
2650 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2651 *file
= ce
->ce_file
;
2652 return fileno (ce
->ce_fp
);
2663 return init_encoding (ct
, openMail
);
2668 openMail (CT ct
, char **file
)
2670 int child_id
, fd
, i
, vecp
;
2672 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2673 struct exbody
*e
= ct
->c_ctexbody
;
2674 CE ce
= &ct
->c_cefile
;
2676 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2687 if (!e
->eb_server
) {
2688 content_error (NULL
, ct
, "missing server parameter");
2695 pidcheck (pidwait (xpid
, NOTOK
));
2699 /* Get buffer ready to go */
2701 buflen
= sizeof(buffer
);
2703 /* Now, construct query message */
2704 snprintf (bp
, buflen
, "Retrieve content");
2710 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2716 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2718 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2720 /* Now, check answer */
2721 if (!getanswer (buffer
))
2725 vec
[vecp
++] = r1bindex (mailproc
, '/');
2726 vec
[vecp
++] = e
->eb_server
;
2727 vec
[vecp
++] = "-subject";
2728 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2729 vec
[vecp
++] = "-body";
2730 vec
[vecp
++] = e
->eb_body
;
2733 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2737 advise ("fork", "unable to");
2741 execvp (mailproc
, vec
);
2742 fprintf (stderr
, "unable to exec ");
2748 if (pidXwait (child_id
, NULL
) == OK
)
2749 advise (NULL
, "request sent");
2753 if (*file
== NULL
) {
2755 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2756 adios(NULL
, "unable to create temporary file in %s",
2759 ce
->ce_file
= add (tempfile
, NULL
);
2762 ce
->ce_file
= add (*file
, NULL
);
2766 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2767 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2771 /* showproc is for mhshow and mhstore, though mhlist -debug
2772 * prints it, too. */
2774 free (ct
->c_showproc
);
2775 ct
->c_showproc
= add ("true", NULL
);
2777 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2778 *file
= ce
->ce_file
;
2779 return fileno (ce
->ce_fp
);
2790 return init_encoding (ct
, openURL
);
2795 openURL (CT ct
, char **file
)
2797 struct exbody
*e
= ct
->c_ctexbody
;
2798 CE ce
= &ct
->c_cefile
;
2799 char *urlprog
, *program
;
2800 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2801 int fd
, caching
, cachetype
;
2802 struct msgs_array args
= { 0, 0, NULL
};
2805 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2809 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2813 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2825 content_error(NULL
, ct
, "missing url parameter");
2832 pidcheck (pidwait (xpid
, NOTOK
));
2836 ce
->ce_unlink
= (*file
== NULL
);
2838 cachefile
[0] = '\0';
2840 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2841 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2842 if (*file
== NULL
) {
2849 ce
->ce_file
= add(*file
, NULL
);
2851 ce
->ce_file
= add(cachefile
, NULL
);
2854 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2855 adios(NULL
, "unable to create temporary file in %s",
2858 ce
->ce_file
= add (tempfile
, NULL
);
2861 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2862 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2866 switch (child_id
= fork()) {
2868 adios ("fork", "unable to");
2872 argsplit_msgarg(&args
, urlprog
, &program
);
2873 app_msgarg(&args
, e
->eb_url
);
2874 app_msgarg(&args
, NULL
);
2875 dup2(fileno(ce
->ce_fp
), 1);
2876 close(fileno(ce
->ce_fp
));
2877 execvp(program
, args
.msgs
);
2878 fprintf(stderr
, "Unable to exec ");
2884 if (pidXwait(child_id
, NULL
)) {
2892 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2897 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2898 if ((fp
= fopen(cachefile
, "w"))) {
2900 FILE *gp
= ce
->ce_fp
;
2902 fseeko(gp
, 0, SEEK_SET
);
2904 while ((cc
= fread(buffer
, sizeof(*buffer
),
2905 sizeof(buffer
), gp
)) > 0)
2906 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2911 admonish(ce
->ce_file
, "error reading");
2912 (void) m_unlink (cachefile
);
2919 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2920 *file
= ce
->ce_file
;
2925 readDigest (CT ct
, char *cp
)
2930 unsigned char *dp
, value
, *ep
;
2936 for (ep
= (dp
= ct
->c_digest
)
2937 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2942 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2944 fprintf (stderr
, "invalid BASE64 encoding\n");
2948 bits
|= value
<< bitno
;
2950 if ((bitno
-= 6) < 0) {
2951 if (dp
+ (3 - skip
) > ep
)
2952 goto invalid_digest
;
2953 *dp
++ = (bits
>> 16) & 0xff;
2955 *dp
++ = (bits
>> 8) & 0xff;
2957 *dp
++ = bits
& 0xff;
2967 goto self_delimiting
;
2972 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2982 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2990 fprintf (stderr
, "MD5 digest=");
2991 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2992 fprintf (stderr
, "%02x", *dp
& 0xff);
2993 fprintf (stderr
, "\n");
3000 /* Multipart parts might have content before the first subpart and/or
3001 after the last subpart that hasn't been stored anywhere else, so do
3004 get_leftover_mp_content (CT ct
, int before
/* or after */) {
3005 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
3007 int found_boundary
= 0;
3008 char buffer
[BUFSIZ
];
3011 char *content
= NULL
;
3013 if (! m
) return NOTOK
;
3016 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
3018 /* Isolate the beginning of this part to the beginning of the
3019 first subpart and save any content between them. */
3020 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
3021 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3022 boundary
= concat ("--", m
->mp_start
, NULL
);
3024 struct part
*last_subpart
= NULL
;
3025 struct part
*subpart
;
3027 /* Go to the last subpart to get its end position. */
3028 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3029 last_subpart
= subpart
;
3032 if (last_subpart
== NULL
) return NOTOK
;
3034 /* Isolate the end of the last subpart to the end of this part
3035 and save any content between them. */
3036 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3037 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3038 boundary
= concat ("--", m
->mp_stop
, NULL
);
3041 /* Back up by 1 to pick up the newline. */
3042 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
3043 read
+= strlen (buffer
);
3044 /* Don't look beyond beginning of first subpart (before) or
3045 next part (after). */
3046 if (read
> max
) buffer
[read
-max
] = '\0';
3049 if (! strcmp (buffer
, boundary
)) {
3053 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
3059 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3061 char *old_content
= content
;
3062 content
= concat (content
, buffer
, NULL
);
3066 ? concat ("\n", buffer
, NULL
)
3067 : concat (buffer
, NULL
);
3072 if (found_boundary
|| read
> max
) break;
3074 if (read
> max
) break;
3078 /* Skip the newline if that's all there is. */
3082 /* Remove trailing newline, except at EOF. */
3083 if ((before
|| ! feof (ct
->c_fp
)) &&
3084 (cp
= content
+ strlen (content
)) > content
&&
3089 if (strlen (content
) > 1) {
3091 m
->mp_content_before
= content
;
3093 m
->mp_content_after
= content
;
3107 ct_type_str (int type
) {
3109 case CT_APPLICATION
:
3110 return "application";
3126 return "unknown_type";
3132 ct_subtype_str (int type
, int subtype
) {
3134 case CT_APPLICATION
:
3136 case APPLICATION_OCTETS
:
3138 case APPLICATION_POSTSCRIPT
:
3139 return "postscript";
3141 return "unknown_app_subtype";
3145 case MESSAGE_RFC822
:
3147 case MESSAGE_PARTIAL
:
3149 case MESSAGE_EXTERNAL
:
3152 return "unknown_msg_subtype";
3158 case MULTI_ALTERNATE
:
3159 return "alternative";
3162 case MULTI_PARALLEL
:
3165 return "unknown_multipart_subtype";
3176 return "unknown_text_subtype";
3179 return "unknown_type";
3184 /* Find the content type and InitFunc for the CT. */
3185 const struct str2init
*
3186 get_ct_init (int type
) {
3187 const struct str2init
*sp
;
3189 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3190 if (type
== sp
->si_val
) {
3199 ce_str (int encoding
) {
3204 return "quoted-printable";
3220 /* Find the content type and InitFunc for the content encoding method. */
3221 const struct str2init
*
3222 get_ce_method (const char *method
) {
3223 struct str2init
*sp
;
3225 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3226 if (! strcasecmp (method
, sp
->si_key
)) {
3235 parse_header_attrs (const char *filename
, int len
, char **header_attrp
, CI ci
,
3237 char **attr
= ci
->ci_attrs
;
3238 char *cp
= *header_attrp
;
3240 while (*cp
== ';') {
3241 char *dp
, *vp
, *up
, c
;
3243 /* Relies on knowledge of this declaration:
3244 * char *ci_attrs[NPARMS + 2];
3246 if (attr
>= ci
->ci_attrs
+ sizeof ci
->ci_attrs
/sizeof (char *) - 2) {
3248 "too many parameters in message %s's %s: field (%d max)",
3249 filename
, TYPE_FIELD
, NPARMS
);
3255 while (isspace ((unsigned char) *cp
))
3259 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3266 "extraneous trailing ';' in message %s's %s: "
3268 filename
, TYPE_FIELD
);
3273 /* down case the attribute name */
3274 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3275 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3276 *dp
= tolower ((unsigned char) *dp
);
3278 for (up
= dp
; isspace ((unsigned char) *dp
);)
3280 if (dp
== cp
|| *dp
!= '=') {
3282 "invalid parameter in message %s's %s: "
3283 "field\n%*.*sparameter %s (error detected at offset %d)",
3284 filename
, TYPE_FIELD
, len
, len
, "", cp
, dp
- cp
);
3289 vp
= (*attr
= add (cp
, NULL
)) + (up
- cp
);
3291 for (dp
++; isspace ((unsigned char) *dp
);)
3294 /* Now store the attribute value. */
3295 ci
->ci_values
[attr
- ci
->ci_attrs
] = vp
= *attr
+ (dp
- cp
);
3298 for (cp
= ++dp
, dp
= vp
;;) {
3299 switch (c
= *cp
++) {
3303 "invalid quoted-string in message %s's %s: "
3304 "field\n%*.*s(parameter %s)",
3305 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3311 if ((c
= *cp
++) == '\0')
3326 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
3332 "invalid parameter in message %s's %s: "
3333 "field\n%*.*s(parameter %s)",
3334 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3339 while (isspace ((unsigned char) *cp
))
3343 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3357 content_charset (CT ct
) {
3358 const char *const charset
= "charset";
3359 char *default_charset
= NULL
;
3360 CI ctinfo
= &ct
->c_ctinfo
;
3362 char **src_charset
= NULL
;
3364 for (ap
= ctinfo
->ci_attrs
, vp
= ctinfo
->ci_values
; *ap
; ++ap
, ++vp
) {
3365 if (! strcasecmp (*ap
, charset
)) {
3371 /* RFC 2045, Sec. 5.2: default to us-ascii. */
3372 if (src_charset
== NULL
) src_charset
= &default_charset
;
3373 if (*src_charset
== NULL
) *src_charset
= "US-ASCII";
3375 return *src_charset
;
3379 /* Change the value of a name=value pair in a header field body.
3380 If the name isn't there, append them. In any case, a new
3381 string will be allocated and must be free'd by the caller.
3382 Trims any trailing newlines. */
3384 update_attr (char *body
, const char *name
, const char *value
) {
3385 char *bp
= nmh_strcasestr (body
, name
);
3389 char *other_attrs
= strchr (bp
, ';');
3391 *(bp
+ strlen (name
)) = '\0';
3392 new_body
= concat (body
, "\"", value
, "\"", NULL
);
3397 /* Trim any trailing newlines. */
3398 for (cp
= &other_attrs
[strlen (other_attrs
) - 1];
3399 cp
> other_attrs
&& *cp
== '\n';
3400 *cp
-- = '\0') continue;
3401 new_body
= add (other_attrs
, new_body
);
3406 /* Append name/value pair, after first removing a final newline
3407 and (extraneous) semicolon. */
3408 if (*(cp
= &body
[strlen (body
) - 1]) == '\n') *cp
= '\0';
3409 if (*(cp
= &body
[strlen (body
) - 1]) == ';') *cp
= '\0';
3410 new_body
= concat (body
, "; ", name
, "\"", value
, "\"", NULL
);