]>
diplodocus.org Git - nmh/blob - uip/mhparse.c
3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
19 #include <h/mhparse.h>
25 extern pid_t xpid
; /* mhshowsbr.c */
28 extern int rcachesw
; /* mhcachesbr.c */
29 extern int wcachesw
; /* mhcachesbr.c */
31 int checksw
= 0; /* check Content-MD5 field */
34 * Directory to place temp files. This must
35 * be set before these routines are called.
40 * Structures for TEXT messages
42 struct k2v SubText
[] = {
43 { "plain", TEXT_PLAIN
},
44 { "richtext", TEXT_RICHTEXT
}, /* defined in RFC-1341 */
45 { "enriched", TEXT_ENRICHED
}, /* defined in RFC-1896 */
46 { NULL
, TEXT_UNKNOWN
} /* this one must be last! */
49 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
52 * Structures for MULTIPART messages
54 struct k2v SubMultiPart
[] = {
55 { "mixed", MULTI_MIXED
},
56 { "alternative", MULTI_ALTERNATE
},
57 { "digest", MULTI_DIGEST
},
58 { "parallel", MULTI_PARALLEL
},
59 { NULL
, MULTI_UNKNOWN
} /* this one must be last! */
63 * Structures for MESSAGE messages
65 struct k2v SubMessage
[] = {
66 { "rfc822", MESSAGE_RFC822
},
67 { "partial", MESSAGE_PARTIAL
},
68 { "external-body", MESSAGE_EXTERNAL
},
69 { NULL
, MESSAGE_UNKNOWN
} /* this one must be last! */
73 * Structure for APPLICATION messages
75 struct k2v SubApplication
[] = {
76 { "octet-stream", APPLICATION_OCTETS
},
77 { "postscript", APPLICATION_POSTSCRIPT
},
78 { NULL
, APPLICATION_UNKNOWN
} /* this one must be last! */
83 int find_cache (CT
, int, int *, char *, char *, int);
86 int part_ok (CT
, int);
87 int type_ok (CT
, int);
88 void content_error (char *, CT
, char *, ...);
91 void free_content (CT
);
92 void free_encoding (CT
, int);
97 static CT
get_content (FILE *, char *, int);
98 static int get_comment (CT
, unsigned char **, int);
100 static int InitGeneric (CT
);
101 static int InitText (CT
);
102 static int InitMultiPart (CT
);
103 static void reverse_parts (CT
);
104 static int InitMessage (CT
);
105 static int InitApplication (CT
);
106 static int init_encoding (CT
, OpenCEFunc
);
107 static unsigned long size_encoding (CT
);
108 static int InitBase64 (CT
);
109 static int openBase64 (CT
, char **);
110 static int InitQuoted (CT
);
111 static int openQuoted (CT
, char **);
112 static int Init7Bit (CT
);
113 static int openExternal (CT
, CT
, CE
, char **, int *);
114 static int InitFile (CT
);
115 static int openFile (CT
, char **);
116 static int InitFTP (CT
);
117 static int openFTP (CT
, char **);
118 static int InitMail (CT
);
119 static int openMail (CT
, char **);
120 static int readDigest (CT
, char *);
122 struct str2init str2cts
[] = {
123 { "application", CT_APPLICATION
, InitApplication
},
124 { "audio", CT_AUDIO
, InitGeneric
},
125 { "image", CT_IMAGE
, InitGeneric
},
126 { "message", CT_MESSAGE
, InitMessage
},
127 { "multipart", CT_MULTIPART
, InitMultiPart
},
128 { "text", CT_TEXT
, InitText
},
129 { "video", CT_VIDEO
, InitGeneric
},
130 { NULL
, CT_EXTENSION
, NULL
}, /* these two must be last! */
131 { NULL
, CT_UNKNOWN
, NULL
},
134 struct str2init str2ces
[] = {
135 { "base64", CE_BASE64
, InitBase64
},
136 { "quoted-printable", CE_QUOTED
, InitQuoted
},
137 { "8bit", CE_8BIT
, Init7Bit
},
138 { "7bit", CE_7BIT
, Init7Bit
},
139 { "binary", CE_BINARY
, Init7Bit
},
140 { NULL
, CE_EXTENSION
, NULL
}, /* these two must be last! */
141 { NULL
, CE_UNKNOWN
, NULL
},
145 * NOTE WELL: si_key MUST NOT have value of NOTOK
147 * si_key is 1 if access method is anonymous.
149 struct str2init str2methods
[] = {
150 { "afs", 1, InitFile
},
151 { "anon-ftp", 1, InitFTP
},
152 { "ftp", 0, InitFTP
},
153 { "local-file", 0, InitFile
},
154 { "mail-server", 0, InitMail
},
160 pidcheck (int status
)
162 if ((status
& 0xff00) == 0xff00 || (status
& 0x007f) != SIGQUIT
)
173 * Main entry point for parsing a MIME message or file.
174 * It returns the Content structure for the top level
175 * entity in the file.
179 parse_mime (char *file
)
187 * Check if file is actually standard input
189 if ((is_stdin
= !(strcmp (file
, "-")))) {
190 char *tfile
= m_mktemp2(NULL
, invo_name
, NULL
, &fp
);
192 advise("mhparse", "unable to create temporary file");
195 file
= add (tfile
, NULL
);
198 while (fgets (buffer
, sizeof(buffer
), stdin
))
202 if (ferror (stdin
)) {
204 advise ("stdin", "error reading");
209 advise (file
, "error writing");
212 fseek (fp
, 0L, SEEK_SET
);
213 } else if ((fp
= fopen (file
, "r")) == NULL
) {
214 advise (file
, "unable to read");
218 if (!(ct
= get_content (fp
, file
, 1))) {
221 advise (NULL
, "unable to decode %s", file
);
226 ct
->c_unlink
= 1; /* temp file to remove */
230 if (ct
->c_end
== 0L) {
231 fseek (fp
, 0L, SEEK_END
);
232 ct
->c_end
= ftell (fp
);
235 if (ct
->c_ctinitfnx
&& (*ct
->c_ctinitfnx
) (ct
) == NOTOK
) {
247 * Main routine for reading/parsing the headers
248 * of a message content.
250 * toplevel = 1 # we are at the top level of the message
251 * toplevel = 0 # we are inside message type or multipart type
252 * # other than multipart/digest
253 * toplevel = -1 # we are inside multipart/digest
254 * NB: on failure we will fclose(in)!
258 get_content (FILE *in
, char *file
, int toplevel
)
261 char buf
[BUFSIZ
], name
[NAMESZ
];
265 m_getfld_state_t gstate
= 0;
267 /* allocate the content structure */
268 if (!(ct
= (CT
) calloc (1, sizeof(*ct
))))
269 adios (NULL
, "out of memory");
272 ct
->c_file
= add (file
, NULL
);
273 ct
->c_begin
= ftell (ct
->c_fp
) + 1;
276 * Parse the header fields for this
277 * content into a linked list.
279 m_getfld_track_filepos (&gstate
, in
);
280 for (compnum
= 1;;) {
281 int bufsz
= sizeof buf
;
282 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
287 /* get copies of the buffers */
288 np
= add (name
, NULL
);
289 vp
= add (buf
, NULL
);
291 /* if necessary, get rest of field */
292 while (state
== FLDPLUS
) {
294 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
295 vp
= add (buf
, vp
); /* add to previous value */
298 /* Now add the header data to the list */
299 add_header (ct
, np
, vp
);
301 /* continue, to see if this isn't the last header field */
302 ct
->c_begin
= ftell (in
) + 1;
306 ct
->c_begin
= ftell (in
) - strlen (buf
);
310 ct
->c_begin
= ftell (in
);
315 adios (NULL
, "message format error in component #%d", compnum
);
318 adios (NULL
, "getfld() returned %d", state
);
321 /* break out of the loop */
324 m_getfld_state_destroy (&gstate
);
327 * Read the content headers. We will parse the
328 * MIME related header fields into their various
329 * structures and set internal flags related to
330 * content type/subtype, etc.
333 hp
= ct
->c_first_hf
; /* start at first header field */
335 /* Get MIME-Version field */
336 if (!mh_strcasecmp (hp
->name
, VRSN_FIELD
)) {
339 unsigned char *cp
, *dp
;
342 advise (NULL
, "message %s has multiple %s: fields",
343 ct
->c_file
, VRSN_FIELD
);
346 ct
->c_vrsn
= add (hp
->value
, NULL
);
348 /* Now, cleanup this field */
351 while (isspace (*cp
))
353 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
355 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
360 fprintf (stderr
, "%s: %s\n", VRSN_FIELD
, cp
);
362 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
)
365 for (dp
= cp
; istoken (*dp
); dp
++)
369 ucmp
= !mh_strcasecmp (cp
, VRSN_VALUE
);
372 admonish (NULL
, "message %s has unknown value for %s: field (%s)",
373 ct
->c_file
, VRSN_FIELD
, cp
);
376 else if (!mh_strcasecmp (hp
->name
, TYPE_FIELD
)) {
377 /* Get Content-Type field */
378 struct str2init
*s2i
;
379 CI ci
= &ct
->c_ctinfo
;
381 /* Check if we've already seen a Content-Type header */
383 advise (NULL
, "message %s has multiple %s: fields",
384 ct
->c_file
, TYPE_FIELD
);
388 /* Parse the Content-Type field */
389 if (get_ctinfo (hp
->value
, ct
, 0) == NOTOK
)
393 * Set the Init function and the internal
394 * flag for this content type.
396 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
397 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
399 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
401 ct
->c_type
= s2i
->si_val
;
402 ct
->c_ctinitfnx
= s2i
->si_init
;
404 else if (!mh_strcasecmp (hp
->name
, ENCODING_FIELD
)) {
405 /* Get Content-Transfer-Encoding field */
407 unsigned char *cp
, *dp
;
408 struct str2init
*s2i
;
411 * Check if we've already seen the
412 * Content-Transfer-Encoding field
415 advise (NULL
, "message %s has multiple %s: fields",
416 ct
->c_file
, ENCODING_FIELD
);
420 /* get copy of this field */
421 ct
->c_celine
= cp
= add (hp
->value
, NULL
);
423 while (isspace (*cp
))
425 for (dp
= cp
; istoken (*dp
); dp
++)
431 * Find the internal flag and Init function
432 * for this transfer encoding.
434 for (s2i
= str2ces
; s2i
->si_key
; s2i
++)
435 if (!mh_strcasecmp (cp
, s2i
->si_key
))
437 if (!s2i
->si_key
&& !uprf (cp
, "X-"))
440 ct
->c_encoding
= s2i
->si_val
;
442 /* Call the Init function for this encoding */
443 if (s2i
->si_init
&& (*s2i
->si_init
) (ct
) == NOTOK
)
446 else if (!mh_strcasecmp (hp
->name
, MD5_FIELD
)) {
447 /* Get Content-MD5 field */
448 unsigned char *cp
, *dp
;
454 if (ct
->c_digested
) {
455 advise (NULL
, "message %s has multiple %s: fields",
456 ct
->c_file
, MD5_FIELD
);
460 ep
= cp
= add (hp
->value
, NULL
); /* get a copy */
462 while (isspace (*cp
))
464 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
466 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
471 fprintf (stderr
, "%s: %s\n", MD5_FIELD
, cp
);
473 if (*cp
== '(' && get_comment (ct
, &cp
, 0) == NOTOK
) {
478 for (dp
= cp
; *dp
&& !isspace (*dp
); dp
++)
486 else if (!mh_strcasecmp (hp
->name
, ID_FIELD
)) {
487 /* Get Content-ID field */
488 ct
->c_id
= add (hp
->value
, ct
->c_id
);
490 else if (!mh_strcasecmp (hp
->name
, DESCR_FIELD
)) {
491 /* Get Content-Description field */
492 ct
->c_descr
= add (hp
->value
, ct
->c_descr
);
494 else if (!mh_strcasecmp (hp
->name
, DISPO_FIELD
)) {
495 /* Get Content-Disposition field */
496 ct
->c_dispo
= add (hp
->value
, ct
->c_dispo
);
500 hp
= hp
->next
; /* next header field */
504 * Check if we saw a Content-Type field.
505 * If not, then assign a default value for
506 * it, and the Init function.
510 * If we are inside a multipart/digest message,
511 * so default type is message/rfc822
514 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
516 ct
->c_type
= CT_MESSAGE
;
517 ct
->c_ctinitfnx
= InitMessage
;
520 * Else default type is text/plain
522 if (get_ctinfo ("text/plain", ct
, 0) == NOTOK
)
524 ct
->c_type
= CT_TEXT
;
525 ct
->c_ctinitfnx
= InitText
;
529 /* Use default Transfer-Encoding, if necessary */
531 ct
->c_encoding
= CE_7BIT
;
544 * small routine to add header field to list
548 add_header (CT ct
, char *name
, char *value
)
552 /* allocate header field structure */
553 hp
= mh_xmalloc (sizeof(*hp
));
555 /* link data into header structure */
560 /* link header structure into the list */
561 if (ct
->c_first_hf
== NULL
) {
562 ct
->c_first_hf
= hp
; /* this is the first */
565 ct
->c_last_hf
->next
= hp
; /* add it to the end */
573 /* Make sure that buf contains at least one appearance of name,
574 followed by =. If not, insert both name and value, just after
575 first semicolon, if any. Note that name should not contain a
576 trailing =. And quotes will be added around the value. Typical
577 usage: make sure that a Content-Disposition header contains
578 filename="foo". If it doesn't and value does, use value from
581 incl_name_value (unsigned char *buf
, char *name
, char *value
) {
584 /* Assume that name is non-null. */
586 char *name_plus_equal
= concat (name
, "=", NULL
);
588 if (! strstr (buf
, name_plus_equal
)) {
591 char *prefix
, *suffix
;
593 /* Trim trailing space, esp. newline. */
594 for (cp
= &buf
[strlen (buf
) - 1];
595 cp
>= buf
&& isspace (*cp
);
600 insertion
= concat ("; ", name
, "=", "\"", value
, "\"", NULL
);
602 /* Insert at first semicolon, if any. If none, append to
604 prefix
= add (buf
, NULL
);
605 if ((cp
= strchr (prefix
, ';'))) {
606 suffix
= concat (cp
, NULL
);
608 newbuf
= concat (prefix
, insertion
, suffix
, "\n", NULL
);
612 newbuf
= concat (buf
, insertion
, "\n", NULL
);
620 free (name_plus_equal
);
626 /* Extract just name_suffix="foo", if any, from value. If there isn't
627 one, return the entire value. Note that, for example, a name_suffix
628 of name will match filename="foo", and return foo. */
630 extract_name_value (char *name_suffix
, char *value
) {
631 char *extracted_name_value
= value
;
632 char *name_suffix_plus_quote
= concat (name_suffix
, "=\"", NULL
);
633 char *name_suffix_equals
= strstr (value
, name_suffix_plus_quote
);
636 free (name_suffix_plus_quote
);
637 if (name_suffix_equals
) {
638 char *name_suffix_begin
;
641 for (cp
= name_suffix_equals
; *cp
!= '"'; ++cp
) /* empty */;
642 name_suffix_begin
= ++cp
;
643 /* Find second \". */
644 for (; *cp
!= '"'; ++cp
) /* empty */;
646 extracted_name_value
= mh_xmalloc (cp
- name_suffix_begin
+ 1);
647 memcpy (extracted_name_value
,
649 cp
- name_suffix_begin
);
650 extracted_name_value
[cp
- name_suffix_begin
] = '\0';
653 return extracted_name_value
;
657 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
658 * directives. Fills in the information of the CTinfo structure.
661 get_ctinfo (unsigned char *cp
, CT ct
, int magic
)
670 i
= strlen (invo_name
) + 2;
672 /* store copy of Content-Type line */
673 cp
= ct
->c_ctline
= add (cp
, NULL
);
675 while (isspace (*cp
)) /* trim leading spaces */
678 /* change newlines to spaces */
679 for (dp
= strchr(cp
, '\n'); dp
; dp
= strchr(dp
, '\n'))
682 /* trim trailing spaces */
683 for (dp
= cp
+ strlen (cp
) - 1; dp
>= cp
; dp
--)
689 fprintf (stderr
, "%s: %s\n", TYPE_FIELD
, cp
);
691 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
694 for (dp
= cp
; istoken (*dp
); dp
++)
697 ci
->ci_type
= add (cp
, NULL
); /* store content type */
701 advise (NULL
, "invalid %s: field in message %s (empty type)",
702 TYPE_FIELD
, ct
->c_file
);
706 /* down case the content type string */
707 for (dp
= ci
->ci_type
; *dp
; dp
++)
708 if (isalpha(*dp
) && isupper (*dp
))
711 while (isspace (*cp
))
714 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
719 ci
->ci_subtype
= add ("", NULL
);
724 while (isspace (*cp
))
727 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
730 for (dp
= cp
; istoken (*dp
); dp
++)
733 ci
->ci_subtype
= add (cp
, NULL
); /* store the content subtype */
736 if (!*ci
->ci_subtype
) {
738 "invalid %s: field in message %s (empty subtype for \"%s\")",
739 TYPE_FIELD
, ct
->c_file
, ci
->ci_type
);
743 /* down case the content subtype string */
744 for (dp
= ci
->ci_subtype
; *dp
; dp
++)
745 if (isalpha(*dp
) && isupper (*dp
))
749 while (isspace (*cp
))
752 if (*cp
== '(' && get_comment (ct
, &cp
, 1) == NOTOK
)
756 * Parse attribute/value pairs given with Content-Type
758 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 (*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 (*dp
); dp
++)
786 if (isalpha(*dp
) && isupper (*dp
))
789 for (up
= dp
; isspace (*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 (*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 (*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 (*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 (*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 (*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
, unsigned char **ap
, int istype
)
967 char c
, buffer
[BUFSIZ
], *dp
;
979 advise (NULL
, "invalid comment in message %s's %s: field",
980 ct
->c_file
, istype
? TYPE_FIELD
: VRSN_FIELD
);
985 if ((c
= *cp
++) == '\0')
1008 if ((dp
= ci
->ci_comment
)) {
1009 ci
->ci_comment
= concat (dp
, " ", buffer
, NULL
);
1012 ci
->ci_comment
= add (buffer
, NULL
);
1016 while (isspace (*cp
))
1027 * Handles content types audio, image, and video.
1028 * There's not much to do right here.
1036 return OK
; /* not much to do here */
1047 char buffer
[BUFSIZ
];
1049 char **ap
, **ep
, *cp
;
1052 CI ci
= &ct
->c_ctinfo
;
1054 /* check for missing subtype */
1055 if (!*ci
->ci_subtype
)
1056 ci
->ci_subtype
= add ("plain", ci
->ci_subtype
);
1059 for (kv
= SubText
; kv
->kv_key
; kv
++)
1060 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1062 ct
->c_subtype
= kv
->kv_value
;
1064 /* allocate text character set structure */
1065 if ((t
= (struct text
*) calloc (1, sizeof(*t
))) == NULL
)
1066 adios (NULL
, "out of memory");
1067 ct
->c_ctparams
= (void *) t
;
1069 /* scan for charset parameter */
1070 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1071 if (!mh_strcasecmp (*ap
, "charset"))
1074 /* check if content specified a character set */
1077 t
->tx_charset
= CHARSET_SPECIFIED
;
1079 t
->tx_charset
= CHARSET_UNSPECIFIED
;
1083 * If we can not handle character set natively,
1084 * then check profile for string to modify the
1085 * terminal or display method.
1087 * termproc is for mhshow, though mhlist -debug prints it, too.
1089 if (chset
!= NULL
&& !check_charset (chset
, strlen (chset
))) {
1090 snprintf (buffer
, sizeof(buffer
), "%s-charset-%s", invo_name
, chset
);
1091 if ((cp
= context_find (buffer
)))
1092 ct
->c_termproc
= getcpy (cp
);
1104 InitMultiPart (CT ct
)
1108 unsigned char *cp
, *dp
;
1110 char *bp
, buffer
[BUFSIZ
];
1111 struct multipart
*m
;
1113 struct part
*part
, **next
;
1114 CI ci
= &ct
->c_ctinfo
;
1119 * The encoding for multipart messages must be either
1120 * 7bit, 8bit, or binary (per RFC2045).
1122 if (ct
->c_encoding
!= CE_7BIT
&& ct
->c_encoding
!= CE_8BIT
1123 && ct
->c_encoding
!= CE_BINARY
) {
1124 /* Copy the Content-Transfer-Encoding header field body so we can
1125 remove any trailing whitespace and leading blanks from it. */
1126 char *cte
= add (ct
->c_celine
? ct
->c_celine
: "(null)", NULL
);
1128 bp
= cte
+ strlen (cte
) - 1;
1129 while (bp
>= cte
&& isspace (*bp
)) *bp
-- = '\0';
1130 for (bp
= cte
; *bp
&& isblank (*bp
); ++bp
) continue;
1133 "\"%s/%s\" type in message %s must be encoded in\n"
1134 "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround "
1135 "is to\nmanually edit the file and change the \"%s\"\n"
1136 "Content-Transfer-Encoding to one of those. For now",
1137 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, bp
);
1144 for (kv
= SubMultiPart
; kv
->kv_key
; kv
++)
1145 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1147 ct
->c_subtype
= kv
->kv_value
;
1150 * Check for "boundary" parameter, which is
1151 * required for multipart messages.
1154 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1155 if (!mh_strcasecmp (*ap
, "boundary")) {
1161 /* complain if boundary parameter is missing */
1164 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1165 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1169 /* allocate primary structure for multipart info */
1170 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
1171 adios (NULL
, "out of memory");
1172 ct
->c_ctparams
= (void *) m
;
1174 /* check if boundary parameter contains only whitespace characters */
1175 for (cp
= bp
; isspace (*cp
); cp
++)
1178 advise (NULL
, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1179 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1183 /* remove trailing whitespace from boundary parameter */
1184 for (cp
= bp
, dp
= cp
+ strlen (cp
) - 1; dp
> cp
; dp
--)
1189 /* record boundary separators */
1190 m
->mp_start
= concat (bp
, "\n", NULL
);
1191 m
->mp_stop
= concat (bp
, "--\n", NULL
);
1193 if (!ct
->c_fp
&& (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1194 advise (ct
->c_file
, "unable to open for reading");
1198 fseek (fp
= ct
->c_fp
, pos
= ct
->c_begin
, SEEK_SET
);
1200 next
= &m
->mp_parts
;
1204 while (fgets (buffer
, sizeof(buffer
) - 1, fp
)) {
1208 pos
+= strlen (buffer
);
1209 if (buffer
[0] != '-' || buffer
[1] != '-')
1212 if (strcmp (buffer
+ 2, m
->mp_start
))
1215 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
1216 adios (NULL
, "out of memory");
1218 next
= &part
->mp_next
;
1220 if (!(p
= get_content (fp
, ct
->c_file
,
1221 ct
->c_subtype
== MULTI_DIGEST
? -1 : 0))) {
1228 fseek (fp
, pos
, SEEK_SET
);
1231 if (strcmp (buffer
+ 2, m
->mp_start
) == 0) {
1235 p
->c_end
= ftell(fp
) - (strlen(buffer
) + 1);
1236 if (p
->c_end
< p
->c_begin
)
1237 p
->c_begin
= p
->c_end
;
1242 if (strcmp (buffer
+ 2, m
->mp_stop
) == 0)
1248 advise (NULL
, "bogus multipart content in message %s", ct
->c_file
);
1249 if (!inout
&& part
) {
1251 p
->c_end
= ct
->c_end
;
1253 if (p
->c_begin
>= p
->c_end
) {
1254 for (next
= &m
->mp_parts
; *next
!= part
;
1255 next
= &((*next
)->mp_next
))
1259 free ((char *) part
);
1264 /* reverse the order of the parts for multipart/alternative */
1265 if (ct
->c_subtype
== MULTI_ALTERNATE
)
1269 * label all subparts with part number, and
1270 * then initialize the content of the subpart.
1275 char partnam
[BUFSIZ
];
1278 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1279 pp
= partnam
+ strlen (partnam
);
1284 for (part
= m
->mp_parts
, partnum
= 1; part
;
1285 part
= part
->mp_next
, partnum
++) {
1288 sprintf (pp
, "%d", partnum
);
1289 p
->c_partno
= add (partnam
, NULL
);
1291 /* initialize the content of the subparts */
1292 if (p
->c_ctinitfnx
&& (*p
->c_ctinitfnx
) (p
) == NOTOK
) {
1307 * reverse the order of the parts of a multipart
1311 reverse_parts (CT ct
)
1314 struct multipart
*m
;
1315 struct part
**base
, **bmp
, **next
, *part
;
1317 m
= (struct multipart
*) ct
->c_ctparams
;
1319 /* if only one part, just return */
1320 if (!m
->mp_parts
|| !m
->mp_parts
->mp_next
)
1323 /* count number of parts */
1325 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1328 /* allocate array of pointers to the parts */
1329 if (!(base
= (struct part
**) calloc ((size_t) (i
+ 1), sizeof(*base
))))
1330 adios (NULL
, "out of memory");
1333 /* point at all the parts */
1334 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1338 /* reverse the order of the parts */
1339 next
= &m
->mp_parts
;
1340 for (bmp
--; bmp
>= base
; bmp
--) {
1343 next
= &part
->mp_next
;
1347 /* free array of pointers */
1348 free ((char *) base
);
1360 CI ci
= &ct
->c_ctinfo
;
1362 if ((ct
->c_encoding
!= CE_7BIT
) && (ct
->c_encoding
!= CE_8BIT
)) {
1364 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1365 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
);
1369 /* check for missing subtype */
1370 if (!*ci
->ci_subtype
)
1371 ci
->ci_subtype
= add ("rfc822", ci
->ci_subtype
);
1374 for (kv
= SubMessage
; kv
->kv_key
; kv
++)
1375 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1377 ct
->c_subtype
= kv
->kv_value
;
1379 switch (ct
->c_subtype
) {
1380 case MESSAGE_RFC822
:
1383 case MESSAGE_PARTIAL
:
1388 if ((p
= (struct partial
*) calloc (1, sizeof(*p
))) == NULL
)
1389 adios (NULL
, "out of memory");
1390 ct
->c_ctparams
= (void *) p
;
1392 /* scan for parameters "id", "number", and "total" */
1393 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1394 if (!mh_strcasecmp (*ap
, "id")) {
1395 p
->pm_partid
= add (*ep
, NULL
);
1398 if (!mh_strcasecmp (*ap
, "number")) {
1399 if (sscanf (*ep
, "%d", &p
->pm_partno
) != 1
1400 || p
->pm_partno
< 1) {
1403 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1404 *ap
, ci
->ci_type
, ci
->ci_subtype
,
1405 ct
->c_file
, TYPE_FIELD
);
1410 if (!mh_strcasecmp (*ap
, "total")) {
1411 if (sscanf (*ep
, "%d", &p
->pm_maxno
) != 1
1420 || (p
->pm_maxno
&& p
->pm_partno
> p
->pm_maxno
)) {
1422 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1423 ci
->ci_type
, ci
->ci_subtype
,
1424 ct
->c_file
, TYPE_FIELD
);
1430 case MESSAGE_EXTERNAL
:
1437 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
1438 adios (NULL
, "out of memory");
1439 ct
->c_ctparams
= (void *) e
;
1442 && (ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
1443 advise (ct
->c_file
, "unable to open for reading");
1447 fseek (fp
= ct
->c_fp
, ct
->c_begin
, SEEK_SET
);
1449 if (!(p
= get_content (fp
, ct
->c_file
, 0))) {
1457 if ((exresult
= params_external (ct
, 0)) != NOTOK
1458 && p
->c_ceopenfnx
== openMail
) {
1462 if ((size
= ct
->c_end
- p
->c_begin
) <= 0) {
1464 content_error (NULL
, ct
,
1465 "empty body for access-type=mail-server");
1469 e
->eb_body
= bp
= mh_xmalloc ((unsigned) size
);
1470 fseek (p
->c_fp
, p
->c_begin
, SEEK_SET
);
1472 switch (cc
= fread (bp
, sizeof(*bp
), size
, p
->c_fp
)) {
1474 adios ("failed", "fread");
1477 adios (NULL
, "unexpected EOF from fread");
1480 bp
+= cc
, size
-= cc
;
1487 p
->c_end
= p
->c_begin
;
1492 if (exresult
== NOTOK
)
1494 if (e
->eb_flags
== NOTOK
)
1497 switch (p
->c_type
) {
1502 if (p
->c_subtype
!= MESSAGE_RFC822
)
1506 e
->eb_partno
= ct
->c_partno
;
1508 (*p
->c_ctinitfnx
) (p
);
1523 params_external (CT ct
, int composing
)
1526 struct exbody
*e
= (struct exbody
*) ct
->c_ctparams
;
1527 CI ci
= &ct
->c_ctinfo
;
1529 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1530 if (!mh_strcasecmp (*ap
, "access-type")) {
1531 struct str2init
*s2i
;
1532 CT p
= e
->eb_content
;
1534 for (s2i
= str2methods
; s2i
->si_key
; s2i
++)
1535 if (!mh_strcasecmp (*ep
, s2i
->si_key
))
1539 e
->eb_flags
= NOTOK
;
1540 p
->c_encoding
= CE_EXTERNAL
;
1543 e
->eb_access
= s2i
->si_key
;
1544 e
->eb_flags
= s2i
->si_val
;
1545 p
->c_encoding
= CE_EXTERNAL
;
1547 /* Call the Init function for this external type */
1548 if ((*s2i
->si_init
)(p
) == NOTOK
)
1552 if (!mh_strcasecmp (*ap
, "name")) {
1556 if (!mh_strcasecmp (*ap
, "permission")) {
1557 e
->eb_permission
= *ep
;
1560 if (!mh_strcasecmp (*ap
, "site")) {
1564 if (!mh_strcasecmp (*ap
, "directory")) {
1568 if (!mh_strcasecmp (*ap
, "mode")) {
1572 if (!mh_strcasecmp (*ap
, "size")) {
1573 sscanf (*ep
, "%lu", &e
->eb_size
);
1576 if (!mh_strcasecmp (*ap
, "server")) {
1580 if (!mh_strcasecmp (*ap
, "subject")) {
1581 e
->eb_subject
= *ep
;
1584 if (composing
&& !mh_strcasecmp (*ap
, "body")) {
1585 e
->eb_body
= getcpy (*ep
);
1590 if (!e
->eb_access
) {
1592 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1593 ci
->ci_type
, ci
->ci_subtype
, ct
->c_file
, TYPE_FIELD
);
1606 InitApplication (CT ct
)
1609 CI ci
= &ct
->c_ctinfo
;
1612 for (kv
= SubApplication
; kv
->kv_key
; kv
++)
1613 if (!mh_strcasecmp (ci
->ci_subtype
, kv
->kv_key
))
1615 ct
->c_subtype
= kv
->kv_value
;
1622 * TRANSFER ENCODINGS
1626 init_encoding (CT ct
, OpenCEFunc openfnx
)
1630 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
1631 adios (NULL
, "out of memory");
1634 ct
->c_ceopenfnx
= openfnx
;
1635 ct
->c_ceclosefnx
= close_encoding
;
1636 ct
->c_cesizefnx
= size_encoding
;
1643 close_encoding (CT ct
)
1647 if (!(ce
= ct
->c_cefile
))
1657 static unsigned long
1658 size_encoding (CT ct
)
1666 if (!(ce
= ct
->c_cefile
))
1667 return (ct
->c_end
- ct
->c_begin
);
1669 if (ce
->ce_fp
&& fstat (fileno (ce
->ce_fp
), &st
) != NOTOK
)
1670 return (long) st
.st_size
;
1673 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1674 return (long) st
.st_size
;
1679 if (ct
->c_encoding
== CE_EXTERNAL
)
1680 return (ct
->c_end
- ct
->c_begin
);
1683 if ((fd
= (*ct
->c_ceopenfnx
) (ct
, &file
)) == NOTOK
)
1684 return (ct
->c_end
- ct
->c_begin
);
1686 if (fstat (fd
, &st
) != NOTOK
)
1687 size
= (long) st
.st_size
;
1691 (*ct
->c_ceclosefnx
) (ct
);
1700 static unsigned char b642nib
[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1705 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1706 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1707 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1708 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1709 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1710 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1711 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1712 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1713 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1714 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1715 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1716 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1723 return init_encoding (ct
, openBase64
);
1728 openBase64 (CT ct
, char **file
)
1730 int bitno
, cc
, digested
;
1731 int fd
, len
, skip
, own_ct_fp
= 0;
1733 unsigned char value
, b
;
1734 unsigned char *cp
, *ep
;
1735 char 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
++) {
1833 if (skip
|| (*cp
& 0x80)
1834 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
1836 fprintf (stderr
, "*cp=0x%x pos=%ld skip=%d\n",
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;
1968 unsigned char *cp
, *ep
;
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
--)
2064 for (; cp
< ep
; cp
++) {
2066 /* in an escape sequence */
2068 /* at byte 1 of an escape sequence */
2069 mask
= hex2nib
[*cp
& 0x7f];
2070 /* next is byte 2 */
2073 /* at byte 2 of an escape sequence */
2075 mask
|= hex2nib
[*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 (cp
[1]) && isxdigit (cp
[2])) {
2103 /* Next 2 bytes are hex digits, making this a valid escape
2104 * sequence; let's decode it (above). */
2108 /* One or both of the next 2 is out of range, making this
2109 * an invalid escape sequence; just show the raw bytes
2114 /* Just show the raw byte. */
2115 putc (*cp
, ce
->ce_fp
);
2118 MD5Update (&mdContext
, (unsigned char *) "\r\n",2);
2120 MD5Update (&mdContext
, (unsigned char *) cp
, 1);
2123 if (ferror (ce
->ce_fp
)) {
2124 content_error (ce
->ce_file
, ct
, "error writing to");
2130 content_error (NULL
, ct
,
2131 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2135 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2137 if (fflush (ce
->ce_fp
)) {
2138 content_error (ce
->ce_file
, ct
, "error writing to");
2143 unsigned char digest
[16];
2145 MD5Final (digest
, &mdContext
);
2146 if (memcmp((char *) digest
, (char *) ct
->c_digest
,
2147 sizeof(digest
) / sizeof(digest
[0])))
2148 content_error (NULL
, ct
,
2149 "content integrity suspect (digest mismatch) -- continuing");
2152 fprintf (stderr
, "content integrity confirmed\n");
2155 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2158 *file
= ce
->ce_file
;
2163 return fileno (ce
->ce_fp
);
2166 free_encoding (ct
, 0);
2182 if (init_encoding (ct
, open7Bit
) == NOTOK
)
2185 ct
->c_cesizefnx
= NULL
; /* no need to decode for real size */
2191 open7Bit (CT ct
, char **file
)
2193 int cc
, fd
, len
, own_ct_fp
= 0;
2194 char buffer
[BUFSIZ
];
2195 /* sbeck -- handle suffixes */
2202 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2207 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2208 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2214 if (*file
== NULL
) {
2215 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2218 ce
->ce_file
= add (*file
, NULL
);
2222 /* sbeck@cise.ufl.edu -- handle suffixes */
2224 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s/%s",
2225 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
2226 cp
= context_find (buffer
);
2227 if (cp
== NULL
|| *cp
== '\0') {
2228 snprintf (buffer
, sizeof(buffer
), "%s-suffix-%s", invo_name
,
2230 cp
= context_find (buffer
);
2232 if (cp
!= NULL
&& *cp
!= '\0') {
2233 if (ce
->ce_unlink
) {
2234 /* Temporary file already exists, so we rename to
2235 version with extension. */
2236 char *file_org
= strdup(ce
->ce_file
);
2237 ce
->ce_file
= add (cp
, ce
->ce_file
);
2238 if (rename(file_org
, ce
->ce_file
)) {
2239 adios (ce
->ce_file
, "unable to rename %s to ", file_org
);
2244 ce
->ce_file
= add (cp
, ce
->ce_file
);
2248 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2249 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2253 if (ct
->c_type
== CT_MULTIPART
) {
2255 CI ci
= &ct
->c_ctinfo
;
2258 fprintf (ce
->ce_fp
, "%s: %s/%s", TYPE_FIELD
, ci
->ci_type
, ci
->ci_subtype
);
2259 len
+= strlen (TYPE_FIELD
) + 2 + strlen (ci
->ci_type
)
2260 + 1 + strlen (ci
->ci_subtype
);
2261 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
2262 putc (';', ce
->ce_fp
);
2265 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
2267 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
2268 fputs ("\n\t", ce
->ce_fp
);
2271 putc (' ', ce
->ce_fp
);
2274 fprintf (ce
->ce_fp
, "%s", buffer
);
2278 if (ci
->ci_comment
) {
2279 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
2280 fputs ("\n\t", ce
->ce_fp
);
2284 putc (' ', ce
->ce_fp
);
2287 fprintf (ce
->ce_fp
, "(%s)", ci
->ci_comment
);
2290 fprintf (ce
->ce_fp
, "\n");
2292 fprintf (ce
->ce_fp
, "%s:%s", ID_FIELD
, ct
->c_id
);
2294 fprintf (ce
->ce_fp
, "%s:%s", DESCR_FIELD
, ct
->c_descr
);
2296 fprintf (ce
->ce_fp
, "%s:%s", DISPO_FIELD
, ct
->c_dispo
);
2297 fprintf (ce
->ce_fp
, "\n");
2300 if ((len
= ct
->c_end
- ct
->c_begin
) < 0)
2301 adios (NULL
, "internal error(3)");
2304 if ((ct
->c_fp
= fopen (ct
->c_file
, "r")) == NULL
) {
2305 content_error (ct
->c_file
, ct
, "unable to open for reading");
2311 lseek (fd
= fileno (ct
->c_fp
), (off_t
) ct
->c_begin
, SEEK_SET
);
2313 switch (cc
= read (fd
, buffer
, sizeof(buffer
) - 1)) {
2315 content_error (ct
->c_file
, ct
, "error reading from");
2319 content_error (NULL
, ct
, "premature eof");
2327 fwrite (buffer
, sizeof(*buffer
), cc
, ce
->ce_fp
);
2328 if (ferror (ce
->ce_fp
)) {
2329 content_error (ce
->ce_file
, ct
, "error writing to");
2334 fseek (ct
->c_fp
, 0L, SEEK_SET
);
2336 if (fflush (ce
->ce_fp
)) {
2337 content_error (ce
->ce_file
, ct
, "error writing to");
2341 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2344 *file
= ce
->ce_file
;
2349 return fileno (ce
->ce_fp
);
2352 free_encoding (ct
, 0);
2366 openExternal (CT ct
, CT cb
, CE ce
, char **file
, int *fd
)
2368 char cachefile
[BUFSIZ
];
2371 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2376 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2377 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2383 if (find_cache (ct
, rcachesw
, (int *) 0, cb
->c_id
,
2384 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2385 if ((ce
->ce_fp
= fopen (cachefile
, "r"))) {
2386 ce
->ce_file
= getcpy (cachefile
);
2390 admonish (cachefile
, "unable to fopen for reading");
2397 *file
= ce
->ce_file
;
2398 *fd
= fileno (ce
->ce_fp
);
2409 return init_encoding (ct
, openFile
);
2414 openFile (CT ct
, char **file
)
2417 char cachefile
[BUFSIZ
];
2418 struct exbody
*e
= ct
->c_ctexbody
;
2419 CE ce
= ct
->c_cefile
;
2421 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2433 content_error (NULL
, ct
, "missing name parameter");
2437 ce
->ce_file
= getcpy (e
->eb_name
);
2440 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "r")) == NULL
) {
2441 content_error (ce
->ce_file
, ct
, "unable to fopen for reading");
2445 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2446 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2447 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2451 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2452 if ((fp
= fopen (cachefile
, "w"))) {
2454 char buffer
[BUFSIZ
];
2455 FILE *gp
= ce
->ce_fp
;
2457 fseek (gp
, 0L, SEEK_SET
);
2459 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2461 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2465 admonish (ce
->ce_file
, "error reading");
2470 admonish (cachefile
, "error writing");
2478 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2479 *file
= ce
->ce_file
;
2480 return fileno (ce
->ce_fp
);
2490 return init_encoding (ct
, openFTP
);
2495 openFTP (CT ct
, char **file
)
2497 int cachetype
, caching
, fd
;
2499 char *bp
, *ftp
, *user
, *pass
;
2500 char buffer
[BUFSIZ
], cachefile
[BUFSIZ
];
2503 static char *username
= NULL
;
2504 static char *password
= NULL
;
2509 if ((ftp
= context_find (nmhaccessftp
)) && !*ftp
)
2515 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2526 if (!e
->eb_name
|| !e
->eb_site
) {
2527 content_error (NULL
, ct
, "missing %s parameter",
2528 e
->eb_name
? "site": "name");
2535 pidcheck (pidwait (xpid
, NOTOK
));
2539 /* Get the buffer ready to go */
2541 buflen
= sizeof(buffer
);
2544 * Construct the query message for user
2546 snprintf (bp
, buflen
, "Retrieve %s", e
->eb_name
);
2552 snprintf (bp
, buflen
, " (content %s)", e
->eb_partno
);
2558 snprintf (bp
, buflen
, "\n using %sFTP from site %s",
2559 e
->eb_flags
? "anonymous " : "", e
->eb_site
);
2564 if (e
->eb_size
> 0) {
2565 snprintf (bp
, buflen
, " (%lu octets)", e
->eb_size
);
2570 snprintf (bp
, buflen
, "? ");
2573 * Now, check the answer
2575 if (!getanswer (buffer
))
2580 snprintf (buffer
, sizeof(buffer
), "%s@%s", getusername (),
2584 ruserpass (e
->eb_site
, &username
, &password
);
2589 ce
->ce_unlink
= (*file
== NULL
);
2591 cachefile
[0] = '\0';
2592 if ((!e
->eb_permission
|| mh_strcasecmp (e
->eb_permission
, "read-write"))
2593 && find_cache (NULL
, wcachesw
, &cachetype
, e
->eb_content
->c_id
,
2594 cachefile
, sizeof(cachefile
)) != NOTOK
) {
2595 if (*file
== NULL
) {
2602 ce
->ce_file
= add (*file
, NULL
);
2604 ce
->ce_file
= add (cachefile
, NULL
);
2606 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2608 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2609 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2614 int child_id
, i
, vecp
;
2618 vec
[vecp
++] = r1bindex (ftp
, '/');
2619 vec
[vecp
++] = e
->eb_site
;
2622 vec
[vecp
++] = e
->eb_dir
;
2623 vec
[vecp
++] = e
->eb_name
;
2624 vec
[vecp
++] = ce
->ce_file
,
2625 vec
[vecp
++] = e
->eb_mode
&& !mh_strcasecmp (e
->eb_mode
, "ascii")
2626 ? "ascii" : "binary";
2631 for (i
= 0; (child_id
= vfork()) == NOTOK
&& i
< 5; i
++)
2635 adios ("fork", "unable to");
2639 close (fileno (ce
->ce_fp
));
2641 fprintf (stderr
, "unable to exec ");
2647 if (pidXwait (child_id
, NULL
)) {
2648 username
= password
= NULL
;
2658 chmod (cachefile
, cachetype
? m_gmprot () : 0444);
2663 mask
= umask (cachetype
? ~m_gmprot () : 0222);
2664 if ((fp
= fopen (cachefile
, "w"))) {
2666 FILE *gp
= ce
->ce_fp
;
2668 fseek (gp
, 0L, SEEK_SET
);
2670 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), gp
))
2672 fwrite (buffer
, sizeof(*buffer
), cc
, fp
);
2676 admonish (ce
->ce_file
, "error reading");
2681 admonish (cachefile
, "error writing");
2690 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2691 *file
= ce
->ce_file
;
2692 return fileno (ce
->ce_fp
);
2703 return init_encoding (ct
, openMail
);
2708 openMail (CT ct
, char **file
)
2710 int child_id
, fd
, i
, vecp
;
2712 char *bp
, buffer
[BUFSIZ
], *vec
[7];
2713 struct exbody
*e
= ct
->c_ctexbody
;
2714 CE ce
= ct
->c_cefile
;
2716 switch (openExternal (e
->eb_parent
, e
->eb_content
, ce
, file
, &fd
)) {
2727 if (!e
->eb_server
) {
2728 content_error (NULL
, ct
, "missing server parameter");
2735 pidcheck (pidwait (xpid
, NOTOK
));
2739 /* Get buffer ready to go */
2741 buflen
= sizeof(buffer
);
2743 /* Now, construct query message */
2744 snprintf (bp
, buflen
, "Retrieve content");
2750 snprintf (bp
, buflen
, " %s", e
->eb_partno
);
2756 snprintf (bp
, buflen
, " by asking %s\n\n%s\n? ",
2758 e
->eb_subject
? e
->eb_subject
: e
->eb_body
);
2760 /* Now, check answer */
2761 if (!getanswer (buffer
))
2765 vec
[vecp
++] = r1bindex (mailproc
, '/');
2766 vec
[vecp
++] = e
->eb_server
;
2767 vec
[vecp
++] = "-subject";
2768 vec
[vecp
++] = e
->eb_subject
? e
->eb_subject
: "mail-server request";
2769 vec
[vecp
++] = "-body";
2770 vec
[vecp
++] = e
->eb_body
;
2773 for (i
= 0; (child_id
= vfork()) == NOTOK
&& i
< 5; i
++)
2777 advise ("fork", "unable to");
2781 execvp (mailproc
, vec
);
2782 fprintf (stderr
, "unable to exec ");
2788 if (pidXwait (child_id
, NULL
) == OK
)
2789 advise (NULL
, "request sent");
2793 if (*file
== NULL
) {
2794 ce
->ce_file
= add (m_mktemp(tmp
, NULL
, NULL
), NULL
);
2797 ce
->ce_file
= add (*file
, NULL
);
2801 if ((ce
->ce_fp
= fopen (ce
->ce_file
, "w+")) == NULL
) {
2802 content_error (ce
->ce_file
, ct
, "unable to fopen for reading/writing");
2806 /* showproc is for mhshow and mhstore, though mhlist -debug
2807 * prints it, too. */
2809 free (ct
->c_showproc
);
2810 ct
->c_showproc
= add ("true", NULL
);
2812 fseek (ce
->ce_fp
, 0L, SEEK_SET
);
2813 *file
= ce
->ce_file
;
2814 return fileno (ce
->ce_fp
);
2819 readDigest (CT ct
, char *cp
)
2824 unsigned char *dp
, value
, *ep
;
2830 for (ep
= (dp
= ct
->c_digest
)
2831 + sizeof(ct
->c_digest
) / sizeof(ct
->c_digest
[0]); *cp
; cp
++)
2836 || (value
= b642nib
[*cp
& 0x7f]) > 0x3f) {
2838 fprintf (stderr
, "invalid BASE64 encoding\n");
2842 bits
|= value
<< bitno
;
2844 if ((bitno
-= 6) < 0) {
2845 if (dp
+ (3 - skip
) > ep
)
2846 goto invalid_digest
;
2847 *dp
++ = (bits
>> 16) & 0xff;
2849 *dp
++ = (bits
>> 8) & 0xff;
2851 *dp
++ = bits
& 0xff;
2861 goto self_delimiting
;
2866 fprintf (stderr
, "premature ending (bitno %d)\n", bitno
);
2876 fprintf (stderr
, "invalid MD5 digest (got %d octets)\n",
2884 fprintf (stderr
, "MD5 digest=");
2885 for (dp
= ct
->c_digest
; dp
< ep
; dp
++)
2886 fprintf (stderr
, "%02x", *dp
& 0xff);
2887 fprintf (stderr
, "\n");