3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
17 #include <h/mhparse.h>
23 extern pid_t xpid
; /* mhshowsbr.c */
26 extern int rcachesw
; /* mhcachesbr.c */
27 extern int wcachesw
; /* mhcachesbr.c */
29 int checksw
= 0; /* check Content-MD5 field */
32 * Directory to place temp files. This must
33 * be set before these routines are called.
38 * These are for mhfixmsg to:
39 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
41 * 2) Suppress the warning about bogus multipart content, and report it.
43 int skip_mp_cte_check
;
44 int suppress_bogus_mp_content_warning
;
48 * Structures for TEXT messages
50 struct k2v SubText
[] = {
51 { "plain", TEXT_PLAIN
},
52 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
53 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
54 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
57 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
60 * Structures for MULTIPART messages
62 struct k2v SubMultiPart
[] = {
63 { "mixed", MULTI_MIXED
},
64 { "alternative", MULTI_ALTERNATE
},
65 { "digest", MULTI_DIGEST
},
66 { "parallel", MULTI_PARALLEL
},
67 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
71 * Structures for MESSAGE messages
73 struct k2v SubMessage
[] = {
74 { "rfc822", MESSAGE_RFC822
},
75 { "partial", MESSAGE_PARTIAL
},
76 { "external-body", MESSAGE_EXTERNAL
},
77 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
81 * Structure for APPLICATION messages
83 struct k2v SubApplication
[] = {
84 { "octet-stream", APPLICATION_OCTETS
},
85 { "postscript", APPLICATION_POSTSCRIPT
},
86 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
91 int find_cache (CT
, int, int *, char *, char *, int);
94 int part_ok (CT
, int);
95 int type_ok (CT
, int);
96 void content_error (char *, CT
, char *, ...);
99 void free_encoding (CT
, int);
104 static CT
get_content (FILE *, char *, int);
105 static int get_comment (const char *, CI
, char **, int);
107 static int InitGeneric (CT
);
108 static int InitText (CT
);
109 static int InitMultiPart (CT
);
110 void reverse_parts (CT
);
111 static int InitMessage (CT
);
112 static int InitApplication (CT
);
113 static int init_encoding (CT
, OpenCEFunc
);
114 static unsigned long size_encoding (CT
);
115 static int InitBase64 (CT
);
116 static int openBase64 (CT
, char **);
117 static int InitQuoted (CT
);
118 static int openQuoted (CT
, char **);
119 static int Init7Bit (CT
);
120 static int openExternal (CT
, CT
, CE
, char **, int *);
121 static int InitFile (CT
);
122 static int openFile (CT
, char **);
123 static int InitFTP (CT
);
124 static int openFTP (CT
, char **);
125 static int InitMail (CT
);
126 static int openMail (CT
, char **);
127 static int readDigest (CT
, char *);
128 static int get_leftover_mp_content (CT
, int);
129 static int InitURL (CT
);
130 static int openURL (CT
, char **);
132 struct str2init str2cts
[] = {
133 { "application", CT_APPLICATION
, InitApplication
},
134 { "audio", CT_AUDIO
, InitGeneric
},
135 { "image", CT_IMAGE
, InitGeneric
},
136 { "message", CT_MESSAGE
, InitMessage
},
137 { "multipart", CT_MULTIPART
, InitMultiPart
},
138 { "text", CT_TEXT
, InitText
},
139 { "video", CT_VIDEO
, InitGeneric
},
140 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
141 { NULL
, CT_UNKNOWN
, NULL
},
144 struct str2init str2ces
[] = {
145 { "base64", CE_BASE64
, InitBase64
},
146 { "quoted-printable", CE_QUOTED
, InitQuoted
},
147 { "8bit", CE_8BIT
, Init7Bit
},
148 { "7bit", CE_7BIT
, Init7Bit
},
149 { "binary", CE_BINARY
, Init7Bit
},
150 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
151 { NULL
, CE_UNKNOWN
, NULL
},
155 * NOTE WELL: si_key MUST NOT have value of NOTOK
157 * si_key is 1 if access method is anonymous.
159 struct str2init str2methods
[] = {
160 { "afs", 1, InitFile
},
161 { "anon-ftp", 1, InitFTP
},
162 { "ftp", 0, InitFTP
},
163 { "local-file", 0, InitFile
},
164 { "mail-server", 0, InitMail
},
165 { "url", 0, InitURL
},
171 pidcheck (int status
)
173 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
184 * Main entry point for parsing a MIME message or file.
185 * It returns the Content structure for the top level
186 * entity in the file.
190 parse_mime (char *file
)
198 * Check if file is actually standard input
200 if ((is_stdin
= !(strcmp (file
, "-")))) {
201 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
203 advise("mhparse", "unable to create temporary file");
206 file
= add (tfile
, NULL
);
209 while (fgets (buffer
, sizeof(buffer
), stdin
))
213 if (ferror (stdin
)) {
215 advise ("stdin", "error reading");
220 advise (file
, "error writing");
223 fseek (fp
, 0L, SEEK_SET
);
224 } else if ((fp
= fopen (file
, "r")) == NULL
) {
225 advise (file
, "unable to read");
229 if (!(ct
= get_content (fp
, file
, 1))) {
232 advise (NULL
, "unable to decode %s", file
);
237 ct
->c_unlink
= 1; /* temp file to remove */
241 if (ct
->c_end
== 0L) {
242 fseek (fp
, 0L, SEEK_END
);
243 ct
->c_end
= ftell (fp
);
246 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
258 * Main routine for reading/parsing the headers
259 * of a message content.
261 * toplevel = 1 # we are at the top level of the message
262 * toplevel = 0 # we are inside message type or multipart type
263 * # other than multipart/digest
264 * toplevel = -1 # we are inside multipart/digest
265 * NB: on failure we will fclose(in)!
269 get_content (FILE *in
, char *file
, int toplevel
)
272 char buf
[BUFSIZ
], name
[NAMESZ
];
276 m_getfld_state_t gstate
= 0;
278 /* allocate the content structure */
279 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
280 adios (NULL
, "out of memory");
283 ct
->c_file
= add (file
, NULL
);
284 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
287 * Parse the header fields for this
288 * content into a linked list.
290 m_getfld_track_filepos (&gstate
, in
);
291 for (compnum
= 1;;) {
292 int bufsz
= sizeof buf
;
293 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
298 /* get copies of the buffers */
299 np
= add (name
, NULL
);
300 vp
= add (buf
, NULL
);
302 /* if necessary, get rest of field */
303 while (state
== FLDPLUS
) {
305 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
306 vp
= add (buf
, vp
); /* add to previous value */
309 /* Now add the header data to the list */
310 add_header (ct
, np
, vp
);
312 /* continue, to see if this isn't the last header field */
313 ct
->c_begin
= ftell (in
) + 1;
317 ct
->c_begin
= ftell (in
) - strlen (buf
);
321 ct
->c_begin
= ftell (in
);
326 adios (NULL
, "message format error in component #%d", compnum
);
329 adios (NULL
, "getfld() returned %d", state
);
332 /* break out of the loop */
335 m_getfld_state_destroy (&gstate
);
338 * Read the content headers. We will parse the
339 * MIME related header fields into their various
340 * structures and set internal flags related to
341 * content type/subtype, etc.
344 hp
= ct
->c_first_hf
; /* start at first header field */
346 /* Get MIME-Version field */
347 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
352 advise (NULL
, "message %s has multiple %s: fields",
353 ct
->c_file
, VRSN_FIELD
);
356 ct
->c_vrsn
= add (hp
->value
, NULL
);
358 /* Now, cleanup this field */
361 while (isspace ((unsigned char) *cp
))
363 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
365 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
366 if (!isspace ((unsigned char) *dp
))
370 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
373 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
)
376 for (dp
= cp
; istoken (*dp
); dp
++)
380 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
383 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
384 ct
->c_file
, VRSN_FIELD
, cp
);
387 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
388 /* Get Content-Type field */
389 struct str2init
*s2i
;
390 CI ci
= &ct
->c_ctinfo
;
392 /* Check if we've already seen a Content-Type header */
394 advise (NULL
, "message %s has multiple %s: fields",
395 ct
->c_file
, TYPE_FIELD
);
399 /* Parse the Content-Type field */
400 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
404 * Set the Init function and the internal
405 * flag for this content type.
407 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
408 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
410 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
412 ct
->c_type
= s2i
->si_val
;
413 ct
->c_ctinitfnx
= s2i
->si_init
;
415 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
416 /* Get Content-Transfer-Encoding field */
418 struct str2init
*s2i
;
421 * Check if we've already seen the
422 * Content-Transfer-Encoding field
425 advise (NULL
, "message %s has multiple %s: fields",
426 ct
->c_file
, ENCODING_FIELD
);
430 /* get copy of this field */
431 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
433 while (isspace ((unsigned char) *cp
))
435 for (dp
= cp
; istoken (*dp
); dp
++)
441 * Find the internal flag and Init function
442 * for this transfer encoding.
444 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
445 if (!strcasecmp (cp
, s2i
->si_key
))
447 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
450 ct
->c_encoding
= s2i
->si_val
;
452 /* Call the Init function for this encoding */
453 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
456 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
457 /* Get Content-MD5 field */
463 if (ct
->c_digested
) {
464 advise (NULL
, "message %s has multiple %s: fields",
465 ct
->c_file
, MD5_FIELD
);
469 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
471 while (isspace ((unsigned char) *cp
))
473 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
475 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
476 if (!isspace ((unsigned char) *dp
))
480 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
483 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
) {
488 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
496 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
497 /* Get Content-ID field */
498 ct
->c_id
= add (hp
->value
, ct
->c_id
);
500 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
501 /* Get Content-Description field */
502 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
504 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
505 /* Get Content-Disposition field */
506 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
510 hp
= hp
->next
; /* next header field */
514 * Check if we saw a Content-Type field.
515 * If not, then assign a default value for
516 * it, and the Init function.
520 * If we are inside a multipart/digest message,
521 * so default type is message/rfc822
524 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
526 ct
->c_type
= CT_MESSAGE
;
527 ct
->c_ctinitfnx
= InitMessage
;
530 * Else default type is text/plain
532 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
534 ct
->c_type
= CT_TEXT
;
535 ct
->c_ctinitfnx
= InitText
;
539 /* Use default Transfer-Encoding, if necessary */
541 ct
->c_encoding
= CE_7BIT
;
554 * small routine to add header field to list
558 add_header (CT ct
, char *name
, char *value
)
562 /* allocate header field structure */
563 hp
= mh_xmalloc (sizeof(*hp
));
565 /* link data into header structure */
570 /* link header structure into the list */
571 if (ct
->c_first_hf
== NULL
) {
572 ct
->c_first_hf
= hp
; /* this is the first */
575 ct
->c_last_hf
->next
= hp
; /* add it to the end */
583 /* Make sure that buf contains at least one appearance of name,
584 followed by =. If not, insert both name and value, just after
585 first semicolon, if any. Note that name should not contain a
586 trailing =. And quotes will be added around the value. Typical
587 usage: make sure that a Content-Disposition header contains
588 filename="foo". If it doesn't and value does, use value from
591 incl_name_value (char *buf
, char *name
, char *value
) {
594 /* Assume that name is non-null. */
596 char *name_plus_equal
= concat (name
, "=", NULL
);
598 if (! strstr (buf
, name_plus_equal
)) {
600 char *cp
, *prefix
, *suffix
;
602 /* Trim trailing space, esp. newline. */
603 for (cp
= &buf
[strlen (buf
) - 1];
604 cp
>= buf
&& isspace ((unsigned char) *cp
);
609 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
611 /* Insert at first semicolon, if any. If none, append to
613 prefix
= add (buf
, NULL
);
614 if ((cp
= strchr (prefix
, ';'))) {
615 suffix
= concat (cp
, NULL
);
617 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
621 newbuf
= concat (buf
, insertion
, "\n", NULL
);
629 free (name_plus_equal
);
635 /* Extract just name_suffix="foo", if any, from value. If there isn't
636 one, return the entire value. Note that, for example, a name_suffix
637 of name will match filename="foo", and return foo. */
639 extract_name_value (char *name_suffix
, char *value
) {
640 char *extracted_name_value
= value
;
641 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
642 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
645 free (name_suffix_plus_quote
);
646 if (name_suffix_equals
) {
647 char *name_suffix_begin
;
650 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
651 name_suffix_begin
= ++cp
;
652 /* Find second \". */
653 for (; *cp
!= '"'; ++cp
) /* empty */;
655 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
656 memcpy (extracted_name_value
,
658 cp
- name_suffix_begin
);
659 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
662 return extracted_name_value
;
666 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
667 * directives. Fills in the information of the CTinfo structure.
670 get_ctinfo (char *cp
, CT ct
, int magic
)
679 i
= strlen (invo_name
) + 2;
681 /* store copy of Content-Type line */
682 cp
= ct
->c_ctline
= add (cp
, NULL
);
684 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
687 /* change newlines to spaces */
688 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
691 /* trim trailing spaces */
692 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
693 if (!isspace ((unsigned char) *dp
))
698 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
700 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
703 for (dp
= cp
; istoken (*dp
); dp
++)
706 ci
->ci_type
= add (cp
, NULL
); /* store content type */
710 advise (NULL
, "invalid %s: field in message %s (empty type)",
711 TYPE_FIELD
, ct
->c_file
);
715 /* down case the content type string */
716 for (dp
= ci
->ci_type
; *dp
; dp
++)
717 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
718 *dp
= tolower ((unsigned char) *dp
);
720 while (isspace ((unsigned char) *cp
))
723 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
728 ci
->ci_subtype
= add ("", NULL
);
733 while (isspace ((unsigned char) *cp
))
736 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
739 for (dp
= cp
; istoken (*dp
); dp
++)
742 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
745 if (!*ci
->ci_subtype
) {
747 "invalid %s: field in message %s (empty subtype for \"%s\")",
748 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
752 /* down case the content subtype string */
753 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
754 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
755 *dp
= tolower ((unsigned char) *dp
);
758 while (isspace ((unsigned char) *cp
))
761 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
764 if (parse_header_attrs (ct
->c_file
, i
, &cp
, ci
, &status
) == NOTOK
) {
769 * Get any <Content-Id> given in buffer
771 if (magic
&& *cp
== '<') {
776 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
777 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
783 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
789 while (isspace ((unsigned char) *cp
))
794 * Get any [Content-Description] given in buffer.
796 if (magic
&& *cp
== '[') {
798 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
802 advise (NULL
, "invalid description in message %s", ct
->c_file
);
810 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
816 while (isspace ((unsigned char) *cp
))
821 * Get any {Content-Disposition} given in buffer.
823 if (magic
&& *cp
== '{') {
825 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
829 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
837 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
843 while (isspace ((unsigned char) *cp
))
848 * Check if anything is left over
852 ci
->ci_magic
= add (cp
, NULL
);
854 /* If there is a Content-Disposition header and it doesn't
855 have a *filename=, extract it from the magic contents.
856 The r1bindex call skips any leading directory
860 incl_name_value (ct
->c_dispo
,
862 r1bindex (extract_name_value ("name",
869 "extraneous information in message %s's %s: field\n%*.*s(%s)",
870 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
878 get_comment (const char *filename
, CI ci
, char **ap
, int istype
)
882 char c
, buffer
[BUFSIZ
], *dp
;
892 advise (NULL
, "invalid comment in message %s's %s: field",
893 filename
, istype
? TYPE_FIELD
: VRSN_FIELD
);
898 if ((c
= *cp
++) == '\0')
921 if ((dp
= ci
->ci_comment
)) {
922 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
925 ci
->ci_comment
= add (buffer
, NULL
);
929 while (isspace ((unsigned char) *cp
))
940 * Handles content types audio, image, and video.
941 * There's not much to do right here.
949 return OK
; /* not much to do here */
962 char **ap
, **ep
, *cp
;
965 CI ci
= &ct
->c_ctinfo
;
967 /* check for missing subtype */
968 if (!*ci
->ci_subtype
)
969 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
972 for (kv
= SubText
; kv
->kv_key
; kv
++)
973 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
975 ct
->c_subtype
= kv
->kv_value
;
977 /* allocate text character set structure */
978 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
979 adios (NULL
, "out of memory");
980 ct
->c_ctparams
= (void *) t
;
982 /* scan for charset parameter */
983 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
984 if (!strcasecmp (*ap
, "charset"))
987 /* check if content specified a character set */
990 t
->tx_charset
= CHARSET_SPECIFIED
;
992 t
->tx_charset
= CHARSET_UNSPECIFIED
;
996 * If we can not handle character set natively,
997 * then check profile for string to modify the
998 * terminal or display method.
1000 * termproc is for mhshow, though mhlist -debug prints it, too.
1002 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1003 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1004 if ((cp
= context_find (buffer
)))
1005 ct
->c_termproc
= getcpy (cp
);
1017 InitMultiPart (CT ct
)
1021 char *cp
, *dp
, **ap
, **ep
;
1022 char *bp
, buffer
[BUFSIZ
];
1023 struct multipart
*m
;
1025 struct part
*part
, **next
;
1026 CI ci
= &ct
->c_ctinfo
;
1031 * The encoding for multipart messages must be either
1032 * 7bit, 8bit, or binary (per RFC2045).
1034 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1035 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1036 /* Copy the Content-Transfer-Encoding header field body so we can
1037 remove any trailing whitespace and leading blanks from it. */
1038 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1040 bp
= cte
+ strlen (cte
) - 1;
1041 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1042 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1045 "\"%s/%s\" type in message %s must be encoded in\n"
1046 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1047 "is to\nmanually edit the file and change the \"%s\"\n"
1048 "Content-Transfer-Encoding to one of those. For now",
1049 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1056 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1057 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1059 ct
->c_subtype
= kv
->kv_value
;
1062 * Check for "boundary" parameter, which is
1063 * required for multipart messages.
1066 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1067 if (!strcasecmp (*ap
, "boundary")) {
1073 /* complain if boundary parameter is missing */
1076 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1077 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1081 /* allocate primary structure for multipart info */
1082 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1083 adios (NULL
, "out of memory");
1084 ct
->c_ctparams
= (void *) m
;
1086 /* check if boundary parameter contains only whitespace characters */
1087 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1090 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1091 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1095 /* remove trailing whitespace from boundary parameter */
1096 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1097 if (!isspace ((unsigned char) *dp
))
1101 /* record boundary separators */
1102 m
->mp_start
= concat (bp
, "\n", NULL
);
1103 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1105 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1106 advise (ct
->c_file
, "unable to open for reading");
1110 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1112 next
= &m
->mp_parts
;
1116 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1120 pos
+= strlen (buffer
);
1121 if (buffer
[0] != '-' || buffer
[1] != '-')
1124 if (strcmp (buffer
+ 2, m
->mp_start
))
1127 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1128 adios (NULL
, "out of memory");
1130 next
= &part
->mp_next
;
1132 if (!(p
= get_content (fp
, ct
->c_file
,
1133 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1140 fseek (fp
, pos
, SEEK_SET
);
1143 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1147 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1148 if (p
->c_end
< p
->c_begin
)
1149 p
->c_begin
= p
->c_end
;
1154 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1160 if (! suppress_bogus_mp_content_warning
) {
1161 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1163 bogus_mp_content
= 1;
1165 if (!inout
&& part
) {
1167 p
->c_end
= ct
->c_end
;
1169 if (p
->c_begin
>= p
->c_end
) {
1170 for (next
= &m
->mp_parts
; *next
!= part
;
1171 next
= &((*next
)->mp_next
))
1175 free ((char *) part
);
1180 /* reverse the order of the parts for multipart/alternative */
1181 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1185 * label all subparts with part number, and
1186 * then initialize the content of the subpart.
1191 char partnam
[BUFSIZ
];
1194 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1195 pp
= partnam
+ strlen (partnam
);
1200 for (part
= m
->mp_parts
, partnum
= 1; part
;
1201 part
= part
->mp_next
, partnum
++) {
1204 sprintf (pp
, "%d", partnum
);
1205 p
->c_partno
= add (partnam
, NULL
);
1207 /* initialize the content of the subparts */
1208 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1216 get_leftover_mp_content (ct
, 1);
1217 get_leftover_mp_content (ct
, 0);
1226 * reverse the order of the parts of a multipart/alternative
1230 reverse_parts (CT ct
)
1232 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1236 /* Reverse the order of its parts by walking the mp_parts list
1237 and pushing each node to the front. */
1238 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1239 next
= part
->mp_next
;
1240 part
->mp_next
= m
->mp_parts
;
1254 CI ci
= &ct
->c_ctinfo
;
1256 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1258 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1259 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1263 /* check for missing subtype */
1264 if (!*ci
->ci_subtype
)
1265 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1268 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1269 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1271 ct
->c_subtype
= kv
->kv_value
;
1273 switch (ct
->c_subtype
) {
1274 case MESSAGE_RFC822
:
1277 case MESSAGE_PARTIAL
:
1282 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1283 adios (NULL
, "out of memory");
1284 ct
->c_ctparams
= (void *) p
;
1286 /* scan for parameters "id", "number", and "total" */
1287 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1288 if (!strcasecmp (*ap
, "id")) {
1289 p
->pm_partid
= add (*ep
, NULL
);
1292 if (!strcasecmp (*ap
, "number")) {
1293 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1294 || p
->pm_partno
< 1) {
1297 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1298 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1299 ct
->c_file
, TYPE_FIELD
);
1304 if (!strcasecmp (*ap
, "total")) {
1305 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1314 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1316 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1317 ci
->ci_type
, ci
->ci_subtype
,
1318 ct
->c_file
, TYPE_FIELD
);
1324 case MESSAGE_EXTERNAL
:
1331 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1332 adios (NULL
, "out of memory");
1333 ct
->c_ctparams
= (void *) e
;
1336 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1337 advise (ct
->c_file
, "unable to open for reading");
1341 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1343 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1351 p
->c_ceopenfnx
= NULL
;
1352 if ((exresult
= params_external (ct
, 0)) != NOTOK
1353 && p
->c_ceopenfnx
== openMail
) {
1357 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1359 content_error (NULL
, ct
,
1360 "empty body for access-type=mail-server");
1364 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1365 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1367 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1369 adios ("failed", "fread");
1372 adios (NULL
, "unexpected EOF from fread");
1375 bp
+= cc
, size
-= cc
;
1382 p
->c_end
= p
->c_begin
;
1387 if (exresult
== NOTOK
)
1389 if (e
->eb_flags
== NOTOK
)
1392 switch (p
->c_type
) {
1397 if (p
->c_subtype
!= MESSAGE_RFC822
)
1401 e
->eb_partno
= ct
->c_partno
;
1403 (*p
->c_ctinitfnx
) (p
);
1418 params_external (CT ct
, int composing
)
1421 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1422 CI ci
= &ct
->c_ctinfo
;
1424 ct
->c_ceopenfnx
= NULL
;
1425 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1426 if (!strcasecmp (*ap
, "access-type")) {
1427 struct str2init
*s2i
;
1428 CT p
= e
->eb_content
;
1430 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1431 if (!strcasecmp (*ep
, s2i
->si_key
))
1435 e
->eb_flags
= NOTOK
;
1436 p
->c_encoding
= CE_EXTERNAL
;
1439 e
->eb_access
= s2i
->si_key
;
1440 e
->eb_flags
= s2i
->si_val
;
1441 p
->c_encoding
= CE_EXTERNAL
;
1443 /* Call the Init function for this external type */
1444 if ((*s2i
->si_init
)(p
) == NOTOK
)
1448 if (!strcasecmp (*ap
, "name")) {
1452 if (!strcasecmp (*ap
, "permission")) {
1453 e
->eb_permission
= *ep
;
1456 if (!strcasecmp (*ap
, "site")) {
1460 if (!strcasecmp (*ap
, "directory")) {
1464 if (!strcasecmp (*ap
, "mode")) {
1468 if (!strcasecmp (*ap
, "size")) {
1469 sscanf (*ep
, "%lu", &e
->eb_size
);
1472 if (!strcasecmp (*ap
, "server")) {
1476 if (!strcasecmp (*ap
, "subject")) {
1477 e
->eb_subject
= *ep
;
1480 if (!strcasecmp (*ap
, "url")) {
1482 * According to RFC 2017, we have to remove all whitespace from
1487 e
->eb_url
= u
= mh_xmalloc(strlen(*ep
) + 1);
1489 for (; *p
!= '\0'; p
++) {
1490 if (! isspace((unsigned char) *p
))
1497 if (composing
&& !strcasecmp (*ap
, "body")) {
1498 e
->eb_body
= getcpy (*ep
);
1503 if (!e
->eb_access
) {
1505 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1506 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1519 InitApplication (CT ct
)
1522 CI ci
= &ct
->c_ctinfo
;
1525 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1526 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1528 ct
->c_subtype
= kv
->kv_value
;
1535 * TRANSFER ENCODINGS
1539 init_encoding (CT ct
, OpenCEFunc openfnx
)
1541 ct
->c_ceopenfnx
= openfnx
;
1542 ct
->c_ceclosefnx
= close_encoding
;
1543 ct
->c_cesizefnx
= size_encoding
;
1550 close_encoding (CT ct
)
1552 CE ce
= &ct
->c_cefile
;
1561 static unsigned long
1562 size_encoding (CT ct
)
1567 CE ce
= &ct
->c_cefile
;
1570 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1571 return (long) st
.st_size
;
1574 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1575 return (long) st
.st_size
;
1580 if (ct
->c_encoding
== CE_EXTERNAL
)
1581 return (ct
->c_end
- ct
->c_begin
);
1584 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1585 return (ct
->c_end
- ct
->c_begin
);
1587 if (fstat (fd
, &st
) != NOTOK
)
1588 size
= (long) st
.st_size
;
1592 (*ct
->c_ceclosefnx
) (ct
);
1601 static unsigned char b642nib
[0x80] = {
1602 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
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, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1608 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1609 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1610 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1611 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1612 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1613 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1614 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1615 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1616 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1617 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1624 return init_encoding (ct
, openBase64
);
1629 openBase64 (CT ct
, char **file
)
1631 int bitno
, cc
, digested
;
1632 int fd
, len
, skip
, own_ct_fp
= 0;
1634 unsigned char value
, b
;
1635 char *cp
, *ep
, buffer
[BUFSIZ
];
1636 /* sbeck -- handle suffixes */
1638 CE ce
= &ct
->c_cefile
;
1642 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1647 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1648 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1654 if (*file
== NULL
) {
1655 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1658 ce
->ce_file
= add (*file
, NULL
);
1662 /* sbeck@cise.ufl.edu -- handle suffixes */
1664 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1665 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1666 cp
= context_find (buffer
);
1667 if (cp
== NULL
|| *cp
== '\0') {
1668 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1670 cp
= context_find (buffer
);
1672 if (cp
!= NULL
&& *cp
!= '\0') {
1673 if (ce
->ce_unlink
) {
1674 /* Temporary file already exists, so we rename to
1675 version with extension. */
1676 char *file_org
= strdup(ce
->ce_file
);
1677 ce
->ce_file
= add (cp
, ce
->ce_file
);
1678 if (rename(file_org
, ce
->ce_file
)) {
1679 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1684 ce
->ce_file
= add (cp
, ce
->ce_file
);
1688 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1689 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1693 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1694 adios (NULL
, "internal error(1)");
1697 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1698 content_error (ct
->c_file
, ct
, "unable to open for reading");
1704 if ((digested
= ct
->c_digested
))
1705 MD5Init (&mdContext
);
1711 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1713 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1715 content_error (ct
->c_file
, ct
, "error reading from");
1719 content_error (NULL
, ct
, "premature eof");
1727 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1730 if (isspace ((unsigned char) *cp
))
1732 if (skip
|| (((unsigned char) *cp
) & 0x80)
1733 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1735 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1736 (unsigned char) *cp
,
1737 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1740 content_error (NULL
, ct
,
1741 "invalid BASE64 encoding -- continuing");
1745 bits
|= value
<< bitno
;
1747 if ((bitno
-= 6) < 0) {
1748 b
= (bits
>> 16) & 0xff;
1749 putc ((char) b
, ce
->ce_fp
);
1751 MD5Update (&mdContext
, &b
, 1);
1753 b
= (bits
>> 8) & 0xff;
1754 putc ((char) b
, ce
->ce_fp
);
1756 MD5Update (&mdContext
, &b
, 1);
1759 putc ((char) b
, ce
->ce_fp
);
1761 MD5Update (&mdContext
, &b
, 1);
1765 if (ferror (ce
->ce_fp
)) {
1766 content_error (ce
->ce_file
, ct
,
1767 "error writing to");
1770 bitno
= 18, bits
= 0L, skip
= 0;
1776 goto self_delimiting
;
1785 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1787 content_error (NULL
, ct
, "invalid BASE64 encoding");
1792 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1794 if (fflush (ce
->ce_fp
)) {
1795 content_error (ce
->ce_file
, ct
, "error writing to");
1800 unsigned char digest
[16];
1802 MD5Final (digest
, &mdContext
);
1803 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1804 sizeof(digest
) / sizeof(digest
[0])))
1805 content_error (NULL
, ct
,
1806 "content integrity suspect (digest mismatch) -- continuing");
1809 fprintf (stderr
, "content integrity confirmed\n");
1812 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1815 *file
= ce
->ce_file
;
1820 return fileno (ce
->ce_fp
);
1827 free_encoding (ct
, 0);
1836 static char hex2nib
[0x80] = {
1837 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1844 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1845 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1846 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1847 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1848 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1849 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1850 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1851 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1852 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1859 return init_encoding (ct
, openQuoted
);
1864 openQuoted (CT ct
, char **file
)
1866 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1868 char buffer
[BUFSIZ
];
1870 CE ce
= &ct
->c_cefile
;
1871 /* sbeck -- handle suffixes */
1876 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1881 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1882 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1888 if (*file
== NULL
) {
1889 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1892 ce
->ce_file
= add (*file
, NULL
);
1896 /* sbeck@cise.ufl.edu -- handle suffixes */
1898 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1899 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1900 cp
= context_find (buffer
);
1901 if (cp
== NULL
|| *cp
== '\0') {
1902 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1904 cp
= context_find (buffer
);
1906 if (cp
!= NULL
&& *cp
!= '\0') {
1907 if (ce
->ce_unlink
) {
1908 /* Temporary file already exists, so we rename to
1909 version with extension. */
1910 char *file_org
= strdup(ce
->ce_file
);
1911 ce
->ce_file
= add (cp
, ce
->ce_file
);
1912 if (rename(file_org
, ce
->ce_file
)) {
1913 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1918 ce
->ce_file
= add (cp
, ce
->ce_file
);
1922 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1923 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1927 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1928 adios (NULL
, "internal error(2)");
1931 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1932 content_error (ct
->c_file
, ct
, "unable to open for reading");
1938 if ((digested
= ct
->c_digested
))
1939 MD5Init (&mdContext
);
1946 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1948 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
1949 content_error (NULL
, ct
, "premature eof");
1953 if ((cc
= strlen (buffer
)) > len
)
1957 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
1958 if (!isspace ((unsigned char) *ep
))
1962 for (; cp
< ep
; cp
++) {
1964 /* in an escape sequence */
1966 /* at byte 1 of an escape sequence */
1967 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
1968 /* next is byte 2 */
1971 /* at byte 2 of an escape sequence */
1973 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
1974 putc (mask
, ce
->ce_fp
);
1976 MD5Update (&mdContext
, &mask
, 1);
1977 if (ferror (ce
->ce_fp
)) {
1978 content_error (ce
->ce_file
, ct
, "error writing to");
1981 /* finished escape sequence; next may be literal or a new
1982 * escape sequence */
1985 /* on to next byte */
1989 /* not in an escape sequence */
1991 /* starting an escape sequence, or invalid '='? */
1992 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
1993 /* "=\n" soft line break, eat the \n */
1997 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
1998 /* We don't have 2 bytes left, so this is an invalid
1999 * escape sequence; just show the raw bytes (below). */
2000 } else if (isxdigit ((unsigned char) cp
[1]) &&
2001 isxdigit ((unsigned char) cp
[2])) {
2002 /* Next 2 bytes are hex digits, making this a valid escape
2003 * sequence; let's decode it (above). */
2007 /* One or both of the next 2 is out of range, making this
2008 * an invalid escape sequence; just show the raw bytes
2013 /* Just show the raw byte. */
2014 putc (*cp
, ce
->ce_fp
);
2017 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2019 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2022 if (ferror (ce
->ce_fp
)) {
2023 content_error (ce
->ce_file
, ct
, "error writing to");
2029 content_error (NULL
, ct
,
2030 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2034 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2036 if (fflush (ce
->ce_fp
)) {
2037 content_error (ce
->ce_file
, ct
, "error writing to");
2042 unsigned char digest
[16];
2044 MD5Final (digest
, &mdContext
);
2045 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2046 sizeof(digest
) / sizeof(digest
[0])))
2047 content_error (NULL
, ct
,
2048 "content integrity suspect (digest mismatch) -- continuing");
2051 fprintf (stderr
, "content integrity confirmed\n");
2054 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2057 *file
= ce
->ce_file
;
2062 return fileno (ce
->ce_fp
);
2065 free_encoding (ct
, 0);
2081 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2084 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2090 open7Bit (CT ct
, char **file
)
2092 int cc
, fd
, len
, own_ct_fp
= 0;
2093 char buffer
[BUFSIZ
];
2094 /* sbeck -- handle suffixes */
2097 CE ce
= &ct
->c_cefile
;
2100 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2105 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2106 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2112 if (*file
== NULL
) {
2113 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2116 ce
->ce_file
= add (*file
, NULL
);
2120 /* sbeck@cise.ufl.edu -- handle suffixes */
2122 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2123 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2124 cp
= context_find (buffer
);
2125 if (cp
== NULL
|| *cp
== '\0') {
2126 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2128 cp
= context_find (buffer
);
2130 if (cp
!= NULL
&& *cp
!= '\0') {
2131 if (ce
->ce_unlink
) {
2132 /* Temporary file already exists, so we rename to
2133 version with extension. */
2134 char *file_org
= strdup(ce
->ce_file
);
2135 ce
->ce_file
= add (cp
, ce
->ce_file
);
2136 if (rename(file_org
, ce
->ce_file
)) {
2137 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2142 ce
->ce_file
= add (cp
, ce
->ce_file
);
2146 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2147 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2151 if (ct
->c_type
== CT_MULTIPART
) {
2153 CI ci
= &ct
->c_ctinfo
;
2156 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2157 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2158 + 1 + strlen (ci
->ci_subtype
);
2159 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2160 putc (';', ce
->ce_fp
);
2163 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2165 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2166 fputs ("\n\t", ce
->ce_fp
);
2169 putc (' ', ce
->ce_fp
);
2172 fprintf (ce
->ce_fp
, "%s", buffer
);
2176 if (ci
->ci_comment
) {
2177 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2178 fputs ("\n\t", ce
->ce_fp
);
2182 putc (' ', ce
->ce_fp
);
2185 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2188 fprintf (ce
->ce_fp
, "\n");
2190 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2192 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2194 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2195 fprintf (ce
->ce_fp
, "\n");
2198 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2199 adios (NULL
, "internal error(3)");
2202 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2203 content_error (ct
->c_file
, ct
, "unable to open for reading");
2209 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2211 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2213 content_error (ct
->c_file
, ct
, "error reading from");
2217 content_error (NULL
, ct
, "premature eof");
2225 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2226 if (ferror (ce
->ce_fp
)) {
2227 content_error (ce
->ce_file
, ct
, "error writing to");
2232 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2234 if (fflush (ce
->ce_fp
)) {
2235 content_error (ce
->ce_file
, ct
, "error writing to");
2239 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2242 *file
= ce
->ce_file
;
2247 return fileno (ce
->ce_fp
);
2250 free_encoding (ct
, 0);
2264 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2266 char cachefile
[BUFSIZ
];
2269 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2274 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2275 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2281 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2282 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2283 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2284 ce
->ce_file
= getcpy (cachefile
);
2288 admonish (cachefile
, "unable to fopen for reading");
2295 *file
= ce
->ce_file
;
2296 *fd
= fileno (ce
->ce_fp
);
2307 return init_encoding (ct
, openFile
);
2312 openFile (CT ct
, char **file
)
2315 char cachefile
[BUFSIZ
];
2316 struct exbody
*e
= ct
->c_ctexbody
;
2317 CE ce
= &ct
->c_cefile
;
2319 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2331 content_error (NULL
, ct
, "missing name parameter");
2335 ce
->ce_file
= getcpy (e
->eb_name
);
2338 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2339 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2343 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2344 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2345 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2349 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2350 if ((fp
= fopen (cachefile
, "w"))) {
2352 char buffer
[BUFSIZ
];
2353 FILE *gp
= ce
->ce_fp
;
2355 fseek (gp
, 0L, SEEK_SET
);
2357 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2359 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2363 admonish (ce
->ce_file
, "error reading");
2368 admonish (cachefile
, "error writing");
2376 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2377 *file
= ce
->ce_file
;
2378 return fileno (ce
->ce_fp
);
2388 return init_encoding (ct
, openFTP
);
2393 openFTP (CT ct
, char **file
)
2395 int cachetype
, caching
, fd
;
2397 char *bp
, *ftp
, *user
, *pass
;
2398 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2400 CE ce
= &ct
->c_cefile
;
2401 static char *username
= NULL
;
2402 static char *password
= NULL
;
2406 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2412 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2423 if (!e
->eb_name
|| !e
->eb_site
) {
2424 content_error (NULL
, ct
, "missing %s parameter",
2425 e
->eb_name
? "site": "name");
2432 pidcheck (pidwait (xpid
, NOTOK
));
2436 /* Get the buffer ready to go */
2438 buflen
= sizeof(buffer
);
2441 * Construct the query message for user
2443 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2449 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2455 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2456 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2461 if (e
->eb_size
> 0) {
2462 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2467 snprintf (bp
, buflen
, "? ");
2470 * Now, check the answer
2472 if (!getanswer (buffer
))
2477 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2481 ruserpass (e
->eb_site
, &username
, &password
);
2486 ce
->ce_unlink
= (*file
== NULL
);
2488 cachefile
[0] = '\0';
2489 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2490 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2491 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2492 if (*file
== NULL
) {
2499 ce
->ce_file
= add (*file
, NULL
);
2501 ce
->ce_file
= add (cachefile
, NULL
);
2503 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2505 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2506 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2511 int child_id
, i
, vecp
;
2515 vec
[vecp
++] = r1bindex (ftp
, '/');
2516 vec
[vecp
++] = e
->eb_site
;
2519 vec
[vecp
++] = e
->eb_dir
;
2520 vec
[vecp
++] = e
->eb_name
;
2521 vec
[vecp
++] = ce
->ce_file
,
2522 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2523 ? "ascii" : "binary";
2528 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2532 adios ("fork", "unable to");
2536 close (fileno (ce
->ce_fp
));
2538 fprintf (stderr
, "unable to exec ");
2544 if (pidXwait (child_id
, NULL
)) {
2545 username
= password
= NULL
;
2555 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2560 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2561 if ((fp
= fopen (cachefile
, "w"))) {
2563 FILE *gp
= ce
->ce_fp
;
2565 fseek (gp
, 0L, SEEK_SET
);
2567 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2569 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2573 admonish (ce
->ce_file
, "error reading");
2578 admonish (cachefile
, "error writing");
2587 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2588 *file
= ce
->ce_file
;
2589 return fileno (ce
->ce_fp
);
2600 return init_encoding (ct
, openMail
);
2605 openMail (CT ct
, char **file
)
2607 int child_id
, fd
, i
, vecp
;
2609 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2610 struct exbody
*e
= ct
->c_ctexbody
;
2611 CE ce
= &ct
->c_cefile
;
2613 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2624 if (!e
->eb_server
) {
2625 content_error (NULL
, ct
, "missing server parameter");
2632 pidcheck (pidwait (xpid
, NOTOK
));
2636 /* Get buffer ready to go */
2638 buflen
= sizeof(buffer
);
2640 /* Now, construct query message */
2641 snprintf (bp
, buflen
, "Retrieve content");
2647 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2653 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2655 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2657 /* Now, check answer */
2658 if (!getanswer (buffer
))
2662 vec
[vecp
++] = r1bindex (mailproc
, '/');
2663 vec
[vecp
++] = e
->eb_server
;
2664 vec
[vecp
++] = "-subject";
2665 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2666 vec
[vecp
++] = "-body";
2667 vec
[vecp
++] = e
->eb_body
;
2670 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2674 advise ("fork", "unable to");
2678 execvp (mailproc
, vec
);
2679 fprintf (stderr
, "unable to exec ");
2685 if (pidXwait (child_id
, NULL
) == OK
)
2686 advise (NULL
, "request sent");
2690 if (*file
== NULL
) {
2691 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2694 ce
->ce_file
= add (*file
, NULL
);
2698 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2699 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2703 /* showproc is for mhshow and mhstore, though mhlist -debug
2704 * prints it, too. */
2706 free (ct
->c_showproc
);
2707 ct
->c_showproc
= add ("true", NULL
);
2709 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2710 *file
= ce
->ce_file
;
2711 return fileno (ce
->ce_fp
);
2722 return init_encoding (ct
, openURL
);
2727 openURL (CT ct
, char **file
)
2729 struct exbody
*e
= ct
->c_ctexbody
;
2730 CE ce
= &ct
->c_cefile
;
2731 char *urlprog
, *program
;
2732 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2733 int fd
, caching
, cachetype
;
2734 struct msgs_array args
= { 0, 0, NULL
};
2737 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2741 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2745 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2757 content_error(NULL
, ct
, "missing url parameter");
2764 pidcheck (pidwait (xpid
, NOTOK
));
2768 ce
->ce_unlink
= (*file
== NULL
);
2770 cachefile
[0] = '\0';
2772 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2773 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2774 if (*file
== NULL
) {
2781 ce
->ce_file
= add(*file
, NULL
);
2783 ce
->ce_file
= add(cachefile
, NULL
);
2785 ce
->ce_file
= add(m_mktemp(tmp
, NULL
, NULL
), NULL
);
2787 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2788 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2792 switch (child_id
= fork()) {
2794 adios ("fork", "unable to");
2798 argsplit_msgarg(&args
, urlprog
, &program
);
2799 app_msgarg(&args
, e
->eb_url
);
2800 app_msgarg(&args
, NULL
);
2801 dup2(fileno(ce
->ce_fp
), 1);
2802 close(fileno(ce
->ce_fp
));
2803 execvp(program
, args
.msgs
);
2804 fprintf(stderr
, "Unable to exec ");
2810 if (pidXwait(child_id
, NULL
)) {
2818 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2823 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2824 if ((fp
= fopen(cachefile
, "w"))) {
2826 FILE *gp
= ce
->ce_fp
;
2828 fseeko(gp
, 0, SEEK_SET
);
2830 while ((cc
= fread(buffer
, sizeof(*buffer
),
2831 sizeof(buffer
), gp
)) > 0)
2832 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2837 admonish(ce
->ce_file
, "error reading");
2845 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2846 *file
= ce
->ce_file
;
2851 readDigest (CT ct
, char *cp
)
2856 unsigned char *dp
, value
, *ep
;
2862 for (ep
= (dp
= ct
->c_digest
)
2863 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2868 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2870 fprintf (stderr
, "invalid BASE64 encoding\n");
2874 bits
|= value
<< bitno
;
2876 if ((bitno
-= 6) < 0) {
2877 if (dp
+ (3 - skip
) > ep
)
2878 goto invalid_digest
;
2879 *dp
++ = (bits
>> 16) & 0xff;
2881 *dp
++ = (bits
>> 8) & 0xff;
2883 *dp
++ = bits
& 0xff;
2893 goto self_delimiting
;
2898 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2908 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2916 fprintf (stderr
, "MD5 digest=");
2917 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2918 fprintf (stderr
, "%02x", *dp
& 0xff);
2919 fprintf (stderr
, "\n");
2926 /* Multipart parts might have content before the first subpart and/or
2927 after the last subpart that hasn't been stored anywhere else, so do
2930 get_leftover_mp_content (CT ct
, int before
/* or after */) {
2931 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2933 int found_boundary
= 0;
2934 char buffer
[BUFSIZ
];
2937 char *content
= NULL
;
2939 if (! m
) return NOTOK
;
2942 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2944 /* Isolate the beginning of this part to the beginning of the
2945 first subpart and save any content between them. */
2946 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2947 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2948 boundary
= concat ("--", m
->mp_start
, NULL
);
2950 struct part
*last_subpart
= NULL
;
2951 struct part
*subpart
;
2953 /* Go to the last subpart to get its end position. */
2954 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
2955 last_subpart
= subpart
;
2958 if (last_subpart
== NULL
) return NOTOK
;
2960 /* Isolate the end of the last subpart to the end of this part
2961 and save any content between them. */
2962 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
2963 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
2964 boundary
= concat ("--", m
->mp_stop
, NULL
);
2967 /* Back up by 1 to pick up the newline. */
2968 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
2969 read
+= strlen (buffer
);
2970 /* Don't look beyond beginning of first subpart (before) or
2971 next part (after). */
2972 if (read
> max
) buffer
[read
-max
] = '\0';
2975 if (! strcmp (buffer
, boundary
)) {
2979 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
2985 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
2987 char *old_content
= content
;
2988 content
= concat (content
, buffer
, NULL
);
2992 ? concat ("\n", buffer
, NULL
)
2993 : concat (buffer
, NULL
);
2998 if (found_boundary
|| read
> max
) break;
3000 if (read
> max
) break;
3004 /* Skip the newline if that's all there is. */
3008 /* Remove trailing newline, except at EOF. */
3009 if ((before
|| ! feof (ct
->c_fp
)) &&
3010 (cp
= content
+ strlen (content
)) > content
&&
3015 if (strlen (content
) > 1) {
3017 m
->mp_content_before
= content
;
3019 m
->mp_content_after
= content
;
3033 ct_type_str (int type
) {
3035 case CT_APPLICATION
:
3036 return "application";
3052 return "unknown_type";
3058 ct_subtype_str (int type
, int subtype
) {
3060 case CT_APPLICATION
:
3062 case APPLICATION_OCTETS
:
3064 case APPLICATION_POSTSCRIPT
:
3065 return "postscript";
3067 return "unknown_app_subtype";
3071 case MESSAGE_RFC822
:
3073 case MESSAGE_PARTIAL
:
3075 case MESSAGE_EXTERNAL
:
3078 return "unknown_msg_subtype";
3084 case MULTI_ALTERNATE
:
3085 return "alternative";
3088 case MULTI_PARALLEL
:
3091 return "unknown_multipart_subtype";
3102 return "unknown_text_subtype";
3105 return "unknown_type";
3110 /* Find the content type and InitFunc for the CT. */
3111 const struct str2init
*
3112 get_ct_init (int type
) {
3113 const struct str2init
*sp
;
3115 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3116 if (type
== sp
->si_val
) {
3125 ce_str (int encoding
) {
3130 return "quoted-printable";
3146 /* Find the content type and InitFunc for the content encoding method. */
3147 const struct str2init
*
3148 get_ce_method (const char *method
) {
3149 struct str2init
*sp
;
3151 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3152 if (! strcasecmp (method
, sp
->si_key
)) {
3161 parse_header_attrs (const char *filename
, int len
, char **header_attrp
, CI ci
,
3163 char **attr
= ci
->ci_attrs
;
3164 char *cp
= *header_attrp
;
3166 while (*cp
== ';') {
3167 char *dp
, *vp
, *up
, c
;
3169 /* Relies on knowledge of this declaration:
3170 * char *ci_attrs[NPARMS + 2];
3172 if (attr
>= ci
->ci_attrs
+ sizeof ci
->ci_attrs
/sizeof (char *) - 2) {
3174 "too many parameters in message %s's %s: field (%d max)",
3175 filename
, TYPE_FIELD
, NPARMS
);
3181 while (isspace ((unsigned char) *cp
))
3185 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3192 "extraneous trailing ';' in message %s's %s: "
3194 filename
, TYPE_FIELD
);
3199 /* down case the attribute name */
3200 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3201 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3202 *dp
= tolower ((unsigned char) *dp
);
3204 for (up
= dp
; isspace ((unsigned char) *dp
);)
3206 if (dp
== cp
|| *dp
!= '=') {
3208 "invalid parameter in message %s's %s: "
3209 "field\n%*.*sparameter %s (error detected at offset %d)",
3210 filename
, TYPE_FIELD
, len
, len
, "", cp
, dp
- cp
);
3215 vp
= (*attr
= add (cp
, NULL
)) + (up
- cp
);
3217 for (dp
++; isspace ((unsigned char) *dp
);)
3220 /* Now store the attribute value. */
3221 ci
->ci_values
[attr
- ci
->ci_attrs
] = vp
= *attr
+ (dp
- cp
);
3224 for (cp
= ++dp
, dp
= vp
;;) {
3225 switch (c
= *cp
++) {
3229 "invalid quoted-string in message %s's %s: "
3230 "field\n%*.*s(parameter %s)",
3231 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3237 if ((c
= *cp
++) == '\0')
3252 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
3258 "invalid parameter in message %s's %s: "
3259 "field\n%*.*s(parameter %s)",
3260 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3265 while (isspace ((unsigned char) *cp
))
3269 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {