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>
18 #include <h/mhparse.h>
24 extern pid_t xpid
; /* mhshowsbr.c */
27 extern int rcachesw
; /* mhcachesbr.c */
28 extern int wcachesw
; /* mhcachesbr.c */
30 int checksw
= 0; /* check Content-MD5 field */
33 * Directory to place temp files. This must
34 * be set before these routines are called.
39 * These are for mhfixmsg to:
40 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
42 * 2) Suppress the warning about bogus multipart content, and report it.
44 int skip_mp_cte_check
;
45 int suppress_bogus_mp_content_warning
;
49 * Structures for TEXT messages
51 struct k2v SubText
[] = {
52 { "plain", TEXT_PLAIN
},
53 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
54 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
55 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
58 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
61 * Structures for MULTIPART messages
63 struct k2v SubMultiPart
[] = {
64 { "mixed", MULTI_MIXED
},
65 { "alternative", MULTI_ALTERNATE
},
66 { "digest", MULTI_DIGEST
},
67 { "parallel", MULTI_PARALLEL
},
68 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
72 * Structures for MESSAGE messages
74 struct k2v SubMessage
[] = {
75 { "rfc822", MESSAGE_RFC822
},
76 { "partial", MESSAGE_PARTIAL
},
77 { "external-body", MESSAGE_EXTERNAL
},
78 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
82 * Structure for APPLICATION messages
84 struct k2v SubApplication
[] = {
85 { "octet-stream", APPLICATION_OCTETS
},
86 { "postscript", APPLICATION_POSTSCRIPT
},
87 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
92 int find_cache (CT
, int, int *, char *, char *, int);
95 int part_ok (CT
, int);
96 int type_ok (CT
, int);
97 void content_error (char *, CT
, char *, ...);
100 void free_encoding (CT
, int);
105 static CT
get_content (FILE *, char *, int);
106 static int get_comment (const char *, CI
, char **, int);
108 static int InitGeneric (CT
);
109 static int InitText (CT
);
110 static int InitMultiPart (CT
);
111 void reverse_parts (CT
);
112 static int InitMessage (CT
);
113 static int InitApplication (CT
);
114 static int init_encoding (CT
, OpenCEFunc
);
115 static unsigned long size_encoding (CT
);
116 static int InitBase64 (CT
);
117 static int openBase64 (CT
, char **);
118 static int InitQuoted (CT
);
119 static int openQuoted (CT
, char **);
120 static int Init7Bit (CT
);
121 static int openExternal (CT
, CT
, CE
, char **, int *);
122 static int InitFile (CT
);
123 static int openFile (CT
, char **);
124 static int InitFTP (CT
);
125 static int openFTP (CT
, char **);
126 static int InitMail (CT
);
127 static int openMail (CT
, char **);
128 static int readDigest (CT
, char *);
129 static int get_leftover_mp_content (CT
, int);
130 static int InitURL (CT
);
131 static int openURL (CT
, char **);
133 struct str2init str2cts
[] = {
134 { "application", CT_APPLICATION
, InitApplication
},
135 { "audio", CT_AUDIO
, InitGeneric
},
136 { "image", CT_IMAGE
, InitGeneric
},
137 { "message", CT_MESSAGE
, InitMessage
},
138 { "multipart", CT_MULTIPART
, InitMultiPart
},
139 { "text", CT_TEXT
, InitText
},
140 { "video", CT_VIDEO
, InitGeneric
},
141 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
142 { NULL
, CT_UNKNOWN
, NULL
},
145 struct str2init str2ces
[] = {
146 { "base64", CE_BASE64
, InitBase64
},
147 { "quoted-printable", CE_QUOTED
, InitQuoted
},
148 { "8bit", CE_8BIT
, Init7Bit
},
149 { "7bit", CE_7BIT
, Init7Bit
},
150 { "binary", CE_BINARY
, Init7Bit
},
151 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
152 { NULL
, CE_UNKNOWN
, NULL
},
156 * NOTE WELL: si_key MUST NOT have value of NOTOK
158 * si_key is 1 if access method is anonymous.
160 struct str2init str2methods
[] = {
161 { "afs", 1, InitFile
},
162 { "anon-ftp", 1, InitFTP
},
163 { "ftp", 0, InitFTP
},
164 { "local-file", 0, InitFile
},
165 { "mail-server", 0, InitMail
},
166 { "url", 0, InitURL
},
172 pidcheck (int status
)
174 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
185 * Main entry point for parsing a MIME message or file.
186 * It returns the Content structure for the top level
187 * entity in the file.
191 parse_mime (char *file
)
199 * Check if file is actually standard input
201 if ((is_stdin
= !(strcmp (file
, "-")))) {
202 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
204 advise("mhparse", "unable to create temporary file");
207 file
= add (tfile
, NULL
);
210 while (fgets (buffer
, sizeof(buffer
), stdin
))
214 if (ferror (stdin
)) {
216 advise ("stdin", "error reading");
221 advise (file
, "error writing");
224 fseek (fp
, 0L, SEEK_SET
);
225 } else if ((fp
= fopen (file
, "r")) == NULL
) {
226 advise (file
, "unable to read");
230 if (!(ct
= get_content (fp
, file
, 1))) {
233 advise (NULL
, "unable to decode %s", file
);
238 ct
->c_unlink
= 1; /* temp file to remove */
242 if (ct
->c_end
== 0L) {
243 fseek (fp
, 0L, SEEK_END
);
244 ct
->c_end
= ftell (fp
);
247 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
259 * Main routine for reading/parsing the headers
260 * of a message content.
262 * toplevel = 1 # we are at the top level of the message
263 * toplevel = 0 # we are inside message type or multipart type
264 * # other than multipart/digest
265 * toplevel = -1 # we are inside multipart/digest
266 * NB: on failure we will fclose(in)!
270 get_content (FILE *in
, char *file
, int toplevel
)
273 char buf
[BUFSIZ
], name
[NAMESZ
];
277 m_getfld_state_t gstate
= 0;
279 /* allocate the content structure */
280 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
281 adios (NULL
, "out of memory");
284 ct
->c_file
= add (file
, NULL
);
285 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
288 * Parse the header fields for this
289 * content into a linked list.
291 m_getfld_track_filepos (&gstate
, in
);
292 for (compnum
= 1;;) {
293 int bufsz
= sizeof buf
;
294 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
299 /* get copies of the buffers */
300 np
= add (name
, NULL
);
301 vp
= add (buf
, NULL
);
303 /* if necessary, get rest of field */
304 while (state
== FLDPLUS
) {
306 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
307 vp
= add (buf
, vp
); /* add to previous value */
310 /* Now add the header data to the list */
311 add_header (ct
, np
, vp
);
313 /* continue, to see if this isn't the last header field */
314 ct
->c_begin
= ftell (in
) + 1;
318 ct
->c_begin
= ftell (in
) - strlen (buf
);
322 ct
->c_begin
= ftell (in
);
327 adios (NULL
, "message format error in component #%d", compnum
);
330 adios (NULL
, "getfld() returned %d", state
);
333 /* break out of the loop */
336 m_getfld_state_destroy (&gstate
);
339 * Read the content headers. We will parse the
340 * MIME related header fields into their various
341 * structures and set internal flags related to
342 * content type/subtype, etc.
345 hp
= ct
->c_first_hf
; /* start at first header field */
347 /* Get MIME-Version field */
348 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
353 advise (NULL
, "message %s has multiple %s: fields",
354 ct
->c_file
, VRSN_FIELD
);
357 ct
->c_vrsn
= add (hp
->value
, NULL
);
359 /* Now, cleanup this field */
362 while (isspace ((unsigned char) *cp
))
364 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
366 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
367 if (!isspace ((unsigned char) *dp
))
371 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
374 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
)
377 for (dp
= cp
; istoken (*dp
); dp
++)
381 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
384 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
385 ct
->c_file
, VRSN_FIELD
, cp
);
388 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
389 /* Get Content-Type field */
390 struct str2init
*s2i
;
391 CI ci
= &ct
->c_ctinfo
;
393 /* Check if we've already seen a Content-Type header */
395 advise (NULL
, "message %s has multiple %s: fields",
396 ct
->c_file
, TYPE_FIELD
);
400 /* Parse the Content-Type field */
401 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
405 * Set the Init function and the internal
406 * flag for this content type.
408 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
409 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
411 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
413 ct
->c_type
= s2i
->si_val
;
414 ct
->c_ctinitfnx
= s2i
->si_init
;
416 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
417 /* Get Content-Transfer-Encoding field */
419 struct str2init
*s2i
;
422 * Check if we've already seen the
423 * Content-Transfer-Encoding field
426 advise (NULL
, "message %s has multiple %s: fields",
427 ct
->c_file
, ENCODING_FIELD
);
431 /* get copy of this field */
432 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
434 while (isspace ((unsigned char) *cp
))
436 for (dp
= cp
; istoken (*dp
); dp
++)
442 * Find the internal flag and Init function
443 * for this transfer encoding.
445 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
446 if (!strcasecmp (cp
, s2i
->si_key
))
448 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
451 ct
->c_encoding
= s2i
->si_val
;
453 /* Call the Init function for this encoding */
454 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
457 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
458 /* Get Content-MD5 field */
464 if (ct
->c_digested
) {
465 advise (NULL
, "message %s has multiple %s: fields",
466 ct
->c_file
, MD5_FIELD
);
470 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
472 while (isspace ((unsigned char) *cp
))
474 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
476 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
477 if (!isspace ((unsigned char) *dp
))
481 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
484 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
) {
489 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
497 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
498 /* Get Content-ID field */
499 ct
->c_id
= add (hp
->value
, ct
->c_id
);
501 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
502 /* Get Content-Description field */
503 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
505 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
506 /* Get Content-Disposition field */
507 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
511 hp
= hp
->next
; /* next header field */
515 * Check if we saw a Content-Type field.
516 * If not, then assign a default value for
517 * it, and the Init function.
521 * If we are inside a multipart/digest message,
522 * so default type is message/rfc822
525 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
527 ct
->c_type
= CT_MESSAGE
;
528 ct
->c_ctinitfnx
= InitMessage
;
531 * Else default type is text/plain
533 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
535 ct
->c_type
= CT_TEXT
;
536 ct
->c_ctinitfnx
= InitText
;
540 /* Use default Transfer-Encoding, if necessary */
542 ct
->c_encoding
= CE_7BIT
;
555 * small routine to add header field to list
559 add_header (CT ct
, char *name
, char *value
)
563 /* allocate header field structure */
564 hp
= mh_xmalloc (sizeof(*hp
));
566 /* link data into header structure */
571 /* link header structure into the list */
572 if (ct
->c_first_hf
== NULL
) {
573 ct
->c_first_hf
= hp
; /* this is the first */
576 ct
->c_last_hf
->next
= hp
; /* add it to the end */
584 /* Make sure that buf contains at least one appearance of name,
585 followed by =. If not, insert both name and value, just after
586 first semicolon, if any. Note that name should not contain a
587 trailing =. And quotes will be added around the value. Typical
588 usage: make sure that a Content-Disposition header contains
589 filename="foo". If it doesn't and value does, use value from
592 incl_name_value (char *buf
, char *name
, char *value
) {
595 /* Assume that name is non-null. */
597 char *name_plus_equal
= concat (name
, "=", NULL
);
599 if (! strstr (buf
, name_plus_equal
)) {
601 char *cp
, *prefix
, *suffix
;
603 /* Trim trailing space, esp. newline. */
604 for (cp
= &buf
[strlen (buf
) - 1];
605 cp
>= buf
&& isspace ((unsigned char) *cp
);
610 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
612 /* Insert at first semicolon, if any. If none, append to
614 prefix
= add (buf
, NULL
);
615 if ((cp
= strchr (prefix
, ';'))) {
616 suffix
= concat (cp
, NULL
);
618 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
622 newbuf
= concat (buf
, insertion
, "\n", NULL
);
630 free (name_plus_equal
);
636 /* Extract just name_suffix="foo", if any, from value. If there isn't
637 one, return the entire value. Note that, for example, a name_suffix
638 of name will match filename="foo", and return foo. */
640 extract_name_value (char *name_suffix
, char *value
) {
641 char *extracted_name_value
= value
;
642 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
643 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
646 free (name_suffix_plus_quote
);
647 if (name_suffix_equals
) {
648 char *name_suffix_begin
;
651 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
652 name_suffix_begin
= ++cp
;
653 /* Find second \". */
654 for (; *cp
!= '"'; ++cp
) /* empty */;
656 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
657 memcpy (extracted_name_value
,
659 cp
- name_suffix_begin
);
660 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
663 return extracted_name_value
;
667 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
668 * directives. Fills in the information of the CTinfo structure.
671 get_ctinfo (char *cp
, CT ct
, int magic
)
680 i
= strlen (invo_name
) + 2;
682 /* store copy of Content-Type line */
683 cp
= ct
->c_ctline
= add (cp
, NULL
);
685 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
688 /* change newlines to spaces */
689 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
692 /* trim trailing spaces */
693 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
694 if (!isspace ((unsigned char) *dp
))
699 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
701 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
704 for (dp
= cp
; istoken (*dp
); dp
++)
707 ci
->ci_type
= add (cp
, NULL
); /* store content type */
711 advise (NULL
, "invalid %s: field in message %s (empty type)",
712 TYPE_FIELD
, ct
->c_file
);
716 /* down case the content type string */
717 for (dp
= ci
->ci_type
; *dp
; dp
++)
718 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
719 *dp
= tolower ((unsigned char) *dp
);
721 while (isspace ((unsigned char) *cp
))
724 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
729 ci
->ci_subtype
= add ("", NULL
);
734 while (isspace ((unsigned char) *cp
))
737 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
740 for (dp
= cp
; istoken (*dp
); dp
++)
743 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
746 if (!*ci
->ci_subtype
) {
748 "invalid %s: field in message %s (empty subtype for \"%s\")",
749 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
753 /* down case the content subtype string */
754 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
755 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
756 *dp
= tolower ((unsigned char) *dp
);
759 while (isspace ((unsigned char) *cp
))
762 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
765 if (parse_header_attrs (ct
->c_file
, i
, &cp
, ci
, &status
) == NOTOK
) {
770 * Get any <Content-Id> given in buffer
772 if (magic
&& *cp
== '<') {
777 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
778 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
784 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
790 while (isspace ((unsigned char) *cp
))
795 * Get any [Content-Description] given in buffer.
797 if (magic
&& *cp
== '[') {
799 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
803 advise (NULL
, "invalid description in message %s", ct
->c_file
);
811 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
817 while (isspace ((unsigned char) *cp
))
822 * Get any {Content-Disposition} given in buffer.
824 if (magic
&& *cp
== '{') {
826 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
830 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
838 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
844 while (isspace ((unsigned char) *cp
))
849 * Check if anything is left over
853 ci
->ci_magic
= add (cp
, NULL
);
855 /* If there is a Content-Disposition header and it doesn't
856 have a *filename=, extract it from the magic contents.
857 The r1bindex call skips any leading directory
861 incl_name_value (ct
->c_dispo
,
863 r1bindex (extract_name_value ("name",
870 "extraneous information in message %s's %s: field\n%*.*s(%s)",
871 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
879 get_comment (const char *filename
, CI ci
, char **ap
, int istype
)
883 char c
, buffer
[BUFSIZ
], *dp
;
893 advise (NULL
, "invalid comment in message %s's %s: field",
894 filename
, istype
? TYPE_FIELD
: VRSN_FIELD
);
899 if ((c
= *cp
++) == '\0')
922 if ((dp
= ci
->ci_comment
)) {
923 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
926 ci
->ci_comment
= add (buffer
, NULL
);
930 while (isspace ((unsigned char) *cp
))
941 * Handles content types audio, image, and video.
942 * There's not much to do right here.
950 return OK
; /* not much to do here */
963 char **ap
, **ep
, *cp
;
966 CI ci
= &ct
->c_ctinfo
;
968 /* check for missing subtype */
969 if (!*ci
->ci_subtype
)
970 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
973 for (kv
= SubText
; kv
->kv_key
; kv
++)
974 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
976 ct
->c_subtype
= kv
->kv_value
;
978 /* allocate text character set structure */
979 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
980 adios (NULL
, "out of memory");
981 ct
->c_ctparams
= (void *) t
;
983 /* scan for charset parameter */
984 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
985 if (!strcasecmp (*ap
, "charset"))
988 /* check if content specified a character set */
991 t
->tx_charset
= CHARSET_SPECIFIED
;
993 t
->tx_charset
= CHARSET_UNSPECIFIED
;
997 * If we can not handle character set natively,
998 * then check profile for string to modify the
999 * terminal or display method.
1001 * termproc is for mhshow, though mhlist -debug prints it, too.
1003 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1004 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1005 if ((cp
= context_find (buffer
)))
1006 ct
->c_termproc
= getcpy (cp
);
1018 InitMultiPart (CT ct
)
1022 char *cp
, *dp
, **ap
, **ep
;
1023 char *bp
, buffer
[BUFSIZ
];
1024 struct multipart
*m
;
1026 struct part
*part
, **next
;
1027 CI ci
= &ct
->c_ctinfo
;
1032 * The encoding for multipart messages must be either
1033 * 7bit, 8bit, or binary (per RFC2045).
1035 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1036 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1037 /* Copy the Content-Transfer-Encoding header field body so we can
1038 remove any trailing whitespace and leading blanks from it. */
1039 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1041 bp
= cte
+ strlen (cte
) - 1;
1042 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1043 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1046 "\"%s/%s\" type in message %s must be encoded in\n"
1047 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1048 "is to\nmanually edit the file and change the \"%s\"\n"
1049 "Content-Transfer-Encoding to one of those. For now",
1050 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1057 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1058 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1060 ct
->c_subtype
= kv
->kv_value
;
1063 * Check for "boundary" parameter, which is
1064 * required for multipart messages.
1067 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1068 if (!strcasecmp (*ap
, "boundary")) {
1074 /* complain if boundary parameter is missing */
1077 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1078 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1082 /* allocate primary structure for multipart info */
1083 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1084 adios (NULL
, "out of memory");
1085 ct
->c_ctparams
= (void *) m
;
1087 /* check if boundary parameter contains only whitespace characters */
1088 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1091 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1092 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1096 /* remove trailing whitespace from boundary parameter */
1097 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1098 if (!isspace ((unsigned char) *dp
))
1102 /* record boundary separators */
1103 m
->mp_start
= concat (bp
, "\n", NULL
);
1104 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1106 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1107 advise (ct
->c_file
, "unable to open for reading");
1111 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1113 next
= &m
->mp_parts
;
1117 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1121 pos
+= strlen (buffer
);
1122 if (buffer
[0] != '-' || buffer
[1] != '-')
1125 if (strcmp (buffer
+ 2, m
->mp_start
))
1128 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1129 adios (NULL
, "out of memory");
1131 next
= &part
->mp_next
;
1133 if (!(p
= get_content (fp
, ct
->c_file
,
1134 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1141 fseek (fp
, pos
, SEEK_SET
);
1144 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1148 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1149 if (p
->c_end
< p
->c_begin
)
1150 p
->c_begin
= p
->c_end
;
1155 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1161 if (! suppress_bogus_mp_content_warning
) {
1162 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1164 bogus_mp_content
= 1;
1166 if (!inout
&& part
) {
1168 p
->c_end
= ct
->c_end
;
1170 if (p
->c_begin
>= p
->c_end
) {
1171 for (next
= &m
->mp_parts
; *next
!= part
;
1172 next
= &((*next
)->mp_next
))
1176 free ((char *) part
);
1181 /* reverse the order of the parts for multipart/alternative */
1182 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1186 * label all subparts with part number, and
1187 * then initialize the content of the subpart.
1192 char partnam
[BUFSIZ
];
1195 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1196 pp
= partnam
+ strlen (partnam
);
1201 for (part
= m
->mp_parts
, partnum
= 1; part
;
1202 part
= part
->mp_next
, partnum
++) {
1205 sprintf (pp
, "%d", partnum
);
1206 p
->c_partno
= add (partnam
, NULL
);
1208 /* initialize the content of the subparts */
1209 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1217 get_leftover_mp_content (ct
, 1);
1218 get_leftover_mp_content (ct
, 0);
1227 * reverse the order of the parts of a multipart/alternative
1231 reverse_parts (CT ct
)
1233 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1237 /* Reverse the order of its parts by walking the mp_parts list
1238 and pushing each node to the front. */
1239 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1240 next
= part
->mp_next
;
1241 part
->mp_next
= m
->mp_parts
;
1255 CI ci
= &ct
->c_ctinfo
;
1257 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1259 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1260 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1264 /* check for missing subtype */
1265 if (!*ci
->ci_subtype
)
1266 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1269 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1270 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1272 ct
->c_subtype
= kv
->kv_value
;
1274 switch (ct
->c_subtype
) {
1275 case MESSAGE_RFC822
:
1278 case MESSAGE_PARTIAL
:
1283 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1284 adios (NULL
, "out of memory");
1285 ct
->c_ctparams
= (void *) p
;
1287 /* scan for parameters "id", "number", and "total" */
1288 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1289 if (!strcasecmp (*ap
, "id")) {
1290 p
->pm_partid
= add (*ep
, NULL
);
1293 if (!strcasecmp (*ap
, "number")) {
1294 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1295 || p
->pm_partno
< 1) {
1298 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1299 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1300 ct
->c_file
, TYPE_FIELD
);
1305 if (!strcasecmp (*ap
, "total")) {
1306 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1315 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1317 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1318 ci
->ci_type
, ci
->ci_subtype
,
1319 ct
->c_file
, TYPE_FIELD
);
1325 case MESSAGE_EXTERNAL
:
1332 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1333 adios (NULL
, "out of memory");
1334 ct
->c_ctparams
= (void *) e
;
1337 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1338 advise (ct
->c_file
, "unable to open for reading");
1342 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1344 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1352 p
->c_ceopenfnx
= NULL
;
1353 if ((exresult
= params_external (ct
, 0)) != NOTOK
1354 && p
->c_ceopenfnx
== openMail
) {
1358 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1360 content_error (NULL
, ct
,
1361 "empty body for access-type=mail-server");
1365 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1366 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1368 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1370 adios ("failed", "fread");
1373 adios (NULL
, "unexpected EOF from fread");
1376 bp
+= cc
, size
-= cc
;
1383 p
->c_end
= p
->c_begin
;
1388 if (exresult
== NOTOK
)
1390 if (e
->eb_flags
== NOTOK
)
1393 switch (p
->c_type
) {
1398 if (p
->c_subtype
!= MESSAGE_RFC822
)
1402 e
->eb_partno
= ct
->c_partno
;
1404 (*p
->c_ctinitfnx
) (p
);
1419 params_external (CT ct
, int composing
)
1422 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1423 CI ci
= &ct
->c_ctinfo
;
1425 ct
->c_ceopenfnx
= NULL
;
1426 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1427 if (!strcasecmp (*ap
, "access-type")) {
1428 struct str2init
*s2i
;
1429 CT p
= e
->eb_content
;
1431 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1432 if (!strcasecmp (*ep
, s2i
->si_key
))
1436 e
->eb_flags
= NOTOK
;
1437 p
->c_encoding
= CE_EXTERNAL
;
1440 e
->eb_access
= s2i
->si_key
;
1441 e
->eb_flags
= s2i
->si_val
;
1442 p
->c_encoding
= CE_EXTERNAL
;
1444 /* Call the Init function for this external type */
1445 if ((*s2i
->si_init
)(p
) == NOTOK
)
1449 if (!strcasecmp (*ap
, "name")) {
1453 if (!strcasecmp (*ap
, "permission")) {
1454 e
->eb_permission
= *ep
;
1457 if (!strcasecmp (*ap
, "site")) {
1461 if (!strcasecmp (*ap
, "directory")) {
1465 if (!strcasecmp (*ap
, "mode")) {
1469 if (!strcasecmp (*ap
, "size")) {
1470 sscanf (*ep
, "%lu", &e
->eb_size
);
1473 if (!strcasecmp (*ap
, "server")) {
1477 if (!strcasecmp (*ap
, "subject")) {
1478 e
->eb_subject
= *ep
;
1481 if (!strcasecmp (*ap
, "url")) {
1483 * According to RFC 2017, we have to remove all whitespace from
1488 e
->eb_url
= u
= mh_xmalloc(strlen(*ep
) + 1);
1490 for (; *p
!= '\0'; p
++) {
1491 if (! isspace((unsigned char) *p
))
1498 if (composing
&& !strcasecmp (*ap
, "body")) {
1499 e
->eb_body
= getcpy (*ep
);
1504 if (!e
->eb_access
) {
1506 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1507 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1520 InitApplication (CT ct
)
1523 CI ci
= &ct
->c_ctinfo
;
1526 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1527 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1529 ct
->c_subtype
= kv
->kv_value
;
1536 * TRANSFER ENCODINGS
1540 init_encoding (CT ct
, OpenCEFunc openfnx
)
1542 ct
->c_ceopenfnx
= openfnx
;
1543 ct
->c_ceclosefnx
= close_encoding
;
1544 ct
->c_cesizefnx
= size_encoding
;
1551 close_encoding (CT ct
)
1553 CE ce
= &ct
->c_cefile
;
1562 static unsigned long
1563 size_encoding (CT ct
)
1568 CE ce
= &ct
->c_cefile
;
1571 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1572 return (long) st
.st_size
;
1575 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1576 return (long) st
.st_size
;
1581 if (ct
->c_encoding
== CE_EXTERNAL
)
1582 return (ct
->c_end
- ct
->c_begin
);
1585 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1586 return (ct
->c_end
- ct
->c_begin
);
1588 if (fstat (fd
, &st
) != NOTOK
)
1589 size
= (long) st
.st_size
;
1593 (*ct
->c_ceclosefnx
) (ct
);
1602 static unsigned char b642nib
[0x80] = {
1603 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
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, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1609 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1610 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1611 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1612 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1613 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1614 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1615 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1616 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1617 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1618 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1625 return init_encoding (ct
, openBase64
);
1630 openBase64 (CT ct
, char **file
)
1632 int bitno
, cc
, digested
;
1633 int fd
, len
, skip
, own_ct_fp
= 0;
1635 unsigned char value
, b
;
1636 char *cp
, *ep
, buffer
[BUFSIZ
];
1637 /* sbeck -- handle suffixes */
1639 CE ce
= &ct
->c_cefile
;
1643 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1648 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1649 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1655 if (*file
== NULL
) {
1656 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1659 ce
->ce_file
= add (*file
, NULL
);
1663 /* sbeck@cise.ufl.edu -- handle suffixes */
1665 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1666 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1667 cp
= context_find (buffer
);
1668 if (cp
== NULL
|| *cp
== '\0') {
1669 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1671 cp
= context_find (buffer
);
1673 if (cp
!= NULL
&& *cp
!= '\0') {
1674 if (ce
->ce_unlink
) {
1675 /* Temporary file already exists, so we rename to
1676 version with extension. */
1677 char *file_org
= strdup(ce
->ce_file
);
1678 ce
->ce_file
= add (cp
, ce
->ce_file
);
1679 if (rename(file_org
, ce
->ce_file
)) {
1680 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1685 ce
->ce_file
= add (cp
, ce
->ce_file
);
1689 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1690 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1694 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1695 adios (NULL
, "internal error(1)");
1698 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1699 content_error (ct
->c_file
, ct
, "unable to open for reading");
1705 if ((digested
= ct
->c_digested
))
1706 MD5Init (&mdContext
);
1712 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1714 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1716 content_error (ct
->c_file
, ct
, "error reading from");
1720 content_error (NULL
, ct
, "premature eof");
1728 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1731 if (isspace ((unsigned char) *cp
))
1733 if (skip
|| (((unsigned char) *cp
) & 0x80)
1734 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1736 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1737 (unsigned char) *cp
,
1738 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1741 content_error (NULL
, ct
,
1742 "invalid BASE64 encoding -- continuing");
1746 bits
|= value
<< bitno
;
1748 if ((bitno
-= 6) < 0) {
1749 b
= (bits
>> 16) & 0xff;
1750 putc ((char) b
, ce
->ce_fp
);
1752 MD5Update (&mdContext
, &b
, 1);
1754 b
= (bits
>> 8) & 0xff;
1755 putc ((char) b
, ce
->ce_fp
);
1757 MD5Update (&mdContext
, &b
, 1);
1760 putc ((char) b
, ce
->ce_fp
);
1762 MD5Update (&mdContext
, &b
, 1);
1766 if (ferror (ce
->ce_fp
)) {
1767 content_error (ce
->ce_file
, ct
,
1768 "error writing to");
1771 bitno
= 18, bits
= 0L, skip
= 0;
1777 goto self_delimiting
;
1786 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1788 content_error (NULL
, ct
, "invalid BASE64 encoding");
1793 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1795 if (fflush (ce
->ce_fp
)) {
1796 content_error (ce
->ce_file
, ct
, "error writing to");
1801 unsigned char digest
[16];
1803 MD5Final (digest
, &mdContext
);
1804 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1805 sizeof(digest
) / sizeof(digest
[0])))
1806 content_error (NULL
, ct
,
1807 "content integrity suspect (digest mismatch) -- continuing");
1810 fprintf (stderr
, "content integrity confirmed\n");
1813 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1816 *file
= ce
->ce_file
;
1821 return fileno (ce
->ce_fp
);
1828 free_encoding (ct
, 0);
1837 static char hex2nib
[0x80] = {
1838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1845 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1846 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1847 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1848 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1849 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1850 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1851 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1852 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1853 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1860 return init_encoding (ct
, openQuoted
);
1865 openQuoted (CT ct
, char **file
)
1867 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1869 char buffer
[BUFSIZ
];
1871 CE ce
= &ct
->c_cefile
;
1872 /* sbeck -- handle suffixes */
1877 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1882 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1883 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1889 if (*file
== NULL
) {
1890 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1893 ce
->ce_file
= add (*file
, NULL
);
1897 /* sbeck@cise.ufl.edu -- handle suffixes */
1899 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1900 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1901 cp
= context_find (buffer
);
1902 if (cp
== NULL
|| *cp
== '\0') {
1903 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1905 cp
= context_find (buffer
);
1907 if (cp
!= NULL
&& *cp
!= '\0') {
1908 if (ce
->ce_unlink
) {
1909 /* Temporary file already exists, so we rename to
1910 version with extension. */
1911 char *file_org
= strdup(ce
->ce_file
);
1912 ce
->ce_file
= add (cp
, ce
->ce_file
);
1913 if (rename(file_org
, ce
->ce_file
)) {
1914 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1919 ce
->ce_file
= add (cp
, ce
->ce_file
);
1923 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1924 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1928 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1929 adios (NULL
, "internal error(2)");
1932 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1933 content_error (ct
->c_file
, ct
, "unable to open for reading");
1939 if ((digested
= ct
->c_digested
))
1940 MD5Init (&mdContext
);
1947 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1949 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
1950 content_error (NULL
, ct
, "premature eof");
1954 if ((cc
= strlen (buffer
)) > len
)
1958 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
1959 if (!isspace ((unsigned char) *ep
))
1963 for (; cp
< ep
; cp
++) {
1965 /* in an escape sequence */
1967 /* at byte 1 of an escape sequence */
1968 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
1969 /* next is byte 2 */
1972 /* at byte 2 of an escape sequence */
1974 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
1975 putc (mask
, ce
->ce_fp
);
1977 MD5Update (&mdContext
, &mask
, 1);
1978 if (ferror (ce
->ce_fp
)) {
1979 content_error (ce
->ce_file
, ct
, "error writing to");
1982 /* finished escape sequence; next may be literal or a new
1983 * escape sequence */
1986 /* on to next byte */
1990 /* not in an escape sequence */
1992 /* starting an escape sequence, or invalid '='? */
1993 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
1994 /* "=\n" soft line break, eat the \n */
1998 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
1999 /* We don't have 2 bytes left, so this is an invalid
2000 * escape sequence; just show the raw bytes (below). */
2001 } else if (isxdigit ((unsigned char) cp
[1]) &&
2002 isxdigit ((unsigned char) cp
[2])) {
2003 /* Next 2 bytes are hex digits, making this a valid escape
2004 * sequence; let's decode it (above). */
2008 /* One or both of the next 2 is out of range, making this
2009 * an invalid escape sequence; just show the raw bytes
2014 /* Just show the raw byte. */
2015 putc (*cp
, ce
->ce_fp
);
2018 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2020 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2023 if (ferror (ce
->ce_fp
)) {
2024 content_error (ce
->ce_file
, ct
, "error writing to");
2030 content_error (NULL
, ct
,
2031 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2035 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2037 if (fflush (ce
->ce_fp
)) {
2038 content_error (ce
->ce_file
, ct
, "error writing to");
2043 unsigned char digest
[16];
2045 MD5Final (digest
, &mdContext
);
2046 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2047 sizeof(digest
) / sizeof(digest
[0])))
2048 content_error (NULL
, ct
,
2049 "content integrity suspect (digest mismatch) -- continuing");
2052 fprintf (stderr
, "content integrity confirmed\n");
2055 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2058 *file
= ce
->ce_file
;
2063 return fileno (ce
->ce_fp
);
2066 free_encoding (ct
, 0);
2082 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2085 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2091 open7Bit (CT ct
, char **file
)
2093 int cc
, fd
, len
, own_ct_fp
= 0;
2094 char buffer
[BUFSIZ
];
2095 /* sbeck -- handle suffixes */
2098 CE ce
= &ct
->c_cefile
;
2101 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2106 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2107 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2113 if (*file
== NULL
) {
2114 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2117 ce
->ce_file
= add (*file
, NULL
);
2121 /* sbeck@cise.ufl.edu -- handle suffixes */
2123 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2124 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2125 cp
= context_find (buffer
);
2126 if (cp
== NULL
|| *cp
== '\0') {
2127 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2129 cp
= context_find (buffer
);
2131 if (cp
!= NULL
&& *cp
!= '\0') {
2132 if (ce
->ce_unlink
) {
2133 /* Temporary file already exists, so we rename to
2134 version with extension. */
2135 char *file_org
= strdup(ce
->ce_file
);
2136 ce
->ce_file
= add (cp
, ce
->ce_file
);
2137 if (rename(file_org
, ce
->ce_file
)) {
2138 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2143 ce
->ce_file
= add (cp
, ce
->ce_file
);
2147 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2148 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2152 if (ct
->c_type
== CT_MULTIPART
) {
2154 CI ci
= &ct
->c_ctinfo
;
2157 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2158 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2159 + 1 + strlen (ci
->ci_subtype
);
2160 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2161 putc (';', ce
->ce_fp
);
2164 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2166 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2167 fputs ("\n\t", ce
->ce_fp
);
2170 putc (' ', ce
->ce_fp
);
2173 fprintf (ce
->ce_fp
, "%s", buffer
);
2177 if (ci
->ci_comment
) {
2178 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2179 fputs ("\n\t", ce
->ce_fp
);
2183 putc (' ', ce
->ce_fp
);
2186 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2189 fprintf (ce
->ce_fp
, "\n");
2191 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2193 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2195 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2196 fprintf (ce
->ce_fp
, "\n");
2199 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2200 adios (NULL
, "internal error(3)");
2203 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2204 content_error (ct
->c_file
, ct
, "unable to open for reading");
2210 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2212 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2214 content_error (ct
->c_file
, ct
, "error reading from");
2218 content_error (NULL
, ct
, "premature eof");
2226 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2227 if (ferror (ce
->ce_fp
)) {
2228 content_error (ce
->ce_file
, ct
, "error writing to");
2233 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2235 if (fflush (ce
->ce_fp
)) {
2236 content_error (ce
->ce_file
, ct
, "error writing to");
2240 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2243 *file
= ce
->ce_file
;
2248 return fileno (ce
->ce_fp
);
2251 free_encoding (ct
, 0);
2265 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2267 char cachefile
[BUFSIZ
];
2270 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2275 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2276 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2282 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2283 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2284 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2285 ce
->ce_file
= getcpy (cachefile
);
2289 admonish (cachefile
, "unable to fopen for reading");
2296 *file
= ce
->ce_file
;
2297 *fd
= fileno (ce
->ce_fp
);
2308 return init_encoding (ct
, openFile
);
2313 openFile (CT ct
, char **file
)
2316 char cachefile
[BUFSIZ
];
2317 struct exbody
*e
= ct
->c_ctexbody
;
2318 CE ce
= &ct
->c_cefile
;
2320 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2332 content_error (NULL
, ct
, "missing name parameter");
2336 ce
->ce_file
= getcpy (e
->eb_name
);
2339 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2340 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2344 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2345 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2346 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2350 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2351 if ((fp
= fopen (cachefile
, "w"))) {
2353 char buffer
[BUFSIZ
];
2354 FILE *gp
= ce
->ce_fp
;
2356 fseek (gp
, 0L, SEEK_SET
);
2358 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2360 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2364 admonish (ce
->ce_file
, "error reading");
2369 admonish (cachefile
, "error writing");
2377 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2378 *file
= ce
->ce_file
;
2379 return fileno (ce
->ce_fp
);
2389 return init_encoding (ct
, openFTP
);
2394 openFTP (CT ct
, char **file
)
2396 int cachetype
, caching
, fd
;
2398 char *bp
, *ftp
, *user
, *pass
;
2399 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2401 CE ce
= &ct
->c_cefile
;
2402 static char *username
= NULL
;
2403 static char *password
= NULL
;
2407 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2413 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2424 if (!e
->eb_name
|| !e
->eb_site
) {
2425 content_error (NULL
, ct
, "missing %s parameter",
2426 e
->eb_name
? "site": "name");
2433 pidcheck (pidwait (xpid
, NOTOK
));
2437 /* Get the buffer ready to go */
2439 buflen
= sizeof(buffer
);
2442 * Construct the query message for user
2444 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2450 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2456 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2457 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2462 if (e
->eb_size
> 0) {
2463 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2468 snprintf (bp
, buflen
, "? ");
2471 * Now, check the answer
2473 if (!getanswer (buffer
))
2478 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2482 ruserpass (e
->eb_site
, &username
, &password
);
2487 ce
->ce_unlink
= (*file
== NULL
);
2489 cachefile
[0] = '\0';
2490 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2491 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2492 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2493 if (*file
== NULL
) {
2500 ce
->ce_file
= add (*file
, NULL
);
2502 ce
->ce_file
= add (cachefile
, NULL
);
2504 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2506 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2507 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2512 int child_id
, i
, vecp
;
2516 vec
[vecp
++] = r1bindex (ftp
, '/');
2517 vec
[vecp
++] = e
->eb_site
;
2520 vec
[vecp
++] = e
->eb_dir
;
2521 vec
[vecp
++] = e
->eb_name
;
2522 vec
[vecp
++] = ce
->ce_file
,
2523 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2524 ? "ascii" : "binary";
2529 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2533 adios ("fork", "unable to");
2537 close (fileno (ce
->ce_fp
));
2539 fprintf (stderr
, "unable to exec ");
2545 if (pidXwait (child_id
, NULL
)) {
2546 username
= password
= NULL
;
2556 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2561 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2562 if ((fp
= fopen (cachefile
, "w"))) {
2564 FILE *gp
= ce
->ce_fp
;
2566 fseek (gp
, 0L, SEEK_SET
);
2568 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2570 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2574 admonish (ce
->ce_file
, "error reading");
2579 admonish (cachefile
, "error writing");
2588 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2589 *file
= ce
->ce_file
;
2590 return fileno (ce
->ce_fp
);
2601 return init_encoding (ct
, openMail
);
2606 openMail (CT ct
, char **file
)
2608 int child_id
, fd
, i
, vecp
;
2610 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2611 struct exbody
*e
= ct
->c_ctexbody
;
2612 CE ce
= &ct
->c_cefile
;
2614 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2625 if (!e
->eb_server
) {
2626 content_error (NULL
, ct
, "missing server parameter");
2633 pidcheck (pidwait (xpid
, NOTOK
));
2637 /* Get buffer ready to go */
2639 buflen
= sizeof(buffer
);
2641 /* Now, construct query message */
2642 snprintf (bp
, buflen
, "Retrieve content");
2648 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2654 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2656 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2658 /* Now, check answer */
2659 if (!getanswer (buffer
))
2663 vec
[vecp
++] = r1bindex (mailproc
, '/');
2664 vec
[vecp
++] = e
->eb_server
;
2665 vec
[vecp
++] = "-subject";
2666 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2667 vec
[vecp
++] = "-body";
2668 vec
[vecp
++] = e
->eb_body
;
2671 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2675 advise ("fork", "unable to");
2679 execvp (mailproc
, vec
);
2680 fprintf (stderr
, "unable to exec ");
2686 if (pidXwait (child_id
, NULL
) == OK
)
2687 advise (NULL
, "request sent");
2691 if (*file
== NULL
) {
2692 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2695 ce
->ce_file
= add (*file
, NULL
);
2699 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2700 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2704 /* showproc is for mhshow and mhstore, though mhlist -debug
2705 * prints it, too. */
2707 free (ct
->c_showproc
);
2708 ct
->c_showproc
= add ("true", NULL
);
2710 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2711 *file
= ce
->ce_file
;
2712 return fileno (ce
->ce_fp
);
2723 return init_encoding (ct
, openURL
);
2728 openURL (CT ct
, char **file
)
2730 struct exbody
*e
= ct
->c_ctexbody
;
2731 CE ce
= &ct
->c_cefile
;
2732 char *urlprog
, *program
;
2733 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2734 int fd
, caching
, cachetype
;
2735 struct msgs_array args
= { 0, 0, NULL
};
2738 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2742 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2746 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2758 content_error(NULL
, ct
, "missing url parameter");
2765 pidcheck (pidwait (xpid
, NOTOK
));
2769 ce
->ce_unlink
= (*file
== NULL
);
2771 cachefile
[0] = '\0';
2773 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2774 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2775 if (*file
== NULL
) {
2782 ce
->ce_file
= add(*file
, NULL
);
2784 ce
->ce_file
= add(cachefile
, NULL
);
2786 ce
->ce_file
= add(m_mktemp(tmp
, NULL
, NULL
), NULL
);
2788 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2789 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2793 switch (child_id
= fork()) {
2795 adios ("fork", "unable to");
2799 argsplit_msgarg(&args
, urlprog
, &program
);
2800 app_msgarg(&args
, e
->eb_url
);
2801 app_msgarg(&args
, NULL
);
2802 dup2(fileno(ce
->ce_fp
), 1);
2803 close(fileno(ce
->ce_fp
));
2804 execvp(program
, args
.msgs
);
2805 fprintf(stderr
, "Unable to exec ");
2811 if (pidXwait(child_id
, NULL
)) {
2819 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2824 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2825 if ((fp
= fopen(cachefile
, "w"))) {
2827 FILE *gp
= ce
->ce_fp
;
2829 fseeko(gp
, 0, SEEK_SET
);
2831 while ((cc
= fread(buffer
, sizeof(*buffer
),
2832 sizeof(buffer
), gp
)) > 0)
2833 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2838 admonish(ce
->ce_file
, "error reading");
2846 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2847 *file
= ce
->ce_file
;
2852 readDigest (CT ct
, char *cp
)
2857 unsigned char *dp
, value
, *ep
;
2863 for (ep
= (dp
= ct
->c_digest
)
2864 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2869 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2871 fprintf (stderr
, "invalid BASE64 encoding\n");
2875 bits
|= value
<< bitno
;
2877 if ((bitno
-= 6) < 0) {
2878 if (dp
+ (3 - skip
) > ep
)
2879 goto invalid_digest
;
2880 *dp
++ = (bits
>> 16) & 0xff;
2882 *dp
++ = (bits
>> 8) & 0xff;
2884 *dp
++ = bits
& 0xff;
2894 goto self_delimiting
;
2899 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2909 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2917 fprintf (stderr
, "MD5 digest=");
2918 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2919 fprintf (stderr
, "%02x", *dp
& 0xff);
2920 fprintf (stderr
, "\n");
2927 /* Multipart parts might have content before the first subpart and/or
2928 after the last subpart that hasn't been stored anywhere else, so do
2931 get_leftover_mp_content (CT ct
, int before
/* or after */) {
2932 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2934 int found_boundary
= 0;
2935 char buffer
[BUFSIZ
];
2938 char *content
= NULL
;
2940 if (! m
) return NOTOK
;
2943 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2945 /* Isolate the beginning of this part to the beginning of the
2946 first subpart and save any content between them. */
2947 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2948 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2949 boundary
= concat ("--", m
->mp_start
, NULL
);
2951 struct part
*last_subpart
= NULL
;
2952 struct part
*subpart
;
2954 /* Go to the last subpart to get its end position. */
2955 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
2956 last_subpart
= subpart
;
2959 if (last_subpart
== NULL
) return NOTOK
;
2961 /* Isolate the end of the last subpart to the end of this part
2962 and save any content between them. */
2963 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
2964 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
2965 boundary
= concat ("--", m
->mp_stop
, NULL
);
2968 /* Back up by 1 to pick up the newline. */
2969 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
2970 read
+= strlen (buffer
);
2971 /* Don't look beyond beginning of first subpart (before) or
2972 next part (after). */
2973 if (read
> max
) buffer
[read
-max
] = '\0';
2976 if (! strcmp (buffer
, boundary
)) {
2980 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
2986 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
2988 char *old_content
= content
;
2989 content
= concat (content
, buffer
, NULL
);
2993 ? concat ("\n", buffer
, NULL
)
2994 : concat (buffer
, NULL
);
2999 if (found_boundary
|| read
> max
) break;
3001 if (read
> max
) break;
3005 /* Skip the newline if that's all there is. */
3009 /* Remove trailing newline, except at EOF. */
3010 if ((before
|| ! feof (ct
->c_fp
)) &&
3011 (cp
= content
+ strlen (content
)) > content
&&
3016 if (strlen (content
) > 1) {
3018 m
->mp_content_before
= content
;
3020 m
->mp_content_after
= content
;
3034 ct_type_str (int type
) {
3036 case CT_APPLICATION
:
3037 return "application";
3053 return "unknown_type";
3059 ct_subtype_str (int type
, int subtype
) {
3061 case CT_APPLICATION
:
3063 case APPLICATION_OCTETS
:
3065 case APPLICATION_POSTSCRIPT
:
3066 return "postscript";
3068 return "unknown_app_subtype";
3072 case MESSAGE_RFC822
:
3074 case MESSAGE_PARTIAL
:
3076 case MESSAGE_EXTERNAL
:
3079 return "unknown_msg_subtype";
3085 case MULTI_ALTERNATE
:
3086 return "alternative";
3089 case MULTI_PARALLEL
:
3092 return "unknown_multipart_subtype";
3103 return "unknown_text_subtype";
3106 return "unknown_type";
3111 /* Find the content type and InitFunc for the CT. */
3112 const struct str2init
*
3113 get_ct_init (int type
) {
3114 const struct str2init
*sp
;
3116 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3117 if (type
== sp
->si_val
) {
3126 ce_str (int encoding
) {
3131 return "quoted-printable";
3147 /* Find the content type and InitFunc for the content encoding method. */
3148 const struct str2init
*
3149 get_ce_method (const char *method
) {
3150 struct str2init
*sp
;
3152 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3153 if (! strcasecmp (method
, sp
->si_key
)) {
3162 parse_header_attrs (const char *filename
, int len
, char **header_attrp
, CI ci
,
3164 char **attr
= ci
->ci_attrs
;
3165 char *cp
= *header_attrp
;
3167 while (*cp
== ';') {
3168 char *dp
, *vp
, *up
, c
;
3170 /* Relies on knowledge of this declaration:
3171 * char *ci_attrs[NPARMS + 2];
3173 if (attr
>= ci
->ci_attrs
+ sizeof ci
->ci_attrs
/sizeof (char *) - 2) {
3175 "too many parameters in message %s's %s: field (%d max)",
3176 filename
, TYPE_FIELD
, NPARMS
);
3182 while (isspace ((unsigned char) *cp
))
3186 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3193 "extraneous trailing ';' in message %s's %s: "
3195 filename
, TYPE_FIELD
);
3200 /* down case the attribute name */
3201 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3202 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3203 *dp
= tolower ((unsigned char) *dp
);
3205 for (up
= dp
; isspace ((unsigned char) *dp
);)
3207 if (dp
== cp
|| *dp
!= '=') {
3209 "invalid parameter in message %s's %s: "
3210 "field\n%*.*sparameter %s (error detected at offset %d)",
3211 filename
, TYPE_FIELD
, len
, len
, "", cp
, dp
- cp
);
3216 vp
= (*attr
= add (cp
, NULL
)) + (up
- cp
);
3218 for (dp
++; isspace ((unsigned char) *dp
);)
3221 /* Now store the attribute value. */
3222 ci
->ci_values
[attr
- ci
->ci_attrs
] = vp
= *attr
+ (dp
- cp
);
3225 for (cp
= ++dp
, dp
= vp
;;) {
3226 switch (c
= *cp
++) {
3230 "invalid quoted-string in message %s's %s: "
3231 "field\n%*.*s(parameter %s)",
3232 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3238 if ((c
= *cp
++) == '\0')
3253 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
3259 "invalid parameter in message %s's %s: "
3260 "field\n%*.*s(parameter %s)",
3261 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3266 while (isspace ((unsigned char) *cp
))
3270 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {