]>
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
];
266 /* allocate the content structure */
267 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
268 adios (NULL
, "out of memory");
271 ct
->c_file
= add (file
, NULL
);
272 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
275 * Parse the header fields for this
276 * content into a linked list.
278 for (compnum
= 1, state
= FLD
;;) {
279 int bufsz
= sizeof buf
;
280 switch (state
= m_getfld (state
, name
, buf
, &bufsz
, in
)) {
286 /* get copies of the buffers */
287 np
= add (name
, NULL
);
288 vp
= add (buf
, NULL
);
290 /* if necessary, get rest of field */
291 while (state
== FLDPLUS
) {
293 state
= m_getfld (state
, name
, buf
, &bufsz
, in
);
294 vp
= add (buf
, vp
); /* add to previous value */
297 /* Now add the header data to the list */
298 add_header (ct
, np
, vp
);
300 /* continue, if this isn't the last header field */
301 if (state
!= FLDEOF
) {
302 ct
->c_begin
= ftell (in
) + 1;
309 ct
->c_begin
= ftell (in
) - strlen (buf
);
313 ct
->c_begin
= ftell (in
);
318 adios (NULL
, "message format error in component #%d", compnum
);
321 adios (NULL
, "getfld() returned %d", state
);
324 /* break out of the loop */
329 * Read the content headers. We will parse the
330 * MIME related header fields into their various
331 * structures and set internal flags related to
332 * content type/subtype, etc.
335 hp
= ct
->c_first_hf
; /* start at first header field */
337 /* Get MIME-Version field */
338 if (!mh_strcasecmp (hp
->name
, VRSN_FIELD
)) {
341 unsigned char *cp
, *dp
;
344 advise (NULL
, "message %s has multiple %s: fields",
345 ct
->c_file
, VRSN_FIELD
);
348 ct
->c_vrsn
= add (hp
->value
, NULL
);
350 /* Now, cleanup this field */
353 while (isspace (*cp
))
355 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
357 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
362 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
364 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
)
367 for (dp
= cp
; istoken (*dp
); dp
++)
371 ucmp
= !mh_strcasecmp (cp
, VRSN_VALUE
);
374 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
375 ct
->c_file
, VRSN_FIELD
, cp
);
378 else if (!mh_strcasecmp (hp
->name
, TYPE_FIELD
)) {
379 /* Get Content-Type field */
380 struct str2init
*s2i
;
381 CI ci
= &ct
->c_ctinfo
;
383 /* Check if we've already seen a Content-Type header */
385 advise (NULL
, "message %s has multiple %s: fields",
386 ct
->c_file
, TYPE_FIELD
);
390 /* Parse the Content-Type field */
391 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
395 * Set the Init function and the internal
396 * flag for this content type.
398 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
399 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
401 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
403 ct
->c_type
= s2i
->si_val
;
404 ct
->c_ctinitfnx
= s2i
->si_init
;
406 else if (!mh_strcasecmp (hp
->name
, ENCODING_FIELD
)) {
407 /* Get Content-Transfer-Encoding field */
409 unsigned char *cp
, *dp
;
410 struct str2init
*s2i
;
413 * Check if we've already seen the
414 * Content-Transfer-Encoding field
417 advise (NULL
, "message %s has multiple %s: fields",
418 ct
->c_file
, ENCODING_FIELD
);
422 /* get copy of this field */
423 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
425 while (isspace (*cp
))
427 for (dp
= cp
; istoken (*dp
); dp
++)
433 * Find the internal flag and Init function
434 * for this transfer encoding.
436 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
437 if (!mh_strcasecmp (cp
, s2i
->si_key
))
439 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
442 ct
->c_encoding
= s2i
->si_val
;
444 /* Call the Init function for this encoding */
445 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
448 else if (!mh_strcasecmp (hp
->name
, MD5_FIELD
)) {
449 /* Get Content-MD5 field */
450 unsigned char *cp
, *dp
;
456 if (ct
->c_digested
) {
457 advise (NULL
, "message %s has multiple %s: fields",
458 ct
->c_file
, MD5_FIELD
);
462 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
464 while (isspace (*cp
))
466 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
468 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
473 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
475 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
) {
480 for (dp
= cp
; *dp
&& !isspace (*dp
); dp
++)
488 else if (!mh_strcasecmp (hp
->name
, ID_FIELD
)) {
489 /* Get Content-ID field */
490 ct
->c_id
= add (hp
->value
, ct
->c_id
);
492 else if (!mh_strcasecmp (hp
->name
, DESCR_FIELD
)) {
493 /* Get Content-Description field */
494 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
496 else if (!mh_strcasecmp (hp
->name
, DISPO_FIELD
)) {
497 /* Get Content-Disposition field */
498 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
502 hp
= hp
->next
; /* next header field */
506 * Check if we saw a Content-Type field.
507 * If not, then assign a default value for
508 * it, and the Init function.
512 * If we are inside a multipart/digest message,
513 * so default type is message/rfc822
516 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
518 ct
->c_type
= CT_MESSAGE
;
519 ct
->c_ctinitfnx
= InitMessage
;
522 * Else default type is text/plain
524 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
526 ct
->c_type
= CT_TEXT
;
527 ct
->c_ctinitfnx
= InitText
;
531 /* Use default Transfer-Encoding, if necessary */
533 ct
->c_encoding
= CE_7BIT
;
546 * small routine to add header field to list
550 add_header (CT ct
, char *name
, char *value
)
554 /* allocate header field structure */
555 hp
= mh_xmalloc (sizeof(*hp
));
557 /* link data into header structure */
562 /* link header structure into the list */
563 if (ct
->c_first_hf
== NULL
) {
564 ct
->c_first_hf
= hp
; /* this is the first */
567 ct
->c_last_hf
->next
= hp
; /* add it to the end */
575 /* Make sure that buf contains at least one appearance of name,
576 followed by =. If not, insert both name and value, just after
577 first semicolon, if any. Note that name should not contain a
578 trailing =. And quotes will be added around the value. Typical
579 usage: make sure that a Content-Disposition header contains
580 filename="foo". If it doesn't and value does, use value from
583 incl_name_value (unsigned char *buf
, char *name
, char *value
) {
586 /* Assume that name is non-null. */
588 char *name_plus_equal
= concat (name
, "=", NULL
);
590 if (! strstr (buf
, name_plus_equal
)) {
593 char *prefix
, *suffix
;
595 /* Trim trailing space, esp. newline. */
596 for (cp
= &buf
[strlen (buf
) - 1];
597 cp
>= buf
&& isspace (*cp
);
602 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
604 /* Insert at first semicolon, if any. If none, append to
606 prefix
= add (buf
, NULL
);
607 if ((cp
= strchr (prefix
, ';'))) {
608 suffix
= concat (cp
, NULL
);
610 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
614 newbuf
= concat (buf
, insertion
, "\n", NULL
);
622 free (name_plus_equal
);
628 /* Extract just name_suffix="foo", if any, from value. If there isn't
629 one, return the entire value. Note that, for example, a name_suffix
630 of name will match filename="foo", and return foo. */
632 extract_name_value (char *name_suffix
, char *value
) {
633 char *extracted_name_value
= value
;
634 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
635 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
638 free (name_suffix_plus_quote
);
639 if (name_suffix_equals
) {
640 char *name_suffix_begin
;
643 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
644 name_suffix_begin
= ++cp
;
645 /* Find second \". */
646 for (; *cp
!= '"'; ++cp
) /* empty */;
648 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
649 memcpy (extracted_name_value
,
651 cp
- name_suffix_begin
);
652 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
655 return extracted_name_value
;
659 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
660 * directives. Fills in the information of the CTinfo structure.
663 get_ctinfo (unsigned char *cp
, CT ct
, int magic
)
672 i
= strlen (invo_name
) + 2;
674 /* store copy of Content-Type line */
675 cp
= ct
->c_ctline
= add (cp
, NULL
);
677 while (isspace (*cp
)) /* trim leading spaces */
680 /* change newlines to spaces */
681 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
684 /* trim trailing spaces */
685 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
691 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
693 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
696 for (dp
= cp
; istoken (*dp
); dp
++)
699 ci
->ci_type
= add (cp
, NULL
); /* store content type */
703 advise (NULL
, "invalid %s: field in message %s (empty type)",
704 TYPE_FIELD
, ct
->c_file
);
708 /* down case the content type string */
709 for (dp
= ci
->ci_type
; *dp
; dp
++)
710 if (isalpha(*dp
) && isupper (*dp
))
713 while (isspace (*cp
))
716 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
721 ci
->ci_subtype
= add ("", NULL
);
726 while (isspace (*cp
))
729 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
732 for (dp
= cp
; istoken (*dp
); dp
++)
735 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
738 if (!*ci
->ci_subtype
) {
740 "invalid %s: field in message %s (empty subtype for \"%s\")",
741 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
745 /* down case the content subtype string */
746 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
747 if (isalpha(*dp
) && isupper (*dp
))
751 while (isspace (*cp
))
754 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
758 * Parse attribute/value pairs given with Content-Type
760 ep
= (ap
= ci
->ci_attrs
) + NPARMS
;
767 "too many parameters in message %s's %s: field (%d max)",
768 ct
->c_file
, TYPE_FIELD
, NPARMS
);
773 while (isspace (*cp
))
776 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
781 "extraneous trailing ';' in message %s's %s: parameter list",
782 ct
->c_file
, TYPE_FIELD
);
786 /* down case the attribute name */
787 for (dp
= cp
; istoken (*dp
); dp
++)
788 if (isalpha(*dp
) && isupper (*dp
))
791 for (up
= dp
; isspace (*dp
);)
793 if (dp
== cp
|| *dp
!= '=') {
795 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
796 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
, dp
- cp
);
800 vp
= (*ap
= add (cp
, NULL
)) + (up
- cp
);
802 for (dp
++; isspace (*dp
);)
805 /* now add the attribute value */
806 ci
->ci_values
[ap
- ci
->ci_attrs
] = vp
= *ap
+ (dp
- cp
);
809 for (cp
= ++dp
, dp
= vp
;;) {
814 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
815 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
820 if ((c
= *cp
++) == '\0')
835 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
841 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
842 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
847 while (isspace (*cp
))
850 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
855 * Get any <Content-Id> given in buffer
857 if (magic
&& *cp
== '<') {
862 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
863 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
869 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
875 while (isspace (*cp
))
880 * Get any [Content-Description] given in buffer.
882 if (magic
&& *cp
== '[') {
884 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
888 advise (NULL
, "invalid description in message %s", ct
->c_file
);
896 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
902 while (isspace (*cp
))
907 * Get any {Content-Disposition} given in buffer.
909 if (magic
&& *cp
== '{') {
911 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
915 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
923 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
929 while (isspace (*cp
))
934 * Check if anything is left over
938 ci
->ci_magic
= add (cp
, NULL
);
940 /* If there is a Content-Disposition header and it doesn't
941 have a *filename=, extract it from the magic contents.
942 The r1bindex call skips any leading directory
946 incl_name_value (ct
->c_dispo
,
948 r1bindex (extract_name_value ("name",
955 "extraneous information in message %s's %s: field\n%*.*s(%s)",
956 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
964 get_comment (CT ct
, unsigned char **ap
, int istype
)
969 char c
, buffer
[BUFSIZ
], *dp
;
981 advise (NULL
, "invalid comment in message %s's %s: field",
982 ct
->c_file
, istype
? TYPE_FIELD
: VRSN_FIELD
);
987 if ((c
= *cp
++) == '\0')
1010 if ((dp
= ci
->ci_comment
)) {
1011 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
1014 ci
->ci_comment
= add (buffer
, NULL
);
1018 while (isspace (*cp
))
1029 * Handles content types audio, image, and video.
1030 * There's not much to do right here.
1038 return OK
; /* not much to do here */
1049 char buffer
[BUFSIZ
];
1051 char **ap
, **ep
, *cp
;
1054 CI ci
= &ct
->c_ctinfo
;
1056 /* check for missing subtype */
1057 if (!*ci
->ci_subtype
)
1058 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1061 for (kv
= SubText
; kv
->kv_key
; kv
++)
1062 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1064 ct
->c_subtype
= kv
->kv_value
;
1066 /* allocate text character set structure */
1067 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1068 adios (NULL
, "out of memory");
1069 ct
->c_ctparams
= (void *) t
;
1071 /* scan for charset parameter */
1072 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1073 if (!mh_strcasecmp (*ap
, "charset"))
1076 /* check if content specified a character set */
1079 t
->tx_charset
= CHARSET_SPECIFIED
;
1081 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1085 * If we can not handle character set natively,
1086 * then check profile for string to modify the
1087 * terminal or display method.
1089 * termproc is for mhshow, though mhlist -debug prints it, too.
1091 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1092 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1093 if ((cp
= context_find (buffer
)))
1094 ct
->c_termproc
= getcpy (cp
);
1106 InitMultiPart (CT ct
)
1110 unsigned char *cp
, *dp
;
1112 char *bp
, buffer
[BUFSIZ
];
1113 struct multipart
*m
;
1115 struct part
*part
, **next
;
1116 CI ci
= &ct
->c_ctinfo
;
1121 * The encoding for multipart messages must be either
1122 * 7bit, 8bit, or binary (per RFC2045).
1124 if (ct
->c_encoding
!= CE_7BIT
&& ct
->c_encoding
!= CE_8BIT
1125 && ct
->c_encoding
!= CE_BINARY
) {
1127 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1128 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1133 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1134 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1136 ct
->c_subtype
= kv
->kv_value
;
1139 * Check for "boundary" parameter, which is
1140 * required for multipart messages.
1143 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1144 if (!mh_strcasecmp (*ap
, "boundary")) {
1150 /* complain if boundary parameter is missing */
1153 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1154 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1158 /* allocate primary structure for multipart info */
1159 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1160 adios (NULL
, "out of memory");
1161 ct
->c_ctparams
= (void *) m
;
1163 /* check if boundary parameter contains only whitespace characters */
1164 for (cp
= bp
; isspace (*cp
); cp
++)
1167 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1168 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1172 /* remove trailing whitespace from boundary parameter */
1173 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1178 /* record boundary separators */
1179 m
->mp_start
= concat (bp
, "\n", NULL
);
1180 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1182 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1183 advise (ct
->c_file
, "unable to open for reading");
1187 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1189 next
= &m
->mp_parts
;
1193 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1197 pos
+= strlen (buffer
);
1198 if (buffer
[0] != '-' || buffer
[1] != '-')
1201 if (strcmp (buffer
+ 2, m
->mp_start
))
1204 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1205 adios (NULL
, "out of memory");
1207 next
= &part
->mp_next
;
1209 if (!(p
= get_content (fp
, ct
->c_file
,
1210 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1217 fseek (fp
, pos
, SEEK_SET
);
1220 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1224 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1225 if (p
->c_end
< p
->c_begin
)
1226 p
->c_begin
= p
->c_end
;
1231 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1237 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1238 if (!inout
&& part
) {
1240 p
->c_end
= ct
->c_end
;
1242 if (p
->c_begin
>= p
->c_end
) {
1243 for (next
= &m
->mp_parts
; *next
!= part
;
1244 next
= &((*next
)->mp_next
))
1248 free ((char *) part
);
1253 /* reverse the order of the parts for multipart/alternative */
1254 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1258 * label all subparts with part number, and
1259 * then initialize the content of the subpart.
1264 char partnam
[BUFSIZ
];
1267 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1268 pp
= partnam
+ strlen (partnam
);
1273 for (part
= m
->mp_parts
, partnum
= 1; part
;
1274 part
= part
->mp_next
, partnum
++) {
1277 sprintf (pp
, "%d", partnum
);
1278 p
->c_partno
= add (partnam
, NULL
);
1280 /* initialize the content of the subparts */
1281 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1296 * reverse the order of the parts of a multipart
1300 reverse_parts (CT ct
)
1303 struct multipart
*m
;
1304 struct part
**base
, **bmp
, **next
, *part
;
1306 m
= (struct multipart
*) ct
->c_ctparams
;
1308 /* if only one part, just return */
1309 if (!m
->mp_parts
|| !m
->mp_parts
->mp_next
)
1312 /* count number of parts */
1314 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1317 /* allocate array of pointers to the parts */
1318 if (!(base
= (struct part
**) calloc ((size_t) (i
+ 1), sizeof(*base
))))
1319 adios (NULL
, "out of memory");
1322 /* point at all the parts */
1323 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1327 /* reverse the order of the parts */
1328 next
= &m
->mp_parts
;
1329 for (bmp
--; bmp
>= base
; bmp
--) {
1332 next
= &part
->mp_next
;
1336 /* free array of pointers */
1337 free ((char *) base
);
1349 CI ci
= &ct
->c_ctinfo
;
1351 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1353 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1354 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1358 /* check for missing subtype */
1359 if (!*ci
->ci_subtype
)
1360 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1363 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1364 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1366 ct
->c_subtype
= kv
->kv_value
;
1368 switch (ct
->c_subtype
) {
1369 case MESSAGE_RFC822
:
1372 case MESSAGE_PARTIAL
:
1377 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1378 adios (NULL
, "out of memory");
1379 ct
->c_ctparams
= (void *) p
;
1381 /* scan for parameters "id", "number", and "total" */
1382 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1383 if (!mh_strcasecmp (*ap
, "id")) {
1384 p
->pm_partid
= add (*ep
, NULL
);
1387 if (!mh_strcasecmp (*ap
, "number")) {
1388 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1389 || p
->pm_partno
< 1) {
1392 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1393 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1394 ct
->c_file
, TYPE_FIELD
);
1399 if (!mh_strcasecmp (*ap
, "total")) {
1400 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1409 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1411 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1412 ci
->ci_type
, ci
->ci_subtype
,
1413 ct
->c_file
, TYPE_FIELD
);
1419 case MESSAGE_EXTERNAL
:
1426 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1427 adios (NULL
, "out of memory");
1428 ct
->c_ctparams
= (void *) e
;
1431 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1432 advise (ct
->c_file
, "unable to open for reading");
1436 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1438 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1446 if ((exresult
= params_external (ct
, 0)) != NOTOK
1447 && p
->c_ceopenfnx
== openMail
) {
1451 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1453 content_error (NULL
, ct
,
1454 "empty body for access-type=mail-server");
1458 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1459 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1461 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1463 adios ("failed", "fread");
1466 adios (NULL
, "unexpected EOF from fread");
1469 bp
+= cc
, size
-= cc
;
1476 p
->c_end
= p
->c_begin
;
1481 if (exresult
== NOTOK
)
1483 if (e
->eb_flags
== NOTOK
)
1486 switch (p
->c_type
) {
1491 if (p
->c_subtype
!= MESSAGE_RFC822
)
1495 e
->eb_partno
= ct
->c_partno
;
1497 (*p
->c_ctinitfnx
) (p
);
1512 params_external (CT ct
, int composing
)
1515 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1516 CI ci
= &ct
->c_ctinfo
;
1518 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1519 if (!mh_strcasecmp (*ap
, "access-type")) {
1520 struct str2init
*s2i
;
1521 CT p
= e
->eb_content
;
1523 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1524 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1528 e
->eb_flags
= NOTOK
;
1529 p
->c_encoding
= CE_EXTERNAL
;
1532 e
->eb_access
= s2i
->si_key
;
1533 e
->eb_flags
= s2i
->si_val
;
1534 p
->c_encoding
= CE_EXTERNAL
;
1536 /* Call the Init function for this external type */
1537 if ((*s2i
->si_init
)(p
) == NOTOK
)
1541 if (!mh_strcasecmp (*ap
, "name")) {
1545 if (!mh_strcasecmp (*ap
, "permission")) {
1546 e
->eb_permission
= *ep
;
1549 if (!mh_strcasecmp (*ap
, "site")) {
1553 if (!mh_strcasecmp (*ap
, "directory")) {
1557 if (!mh_strcasecmp (*ap
, "mode")) {
1561 if (!mh_strcasecmp (*ap
, "size")) {
1562 sscanf (*ep
, "%lu", &e
->eb_size
);
1565 if (!mh_strcasecmp (*ap
, "server")) {
1569 if (!mh_strcasecmp (*ap
, "subject")) {
1570 e
->eb_subject
= *ep
;
1573 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1574 e
->eb_body
= getcpy (*ep
);
1579 if (!e
->eb_access
) {
1581 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1582 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1595 InitApplication (CT ct
)
1598 CI ci
= &ct
->c_ctinfo
;
1601 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1602 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1604 ct
->c_subtype
= kv
->kv_value
;
1611 * TRANSFER ENCODINGS
1615 init_encoding (CT ct
, OpenCEFunc openfnx
)
1619 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
1620 adios (NULL
, "out of memory");
1623 ct
->c_ceopenfnx
= openfnx
;
1624 ct
->c_ceclosefnx
= close_encoding
;
1625 ct
->c_cesizefnx
= size_encoding
;
1632 close_encoding (CT ct
)
1636 if (!(ce
= ct
->c_cefile
))
1646 static unsigned long
1647 size_encoding (CT ct
)
1655 if (!(ce
= ct
->c_cefile
))
1656 return (ct
->c_end
- ct
->c_begin
);
1658 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1659 return (long) st
.st_size
;
1662 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1663 return (long) st
.st_size
;
1668 if (ct
->c_encoding
== CE_EXTERNAL
)
1669 return (ct
->c_end
- ct
->c_begin
);
1672 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1673 return (ct
->c_end
- ct
->c_begin
);
1675 if (fstat (fd
, &st
) != NOTOK
)
1676 size
= (long) st
.st_size
;
1680 (*ct
->c_ceclosefnx
) (ct
);
1689 static unsigned char b642nib
[0x80] = {
1690 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1691 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1692 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1693 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1694 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1695 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1696 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1697 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1698 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1699 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1700 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1701 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1702 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1703 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1704 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1705 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1712 return init_encoding (ct
, openBase64
);
1717 openBase64 (CT ct
, char **file
)
1719 int bitno
, cc
, digested
;
1720 int fd
, len
, skip
, own_ct_fp
= 0;
1722 unsigned char value
, b
;
1723 unsigned char *cp
, *ep
;
1724 char buffer
[BUFSIZ
];
1725 /* sbeck -- handle suffixes */
1732 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1737 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1738 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1744 if (*file
== NULL
) {
1745 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1748 ce
->ce_file
= add (*file
, NULL
);
1752 /* sbeck@cise.ufl.edu -- handle suffixes */
1754 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1755 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1756 cp
= context_find (buffer
);
1757 if (cp
== NULL
|| *cp
== '\0') {
1758 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1760 cp
= context_find (buffer
);
1762 if (cp
!= NULL
&& *cp
!= '\0') {
1763 if (ce
->ce_unlink
) {
1764 /* Temporary file already exists, so we rename to
1765 version with extension. */
1766 char *file_org
= strdup(ce
->ce_file
);
1767 ce
->ce_file
= add (cp
, ce
->ce_file
);
1768 if (rename(file_org
, ce
->ce_file
)) {
1769 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1774 ce
->ce_file
= add (cp
, ce
->ce_file
);
1778 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1779 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1783 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1784 adios (NULL
, "internal error(1)");
1787 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1788 content_error (ct
->c_file
, ct
, "unable to open for reading");
1794 if ((digested
= ct
->c_digested
))
1795 MD5Init (&mdContext
);
1801 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1803 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1805 content_error (ct
->c_file
, ct
, "error reading from");
1809 content_error (NULL
, ct
, "premature eof");
1817 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1822 if (skip
|| (*cp
& 0x80)
1823 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
1825 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1827 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1830 content_error (NULL
, ct
,
1831 "invalid BASE64 encoding -- continuing");
1835 bits
|= value
<< bitno
;
1837 if ((bitno
-= 6) < 0) {
1838 b
= (bits
>> 16) & 0xff;
1839 putc ((char) b
, ce
->ce_fp
);
1841 MD5Update (&mdContext
, &b
, 1);
1843 b
= (bits
>> 8) & 0xff;
1844 putc ((char) b
, ce
->ce_fp
);
1846 MD5Update (&mdContext
, &b
, 1);
1849 putc ((char) b
, ce
->ce_fp
);
1851 MD5Update (&mdContext
, &b
, 1);
1855 if (ferror (ce
->ce_fp
)) {
1856 content_error (ce
->ce_file
, ct
,
1857 "error writing to");
1860 bitno
= 18, bits
= 0L, skip
= 0;
1866 goto self_delimiting
;
1875 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1877 content_error (NULL
, ct
, "invalid BASE64 encoding");
1882 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1884 if (fflush (ce
->ce_fp
)) {
1885 content_error (ce
->ce_file
, ct
, "error writing to");
1890 unsigned char digest
[16];
1892 MD5Final (digest
, &mdContext
);
1893 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1894 sizeof(digest
) / sizeof(digest
[0])))
1895 content_error (NULL
, ct
,
1896 "content integrity suspect (digest mismatch) -- continuing");
1899 fprintf (stderr
, "content integrity confirmed\n");
1902 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1905 *file
= ce
->ce_file
;
1910 return fileno (ce
->ce_fp
);
1917 free_encoding (ct
, 0);
1926 static char hex2nib
[0x80] = {
1927 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1929 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1934 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1935 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x00, 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
1949 return init_encoding (ct
, openQuoted
);
1954 openQuoted (CT ct
, char **file
)
1956 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1957 unsigned char *cp
, *ep
;
1958 char buffer
[BUFSIZ
];
1961 /* sbeck -- handle suffixes */
1967 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1972 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1973 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1979 if (*file
== NULL
) {
1980 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1983 ce
->ce_file
= add (*file
, NULL
);
1987 /* sbeck@cise.ufl.edu -- handle suffixes */
1989 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1990 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1991 cp
= context_find (buffer
);
1992 if (cp
== NULL
|| *cp
== '\0') {
1993 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1995 cp
= context_find (buffer
);
1997 if (cp
!= NULL
&& *cp
!= '\0') {
1998 if (ce
->ce_unlink
) {
1999 /* Temporary file already exists, so we rename to
2000 version with extension. */
2001 char *file_org
= strdup(ce
->ce_file
);
2002 ce
->ce_file
= add (cp
, ce
->ce_file
);
2003 if (rename(file_org
, ce
->ce_file
)) {
2004 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2009 ce
->ce_file
= add (cp
, ce
->ce_file
);
2013 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2014 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2018 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2019 adios (NULL
, "internal error(2)");
2022 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2023 content_error (ct
->c_file
, ct
, "unable to open for reading");
2029 if ((digested
= ct
->c_digested
))
2030 MD5Init (&mdContext
);
2037 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2039 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2040 content_error (NULL
, ct
, "premature eof");
2044 if ((cc
= strlen (buffer
)) > len
)
2048 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2053 for (; cp
< ep
; cp
++) {
2055 /* in an escape sequence */
2057 /* at byte 1 of an escape sequence */
2058 mask
= hex2nib
[*cp
& 0x7f];
2059 /* next is byte 2 */
2062 /* at byte 2 of an escape sequence */
2064 mask
|= hex2nib
[*cp
& 0x7f];
2065 putc (mask
, ce
->ce_fp
);
2067 MD5Update (&mdContext
, &mask
, 1);
2068 if (ferror (ce
->ce_fp
)) {
2069 content_error (ce
->ce_file
, ct
, "error writing to");
2072 /* finished escape sequence; next may be literal or a new
2073 * escape sequence */
2076 /* on to next byte */
2080 /* not in an escape sequence */
2082 /* starting an escape sequence, or invalid '='? */
2083 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2084 /* "=\n" soft line break, eat the \n */
2088 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2089 /* We don't have 2 bytes left, so this is an invalid
2090 * escape sequence; just show the raw bytes (below). */
2091 } else if (isxdigit (cp
[1]) && isxdigit (cp
[2])) {
2092 /* Next 2 bytes are hex digits, making this a valid escape
2093 * sequence; let's decode it (above). */
2097 /* One or both of the next 2 is out of range, making this
2098 * an invalid escape sequence; just show the raw bytes
2103 /* Just show the raw byte. */
2104 putc (*cp
, ce
->ce_fp
);
2107 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2109 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2112 if (ferror (ce
->ce_fp
)) {
2113 content_error (ce
->ce_file
, ct
, "error writing to");
2119 content_error (NULL
, ct
,
2120 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2124 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2126 if (fflush (ce
->ce_fp
)) {
2127 content_error (ce
->ce_file
, ct
, "error writing to");
2132 unsigned char digest
[16];
2134 MD5Final (digest
, &mdContext
);
2135 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2136 sizeof(digest
) / sizeof(digest
[0])))
2137 content_error (NULL
, ct
,
2138 "content integrity suspect (digest mismatch) -- continuing");
2141 fprintf (stderr
, "content integrity confirmed\n");
2144 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2147 *file
= ce
->ce_file
;
2152 return fileno (ce
->ce_fp
);
2155 free_encoding (ct
, 0);
2171 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2174 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2180 open7Bit (CT ct
, char **file
)
2182 int cc
, fd
, len
, own_ct_fp
= 0;
2183 char buffer
[BUFSIZ
];
2184 /* sbeck -- handle suffixes */
2191 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2196 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2197 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2203 if (*file
== NULL
) {
2204 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2207 ce
->ce_file
= add (*file
, NULL
);
2211 /* sbeck@cise.ufl.edu -- handle suffixes */
2213 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2214 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2215 cp
= context_find (buffer
);
2216 if (cp
== NULL
|| *cp
== '\0') {
2217 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2219 cp
= context_find (buffer
);
2221 if (cp
!= NULL
&& *cp
!= '\0') {
2222 if (ce
->ce_unlink
) {
2223 /* Temporary file already exists, so we rename to
2224 version with extension. */
2225 char *file_org
= strdup(ce
->ce_file
);
2226 ce
->ce_file
= add (cp
, ce
->ce_file
);
2227 if (rename(file_org
, ce
->ce_file
)) {
2228 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2233 ce
->ce_file
= add (cp
, ce
->ce_file
);
2237 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2238 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2242 if (ct
->c_type
== CT_MULTIPART
) {
2244 CI ci
= &ct
->c_ctinfo
;
2247 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2248 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2249 + 1 + strlen (ci
->ci_subtype
);
2250 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2251 putc (';', ce
->ce_fp
);
2254 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2256 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2257 fputs ("\n\t", ce
->ce_fp
);
2260 putc (' ', ce
->ce_fp
);
2263 fprintf (ce
->ce_fp
, "%s", buffer
);
2267 if (ci
->ci_comment
) {
2268 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2269 fputs ("\n\t", ce
->ce_fp
);
2273 putc (' ', ce
->ce_fp
);
2276 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2279 fprintf (ce
->ce_fp
, "\n");
2281 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2283 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2285 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2286 fprintf (ce
->ce_fp
, "\n");
2289 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2290 adios (NULL
, "internal error(3)");
2293 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2294 content_error (ct
->c_file
, ct
, "unable to open for reading");
2300 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2302 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2304 content_error (ct
->c_file
, ct
, "error reading from");
2308 content_error (NULL
, ct
, "premature eof");
2316 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2317 if (ferror (ce
->ce_fp
)) {
2318 content_error (ce
->ce_file
, ct
, "error writing to");
2323 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2325 if (fflush (ce
->ce_fp
)) {
2326 content_error (ce
->ce_file
, ct
, "error writing to");
2330 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2333 *file
= ce
->ce_file
;
2338 return fileno (ce
->ce_fp
);
2341 free_encoding (ct
, 0);
2355 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2357 char cachefile
[BUFSIZ
];
2360 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2365 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2366 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2372 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2373 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2374 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2375 ce
->ce_file
= getcpy (cachefile
);
2379 admonish (cachefile
, "unable to fopen for reading");
2386 *file
= ce
->ce_file
;
2387 *fd
= fileno (ce
->ce_fp
);
2398 return init_encoding (ct
, openFile
);
2403 openFile (CT ct
, char **file
)
2406 char cachefile
[BUFSIZ
];
2407 struct exbody
*e
= ct
->c_ctexbody
;
2408 CE ce
= ct
->c_cefile
;
2410 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2422 content_error (NULL
, ct
, "missing name parameter");
2426 ce
->ce_file
= getcpy (e
->eb_name
);
2429 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2430 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2434 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2435 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2436 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2440 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2441 if ((fp
= fopen (cachefile
, "w"))) {
2443 char buffer
[BUFSIZ
];
2444 FILE *gp
= ce
->ce_fp
;
2446 fseek (gp
, 0L, SEEK_SET
);
2448 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2450 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2454 admonish (ce
->ce_file
, "error reading");
2459 admonish (cachefile
, "error writing");
2467 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2468 *file
= ce
->ce_file
;
2469 return fileno (ce
->ce_fp
);
2479 return init_encoding (ct
, openFTP
);
2484 openFTP (CT ct
, char **file
)
2486 int cachetype
, caching
, fd
;
2488 char *bp
, *ftp
, *user
, *pass
;
2489 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2492 static char *username
= NULL
;
2493 static char *password
= NULL
;
2498 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2504 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2515 if (!e
->eb_name
|| !e
->eb_site
) {
2516 content_error (NULL
, ct
, "missing %s parameter",
2517 e
->eb_name
? "site": "name");
2524 pidcheck (pidwait (xpid
, NOTOK
));
2528 /* Get the buffer ready to go */
2530 buflen
= sizeof(buffer
);
2533 * Construct the query message for user
2535 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2541 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2547 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2548 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2553 if (e
->eb_size
> 0) {
2554 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2559 snprintf (bp
, buflen
, "? ");
2562 * Now, check the answer
2564 if (!getanswer (buffer
))
2569 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2573 ruserpass (e
->eb_site
, &username
, &password
);
2578 ce
->ce_unlink
= (*file
== NULL
);
2580 cachefile
[0] = '\0';
2581 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2582 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2583 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2584 if (*file
== NULL
) {
2591 ce
->ce_file
= add (*file
, NULL
);
2593 ce
->ce_file
= add (cachefile
, NULL
);
2595 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2597 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2598 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2603 int child_id
, i
, vecp
;
2607 vec
[vecp
++] = r1bindex (ftp
, '/');
2608 vec
[vecp
++] = e
->eb_site
;
2611 vec
[vecp
++] = e
->eb_dir
;
2612 vec
[vecp
++] = e
->eb_name
;
2613 vec
[vecp
++] = ce
->ce_file
,
2614 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2615 ? "ascii" : "binary";
2620 for (i
= 0; (child_id
= vfork()) == NOTOK
&& i
< 5; i
++)
2624 adios ("fork", "unable to");
2628 close (fileno (ce
->ce_fp
));
2630 fprintf (stderr
, "unable to exec ");
2636 if (pidXwait (child_id
, NULL
)) {
2637 username
= password
= NULL
;
2647 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2652 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2653 if ((fp
= fopen (cachefile
, "w"))) {
2655 FILE *gp
= ce
->ce_fp
;
2657 fseek (gp
, 0L, SEEK_SET
);
2659 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2661 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2665 admonish (ce
->ce_file
, "error reading");
2670 admonish (cachefile
, "error writing");
2679 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2680 *file
= ce
->ce_file
;
2681 return fileno (ce
->ce_fp
);
2692 return init_encoding (ct
, openMail
);
2697 openMail (CT ct
, char **file
)
2699 int child_id
, fd
, i
, vecp
;
2701 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2702 struct exbody
*e
= ct
->c_ctexbody
;
2703 CE ce
= ct
->c_cefile
;
2705 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2716 if (!e
->eb_server
) {
2717 content_error (NULL
, ct
, "missing server parameter");
2724 pidcheck (pidwait (xpid
, NOTOK
));
2728 /* Get buffer ready to go */
2730 buflen
= sizeof(buffer
);
2732 /* Now, construct query message */
2733 snprintf (bp
, buflen
, "Retrieve content");
2739 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2745 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2747 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2749 /* Now, check answer */
2750 if (!getanswer (buffer
))
2754 vec
[vecp
++] = r1bindex (mailproc
, '/');
2755 vec
[vecp
++] = e
->eb_server
;
2756 vec
[vecp
++] = "-subject";
2757 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2758 vec
[vecp
++] = "-body";
2759 vec
[vecp
++] = e
->eb_body
;
2762 for (i
= 0; (child_id
= vfork()) == NOTOK
&& i
< 5; i
++)
2766 advise ("fork", "unable to");
2770 execvp (mailproc
, vec
);
2771 fprintf (stderr
, "unable to exec ");
2777 if (pidXwait (child_id
, NULL
) == OK
)
2778 advise (NULL
, "request sent");
2782 if (*file
== NULL
) {
2783 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2786 ce
->ce_file
= add (*file
, NULL
);
2790 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2791 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2795 /* showproc is for mhshow and mhstore, though mhlist -debug
2796 * prints it, too. */
2798 free (ct
->c_showproc
);
2799 ct
->c_showproc
= add ("true", NULL
);
2801 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2802 *file
= ce
->ce_file
;
2803 return fileno (ce
->ce_fp
);
2808 readDigest (CT ct
, char *cp
)
2813 unsigned char *dp
, value
, *ep
;
2819 for (ep
= (dp
= ct
->c_digest
)
2820 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2825 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2827 fprintf (stderr
, "invalid BASE64 encoding\n");
2831 bits
|= value
<< bitno
;
2833 if ((bitno
-= 6) < 0) {
2834 if (dp
+ (3 - skip
) > ep
)
2835 goto invalid_digest
;
2836 *dp
++ = (bits
>> 16) & 0xff;
2838 *dp
++ = (bits
>> 8) & 0xff;
2840 *dp
++ = bits
& 0xff;
2850 goto self_delimiting
;
2855 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2865 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2873 fprintf (stderr
, "MD5 digest=");
2874 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2875 fprintf (stderr
, "%02x", *dp
& 0xff);
2876 fprintf (stderr
, "\n");