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>
19 #include <h/mhparse.h>
25 extern pid_t xpid
; /* mhshowsbr.c */
28 extern int rcachesw
; /* mhcachesbr.c */
29 extern int wcachesw
; /* mhcachesbr.c */
31 int checksw
= 0; /* check Content-MD5 field */
34 * Directory to place temp files. This must
35 * be set before these routines are called.
40 * These are for mhfixmsg to:
41 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
43 * 2) Suppress the warning about bogus multipart content, and report it.
45 int skip_mp_cte_check
;
46 int suppress_bogus_mp_content_warning
;
50 * Structures for TEXT messages
52 struct k2v SubText
[] = {
53 { "plain", TEXT_PLAIN
},
54 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
55 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
56 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
59 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
62 * Structures for MULTIPART messages
64 struct k2v SubMultiPart
[] = {
65 { "mixed", MULTI_MIXED
},
66 { "alternative", MULTI_ALTERNATE
},
67 { "digest", MULTI_DIGEST
},
68 { "parallel", MULTI_PARALLEL
},
69 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
73 * Structures for MESSAGE messages
75 struct k2v SubMessage
[] = {
76 { "rfc822", MESSAGE_RFC822
},
77 { "partial", MESSAGE_PARTIAL
},
78 { "external-body", MESSAGE_EXTERNAL
},
79 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
83 * Structure for APPLICATION messages
85 struct k2v SubApplication
[] = {
86 { "octet-stream", APPLICATION_OCTETS
},
87 { "postscript", APPLICATION_POSTSCRIPT
},
88 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
93 int find_cache (CT
, int, int *, char *, char *, int);
96 int part_ok (CT
, int);
97 int type_ok (CT
, int);
98 void content_error (char *, CT
, char *, ...);
101 void free_encoding (CT
, int);
106 static CT
get_content (FILE *, char *, int);
107 static int get_comment (const char *, CI
, char **, int);
109 static int InitGeneric (CT
);
110 static int InitText (CT
);
111 static int InitMultiPart (CT
);
112 void reverse_parts (CT
);
113 static int InitMessage (CT
);
114 static int InitApplication (CT
);
115 static int init_encoding (CT
, OpenCEFunc
);
116 static unsigned long size_encoding (CT
);
117 static int InitBase64 (CT
);
118 static int openBase64 (CT
, char **);
119 static int InitQuoted (CT
);
120 static int openQuoted (CT
, char **);
121 static int Init7Bit (CT
);
122 static int openExternal (CT
, CT
, CE
, char **, int *);
123 static int InitFile (CT
);
124 static int openFile (CT
, char **);
125 static int InitFTP (CT
);
126 static int openFTP (CT
, char **);
127 static int InitMail (CT
);
128 static int openMail (CT
, char **);
129 static int readDigest (CT
, char *);
130 static int get_leftover_mp_content (CT
, int);
131 static int InitURL (CT
);
132 static int openURL (CT
, char **);
134 struct str2init str2cts
[] = {
135 { "application", CT_APPLICATION
, InitApplication
},
136 { "audio", CT_AUDIO
, InitGeneric
},
137 { "image", CT_IMAGE
, InitGeneric
},
138 { "message", CT_MESSAGE
, InitMessage
},
139 { "multipart", CT_MULTIPART
, InitMultiPart
},
140 { "text", CT_TEXT
, InitText
},
141 { "video", CT_VIDEO
, InitGeneric
},
142 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
143 { NULL
, CT_UNKNOWN
, NULL
},
146 struct str2init str2ces
[] = {
147 { "base64", CE_BASE64
, InitBase64
},
148 { "quoted-printable", CE_QUOTED
, InitQuoted
},
149 { "8bit", CE_8BIT
, Init7Bit
},
150 { "7bit", CE_7BIT
, Init7Bit
},
151 { "binary", CE_BINARY
, Init7Bit
},
152 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
153 { NULL
, CE_UNKNOWN
, NULL
},
157 * NOTE WELL: si_key MUST NOT have value of NOTOK
159 * si_key is 1 if access method is anonymous.
161 struct str2init str2methods
[] = {
162 { "afs", 1, InitFile
},
163 { "anon-ftp", 1, InitFTP
},
164 { "ftp", 0, InitFTP
},
165 { "local-file", 0, InitFile
},
166 { "mail-server", 0, InitMail
},
167 { "url", 0, InitURL
},
173 pidcheck (int status
)
175 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
186 * Main entry point for parsing a MIME message or file.
187 * It returns the Content structure for the top level
188 * entity in the file.
192 parse_mime (char *file
)
200 * Check if file is actually standard input
202 if ((is_stdin
= !(strcmp (file
, "-")))) {
203 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
205 advise("mhparse", "unable to create temporary file");
208 file
= add (tfile
, NULL
);
211 while (fgets (buffer
, sizeof(buffer
), stdin
))
215 if (ferror (stdin
)) {
217 advise ("stdin", "error reading");
222 advise (file
, "error writing");
225 fseek (fp
, 0L, SEEK_SET
);
226 } else if ((fp
= fopen (file
, "r")) == NULL
) {
227 advise (file
, "unable to read");
231 if (!(ct
= get_content (fp
, file
, 1))) {
234 advise (NULL
, "unable to decode %s", file
);
239 ct
->c_unlink
= 1; /* temp file to remove */
243 if (ct
->c_end
== 0L) {
244 fseek (fp
, 0L, SEEK_END
);
245 ct
->c_end
= ftell (fp
);
248 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
260 * Main routine for reading/parsing the headers
261 * of a message content.
263 * toplevel = 1 # we are at the top level of the message
264 * toplevel = 0 # we are inside message type or multipart type
265 * # other than multipart/digest
266 * toplevel = -1 # we are inside multipart/digest
267 * NB: on failure we will fclose(in)!
271 get_content (FILE *in
, char *file
, int toplevel
)
274 char buf
[BUFSIZ
], name
[NAMESZ
];
278 m_getfld_state_t gstate
= 0;
280 /* allocate the content structure */
281 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
282 adios (NULL
, "out of memory");
285 ct
->c_file
= add (file
, NULL
);
286 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
289 * Parse the header fields for this
290 * content into a linked list.
292 m_getfld_track_filepos (&gstate
, in
);
293 for (compnum
= 1;;) {
294 int bufsz
= sizeof buf
;
295 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
300 /* get copies of the buffers */
301 np
= add (name
, NULL
);
302 vp
= add (buf
, NULL
);
304 /* if necessary, get rest of field */
305 while (state
== FLDPLUS
) {
307 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
308 vp
= add (buf
, vp
); /* add to previous value */
311 /* Now add the header data to the list */
312 add_header (ct
, np
, vp
);
314 /* continue, to see if this isn't the last header field */
315 ct
->c_begin
= ftell (in
) + 1;
319 ct
->c_begin
= ftell (in
) - strlen (buf
);
323 ct
->c_begin
= ftell (in
);
328 adios (NULL
, "message format error in component #%d", compnum
);
331 adios (NULL
, "getfld() returned %d", state
);
334 /* break out of the loop */
337 m_getfld_state_destroy (&gstate
);
340 * Read the content headers. We will parse the
341 * MIME related header fields into their various
342 * structures and set internal flags related to
343 * content type/subtype, etc.
346 hp
= ct
->c_first_hf
; /* start at first header field */
348 /* Get MIME-Version field */
349 if (!mh_strcasecmp (hp
->name
, VRSN_FIELD
)) {
354 advise (NULL
, "message %s has multiple %s: fields",
355 ct
->c_file
, VRSN_FIELD
);
358 ct
->c_vrsn
= add (hp
->value
, NULL
);
360 /* Now, cleanup this field */
363 while (isspace ((unsigned char) *cp
))
365 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
367 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
368 if (!isspace ((unsigned char) *dp
))
372 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
375 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
)
378 for (dp
= cp
; istoken (*dp
); dp
++)
382 ucmp
= !mh_strcasecmp (cp
, VRSN_VALUE
);
385 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
386 ct
->c_file
, VRSN_FIELD
, cp
);
389 else if (!mh_strcasecmp (hp
->name
, TYPE_FIELD
)) {
390 /* Get Content-Type field */
391 struct str2init
*s2i
;
392 CI ci
= &ct
->c_ctinfo
;
394 /* Check if we've already seen a Content-Type header */
396 advise (NULL
, "message %s has multiple %s: fields",
397 ct
->c_file
, TYPE_FIELD
);
401 /* Parse the Content-Type field */
402 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
406 * Set the Init function and the internal
407 * flag for this content type.
409 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
410 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
412 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
414 ct
->c_type
= s2i
->si_val
;
415 ct
->c_ctinitfnx
= s2i
->si_init
;
417 else if (!mh_strcasecmp (hp
->name
, ENCODING_FIELD
)) {
418 /* Get Content-Transfer-Encoding field */
420 struct str2init
*s2i
;
423 * Check if we've already seen the
424 * Content-Transfer-Encoding field
427 advise (NULL
, "message %s has multiple %s: fields",
428 ct
->c_file
, ENCODING_FIELD
);
432 /* get copy of this field */
433 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
435 while (isspace ((unsigned char) *cp
))
437 for (dp
= cp
; istoken (*dp
); dp
++)
443 * Find the internal flag and Init function
444 * for this transfer encoding.
446 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
447 if (!mh_strcasecmp (cp
, s2i
->si_key
))
449 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
452 ct
->c_encoding
= s2i
->si_val
;
454 /* Call the Init function for this encoding */
455 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
458 else if (!mh_strcasecmp (hp
->name
, MD5_FIELD
)) {
459 /* Get Content-MD5 field */
465 if (ct
->c_digested
) {
466 advise (NULL
, "message %s has multiple %s: fields",
467 ct
->c_file
, MD5_FIELD
);
471 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
473 while (isspace ((unsigned char) *cp
))
475 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
477 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
478 if (!isspace ((unsigned char) *dp
))
482 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
485 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
) {
490 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
498 else if (!mh_strcasecmp (hp
->name
, ID_FIELD
)) {
499 /* Get Content-ID field */
500 ct
->c_id
= add (hp
->value
, ct
->c_id
);
502 else if (!mh_strcasecmp (hp
->name
, DESCR_FIELD
)) {
503 /* Get Content-Description field */
504 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
506 else if (!mh_strcasecmp (hp
->name
, DISPO_FIELD
)) {
507 /* Get Content-Disposition field */
508 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
512 hp
= hp
->next
; /* next header field */
516 * Check if we saw a Content-Type field.
517 * If not, then assign a default value for
518 * it, and the Init function.
522 * If we are inside a multipart/digest message,
523 * so default type is message/rfc822
526 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
528 ct
->c_type
= CT_MESSAGE
;
529 ct
->c_ctinitfnx
= InitMessage
;
532 * Else default type is text/plain
534 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
536 ct
->c_type
= CT_TEXT
;
537 ct
->c_ctinitfnx
= InitText
;
541 /* Use default Transfer-Encoding, if necessary */
543 ct
->c_encoding
= CE_7BIT
;
556 * small routine to add header field to list
560 add_header (CT ct
, char *name
, char *value
)
564 /* allocate header field structure */
565 hp
= mh_xmalloc (sizeof(*hp
));
567 /* link data into header structure */
572 /* link header structure into the list */
573 if (ct
->c_first_hf
== NULL
) {
574 ct
->c_first_hf
= hp
; /* this is the first */
577 ct
->c_last_hf
->next
= hp
; /* add it to the end */
585 /* Make sure that buf contains at least one appearance of name,
586 followed by =. If not, insert both name and value, just after
587 first semicolon, if any. Note that name should not contain a
588 trailing =. And quotes will be added around the value. Typical
589 usage: make sure that a Content-Disposition header contains
590 filename="foo". If it doesn't and value does, use value from
593 incl_name_value (char *buf
, char *name
, char *value
) {
596 /* Assume that name is non-null. */
598 char *name_plus_equal
= concat (name
, "=", NULL
);
600 if (! strstr (buf
, name_plus_equal
)) {
602 char *cp
, *prefix
, *suffix
;
604 /* Trim trailing space, esp. newline. */
605 for (cp
= &buf
[strlen (buf
) - 1];
606 cp
>= buf
&& isspace ((unsigned char) *cp
);
611 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
613 /* Insert at first semicolon, if any. If none, append to
615 prefix
= add (buf
, NULL
);
616 if ((cp
= strchr (prefix
, ';'))) {
617 suffix
= concat (cp
, NULL
);
619 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
623 newbuf
= concat (buf
, insertion
, "\n", NULL
);
631 free (name_plus_equal
);
637 /* Extract just name_suffix="foo", if any, from value. If there isn't
638 one, return the entire value. Note that, for example, a name_suffix
639 of name will match filename="foo", and return foo. */
641 extract_name_value (char *name_suffix
, char *value
) {
642 char *extracted_name_value
= value
;
643 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
644 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
647 free (name_suffix_plus_quote
);
648 if (name_suffix_equals
) {
649 char *name_suffix_begin
;
652 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
653 name_suffix_begin
= ++cp
;
654 /* Find second \". */
655 for (; *cp
!= '"'; ++cp
) /* empty */;
657 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
658 memcpy (extracted_name_value
,
660 cp
- name_suffix_begin
);
661 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
664 return extracted_name_value
;
668 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
669 * directives. Fills in the information of the CTinfo structure.
672 get_ctinfo (char *cp
, CT ct
, int magic
)
681 i
= strlen (invo_name
) + 2;
683 /* store copy of Content-Type line */
684 cp
= ct
->c_ctline
= add (cp
, NULL
);
686 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
689 /* change newlines to spaces */
690 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
693 /* trim trailing spaces */
694 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
695 if (!isspace ((unsigned char) *dp
))
700 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
702 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
705 for (dp
= cp
; istoken (*dp
); dp
++)
708 ci
->ci_type
= add (cp
, NULL
); /* store content type */
712 advise (NULL
, "invalid %s: field in message %s (empty type)",
713 TYPE_FIELD
, ct
->c_file
);
717 /* down case the content type string */
718 for (dp
= ci
->ci_type
; *dp
; dp
++)
719 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
720 *dp
= tolower ((unsigned char) *dp
);
722 while (isspace ((unsigned char) *cp
))
725 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
730 ci
->ci_subtype
= add ("", NULL
);
735 while (isspace ((unsigned char) *cp
))
738 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
741 for (dp
= cp
; istoken (*dp
); dp
++)
744 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
747 if (!*ci
->ci_subtype
) {
749 "invalid %s: field in message %s (empty subtype for \"%s\")",
750 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
754 /* down case the content subtype string */
755 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
756 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
757 *dp
= tolower ((unsigned char) *dp
);
760 while (isspace ((unsigned char) *cp
))
763 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
766 if (parse_header_attrs (ct
->c_file
, i
, &cp
, ci
, &status
) == NOTOK
) {
771 * Get any <Content-Id> given in buffer
773 if (magic
&& *cp
== '<') {
778 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
779 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
785 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
791 while (isspace ((unsigned char) *cp
))
796 * Get any [Content-Description] given in buffer.
798 if (magic
&& *cp
== '[') {
800 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
804 advise (NULL
, "invalid description in message %s", ct
->c_file
);
812 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
818 while (isspace ((unsigned char) *cp
))
823 * Get any {Content-Disposition} given in buffer.
825 if (magic
&& *cp
== '{') {
827 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
831 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
839 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
845 while (isspace ((unsigned char) *cp
))
850 * Check if anything is left over
854 ci
->ci_magic
= add (cp
, NULL
);
856 /* If there is a Content-Disposition header and it doesn't
857 have a *filename=, extract it from the magic contents.
858 The r1bindex call skips any leading directory
862 incl_name_value (ct
->c_dispo
,
864 r1bindex (extract_name_value ("name",
871 "extraneous information in message %s's %s: field\n%*.*s(%s)",
872 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
880 get_comment (const char *filename
, CI ci
, char **ap
, int istype
)
884 char c
, buffer
[BUFSIZ
], *dp
;
894 advise (NULL
, "invalid comment in message %s's %s: field",
895 filename
, istype
? TYPE_FIELD
: VRSN_FIELD
);
900 if ((c
= *cp
++) == '\0')
923 if ((dp
= ci
->ci_comment
)) {
924 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
927 ci
->ci_comment
= add (buffer
, NULL
);
931 while (isspace ((unsigned char) *cp
))
942 * Handles content types audio, image, and video.
943 * There's not much to do right here.
951 return OK
; /* not much to do here */
964 char **ap
, **ep
, *cp
;
967 CI ci
= &ct
->c_ctinfo
;
969 /* check for missing subtype */
970 if (!*ci
->ci_subtype
)
971 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
974 for (kv
= SubText
; kv
->kv_key
; kv
++)
975 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
977 ct
->c_subtype
= kv
->kv_value
;
979 /* allocate text character set structure */
980 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
981 adios (NULL
, "out of memory");
982 ct
->c_ctparams
= (void *) t
;
984 /* scan for charset parameter */
985 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
986 if (!mh_strcasecmp (*ap
, "charset"))
989 /* check if content specified a character set */
992 t
->tx_charset
= CHARSET_SPECIFIED
;
994 t
->tx_charset
= CHARSET_UNSPECIFIED
;
998 * If we can not handle character set natively,
999 * then check profile for string to modify the
1000 * terminal or display method.
1002 * termproc is for mhshow, though mhlist -debug prints it, too.
1004 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1005 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1006 if ((cp
= context_find (buffer
)))
1007 ct
->c_termproc
= getcpy (cp
);
1019 InitMultiPart (CT ct
)
1023 char *cp
, *dp
, **ap
, **ep
;
1024 char *bp
, buffer
[BUFSIZ
];
1025 struct multipart
*m
;
1027 struct part
*part
, **next
;
1028 CI ci
= &ct
->c_ctinfo
;
1033 * The encoding for multipart messages must be either
1034 * 7bit, 8bit, or binary (per RFC2045).
1036 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1037 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1038 /* Copy the Content-Transfer-Encoding header field body so we can
1039 remove any trailing whitespace and leading blanks from it. */
1040 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1042 bp
= cte
+ strlen (cte
) - 1;
1043 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1044 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1047 "\"%s/%s\" type in message %s must be encoded in\n"
1048 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1049 "is to\nmanually edit the file and change the \"%s\"\n"
1050 "Content-Transfer-Encoding to one of those. For now",
1051 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1058 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1059 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1061 ct
->c_subtype
= kv
->kv_value
;
1064 * Check for "boundary" parameter, which is
1065 * required for multipart messages.
1068 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1069 if (!mh_strcasecmp (*ap
, "boundary")) {
1075 /* complain if boundary parameter is missing */
1078 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1079 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1083 /* allocate primary structure for multipart info */
1084 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1085 adios (NULL
, "out of memory");
1086 ct
->c_ctparams
= (void *) m
;
1088 /* check if boundary parameter contains only whitespace characters */
1089 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1092 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1093 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1097 /* remove trailing whitespace from boundary parameter */
1098 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1099 if (!isspace ((unsigned char) *dp
))
1103 /* record boundary separators */
1104 m
->mp_start
= concat (bp
, "\n", NULL
);
1105 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1107 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1108 advise (ct
->c_file
, "unable to open for reading");
1112 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1114 next
= &m
->mp_parts
;
1118 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1122 pos
+= strlen (buffer
);
1123 if (buffer
[0] != '-' || buffer
[1] != '-')
1126 if (strcmp (buffer
+ 2, m
->mp_start
))
1129 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1130 adios (NULL
, "out of memory");
1132 next
= &part
->mp_next
;
1134 if (!(p
= get_content (fp
, ct
->c_file
,
1135 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1142 fseek (fp
, pos
, SEEK_SET
);
1145 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1149 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1150 if (p
->c_end
< p
->c_begin
)
1151 p
->c_begin
= p
->c_end
;
1156 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1162 if (! suppress_bogus_mp_content_warning
) {
1163 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1165 bogus_mp_content
= 1;
1167 if (!inout
&& part
) {
1169 p
->c_end
= ct
->c_end
;
1171 if (p
->c_begin
>= p
->c_end
) {
1172 for (next
= &m
->mp_parts
; *next
!= part
;
1173 next
= &((*next
)->mp_next
))
1177 free ((char *) part
);
1182 /* reverse the order of the parts for multipart/alternative */
1183 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1187 * label all subparts with part number, and
1188 * then initialize the content of the subpart.
1193 char partnam
[BUFSIZ
];
1196 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1197 pp
= partnam
+ strlen (partnam
);
1202 for (part
= m
->mp_parts
, partnum
= 1; part
;
1203 part
= part
->mp_next
, partnum
++) {
1206 sprintf (pp
, "%d", partnum
);
1207 p
->c_partno
= add (partnam
, NULL
);
1209 /* initialize the content of the subparts */
1210 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1218 get_leftover_mp_content (ct
, 1);
1219 get_leftover_mp_content (ct
, 0);
1228 * reverse the order of the parts of a multipart/alternative
1232 reverse_parts (CT ct
)
1234 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1238 /* Reverse the order of its parts by walking the mp_parts list
1239 and pushing each node to the front. */
1240 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1241 next
= part
->mp_next
;
1242 part
->mp_next
= m
->mp_parts
;
1256 CI ci
= &ct
->c_ctinfo
;
1258 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1260 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1261 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1265 /* check for missing subtype */
1266 if (!*ci
->ci_subtype
)
1267 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1270 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1271 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1273 ct
->c_subtype
= kv
->kv_value
;
1275 switch (ct
->c_subtype
) {
1276 case MESSAGE_RFC822
:
1279 case MESSAGE_PARTIAL
:
1284 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1285 adios (NULL
, "out of memory");
1286 ct
->c_ctparams
= (void *) p
;
1288 /* scan for parameters "id", "number", and "total" */
1289 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1290 if (!mh_strcasecmp (*ap
, "id")) {
1291 p
->pm_partid
= add (*ep
, NULL
);
1294 if (!mh_strcasecmp (*ap
, "number")) {
1295 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1296 || p
->pm_partno
< 1) {
1299 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1300 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1301 ct
->c_file
, TYPE_FIELD
);
1306 if (!mh_strcasecmp (*ap
, "total")) {
1307 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1316 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1318 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1319 ci
->ci_type
, ci
->ci_subtype
,
1320 ct
->c_file
, TYPE_FIELD
);
1326 case MESSAGE_EXTERNAL
:
1333 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1334 adios (NULL
, "out of memory");
1335 ct
->c_ctparams
= (void *) e
;
1338 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1339 advise (ct
->c_file
, "unable to open for reading");
1343 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1345 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1353 p
->c_ceopenfnx
= NULL
;
1354 if ((exresult
= params_external (ct
, 0)) != NOTOK
1355 && p
->c_ceopenfnx
== openMail
) {
1359 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1361 content_error (NULL
, ct
,
1362 "empty body for access-type=mail-server");
1366 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1367 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1369 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1371 adios ("failed", "fread");
1374 adios (NULL
, "unexpected EOF from fread");
1377 bp
+= cc
, size
-= cc
;
1384 p
->c_end
= p
->c_begin
;
1389 if (exresult
== NOTOK
)
1391 if (e
->eb_flags
== NOTOK
)
1394 switch (p
->c_type
) {
1399 if (p
->c_subtype
!= MESSAGE_RFC822
)
1403 e
->eb_partno
= ct
->c_partno
;
1405 (*p
->c_ctinitfnx
) (p
);
1420 params_external (CT ct
, int composing
)
1423 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1424 CI ci
= &ct
->c_ctinfo
;
1426 ct
->c_ceopenfnx
= NULL
;
1427 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1428 if (!mh_strcasecmp (*ap
, "access-type")) {
1429 struct str2init
*s2i
;
1430 CT p
= e
->eb_content
;
1432 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1433 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1437 e
->eb_flags
= NOTOK
;
1438 p
->c_encoding
= CE_EXTERNAL
;
1441 e
->eb_access
= s2i
->si_key
;
1442 e
->eb_flags
= s2i
->si_val
;
1443 p
->c_encoding
= CE_EXTERNAL
;
1445 /* Call the Init function for this external type */
1446 if ((*s2i
->si_init
)(p
) == NOTOK
)
1450 if (!mh_strcasecmp (*ap
, "name")) {
1454 if (!mh_strcasecmp (*ap
, "permission")) {
1455 e
->eb_permission
= *ep
;
1458 if (!mh_strcasecmp (*ap
, "site")) {
1462 if (!mh_strcasecmp (*ap
, "directory")) {
1466 if (!mh_strcasecmp (*ap
, "mode")) {
1470 if (!mh_strcasecmp (*ap
, "size")) {
1471 sscanf (*ep
, "%lu", &e
->eb_size
);
1474 if (!mh_strcasecmp (*ap
, "server")) {
1478 if (!mh_strcasecmp (*ap
, "subject")) {
1479 e
->eb_subject
= *ep
;
1482 if (!mh_strcasecmp (*ap
, "url")) {
1484 * According to RFC 2017, we have to remove all whitespace from
1489 e
->eb_url
= u
= mh_xmalloc(strlen(*ep
) + 1);
1491 for (; *p
!= '\0'; p
++) {
1492 if (! isspace((unsigned char) *p
))
1499 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1500 e
->eb_body
= getcpy (*ep
);
1505 if (!e
->eb_access
) {
1507 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1508 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1521 InitApplication (CT ct
)
1524 CI ci
= &ct
->c_ctinfo
;
1527 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1528 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1530 ct
->c_subtype
= kv
->kv_value
;
1537 * TRANSFER ENCODINGS
1541 init_encoding (CT ct
, OpenCEFunc openfnx
)
1543 ct
->c_ceopenfnx
= openfnx
;
1544 ct
->c_ceclosefnx
= close_encoding
;
1545 ct
->c_cesizefnx
= size_encoding
;
1552 close_encoding (CT ct
)
1554 CE ce
= &ct
->c_cefile
;
1563 static unsigned long
1564 size_encoding (CT ct
)
1569 CE ce
= &ct
->c_cefile
;
1572 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1573 return (long) st
.st_size
;
1576 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1577 return (long) st
.st_size
;
1582 if (ct
->c_encoding
== CE_EXTERNAL
)
1583 return (ct
->c_end
- ct
->c_begin
);
1586 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1587 return (ct
->c_end
- ct
->c_begin
);
1589 if (fstat (fd
, &st
) != NOTOK
)
1590 size
= (long) st
.st_size
;
1594 (*ct
->c_ceclosefnx
) (ct
);
1603 static unsigned char b642nib
[0x80] = {
1604 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1605 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1606 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1607 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1608 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1609 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1610 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1611 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1612 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1613 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1614 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1615 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1616 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1617 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1618 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1619 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1626 return init_encoding (ct
, openBase64
);
1631 openBase64 (CT ct
, char **file
)
1633 int bitno
, cc
, digested
;
1634 int fd
, len
, skip
, own_ct_fp
= 0;
1636 unsigned char value
, b
;
1637 char *cp
, *ep
, buffer
[BUFSIZ
];
1638 /* sbeck -- handle suffixes */
1640 CE ce
= &ct
->c_cefile
;
1644 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1649 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1650 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1656 if (*file
== NULL
) {
1657 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1660 ce
->ce_file
= add (*file
, NULL
);
1664 /* sbeck@cise.ufl.edu -- handle suffixes */
1666 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1667 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1668 cp
= context_find (buffer
);
1669 if (cp
== NULL
|| *cp
== '\0') {
1670 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1672 cp
= context_find (buffer
);
1674 if (cp
!= NULL
&& *cp
!= '\0') {
1675 if (ce
->ce_unlink
) {
1676 /* Temporary file already exists, so we rename to
1677 version with extension. */
1678 char *file_org
= strdup(ce
->ce_file
);
1679 ce
->ce_file
= add (cp
, ce
->ce_file
);
1680 if (rename(file_org
, ce
->ce_file
)) {
1681 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1686 ce
->ce_file
= add (cp
, ce
->ce_file
);
1690 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1691 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1695 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1696 adios (NULL
, "internal error(1)");
1699 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1700 content_error (ct
->c_file
, ct
, "unable to open for reading");
1706 if ((digested
= ct
->c_digested
))
1707 MD5Init (&mdContext
);
1713 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1715 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1717 content_error (ct
->c_file
, ct
, "error reading from");
1721 content_error (NULL
, ct
, "premature eof");
1729 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1732 if (isspace ((unsigned char) *cp
))
1734 if (skip
|| (((unsigned char) *cp
) & 0x80)
1735 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1737 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1738 (unsigned char) *cp
,
1739 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1742 content_error (NULL
, ct
,
1743 "invalid BASE64 encoding -- continuing");
1747 bits
|= value
<< bitno
;
1749 if ((bitno
-= 6) < 0) {
1750 b
= (bits
>> 16) & 0xff;
1751 putc ((char) b
, ce
->ce_fp
);
1753 MD5Update (&mdContext
, &b
, 1);
1755 b
= (bits
>> 8) & 0xff;
1756 putc ((char) b
, ce
->ce_fp
);
1758 MD5Update (&mdContext
, &b
, 1);
1761 putc ((char) b
, ce
->ce_fp
);
1763 MD5Update (&mdContext
, &b
, 1);
1767 if (ferror (ce
->ce_fp
)) {
1768 content_error (ce
->ce_file
, ct
,
1769 "error writing to");
1772 bitno
= 18, bits
= 0L, skip
= 0;
1778 goto self_delimiting
;
1787 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1789 content_error (NULL
, ct
, "invalid BASE64 encoding");
1794 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1796 if (fflush (ce
->ce_fp
)) {
1797 content_error (ce
->ce_file
, ct
, "error writing to");
1802 unsigned char digest
[16];
1804 MD5Final (digest
, &mdContext
);
1805 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1806 sizeof(digest
) / sizeof(digest
[0])))
1807 content_error (NULL
, ct
,
1808 "content integrity suspect (digest mismatch) -- continuing");
1811 fprintf (stderr
, "content integrity confirmed\n");
1814 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1817 *file
= ce
->ce_file
;
1822 return fileno (ce
->ce_fp
);
1829 free_encoding (ct
, 0);
1838 static char hex2nib
[0x80] = {
1839 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1840 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1841 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1842 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1843 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1844 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1845 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1846 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1847 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1848 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1849 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1850 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1851 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1852 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1853 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1854 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1861 return init_encoding (ct
, openQuoted
);
1866 openQuoted (CT ct
, char **file
)
1868 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1870 char buffer
[BUFSIZ
];
1872 CE ce
= &ct
->c_cefile
;
1873 /* sbeck -- handle suffixes */
1878 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1883 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1884 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1890 if (*file
== NULL
) {
1891 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1894 ce
->ce_file
= add (*file
, NULL
);
1898 /* sbeck@cise.ufl.edu -- handle suffixes */
1900 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1901 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1902 cp
= context_find (buffer
);
1903 if (cp
== NULL
|| *cp
== '\0') {
1904 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1906 cp
= context_find (buffer
);
1908 if (cp
!= NULL
&& *cp
!= '\0') {
1909 if (ce
->ce_unlink
) {
1910 /* Temporary file already exists, so we rename to
1911 version with extension. */
1912 char *file_org
= strdup(ce
->ce_file
);
1913 ce
->ce_file
= add (cp
, ce
->ce_file
);
1914 if (rename(file_org
, ce
->ce_file
)) {
1915 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1920 ce
->ce_file
= add (cp
, ce
->ce_file
);
1924 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1925 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1929 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1930 adios (NULL
, "internal error(2)");
1933 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1934 content_error (ct
->c_file
, ct
, "unable to open for reading");
1940 if ((digested
= ct
->c_digested
))
1941 MD5Init (&mdContext
);
1948 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1950 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
1951 content_error (NULL
, ct
, "premature eof");
1955 if ((cc
= strlen (buffer
)) > len
)
1959 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
1960 if (!isspace ((unsigned char) *ep
))
1964 for (; cp
< ep
; cp
++) {
1966 /* in an escape sequence */
1968 /* at byte 1 of an escape sequence */
1969 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
1970 /* next is byte 2 */
1973 /* at byte 2 of an escape sequence */
1975 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
1976 putc (mask
, ce
->ce_fp
);
1978 MD5Update (&mdContext
, &mask
, 1);
1979 if (ferror (ce
->ce_fp
)) {
1980 content_error (ce
->ce_file
, ct
, "error writing to");
1983 /* finished escape sequence; next may be literal or a new
1984 * escape sequence */
1987 /* on to next byte */
1991 /* not in an escape sequence */
1993 /* starting an escape sequence, or invalid '='? */
1994 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
1995 /* "=\n" soft line break, eat the \n */
1999 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2000 /* We don't have 2 bytes left, so this is an invalid
2001 * escape sequence; just show the raw bytes (below). */
2002 } else if (isxdigit ((unsigned char) cp
[1]) &&
2003 isxdigit ((unsigned char) cp
[2])) {
2004 /* Next 2 bytes are hex digits, making this a valid escape
2005 * sequence; let's decode it (above). */
2009 /* One or both of the next 2 is out of range, making this
2010 * an invalid escape sequence; just show the raw bytes
2015 /* Just show the raw byte. */
2016 putc (*cp
, ce
->ce_fp
);
2019 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2021 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2024 if (ferror (ce
->ce_fp
)) {
2025 content_error (ce
->ce_file
, ct
, "error writing to");
2031 content_error (NULL
, ct
,
2032 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2036 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2038 if (fflush (ce
->ce_fp
)) {
2039 content_error (ce
->ce_file
, ct
, "error writing to");
2044 unsigned char digest
[16];
2046 MD5Final (digest
, &mdContext
);
2047 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2048 sizeof(digest
) / sizeof(digest
[0])))
2049 content_error (NULL
, ct
,
2050 "content integrity suspect (digest mismatch) -- continuing");
2053 fprintf (stderr
, "content integrity confirmed\n");
2056 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2059 *file
= ce
->ce_file
;
2064 return fileno (ce
->ce_fp
);
2067 free_encoding (ct
, 0);
2083 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2086 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2092 open7Bit (CT ct
, char **file
)
2094 int cc
, fd
, len
, own_ct_fp
= 0;
2095 char buffer
[BUFSIZ
];
2096 /* sbeck -- handle suffixes */
2099 CE ce
= &ct
->c_cefile
;
2102 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2107 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2108 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2114 if (*file
== NULL
) {
2115 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2118 ce
->ce_file
= add (*file
, NULL
);
2122 /* sbeck@cise.ufl.edu -- handle suffixes */
2124 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2125 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2126 cp
= context_find (buffer
);
2127 if (cp
== NULL
|| *cp
== '\0') {
2128 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2130 cp
= context_find (buffer
);
2132 if (cp
!= NULL
&& *cp
!= '\0') {
2133 if (ce
->ce_unlink
) {
2134 /* Temporary file already exists, so we rename to
2135 version with extension. */
2136 char *file_org
= strdup(ce
->ce_file
);
2137 ce
->ce_file
= add (cp
, ce
->ce_file
);
2138 if (rename(file_org
, ce
->ce_file
)) {
2139 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2144 ce
->ce_file
= add (cp
, ce
->ce_file
);
2148 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2149 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2153 if (ct
->c_type
== CT_MULTIPART
) {
2155 CI ci
= &ct
->c_ctinfo
;
2158 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2159 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2160 + 1 + strlen (ci
->ci_subtype
);
2161 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2162 putc (';', ce
->ce_fp
);
2165 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2167 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2168 fputs ("\n\t", ce
->ce_fp
);
2171 putc (' ', ce
->ce_fp
);
2174 fprintf (ce
->ce_fp
, "%s", buffer
);
2178 if (ci
->ci_comment
) {
2179 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2180 fputs ("\n\t", ce
->ce_fp
);
2184 putc (' ', ce
->ce_fp
);
2187 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2190 fprintf (ce
->ce_fp
, "\n");
2192 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2194 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2196 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2197 fprintf (ce
->ce_fp
, "\n");
2200 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2201 adios (NULL
, "internal error(3)");
2204 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2205 content_error (ct
->c_file
, ct
, "unable to open for reading");
2211 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2213 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2215 content_error (ct
->c_file
, ct
, "error reading from");
2219 content_error (NULL
, ct
, "premature eof");
2227 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2228 if (ferror (ce
->ce_fp
)) {
2229 content_error (ce
->ce_file
, ct
, "error writing to");
2234 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2236 if (fflush (ce
->ce_fp
)) {
2237 content_error (ce
->ce_file
, ct
, "error writing to");
2241 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2244 *file
= ce
->ce_file
;
2249 return fileno (ce
->ce_fp
);
2252 free_encoding (ct
, 0);
2266 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2268 char cachefile
[BUFSIZ
];
2271 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2276 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2277 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2283 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2284 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2285 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2286 ce
->ce_file
= getcpy (cachefile
);
2290 admonish (cachefile
, "unable to fopen for reading");
2297 *file
= ce
->ce_file
;
2298 *fd
= fileno (ce
->ce_fp
);
2309 return init_encoding (ct
, openFile
);
2314 openFile (CT ct
, char **file
)
2317 char cachefile
[BUFSIZ
];
2318 struct exbody
*e
= ct
->c_ctexbody
;
2319 CE ce
= &ct
->c_cefile
;
2321 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2333 content_error (NULL
, ct
, "missing name parameter");
2337 ce
->ce_file
= getcpy (e
->eb_name
);
2340 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2341 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2345 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2346 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2347 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2351 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2352 if ((fp
= fopen (cachefile
, "w"))) {
2354 char buffer
[BUFSIZ
];
2355 FILE *gp
= ce
->ce_fp
;
2357 fseek (gp
, 0L, SEEK_SET
);
2359 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2361 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2365 admonish (ce
->ce_file
, "error reading");
2370 admonish (cachefile
, "error writing");
2378 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2379 *file
= ce
->ce_file
;
2380 return fileno (ce
->ce_fp
);
2390 return init_encoding (ct
, openFTP
);
2395 openFTP (CT ct
, char **file
)
2397 int cachetype
, caching
, fd
;
2399 char *bp
, *ftp
, *user
, *pass
;
2400 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2402 CE ce
= &ct
->c_cefile
;
2403 static char *username
= NULL
;
2404 static char *password
= NULL
;
2408 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2414 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2425 if (!e
->eb_name
|| !e
->eb_site
) {
2426 content_error (NULL
, ct
, "missing %s parameter",
2427 e
->eb_name
? "site": "name");
2434 pidcheck (pidwait (xpid
, NOTOK
));
2438 /* Get the buffer ready to go */
2440 buflen
= sizeof(buffer
);
2443 * Construct the query message for user
2445 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2451 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2457 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2458 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2463 if (e
->eb_size
> 0) {
2464 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2469 snprintf (bp
, buflen
, "? ");
2472 * Now, check the answer
2474 if (!getanswer (buffer
))
2479 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2483 ruserpass (e
->eb_site
, &username
, &password
);
2488 ce
->ce_unlink
= (*file
== NULL
);
2490 cachefile
[0] = '\0';
2491 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2492 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2493 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2494 if (*file
== NULL
) {
2501 ce
->ce_file
= add (*file
, NULL
);
2503 ce
->ce_file
= add (cachefile
, NULL
);
2505 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2507 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2508 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2513 int child_id
, i
, vecp
;
2517 vec
[vecp
++] = r1bindex (ftp
, '/');
2518 vec
[vecp
++] = e
->eb_site
;
2521 vec
[vecp
++] = e
->eb_dir
;
2522 vec
[vecp
++] = e
->eb_name
;
2523 vec
[vecp
++] = ce
->ce_file
,
2524 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2525 ? "ascii" : "binary";
2530 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2534 adios ("fork", "unable to");
2538 close (fileno (ce
->ce_fp
));
2540 fprintf (stderr
, "unable to exec ");
2546 if (pidXwait (child_id
, NULL
)) {
2547 username
= password
= NULL
;
2557 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2562 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2563 if ((fp
= fopen (cachefile
, "w"))) {
2565 FILE *gp
= ce
->ce_fp
;
2567 fseek (gp
, 0L, SEEK_SET
);
2569 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2571 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2575 admonish (ce
->ce_file
, "error reading");
2580 admonish (cachefile
, "error writing");
2589 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2590 *file
= ce
->ce_file
;
2591 return fileno (ce
->ce_fp
);
2602 return init_encoding (ct
, openMail
);
2607 openMail (CT ct
, char **file
)
2609 int child_id
, fd
, i
, vecp
;
2611 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2612 struct exbody
*e
= ct
->c_ctexbody
;
2613 CE ce
= &ct
->c_cefile
;
2615 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2626 if (!e
->eb_server
) {
2627 content_error (NULL
, ct
, "missing server parameter");
2634 pidcheck (pidwait (xpid
, NOTOK
));
2638 /* Get buffer ready to go */
2640 buflen
= sizeof(buffer
);
2642 /* Now, construct query message */
2643 snprintf (bp
, buflen
, "Retrieve content");
2649 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2655 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2657 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2659 /* Now, check answer */
2660 if (!getanswer (buffer
))
2664 vec
[vecp
++] = r1bindex (mailproc
, '/');
2665 vec
[vecp
++] = e
->eb_server
;
2666 vec
[vecp
++] = "-subject";
2667 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2668 vec
[vecp
++] = "-body";
2669 vec
[vecp
++] = e
->eb_body
;
2672 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2676 advise ("fork", "unable to");
2680 execvp (mailproc
, vec
);
2681 fprintf (stderr
, "unable to exec ");
2687 if (pidXwait (child_id
, NULL
) == OK
)
2688 advise (NULL
, "request sent");
2692 if (*file
== NULL
) {
2693 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2696 ce
->ce_file
= add (*file
, NULL
);
2700 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2701 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2705 /* showproc is for mhshow and mhstore, though mhlist -debug
2706 * prints it, too. */
2708 free (ct
->c_showproc
);
2709 ct
->c_showproc
= add ("true", NULL
);
2711 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2712 *file
= ce
->ce_file
;
2713 return fileno (ce
->ce_fp
);
2724 return init_encoding (ct
, openURL
);
2729 openURL (CT ct
, char **file
)
2731 struct exbody
*e
= ct
->c_ctexbody
;
2732 CE ce
= &ct
->c_cefile
;
2733 char *urlprog
, *program
;
2734 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2735 int fd
, caching
, cachetype
;
2736 struct msgs_array args
= { 0, 0, NULL
};
2739 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2743 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2747 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2759 content_error(NULL
, ct
, "missing url parameter");
2766 pidcheck (pidwait (xpid
, NOTOK
));
2770 ce
->ce_unlink
= (*file
== NULL
);
2772 cachefile
[0] = '\0';
2774 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2775 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2776 if (*file
== NULL
) {
2783 ce
->ce_file
= add(*file
, NULL
);
2785 ce
->ce_file
= add(cachefile
, NULL
);
2787 ce
->ce_file
= add(m_mktemp(tmp
, NULL
, NULL
), NULL
);
2789 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2790 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2794 switch (child_id
= fork()) {
2796 adios ("fork", "unable to");
2800 argsplit_msgarg(&args
, urlprog
, &program
);
2801 app_msgarg(&args
, e
->eb_url
);
2802 app_msgarg(&args
, NULL
);
2803 dup2(fileno(ce
->ce_fp
), 1);
2804 close(fileno(ce
->ce_fp
));
2805 execvp(program
, args
.msgs
);
2806 fprintf(stderr
, "Unable to exec ");
2812 if (pidXwait(child_id
, NULL
)) {
2820 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2825 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2826 if ((fp
= fopen(cachefile
, "w"))) {
2828 FILE *gp
= ce
->ce_fp
;
2830 fseeko(gp
, 0, SEEK_SET
);
2832 while ((cc
= fread(buffer
, sizeof(*buffer
),
2833 sizeof(buffer
), gp
)) > 0)
2834 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2839 admonish(ce
->ce_file
, "error reading");
2847 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2848 *file
= ce
->ce_file
;
2853 readDigest (CT ct
, char *cp
)
2858 unsigned char *dp
, value
, *ep
;
2864 for (ep
= (dp
= ct
->c_digest
)
2865 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2870 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2872 fprintf (stderr
, "invalid BASE64 encoding\n");
2876 bits
|= value
<< bitno
;
2878 if ((bitno
-= 6) < 0) {
2879 if (dp
+ (3 - skip
) > ep
)
2880 goto invalid_digest
;
2881 *dp
++ = (bits
>> 16) & 0xff;
2883 *dp
++ = (bits
>> 8) & 0xff;
2885 *dp
++ = bits
& 0xff;
2895 goto self_delimiting
;
2900 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2910 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2918 fprintf (stderr
, "MD5 digest=");
2919 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2920 fprintf (stderr
, "%02x", *dp
& 0xff);
2921 fprintf (stderr
, "\n");
2928 /* Multipart parts might have content before the first subpart and/or
2929 after the last subpart that hasn't been stored anywhere else, so do
2932 get_leftover_mp_content (CT ct
, int before
/* or after */) {
2933 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2935 int found_boundary
= 0;
2936 char buffer
[BUFSIZ
];
2939 char *content
= NULL
;
2941 if (! m
) return NOTOK
;
2944 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2946 /* Isolate the beginning of this part to the beginning of the
2947 first subpart and save any content between them. */
2948 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2949 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2950 boundary
= concat ("--", m
->mp_start
, NULL
);
2952 struct part
*last_subpart
= NULL
;
2953 struct part
*subpart
;
2955 /* Go to the last subpart to get its end position. */
2956 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
2957 last_subpart
= subpart
;
2960 if (last_subpart
== NULL
) return NOTOK
;
2962 /* Isolate the end of the last subpart to the end of this part
2963 and save any content between them. */
2964 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
2965 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
2966 boundary
= concat ("--", m
->mp_stop
, NULL
);
2969 /* Back up by 1 to pick up the newline. */
2970 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
2971 read
+= strlen (buffer
);
2972 /* Don't look beyond beginning of first subpart (before) or
2973 next part (after). */
2974 if (read
> max
) buffer
[read
-max
] = '\0';
2977 if (! strcmp (buffer
, boundary
)) {
2981 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
2987 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
2989 char *old_content
= content
;
2990 content
= concat (content
, buffer
, NULL
);
2994 ? concat ("\n", buffer
, NULL
)
2995 : concat (buffer
, NULL
);
3000 if (found_boundary
|| read
> max
) break;
3002 if (read
> max
) break;
3006 /* Skip the newline if that's all there is. */
3010 /* Remove trailing newline, except at EOF. */
3011 if ((before
|| ! feof (ct
->c_fp
)) &&
3012 (cp
= content
+ strlen (content
)) > content
&&
3017 if (strlen (content
) > 1) {
3019 m
->mp_content_before
= content
;
3021 m
->mp_content_after
= content
;
3035 ct_type_str (int type
) {
3037 case CT_APPLICATION
:
3038 return "application";
3054 return "unknown_type";
3060 ct_subtype_str (int type
, int subtype
) {
3062 case CT_APPLICATION
:
3064 case APPLICATION_OCTETS
:
3066 case APPLICATION_POSTSCRIPT
:
3067 return "postscript";
3069 return "unknown_app_subtype";
3073 case MESSAGE_RFC822
:
3075 case MESSAGE_PARTIAL
:
3077 case MESSAGE_EXTERNAL
:
3080 return "unknown_msg_subtype";
3086 case MULTI_ALTERNATE
:
3087 return "alternative";
3090 case MULTI_PARALLEL
:
3093 return "unknown_multipart_subtype";
3104 return "unknown_text_subtype";
3107 return "unknown_type";
3112 /* Find the content type and InitFunc for the CT. */
3113 const struct str2init
*
3114 get_ct_init (int type
) {
3115 const struct str2init
*sp
;
3117 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3118 if (type
== sp
->si_val
) {
3127 ce_str (int encoding
) {
3148 /* Find the content type and InitFunc for the content encoding method. */
3149 const struct str2init
*
3150 get_ce_method (const char *method
) {
3151 struct str2init
*sp
;
3153 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3154 if (! strcasecmp (method
, sp
->si_key
)) {
3163 parse_header_attrs (const char *filename
, int len
, char **header_attrp
, CI ci
,
3165 char **attr
= ci
->ci_attrs
;
3166 char *cp
= *header_attrp
;
3168 while (*cp
== ';') {
3169 char *dp
, *vp
, *up
, c
;
3171 /* Relies on knowledge of this declaration:
3172 * char *ci_attrs[NPARMS + 2];
3174 if (attr
>= ci
->ci_attrs
+ sizeof ci
->ci_attrs
/sizeof (char *) - 2) {
3176 "too many parameters in message %s's %s: field (%d max)",
3177 filename
, TYPE_FIELD
, NPARMS
);
3183 while (isspace ((unsigned char) *cp
))
3187 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3194 "extraneous trailing ';' in message %s's %s: "
3196 filename
, TYPE_FIELD
);
3201 /* down case the attribute name */
3202 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3203 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3204 *dp
= tolower ((unsigned char) *dp
);
3206 for (up
= dp
; isspace ((unsigned char) *dp
);)
3208 if (dp
== cp
|| *dp
!= '=') {
3210 "invalid parameter in message %s's %s: "
3211 "field\n%*.*sparameter %s (error detected at offset %d)",
3212 filename
, TYPE_FIELD
, len
, len
, "", cp
, dp
- cp
);
3217 vp
= (*attr
= add (cp
, NULL
)) + (up
- cp
);
3219 for (dp
++; isspace ((unsigned char) *dp
);)
3222 /* Now store the attribute value. */
3223 ci
->ci_values
[attr
- ci
->ci_attrs
] = vp
= *attr
+ (dp
- cp
);
3226 for (cp
= ++dp
, dp
= vp
;;) {
3227 switch (c
= *cp
++) {
3231 "invalid quoted-string in message %s's %s: "
3232 "field\n%*.*s(parameter %s)",
3233 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3239 if ((c
= *cp
++) == '\0')
3254 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
3260 "invalid parameter in message %s's %s: "
3261 "field\n%*.*s(parameter %s)",
3262 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3267 while (isspace ((unsigned char) *cp
))
3271 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {