]>
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_content (CT
);
98 void free_encoding (CT
, int);
103 static CT
get_content (FILE *, char *, int);
104 static int get_comment (CT
, char **, int);
106 static int InitGeneric (CT
);
107 static int InitText (CT
);
108 static int InitMultiPart (CT
);
109 static void reverse_parts (CT
);
110 static int InitMessage (CT
);
111 static int InitApplication (CT
);
112 static int init_encoding (CT
, OpenCEFunc
);
113 static unsigned long size_encoding (CT
);
114 static int InitBase64 (CT
);
115 static int openBase64 (CT
, char **);
116 static int InitQuoted (CT
);
117 static int openQuoted (CT
, char **);
118 static int Init7Bit (CT
);
119 static int openExternal (CT
, CT
, CE
, char **, int *);
120 static int InitFile (CT
);
121 static int openFile (CT
, char **);
122 static int InitFTP (CT
);
123 static int openFTP (CT
, char **);
124 static int InitMail (CT
);
125 static int openMail (CT
, char **);
126 static int readDigest (CT
, char *);
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
) {
1305 * reverse the order of the parts of a multipart
1309 reverse_parts (CT ct
)
1312 struct multipart
*m
;
1313 struct part
**base
, **bmp
, **next
, *part
;
1315 m
= (struct multipart
*) ct
->c_ctparams
;
1317 /* if only one part, just return */
1318 if (!m
->mp_parts
|| !m
->mp_parts
->mp_next
)
1321 /* count number of parts */
1323 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1326 /* allocate array of pointers to the parts */
1327 if (!(base
= (struct part
**) calloc ((size_t) (i
+ 1), sizeof(*base
))))
1328 adios (NULL
, "out of memory");
1331 /* point at all the parts */
1332 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1336 /* reverse the order of the parts */
1337 next
= &m
->mp_parts
;
1338 for (bmp
--; bmp
>= base
; bmp
--) {
1341 next
= &part
->mp_next
;
1345 /* free array of pointers */
1346 free ((char *) base
);
1358 CI ci
= &ct
->c_ctinfo
;
1360 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1362 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1363 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1367 /* check for missing subtype */
1368 if (!*ci
->ci_subtype
)
1369 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1372 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1373 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1375 ct
->c_subtype
= kv
->kv_value
;
1377 switch (ct
->c_subtype
) {
1378 case MESSAGE_RFC822
:
1381 case MESSAGE_PARTIAL
:
1386 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1387 adios (NULL
, "out of memory");
1388 ct
->c_ctparams
= (void *) p
;
1390 /* scan for parameters "id", "number", and "total" */
1391 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1392 if (!mh_strcasecmp (*ap
, "id")) {
1393 p
->pm_partid
= add (*ep
, NULL
);
1396 if (!mh_strcasecmp (*ap
, "number")) {
1397 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1398 || p
->pm_partno
< 1) {
1401 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1402 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1403 ct
->c_file
, TYPE_FIELD
);
1408 if (!mh_strcasecmp (*ap
, "total")) {
1409 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1418 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1420 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1421 ci
->ci_type
, ci
->ci_subtype
,
1422 ct
->c_file
, TYPE_FIELD
);
1428 case MESSAGE_EXTERNAL
:
1435 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1436 adios (NULL
, "out of memory");
1437 ct
->c_ctparams
= (void *) e
;
1440 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1441 advise (ct
->c_file
, "unable to open for reading");
1445 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1447 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1455 if ((exresult
= params_external (ct
, 0)) != NOTOK
1456 && p
->c_ceopenfnx
== openMail
) {
1460 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1462 content_error (NULL
, ct
,
1463 "empty body for access-type=mail-server");
1467 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1468 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1470 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1472 adios ("failed", "fread");
1475 adios (NULL
, "unexpected EOF from fread");
1478 bp
+= cc
, size
-= cc
;
1485 p
->c_end
= p
->c_begin
;
1490 if (exresult
== NOTOK
)
1492 if (e
->eb_flags
== NOTOK
)
1495 switch (p
->c_type
) {
1500 if (p
->c_subtype
!= MESSAGE_RFC822
)
1504 e
->eb_partno
= ct
->c_partno
;
1506 (*p
->c_ctinitfnx
) (p
);
1521 params_external (CT ct
, int composing
)
1524 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1525 CI ci
= &ct
->c_ctinfo
;
1527 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1528 if (!mh_strcasecmp (*ap
, "access-type")) {
1529 struct str2init
*s2i
;
1530 CT p
= e
->eb_content
;
1532 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1533 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1537 e
->eb_flags
= NOTOK
;
1538 p
->c_encoding
= CE_EXTERNAL
;
1541 e
->eb_access
= s2i
->si_key
;
1542 e
->eb_flags
= s2i
->si_val
;
1543 p
->c_encoding
= CE_EXTERNAL
;
1545 /* Call the Init function for this external type */
1546 if ((*s2i
->si_init
)(p
) == NOTOK
)
1550 if (!mh_strcasecmp (*ap
, "name")) {
1554 if (!mh_strcasecmp (*ap
, "permission")) {
1555 e
->eb_permission
= *ep
;
1558 if (!mh_strcasecmp (*ap
, "site")) {
1562 if (!mh_strcasecmp (*ap
, "directory")) {
1566 if (!mh_strcasecmp (*ap
, "mode")) {
1570 if (!mh_strcasecmp (*ap
, "size")) {
1571 sscanf (*ep
, "%lu", &e
->eb_size
);
1574 if (!mh_strcasecmp (*ap
, "server")) {
1578 if (!mh_strcasecmp (*ap
, "subject")) {
1579 e
->eb_subject
= *ep
;
1582 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1583 e
->eb_body
= getcpy (*ep
);
1588 if (!e
->eb_access
) {
1590 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1591 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1604 InitApplication (CT ct
)
1607 CI ci
= &ct
->c_ctinfo
;
1610 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1611 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1613 ct
->c_subtype
= kv
->kv_value
;
1620 * TRANSFER ENCODINGS
1624 init_encoding (CT ct
, OpenCEFunc openfnx
)
1628 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
1629 adios (NULL
, "out of memory");
1632 ct
->c_ceopenfnx
= openfnx
;
1633 ct
->c_ceclosefnx
= close_encoding
;
1634 ct
->c_cesizefnx
= size_encoding
;
1641 close_encoding (CT ct
)
1645 if (!(ce
= ct
->c_cefile
))
1655 static unsigned long
1656 size_encoding (CT ct
)
1664 if (!(ce
= ct
->c_cefile
))
1665 return (ct
->c_end
- ct
->c_begin
);
1667 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1668 return (long) st
.st_size
;
1671 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1672 return (long) st
.st_size
;
1677 if (ct
->c_encoding
== CE_EXTERNAL
)
1678 return (ct
->c_end
- ct
->c_begin
);
1681 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1682 return (ct
->c_end
- ct
->c_begin
);
1684 if (fstat (fd
, &st
) != NOTOK
)
1685 size
= (long) st
.st_size
;
1689 (*ct
->c_ceclosefnx
) (ct
);
1698 static unsigned char b642nib
[0x80] = {
1699 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1700 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1701 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1702 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1703 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1704 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1705 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1706 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1707 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1708 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1709 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1710 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1711 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1712 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1713 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1714 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1721 return init_encoding (ct
, openBase64
);
1726 openBase64 (CT ct
, char **file
)
1728 int bitno
, cc
, digested
;
1729 int fd
, len
, skip
, own_ct_fp
= 0;
1731 unsigned char value
, b
;
1732 char *cp
, *ep
, buffer
[BUFSIZ
];
1733 /* sbeck -- handle suffixes */
1740 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1745 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1746 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1752 if (*file
== NULL
) {
1753 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1756 ce
->ce_file
= add (*file
, NULL
);
1760 /* sbeck@cise.ufl.edu -- handle suffixes */
1762 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1763 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1764 cp
= context_find (buffer
);
1765 if (cp
== NULL
|| *cp
== '\0') {
1766 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
1768 cp
= context_find (buffer
);
1770 if (cp
!= NULL
&& *cp
!= '\0') {
1771 if (ce
->ce_unlink
) {
1772 /* Temporary file already exists, so we rename to
1773 version with extension. */
1774 char *file_org
= strdup(ce
->ce_file
);
1775 ce
->ce_file
= add (cp
, ce
->ce_file
);
1776 if (rename(file_org
, ce
->ce_file
)) {
1777 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
1782 ce
->ce_file
= add (cp
, ce
->ce_file
);
1786 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
1787 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
1791 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
1792 adios (NULL
, "internal error(1)");
1795 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1796 content_error (ct
->c_file
, ct
, "unable to open for reading");
1802 if ((digested
= ct
->c_digested
))
1803 MD5Init (&mdContext
);
1809 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
1811 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
1813 content_error (ct
->c_file
, ct
, "error reading from");
1817 content_error (NULL
, ct
, "premature eof");
1825 for (ep
= (cp
= buffer
) + cc
; cp
< ep
; cp
++) {
1828 if (isspace ((unsigned char) *cp
))
1830 if (skip
|| (((unsigned char) *cp
) & 0x80)
1831 || (value
= b642nib
[((unsigned char) *cp
) & 0x7f]) > 0x3f) {
1833 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
1834 (unsigned char) *cp
,
1835 (long) (lseek (fd
, (off_t
) 0, SEEK_CUR
) - (ep
- cp
)),
1838 content_error (NULL
, ct
,
1839 "invalid BASE64 encoding -- continuing");
1843 bits
|= value
<< bitno
;
1845 if ((bitno
-= 6) < 0) {
1846 b
= (bits
>> 16) & 0xff;
1847 putc ((char) b
, ce
->ce_fp
);
1849 MD5Update (&mdContext
, &b
, 1);
1851 b
= (bits
>> 8) & 0xff;
1852 putc ((char) b
, ce
->ce_fp
);
1854 MD5Update (&mdContext
, &b
, 1);
1857 putc ((char) b
, ce
->ce_fp
);
1859 MD5Update (&mdContext
, &b
, 1);
1863 if (ferror (ce
->ce_fp
)) {
1864 content_error (ce
->ce_file
, ct
,
1865 "error writing to");
1868 bitno
= 18, bits
= 0L, skip
= 0;
1874 goto self_delimiting
;
1883 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
1885 content_error (NULL
, ct
, "invalid BASE64 encoding");
1890 fseek (ct
->c_fp
, 0L, SEEK_SET
);
1892 if (fflush (ce
->ce_fp
)) {
1893 content_error (ce
->ce_file
, ct
, "error writing to");
1898 unsigned char digest
[16];
1900 MD5Final (digest
, &mdContext
);
1901 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
1902 sizeof(digest
) / sizeof(digest
[0])))
1903 content_error (NULL
, ct
,
1904 "content integrity suspect (digest mismatch) -- continuing");
1907 fprintf (stderr
, "content integrity confirmed\n");
1910 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1913 *file
= ce
->ce_file
;
1918 return fileno (ce
->ce_fp
);
1925 free_encoding (ct
, 0);
1934 static char hex2nib
[0x80] = {
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1942 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1957 return init_encoding (ct
, openQuoted
);
1962 openQuoted (CT ct
, char **file
)
1964 int cc
, digested
, len
, quoted
, own_ct_fp
= 0;
1966 char buffer
[BUFSIZ
];
1969 /* sbeck -- handle suffixes */
1975 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
1980 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
1981 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
1987 if (*file
== NULL
) {
1988 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
1991 ce
->ce_file
= add (*file
, NULL
);
1995 /* sbeck@cise.ufl.edu -- handle suffixes */
1997 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
1998 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
1999 cp
= context_find (buffer
);
2000 if (cp
== NULL
|| *cp
== '\0') {
2001 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2003 cp
= context_find (buffer
);
2005 if (cp
!= NULL
&& *cp
!= '\0') {
2006 if (ce
->ce_unlink
) {
2007 /* Temporary file already exists, so we rename to
2008 version with extension. */
2009 char *file_org
= strdup(ce
->ce_file
);
2010 ce
->ce_file
= add (cp
, ce
->ce_file
);
2011 if (rename(file_org
, ce
->ce_file
)) {
2012 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2017 ce
->ce_file
= add (cp
, ce
->ce_file
);
2021 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2022 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2026 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2027 adios (NULL
, "internal error(2)");
2030 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2031 content_error (ct
->c_file
, ct
, "unable to open for reading");
2037 if ((digested
= ct
->c_digested
))
2038 MD5Init (&mdContext
);
2045 fseek (ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
2047 if (fgets (buffer
, sizeof(buffer
) - 1, ct
->c_fp
) == NULL
) {
2048 content_error (NULL
, ct
, "premature eof");
2052 if ((cc
= strlen (buffer
)) > len
)
2056 for (ep
= (cp
= buffer
) + cc
- 1; cp
<= ep
; ep
--)
2057 if (!isspace ((unsigned char) *ep
))
2061 for (; cp
< ep
; cp
++) {
2063 /* in an escape sequence */
2065 /* at byte 1 of an escape sequence */
2066 mask
= hex2nib
[((unsigned char) *cp
) & 0x7f];
2067 /* next is byte 2 */
2070 /* at byte 2 of an escape sequence */
2072 mask
|= hex2nib
[((unsigned char) *cp
) & 0x7f];
2073 putc (mask
, ce
->ce_fp
);
2075 MD5Update (&mdContext
, &mask
, 1);
2076 if (ferror (ce
->ce_fp
)) {
2077 content_error (ce
->ce_file
, ct
, "error writing to");
2080 /* finished escape sequence; next may be literal or a new
2081 * escape sequence */
2084 /* on to next byte */
2088 /* not in an escape sequence */
2090 /* starting an escape sequence, or invalid '='? */
2091 if (cp
+ 1 < ep
&& cp
[1] == '\n') {
2092 /* "=\n" soft line break, eat the \n */
2096 if (cp
+ 1 >= ep
|| cp
+ 2 >= ep
) {
2097 /* We don't have 2 bytes left, so this is an invalid
2098 * escape sequence; just show the raw bytes (below). */
2099 } else if (isxdigit ((unsigned char) cp
[1]) &&
2100 isxdigit ((unsigned char) cp
[2])) {
2101 /* Next 2 bytes are hex digits, making this a valid escape
2102 * sequence; let's decode it (above). */
2106 /* One or both of the next 2 is out of range, making this
2107 * an invalid escape sequence; just show the raw bytes
2112 /* Just show the raw byte. */
2113 putc (*cp
, ce
->ce_fp
);
2116 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2118 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2121 if (ferror (ce
->ce_fp
)) {
2122 content_error (ce
->ce_file
, ct
, "error writing to");
2128 content_error (NULL
, ct
,
2129 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2133 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2135 if (fflush (ce
->ce_fp
)) {
2136 content_error (ce
->ce_file
, ct
, "error writing to");
2141 unsigned char digest
[16];
2143 MD5Final (digest
, &mdContext
);
2144 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2145 sizeof(digest
) / sizeof(digest
[0])))
2146 content_error (NULL
, ct
,
2147 "content integrity suspect (digest mismatch) -- continuing");
2150 fprintf (stderr
, "content integrity confirmed\n");
2153 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2156 *file
= ce
->ce_file
;
2161 return fileno (ce
->ce_fp
);
2164 free_encoding (ct
, 0);
2180 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2183 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2189 open7Bit (CT ct
, char **file
)
2191 int cc
, fd
, len
, own_ct_fp
= 0;
2192 char buffer
[BUFSIZ
];
2193 /* sbeck -- handle suffixes */
2200 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2205 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2206 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2212 if (*file
== NULL
) {
2213 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2216 ce
->ce_file
= add (*file
, NULL
);
2220 /* sbeck@cise.ufl.edu -- handle suffixes */
2222 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2223 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2224 cp
= context_find (buffer
);
2225 if (cp
== NULL
|| *cp
== '\0') {
2226 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2228 cp
= context_find (buffer
);
2230 if (cp
!= NULL
&& *cp
!= '\0') {
2231 if (ce
->ce_unlink
) {
2232 /* Temporary file already exists, so we rename to
2233 version with extension. */
2234 char *file_org
= strdup(ce
->ce_file
);
2235 ce
->ce_file
= add (cp
, ce
->ce_file
);
2236 if (rename(file_org
, ce
->ce_file
)) {
2237 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2242 ce
->ce_file
= add (cp
, ce
->ce_file
);
2246 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2247 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2251 if (ct
->c_type
== CT_MULTIPART
) {
2253 CI ci
= &ct
->c_ctinfo
;
2256 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2257 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2258 + 1 + strlen (ci
->ci_subtype
);
2259 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2260 putc (';', ce
->ce_fp
);
2263 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2265 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2266 fputs ("\n\t", ce
->ce_fp
);
2269 putc (' ', ce
->ce_fp
);
2272 fprintf (ce
->ce_fp
, "%s", buffer
);
2276 if (ci
->ci_comment
) {
2277 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2278 fputs ("\n\t", ce
->ce_fp
);
2282 putc (' ', ce
->ce_fp
);
2285 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2288 fprintf (ce
->ce_fp
, "\n");
2290 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2292 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2294 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2295 fprintf (ce
->ce_fp
, "\n");
2298 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2299 adios (NULL
, "internal error(3)");
2302 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2303 content_error (ct
->c_file
, ct
, "unable to open for reading");
2309 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2311 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2313 content_error (ct
->c_file
, ct
, "error reading from");
2317 content_error (NULL
, ct
, "premature eof");
2325 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2326 if (ferror (ce
->ce_fp
)) {
2327 content_error (ce
->ce_file
, ct
, "error writing to");
2332 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2334 if (fflush (ce
->ce_fp
)) {
2335 content_error (ce
->ce_file
, ct
, "error writing to");
2339 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2342 *file
= ce
->ce_file
;
2347 return fileno (ce
->ce_fp
);
2350 free_encoding (ct
, 0);
2364 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2366 char cachefile
[BUFSIZ
];
2369 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2374 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2375 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2381 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2382 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2383 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2384 ce
->ce_file
= getcpy (cachefile
);
2388 admonish (cachefile
, "unable to fopen for reading");
2395 *file
= ce
->ce_file
;
2396 *fd
= fileno (ce
->ce_fp
);
2407 return init_encoding (ct
, openFile
);
2412 openFile (CT ct
, char **file
)
2415 char cachefile
[BUFSIZ
];
2416 struct exbody
*e
= ct
->c_ctexbody
;
2417 CE ce
= ct
->c_cefile
;
2419 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2431 content_error (NULL
, ct
, "missing name parameter");
2435 ce
->ce_file
= getcpy (e
->eb_name
);
2438 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2439 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2443 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2444 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2445 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2449 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2450 if ((fp
= fopen (cachefile
, "w"))) {
2452 char buffer
[BUFSIZ
];
2453 FILE *gp
= ce
->ce_fp
;
2455 fseek (gp
, 0L, SEEK_SET
);
2457 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2459 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2463 admonish (ce
->ce_file
, "error reading");
2468 admonish (cachefile
, "error writing");
2476 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2477 *file
= ce
->ce_file
;
2478 return fileno (ce
->ce_fp
);
2488 return init_encoding (ct
, openFTP
);
2493 openFTP (CT ct
, char **file
)
2495 int cachetype
, caching
, fd
;
2497 char *bp
, *ftp
, *user
, *pass
;
2498 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2501 static char *username
= NULL
;
2502 static char *password
= NULL
;
2507 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2513 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2524 if (!e
->eb_name
|| !e
->eb_site
) {
2525 content_error (NULL
, ct
, "missing %s parameter",
2526 e
->eb_name
? "site": "name");
2533 pidcheck (pidwait (xpid
, NOTOK
));
2537 /* Get the buffer ready to go */
2539 buflen
= sizeof(buffer
);
2542 * Construct the query message for user
2544 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2550 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2556 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2557 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2562 if (e
->eb_size
> 0) {
2563 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2568 snprintf (bp
, buflen
, "? ");
2571 * Now, check the answer
2573 if (!getanswer (buffer
))
2578 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2582 ruserpass (e
->eb_site
, &username
, &password
);
2587 ce
->ce_unlink
= (*file
== NULL
);
2589 cachefile
[0] = '\0';
2590 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2591 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2592 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2593 if (*file
== NULL
) {
2600 ce
->ce_file
= add (*file
, NULL
);
2602 ce
->ce_file
= add (cachefile
, NULL
);
2604 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2606 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2607 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2612 int child_id
, i
, vecp
;
2616 vec
[vecp
++] = r1bindex (ftp
, '/');
2617 vec
[vecp
++] = e
->eb_site
;
2620 vec
[vecp
++] = e
->eb_dir
;
2621 vec
[vecp
++] = e
->eb_name
;
2622 vec
[vecp
++] = ce
->ce_file
,
2623 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2624 ? "ascii" : "binary";
2629 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2633 adios ("fork", "unable to");
2637 close (fileno (ce
->ce_fp
));
2639 fprintf (stderr
, "unable to exec ");
2645 if (pidXwait (child_id
, NULL
)) {
2646 username
= password
= NULL
;
2656 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2661 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2662 if ((fp
= fopen (cachefile
, "w"))) {
2664 FILE *gp
= ce
->ce_fp
;
2666 fseek (gp
, 0L, SEEK_SET
);
2668 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2670 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2674 admonish (ce
->ce_file
, "error reading");
2679 admonish (cachefile
, "error writing");
2688 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2689 *file
= ce
->ce_file
;
2690 return fileno (ce
->ce_fp
);
2701 return init_encoding (ct
, openMail
);
2706 openMail (CT ct
, char **file
)
2708 int child_id
, fd
, i
, vecp
;
2710 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2711 struct exbody
*e
= ct
->c_ctexbody
;
2712 CE ce
= ct
->c_cefile
;
2714 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2725 if (!e
->eb_server
) {
2726 content_error (NULL
, ct
, "missing server parameter");
2733 pidcheck (pidwait (xpid
, NOTOK
));
2737 /* Get buffer ready to go */
2739 buflen
= sizeof(buffer
);
2741 /* Now, construct query message */
2742 snprintf (bp
, buflen
, "Retrieve content");
2748 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2754 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2756 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2758 /* Now, check answer */
2759 if (!getanswer (buffer
))
2763 vec
[vecp
++] = r1bindex (mailproc
, '/');
2764 vec
[vecp
++] = e
->eb_server
;
2765 vec
[vecp
++] = "-subject";
2766 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2767 vec
[vecp
++] = "-body";
2768 vec
[vecp
++] = e
->eb_body
;
2771 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
< 5; i
++)
2775 advise ("fork", "unable to");
2779 execvp (mailproc
, vec
);
2780 fprintf (stderr
, "unable to exec ");
2786 if (pidXwait (child_id
, NULL
) == OK
)
2787 advise (NULL
, "request sent");
2791 if (*file
== NULL
) {
2792 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2795 ce
->ce_file
= add (*file
, NULL
);
2799 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2800 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2804 /* showproc is for mhshow and mhstore, though mhlist -debug
2805 * prints it, too. */
2807 free (ct
->c_showproc
);
2808 ct
->c_showproc
= add ("true", NULL
);
2810 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2811 *file
= ce
->ce_file
;
2812 return fileno (ce
->ce_fp
);
2817 readDigest (CT ct
, char *cp
)
2822 unsigned char *dp
, value
, *ep
;
2828 for (ep
= (dp
= ct
->c_digest
)
2829 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2834 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2836 fprintf (stderr
, "invalid BASE64 encoding\n");
2840 bits
|= value
<< bitno
;
2842 if ((bitno
-= 6) < 0) {
2843 if (dp
+ (3 - skip
) > ep
)
2844 goto invalid_digest
;
2845 *dp
++ = (bits
>> 16) & 0xff;
2847 *dp
++ = (bits
>> 8) & 0xff;
2849 *dp
++ = bits
& 0xff;
2859 goto self_delimiting
;
2864 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2874 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2882 fprintf (stderr
, "MD5 digest=");
2883 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2884 fprintf (stderr
, "%02x", *dp
& 0xff);
2885 fprintf (stderr
, "\n");