]>
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 * Instruct parser not to detect invalid Content-Transfer-Encoding
43 int skip_mp_cte_check
;
46 * Structures for TEXT messages
48 struct k2v SubText
[] = {
49 { "plain", TEXT_PLAIN
},
50 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
51 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
52 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
55 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
58 * Structures for MULTIPART messages
60 struct k2v SubMultiPart
[] = {
61 { "mixed", MULTI_MIXED
},
62 { "alternative", MULTI_ALTERNATE
},
63 { "digest", MULTI_DIGEST
},
64 { "parallel", MULTI_PARALLEL
},
65 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
69 * Structures for MESSAGE messages
71 struct k2v SubMessage
[] = {
72 { "rfc822", MESSAGE_RFC822
},
73 { "partial", MESSAGE_PARTIAL
},
74 { "external-body", MESSAGE_EXTERNAL
},
75 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
79 * Structure for APPLICATION messages
81 struct k2v SubApplication
[] = {
82 { "octet-stream", APPLICATION_OCTETS
},
83 { "postscript", APPLICATION_POSTSCRIPT
},
84 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
89 int find_cache (CT
, int, int *, char *, char *, int);
92 int part_ok (CT
, int);
93 int type_ok (CT
, int);
94 void content_error (char *, CT
, char *, ...);
97 void free_encoding (CT
, int);
102 static CT
get_content (FILE *, char *, int);
103 static int get_comment (CT
, char **, int);
105 static int InitGeneric (CT
);
106 static int InitText (CT
);
107 static int InitMultiPart (CT
);
108 static void reverse_parts (CT
);
109 static int InitMessage (CT
);
110 static int InitApplication (CT
);
111 static int init_encoding (CT
, OpenCEFunc
);
112 static unsigned long size_encoding (CT
);
113 static int InitBase64 (CT
);
114 static int openBase64 (CT
, char **);
115 static int InitQuoted (CT
);
116 static int openQuoted (CT
, char **);
117 static int Init7Bit (CT
);
118 static int openExternal (CT
, CT
, CE
, char **, int *);
119 static int InitFile (CT
);
120 static int openFile (CT
, char **);
121 static int InitFTP (CT
);
122 static int openFTP (CT
, char **);
123 static int InitMail (CT
);
124 static int openMail (CT
, char **);
125 static int readDigest (CT
, char *);
126 static int get_leftover_mp_content (CT
, int);
128 struct str2init str2cts
[] = {
129 { "application", CT_APPLICATION
, InitApplication
},
130 { "audio", CT_AUDIO
, InitGeneric
},
131 { "image", CT_IMAGE
, InitGeneric
},
132 { "message", CT_MESSAGE
, InitMessage
},
133 { "multipart", CT_MULTIPART
, InitMultiPart
},
134 { "text", CT_TEXT
, InitText
},
135 { "video", CT_VIDEO
, InitGeneric
},
136 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
137 { NULL
, CT_UNKNOWN
, NULL
},
140 struct str2init str2ces
[] = {
141 { "base64", CE_BASE64
, InitBase64
},
142 { "quoted-printable", CE_QUOTED
, InitQuoted
},
143 { "8bit", CE_8BIT
, Init7Bit
},
144 { "7bit", CE_7BIT
, Init7Bit
},
145 { "binary", CE_BINARY
, Init7Bit
},
146 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
147 { NULL
, CE_UNKNOWN
, NULL
},
151 * NOTE WELL: si_key MUST NOT have value of NOTOK
153 * si_key is 1 if access method is anonymous.
155 struct str2init str2methods
[] = {
156 { "afs", 1, InitFile
},
157 { "anon-ftp", 1, InitFTP
},
158 { "ftp", 0, InitFTP
},
159 { "local-file", 0, InitFile
},
160 { "mail-server", 0, InitMail
},
166 pidcheck (int status
)
168 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
179 * Main entry point for parsing a MIME message or file.
180 * It returns the Content structure for the top level
181 * entity in the file.
185 parse_mime (char *file
)
193 * Check if file is actually standard input
195 if ((is_stdin
= !(strcmp (file
, "-")))) {
196 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
198 advise("mhparse", "unable to create temporary file");
201 file
= add (tfile
, NULL
);
204 while (fgets (buffer
, sizeof(buffer
), stdin
))
208 if (ferror (stdin
)) {
210 advise ("stdin", "error reading");
215 advise (file
, "error writing");
218 fseek (fp
, 0L, SEEK_SET
);
219 } else if ((fp
= fopen (file
, "r")) == NULL
) {
220 advise (file
, "unable to read");
224 if (!(ct
= get_content (fp
, file
, 1))) {
227 advise (NULL
, "unable to decode %s", file
);
232 ct
->c_unlink
= 1; /* temp file to remove */
236 if (ct
->c_end
== 0L) {
237 fseek (fp
, 0L, SEEK_END
);
238 ct
->c_end
= ftell (fp
);
241 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
253 * Main routine for reading/parsing the headers
254 * of a message content.
256 * toplevel = 1 # we are at the top level of the message
257 * toplevel = 0 # we are inside message type or multipart type
258 * # other than multipart/digest
259 * toplevel = -1 # we are inside multipart/digest
260 * NB: on failure we will fclose(in)!
264 get_content (FILE *in
, char *file
, int toplevel
)
267 char buf
[BUFSIZ
], name
[NAMESZ
];
271 m_getfld_state_t gstate
= 0;
273 /* allocate the content structure */
274 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
275 adios (NULL
, "out of memory");
278 ct
->c_file
= add (file
, NULL
);
279 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
282 * Parse the header fields for this
283 * content into a linked list.
285 m_getfld_track_filepos (&gstate
, in
);
286 for (compnum
= 1;;) {
287 int bufsz
= sizeof buf
;
288 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
293 /* get copies of the buffers */
294 np
= add (name
, NULL
);
295 vp
= add (buf
, NULL
);
297 /* if necessary, get rest of field */
298 while (state
== FLDPLUS
) {
300 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
301 vp
= add (buf
, vp
); /* add to previous value */
304 /* Now add the header data to the list */
305 add_header (ct
, np
, vp
);
307 /* continue, to see if this isn't the last header field */
308 ct
->c_begin
= ftell (in
) + 1;
312 ct
->c_begin
= ftell (in
) - strlen (buf
);
316 ct
->c_begin
= ftell (in
);
321 adios (NULL
, "message format error in component #%d", compnum
);
324 adios (NULL
, "getfld() returned %d", state
);
327 /* break out of the loop */
330 m_getfld_state_destroy (&gstate
);
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
)) {
347 advise (NULL
, "message %s has multiple %s: fields",
348 ct
->c_file
, VRSN_FIELD
);
351 ct
->c_vrsn
= add (hp
->value
, NULL
);
353 /* Now, cleanup this field */
356 while (isspace ((unsigned char) *cp
))
358 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
360 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
361 if (!isspace ((unsigned char) *dp
))
365 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
367 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
)
370 for (dp
= cp
; istoken (*dp
); dp
++)
374 ucmp
= !mh_strcasecmp (cp
, VRSN_VALUE
);
377 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
378 ct
->c_file
, VRSN_FIELD
, cp
);
381 else if (!mh_strcasecmp (hp
->name
, TYPE_FIELD
)) {
382 /* Get Content-Type field */
383 struct str2init
*s2i
;
384 CI ci
= &ct
->c_ctinfo
;
386 /* Check if we've already seen a Content-Type header */
388 advise (NULL
, "message %s has multiple %s: fields",
389 ct
->c_file
, TYPE_FIELD
);
393 /* Parse the Content-Type field */
394 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
398 * Set the Init function and the internal
399 * flag for this content type.
401 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
402 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
404 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
406 ct
->c_type
= s2i
->si_val
;
407 ct
->c_ctinitfnx
= s2i
->si_init
;
409 else if (!mh_strcasecmp (hp
->name
, ENCODING_FIELD
)) {
410 /* Get Content-Transfer-Encoding field */
412 struct str2init
*s2i
;
415 * Check if we've already seen the
416 * Content-Transfer-Encoding field
419 advise (NULL
, "message %s has multiple %s: fields",
420 ct
->c_file
, ENCODING_FIELD
);
424 /* get copy of this field */
425 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
427 while (isspace ((unsigned char) *cp
))
429 for (dp
= cp
; istoken (*dp
); dp
++)
435 * Find the internal flag and Init function
436 * for this transfer encoding.
438 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
439 if (!mh_strcasecmp (cp
, s2i
->si_key
))
441 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
444 ct
->c_encoding
= s2i
->si_val
;
446 /* Call the Init function for this encoding */
447 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
450 else if (!mh_strcasecmp (hp
->name
, MD5_FIELD
)) {
451 /* Get Content-MD5 field */
457 if (ct
->c_digested
) {
458 advise (NULL
, "message %s has multiple %s: fields",
459 ct
->c_file
, MD5_FIELD
);
463 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
465 while (isspace ((unsigned char) *cp
))
467 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
469 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
470 if (!isspace ((unsigned char) *dp
))
474 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
476 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
) {
481 for (dp
= cp
; *dp
&& !isspace ((unsigned char) *dp
); dp
++)
489 else if (!mh_strcasecmp (hp
->name
, ID_FIELD
)) {
490 /* Get Content-ID field */
491 ct
->c_id
= add (hp
->value
, ct
->c_id
);
493 else if (!mh_strcasecmp (hp
->name
, DESCR_FIELD
)) {
494 /* Get Content-Description field */
495 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
497 else if (!mh_strcasecmp (hp
->name
, DISPO_FIELD
)) {
498 /* Get Content-Disposition field */
499 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
503 hp
= hp
->next
; /* next header field */
507 * Check if we saw a Content-Type field.
508 * If not, then assign a default value for
509 * it, and the Init function.
513 * If we are inside a multipart/digest message,
514 * so default type is message/rfc822
517 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
519 ct
->c_type
= CT_MESSAGE
;
520 ct
->c_ctinitfnx
= InitMessage
;
523 * Else default type is text/plain
525 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
527 ct
->c_type
= CT_TEXT
;
528 ct
->c_ctinitfnx
= InitText
;
532 /* Use default Transfer-Encoding, if necessary */
534 ct
->c_encoding
= CE_7BIT
;
547 * small routine to add header field to list
551 add_header (CT ct
, char *name
, char *value
)
555 /* allocate header field structure */
556 hp
= mh_xmalloc (sizeof(*hp
));
558 /* link data into header structure */
563 /* link header structure into the list */
564 if (ct
->c_first_hf
== NULL
) {
565 ct
->c_first_hf
= hp
; /* this is the first */
568 ct
->c_last_hf
->next
= hp
; /* add it to the end */
576 /* Make sure that buf contains at least one appearance of name,
577 followed by =. If not, insert both name and value, just after
578 first semicolon, if any. Note that name should not contain a
579 trailing =. And quotes will be added around the value. Typical
580 usage: make sure that a Content-Disposition header contains
581 filename="foo". If it doesn't and value does, use value from
584 incl_name_value (char *buf
, char *name
, char *value
) {
587 /* Assume that name is non-null. */
589 char *name_plus_equal
= concat (name
, "=", NULL
);
591 if (! strstr (buf
, name_plus_equal
)) {
593 char *cp
, *prefix
, *suffix
;
595 /* Trim trailing space, esp. newline. */
596 for (cp
= &buf
[strlen (buf
) - 1];
597 cp
>= buf
&& isspace ((unsigned char) *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 (char *cp
, CT ct
, int magic
)
666 char *dp
, **ap
, **ep
;
671 i
= strlen (invo_name
) + 2;
673 /* store copy of Content-Type line */
674 cp
= ct
->c_ctline
= add (cp
, NULL
);
676 while (isspace ((unsigned char) *cp
)) /* trim leading spaces */
679 /* change newlines to spaces */
680 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
683 /* trim trailing spaces */
684 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
685 if (!isspace ((unsigned char) *dp
))
690 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
692 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
695 for (dp
= cp
; istoken (*dp
); dp
++)
698 ci
->ci_type
= add (cp
, NULL
); /* store content type */
702 advise (NULL
, "invalid %s: field in message %s (empty type)",
703 TYPE_FIELD
, ct
->c_file
);
707 /* down case the content type string */
708 for (dp
= ci
->ci_type
; *dp
; dp
++)
709 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
710 *dp
= tolower ((unsigned char) *dp
);
712 while (isspace ((unsigned char) *cp
))
715 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
720 ci
->ci_subtype
= add ("", NULL
);
725 while (isspace ((unsigned char) *cp
))
728 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
731 for (dp
= cp
; istoken (*dp
); dp
++)
734 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
737 if (!*ci
->ci_subtype
) {
739 "invalid %s: field in message %s (empty subtype for \"%s\")",
740 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
744 /* down case the content subtype string */
745 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
746 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
747 *dp
= tolower ((unsigned char) *dp
);
750 while (isspace ((unsigned char) *cp
))
753 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
757 * Parse attribute/value pairs given with Content-Type
759 ep
= (ap
= ci
->ci_attrs
) + NPARMS
;
765 "too many parameters in message %s's %s: field (%d max)",
766 ct
->c_file
, TYPE_FIELD
, NPARMS
);
771 while (isspace ((unsigned char) *cp
))
774 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
779 "extraneous trailing ';' in message %s's %s: parameter list",
780 ct
->c_file
, TYPE_FIELD
);
784 /* down case the attribute name */
785 for (dp
= cp
; istoken ((unsigned char) *dp
); dp
++)
786 if (isalpha((unsigned char) *dp
) && isupper ((unsigned char) *dp
))
787 *dp
= tolower ((unsigned char) *dp
);
789 for (up
= dp
; isspace ((unsigned char) *dp
);)
791 if (dp
== cp
|| *dp
!= '=') {
793 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
794 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
, dp
- cp
);
798 vp
= (*ap
= add (cp
, NULL
)) + (up
- cp
);
800 for (dp
++; isspace ((unsigned char) *dp
);)
803 /* now add the attribute value */
804 ci
->ci_values
[ap
- ci
->ci_attrs
] = vp
= *ap
+ (dp
- cp
);
807 for (cp
= ++dp
, dp
= vp
;;) {
812 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
813 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
818 if ((c
= *cp
++) == '\0')
833 for (cp
= dp
, dp
= vp
; istoken (*cp
); cp
++, dp
++)
839 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
840 ct
->c_file
, TYPE_FIELD
, i
, i
, "", *ap
);
845 while (isspace ((unsigned char) *cp
))
848 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
853 * Get any <Content-Id> given in buffer
855 if (magic
&& *cp
== '<') {
860 if (!(dp
= strchr(ct
->c_id
= ++cp
, '>'))) {
861 advise (NULL
, "invalid ID in message %s", ct
->c_file
);
867 ct
->c_id
= concat ("<", ct
->c_id
, ">\n", NULL
);
873 while (isspace ((unsigned char) *cp
))
878 * Get any [Content-Description] given in buffer.
880 if (magic
&& *cp
== '[') {
882 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
886 advise (NULL
, "invalid description in message %s", ct
->c_file
);
894 ct
->c_descr
= concat (ct
->c_descr
, "\n", NULL
);
900 while (isspace ((unsigned char) *cp
))
905 * Get any {Content-Disposition} given in buffer.
907 if (magic
&& *cp
== '{') {
909 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
913 advise (NULL
, "invalid disposition in message %s", ct
->c_file
);
921 ct
->c_dispo
= concat (ct
->c_dispo
, "\n", NULL
);
927 while (isspace ((unsigned char) *cp
))
932 * Check if anything is left over
936 ci
->ci_magic
= add (cp
, NULL
);
938 /* If there is a Content-Disposition header and it doesn't
939 have a *filename=, extract it from the magic contents.
940 The r1bindex call skips any leading directory
944 incl_name_value (ct
->c_dispo
,
946 r1bindex (extract_name_value ("name",
953 "extraneous information in message %s's %s: field\n%*.*s(%s)",
954 ct
->c_file
, TYPE_FIELD
, i
, i
, "", cp
);
962 get_comment (CT ct
, char **ap
, int istype
)
966 char c
, buffer
[BUFSIZ
], *dp
;
978 advise (NULL
, "invalid comment in message %s's %s: field",
979 ct
->c_file
, istype
? TYPE_FIELD
: VRSN_FIELD
);
984 if ((c
= *cp
++) == '\0')
1007 if ((dp
= ci
->ci_comment
)) {
1008 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
1011 ci
->ci_comment
= add (buffer
, NULL
);
1015 while (isspace ((unsigned char) *cp
))
1026 * Handles content types audio, image, and video.
1027 * There's not much to do right here.
1035 return OK
; /* not much to do here */
1046 char buffer
[BUFSIZ
];
1048 char **ap
, **ep
, *cp
;
1051 CI ci
= &ct
->c_ctinfo
;
1053 /* check for missing subtype */
1054 if (!*ci
->ci_subtype
)
1055 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1058 for (kv
= SubText
; kv
->kv_key
; kv
++)
1059 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1061 ct
->c_subtype
= kv
->kv_value
;
1063 /* allocate text character set structure */
1064 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1065 adios (NULL
, "out of memory");
1066 ct
->c_ctparams
= (void *) t
;
1068 /* scan for charset parameter */
1069 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1070 if (!mh_strcasecmp (*ap
, "charset"))
1073 /* check if content specified a character set */
1076 t
->tx_charset
= CHARSET_SPECIFIED
;
1078 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1082 * If we can not handle character set natively,
1083 * then check profile for string to modify the
1084 * terminal or display method.
1086 * termproc is for mhshow, though mhlist -debug prints it, too.
1088 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1089 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1090 if ((cp
= context_find (buffer
)))
1091 ct
->c_termproc
= getcpy (cp
);
1103 InitMultiPart (CT ct
)
1107 char *cp
, *dp
, **ap
, **ep
;
1108 char *bp
, buffer
[BUFSIZ
];
1109 struct multipart
*m
;
1111 struct part
*part
, **next
;
1112 CI ci
= &ct
->c_ctinfo
;
1117 * The encoding for multipart messages must be either
1118 * 7bit, 8bit, or binary (per RFC2045).
1120 if (! skip_mp_cte_check
&& ct
->c_encoding
!= CE_7BIT
&&
1121 ct
->c_encoding
!= CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
) {
1122 /* Copy the Content-Transfer-Encoding header field body so we can
1123 remove any trailing whitespace and leading blanks from it. */
1124 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1126 bp
= cte
+ strlen (cte
) - 1;
1127 while (bp
>= cte
&& isspace ((unsigned char) *bp
)) *bp
-- = '\0';
1128 for (bp
= cte
; *bp
&& isblank ((unsigned char) *bp
); ++bp
) continue;
1131 "\"%s/%s\" type in message %s must be encoded in\n"
1132 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1133 "is to\nmanually edit the file and change the \"%s\"\n"
1134 "Content-Transfer-Encoding to one of those. For now",
1135 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1142 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1143 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1145 ct
->c_subtype
= kv
->kv_value
;
1148 * Check for "boundary" parameter, which is
1149 * required for multipart messages.
1152 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1153 if (!mh_strcasecmp (*ap
, "boundary")) {
1159 /* complain if boundary parameter is missing */
1162 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1163 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1167 /* allocate primary structure for multipart info */
1168 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1169 adios (NULL
, "out of memory");
1170 ct
->c_ctparams
= (void *) m
;
1172 /* check if boundary parameter contains only whitespace characters */
1173 for (cp
= bp
; isspace ((unsigned char) *cp
); cp
++)
1176 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1177 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1181 /* remove trailing whitespace from boundary parameter */
1182 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1183 if (!isspace ((unsigned char) *dp
))
1187 /* record boundary separators */
1188 m
->mp_start
= concat (bp
, "\n", NULL
);
1189 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1191 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1192 advise (ct
->c_file
, "unable to open for reading");
1196 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1198 next
= &m
->mp_parts
;
1202 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1206 pos
+= strlen (buffer
);
1207 if (buffer
[0] != '-' || buffer
[1] != '-')
1210 if (strcmp (buffer
+ 2, m
->mp_start
))
1213 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1214 adios (NULL
, "out of memory");
1216 next
= &part
->mp_next
;
1218 if (!(p
= get_content (fp
, ct
->c_file
,
1219 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1226 fseek (fp
, pos
, SEEK_SET
);
1229 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1233 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1234 if (p
->c_end
< p
->c_begin
)
1235 p
->c_begin
= p
->c_end
;
1240 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1246 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1247 if (!inout
&& part
) {
1249 p
->c_end
= ct
->c_end
;
1251 if (p
->c_begin
>= p
->c_end
) {
1252 for (next
= &m
->mp_parts
; *next
!= part
;
1253 next
= &((*next
)->mp_next
))
1257 free ((char *) part
);
1262 /* reverse the order of the parts for multipart/alternative */
1263 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1267 * label all subparts with part number, and
1268 * then initialize the content of the subpart.
1273 char partnam
[BUFSIZ
];
1276 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1277 pp
= partnam
+ strlen (partnam
);
1282 for (part
= m
->mp_parts
, partnum
= 1; part
;
1283 part
= part
->mp_next
, partnum
++) {
1286 sprintf (pp
, "%d", partnum
);
1287 p
->c_partno
= add (partnam
, NULL
);
1289 /* initialize the content of the subparts */
1290 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1298 get_leftover_mp_content (ct
, 1);
1299 get_leftover_mp_content (ct
, 0);
1308 * reverse the order of the parts of a multipart
1312 reverse_parts (CT ct
)
1315 struct multipart
*m
;
1316 struct part
**base
, **bmp
, **next
, *part
;
1318 m
= (struct multipart
*) ct
->c_ctparams
;
1320 /* if only one part, just return */
1321 if (!m
->mp_parts
|| !m
->mp_parts
->mp_next
)
1324 /* count number of parts */
1326 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1329 /* allocate array of pointers to the parts */
1330 if (!(base
= (struct part
**) calloc ((size_t) (i
+ 1), sizeof(*base
))))
1331 adios (NULL
, "out of memory");
1334 /* point at all the parts */
1335 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1339 /* reverse the order of the parts */
1340 next
= &m
->mp_parts
;
1341 for (bmp
--; bmp
>= base
; bmp
--) {
1344 next
= &part
->mp_next
;
1348 /* free array of pointers */
1349 free ((char *) base
);
1361 CI ci
= &ct
->c_ctinfo
;
1363 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1365 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1366 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1370 /* check for missing subtype */
1371 if (!*ci
->ci_subtype
)
1372 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1375 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1376 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1378 ct
->c_subtype
= kv
->kv_value
;
1380 switch (ct
->c_subtype
) {
1381 case MESSAGE_RFC822
:
1384 case MESSAGE_PARTIAL
:
1389 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1390 adios (NULL
, "out of memory");
1391 ct
->c_ctparams
= (void *) p
;
1393 /* scan for parameters "id", "number", and "total" */
1394 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1395 if (!mh_strcasecmp (*ap
, "id")) {
1396 p
->pm_partid
= add (*ep
, NULL
);
1399 if (!mh_strcasecmp (*ap
, "number")) {
1400 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1401 || p
->pm_partno
< 1) {
1404 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1405 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1406 ct
->c_file
, TYPE_FIELD
);
1411 if (!mh_strcasecmp (*ap
, "total")) {
1412 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1421 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1423 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1424 ci
->ci_type
, ci
->ci_subtype
,
1425 ct
->c_file
, TYPE_FIELD
);
1431 case MESSAGE_EXTERNAL
:
1438 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1439 adios (NULL
, "out of memory");
1440 ct
->c_ctparams
= (void *) e
;
1443 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1444 advise (ct
->c_file
, "unable to open for reading");
1448 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1450 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1458 if ((exresult
= params_external (ct
, 0)) != NOTOK
1459 && p
->c_ceopenfnx
== openMail
) {
1463 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1465 content_error (NULL
, ct
,
1466 "empty body for access-type=mail-server");
1470 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1471 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1473 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1475 adios ("failed", "fread");
1478 adios (NULL
, "unexpected EOF from fread");
1481 bp
+= cc
, size
-= cc
;
1488 p
->c_end
= p
->c_begin
;
1493 if (exresult
== NOTOK
)
1495 if (e
->eb_flags
== NOTOK
)
1498 switch (p
->c_type
) {
1503 if (p
->c_subtype
!= MESSAGE_RFC822
)
1507 e
->eb_partno
= ct
->c_partno
;
1509 (*p
->c_ctinitfnx
) (p
);
1524 params_external (CT ct
, int composing
)
1527 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1528 CI ci
= &ct
->c_ctinfo
;
1530 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1531 if (!mh_strcasecmp (*ap
, "access-type")) {
1532 struct str2init
*s2i
;
1533 CT p
= e
->eb_content
;
1535 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1536 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1540 e
->eb_flags
= NOTOK
;
1541 p
->c_encoding
= CE_EXTERNAL
;
1544 e
->eb_access
= s2i
->si_key
;
1545 e
->eb_flags
= s2i
->si_val
;
1546 p
->c_encoding
= CE_EXTERNAL
;
1548 /* Call the Init function for this external type */
1549 if ((*s2i
->si_init
)(p
) == NOTOK
)
1553 if (!mh_strcasecmp (*ap
, "name")) {
1557 if (!mh_strcasecmp (*ap
, "permission")) {
1558 e
->eb_permission
= *ep
;
1561 if (!mh_strcasecmp (*ap
, "site")) {
1565 if (!mh_strcasecmp (*ap
, "directory")) {
1569 if (!mh_strcasecmp (*ap
, "mode")) {
1573 if (!mh_strcasecmp (*ap
, "size")) {
1574 sscanf (*ep
, "%lu", &e
->eb_size
);
1577 if (!mh_strcasecmp (*ap
, "server")) {
1581 if (!mh_strcasecmp (*ap
, "subject")) {
1582 e
->eb_subject
= *ep
;
1585 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1586 e
->eb_body
= getcpy (*ep
);
1591 if (!e
->eb_access
) {
1593 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1594 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1607 InitApplication (CT ct
)
1610 CI ci
= &ct
->c_ctinfo
;
1613 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1614 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1616 ct
->c_subtype
= kv
->kv_value
;
1623 * TRANSFER ENCODINGS
1627 init_encoding (CT ct
, OpenCEFunc openfnx
)
1631 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
1632 adios (NULL
, "out of memory");
1635 ct
->c_ceopenfnx
= openfnx
;
1636 ct
->c_ceclosefnx
= close_encoding
;
1637 ct
->c_cesizefnx
= size_encoding
;
1644 close_encoding (CT ct
)
1648 if (!(ce
= ct
->c_cefile
))
1658 static unsigned long
1659 size_encoding (CT ct
)
1667 if (!(ce
= ct
->c_cefile
))
1668 return (ct
->c_end
- ct
->c_begin
);
1670 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1671 return (long) st
.st_size
;
1674 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1675 return (long) st
.st_size
;
1680 if (ct
->c_encoding
== CE_EXTERNAL
)
1681 return (ct
->c_end
- ct
->c_begin
);
1684 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1685 return (ct
->c_end
- ct
->c_begin
);
1687 if (fstat (fd
, &st
) != NOTOK
)
1688 size
= (long) st
.st_size
;
1692 (*ct
->c_ceclosefnx
) (ct
);
1701 static unsigned char b642nib
[0x80] = {
1702 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1703 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1704 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1705 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1706 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1707 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1708 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1709 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1710 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1711 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1712 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1713 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1714 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1715 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1716 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1717 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1724 return init_encoding (ct
, openBase64
);
1729 openBase64 (CT ct
, char **file
)
1731 int bitno
, cc
, digested
;
1732 int fd
, len
, skip
, own_ct_fp
= 0;
1734 unsigned char value
, b
;
1735 char *cp
, *ep
, buffer
[BUFSIZ
];
1736 /* sbeck -- handle suffixes */
1743 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1748 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1749 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1755 if (*file
== NULL
) {
1756 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1759 ce
->ce_file
= add (*file
, NULL
);
1763 /* sbeck@cise.ufl.edu -- handle suffixes */
1765 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1766 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1767 cp
= context_find (buffer
);
1768 if (cp
== NULL
|| *cp
== '\0') {
1769 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1771 cp
= context_find (buffer
);
1773 if (cp
!= NULL
&& *cp
!= '\0') {
1774 if (ce
->ce_unlink
) {
1775 /* Temporary file already exists, so we rename to
1776 version with extension. */
1777 char *file_org
= strdup(ce
->ce_file
);
1778 ce
->ce_file
= add (cp
, ce
->ce_file
);
1779 if (rename(file_org
, ce
->ce_file
)) {
1780 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1785 ce
->ce_file
= add (cp
, ce
->ce_file
);
1789 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1790 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1794 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1795 adios (NULL
, "internal error(1)");
1798 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1799 content_error (ct
->c_file
, ct
, "unable to open for reading");
1805 if ((digested
= ct
->c_digested
))
1806 MD5Init (&mdContext
);
1812 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1814 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1816 content_error (ct
->c_file
, ct
, "error reading from");
1820 content_error (NULL
, ct
, "premature eof");
1828 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1831 if (isspace ((unsigned char) *cp
))
1833 if (skip
|| (((unsigned char) *cp
) & 0x80)
1834 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1836 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1837 (unsigned char) *cp
,
1838 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1841 content_error (NULL
, ct
,
1842 "invalid BASE64 encoding -- continuing");
1846 bits
|= value
<< bitno
;
1848 if ((bitno
-= 6) < 0) {
1849 b
= (bits
>> 16) & 0xff;
1850 putc ((char) b
, ce
->ce_fp
);
1852 MD5Update (&mdContext
, &b
, 1);
1854 b
= (bits
>> 8) & 0xff;
1855 putc ((char) b
, ce
->ce_fp
);
1857 MD5Update (&mdContext
, &b
, 1);
1860 putc ((char) b
, ce
->ce_fp
);
1862 MD5Update (&mdContext
, &b
, 1);
1866 if (ferror (ce
->ce_fp
)) {
1867 content_error (ce
->ce_file
, ct
,
1868 "error writing to");
1871 bitno
= 18, bits
= 0L, skip
= 0;
1877 goto self_delimiting
;
1886 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1888 content_error (NULL
, ct
, "invalid BASE64 encoding");
1893 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1895 if (fflush (ce
->ce_fp
)) {
1896 content_error (ce
->ce_file
, ct
, "error writing to");
1901 unsigned char digest
[16];
1903 MD5Final (digest
, &mdContext
);
1904 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1905 sizeof(digest
) / sizeof(digest
[0])))
1906 content_error (NULL
, ct
,
1907 "content integrity suspect (digest mismatch) -- continuing");
1910 fprintf (stderr
, "content integrity confirmed\n");
1913 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1916 *file
= ce
->ce_file
;
1921 return fileno (ce
->ce_fp
);
1928 free_encoding (ct
, 0);
1937 static char hex2nib
[0x80] = {
1938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1944 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1945 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1951 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1952 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1953 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1960 return init_encoding (ct
, openQuoted
);
1965 openQuoted (CT ct
, char **file
)
1967 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1969 char buffer
[BUFSIZ
];
1972 /* sbeck -- handle suffixes */
1978 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1983 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1984 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1990 if (*file
== NULL
) {
1991 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1994 ce
->ce_file
= add (*file
, NULL
);
1998 /* sbeck@cise.ufl.edu -- handle suffixes */
2000 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2001 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2002 cp
= context_find (buffer
);
2003 if (cp
== NULL
|| *cp
== '\0') {
2004 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2006 cp
= context_find (buffer
);
2008 if (cp
!= NULL
&& *cp
!= '\0') {
2009 if (ce
->ce_unlink
) {
2010 /* Temporary file already exists, so we rename to
2011 version with extension. */
2012 char *file_org
= strdup(ce
->ce_file
);
2013 ce
->ce_file
= add (cp
, ce
->ce_file
);
2014 if (rename(file_org
, ce
->ce_file
)) {
2015 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2020 ce
->ce_file
= add (cp
, ce
->ce_file
);
2024 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2025 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2029 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2030 adios (NULL
, "internal error(2)");
2033 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2034 content_error (ct
->c_file
, ct
, "unable to open for reading");
2040 if ((digested
= ct
->c_digested
))
2041 MD5Init (&mdContext
);
2048 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2050 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2051 content_error (NULL
, ct
, "premature eof");
2055 if ((cc
= strlen (buffer
)) > len
)
2059 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2060 if (!isspace ((unsigned char) *ep
))
2064 for (; cp
< ep
; cp
++) {
2066 /* in an escape sequence */
2068 /* at byte 1 of an escape sequence */
2069 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2070 /* next is byte 2 */
2073 /* at byte 2 of an escape sequence */
2075 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2076 putc (mask
, ce
->ce_fp
);
2078 MD5Update (&mdContext
, &mask
, 1);
2079 if (ferror (ce
->ce_fp
)) {
2080 content_error (ce
->ce_file
, ct
, "error writing to");
2083 /* finished escape sequence; next may be literal or a new
2084 * escape sequence */
2087 /* on to next byte */
2091 /* not in an escape sequence */
2093 /* starting an escape sequence, or invalid '='? */
2094 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2095 /* "=\n" soft line break, eat the \n */
2099 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2100 /* We don't have 2 bytes left, so this is an invalid
2101 * escape sequence; just show the raw bytes (below). */
2102 } else if (isxdigit ((unsigned char) cp
[1]) &&
2103 isxdigit ((unsigned char) cp
[2])) {
2104 /* Next 2 bytes are hex digits, making this a valid escape
2105 * sequence; let's decode it (above). */
2109 /* One or both of the next 2 is out of range, making this
2110 * an invalid escape sequence; just show the raw bytes
2115 /* Just show the raw byte. */
2116 putc (*cp
, ce
->ce_fp
);
2119 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2121 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2124 if (ferror (ce
->ce_fp
)) {
2125 content_error (ce
->ce_file
, ct
, "error writing to");
2131 content_error (NULL
, ct
,
2132 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2136 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2138 if (fflush (ce
->ce_fp
)) {
2139 content_error (ce
->ce_file
, ct
, "error writing to");
2144 unsigned char digest
[16];
2146 MD5Final (digest
, &mdContext
);
2147 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2148 sizeof(digest
) / sizeof(digest
[0])))
2149 content_error (NULL
, ct
,
2150 "content integrity suspect (digest mismatch) -- continuing");
2153 fprintf (stderr
, "content integrity confirmed\n");
2156 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2159 *file
= ce
->ce_file
;
2164 return fileno (ce
->ce_fp
);
2167 free_encoding (ct
, 0);
2183 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2186 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2192 open7Bit (CT ct
, char **file
)
2194 int cc
, fd
, len
, own_ct_fp
= 0;
2195 char buffer
[BUFSIZ
];
2196 /* sbeck -- handle suffixes */
2203 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2208 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2209 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2215 if (*file
== NULL
) {
2216 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2219 ce
->ce_file
= add (*file
, NULL
);
2223 /* sbeck@cise.ufl.edu -- handle suffixes */
2225 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2226 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2227 cp
= context_find (buffer
);
2228 if (cp
== NULL
|| *cp
== '\0') {
2229 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2231 cp
= context_find (buffer
);
2233 if (cp
!= NULL
&& *cp
!= '\0') {
2234 if (ce
->ce_unlink
) {
2235 /* Temporary file already exists, so we rename to
2236 version with extension. */
2237 char *file_org
= strdup(ce
->ce_file
);
2238 ce
->ce_file
= add (cp
, ce
->ce_file
);
2239 if (rename(file_org
, ce
->ce_file
)) {
2240 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2245 ce
->ce_file
= add (cp
, ce
->ce_file
);
2249 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2250 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2254 if (ct
->c_type
== CT_MULTIPART
) {
2256 CI ci
= &ct
->c_ctinfo
;
2259 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2260 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2261 + 1 + strlen (ci
->ci_subtype
);
2262 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2263 putc (';', ce
->ce_fp
);
2266 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2268 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2269 fputs ("\n\t", ce
->ce_fp
);
2272 putc (' ', ce
->ce_fp
);
2275 fprintf (ce
->ce_fp
, "%s", buffer
);
2279 if (ci
->ci_comment
) {
2280 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2281 fputs ("\n\t", ce
->ce_fp
);
2285 putc (' ', ce
->ce_fp
);
2288 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2291 fprintf (ce
->ce_fp
, "\n");
2293 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2295 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2297 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2298 fprintf (ce
->ce_fp
, "\n");
2301 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2302 adios (NULL
, "internal error(3)");
2305 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2306 content_error (ct
->c_file
, ct
, "unable to open for reading");
2312 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2314 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2316 content_error (ct
->c_file
, ct
, "error reading from");
2320 content_error (NULL
, ct
, "premature eof");
2328 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2329 if (ferror (ce
->ce_fp
)) {
2330 content_error (ce
->ce_file
, ct
, "error writing to");
2335 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2337 if (fflush (ce
->ce_fp
)) {
2338 content_error (ce
->ce_file
, ct
, "error writing to");
2342 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2345 *file
= ce
->ce_file
;
2350 return fileno (ce
->ce_fp
);
2353 free_encoding (ct
, 0);
2367 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2369 char cachefile
[BUFSIZ
];
2372 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2377 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2378 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2384 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2385 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2386 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2387 ce
->ce_file
= getcpy (cachefile
);
2391 admonish (cachefile
, "unable to fopen for reading");
2398 *file
= ce
->ce_file
;
2399 *fd
= fileno (ce
->ce_fp
);
2410 return init_encoding (ct
, openFile
);
2415 openFile (CT ct
, char **file
)
2418 char cachefile
[BUFSIZ
];
2419 struct exbody
*e
= ct
->c_ctexbody
;
2420 CE ce
= ct
->c_cefile
;
2422 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2434 content_error (NULL
, ct
, "missing name parameter");
2438 ce
->ce_file
= getcpy (e
->eb_name
);
2441 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2442 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2446 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2447 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2448 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2452 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2453 if ((fp
= fopen (cachefile
, "w"))) {
2455 char buffer
[BUFSIZ
];
2456 FILE *gp
= ce
->ce_fp
;
2458 fseek (gp
, 0L, SEEK_SET
);
2460 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2462 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2466 admonish (ce
->ce_file
, "error reading");
2471 admonish (cachefile
, "error writing");
2479 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2480 *file
= ce
->ce_file
;
2481 return fileno (ce
->ce_fp
);
2491 return init_encoding (ct
, openFTP
);
2496 openFTP (CT ct
, char **file
)
2498 int cachetype
, caching
, fd
;
2500 char *bp
, *ftp
, *user
, *pass
;
2501 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2504 static char *username
= NULL
;
2505 static char *password
= NULL
;
2510 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2516 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2527 if (!e
->eb_name
|| !e
->eb_site
) {
2528 content_error (NULL
, ct
, "missing %s parameter",
2529 e
->eb_name
? "site": "name");
2536 pidcheck (pidwait (xpid
, NOTOK
));
2540 /* Get the buffer ready to go */
2542 buflen
= sizeof(buffer
);
2545 * Construct the query message for user
2547 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2553 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2559 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2560 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2565 if (e
->eb_size
> 0) {
2566 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2571 snprintf (bp
, buflen
, "? ");
2574 * Now, check the answer
2576 if (!getanswer (buffer
))
2581 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2585 ruserpass (e
->eb_site
, &username
, &password
);
2590 ce
->ce_unlink
= (*file
== NULL
);
2592 cachefile
[0] = '\0';
2593 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2594 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2595 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2596 if (*file
== NULL
) {
2603 ce
->ce_file
= add (*file
, NULL
);
2605 ce
->ce_file
= add (cachefile
, NULL
);
2607 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2609 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2610 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2615 int child_id
, i
, vecp
;
2619 vec
[vecp
++] = r1bindex (ftp
, '/');
2620 vec
[vecp
++] = e
->eb_site
;
2623 vec
[vecp
++] = e
->eb_dir
;
2624 vec
[vecp
++] = e
->eb_name
;
2625 vec
[vecp
++] = ce
->ce_file
,
2626 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2627 ? "ascii" : "binary";
2632 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2636 adios ("fork", "unable to");
2640 close (fileno (ce
->ce_fp
));
2642 fprintf (stderr
, "unable to exec ");
2648 if (pidXwait (child_id
, NULL
)) {
2649 username
= password
= NULL
;
2659 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2664 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2665 if ((fp
= fopen (cachefile
, "w"))) {
2667 FILE *gp
= ce
->ce_fp
;
2669 fseek (gp
, 0L, SEEK_SET
);
2671 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2673 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2677 admonish (ce
->ce_file
, "error reading");
2682 admonish (cachefile
, "error writing");
2691 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2692 *file
= ce
->ce_file
;
2693 return fileno (ce
->ce_fp
);
2704 return init_encoding (ct
, openMail
);
2709 openMail (CT ct
, char **file
)
2711 int child_id
, fd
, i
, vecp
;
2713 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2714 struct exbody
*e
= ct
->c_ctexbody
;
2715 CE ce
= ct
->c_cefile
;
2717 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2728 if (!e
->eb_server
) {
2729 content_error (NULL
, ct
, "missing server parameter");
2736 pidcheck (pidwait (xpid
, NOTOK
));
2740 /* Get buffer ready to go */
2742 buflen
= sizeof(buffer
);
2744 /* Now, construct query message */
2745 snprintf (bp
, buflen
, "Retrieve content");
2751 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2757 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2759 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2761 /* Now, check answer */
2762 if (!getanswer (buffer
))
2766 vec
[vecp
++] = r1bindex (mailproc
, '/');
2767 vec
[vecp
++] = e
->eb_server
;
2768 vec
[vecp
++] = "-subject";
2769 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2770 vec
[vecp
++] = "-body";
2771 vec
[vecp
++] = e
->eb_body
;
2774 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2778 advise ("fork", "unable to");
2782 execvp (mailproc
, vec
);
2783 fprintf (stderr
, "unable to exec ");
2789 if (pidXwait (child_id
, NULL
) == OK
)
2790 advise (NULL
, "request sent");
2794 if (*file
== NULL
) {
2795 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2798 ce
->ce_file
= add (*file
, NULL
);
2802 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2803 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2807 /* showproc is for mhshow and mhstore, though mhlist -debug
2808 * prints it, too. */
2810 free (ct
->c_showproc
);
2811 ct
->c_showproc
= add ("true", NULL
);
2813 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2814 *file
= ce
->ce_file
;
2815 return fileno (ce
->ce_fp
);
2820 readDigest (CT ct
, char *cp
)
2825 unsigned char *dp
, value
, *ep
;
2831 for (ep
= (dp
= ct
->c_digest
)
2832 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2837 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2839 fprintf (stderr
, "invalid BASE64 encoding\n");
2843 bits
|= value
<< bitno
;
2845 if ((bitno
-= 6) < 0) {
2846 if (dp
+ (3 - skip
) > ep
)
2847 goto invalid_digest
;
2848 *dp
++ = (bits
>> 16) & 0xff;
2850 *dp
++ = (bits
>> 8) & 0xff;
2852 *dp
++ = bits
& 0xff;
2862 goto self_delimiting
;
2867 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2877 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2885 fprintf (stderr
, "MD5 digest=");
2886 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2887 fprintf (stderr
, "%02x", *dp
& 0xff);
2888 fprintf (stderr
, "\n");
2895 /* Multipart parts might have content before the first subpart and/or
2896 after the last subpart that hasn't been stored anywhere else, so do
2899 get_leftover_mp_content (CT ct
, int before
/* or after */) {
2900 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
2902 int found_boundary
= 0;
2903 char buffer
[BUFSIZ
];
2906 char *content
= NULL
;
2908 if (! m
) return NOTOK
;
2911 if (! m
->mp_parts
|| ! m
->mp_parts
->mp_part
) return NOTOK
;
2913 /* Isolate the beginning of this part to the beginning of the
2914 first subpart and save any content between them. */
2915 fseeko (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2916 max
= m
->mp_parts
->mp_part
->c_begin
- ct
->c_begin
;
2917 boundary
= concat ("--", m
->mp_start
, NULL
);
2919 struct part
*last_subpart
= NULL
;
2920 struct part
*subpart
;
2922 /* Go to the last subpart to get its end position. */
2923 for (subpart
= m
->mp_parts
; subpart
; subpart
= subpart
->mp_next
) {
2924 last_subpart
= subpart
;
2927 if (last_subpart
== NULL
) return NOTOK
;
2929 /* Isolate the end of the last subpart to the end of this part
2930 and save any content between them. */
2931 fseeko (ct
->c_fp
, last_subpart
->mp_part
->c_end
, SEEK_SET
);
2932 max
= ct
->c_end
- last_subpart
->mp_part
->c_end
;
2933 boundary
= concat ("--", m
->mp_stop
, NULL
);
2936 /* Back up by 1 to pick up the newline. */
2937 while (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
)) {
2938 read
+= strlen (buffer
);
2939 /* Don't look beyond beginning of first subpart (before) or
2940 next part (after). */
2941 if (read
> max
) buffer
[read
-max
] = '\0';
2944 if (! strcmp (buffer
, boundary
)) {
2948 if (! found_boundary
&& ! strcmp (buffer
, boundary
)) {
2954 if ((before
&& ! found_boundary
) || (! before
&& found_boundary
)) {
2956 char *old_content
= content
;
2957 content
= concat (content
, buffer
, NULL
);
2961 ? concat ("\n", buffer
, NULL
)
2962 : concat (buffer
, NULL
);
2967 if (found_boundary
|| read
> max
) break;
2969 if (read
> max
) break;
2973 /* Skip the newline if that's all there is. */
2977 /* Remove trailing newline, except at EOF. */
2978 if ((before
|| ! feof (ct
->c_fp
)) &&
2979 (cp
= content
+ strlen (content
)) > content
&&
2984 if (strlen (content
) > 1) {
2986 m
->mp_content_before
= content
;
2988 m
->mp_content_after
= content
;