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;
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
) {
1704 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1705 adios(NULL
, "unable to create temporary file in %s",
1708 ce
->ce_file
= add (tempfile
, NULL
);
1711 ce
->ce_file
= add (*file
, NULL
);
1715 /* sbeck@cise.ufl.edu -- handle suffixes */
1717 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1718 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1719 cp
= context_find (buffer
);
1720 if (cp
== NULL
|| *cp
== '\0') {
1721 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1723 cp
= context_find (buffer
);
1725 if (cp
!= NULL
&& *cp
!= '\0') {
1726 if (! ce
->ce_unlink
) {
1727 ce
->ce_file
= add (cp
, ce
->ce_file
);
1731 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1732 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1736 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1737 adios (NULL
, "internal error(1)");
1740 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1741 content_error (ct
->c_file
, ct
, "unable to open for reading");
1747 if ((digested
= ct
->c_digested
))
1748 MD5Init (&mdContext
);
1754 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1756 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1758 content_error (ct
->c_file
, ct
, "error reading from");
1762 content_error (NULL
, ct
, "premature eof");
1770 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1773 if (isspace ((unsigned char) *cp
))
1775 if (skip
|| (((unsigned char) *cp
) & 0x80)
1776 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1778 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1779 (unsigned char) *cp
,
1780 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1783 content_error (NULL
, ct
,
1784 "invalid BASE64 encoding -- continuing");
1788 bits
|= value
<< bitno
;
1790 if ((bitno
-= 6) < 0) {
1791 b
= (bits
>> 16) & 0xff;
1792 putc ((char) b
, ce
->ce_fp
);
1794 MD5Update (&mdContext
, &b
, 1);
1796 b
= (bits
>> 8) & 0xff;
1797 putc ((char) b
, ce
->ce_fp
);
1799 MD5Update (&mdContext
, &b
, 1);
1802 putc ((char) b
, ce
->ce_fp
);
1804 MD5Update (&mdContext
, &b
, 1);
1808 if (ferror (ce
->ce_fp
)) {
1809 content_error (ce
->ce_file
, ct
,
1810 "error writing to");
1813 bitno
= 18, bits
= 0L, skip
= 0;
1819 goto self_delimiting
;
1828 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1830 content_error (NULL
, ct
, "invalid BASE64 encoding");
1835 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1837 if (fflush (ce
->ce_fp
)) {
1838 content_error (ce
->ce_file
, ct
, "error writing to");
1843 unsigned char digest
[16];
1845 MD5Final (digest
, &mdContext
);
1846 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1847 sizeof(digest
) / sizeof(digest
[0])))
1848 content_error (NULL
, ct
,
1849 "content integrity suspect (digest mismatch) -- continuing");
1852 fprintf (stderr
, "content integrity confirmed\n");
1855 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1858 *file
= ce
->ce_file
;
1863 return fileno (ce
->ce_fp
);
1870 free_encoding (ct
, 0);
1879 static char hex2nib
[0x80] = {
1880 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1881 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1882 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1883 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1884 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1885 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1886 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1887 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1888 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1889 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1892 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 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
1902 return init_encoding (ct
, openQuoted
);
1907 openQuoted (CT ct
, char **file
)
1909 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1911 char buffer
[BUFSIZ
];
1913 CE ce
= &ct
->c_cefile
;
1914 /* sbeck -- handle suffixes */
1919 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1924 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1925 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1931 if (*file
== NULL
) {
1933 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1934 adios(NULL
, "unable to create temporary file in %s",
1937 ce
->ce_file
= add (tempfile
, NULL
);
1940 ce
->ce_file
= add (*file
, NULL
);
1944 /* sbeck@cise.ufl.edu -- handle suffixes */
1946 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1947 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1948 cp
= context_find (buffer
);
1949 if (cp
== NULL
|| *cp
== '\0') {
1950 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1952 cp
= context_find (buffer
);
1954 if (cp
!= NULL
&& *cp
!= '\0') {
1955 if (! ce
->ce_unlink
) {
1956 ce
->ce_file
= add (cp
, ce
->ce_file
);
1960 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1961 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1965 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1966 adios (NULL
, "internal error(2)");
1969 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1970 content_error (ct
->c_file
, ct
, "unable to open for reading");
1976 if ((digested
= ct
->c_digested
))
1977 MD5Init (&mdContext
);
1984 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1986 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
1987 content_error (NULL
, ct
, "premature eof");
1991 if ((cc
= strlen (buffer
)) > len
)
1995 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
1996 if (!isspace ((unsigned char) *ep
))
2000 for (; cp
< ep
; cp
++) {
2002 /* in an escape sequence */
2004 /* at byte 1 of an escape sequence */
2005 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2006 /* next is byte 2 */
2009 /* at byte 2 of an escape sequence */
2011 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2012 putc (mask
, ce
->ce_fp
);
2014 MD5Update (&mdContext
, &mask
, 1);
2015 if (ferror (ce
->ce_fp
)) {
2016 content_error (ce
->ce_file
, ct
, "error writing to");
2019 /* finished escape sequence; next may be literal or a new
2020 * escape sequence */
2023 /* on to next byte */
2027 /* not in an escape sequence */
2029 /* starting an escape sequence, or invalid '='? */
2030 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2031 /* "=\n" soft line break, eat the \n */
2035 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2036 /* We don't have 2 bytes left, so this is an invalid
2037 * escape sequence; just show the raw bytes (below). */
2038 } else if (isxdigit ((unsigned char) cp
[1]) &&
2039 isxdigit ((unsigned char) cp
[2])) {
2040 /* Next 2 bytes are hex digits, making this a valid escape
2041 * sequence; let's decode it (above). */
2045 /* One or both of the next 2 is out of range, making this
2046 * an invalid escape sequence; just show the raw bytes
2051 /* Just show the raw byte. */
2052 putc (*cp
, ce
->ce_fp
);
2055 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2057 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2060 if (ferror (ce
->ce_fp
)) {
2061 content_error (ce
->ce_file
, ct
, "error writing to");
2067 content_error (NULL
, ct
,
2068 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2072 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2074 if (fflush (ce
->ce_fp
)) {
2075 content_error (ce
->ce_file
, ct
, "error writing to");
2080 unsigned char digest
[16];
2082 MD5Final (digest
, &mdContext
);
2083 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2084 sizeof(digest
) / sizeof(digest
[0])))
2085 content_error (NULL
, ct
,
2086 "content integrity suspect (digest mismatch) -- continuing");
2089 fprintf (stderr
, "content integrity confirmed\n");
2092 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2095 *file
= ce
->ce_file
;
2100 return fileno (ce
->ce_fp
);
2103 free_encoding (ct
, 0);
2119 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2122 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2128 open7Bit (CT ct
, char **file
)
2130 int cc
, fd
, len
, own_ct_fp
= 0;
2131 char buffer
[BUFSIZ
];
2132 /* sbeck -- handle suffixes */
2135 CE ce
= &ct
->c_cefile
;
2138 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2143 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2144 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2150 if (*file
== NULL
) {
2152 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2153 adios(NULL
, "unable to create temporary file in %s",
2156 ce
->ce_file
= add (tempfile
, NULL
);
2159 ce
->ce_file
= add (*file
, NULL
);
2163 /* sbeck@cise.ufl.edu -- handle suffixes */
2165 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2166 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2167 cp
= context_find (buffer
);
2168 if (cp
== NULL
|| *cp
== '\0') {
2169 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2171 cp
= context_find (buffer
);
2173 if (cp
!= NULL
&& *cp
!= '\0') {
2174 if (! ce
->ce_unlink
) {
2175 ce
->ce_file
= add (cp
, ce
->ce_file
);
2179 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2180 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2184 if (ct
->c_type
== CT_MULTIPART
) {
2186 CI ci
= &ct
->c_ctinfo
;
2189 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2190 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2191 + 1 + strlen (ci
->ci_subtype
);
2192 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2193 putc (';', ce
->ce_fp
);
2196 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2198 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2199 fputs ("\n\t", ce
->ce_fp
);
2202 putc (' ', ce
->ce_fp
);
2205 fprintf (ce
->ce_fp
, "%s", buffer
);
2209 if (ci
->ci_comment
) {
2210 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2211 fputs ("\n\t", ce
->ce_fp
);
2215 putc (' ', ce
->ce_fp
);
2218 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2221 fprintf (ce
->ce_fp
, "\n");
2223 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2225 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2227 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2228 fprintf (ce
->ce_fp
, "\n");
2231 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2232 adios (NULL
, "internal error(3)");
2235 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2236 content_error (ct
->c_file
, ct
, "unable to open for reading");
2242 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2244 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2246 content_error (ct
->c_file
, ct
, "error reading from");
2250 content_error (NULL
, ct
, "premature eof");
2258 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2259 if (ferror (ce
->ce_fp
)) {
2260 content_error (ce
->ce_file
, ct
, "error writing to");
2265 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2267 if (fflush (ce
->ce_fp
)) {
2268 content_error (ce
->ce_file
, ct
, "error writing to");
2272 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2275 *file
= ce
->ce_file
;
2280 return fileno (ce
->ce_fp
);
2283 free_encoding (ct
, 0);
2297 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2299 char cachefile
[BUFSIZ
];
2302 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2307 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2308 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2314 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2315 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2316 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2317 ce
->ce_file
= getcpy (cachefile
);
2321 admonish (cachefile
, "unable to fopen for reading");
2328 *file
= ce
->ce_file
;
2329 *fd
= fileno (ce
->ce_fp
);
2340 return init_encoding (ct
, openFile
);
2345 openFile (CT ct
, char **file
)
2348 char cachefile
[BUFSIZ
];
2349 struct exbody
*e
= ct
->c_ctexbody
;
2350 CE ce
= &ct
->c_cefile
;
2352 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2364 content_error (NULL
, ct
, "missing name parameter");
2368 ce
->ce_file
= getcpy (e
->eb_name
);
2371 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2372 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2376 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2377 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2378 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2382 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2383 if ((fp
= fopen (cachefile
, "w"))) {
2385 char buffer
[BUFSIZ
];
2386 FILE *gp
= ce
->ce_fp
;
2388 fseek (gp
, 0L, SEEK_SET
);
2390 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2392 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2396 admonish (ce
->ce_file
, "error reading");
2397 (void) m_unlink (cachefile
);
2401 admonish (cachefile
, "error writing");
2402 (void) m_unlink (cachefile
);
2409 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2410 *file
= ce
->ce_file
;
2411 return fileno (ce
->ce_fp
);
2421 return init_encoding (ct
, openFTP
);
2426 openFTP (CT ct
, char **file
)
2428 int cachetype
, caching
, fd
;
2430 char *bp
, *ftp
, *user
, *pass
;
2431 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2433 CE ce
= &ct
->c_cefile
;
2434 static char *username
= NULL
;
2435 static char *password
= NULL
;
2439 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2445 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2456 if (!e
->eb_name
|| !e
->eb_site
) {
2457 content_error (NULL
, ct
, "missing %s parameter",
2458 e
->eb_name
? "site": "name");
2465 pidcheck (pidwait (xpid
, NOTOK
));
2469 /* Get the buffer ready to go */
2471 buflen
= sizeof(buffer
);
2474 * Construct the query message for user
2476 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2482 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2488 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2489 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2494 if (e
->eb_size
> 0) {
2495 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2500 snprintf (bp
, buflen
, "? ");
2503 * Now, check the answer
2505 if (!getanswer (buffer
))
2510 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2514 ruserpass (e
->eb_site
, &username
, &password
);
2519 ce
->ce_unlink
= (*file
== NULL
);
2521 cachefile
[0] = '\0';
2522 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2523 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2524 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2525 if (*file
== NULL
) {
2532 ce
->ce_file
= add (*file
, NULL
);
2534 ce
->ce_file
= add (cachefile
, NULL
);
2537 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2538 adios(NULL
, "unable to create temporary file in %s",
2541 ce
->ce_file
= add (tempfile
, NULL
);
2544 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2545 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2550 int child_id
, i
, vecp
;
2554 vec
[vecp
++] = r1bindex (ftp
, '/');
2555 vec
[vecp
++] = e
->eb_site
;
2558 vec
[vecp
++] = e
->eb_dir
;
2559 vec
[vecp
++] = e
->eb_name
;
2560 vec
[vecp
++] = ce
->ce_file
,
2561 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2562 ? "ascii" : "binary";
2567 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2571 adios ("fork", "unable to");
2575 close (fileno (ce
->ce_fp
));
2577 fprintf (stderr
, "unable to exec ");
2583 if (pidXwait (child_id
, NULL
)) {
2584 username
= password
= NULL
;
2594 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2599 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2600 if ((fp
= fopen (cachefile
, "w"))) {
2602 FILE *gp
= ce
->ce_fp
;
2604 fseek (gp
, 0L, SEEK_SET
);
2606 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2608 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2612 admonish (ce
->ce_file
, "error reading");
2613 (void) m_unlink (cachefile
);
2617 admonish (cachefile
, "error writing");
2618 (void) m_unlink (cachefile
);
2626 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2627 *file
= ce
->ce_file
;
2628 return fileno (ce
->ce_fp
);
2639 return init_encoding (ct
, openMail
);
2644 openMail (CT ct
, char **file
)
2646 int child_id
, fd
, i
, vecp
;
2648 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2649 struct exbody
*e
= ct
->c_ctexbody
;
2650 CE ce
= &ct
->c_cefile
;
2652 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2663 if (!e
->eb_server
) {
2664 content_error (NULL
, ct
, "missing server parameter");
2671 pidcheck (pidwait (xpid
, NOTOK
));
2675 /* Get buffer ready to go */
2677 buflen
= sizeof(buffer
);
2679 /* Now, construct query message */
2680 snprintf (bp
, buflen
, "Retrieve content");
2686 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2692 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2694 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2696 /* Now, check answer */
2697 if (!getanswer (buffer
))
2701 vec
[vecp
++] = r1bindex (mailproc
, '/');
2702 vec
[vecp
++] = e
->eb_server
;
2703 vec
[vecp
++] = "-subject";
2704 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2705 vec
[vecp
++] = "-body";
2706 vec
[vecp
++] = e
->eb_body
;
2709 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2713 advise ("fork", "unable to");
2717 execvp (mailproc
, vec
);
2718 fprintf (stderr
, "unable to exec ");
2724 if (pidXwait (child_id
, NULL
) == OK
)
2725 advise (NULL
, "request sent");
2729 if (*file
== NULL
) {
2731 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2732 adios(NULL
, "unable to create temporary file in %s",
2735 ce
->ce_file
= add (tempfile
, NULL
);
2738 ce
->ce_file
= add (*file
, NULL
);
2742 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2743 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2747 /* showproc is for mhshow and mhstore, though mhlist -debug
2748 * prints it, too. */
2750 free (ct
->c_showproc
);
2751 ct
->c_showproc
= add ("true", NULL
);
2753 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2754 *file
= ce
->ce_file
;
2755 return fileno (ce
->ce_fp
);
2766 return init_encoding (ct
, openURL
);
2771 openURL (CT ct
, char **file
)
2773 struct exbody
*e
= ct
->c_ctexbody
;
2774 CE ce
= &ct
->c_cefile
;
2775 char *urlprog
, *program
;
2776 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2777 int fd
, caching
, cachetype
;
2778 struct msgs_array args
= { 0, 0, NULL
};
2781 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2785 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2789 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2801 content_error(NULL
, ct
, "missing url parameter");
2808 pidcheck (pidwait (xpid
, NOTOK
));
2812 ce
->ce_unlink
= (*file
== NULL
);
2814 cachefile
[0] = '\0';
2816 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2817 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2818 if (*file
== NULL
) {
2825 ce
->ce_file
= add(*file
, NULL
);
2827 ce
->ce_file
= add(cachefile
, NULL
);
2830 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2831 adios(NULL
, "unable to create temporary file in %s",
2834 ce
->ce_file
= add (tempfile
, NULL
);
2837 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2838 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2842 switch (child_id
= fork()) {
2844 adios ("fork", "unable to");
2848 argsplit_msgarg(&args
, urlprog
, &program
);
2849 app_msgarg(&args
, e
->eb_url
);
2850 app_msgarg(&args
, NULL
);
2851 dup2(fileno(ce
->ce_fp
), 1);
2852 close(fileno(ce
->ce_fp
));
2853 execvp(program
, args
.msgs
);
2854 fprintf(stderr
, "Unable to exec ");
2860 if (pidXwait(child_id
, NULL
)) {
2868 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2873 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2874 if ((fp
= fopen(cachefile
, "w"))) {
2876 FILE *gp
= ce
->ce_fp
;
2878 fseeko(gp
, 0, SEEK_SET
);
2880 while ((cc
= fread(buffer
, sizeof(*buffer
),
2881 sizeof(buffer
), gp
)) > 0)
2882 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2887 admonish(ce
->ce_file
, "error reading");
2888 (void) m_unlink (cachefile
);
2895 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2896 *file
= ce
->ce_file
;
2901 readDigest (CT ct
, char *cp
)
2906 unsigned char *dp
, value
, *ep
;
2912 for (ep
= (dp
= ct
->c_digest
)
2913 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2918 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2920 fprintf (stderr
, "invalid BASE64 encoding\n");
2924 bits
|= value
<< bitno
;
2926 if ((bitno
-= 6) < 0) {
2927 if (dp
+ (3 - skip
) > ep
)
2928 goto invalid_digest
;
2929 *dp
++ = (bits
>> 16) & 0xff;
2931 *dp
++ = (bits
>> 8) & 0xff;
2933 *dp
++ = bits
& 0xff;
2943 goto self_delimiting
;
2948 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2958 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2966 fprintf (stderr
, "MD5 digest=");
2967 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2968 fprintf (stderr
, "%02x", *dp
& 0xff);
2969 fprintf (stderr
, "\n");
2976 /* Multipart parts might have content before the first subpart and/or
2977 after the last subpart that hasn't been stored anywhere else, so do
2980 get_leftover_mp_content (CT ct
, int before
/* or after */) {
2981 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2983 int found_boundary
= 0;
2984 char buffer
[BUFSIZ
];
2987 char *content
= NULL
;
2989 if (! m
) return NOTOK
;
2992 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2994 /* Isolate the beginning of this part to the beginning of the
2995 first subpart and save any content between them. */
2996 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2997 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2998 boundary
= concat ("--", m
->mp_start
, NULL
);
3000 struct part
*last_subpart
= NULL
;
3001 struct part
*subpart
;
3003 /* Go to the last subpart to get its end position. */
3004 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3005 last_subpart
= subpart
;
3008 if (last_subpart
== NULL
) return NOTOK
;
3010 /* Isolate the end of the last subpart to the end of this part
3011 and save any content between them. */
3012 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3013 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3014 boundary
= concat ("--", m
->mp_stop
, NULL
);
3017 /* Back up by 1 to pick up the newline. */
3018 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
3019 read
+= strlen (buffer
);
3020 /* Don't look beyond beginning of first subpart (before) or
3021 next part (after). */
3022 if (read
> max
) buffer
[read
-max
] = '\0';
3025 if (! strcmp (buffer
, boundary
)) {
3029 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
3035 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3037 char *old_content
= content
;
3038 content
= concat (content
, buffer
, NULL
);
3042 ? concat ("\n", buffer
, NULL
)
3043 : concat (buffer
, NULL
);
3048 if (found_boundary
|| read
> max
) break;
3050 if (read
> max
) break;
3054 /* Skip the newline if that's all there is. */
3058 /* Remove trailing newline, except at EOF. */
3059 if ((before
|| ! feof (ct
->c_fp
)) &&
3060 (cp
= content
+ strlen (content
)) > content
&&
3065 if (strlen (content
) > 1) {
3067 m
->mp_content_before
= content
;
3069 m
->mp_content_after
= content
;
3083 ct_type_str (int type
) {
3085 case CT_APPLICATION
:
3086 return "application";
3102 return "unknown_type";
3108 ct_subtype_str (int type
, int subtype
) {
3110 case CT_APPLICATION
:
3112 case APPLICATION_OCTETS
:
3114 case APPLICATION_POSTSCRIPT
:
3115 return "postscript";
3117 return "unknown_app_subtype";
3121 case MESSAGE_RFC822
:
3123 case MESSAGE_PARTIAL
:
3125 case MESSAGE_EXTERNAL
:
3128 return "unknown_msg_subtype";
3134 case MULTI_ALTERNATE
:
3135 return "alternative";
3138 case MULTI_PARALLEL
:
3141 return "unknown_multipart_subtype";
3152 return "unknown_text_subtype";
3155 return "unknown_type";
3160 /* Find the content type and InitFunc for the CT. */
3161 const struct str2init
*
3162 get_ct_init (int type
) {
3163 const struct str2init
*sp
;
3165 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3166 if (type
== sp
->si_val
) {
3175 ce_str (int encoding
) {
3180 return "quoted-printable";
3196 /* Find the content type and InitFunc for the content encoding method. */
3197 const struct str2init
*
3198 get_ce_method (const char *method
) {
3199 struct str2init
*sp
;
3201 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3202 if (! strcasecmp (method
, sp
->si_key
)) {
3211 parse_header_attrs (const char *filename
, int len
, char **header_attrp
, CI ci
,
3213 char **attr
= ci
->ci_attrs
;
3214 char *cp
= *header_attrp
;
3216 while (*cp
== ';') {
3217 char *dp
, *vp
, *up
, c
;
3219 /* Relies on knowledge of this declaration:
3220 * char *ci_attrs[NPARMS + 2];
3222 if (attr
>= ci
->ci_attrs
+ sizeof ci
->ci_attrs
/sizeof (char *) - 2) {
3224 "too many parameters in message %s's %s: field (%d max)",
3225 filename
, TYPE_FIELD
, NPARMS
);
3231 while (isspace ((unsigned char) *cp
))
3235 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3242 "extraneous trailing ';' in message %s's %s: "
3244 filename
, TYPE_FIELD
);
3249 /* down case the attribute name */
3250 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3251 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3252 *dp
= tolower ((unsigned char) *dp
);
3254 for (up
= dp
; isspace ((unsigned char) *dp
);)
3256 if (dp
== cp
|| *dp
!= '=') {
3258 "invalid parameter in message %s's %s: "
3259 "field\n%*.*sparameter %s (error detected at offset %d)",
3260 filename
, TYPE_FIELD
, len
, len
, "", cp
, dp
- cp
);
3265 vp
= (*attr
= add (cp
, NULL
)) + (up
- cp
);
3267 for (dp
++; isspace ((unsigned char) *dp
);)
3270 /* Now store the attribute value. */
3271 ci
->ci_values
[attr
- ci
->ci_attrs
] = vp
= *attr
+ (dp
- cp
);
3274 for (cp
= ++dp
, dp
= vp
;;) {
3275 switch (c
= *cp
++) {
3279 "invalid quoted-string in message %s's %s: "
3280 "field\n%*.*s(parameter %s)",
3281 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3287 if ((c
= *cp
++) == '\0')
3302 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
3308 "invalid parameter in message %s's %s: "
3309 "field\n%*.*s(parameter %s)",
3310 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3315 while (isspace ((unsigned char) *cp
))
3319 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {