3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
17 #include <h/mhparse.h>
23 extern pid_t xpid
; /* mhshowsbr.c */
26 extern int rcachesw
; /* mhcachesbr.c */
27 extern int wcachesw
; /* mhcachesbr.c */
29 int checksw
= 0; /* check Content-MD5 field */
32 * These are for mhfixmsg to:
33 * 1) Instruct parser not to detect invalid Content-Transfer-Encoding
35 * 2) Suppress the warning about bogus multipart content, and report it.
37 int skip_mp_cte_check
;
38 int suppress_bogus_mp_content_warning
;
42 * Structures for TEXT messages
44 struct k2v SubText
[] = {
45 { "plain", TEXT_PLAIN
},
46 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
47 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
48 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
51 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
54 * Structures for MULTIPART messages
56 struct k2v SubMultiPart
[] = {
57 { "mixed", MULTI_MIXED
},
58 { "alternative", MULTI_ALTERNATE
},
59 { "digest", MULTI_DIGEST
},
60 { "parallel", MULTI_PARALLEL
},
61 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
65 * Structures for MESSAGE messages
67 struct k2v SubMessage
[] = {
68 { "rfc822", MESSAGE_RFC822
},
69 { "partial", MESSAGE_PARTIAL
},
70 { "external-body", MESSAGE_EXTERNAL
},
71 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
75 * Structure for APPLICATION messages
77 struct k2v SubApplication
[] = {
78 { "octet-stream", APPLICATION_OCTETS
},
79 { "postscript", APPLICATION_POSTSCRIPT
},
80 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
84 * Mapping of names of CTE types in mhbuild directives
86 static struct k2v EncodingType
[] = {
90 { "quoted-printable", CE_QUOTED
},
92 { "base64", CE_BASE64
},
98 int find_cache (CT
, int, int *, char *, char *, int);
101 int part_ok (CT
, int);
102 int type_ok (CT
, int);
103 void content_error (char *, CT
, char *, ...);
106 void free_encoding (CT
, int);
111 static CT
get_content (FILE *, char *, int);
112 static int get_comment (const char *, CI
, char **, int);
114 static int InitGeneric (CT
);
115 static int InitText (CT
);
116 static int InitMultiPart (CT
);
117 void reverse_parts (CT
);
118 static int InitMessage (CT
);
119 static int InitApplication (CT
);
120 static int init_encoding (CT
, OpenCEFunc
);
121 static unsigned long size_encoding (CT
);
122 static int InitBase64 (CT
);
123 static int openBase64 (CT
, char **);
124 static int InitQuoted (CT
);
125 static int openQuoted (CT
, char **);
126 static int Init7Bit (CT
);
127 static int openExternal (CT
, CT
, CE
, char **, int *);
128 static int InitFile (CT
);
129 static int openFile (CT
, char **);
130 static int InitFTP (CT
);
131 static int openFTP (CT
, char **);
132 static int InitMail (CT
);
133 static int openMail (CT
, char **);
134 static int readDigest (CT
, char *);
135 static int get_leftover_mp_content (CT
, int);
136 static int InitURL (CT
);
137 static int openURL (CT
, char **);
138 static size_t param_len(PM
, int, size_t, int *);
140 struct str2init str2cts
[] = {
141 { "application", CT_APPLICATION
, InitApplication
},
142 { "audio", CT_AUDIO
, InitGeneric
},
143 { "image", CT_IMAGE
, InitGeneric
},
144 { "message", CT_MESSAGE
, InitMessage
},
145 { "multipart", CT_MULTIPART
, InitMultiPart
},
146 { "text", CT_TEXT
, InitText
},
147 { "video", CT_VIDEO
, InitGeneric
},
148 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
149 { NULL
, CT_UNKNOWN
, NULL
},
152 struct str2init str2ces
[] = {
153 { "base64", CE_BASE64
, InitBase64
},
154 { "quoted-printable", CE_QUOTED
, InitQuoted
},
155 { "8bit", CE_8BIT
, Init7Bit
},
156 { "7bit", CE_7BIT
, Init7Bit
},
157 { "binary", CE_BINARY
, Init7Bit
},
158 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
159 { NULL
, CE_UNKNOWN
, NULL
},
163 * NOTE WELL: si_key MUST NOT have value of NOTOK
165 * si_key is 1 if access method is anonymous.
167 struct str2init str2methods
[] = {
168 { "afs", 1, InitFile
},
169 { "anon-ftp", 1, InitFTP
},
170 { "ftp", 0, InitFTP
},
171 { "local-file", 0, InitFile
},
172 { "mail-server", 0, InitMail
},
173 { "url", 0, InitURL
},
179 pidcheck (int status
)
181 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
192 * Main entry point for parsing a MIME message or file.
193 * It returns the Content structure for the top level
194 * entity in the file.
198 parse_mime (char *file
)
206 * Check if file is actually standard input
208 if ((is_stdin
= !(strcmp (file
, "-")))) {
209 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
211 advise("mhparse", "unable to create temporary file in %s",
215 file
= add (tfile
, NULL
);
217 while (fgets (buffer
, sizeof(buffer
), stdin
))
221 if (ferror (stdin
)) {
222 (void) m_unlink (file
);
223 advise ("stdin", "error reading");
227 (void) m_unlink (file
);
228 advise (file
, "error writing");
231 fseek (fp
, 0L, SEEK_SET
);
232 } else if ((fp
= fopen (file
, "r")) == NULL
) {
233 advise (file
, "unable to read");
237 if (!(ct
= get_content (fp
, file
, 1))) {
239 (void) m_unlink (file
);
240 advise (NULL
, "unable to decode %s", file
);
245 ct
->c_unlink
= 1; /* temp file to remove */
249 if (ct
->c_end
== 0L) {
250 fseek (fp
, 0L, SEEK_END
);
251 ct
->c_end
= ftell (fp
);
254 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
266 * Main routine for reading/parsing the headers
267 * of a message content.
269 * toplevel = 1 # we are at the top level of the message
270 * toplevel = 0 # we are inside message type or multipart type
271 * # other than multipart/digest
272 * toplevel = -1 # we are inside multipart/digest
273 * NB: on failure we will fclose(in)!
277 get_content (FILE *in
, char *file
, int toplevel
)
280 char buf
[BUFSIZ
], name
[NAMESZ
];
284 m_getfld_state_t gstate
= 0;
286 /* allocate the content structure */
287 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
288 adios (NULL
, "out of memory");
291 ct
->c_file
= add (file
, NULL
);
292 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
295 * Parse the header fields for this
296 * content into a linked list.
298 m_getfld_track_filepos (&gstate
, in
);
299 for (compnum
= 1;;) {
300 int bufsz
= sizeof buf
;
301 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
306 /* get copies of the buffers */
307 np
= add (name
, NULL
);
308 vp
= add (buf
, NULL
);
310 /* if necessary, get rest of field */
311 while (state
== FLDPLUS
) {
313 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
314 vp
= add (buf
, vp
); /* add to previous value */
317 /* Now add the header data to the list */
318 add_header (ct
, np
, vp
);
320 /* continue, to see if this isn't the last header field */
321 ct
->c_begin
= ftell (in
) + 1;
325 ct
->c_begin
= ftell (in
) - strlen (buf
);
329 ct
->c_begin
= ftell (in
);
334 adios (NULL
, "message format error in component #%d", compnum
);
337 adios (NULL
, "getfld() returned %d", state
);
340 /* break out of the loop */
343 m_getfld_state_destroy (&gstate
);
346 * Read the content headers. We will parse the
347 * MIME related header fields into their various
348 * structures and set internal flags related to
349 * content type/subtype, etc.
352 hp
= ct
->c_first_hf
; /* start at first header field */
354 /* Get MIME-Version field */
355 if (!strcasecmp (hp
->name
, VRSN_FIELD
)) {
360 advise (NULL
, "message %s has multiple %s: fields",
361 ct
->c_file
, VRSN_FIELD
);
364 ct
->c_vrsn
= add (hp
->value
, NULL
);
366 /* Now, cleanup this field */
369 while (isspace ((unsigned char) *cp
))
371 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
373 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
374 if (!isspace ((unsigned char) *dp
))
378 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
381 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
)
384 for (dp
= cp
; istoken (*dp
); dp
++)
388 ucmp
= !strcasecmp (cp
, VRSN_VALUE
);
391 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
392 ct
->c_file
, VRSN_FIELD
, cp
);
395 else if (!strcasecmp (hp
->name
, TYPE_FIELD
)) {
396 /* Get Content-Type field */
397 struct str2init
*s2i
;
398 CI ci
= &ct
->c_ctinfo
;
400 /* Check if we've already seen a Content-Type header */
402 advise (NULL
, "message %s has multiple %s: fields",
403 ct
->c_file
, TYPE_FIELD
);
407 /* Parse the Content-Type field */
408 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
412 * Set the Init function and the internal
413 * flag for this content type.
415 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
416 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
418 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
420 ct
->c_type
= s2i
->si_val
;
421 ct
->c_ctinitfnx
= s2i
->si_init
;
423 else if (!strcasecmp (hp
->name
, ENCODING_FIELD
)) {
424 /* Get Content-Transfer-Encoding field */
426 struct str2init
*s2i
;
429 * Check if we've already seen the
430 * Content-Transfer-Encoding field
433 advise (NULL
, "message %s has multiple %s: fields",
434 ct
->c_file
, ENCODING_FIELD
);
438 /* get copy of this field */
439 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
441 while (isspace ((unsigned char) *cp
))
443 for (dp
= cp
; istoken (*dp
); dp
++)
449 * Find the internal flag and Init function
450 * for this transfer encoding.
452 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
453 if (!strcasecmp (cp
, s2i
->si_key
))
455 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
458 ct
->c_encoding
= s2i
->si_val
;
460 /* Call the Init function for this encoding */
461 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
464 else if (!strcasecmp (hp
->name
, MD5_FIELD
)) {
465 /* Get Content-MD5 field */
471 if (ct
->c_digested
) {
472 advise (NULL
, "message %s has multiple %s: fields",
473 ct
->c_file
, MD5_FIELD
);
477 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
479 while (isspace ((unsigned char) *cp
))
481 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
483 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
484 if (!isspace ((unsigned char) *dp
))
488 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
491 get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 0) == NOTOK
) {
496 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
504 else if (!strcasecmp (hp
->name
, ID_FIELD
)) {
505 /* Get Content-ID field */
506 ct
->c_id
= add (hp
->value
, ct
->c_id
);
508 else if (!strcasecmp (hp
->name
, DESCR_FIELD
)) {
509 /* Get Content-Description field */
510 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
512 else if (!strcasecmp (hp
->name
, DISPO_FIELD
)) {
513 /* Get Content-Disposition field */
514 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
518 hp
= hp
->next
; /* next header field */
522 * Check if we saw a Content-Type field.
523 * If not, then assign a default value for
524 * it, and the Init function.
528 * If we are inside a multipart/digest message,
529 * so default type is message/rfc822
532 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
534 ct
->c_type
= CT_MESSAGE
;
535 ct
->c_ctinitfnx
= InitMessage
;
538 * Else default type is text/plain
540 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
542 ct
->c_type
= CT_TEXT
;
543 ct
->c_ctinitfnx
= InitText
;
547 /* Use default Transfer-Encoding, if necessary */
549 ct
->c_encoding
= CE_7BIT
;
562 * small routine to add header field to list
566 add_header (CT ct
, char *name
, char *value
)
570 /* allocate header field structure */
571 hp
= mh_xmalloc (sizeof(*hp
));
573 /* link data into header structure */
578 /* link header structure into the list */
579 if (ct
->c_first_hf
== NULL
) {
580 ct
->c_first_hf
= hp
; /* this is the first */
583 ct
->c_last_hf
->next
= hp
; /* add it to the end */
591 /* Make sure that buf contains at least one appearance of name,
592 followed by =. If not, insert both name and value, just after
593 first semicolon, if any. Note that name should not contain a
594 trailing =. And quotes will be added around the value. Typical
595 usage: make sure that a Content-Disposition header contains
596 filename="foo". If it doesn't and value does, use value from
599 incl_name_value (char *buf
, char *name
, char *value
) {
602 /* Assume that name is non-null. */
604 char *name_plus_equal
= concat (name
, "=", NULL
);
606 if (! strstr (buf
, name_plus_equal
)) {
608 char *cp
, *prefix
, *suffix
;
610 /* Trim trailing space, esp. newline. */
611 for (cp
= &buf
[strlen (buf
) - 1];
612 cp
>= buf
&& isspace ((unsigned char) *cp
);
617 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
619 /* Insert at first semicolon, if any. If none, append to
621 prefix
= add (buf
, NULL
);
622 if ((cp
= strchr (prefix
, ';'))) {
623 suffix
= concat (cp
, NULL
);
625 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
629 newbuf
= concat (buf
, insertion
, "\n", NULL
);
637 free (name_plus_equal
);
643 /* Extract just name_suffix="foo", if any, from value. If there isn't
644 one, return the entire value. Note that, for example, a name_suffix
645 of name will match filename="foo", and return foo. */
647 extract_name_value (char *name_suffix
, char *value
) {
648 char *extracted_name_value
= value
;
649 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
650 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
653 free (name_suffix_plus_quote
);
654 if (name_suffix_equals
) {
655 char *name_suffix_begin
;
658 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
659 name_suffix_begin
= ++cp
;
660 /* Find second \". */
661 for (; *cp
!= '"'; ++cp
) /* empty */;
663 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
664 memcpy (extracted_name_value
,
666 cp
- name_suffix_begin
);
667 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
670 return extracted_name_value
;
674 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
675 * directives. Fills in the information of the CTinfo structure.
678 get_ctinfo (char *cp
, CT ct
, int magic
)
687 i
= strlen (invo_name
) + 2;
689 /* store copy of Content-Type line */
690 cp
= ct
->c_ctline
= add (cp
, NULL
);
692 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
695 /* change newlines to spaces */
696 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
699 /* trim trailing spaces */
700 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
701 if (!isspace ((unsigned char) *dp
))
706 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
708 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
711 for (dp
= cp
; istoken (*dp
); dp
++)
714 ci
->ci_type
= add (cp
, NULL
); /* store content type */
718 advise (NULL
, "invalid %s: field in message %s (empty type)",
719 TYPE_FIELD
, ct
->c_file
);
723 /* down case the content type string */
724 for (dp
= ci
->ci_type
; *dp
; dp
++)
725 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
726 *dp
= tolower ((unsigned char) *dp
);
728 while (isspace ((unsigned char) *cp
))
731 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
736 ci
->ci_subtype
= add ("", NULL
);
741 while (isspace ((unsigned char) *cp
))
744 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
747 for (dp
= cp
; istoken (*dp
); dp
++)
750 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
753 if (!*ci
->ci_subtype
) {
755 "invalid %s: field in message %s (empty subtype for \"%s\")",
756 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
760 /* down case the content subtype string */
761 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
762 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
763 *dp
= tolower ((unsigned char) *dp
);
766 while (isspace ((unsigned char) *cp
))
769 if (*cp
== '(' && get_comment (ct
->c_file
, &ct
->c_ctinfo
, &cp
, 1) == NOTOK
)
772 if (parse_header_attrs (ct
->c_file
, i
, &cp
, ci
, &status
) == NOTOK
) {
777 * Get any <Content-Id> given in buffer
779 if (magic
&& *cp
== '<') {
784 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
785 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
791 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
797 while (isspace ((unsigned char) *cp
))
802 * Get any [Content-Description] given in buffer.
804 if (magic
&& *cp
== '[') {
806 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
810 advise (NULL
, "invalid description in message %s", ct
->c_file
);
818 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
824 while (isspace ((unsigned char) *cp
))
829 * Get any {Content-Disposition} given in buffer.
831 if (magic
&& *cp
== '{') {
833 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
837 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
845 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
851 while (isspace ((unsigned char) *cp
))
856 * Get any extension directives (right now just the content transfer
857 * encoding, but maybe others) that we care about.
860 if (magic
&& *cp
== '*') {
862 * See if it's a CTE we match on
867 while (*cp
!= '\0' && ! isspace((unsigned char) *cp
))
871 advise (NULL
, "invalid null transfer encoding specification");
878 ct
->c_reqencoding
= CE_UNKNOWN
;
880 for (kv
= EncodingType
; kv
->kv_key
; kv
++) {
881 if (strcasecmp(kv
->kv_key
, dp
) == 0) {
882 ct
->c_reqencoding
= kv
->kv_value
;
887 if (ct
->c_reqencoding
== CE_UNKNOWN
) {
888 advise (NULL
, "invalid CTE specification: \"%s\"", dp
);
892 while (isspace ((unsigned char) *cp
))
897 * Check if anything is left over
901 ci
->ci_magic
= add (cp
, NULL
);
903 /* If there is a Content-Disposition header and it doesn't
904 have a *filename=, extract it from the magic contents.
905 The r1bindex call skips any leading directory
909 incl_name_value (ct
->c_dispo
,
911 r1bindex (extract_name_value ("name",
918 "extraneous information in message %s's %s: field\n%*.*s(%s)",
919 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
927 get_comment (const char *filename
, CI ci
, char **ap
, int istype
)
931 char c
, buffer
[BUFSIZ
], *dp
;
941 advise (NULL
, "invalid comment in message %s's %s: field",
942 filename
, istype
? TYPE_FIELD
: VRSN_FIELD
);
947 if ((c
= *cp
++) == '\0')
970 if ((dp
= ci
->ci_comment
)) {
971 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
974 ci
->ci_comment
= add (buffer
, NULL
);
978 while (isspace ((unsigned char) *cp
))
989 * Handles content types audio, image, and video.
990 * There's not much to do right here.
998 return OK
; /* not much to do here */
1009 char buffer
[BUFSIZ
];
1015 CI ci
= &ct
->c_ctinfo
;
1017 /* check for missing subtype */
1018 if (!*ci
->ci_subtype
)
1019 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1022 for (kv
= SubText
; kv
->kv_key
; kv
++)
1023 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1025 ct
->c_subtype
= kv
->kv_value
;
1027 /* allocate text character set structure */
1028 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1029 adios (NULL
, "out of memory");
1030 ct
->c_ctparams
= (void *) t
;
1032 /* scan for charset parameter */
1033 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
)
1034 if (!strcasecmp (pm
->pm_name
, "charset"))
1037 /* check if content specified a character set */
1039 chset
= pm
->pm_value
;
1040 t
->tx_charset
= CHARSET_SPECIFIED
;
1042 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1046 * If we can not handle character set natively,
1047 * then check profile for string to modify the
1048 * terminal or display method.
1050 * termproc is for mhshow, though mhlist -debug prints it, too.
1052 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1053 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1054 if ((cp
= context_find (buffer
)))
1055 ct
->c_termproc
= getcpy (cp
);
1067 InitMultiPart (CT ct
)
1073 char *bp
, buffer
[BUFSIZ
];
1074 struct multipart
*m
;
1076 struct part
*part
, **next
;
1077 CI ci
= &ct
->c_ctinfo
;
1082 * The encoding for multipart messages must be either
1083 * 7bit, 8bit, or binary (per RFC2045).
1085 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1086 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1087 /* Copy the Content-Transfer-Encoding header field body so we can
1088 remove any trailing whitespace and leading blanks from it. */
1089 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1091 bp
= cte
+ strlen (cte
) - 1;
1092 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1093 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1096 "\"%s/%s\" type in message %s must be encoded in\n"
1097 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1098 "is to\nmanually edit the file and change the \"%s\"\n"
1099 "Content-Transfer-Encoding to one of those. For now",
1100 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1107 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1108 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1110 ct
->c_subtype
= kv
->kv_value
;
1113 * Check for "boundary" parameter, which is
1114 * required for multipart messages.
1117 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1118 if (!strcasecmp (pm
->pm_name
, "boundary")) {
1124 /* complain if boundary parameter is missing */
1127 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1128 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1132 /* allocate primary structure for multipart info */
1133 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1134 adios (NULL
, "out of memory");
1135 ct
->c_ctparams
= (void *) m
;
1137 /* check if boundary parameter contains only whitespace characters */
1138 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1141 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1142 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1146 /* remove trailing whitespace from boundary parameter */
1147 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1148 if (!isspace ((unsigned char) *dp
))
1152 /* record boundary separators */
1153 m
->mp_start
= concat (bp
, "\n", NULL
);
1154 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1156 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1157 advise (ct
->c_file
, "unable to open for reading");
1161 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1163 next
= &m
->mp_parts
;
1167 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1171 pos
+= strlen (buffer
);
1172 if (buffer
[0] != '-' || buffer
[1] != '-')
1175 if (strcmp (buffer
+ 2, m
->mp_start
))
1178 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1179 adios (NULL
, "out of memory");
1181 next
= &part
->mp_next
;
1183 if (!(p
= get_content (fp
, ct
->c_file
,
1184 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1191 fseek (fp
, pos
, SEEK_SET
);
1194 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1198 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1199 if (p
->c_end
< p
->c_begin
)
1200 p
->c_begin
= p
->c_end
;
1205 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1211 if (! suppress_bogus_mp_content_warning
) {
1212 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1214 bogus_mp_content
= 1;
1216 if (!inout
&& part
) {
1218 p
->c_end
= ct
->c_end
;
1220 if (p
->c_begin
>= p
->c_end
) {
1221 for (next
= &m
->mp_parts
; *next
!= part
;
1222 next
= &((*next
)->mp_next
))
1226 free ((char *) part
);
1231 /* reverse the order of the parts for multipart/alternative */
1232 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1236 * label all subparts with part number, and
1237 * then initialize the content of the subpart.
1242 char partnam
[BUFSIZ
];
1245 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1246 pp
= partnam
+ strlen (partnam
);
1251 for (part
= m
->mp_parts
, partnum
= 1; part
;
1252 part
= part
->mp_next
, partnum
++) {
1255 sprintf (pp
, "%d", partnum
);
1256 p
->c_partno
= add (partnam
, NULL
);
1258 /* initialize the content of the subparts */
1259 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1267 get_leftover_mp_content (ct
, 1);
1268 get_leftover_mp_content (ct
, 0);
1277 * reverse the order of the parts of a multipart/alternative
1281 reverse_parts (CT ct
)
1283 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1287 /* Reverse the order of its parts by walking the mp_parts list
1288 and pushing each node to the front. */
1289 for (part
= m
->mp_parts
, m
->mp_parts
= NULL
; part
; part
= next
) {
1290 next
= part
->mp_next
;
1291 part
->mp_next
= m
->mp_parts
;
1305 CI ci
= &ct
->c_ctinfo
;
1307 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1309 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1310 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1314 /* check for missing subtype */
1315 if (!*ci
->ci_subtype
)
1316 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1319 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1320 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1322 ct
->c_subtype
= kv
->kv_value
;
1324 switch (ct
->c_subtype
) {
1325 case MESSAGE_RFC822
:
1328 case MESSAGE_PARTIAL
:
1333 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1334 adios (NULL
, "out of memory");
1335 ct
->c_ctparams
= (void *) p
;
1337 /* scan for parameters "id", "number", and "total" */
1338 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1339 if (!strcasecmp (pm
->pm_name
, "id")) {
1340 p
->pm_partid
= add (pm
->pm_value
, NULL
);
1343 if (!strcasecmp (pm
->pm_name
, "number")) {
1344 if (sscanf (pm
->pm_value
, "%d", &p
->pm_partno
) != 1
1345 || p
->pm_partno
< 1) {
1348 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1349 pm
->pm_name
, ci
->ci_type
, ci
->ci_subtype
,
1350 ct
->c_file
, TYPE_FIELD
);
1355 if (!strcasecmp (pm
->pm_name
, "total")) {
1356 if (sscanf (pm
->pm_value
, "%d", &p
->pm_maxno
) != 1
1365 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1367 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1368 ci
->ci_type
, ci
->ci_subtype
,
1369 ct
->c_file
, TYPE_FIELD
);
1375 case MESSAGE_EXTERNAL
:
1382 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1383 adios (NULL
, "out of memory");
1384 ct
->c_ctparams
= (void *) e
;
1387 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1388 advise (ct
->c_file
, "unable to open for reading");
1392 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1394 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1402 p
->c_ceopenfnx
= NULL
;
1403 if ((exresult
= params_external (ct
, 0)) != NOTOK
1404 && p
->c_ceopenfnx
== openMail
) {
1408 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1410 content_error (NULL
, ct
,
1411 "empty body for access-type=mail-server");
1415 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1416 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1418 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1420 adios ("failed", "fread");
1423 adios (NULL
, "unexpected EOF from fread");
1426 bp
+= cc
, size
-= cc
;
1433 p
->c_end
= p
->c_begin
;
1438 if (exresult
== NOTOK
)
1440 if (e
->eb_flags
== NOTOK
)
1443 switch (p
->c_type
) {
1448 if (p
->c_subtype
!= MESSAGE_RFC822
)
1452 e
->eb_partno
= ct
->c_partno
;
1454 (*p
->c_ctinitfnx
) (p
);
1469 params_external (CT ct
, int composing
)
1472 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1473 CI ci
= &ct
->c_ctinfo
;
1475 ct
->c_ceopenfnx
= NULL
;
1476 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
1477 if (!strcasecmp (pm
->pm_name
, "access-type")) {
1478 struct str2init
*s2i
;
1479 CT p
= e
->eb_content
;
1481 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1482 if (!strcasecmp (pm
->pm_value
, s2i
->si_key
))
1485 e
->eb_access
= pm
->pm_value
;
1486 e
->eb_flags
= NOTOK
;
1487 p
->c_encoding
= CE_EXTERNAL
;
1490 e
->eb_access
= s2i
->si_key
;
1491 e
->eb_flags
= s2i
->si_val
;
1492 p
->c_encoding
= CE_EXTERNAL
;
1494 /* Call the Init function for this external type */
1495 if ((*s2i
->si_init
)(p
) == NOTOK
)
1499 if (!strcasecmp (pm
->pm_name
, "name")) {
1500 e
->eb_name
= pm
->pm_value
;
1503 if (!strcasecmp (pm
->pm_name
, "permission")) {
1504 e
->eb_permission
= pm
->pm_value
;
1507 if (!strcasecmp (pm
->pm_name
, "site")) {
1508 e
->eb_site
= pm
->pm_value
;
1511 if (!strcasecmp (pm
->pm_name
, "directory")) {
1512 e
->eb_dir
= pm
->pm_value
;
1515 if (!strcasecmp (pm
->pm_name
, "mode")) {
1516 e
->eb_mode
= pm
->pm_value
;
1519 if (!strcasecmp (pm
->pm_name
, "size")) {
1520 sscanf (pm
->pm_value
, "%lu", &e
->eb_size
);
1523 if (!strcasecmp (pm
->pm_name
, "server")) {
1524 e
->eb_server
= pm
->pm_value
;
1527 if (!strcasecmp (pm
->pm_name
, "subject")) {
1528 e
->eb_subject
= pm
->pm_value
;
1531 if (!strcasecmp (pm
->pm_name
, "url")) {
1533 * According to RFC 2017, we have to remove all whitespace from
1537 char *u
, *p
= pm
->pm_value
;
1538 e
->eb_url
= u
= mh_xmalloc(strlen(pm
->pm_value
) + 1);
1540 for (; *p
!= '\0'; p
++) {
1541 if (! isspace((unsigned char) *p
))
1548 if (composing
&& !strcasecmp (pm
->pm_name
, "body")) {
1549 e
->eb_body
= getcpy (pm
->pm_value
);
1554 if (!e
->eb_access
) {
1556 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1557 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1570 InitApplication (CT ct
)
1573 CI ci
= &ct
->c_ctinfo
;
1576 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1577 if (!strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1579 ct
->c_subtype
= kv
->kv_value
;
1586 * TRANSFER ENCODINGS
1590 init_encoding (CT ct
, OpenCEFunc openfnx
)
1592 ct
->c_ceopenfnx
= openfnx
;
1593 ct
->c_ceclosefnx
= close_encoding
;
1594 ct
->c_cesizefnx
= size_encoding
;
1601 close_encoding (CT ct
)
1603 CE ce
= &ct
->c_cefile
;
1612 static unsigned long
1613 size_encoding (CT ct
)
1618 CE ce
= &ct
->c_cefile
;
1621 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1622 return (long) st
.st_size
;
1625 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1626 return (long) st
.st_size
;
1631 if (ct
->c_encoding
== CE_EXTERNAL
)
1632 return (ct
->c_end
- ct
->c_begin
);
1635 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1636 return (ct
->c_end
- ct
->c_begin
);
1638 if (fstat (fd
, &st
) != NOTOK
)
1639 size
= (long) st
.st_size
;
1643 (*ct
->c_ceclosefnx
) (ct
);
1652 static unsigned char b642nib
[0x80] = {
1653 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1654 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1655 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1656 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1657 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1658 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1659 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1660 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1661 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1662 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1663 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1664 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1665 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1666 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1667 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1668 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1675 return init_encoding (ct
, openBase64
);
1680 openBase64 (CT ct
, char **file
)
1682 int bitno
, cc
, digested
;
1683 int fd
, len
, skip
, own_ct_fp
= 0, text
= ct
->c_type
== CT_TEXT
;
1685 unsigned char value
, b
;
1686 char *cp
, *ep
, buffer
[BUFSIZ
];
1687 /* sbeck -- handle suffixes */
1689 CE ce
= &ct
->c_cefile
;
1693 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1698 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1699 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1705 if (*file
== NULL
) {
1708 ce
->ce_file
= add (*file
, NULL
);
1712 /* sbeck@cise.ufl.edu -- handle suffixes */
1714 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1715 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1716 cp
= context_find (buffer
);
1717 if (cp
== NULL
|| *cp
== '\0') {
1718 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1720 cp
= context_find (buffer
);
1722 if (cp
!= NULL
&& *cp
!= '\0') {
1723 if (ce
->ce_unlink
) {
1724 /* Create temporary file with filename extension. */
1725 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1726 adios(NULL
, "unable to create temporary file in %s",
1730 ce
->ce_file
= add (cp
, ce
->ce_file
);
1732 } else if (*file
== NULL
) {
1734 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1735 adios(NULL
, "unable to create temporary file in %s",
1738 ce
->ce_file
= add (tempfile
, NULL
);
1741 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1742 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1746 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1747 adios (NULL
, "internal error(1)");
1750 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1751 content_error (ct
->c_file
, ct
, "unable to open for reading");
1757 if ((digested
= ct
->c_digested
))
1758 MD5Init (&mdContext
);
1764 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1766 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1768 content_error (ct
->c_file
, ct
, "error reading from");
1772 content_error (NULL
, ct
, "premature eof");
1780 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1783 if (isspace ((unsigned char) *cp
))
1785 if (skip
|| (((unsigned char) *cp
) & 0x80)
1786 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1788 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1789 (unsigned char) *cp
,
1790 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1793 content_error (NULL
, ct
,
1794 "invalid BASE64 encoding -- continuing");
1798 bits
|= value
<< bitno
;
1800 if ((bitno
-= 6) < 0) {
1801 b
= (bits
>> 16) & 0xff;
1802 if (!text
|| b
!= '\r')
1803 putc ((char) b
, ce
->ce_fp
);
1805 MD5Update (&mdContext
, &b
, 1);
1807 b
= (bits
>> 8) & 0xff;
1808 if (! text
|| b
!= '\r')
1809 putc ((char) b
, ce
->ce_fp
);
1811 MD5Update (&mdContext
, &b
, 1);
1814 if (! text
|| b
!= '\r')
1815 putc ((char) b
, ce
->ce_fp
);
1817 MD5Update (&mdContext
, &b
, 1);
1821 if (ferror (ce
->ce_fp
)) {
1822 content_error (ce
->ce_file
, ct
,
1823 "error writing to");
1826 bitno
= 18, bits
= 0L, skip
= 0;
1832 goto self_delimiting
;
1841 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1843 content_error (NULL
, ct
, "invalid BASE64 encoding");
1848 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1850 if (fflush (ce
->ce_fp
)) {
1851 content_error (ce
->ce_file
, ct
, "error writing to");
1856 unsigned char digest
[16];
1858 MD5Final (digest
, &mdContext
);
1859 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1860 sizeof(digest
) / sizeof(digest
[0])))
1861 content_error (NULL
, ct
,
1862 "content integrity suspect (digest mismatch) -- continuing");
1865 fprintf (stderr
, "content integrity confirmed\n");
1868 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1871 *file
= ce
->ce_file
;
1876 return fileno (ce
->ce_fp
);
1883 free_encoding (ct
, 0);
1892 static char hex2nib
[0x80] = {
1893 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1894 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1895 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1896 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1898 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1899 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1900 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1901 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1902 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1905 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1915 return init_encoding (ct
, openQuoted
);
1920 openQuoted (CT ct
, char **file
)
1922 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1924 char buffer
[BUFSIZ
];
1926 CE ce
= &ct
->c_cefile
;
1927 /* sbeck -- handle suffixes */
1932 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1937 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1938 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1944 if (*file
== NULL
) {
1947 ce
->ce_file
= add (*file
, NULL
);
1951 /* sbeck@cise.ufl.edu -- handle suffixes */
1953 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1954 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1955 cp
= context_find (buffer
);
1956 if (cp
== NULL
|| *cp
== '\0') {
1957 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1959 cp
= context_find (buffer
);
1961 if (cp
!= NULL
&& *cp
!= '\0') {
1962 if (ce
->ce_unlink
) {
1963 /* Create temporary file with filename extension. */
1964 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
1965 adios(NULL
, "unable to create temporary file in %s",
1969 ce
->ce_file
= add (cp
, ce
->ce_file
);
1971 } else if (*file
== NULL
) {
1973 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
1974 adios(NULL
, "unable to create temporary file in %s",
1977 ce
->ce_file
= add (tempfile
, NULL
);
1980 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1981 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1985 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1986 adios (NULL
, "internal error(2)");
1989 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1990 content_error (ct
->c_file
, ct
, "unable to open for reading");
1996 if ((digested
= ct
->c_digested
))
1997 MD5Init (&mdContext
);
2004 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2006 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2007 content_error (NULL
, ct
, "premature eof");
2011 if ((cc
= strlen (buffer
)) > len
)
2015 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2016 if (!isspace ((unsigned char) *ep
))
2020 for (; cp
< ep
; cp
++) {
2022 /* in an escape sequence */
2024 /* at byte 1 of an escape sequence */
2025 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2026 /* next is byte 2 */
2029 /* at byte 2 of an escape sequence */
2031 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2032 putc (mask
, ce
->ce_fp
);
2034 MD5Update (&mdContext
, &mask
, 1);
2035 if (ferror (ce
->ce_fp
)) {
2036 content_error (ce
->ce_file
, ct
, "error writing to");
2039 /* finished escape sequence; next may be literal or a new
2040 * escape sequence */
2043 /* on to next byte */
2047 /* not in an escape sequence */
2049 /* starting an escape sequence, or invalid '='? */
2050 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2051 /* "=\n" soft line break, eat the \n */
2055 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2056 /* We don't have 2 bytes left, so this is an invalid
2057 * escape sequence; just show the raw bytes (below). */
2058 } else if (isxdigit ((unsigned char) cp
[1]) &&
2059 isxdigit ((unsigned char) cp
[2])) {
2060 /* Next 2 bytes are hex digits, making this a valid escape
2061 * sequence; let's decode it (above). */
2065 /* One or both of the next 2 is out of range, making this
2066 * an invalid escape sequence; just show the raw bytes
2071 /* Just show the raw byte. */
2072 putc (*cp
, ce
->ce_fp
);
2075 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2077 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2080 if (ferror (ce
->ce_fp
)) {
2081 content_error (ce
->ce_file
, ct
, "error writing to");
2087 content_error (NULL
, ct
,
2088 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2092 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2094 if (fflush (ce
->ce_fp
)) {
2095 content_error (ce
->ce_file
, ct
, "error writing to");
2100 unsigned char digest
[16];
2102 MD5Final (digest
, &mdContext
);
2103 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2104 sizeof(digest
) / sizeof(digest
[0])))
2105 content_error (NULL
, ct
,
2106 "content integrity suspect (digest mismatch) -- continuing");
2109 fprintf (stderr
, "content integrity confirmed\n");
2112 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2115 *file
= ce
->ce_file
;
2120 return fileno (ce
->ce_fp
);
2123 free_encoding (ct
, 0);
2139 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2142 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2148 open7Bit (CT ct
, char **file
)
2150 int cc
, fd
, len
, own_ct_fp
= 0;
2151 char buffer
[BUFSIZ
];
2152 /* sbeck -- handle suffixes */
2155 CE ce
= &ct
->c_cefile
;
2158 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2163 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2164 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2170 if (*file
== NULL
) {
2173 ce
->ce_file
= add (*file
, NULL
);
2177 /* sbeck@cise.ufl.edu -- handle suffixes */
2179 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2180 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2181 cp
= context_find (buffer
);
2182 if (cp
== NULL
|| *cp
== '\0') {
2183 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2185 cp
= context_find (buffer
);
2187 if (cp
!= NULL
&& *cp
!= '\0') {
2188 if (ce
->ce_unlink
) {
2189 /* Create temporary file with filename extension. */
2190 if ((ce
->ce_file
= m_mktemps(invo_name
, cp
, NULL
, NULL
)) == NULL
) {
2191 adios(NULL
, "unable to create temporary file in %s",
2195 ce
->ce_file
= add (cp
, ce
->ce_file
);
2197 } else if (*file
== NULL
) {
2199 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2200 adios(NULL
, "unable to create temporary file in %s",
2203 ce
->ce_file
= add (tempfile
, NULL
);
2206 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2207 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2211 if (ct
->c_type
== CT_MULTIPART
) {
2213 CI ci
= &ct
->c_ctinfo
;
2216 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2217 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2218 + 1 + strlen (ci
->ci_subtype
);
2219 for (pm
= ci
->ci_first_pm
; pm
; pm
= pm
->pm_next
) {
2220 putc (';', ce
->ce_fp
);
2223 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", , *ep
);
2225 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2226 fputs ("\n\t", ce
->ce_fp
);
2229 putc (' ', ce
->ce_fp
);
2232 fprintf (ce
->ce_fp
, "%s", buffer
);
2236 if (ci
->ci_comment
) {
2237 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2238 fputs ("\n\t", ce
->ce_fp
);
2242 putc (' ', ce
->ce_fp
);
2245 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2248 fprintf (ce
->ce_fp
, "\n");
2250 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2252 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2254 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2255 fprintf (ce
->ce_fp
, "\n");
2258 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2259 adios (NULL
, "internal error(3)");
2262 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2263 content_error (ct
->c_file
, ct
, "unable to open for reading");
2269 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2271 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2273 content_error (ct
->c_file
, ct
, "error reading from");
2277 content_error (NULL
, ct
, "premature eof");
2285 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2286 if (ferror (ce
->ce_fp
)) {
2287 content_error (ce
->ce_file
, ct
, "error writing to");
2292 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2294 if (fflush (ce
->ce_fp
)) {
2295 content_error (ce
->ce_file
, ct
, "error writing to");
2299 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2302 *file
= ce
->ce_file
;
2307 return fileno (ce
->ce_fp
);
2310 free_encoding (ct
, 0);
2324 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2326 char cachefile
[BUFSIZ
];
2329 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2334 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2335 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2341 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2342 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2343 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2344 ce
->ce_file
= getcpy (cachefile
);
2348 admonish (cachefile
, "unable to fopen for reading");
2355 *file
= ce
->ce_file
;
2356 *fd
= fileno (ce
->ce_fp
);
2367 return init_encoding (ct
, openFile
);
2372 openFile (CT ct
, char **file
)
2375 char cachefile
[BUFSIZ
];
2376 struct exbody
*e
= ct
->c_ctexbody
;
2377 CE ce
= &ct
->c_cefile
;
2379 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2391 content_error (NULL
, ct
, "missing name parameter");
2395 ce
->ce_file
= getcpy (e
->eb_name
);
2398 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2399 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2403 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2404 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2405 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2409 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2410 if ((fp
= fopen (cachefile
, "w"))) {
2412 char buffer
[BUFSIZ
];
2413 FILE *gp
= ce
->ce_fp
;
2415 fseek (gp
, 0L, SEEK_SET
);
2417 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2419 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2423 admonish (ce
->ce_file
, "error reading");
2424 (void) m_unlink (cachefile
);
2428 admonish (cachefile
, "error writing");
2429 (void) m_unlink (cachefile
);
2436 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2437 *file
= ce
->ce_file
;
2438 return fileno (ce
->ce_fp
);
2448 return init_encoding (ct
, openFTP
);
2453 openFTP (CT ct
, char **file
)
2455 int cachetype
, caching
, fd
;
2457 char *bp
, *ftp
, *user
, *pass
;
2458 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2460 CE ce
= &ct
->c_cefile
;
2461 static char *username
= NULL
;
2462 static char *password
= NULL
;
2466 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2472 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2483 if (!e
->eb_name
|| !e
->eb_site
) {
2484 content_error (NULL
, ct
, "missing %s parameter",
2485 e
->eb_name
? "site": "name");
2492 pidcheck (pidwait (xpid
, NOTOK
));
2496 /* Get the buffer ready to go */
2498 buflen
= sizeof(buffer
);
2501 * Construct the query message for user
2503 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2509 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2515 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2516 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2521 if (e
->eb_size
> 0) {
2522 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2527 snprintf (bp
, buflen
, "? ");
2530 * Now, check the answer
2532 if (!getanswer (buffer
))
2537 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2541 ruserpass (e
->eb_site
, &username
, &password
);
2546 ce
->ce_unlink
= (*file
== NULL
);
2548 cachefile
[0] = '\0';
2549 if ((!e
->eb_permission
|| strcasecmp (e
->eb_permission
, "read-write"))
2550 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2551 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2552 if (*file
== NULL
) {
2559 ce
->ce_file
= add (*file
, NULL
);
2561 ce
->ce_file
= add (cachefile
, NULL
);
2564 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2565 adios(NULL
, "unable to create temporary file in %s",
2568 ce
->ce_file
= add (tempfile
, NULL
);
2571 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2572 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2577 int child_id
, i
, vecp
;
2581 vec
[vecp
++] = r1bindex (ftp
, '/');
2582 vec
[vecp
++] = e
->eb_site
;
2585 vec
[vecp
++] = e
->eb_dir
;
2586 vec
[vecp
++] = e
->eb_name
;
2587 vec
[vecp
++] = ce
->ce_file
,
2588 vec
[vecp
++] = e
->eb_mode
&& !strcasecmp (e
->eb_mode
, "ascii")
2589 ? "ascii" : "binary";
2594 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2598 adios ("fork", "unable to");
2602 close (fileno (ce
->ce_fp
));
2604 fprintf (stderr
, "unable to exec ");
2610 if (pidXwait (child_id
, NULL
)) {
2611 username
= password
= NULL
;
2621 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2626 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2627 if ((fp
= fopen (cachefile
, "w"))) {
2629 FILE *gp
= ce
->ce_fp
;
2631 fseek (gp
, 0L, SEEK_SET
);
2633 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2635 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2639 admonish (ce
->ce_file
, "error reading");
2640 (void) m_unlink (cachefile
);
2644 admonish (cachefile
, "error writing");
2645 (void) m_unlink (cachefile
);
2653 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2654 *file
= ce
->ce_file
;
2655 return fileno (ce
->ce_fp
);
2666 return init_encoding (ct
, openMail
);
2671 openMail (CT ct
, char **file
)
2673 int child_id
, fd
, i
, vecp
;
2675 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2676 struct exbody
*e
= ct
->c_ctexbody
;
2677 CE ce
= &ct
->c_cefile
;
2679 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2690 if (!e
->eb_server
) {
2691 content_error (NULL
, ct
, "missing server parameter");
2698 pidcheck (pidwait (xpid
, NOTOK
));
2702 /* Get buffer ready to go */
2704 buflen
= sizeof(buffer
);
2706 /* Now, construct query message */
2707 snprintf (bp
, buflen
, "Retrieve content");
2713 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2719 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2721 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2723 /* Now, check answer */
2724 if (!getanswer (buffer
))
2728 vec
[vecp
++] = r1bindex (mailproc
, '/');
2729 vec
[vecp
++] = e
->eb_server
;
2730 vec
[vecp
++] = "-subject";
2731 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2732 vec
[vecp
++] = "-body";
2733 vec
[vecp
++] = e
->eb_body
;
2736 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2740 advise ("fork", "unable to");
2744 execvp (mailproc
, vec
);
2745 fprintf (stderr
, "unable to exec ");
2751 if (pidXwait (child_id
, NULL
) == OK
)
2752 advise (NULL
, "request sent");
2756 if (*file
== NULL
) {
2758 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2759 adios(NULL
, "unable to create temporary file in %s",
2762 ce
->ce_file
= add (tempfile
, NULL
);
2765 ce
->ce_file
= add (*file
, NULL
);
2769 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2770 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2774 /* showproc is for mhshow and mhstore, though mhlist -debug
2775 * prints it, too. */
2777 free (ct
->c_showproc
);
2778 ct
->c_showproc
= add ("true", NULL
);
2780 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2781 *file
= ce
->ce_file
;
2782 return fileno (ce
->ce_fp
);
2793 return init_encoding (ct
, openURL
);
2798 openURL (CT ct
, char **file
)
2800 struct exbody
*e
= ct
->c_ctexbody
;
2801 CE ce
= &ct
->c_cefile
;
2802 char *urlprog
, *program
;
2803 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2804 int fd
, caching
, cachetype
;
2805 struct msgs_array args
= { 0, 0, NULL
};
2808 if ((urlprog
= context_find(nmhaccessurl
)) && *urlprog
== '\0')
2812 content_error(NULL
, ct
, "No entry for nmh-access-url in profile");
2816 switch (openExternal(e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2828 content_error(NULL
, ct
, "missing url parameter");
2835 pidcheck (pidwait (xpid
, NOTOK
));
2839 ce
->ce_unlink
= (*file
== NULL
);
2841 cachefile
[0] = '\0';
2843 if (find_cache(NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2844 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2845 if (*file
== NULL
) {
2852 ce
->ce_file
= add(*file
, NULL
);
2854 ce
->ce_file
= add(cachefile
, NULL
);
2857 if ((tempfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
)) == NULL
) {
2858 adios(NULL
, "unable to create temporary file in %s",
2861 ce
->ce_file
= add (tempfile
, NULL
);
2864 if ((ce
->ce_fp
= fopen(ce
->ce_file
, "w+")) == NULL
) {
2865 content_error(ce
->ce_file
, ct
, "unable to fopen for read/writing");
2869 switch (child_id
= fork()) {
2871 adios ("fork", "unable to");
2875 argsplit_msgarg(&args
, urlprog
, &program
);
2876 app_msgarg(&args
, e
->eb_url
);
2877 app_msgarg(&args
, NULL
);
2878 dup2(fileno(ce
->ce_fp
), 1);
2879 close(fileno(ce
->ce_fp
));
2880 execvp(program
, args
.msgs
);
2881 fprintf(stderr
, "Unable to exec ");
2887 if (pidXwait(child_id
, NULL
)) {
2895 chmod(cachefile
, cachetype
? m_gmprot() : 0444);
2900 mask
= umask (cachetype
? ~m_gmprot() : 0222);
2901 if ((fp
= fopen(cachefile
, "w"))) {
2903 FILE *gp
= ce
->ce_fp
;
2905 fseeko(gp
, 0, SEEK_SET
);
2907 while ((cc
= fread(buffer
, sizeof(*buffer
),
2908 sizeof(buffer
), gp
)) > 0)
2909 fwrite(buffer
, sizeof(*buffer
), cc
, fp
);
2914 admonish(ce
->ce_file
, "error reading");
2915 (void) m_unlink (cachefile
);
2922 fseeko(ce
->ce_fp
, 0, SEEK_SET
);
2923 *file
= ce
->ce_file
;
2928 readDigest (CT ct
, char *cp
)
2933 unsigned char *dp
, value
, *ep
;
2939 for (ep
= (dp
= ct
->c_digest
)
2940 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2945 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2947 fprintf (stderr
, "invalid BASE64 encoding\n");
2951 bits
|= value
<< bitno
;
2953 if ((bitno
-= 6) < 0) {
2954 if (dp
+ (3 - skip
) > ep
)
2955 goto invalid_digest
;
2956 *dp
++ = (bits
>> 16) & 0xff;
2958 *dp
++ = (bits
>> 8) & 0xff;
2960 *dp
++ = bits
& 0xff;
2970 goto self_delimiting
;
2975 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2985 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2993 fprintf (stderr
, "MD5 digest=");
2994 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2995 fprintf (stderr
, "%02x", *dp
& 0xff);
2996 fprintf (stderr
, "\n");
3003 /* Multipart parts might have content before the first subpart and/or
3004 after the last subpart that hasn't been stored anywhere else, so do
3007 get_leftover_mp_content (CT ct
, int before
/* or after */) {
3008 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
3010 int found_boundary
= 0;
3011 char buffer
[BUFSIZ
];
3014 char *content
= NULL
;
3016 if (! m
) return NOTOK
;
3019 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
3021 /* Isolate the beginning of this part to the beginning of the
3022 first subpart and save any content between them. */
3023 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
3024 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
3025 boundary
= concat ("--", m
->mp_start
, NULL
);
3027 struct part
*last_subpart
= NULL
;
3028 struct part
*subpart
;
3030 /* Go to the last subpart to get its end position. */
3031 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
3032 last_subpart
= subpart
;
3035 if (last_subpart
== NULL
) return NOTOK
;
3037 /* Isolate the end of the last subpart to the end of this part
3038 and save any content between them. */
3039 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
3040 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
3041 boundary
= concat ("--", m
->mp_stop
, NULL
);
3044 /* Back up by 1 to pick up the newline. */
3045 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
3046 read
+= strlen (buffer
);
3047 /* Don't look beyond beginning of first subpart (before) or
3048 next part (after). */
3049 if (read
> max
) buffer
[read
-max
] = '\0';
3052 if (! strcmp (buffer
, boundary
)) {
3056 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
3062 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
3064 char *old_content
= content
;
3065 content
= concat (content
, buffer
, NULL
);
3069 ? concat ("\n", buffer
, NULL
)
3070 : concat (buffer
, NULL
);
3075 if (found_boundary
|| read
> max
) break;
3077 if (read
> max
) break;
3081 /* Skip the newline if that's all there is. */
3085 /* Remove trailing newline, except at EOF. */
3086 if ((before
|| ! feof (ct
->c_fp
)) &&
3087 (cp
= content
+ strlen (content
)) > content
&&
3092 if (strlen (content
) > 1) {
3094 m
->mp_content_before
= content
;
3096 m
->mp_content_after
= content
;
3110 ct_type_str (int type
) {
3112 case CT_APPLICATION
:
3113 return "application";
3129 return "unknown_type";
3135 ct_subtype_str (int type
, int subtype
) {
3137 case CT_APPLICATION
:
3139 case APPLICATION_OCTETS
:
3141 case APPLICATION_POSTSCRIPT
:
3142 return "postscript";
3144 return "unknown_app_subtype";
3148 case MESSAGE_RFC822
:
3150 case MESSAGE_PARTIAL
:
3152 case MESSAGE_EXTERNAL
:
3155 return "unknown_msg_subtype";
3161 case MULTI_ALTERNATE
:
3162 return "alternative";
3165 case MULTI_PARALLEL
:
3168 return "unknown_multipart_subtype";
3179 return "unknown_text_subtype";
3182 return "unknown_type";
3187 /* Find the content type and InitFunc for the CT. */
3188 const struct str2init
*
3189 get_ct_init (int type
) {
3190 const struct str2init
*sp
;
3192 for (sp
= str2cts
; sp
->si_key
; ++sp
) {
3193 if (type
== sp
->si_val
) {
3202 ce_str (int encoding
) {
3207 return "quoted-printable";
3223 /* Find the content type and InitFunc for the content encoding method. */
3224 const struct str2init
*
3225 get_ce_method (const char *method
) {
3226 struct str2init
*sp
;
3228 for (sp
= str2ces
; sp
->si_key
; ++sp
) {
3229 if (! strcasecmp (method
, sp
->si_key
)) {
3238 parse_header_attrs (const char *filename
, int len
, char **header_attrp
, CI ci
,
3240 char **attr
= ci
->ci_attrs
;
3241 char *cp
= *header_attrp
;
3243 while (*cp
== ';') {
3244 char *dp
, *vp
, *up
, c
;
3246 /* Relies on knowledge of this declaration:
3247 * char *ci_attrs[NPARMS + 2];
3249 if (attr
>= ci
->ci_attrs
+ sizeof ci
->ci_attrs
/sizeof (char *) - 2) {
3251 "too many parameters in message %s's %s: field (%d max)",
3252 filename
, TYPE_FIELD
, NPARMS
);
3258 while (isspace ((unsigned char) *cp
))
3262 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3269 "extraneous trailing ';' in message %s's %s: "
3271 filename
, TYPE_FIELD
);
3276 /* down case the attribute name */
3277 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
3278 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
3279 *dp
= tolower ((unsigned char) *dp
);
3281 for (up
= dp
; isspace ((unsigned char) *dp
);)
3283 if (dp
== cp
|| *dp
!= '=') {
3285 "invalid parameter in message %s's %s: "
3286 "field\n%*.*sparameter %s (error detected at offset %d)",
3287 filename
, TYPE_FIELD
, len
, len
, "", cp
, dp
- cp
);
3292 vp
= (*attr
= add (cp
, NULL
)) + (up
- cp
);
3294 for (dp
++; isspace ((unsigned char) *dp
);)
3297 /* Now store the attribute value. */
3298 ci
->ci_values
[attr
- ci
->ci_attrs
] = vp
= *attr
+ (dp
- cp
);
3301 for (cp
= ++dp
, dp
= vp
;;) {
3302 switch (c
= *cp
++) {
3306 "invalid quoted-string in message %s's %s: "
3307 "field\n%*.*s(parameter %s)",
3308 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3314 if ((c
= *cp
++) == '\0')
3329 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
3335 "invalid parameter in message %s's %s: "
3336 "field\n%*.*s(parameter %s)",
3337 filename
, TYPE_FIELD
, len
, len
, "", *attr
);
3342 while (isspace ((unsigned char) *cp
))
3346 get_comment (filename
, ci
, &cp
, 1) == NOTOK
) {
3359 * Create a string based on a list of output parameters. Assume that this
3360 * parameter string will be appended to an existing header, so start out
3361 * with the separator (;). Perform RFC 2231 encoding when necessary.
3365 output_params(size_t initialwidth
, PM params
)
3367 char *paramout
= NULL
;
3368 char line
[CPERLIN
+ 1], *q
;
3369 int curlen
, index
, eightbit
;
3372 while (params
!= NULL
) {
3377 curlen
= param_len(params
, index
, valoff
, &eightbit
);
3380 * If this won't fit on the line, start a new one. Save room in
3381 * case we need a semicolon on the end
3384 if (initialwidth
+ curlen
> CPERLIN
- 1) {
3385 paramout
= add(";\n\t", paramout
);
3390 * Loop until we get a parameter that fits within a line.
3393 while (initialwidth
+ curlen
> CPERLIN
- 1) {
3394 int curvallen
= strlen(params
->pm_value
+ valoff
) -
3395 (initialwidth
+ curlen
- (CPERLIN
- 1));
3398 * curvallen holds how many characters we take from this
3399 * current value. Make sure it's at least 1.
3405 params
= params
->pm_next
;
3412 * Calculate the size of a parameter. Include any necessary encoding.
3413 * Start the length computation from where "offset" is marked.
3417 param_len(PM pm
, int index
, size_t valueoff
, int *eightbit
)
3419 char *start
= pm
->pm_value
+ valueoff
, *p
;
3425 * Add up the length. First, start with the parameter name, and include
3429 len
+= strlen(pm
->pm_name
) + 1;
3432 * Scan the parameter value. If we find an 8-bit character, then
3433 * we need to compute the locale name for the length.
3436 for (p
= start
; *p
!= '\0'; p
++) {
3437 if (! isascii((unsigned char) *p
)) {
3444 * If we've got 8-bit character, put the locale on the front (if we're
3445 * doing part 0. Also compute the length of the string based on the
3446 * encoding we need to do.
3450 len
++; /* For the encoding we need to do */
3452 len
+= strlen(write_charset_8bit()) + 2; /* Plus extra '' */
3455 * We know we definitely need to include an index.
3456 * This will get the length wrong if we have more than 99
3457 * sections. I can live with that.
3459 len
+= 2; /* *<N> */
3463 for (p
= start
; *p
!= '\0'; p
++) {
3464 if (isparamencode(*p
))
3471 * Much simpler! Don't forget opening and closing quotes.
3473 len
+= strlen(start
) + 2;