]>
diplodocus.org Git - nmh/blob - uip/mhparse.c
3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
19 #include <h/mhparse.h>
25 extern pid_t xpid
; /* mhshowsbr.c */
28 extern int rcachesw
; /* mhcachesbr.c */
29 extern int wcachesw
; /* mhcachesbr.c */
31 int checksw
= 0; /* check Content-MD5 field */
34 * Directory to place temp files. This must
35 * be set before these routines are called.
40 * Structures for TEXT messages
42 struct k2v SubText
[] = {
43 { "plain", TEXT_PLAIN
},
44 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
45 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
46 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
49 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
52 * Structures for MULTIPART messages
54 struct k2v SubMultiPart
[] = {
55 { "mixed", MULTI_MIXED
},
56 { "alternative", MULTI_ALTERNATE
},
57 { "digest", MULTI_DIGEST
},
58 { "parallel", MULTI_PARALLEL
},
59 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
63 * Structures for MESSAGE messages
65 struct k2v SubMessage
[] = {
66 { "rfc822", MESSAGE_RFC822
},
67 { "partial", MESSAGE_PARTIAL
},
68 { "external-body", MESSAGE_EXTERNAL
},
69 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
73 * Structure for APPLICATION messages
75 struct k2v SubApplication
[] = {
76 { "octet-stream", APPLICATION_OCTETS
},
77 { "postscript", APPLICATION_POSTSCRIPT
},
78 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
83 int find_cache (CT
, int, int *, char *, char *, int);
86 int part_ok (CT
, int);
87 int type_ok (CT
, int);
88 void content_error (char *, CT
, char *, ...);
91 void free_content (CT
);
92 void free_encoding (CT
, int);
97 static CT
get_content (FILE *, char *, int);
98 static int get_comment (CT
, unsigned char **, int);
100 static int InitGeneric (CT
);
101 static int InitText (CT
);
102 static int InitMultiPart (CT
);
103 static void reverse_parts (CT
);
104 static int InitMessage (CT
);
105 static int InitApplication (CT
);
106 static int init_encoding (CT
, OpenCEFunc
);
107 static unsigned long size_encoding (CT
);
108 static int InitBase64 (CT
);
109 static int openBase64 (CT
, char **);
110 static int InitQuoted (CT
);
111 static int openQuoted (CT
, char **);
112 static int Init7Bit (CT
);
113 static int openExternal (CT
, CT
, CE
, char **, int *);
114 static int InitFile (CT
);
115 static int openFile (CT
, char **);
116 static int InitFTP (CT
);
117 static int openFTP (CT
, char **);
118 static int InitMail (CT
);
119 static int openMail (CT
, char **);
120 static int readDigest (CT
, char *);
122 struct str2init str2cts
[] = {
123 { "application", CT_APPLICATION
, InitApplication
},
124 { "audio", CT_AUDIO
, InitGeneric
},
125 { "image", CT_IMAGE
, InitGeneric
},
126 { "message", CT_MESSAGE
, InitMessage
},
127 { "multipart", CT_MULTIPART
, InitMultiPart
},
128 { "text", CT_TEXT
, InitText
},
129 { "video", CT_VIDEO
, InitGeneric
},
130 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
131 { NULL
, CT_UNKNOWN
, NULL
},
134 struct str2init str2ces
[] = {
135 { "base64", CE_BASE64
, InitBase64
},
136 { "quoted-printable", CE_QUOTED
, InitQuoted
},
137 { "8bit", CE_8BIT
, Init7Bit
},
138 { "7bit", CE_7BIT
, Init7Bit
},
139 { "binary", CE_BINARY
, Init7Bit
},
140 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
141 { NULL
, CE_UNKNOWN
, NULL
},
145 * NOTE WELL: si_key MUST NOT have value of NOTOK
147 * si_key is 1 if access method is anonymous.
149 struct str2init str2methods
[] = {
150 { "afs", 1, InitFile
},
151 { "anon-ftp", 1, InitFTP
},
152 { "ftp", 0, InitFTP
},
153 { "local-file", 0, InitFile
},
154 { "mail-server", 0, InitMail
},
160 pidcheck (int status
)
162 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
173 * Main entry point for parsing a MIME message or file.
174 * It returns the Content structure for the top level
175 * entity in the file.
179 parse_mime (char *file
)
187 * Check if file is actually standard input
189 if ((is_stdin
= !(strcmp (file
, "-")))) {
190 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
192 advise("mhparse", "unable to create temporary file");
195 file
= add (tfile
, NULL
);
198 while (fgets (buffer
, sizeof(buffer
), stdin
))
202 if (ferror (stdin
)) {
204 advise ("stdin", "error reading");
209 advise (file
, "error writing");
212 fseek (fp
, 0L, SEEK_SET
);
213 } else if ((fp
= fopen (file
, "r")) == NULL
) {
214 advise (file
, "unable to read");
218 if (!(ct
= get_content (fp
, file
, 1))) {
221 advise (NULL
, "unable to decode %s", file
);
226 ct
->c_unlink
= 1; /* temp file to remove */
230 if (ct
->c_end
== 0L) {
231 fseek (fp
, 0L, SEEK_END
);
232 ct
->c_end
= ftell (fp
);
235 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
247 * Main routine for reading/parsing the headers
248 * of a message content.
250 * toplevel = 1 # we are at the top level of the message
251 * toplevel = 0 # we are inside message type or multipart type
252 * # other than multipart/digest
253 * toplevel = -1 # we are inside multipart/digest
254 * NB: on failure we will fclose(in)!
258 get_content (FILE *in
, char *file
, int toplevel
)
261 char buf
[BUFSIZ
], name
[NAMESZ
];
267 /* allocate the content structure */
268 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
269 adios (NULL
, "out of memory");
272 ct
->c_file
= add (file
, NULL
);
273 ct
->c_begin
= (filepos
= ftell (ct
->c_fp
)) + 1;
276 * Parse the header fields for this
277 * content into a linked list.
279 for (compnum
= 1, state
= FLD
;;) {
280 int bufsz
= sizeof buf
;
281 switch (state
= m_getfld (state
, name
, buf
, &bufsz
, in
)) {
288 /* get copies of the buffers */
289 np
= add (name
, NULL
);
290 vp
= add (buf
, NULL
);
292 /* if necessary, get rest of field */
293 while (state
== FLDPLUS
) {
295 state
= m_getfld (state
, name
, buf
, &bufsz
, in
);
296 vp
= add (buf
, vp
); /* add to previous value */
299 /* Now add the header data to the list */
300 add_header (ct
, np
, vp
);
302 /* continue, if this isn't the last header field */
303 if (state
!= FLDEOF
) {
304 ct
->c_begin
= filepos
+ 1;
312 ct
->c_begin
= filepos
- strlen (buf
);
317 ct
->c_begin
= filepos
;
322 adios (NULL
, "message format error in component #%d", compnum
);
325 adios (NULL
, "getfld() returned %d", state
);
328 /* break out of the loop */
333 * Read the content headers. We will parse the
334 * MIME related header fields into their various
335 * structures and set internal flags related to
336 * content type/subtype, etc.
339 hp
= ct
->c_first_hf
; /* start at first header field */
341 /* Get MIME-Version field */
342 if (!mh_strcasecmp (hp
->name
, VRSN_FIELD
)) {
345 unsigned char *cp
, *dp
;
348 advise (NULL
, "message %s has multiple %s: fields",
349 ct
->c_file
, VRSN_FIELD
);
352 ct
->c_vrsn
= add (hp
->value
, NULL
);
354 /* Now, cleanup this field */
357 while (isspace (*cp
))
359 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
361 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
366 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
368 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
)
371 for (dp
= cp
; istoken (*dp
); dp
++)
375 ucmp
= !mh_strcasecmp (cp
, VRSN_VALUE
);
378 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
379 ct
->c_file
, VRSN_FIELD
, cp
);
382 else if (!mh_strcasecmp (hp
->name
, TYPE_FIELD
)) {
383 /* Get Content-Type field */
384 struct str2init
*s2i
;
385 CI ci
= &ct
->c_ctinfo
;
387 /* Check if we've already seen a Content-Type header */
389 advise (NULL
, "message %s has multiple %s: fields",
390 ct
->c_file
, TYPE_FIELD
);
394 /* Parse the Content-Type field */
395 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
399 * Set the Init function and the internal
400 * flag for this content type.
402 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
403 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
405 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
407 ct
->c_type
= s2i
->si_val
;
408 ct
->c_ctinitfnx
= s2i
->si_init
;
410 else if (!mh_strcasecmp (hp
->name
, ENCODING_FIELD
)) {
411 /* Get Content-Transfer-Encoding field */
413 unsigned char *cp
, *dp
;
414 struct str2init
*s2i
;
417 * Check if we've already seen the
418 * Content-Transfer-Encoding field
421 advise (NULL
, "message %s has multiple %s: fields",
422 ct
->c_file
, ENCODING_FIELD
);
426 /* get copy of this field */
427 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
429 while (isspace (*cp
))
431 for (dp
= cp
; istoken (*dp
); dp
++)
437 * Find the internal flag and Init function
438 * for this transfer encoding.
440 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
441 if (!mh_strcasecmp (cp
, s2i
->si_key
))
443 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
446 ct
->c_encoding
= s2i
->si_val
;
448 /* Call the Init function for this encoding */
449 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
452 else if (!mh_strcasecmp (hp
->name
, MD5_FIELD
)) {
453 /* Get Content-MD5 field */
454 unsigned char *cp
, *dp
;
460 if (ct
->c_digested
) {
461 advise (NULL
, "message %s has multiple %s: fields",
462 ct
->c_file
, MD5_FIELD
);
466 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
468 while (isspace (*cp
))
470 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
472 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
477 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
479 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
) {
484 for (dp
= cp
; *dp
&& !isspace (*dp
); dp
++)
492 else if (!mh_strcasecmp (hp
->name
, ID_FIELD
)) {
493 /* Get Content-ID field */
494 ct
->c_id
= add (hp
->value
, ct
->c_id
);
496 else if (!mh_strcasecmp (hp
->name
, DESCR_FIELD
)) {
497 /* Get Content-Description field */
498 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
500 else if (!mh_strcasecmp (hp
->name
, DISPO_FIELD
)) {
501 /* Get Content-Disposition field */
502 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
506 hp
= hp
->next
; /* next header field */
510 * Check if we saw a Content-Type field.
511 * If not, then assign a default value for
512 * it, and the Init function.
516 * If we are inside a multipart/digest message,
517 * so default type is message/rfc822
520 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
522 ct
->c_type
= CT_MESSAGE
;
523 ct
->c_ctinitfnx
= InitMessage
;
526 * Else default type is text/plain
528 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
530 ct
->c_type
= CT_TEXT
;
531 ct
->c_ctinitfnx
= InitText
;
535 /* Use default Transfer-Encoding, if necessary */
537 ct
->c_encoding
= CE_7BIT
;
550 * small routine to add header field to list
554 add_header (CT ct
, char *name
, char *value
)
558 /* allocate header field structure */
559 hp
= mh_xmalloc (sizeof(*hp
));
561 /* link data into header structure */
566 /* link header structure into the list */
567 if (ct
->c_first_hf
== NULL
) {
568 ct
->c_first_hf
= hp
; /* this is the first */
571 ct
->c_last_hf
->next
= hp
; /* add it to the end */
579 /* Make sure that buf contains at least one appearance of name,
580 followed by =. If not, insert both name and value, just after
581 first semicolon, if any. Note that name should not contain a
582 trailing =. And quotes will be added around the value. Typical
583 usage: make sure that a Content-Disposition header contains
584 filename="foo". If it doesn't and value does, use value from
587 incl_name_value (unsigned char *buf
, char *name
, char *value
) {
590 /* Assume that name is non-null. */
592 char *name_plus_equal
= concat (name
, "=", NULL
);
594 if (! strstr (buf
, name_plus_equal
)) {
597 char *prefix
, *suffix
;
599 /* Trim trailing space, esp. newline. */
600 for (cp
= &buf
[strlen (buf
) - 1];
601 cp
>= buf
&& isspace (*cp
);
606 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
608 /* Insert at first semicolon, if any. If none, append to
610 prefix
= add (buf
, NULL
);
611 if ((cp
= strchr (prefix
, ';'))) {
612 suffix
= concat (cp
, NULL
);
614 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
618 newbuf
= concat (buf
, insertion
, "\n", NULL
);
626 free (name_plus_equal
);
632 /* Extract just name_suffix="foo", if any, from value. If there isn't
633 one, return the entire value. Note that, for example, a name_suffix
634 of name will match filename="foo", and return foo. */
636 extract_name_value (char *name_suffix
, char *value
) {
637 char *extracted_name_value
= value
;
638 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
639 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
642 free (name_suffix_plus_quote
);
643 if (name_suffix_equals
) {
644 char *name_suffix_begin
;
647 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
648 name_suffix_begin
= ++cp
;
649 /* Find second \". */
650 for (; *cp
!= '"'; ++cp
) /* empty */;
652 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
653 memcpy (extracted_name_value
,
655 cp
- name_suffix_begin
);
656 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
659 return extracted_name_value
;
663 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
664 * directives. Fills in the information of the CTinfo structure.
667 get_ctinfo (unsigned char *cp
, CT ct
, int magic
)
676 i
= strlen (invo_name
) + 2;
678 /* store copy of Content-Type line */
679 cp
= ct
->c_ctline
= add (cp
, NULL
);
681 while (isspace (*cp
)) /* trim leading spaces */
684 /* change newlines to spaces */
685 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
688 /* trim trailing spaces */
689 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
695 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
697 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
700 for (dp
= cp
; istoken (*dp
); dp
++)
703 ci
->ci_type
= add (cp
, NULL
); /* store content type */
707 advise (NULL
, "invalid %s: field in message %s (empty type)",
708 TYPE_FIELD
, ct
->c_file
);
712 /* down case the content type string */
713 for (dp
= ci
->ci_type
; *dp
; dp
++)
714 if (isalpha(*dp
) && isupper (*dp
))
717 while (isspace (*cp
))
720 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
725 ci
->ci_subtype
= add ("", NULL
);
730 while (isspace (*cp
))
733 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
736 for (dp
= cp
; istoken (*dp
); dp
++)
739 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
742 if (!*ci
->ci_subtype
) {
744 "invalid %s: field in message %s (empty subtype for \"%s\")",
745 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
749 /* down case the content subtype string */
750 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
751 if (isalpha(*dp
) && isupper (*dp
))
755 while (isspace (*cp
))
758 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
762 * Parse attribute/value pairs given with Content-Type
764 ep
= (ap
= ci
->ci_attrs
) + NPARMS
;
771 "too many parameters in message %s's %s: field (%d max)",
772 ct
->c_file
, TYPE_FIELD
, NPARMS
);
777 while (isspace (*cp
))
780 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
785 "extraneous trailing ';' in message %s's %s: parameter list",
786 ct
->c_file
, TYPE_FIELD
);
790 /* down case the attribute name */
791 for (dp
= cp
; istoken (*dp
); dp
++)
792 if (isalpha(*dp
) && isupper (*dp
))
795 for (up
= dp
; isspace (*dp
);)
797 if (dp
== cp
|| *dp
!= '=') {
799 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
800 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
, dp
- cp
);
804 vp
= (*ap
= add (cp
, NULL
)) + (up
- cp
);
806 for (dp
++; isspace (*dp
);)
809 /* now add the attribute value */
810 ci
->ci_values
[ap
- ci
->ci_attrs
] = vp
= *ap
+ (dp
- cp
);
813 for (cp
= ++dp
, dp
= vp
;;) {
818 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
819 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
824 if ((c
= *cp
++) == '\0')
839 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
845 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
846 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
851 while (isspace (*cp
))
854 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
859 * Get any <Content-Id> given in buffer
861 if (magic
&& *cp
== '<') {
866 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
867 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
873 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
879 while (isspace (*cp
))
884 * Get any [Content-Description] given in buffer.
886 if (magic
&& *cp
== '[') {
888 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
892 advise (NULL
, "invalid description in message %s", ct
->c_file
);
900 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
906 while (isspace (*cp
))
911 * Get any {Content-Disposition} given in buffer.
913 if (magic
&& *cp
== '{') {
915 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
919 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
927 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
933 while (isspace (*cp
))
938 * Check if anything is left over
942 ci
->ci_magic
= add (cp
, NULL
);
944 /* If there is a Content-Disposition header and it doesn't
945 have a *filename=, extract it from the magic contents.
946 The r1bindex call skips any leading directory
950 incl_name_value (ct
->c_dispo
,
952 r1bindex (extract_name_value ("name",
959 "extraneous information in message %s's %s: field\n%*.*s(%s)",
960 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
968 get_comment (CT ct
, unsigned char **ap
, int istype
)
973 char c
, buffer
[BUFSIZ
], *dp
;
985 advise (NULL
, "invalid comment in message %s's %s: field",
986 ct
->c_file
, istype
? TYPE_FIELD
: VRSN_FIELD
);
991 if ((c
= *cp
++) == '\0')
1014 if ((dp
= ci
->ci_comment
)) {
1015 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
1018 ci
->ci_comment
= add (buffer
, NULL
);
1022 while (isspace (*cp
))
1033 * Handles content types audio, image, and video.
1034 * There's not much to do right here.
1042 return OK
; /* not much to do here */
1053 char buffer
[BUFSIZ
];
1055 char **ap
, **ep
, *cp
;
1058 CI ci
= &ct
->c_ctinfo
;
1060 /* check for missing subtype */
1061 if (!*ci
->ci_subtype
)
1062 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1065 for (kv
= SubText
; kv
->kv_key
; kv
++)
1066 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1068 ct
->c_subtype
= kv
->kv_value
;
1070 /* allocate text character set structure */
1071 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1072 adios (NULL
, "out of memory");
1073 ct
->c_ctparams
= (void *) t
;
1075 /* scan for charset parameter */
1076 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1077 if (!mh_strcasecmp (*ap
, "charset"))
1080 /* check if content specified a character set */
1083 t
->tx_charset
= CHARSET_SPECIFIED
;
1085 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1089 * If we can not handle character set natively,
1090 * then check profile for string to modify the
1091 * terminal or display method.
1093 * termproc is for mhshow, though mhlist -debug prints it, too.
1095 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1096 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1097 if ((cp
= context_find (buffer
)))
1098 ct
->c_termproc
= getcpy (cp
);
1110 InitMultiPart (CT ct
)
1114 unsigned char *cp
, *dp
;
1116 char *bp
, buffer
[BUFSIZ
];
1117 struct multipart
*m
;
1119 struct part
*part
, **next
;
1120 CI ci
= &ct
->c_ctinfo
;
1125 * The encoding for multipart messages must be either
1126 * 7bit, 8bit, or binary (per RFC2045).
1128 if (ct
->c_encoding
!= CE_7BIT
&& ct
->c_encoding
!= CE_8BIT
1129 && ct
->c_encoding
!= CE_BINARY
) {
1131 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1132 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1137 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1138 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1140 ct
->c_subtype
= kv
->kv_value
;
1143 * Check for "boundary" parameter, which is
1144 * required for multipart messages.
1147 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1148 if (!mh_strcasecmp (*ap
, "boundary")) {
1154 /* complain if boundary parameter is missing */
1157 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1158 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1162 /* allocate primary structure for multipart info */
1163 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1164 adios (NULL
, "out of memory");
1165 ct
->c_ctparams
= (void *) m
;
1167 /* check if boundary parameter contains only whitespace characters */
1168 for (cp
= bp
; isspace (*cp
); cp
++)
1171 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1172 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1176 /* remove trailing whitespace from boundary parameter */
1177 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1182 /* record boundary separators */
1183 m
->mp_start
= concat (bp
, "\n", NULL
);
1184 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1186 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1187 advise (ct
->c_file
, "unable to open for reading");
1191 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1193 next
= &m
->mp_parts
;
1197 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1201 pos
+= strlen (buffer
);
1202 if (buffer
[0] != '-' || buffer
[1] != '-')
1205 if (strcmp (buffer
+ 2, m
->mp_start
))
1208 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1209 adios (NULL
, "out of memory");
1211 next
= &part
->mp_next
;
1213 if (!(p
= get_content (fp
, ct
->c_file
,
1214 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1221 fseek (fp
, pos
, SEEK_SET
);
1224 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1228 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1229 if (p
->c_end
< p
->c_begin
)
1230 p
->c_begin
= p
->c_end
;
1235 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1241 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1242 if (!inout
&& part
) {
1244 p
->c_end
= ct
->c_end
;
1246 if (p
->c_begin
>= p
->c_end
) {
1247 for (next
= &m
->mp_parts
; *next
!= part
;
1248 next
= &((*next
)->mp_next
))
1252 free ((char *) part
);
1257 /* reverse the order of the parts for multipart/alternative */
1258 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1262 * label all subparts with part number, and
1263 * then initialize the content of the subpart.
1268 char partnam
[BUFSIZ
];
1271 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1272 pp
= partnam
+ strlen (partnam
);
1277 for (part
= m
->mp_parts
, partnum
= 1; part
;
1278 part
= part
->mp_next
, partnum
++) {
1281 sprintf (pp
, "%d", partnum
);
1282 p
->c_partno
= add (partnam
, NULL
);
1284 /* initialize the content of the subparts */
1285 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1300 * reverse the order of the parts of a multipart
1304 reverse_parts (CT ct
)
1307 struct multipart
*m
;
1308 struct part
**base
, **bmp
, **next
, *part
;
1310 m
= (struct multipart
*) ct
->c_ctparams
;
1312 /* if only one part, just return */
1313 if (!m
->mp_parts
|| !m
->mp_parts
->mp_next
)
1316 /* count number of parts */
1318 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1321 /* allocate array of pointers to the parts */
1322 if (!(base
= (struct part
**) calloc ((size_t) (i
+ 1), sizeof(*base
))))
1323 adios (NULL
, "out of memory");
1326 /* point at all the parts */
1327 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1331 /* reverse the order of the parts */
1332 next
= &m
->mp_parts
;
1333 for (bmp
--; bmp
>= base
; bmp
--) {
1336 next
= &part
->mp_next
;
1340 /* free array of pointers */
1341 free ((char *) base
);
1353 CI ci
= &ct
->c_ctinfo
;
1355 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1357 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1358 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1362 /* check for missing subtype */
1363 if (!*ci
->ci_subtype
)
1364 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1367 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1368 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1370 ct
->c_subtype
= kv
->kv_value
;
1372 switch (ct
->c_subtype
) {
1373 case MESSAGE_RFC822
:
1376 case MESSAGE_PARTIAL
:
1381 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1382 adios (NULL
, "out of memory");
1383 ct
->c_ctparams
= (void *) p
;
1385 /* scan for parameters "id", "number", and "total" */
1386 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1387 if (!mh_strcasecmp (*ap
, "id")) {
1388 p
->pm_partid
= add (*ep
, NULL
);
1391 if (!mh_strcasecmp (*ap
, "number")) {
1392 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1393 || p
->pm_partno
< 1) {
1396 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1397 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1398 ct
->c_file
, TYPE_FIELD
);
1403 if (!mh_strcasecmp (*ap
, "total")) {
1404 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1413 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1415 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1416 ci
->ci_type
, ci
->ci_subtype
,
1417 ct
->c_file
, TYPE_FIELD
);
1423 case MESSAGE_EXTERNAL
:
1430 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1431 adios (NULL
, "out of memory");
1432 ct
->c_ctparams
= (void *) e
;
1435 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1436 advise (ct
->c_file
, "unable to open for reading");
1440 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1442 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1450 if ((exresult
= params_external (ct
, 0)) != NOTOK
1451 && p
->c_ceopenfnx
== openMail
) {
1455 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1457 content_error (NULL
, ct
,
1458 "empty body for access-type=mail-server");
1462 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1463 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1465 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1467 adios ("failed", "fread");
1470 adios (NULL
, "unexpected EOF from fread");
1473 bp
+= cc
, size
-= cc
;
1480 p
->c_end
= p
->c_begin
;
1485 if (exresult
== NOTOK
)
1487 if (e
->eb_flags
== NOTOK
)
1490 switch (p
->c_type
) {
1495 if (p
->c_subtype
!= MESSAGE_RFC822
)
1499 e
->eb_partno
= ct
->c_partno
;
1501 (*p
->c_ctinitfnx
) (p
);
1516 params_external (CT ct
, int composing
)
1519 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1520 CI ci
= &ct
->c_ctinfo
;
1522 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1523 if (!mh_strcasecmp (*ap
, "access-type")) {
1524 struct str2init
*s2i
;
1525 CT p
= e
->eb_content
;
1527 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1528 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1532 e
->eb_flags
= NOTOK
;
1533 p
->c_encoding
= CE_EXTERNAL
;
1536 e
->eb_access
= s2i
->si_key
;
1537 e
->eb_flags
= s2i
->si_val
;
1538 p
->c_encoding
= CE_EXTERNAL
;
1540 /* Call the Init function for this external type */
1541 if ((*s2i
->si_init
)(p
) == NOTOK
)
1545 if (!mh_strcasecmp (*ap
, "name")) {
1549 if (!mh_strcasecmp (*ap
, "permission")) {
1550 e
->eb_permission
= *ep
;
1553 if (!mh_strcasecmp (*ap
, "site")) {
1557 if (!mh_strcasecmp (*ap
, "directory")) {
1561 if (!mh_strcasecmp (*ap
, "mode")) {
1565 if (!mh_strcasecmp (*ap
, "size")) {
1566 sscanf (*ep
, "%lu", &e
->eb_size
);
1569 if (!mh_strcasecmp (*ap
, "server")) {
1573 if (!mh_strcasecmp (*ap
, "subject")) {
1574 e
->eb_subject
= *ep
;
1577 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1578 e
->eb_body
= getcpy (*ep
);
1583 if (!e
->eb_access
) {
1585 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1586 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1599 InitApplication (CT ct
)
1602 CI ci
= &ct
->c_ctinfo
;
1605 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1606 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1608 ct
->c_subtype
= kv
->kv_value
;
1615 * TRANSFER ENCODINGS
1619 init_encoding (CT ct
, OpenCEFunc openfnx
)
1623 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
1624 adios (NULL
, "out of memory");
1627 ct
->c_ceopenfnx
= openfnx
;
1628 ct
->c_ceclosefnx
= close_encoding
;
1629 ct
->c_cesizefnx
= size_encoding
;
1636 close_encoding (CT ct
)
1640 if (!(ce
= ct
->c_cefile
))
1650 static unsigned long
1651 size_encoding (CT ct
)
1659 if (!(ce
= ct
->c_cefile
))
1660 return (ct
->c_end
- ct
->c_begin
);
1662 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1663 return (long) st
.st_size
;
1666 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1667 return (long) st
.st_size
;
1672 if (ct
->c_encoding
== CE_EXTERNAL
)
1673 return (ct
->c_end
- ct
->c_begin
);
1676 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1677 return (ct
->c_end
- ct
->c_begin
);
1679 if (fstat (fd
, &st
) != NOTOK
)
1680 size
= (long) st
.st_size
;
1684 (*ct
->c_ceclosefnx
) (ct
);
1693 static unsigned char b642nib
[0x80] = {
1694 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1695 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1696 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1697 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1698 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1699 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1700 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1701 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1702 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1703 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1704 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1705 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1706 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1707 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1708 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1709 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1716 return init_encoding (ct
, openBase64
);
1721 openBase64 (CT ct
, char **file
)
1723 int bitno
, cc
, digested
;
1724 int fd
, len
, skip
, own_ct_fp
= 0;
1726 unsigned char value
, b
;
1727 unsigned char *cp
, *ep
;
1728 char buffer
[BUFSIZ
];
1729 /* sbeck -- handle suffixes */
1736 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1741 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1742 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1748 if (*file
== NULL
) {
1749 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1752 ce
->ce_file
= add (*file
, NULL
);
1756 /* sbeck@cise.ufl.edu -- handle suffixes */
1758 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1759 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1760 cp
= context_find (buffer
);
1761 if (cp
== NULL
|| *cp
== '\0') {
1762 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1764 cp
= context_find (buffer
);
1766 if (cp
!= NULL
&& *cp
!= '\0') {
1767 if (ce
->ce_unlink
) {
1768 /* Temporary file already exists, so we rename to
1769 version with extension. */
1770 char *file_org
= strdup(ce
->ce_file
);
1771 ce
->ce_file
= add (cp
, ce
->ce_file
);
1772 if (rename(file_org
, ce
->ce_file
)) {
1773 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1778 ce
->ce_file
= add (cp
, ce
->ce_file
);
1782 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1783 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1787 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1788 adios (NULL
, "internal error(1)");
1791 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1792 content_error (ct
->c_file
, ct
, "unable to open for reading");
1798 if ((digested
= ct
->c_digested
))
1799 MD5Init (&mdContext
);
1805 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1807 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1809 content_error (ct
->c_file
, ct
, "error reading from");
1813 content_error (NULL
, ct
, "premature eof");
1821 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1826 if (skip
|| (*cp
& 0x80)
1827 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
1829 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1831 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1834 content_error (NULL
, ct
,
1835 "invalid BASE64 encoding -- continuing");
1839 bits
|= value
<< bitno
;
1841 if ((bitno
-= 6) < 0) {
1842 b
= (bits
>> 16) & 0xff;
1843 putc ((char) b
, ce
->ce_fp
);
1845 MD5Update (&mdContext
, &b
, 1);
1847 b
= (bits
>> 8) & 0xff;
1848 putc ((char) b
, ce
->ce_fp
);
1850 MD5Update (&mdContext
, &b
, 1);
1853 putc ((char) b
, ce
->ce_fp
);
1855 MD5Update (&mdContext
, &b
, 1);
1859 if (ferror (ce
->ce_fp
)) {
1860 content_error (ce
->ce_file
, ct
,
1861 "error writing to");
1864 bitno
= 18, bits
= 0L, skip
= 0;
1870 goto self_delimiting
;
1879 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1881 content_error (NULL
, ct
, "invalid BASE64 encoding");
1886 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1888 if (fflush (ce
->ce_fp
)) {
1889 content_error (ce
->ce_file
, ct
, "error writing to");
1894 unsigned char digest
[16];
1896 MD5Final (digest
, &mdContext
);
1897 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1898 sizeof(digest
) / sizeof(digest
[0])))
1899 content_error (NULL
, ct
,
1900 "content integrity suspect (digest mismatch) -- continuing");
1903 fprintf (stderr
, "content integrity confirmed\n");
1906 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1909 *file
= ce
->ce_file
;
1914 return fileno (ce
->ce_fp
);
1921 free_encoding (ct
, 0);
1930 static char hex2nib
[0x80] = {
1931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1938 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1953 return init_encoding (ct
, openQuoted
);
1958 openQuoted (CT ct
, char **file
)
1960 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1961 unsigned char *cp
, *ep
;
1962 char buffer
[BUFSIZ
];
1965 /* sbeck -- handle suffixes */
1971 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1976 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1977 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1983 if (*file
== NULL
) {
1984 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1987 ce
->ce_file
= add (*file
, NULL
);
1991 /* sbeck@cise.ufl.edu -- handle suffixes */
1993 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1994 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1995 cp
= context_find (buffer
);
1996 if (cp
== NULL
|| *cp
== '\0') {
1997 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1999 cp
= context_find (buffer
);
2001 if (cp
!= NULL
&& *cp
!= '\0') {
2002 if (ce
->ce_unlink
) {
2003 /* Temporary file already exists, so we rename to
2004 version with extension. */
2005 char *file_org
= strdup(ce
->ce_file
);
2006 ce
->ce_file
= add (cp
, ce
->ce_file
);
2007 if (rename(file_org
, ce
->ce_file
)) {
2008 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2013 ce
->ce_file
= add (cp
, ce
->ce_file
);
2017 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2018 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2022 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2023 adios (NULL
, "internal error(2)");
2026 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2027 content_error (ct
->c_file
, ct
, "unable to open for reading");
2033 if ((digested
= ct
->c_digested
))
2034 MD5Init (&mdContext
);
2041 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2043 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2044 content_error (NULL
, ct
, "premature eof");
2048 if ((cc
= strlen (buffer
)) > len
)
2052 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2057 for (; cp
< ep
; cp
++) {
2059 /* in an escape sequence */
2061 /* at byte 1 of an escape sequence */
2062 mask
= hex2nib
[*cp
& 0x7f];
2063 /* next is byte 2 */
2066 /* at byte 2 of an escape sequence */
2068 mask
|= hex2nib
[*cp
& 0x7f];
2069 putc (mask
, ce
->ce_fp
);
2071 MD5Update (&mdContext
, &mask
, 1);
2072 if (ferror (ce
->ce_fp
)) {
2073 content_error (ce
->ce_file
, ct
, "error writing to");
2076 /* finished escape sequence; next may be literal or a new
2077 * escape sequence */
2080 /* on to next byte */
2084 /* not in an escape sequence */
2086 /* starting an escape sequence, or invalid '='? */
2087 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2088 /* "=\n" soft line break, eat the \n */
2092 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2093 /* We don't have 2 bytes left, so this is an invalid
2094 * escape sequence; just show the raw bytes (below). */
2095 } else if (isxdigit (cp
[1]) && isxdigit (cp
[2])) {
2096 /* Next 2 bytes are hex digits, making this a valid escape
2097 * sequence; let's decode it (above). */
2101 /* One or both of the next 2 is out of range, making this
2102 * an invalid escape sequence; just show the raw bytes
2107 /* Just show the raw byte. */
2108 putc (*cp
, ce
->ce_fp
);
2111 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2113 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2116 if (ferror (ce
->ce_fp
)) {
2117 content_error (ce
->ce_file
, ct
, "error writing to");
2123 content_error (NULL
, ct
,
2124 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2128 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2130 if (fflush (ce
->ce_fp
)) {
2131 content_error (ce
->ce_file
, ct
, "error writing to");
2136 unsigned char digest
[16];
2138 MD5Final (digest
, &mdContext
);
2139 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2140 sizeof(digest
) / sizeof(digest
[0])))
2141 content_error (NULL
, ct
,
2142 "content integrity suspect (digest mismatch) -- continuing");
2145 fprintf (stderr
, "content integrity confirmed\n");
2148 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2151 *file
= ce
->ce_file
;
2156 return fileno (ce
->ce_fp
);
2159 free_encoding (ct
, 0);
2175 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2178 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2184 open7Bit (CT ct
, char **file
)
2186 int cc
, fd
, len
, own_ct_fp
= 0;
2187 char buffer
[BUFSIZ
];
2188 /* sbeck -- handle suffixes */
2195 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2200 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2201 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2207 if (*file
== NULL
) {
2208 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2211 ce
->ce_file
= add (*file
, NULL
);
2215 /* sbeck@cise.ufl.edu -- handle suffixes */
2217 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2218 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2219 cp
= context_find (buffer
);
2220 if (cp
== NULL
|| *cp
== '\0') {
2221 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2223 cp
= context_find (buffer
);
2225 if (cp
!= NULL
&& *cp
!= '\0') {
2226 if (ce
->ce_unlink
) {
2227 /* Temporary file already exists, so we rename to
2228 version with extension. */
2229 char *file_org
= strdup(ce
->ce_file
);
2230 ce
->ce_file
= add (cp
, ce
->ce_file
);
2231 if (rename(file_org
, ce
->ce_file
)) {
2232 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2237 ce
->ce_file
= add (cp
, ce
->ce_file
);
2241 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2242 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2246 if (ct
->c_type
== CT_MULTIPART
) {
2248 CI ci
= &ct
->c_ctinfo
;
2251 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2252 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2253 + 1 + strlen (ci
->ci_subtype
);
2254 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2255 putc (';', ce
->ce_fp
);
2258 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2260 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2261 fputs ("\n\t", ce
->ce_fp
);
2264 putc (' ', ce
->ce_fp
);
2267 fprintf (ce
->ce_fp
, "%s", buffer
);
2271 if (ci
->ci_comment
) {
2272 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2273 fputs ("\n\t", ce
->ce_fp
);
2277 putc (' ', ce
->ce_fp
);
2280 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2283 fprintf (ce
->ce_fp
, "\n");
2285 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2287 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2289 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2290 fprintf (ce
->ce_fp
, "\n");
2293 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2294 adios (NULL
, "internal error(3)");
2297 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2298 content_error (ct
->c_file
, ct
, "unable to open for reading");
2304 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2306 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2308 content_error (ct
->c_file
, ct
, "error reading from");
2312 content_error (NULL
, ct
, "premature eof");
2320 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2321 if (ferror (ce
->ce_fp
)) {
2322 content_error (ce
->ce_file
, ct
, "error writing to");
2327 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2329 if (fflush (ce
->ce_fp
)) {
2330 content_error (ce
->ce_file
, ct
, "error writing to");
2334 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2337 *file
= ce
->ce_file
;
2342 return fileno (ce
->ce_fp
);
2345 free_encoding (ct
, 0);
2359 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2361 char cachefile
[BUFSIZ
];
2364 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2369 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2370 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2376 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2377 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2378 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2379 ce
->ce_file
= getcpy (cachefile
);
2383 admonish (cachefile
, "unable to fopen for reading");
2390 *file
= ce
->ce_file
;
2391 *fd
= fileno (ce
->ce_fp
);
2402 return init_encoding (ct
, openFile
);
2407 openFile (CT ct
, char **file
)
2410 char cachefile
[BUFSIZ
];
2411 struct exbody
*e
= ct
->c_ctexbody
;
2412 CE ce
= ct
->c_cefile
;
2414 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2426 content_error (NULL
, ct
, "missing name parameter");
2430 ce
->ce_file
= getcpy (e
->eb_name
);
2433 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2434 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2438 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2439 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2440 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2444 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2445 if ((fp
= fopen (cachefile
, "w"))) {
2447 char buffer
[BUFSIZ
];
2448 FILE *gp
= ce
->ce_fp
;
2450 fseek (gp
, 0L, SEEK_SET
);
2452 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2454 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2458 admonish (ce
->ce_file
, "error reading");
2463 admonish (cachefile
, "error writing");
2471 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2472 *file
= ce
->ce_file
;
2473 return fileno (ce
->ce_fp
);
2483 return init_encoding (ct
, openFTP
);
2488 openFTP (CT ct
, char **file
)
2490 int cachetype
, caching
, fd
;
2492 char *bp
, *ftp
, *user
, *pass
;
2493 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2496 static char *username
= NULL
;
2497 static char *password
= NULL
;
2502 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2508 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2519 if (!e
->eb_name
|| !e
->eb_site
) {
2520 content_error (NULL
, ct
, "missing %s parameter",
2521 e
->eb_name
? "site": "name");
2528 pidcheck (pidwait (xpid
, NOTOK
));
2532 /* Get the buffer ready to go */
2534 buflen
= sizeof(buffer
);
2537 * Construct the query message for user
2539 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2545 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2551 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2552 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2557 if (e
->eb_size
> 0) {
2558 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2563 snprintf (bp
, buflen
, "? ");
2566 * Now, check the answer
2568 if (!getanswer (buffer
))
2573 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2577 ruserpass (e
->eb_site
, &username
, &password
);
2582 ce
->ce_unlink
= (*file
== NULL
);
2584 cachefile
[0] = '\0';
2585 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2586 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2587 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2588 if (*file
== NULL
) {
2595 ce
->ce_file
= add (*file
, NULL
);
2597 ce
->ce_file
= add (cachefile
, NULL
);
2599 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2601 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2602 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2607 int child_id
, i
, vecp
;
2611 vec
[vecp
++] = r1bindex (ftp
, '/');
2612 vec
[vecp
++] = e
->eb_site
;
2615 vec
[vecp
++] = e
->eb_dir
;
2616 vec
[vecp
++] = e
->eb_name
;
2617 vec
[vecp
++] = ce
->ce_file
,
2618 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2619 ? "ascii" : "binary";
2624 for (i
= 0; (child_id
= vfork()) == NOTOK
&& i
< 5; i
++)
2628 adios ("fork", "unable to");
2632 close (fileno (ce
->ce_fp
));
2634 fprintf (stderr
, "unable to exec ");
2640 if (pidXwait (child_id
, NULL
)) {
2641 username
= password
= NULL
;
2651 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2656 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2657 if ((fp
= fopen (cachefile
, "w"))) {
2659 FILE *gp
= ce
->ce_fp
;
2661 fseek (gp
, 0L, SEEK_SET
);
2663 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2665 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2669 admonish (ce
->ce_file
, "error reading");
2674 admonish (cachefile
, "error writing");
2683 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2684 *file
= ce
->ce_file
;
2685 return fileno (ce
->ce_fp
);
2696 return init_encoding (ct
, openMail
);
2701 openMail (CT ct
, char **file
)
2703 int child_id
, fd
, i
, vecp
;
2705 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2706 struct exbody
*e
= ct
->c_ctexbody
;
2707 CE ce
= ct
->c_cefile
;
2709 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2720 if (!e
->eb_server
) {
2721 content_error (NULL
, ct
, "missing server parameter");
2728 pidcheck (pidwait (xpid
, NOTOK
));
2732 /* Get buffer ready to go */
2734 buflen
= sizeof(buffer
);
2736 /* Now, construct query message */
2737 snprintf (bp
, buflen
, "Retrieve content");
2743 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2749 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2751 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2753 /* Now, check answer */
2754 if (!getanswer (buffer
))
2758 vec
[vecp
++] = r1bindex (mailproc
, '/');
2759 vec
[vecp
++] = e
->eb_server
;
2760 vec
[vecp
++] = "-subject";
2761 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2762 vec
[vecp
++] = "-body";
2763 vec
[vecp
++] = e
->eb_body
;
2766 for (i
= 0; (child_id
= vfork()) == NOTOK
&& i
< 5; i
++)
2770 advise ("fork", "unable to");
2774 execvp (mailproc
, vec
);
2775 fprintf (stderr
, "unable to exec ");
2781 if (pidXwait (child_id
, NULL
) == OK
)
2782 advise (NULL
, "request sent");
2786 if (*file
== NULL
) {
2787 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2790 ce
->ce_file
= add (*file
, NULL
);
2794 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2795 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2799 /* showproc is for mhshow and mhstore, though mhlist -debug
2800 * prints it, too. */
2802 free (ct
->c_showproc
);
2803 ct
->c_showproc
= add ("true", NULL
);
2805 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2806 *file
= ce
->ce_file
;
2807 return fileno (ce
->ce_fp
);
2812 readDigest (CT ct
, char *cp
)
2817 unsigned char *dp
, value
, *ep
;
2823 for (ep
= (dp
= ct
->c_digest
)
2824 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2829 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2831 fprintf (stderr
, "invalid BASE64 encoding\n");
2835 bits
|= value
<< bitno
;
2837 if ((bitno
-= 6) < 0) {
2838 if (dp
+ (3 - skip
) > ep
)
2839 goto invalid_digest
;
2840 *dp
++ = (bits
>> 16) & 0xff;
2842 *dp
++ = (bits
>> 8) & 0xff;
2844 *dp
++ = bits
& 0xff;
2854 goto self_delimiting
;
2859 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2869 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2877 fprintf (stderr
, "MD5 digest=");
2878 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2879 fprintf (stderr
, "%02x", *dp
& 0xff);
2880 fprintf (stderr
, "\n");