3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
13 * This code was originally part of mhn.c. I split it into
14 * a separate program (mhbuild.c) and then later split it
15 * again (mhbuildsbr.c). But the code still has some of
16 * the mhn.c code in it. This program needs additional
17 * streamlining and removal of unneeded code.
22 #include <h/signals.h>
29 #include <h/mhparse.h>
32 #ifdef TIME_WITH_SYS_TIME
33 # include <sys/time.h>
36 # ifdef TM_IN_SYS_TIME
37 # include <sys/time.h>
43 #ifdef HAVE_SYS_WAIT_H
44 # include <sys/wait.h>
54 extern int contentidsw;
56 extern int endian; /* mhmisc.c */
59 extern int rcachesw; /* mhcachesbr.c */
60 extern int wcachesw; /* mhcachesbr.c */
62 int checksw = 0; /* Add Content-MD5 field */
65 * Directory to place tmp files. This must
66 * be set before these routines are called.
72 static char prefix[] = "----- =_aaaaaaaaaa";
75 * Structure for mapping types to their internal flags
83 * Structures for TEXT messages
85 static struct k2v SubText[] = {
86 { "plain", TEXT_PLAIN },
87 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
88 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
89 { NULL, TEXT_UNKNOWN } /* this one must be last! */
92 static struct k2v Charset[] = {
93 { "us-ascii", CHARSET_USASCII },
94 { "iso-8859-1", CHARSET_LATIN },
95 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
99 * Structures for MULTIPART messages
101 static struct k2v SubMultiPart[] = {
102 { "mixed", MULTI_MIXED },
103 { "alternative", MULTI_ALTERNATE },
104 { "digest", MULTI_DIGEST },
105 { "parallel", MULTI_PARALLEL },
106 { NULL, MULTI_UNKNOWN } /* this one must be last! */
110 * Structures for MESSAGE messages
112 static struct k2v SubMessage[] = {
113 { "rfc822", MESSAGE_RFC822 },
114 { "partial", MESSAGE_PARTIAL },
115 { "external-body", MESSAGE_EXTERNAL },
116 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
120 * Structure for APPLICATION messages
122 static struct k2v SubApplication[] = {
123 { "octet-stream", APPLICATION_OCTETS },
124 { "postscript", APPLICATION_POSTSCRIPT },
125 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
130 int make_intermediates (char *);
131 void content_error (char *, CT, char *, ...);
134 int find_cache (CT, int, int *, char *, char *, int);
137 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
140 void free_content (CT);
141 void free_ctinfo (CT);
142 void free_encoding (CT, int);
147 CT build_mime (char *);
153 static CT get_content (FILE *, char *, int);
154 static int add_header (CT, char *, char *);
155 static int get_ctinfo (char *, CT, int);
156 static int get_comment (CT, char **, int);
157 static int InitGeneric (CT);
158 static int InitText (CT);
159 static int InitMultiPart (CT);
160 static void reverse_parts (CT);
161 static int InitMessage (CT);
162 static int params_external (CT, int);
163 static int InitApplication (CT);
164 static int init_decoded_content (CT);
165 static int init_encoding (CT, OpenCEFunc);
166 static void close_encoding (CT);
167 static unsigned long size_encoding (CT);
168 static int InitBase64 (CT);
169 static int openBase64 (CT, char **);
170 static int InitQuoted (CT);
171 static int openQuoted (CT, char **);
172 static int Init7Bit (CT);
173 static int open7Bit (CT, char **);
174 static int openExternal (CT, CT, CE, char **, int *);
175 static int InitFile (CT);
176 static int openFile (CT, char **);
177 static int InitFTP (CT);
178 static int openFTP (CT, char **);
179 static int InitMail (CT);
180 static int openMail (CT, char **);
181 static char *fgetstr (char *, int, FILE *);
182 static int user_content (FILE *, char *, char *, CT *);
183 static void set_id (CT, int);
184 static int compose_content (CT);
185 static int scan_content (CT);
186 static int build_headers (CT);
187 static char *calculate_digest (CT, int);
188 static int readDigest (CT, char *);
189 static char *incl_name_value (char *, char *, char *);
190 static char *extract_name_value (char *, char *);
193 * Structures for mapping (content) types to
194 * the functions to handle them.
202 static struct str2init str2cts[] = {
203 { "application", CT_APPLICATION, InitApplication },
204 { "audio", CT_AUDIO, InitGeneric },
205 { "image", CT_IMAGE, InitGeneric },
206 { "message", CT_MESSAGE, InitMessage },
207 { "multipart", CT_MULTIPART, InitMultiPart },
208 { "text", CT_TEXT, InitText },
209 { "video", CT_VIDEO, InitGeneric },
210 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
211 { NULL, CT_UNKNOWN, NULL },
214 static struct str2init str2ces[] = {
215 { "base64", CE_BASE64, InitBase64 },
216 { "quoted-printable", CE_QUOTED, InitQuoted },
217 { "8bit", CE_8BIT, Init7Bit },
218 { "7bit", CE_7BIT, Init7Bit },
219 { "binary", CE_BINARY, NULL },
220 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
221 { NULL, CE_UNKNOWN, NULL },
225 * NOTE WELL: si_key MUST NOT have value of NOTOK
227 * si_key is 1 if access method is anonymous.
229 static struct str2init str2methods[] = {
230 { "afs", 1, InitFile },
231 { "anon-ftp", 1, InitFTP },
232 { "ftp", 0, InitFTP },
233 { "local-file", 0, InitFile },
234 { "mail-server", 0, InitMail },
240 pidcheck (int status)
242 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
252 * Main routine for translating composition file
253 * into valid MIME message. It translates the draft
254 * into a content structure (actually a tree of content
255 * structures). This message then can be manipulated
256 * in various ways, including being output via
261 build_mime (char *infile)
264 char buf[BUFSIZ], name[NAMESZ];
271 umask (~m_gmprot ());
273 /* open the composition draft */
274 if ((in = fopen (infile, "r")) == NULL)
275 adios (infile, "unable to open for reading");
278 * Allocate space for primary (outside) content
280 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
281 adios (NULL, "out of memory");
284 * Allocate structure for handling decoded content
285 * for this part. We don't really need this, but
286 * allocate it to remain consistent.
288 init_decoded_content (ct);
291 * Parse some of the header fields in the composition
292 * draft into the linked list of header fields for
293 * the new MIME message.
295 for (compnum = 1, state = FLD;;) {
296 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
302 /* abort if draft has Mime-Version header field */
303 if (!strcasecmp (name, VRSN_FIELD))
304 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
306 /* abort if draft has Content-Transfer-Encoding header field */
307 if (!strcasecmp (name, ENCODING_FIELD))
308 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
310 /* ignore any Content-Type fields in the header */
311 if (!strcasecmp (name, TYPE_FIELD)) {
312 while (state == FLDPLUS)
313 state = m_getfld (state, name, buf, sizeof(buf), in);
317 /* get copies of the buffers */
318 np = add (name, NULL);
319 vp = add (buf, NULL);
321 /* if necessary, get rest of field */
322 while (state == FLDPLUS) {
323 state = m_getfld (state, name, buf, sizeof(buf), in);
324 vp = add (buf, vp); /* add to previous value */
327 /* Now add the header data to the list */
328 add_header (ct, np, vp);
331 /* if this wasn't the last header field, then continue */
337 adios (NULL, "draft has empty body -- no directives!");
342 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
347 adios (NULL, "message format error in component #%d", compnum);
350 adios (NULL, "getfld() returned %d", state);
356 * Now add the MIME-Version header field
357 * to the list of header fields.
359 np = add (VRSN_FIELD, NULL);
360 vp = concat (" ", VRSN_VALUE, "\n", NULL);
361 add_header (ct, np, vp);
364 * We initally assume we will find multiple contents in the
365 * draft. So create a multipart/mixed content to hold everything.
366 * We can remove this later, if it is not needed.
368 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
370 ct->c_type = CT_MULTIPART;
371 ct->c_subtype = MULTI_MIXED;
372 ct->c_file = add (infile, NULL);
374 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
375 adios (NULL, "out of memory");
376 ct->c_ctparams = (void *) m;
380 * read and parse the composition file
381 * and the directives it contains.
383 while (fgetstr (buf, sizeof(buf) - 1, in)) {
387 if (user_content (in, infile, buf, &p) == DONE) {
388 admonish (NULL, "ignoring spurious #end");
394 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
395 adios (NULL, "out of memory");
402 * close the composition draft since
403 * it's not needed any longer.
407 /* check if any contents were found */
409 adios (NULL, "no content directives found");
412 * If only one content was found, then remove and
413 * free the outer multipart content.
415 if (!m->mp_parts->mp_next) {
418 p = m->mp_parts->mp_part;
419 m->mp_parts->mp_part = NULL;
421 /* move header fields */
422 p->c_first_hf = ct->c_first_hf;
423 p->c_last_hf = ct->c_last_hf;
424 ct->c_first_hf = NULL;
425 ct->c_last_hf = NULL;
434 * Fill out, or expand directives. Parse and execute
435 * commands specified by profile composition strings.
437 compose_content (ct);
439 if ((cp = strchr(prefix, 'a')) == NULL)
440 adios (NULL, "internal error(4)");
443 * Scan the contents. Choose a transfer encoding, and
444 * check if prefix for multipart boundary clashes with
445 * any of the contents.
447 while (scan_content (ct) == NOTOK) {
452 adios (NULL, "giving up trying to find a unique delimiter string");
458 /* Build the rest of the header field structures */
466 * Main routine for reading/parsing the headers
467 * of a message content.
469 * toplevel = 1 # we are at the top level of the message
470 * toplevel = 0 # we are inside message type or multipart type
471 * # other than multipart/digest
472 * toplevel = -1 # we are inside multipart/digest
476 get_content (FILE *in, char *file, int toplevel)
479 char buf[BUFSIZ], name[NAMESZ];
482 if (!(ct = (CT) calloc (1, sizeof(*ct))))
483 adios (NULL, "out of memory");
486 ct->c_file = add (file, NULL);
487 ct->c_begin = ftell (ct->c_fp) + 1;
490 * Read the content headers
492 for (compnum = 1, state = FLD;;) {
493 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
499 /* Get MIME-Version field */
500 if (!strcasecmp (name, VRSN_FIELD)) {
504 cp = add (buf, NULL);
505 while (state == FLDPLUS) {
506 state = m_getfld (state, name, buf, sizeof(buf), in);
511 advise (NULL, "message %s has multiple %s: fields (%s)",
512 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
519 while (isspace (*cp))
521 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
523 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
528 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
530 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
533 for (dp = cp; istoken (*dp); dp++)
536 ucmp = !strcasecmp (cp, VRSN_VALUE);
540 "message %s has unknown value for %s: field (%s)",
541 ct->c_file, VRSN_FIELD, cp);
545 /* Get Content-Type field */
546 if (!strcasecmp (name, TYPE_FIELD)) {
548 struct str2init *s2i;
549 CI ci = &ct->c_ctinfo;
551 cp = add (buf, NULL);
552 while (state == FLDPLUS) {
553 state = m_getfld (state, name, buf, sizeof(buf), in);
557 /* Check if we've already seen a Content-Type header */
559 char *dp = trimcpy (cp);
561 advise (NULL, "message %s has multiple %s: fields (%s)",
562 ct->c_file, TYPE_FIELD, dp);
568 /* Parse the Content-Type field */
569 if (get_ctinfo (cp, ct, 0) == NOTOK)
573 * Set the Init function and the internal
574 * flag for this content type.
576 for (s2i = str2cts; s2i->si_key; s2i++)
577 if (!strcasecmp (ci->ci_type, s2i->si_key))
579 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
581 ct->c_type = s2i->si_val;
582 ct->c_ctinitfnx = s2i->si_init;
586 /* Get Content-Transfer-Encoding field */
587 if (!strcasecmp (name, ENCODING_FIELD)) {
590 struct str2init *s2i;
592 cp = add (buf, NULL);
593 while (state == FLDPLUS) {
594 state = m_getfld (state, name, buf, sizeof(buf), in);
599 * Check if we've already seen the
600 * Content-Transfer-Encoding field
603 advise (NULL, "message %s has multiple %s: fields (%s)",
604 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
610 ct->c_celine = cp; /* Save copy of this field */
611 while (isspace (*cp))
613 for (dp = cp; istoken (*dp); dp++)
619 * Find the internal flag and Init function
620 * for this transfer encoding.
622 for (s2i = str2ces; s2i->si_key; s2i++)
623 if (!strcasecmp (cp, s2i->si_key))
625 if (!s2i->si_key && !uprf (cp, "X-"))
628 ct->c_encoding = s2i->si_val;
630 /* Call the Init function for this encoding */
631 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
636 /* Get Content-ID field */
637 if (!strcasecmp (name, ID_FIELD)) {
638 ct->c_id = add (buf, ct->c_id);
639 while (state == FLDPLUS) {
640 state = m_getfld (state, name, buf, sizeof(buf), in);
641 ct->c_id = add (buf, ct->c_id);
646 /* Get Content-Description field */
647 if (!strcasecmp (name, DESCR_FIELD)) {
648 ct->c_descr = add (buf, ct->c_descr);
649 while (state == FLDPLUS) {
650 state = m_getfld (state, name, buf, sizeof(buf), in);
651 ct->c_descr = add (buf, ct->c_descr);
656 /* Get Content-Disposition field */
657 if (!strcasecmp (name, DISPO_FIELD)) {
658 ct->c_dispo = add (buf, ct->c_dispo);
659 while (state == FLDPLUS) {
660 state = m_getfld (state, name, buf, sizeof(buf), in);
661 ct->c_dispo = add (buf, ct->c_dispo);
666 /* Get Content-MD5 field */
667 if (!strcasecmp (name, MD5_FIELD)) {
670 cp = add (buf, NULL);
671 while (state == FLDPLUS) {
672 state = m_getfld (state, name, buf, sizeof(buf), in);
681 if (ct->c_digested) {
682 advise (NULL, "message %s has multiple %s: fields (%s)",
683 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
690 while (isspace (*cp))
692 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
694 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
699 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
701 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
706 for (dp = cp; *dp && !isspace (*dp); dp++)
717 if (uprf (name, XXX_FIELD_PRF))
718 advise (NULL, "unknown field (%s) in message %s",
723 while (state == FLDPLUS)
724 state = m_getfld (state, name, buf, sizeof(buf), in);
727 if (state != FLDEOF) {
728 ct->c_begin = ftell (in) + 1;
735 ct->c_begin = ftell (in) - strlen (buf);
739 ct->c_begin = ftell (in);
744 adios (NULL, "message format error in component #%d", compnum);
747 adios (NULL, "getfld() returned %d", state);
753 * Check if we saw a Content-Type field.
754 * If not, then assign a default value for
755 * it, and the Init function.
759 * If we are inside a multipart/digest message,
760 * so default type is message/rfc822
763 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
765 ct->c_type = CT_MESSAGE;
766 ct->c_ctinitfnx = InitMessage;
769 * Else default type is text/plain
771 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
773 ct->c_type = CT_TEXT;
774 ct->c_ctinitfnx = InitText;
778 /* Use default Transfer-Encoding, if necessary */
780 ct->c_encoding = CE_7BIT;
793 * small routine to add header field to list
797 add_header (CT ct, char *name, char *value)
801 /* allocate header field structure */
802 hp = mh_xmalloc (sizeof(*hp));
804 /* link data into header structure */
809 /* link header structure into the list */
810 if (ct->c_first_hf == NULL) {
811 ct->c_first_hf = hp; /* this is the first */
814 ct->c_last_hf->next = hp; /* add it to the end */
823 * Used to parse both:
824 * 1) Content-Type line
825 * 2) composition directives
827 * and fills in the information of the CTinfo structure.
831 get_ctinfo (char *cp, CT ct, int magic)
834 char *dp, **ap, **ep;
839 i = strlen (invo_name) + 2;
841 /* store copy of Content-Type line */
842 cp = ct->c_ctline = add (cp, NULL);
844 while (isspace (*cp)) /* trim leading spaces */
847 /* change newlines to spaces */
848 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
851 /* trim trailing spaces */
852 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
858 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
860 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
863 for (dp = cp; istoken (*dp); dp++)
866 ci->ci_type = add (cp, NULL); /* store content type */
870 advise (NULL, "invalid %s: field in message %s (empty type)",
871 TYPE_FIELD, ct->c_file);
875 /* down case the content type string */
876 for (dp = ci->ci_type; *dp; dp++)
877 if (isalpha(*dp) && isupper (*dp))
880 while (isspace (*cp))
883 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
888 ci->ci_subtype = add ("", NULL);
893 while (isspace (*cp))
896 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
899 for (dp = cp; istoken (*dp); dp++)
902 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
905 if (!*ci->ci_subtype) {
907 "invalid %s: field in message %s (empty subtype for \"%s\")",
908 TYPE_FIELD, ct->c_file, ci->ci_type);
912 /* down case the content subtype string */
913 for (dp = ci->ci_subtype; *dp; dp++)
914 if (isalpha(*dp) && isupper (*dp))
918 while (isspace (*cp))
921 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
925 * Parse attribute/value pairs given with Content-Type
927 ep = (ap = ci->ci_attrs) + NPARMS;
933 "too many parameters in message %s's %s: field (%d max)",
934 ct->c_file, TYPE_FIELD, NPARMS);
939 while (isspace (*cp))
942 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
947 "extraneous trailing ';' in message %s's %s: parameter list",
948 ct->c_file, TYPE_FIELD);
952 /* down case the attribute name */
953 for (dp = cp; istoken (*dp); dp++)
954 if (isalpha(*dp) && isupper (*dp))
957 for (up = dp; isspace (*dp); )
959 if (dp == cp || *dp != '=') {
961 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
962 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
966 vp = (*ap = add (cp, NULL)) + (up - cp);
968 for (dp++; isspace (*dp); )
971 /* now add the attribute value */
972 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
975 for (cp = ++dp, dp = vp;;) {
980 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
981 ct->c_file, TYPE_FIELD, i, i, "", *ap);
986 if ((c = *cp++) == '\0')
1001 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
1007 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
1008 ct->c_file, TYPE_FIELD, i, i, "", *ap);
1013 while (isspace (*cp))
1016 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1021 * Get any <Content-Id> given in buffer
1023 if (magic && *cp == '<') {
1028 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1029 advise (NULL, "invalid ID in message %s", ct->c_file);
1035 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1041 while (isspace (*cp))
1046 * Get any [Content-Description] given in buffer.
1048 if (magic && *cp == '[') {
1050 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1054 advise (NULL, "invalid description in message %s", ct->c_file);
1062 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1068 while (isspace (*cp))
1073 * Get any {Content-Disposition} given in buffer.
1075 if (magic && *cp == '{') {
1077 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1081 advise (NULL, "invalid disposition in message %s", ct->c_file);
1089 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
1095 while (isspace (*cp))
1100 * Check if anything is left over
1104 ci->ci_magic = add (cp, NULL);
1106 /* If there is a Content-Disposition header and it doesn't
1107 have a *filename=, extract it from the magic contents.
1108 The r1bindex call skips any leading directory
1112 incl_name_value (ct->c_dispo,
1114 r1bindex (extract_name_value ("name",
1120 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1121 ct->c_file, TYPE_FIELD, i, i, "", cp);
1129 get_comment (CT ct, char **ap, int istype)
1133 char c, buffer[BUFSIZ], *dp;
1142 switch (c = *cp++) {
1145 advise (NULL, "invalid comment in message %s's %s: field",
1146 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1151 if ((c = *cp++) == '\0')
1174 if ((dp = ci->ci_comment)) {
1175 ci->ci_comment = concat (dp, " ", buffer, NULL);
1178 ci->ci_comment = add (buffer, NULL);
1182 while (isspace (*cp))
1193 * Handles content types audio, image, and video.
1194 * There's not much to do right here.
1200 return OK; /* not much to do here */
1214 CI ci = &ct->c_ctinfo;
1216 /* check for missing subtype */
1217 if (!*ci->ci_subtype)
1218 ci->ci_subtype = add ("plain", ci->ci_subtype);
1221 for (kv = SubText; kv->kv_key; kv++)
1222 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1224 ct->c_subtype = kv->kv_value;
1226 /* allocate text character set structure */
1227 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1228 adios (NULL, "out of memory");
1229 ct->c_ctparams = (void *) t;
1231 /* initially mark character set as unspecified */
1232 t->tx_charset = CHARSET_UNSPECIFIED;
1234 /* scan for charset parameter */
1235 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1236 if (!strcasecmp (*ap, "charset"))
1239 /* check if content specified a character set */
1241 /* match character set or set to CHARSET_UNKNOWN */
1242 for (kv = Charset; kv->kv_key; kv++)
1243 if (!strcasecmp (*ep, kv->kv_key))
1245 t->tx_charset = kv->kv_value;
1257 InitMultiPart (CT ct)
1261 char *cp, *dp, **ap, **ep;
1262 char *bp, buffer[BUFSIZ];
1263 struct multipart *m;
1265 struct part *part, **next;
1266 CI ci = &ct->c_ctinfo;
1271 * The encoding for multipart messages must be either
1272 * 7bit, 8bit, or binary (per RFC2045).
1274 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1275 && ct->c_encoding != CE_BINARY) {
1277 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1278 ci->ci_type, ci->ci_subtype, ct->c_file);
1283 for (kv = SubMultiPart; kv->kv_key; kv++)
1284 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1286 ct->c_subtype = kv->kv_value;
1289 * Check for "boundary" parameter, which is
1290 * required for multipart messages.
1292 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1293 if (!strcasecmp (*ap, "boundary")) {
1299 /* complain if boundary parameter is missing */
1302 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1303 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1307 /* allocate primary structure for multipart info */
1308 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1309 adios (NULL, "out of memory");
1310 ct->c_ctparams = (void *) m;
1312 /* check if boundary parameter contains only whitespace characters */
1313 for (cp = bp; isspace (*cp); cp++)
1316 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1317 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1321 /* remove trailing whitespace from boundary parameter */
1322 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1327 /* record boundary separators */
1328 m->mp_start = concat (bp, "\n", NULL);
1329 m->mp_stop = concat (bp, "--\n", NULL);
1331 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1332 advise (ct->c_file, "unable to open for reading");
1336 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1338 next = &m->mp_parts;
1342 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1346 pos += strlen (buffer);
1347 if (buffer[0] != '-' || buffer[1] != '-')
1350 if (strcmp (buffer + 2, m->mp_start))
1353 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1354 adios (NULL, "out of memory");
1356 next = &part->mp_next;
1358 if (!(p = get_content (fp, ct->c_file,
1359 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1367 fseek (fp, pos, SEEK_SET);
1370 if (strcmp (buffer + 2, m->mp_start) == 0) {
1374 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1375 if (p->c_end < p->c_begin)
1376 p->c_begin = p->c_end;
1381 if (strcmp (buffer + 2, m->mp_stop) == 0)
1387 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1388 if (!inout && part) {
1390 p->c_end = ct->c_end;
1392 if (p->c_begin >= p->c_end) {
1393 for (next = &m->mp_parts; *next != part;
1394 next = &((*next)->mp_next))
1398 free ((char *) part);
1403 /* reverse the order of the parts for multipart/alternative */
1404 if (ct->c_subtype == MULTI_ALTERNATE)
1408 * label all subparts with part number, and
1409 * then initialize the content of the subpart.
1414 char partnam[BUFSIZ];
1417 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1418 pp = partnam + strlen (partnam);
1423 for (part = m->mp_parts, partnum = 1; part;
1424 part = part->mp_next, partnum++) {
1427 sprintf (pp, "%d", partnum);
1428 p->c_partno = add (partnam, NULL);
1430 /* initialize the content of the subparts */
1431 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1446 * reverse the order of the parts of a multipart
1450 reverse_parts (CT ct)
1453 struct multipart *m;
1454 struct part **base, **bmp, **next, *part;
1456 m = (struct multipart *) ct->c_ctparams;
1458 /* if only one part, just return */
1459 if (!m->mp_parts || !m->mp_parts->mp_next)
1462 /* count number of parts */
1464 for (part = m->mp_parts; part; part = part->mp_next)
1467 /* allocate array of pointers to the parts */
1468 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1469 adios (NULL, "out of memory");
1472 /* point at all the parts */
1473 for (part = m->mp_parts; part; part = part->mp_next)
1477 /* reverse the order of the parts */
1478 next = &m->mp_parts;
1479 for (bmp--; bmp >= base; bmp--) {
1482 next = &part->mp_next;
1486 /* free array of pointers */
1487 free ((char *) base);
1499 CI ci = &ct->c_ctinfo;
1501 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1503 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1504 ci->ci_type, ci->ci_subtype, ct->c_file);
1508 /* check for missing subtype */
1509 if (!*ci->ci_subtype)
1510 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1513 for (kv = SubMessage; kv->kv_key; kv++)
1514 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1516 ct->c_subtype = kv->kv_value;
1518 switch (ct->c_subtype) {
1519 case MESSAGE_RFC822:
1522 case MESSAGE_PARTIAL:
1527 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1528 adios (NULL, "out of memory");
1529 ct->c_ctparams = (void *) p;
1531 /* scan for parameters "id", "number", and "total" */
1532 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1533 if (!strcasecmp (*ap, "id")) {
1534 p->pm_partid = add (*ep, NULL);
1537 if (!strcasecmp (*ap, "number")) {
1538 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1539 || p->pm_partno < 1) {
1542 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1543 *ap, ci->ci_type, ci->ci_subtype,
1544 ct->c_file, TYPE_FIELD);
1549 if (!strcasecmp (*ap, "total")) {
1550 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1559 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1561 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1562 ci->ci_type, ci->ci_subtype,
1563 ct->c_file, TYPE_FIELD);
1569 case MESSAGE_EXTERNAL:
1576 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1577 adios (NULL, "out of memory");
1578 ct->c_ctparams = (void *) e;
1581 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1582 advise (ct->c_file, "unable to open for reading");
1586 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1588 if (!(p = get_content (fp, ct->c_file, 0))) {
1597 if ((exresult = params_external (ct, 0)) != NOTOK
1598 && p->c_ceopenfnx == openMail) {
1602 if ((size = ct->c_end - p->c_begin) <= 0) {
1604 content_error (NULL, ct,
1605 "empty body for access-type=mail-server");
1609 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1610 fseek (p->c_fp, p->c_begin, SEEK_SET);
1612 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1614 adios ("failed", "fread");
1617 adios (NULL, "unexpected EOF from fread");
1620 bp += cc, size -= cc;
1627 p->c_end = p->c_begin;
1632 if (exresult == NOTOK)
1634 if (e->eb_flags == NOTOK)
1637 switch (p->c_type) {
1642 if (p->c_subtype != MESSAGE_RFC822)
1646 e->eb_partno = ct->c_partno;
1648 (*p->c_ctinitfnx) (p);
1663 params_external (CT ct, int composing)
1666 struct exbody *e = (struct exbody *) ct->c_ctparams;
1667 CI ci = &ct->c_ctinfo;
1669 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1670 if (!strcasecmp (*ap, "access-type")) {
1671 struct str2init *s2i;
1672 CT p = e->eb_content;
1674 for (s2i = str2methods; s2i->si_key; s2i++)
1675 if (!strcasecmp (*ep, s2i->si_key))
1680 e->eb_flags = NOTOK;
1681 p->c_encoding = CE_EXTERNAL;
1684 e->eb_access = s2i->si_key;
1685 e->eb_flags = s2i->si_val;
1686 p->c_encoding = CE_EXTERNAL;
1688 /* Call the Init function for this external type */
1689 if ((*s2i->si_init)(p) == NOTOK)
1693 if (!strcasecmp (*ap, "name")) {
1697 if (!strcasecmp (*ap, "permission")) {
1698 e->eb_permission = *ep;
1701 if (!strcasecmp (*ap, "site")) {
1705 if (!strcasecmp (*ap, "directory")) {
1709 if (!strcasecmp (*ap, "mode")) {
1713 if (!strcasecmp (*ap, "size")) {
1714 sscanf (*ep, "%lu", &e->eb_size);
1717 if (!strcasecmp (*ap, "server")) {
1721 if (!strcasecmp (*ap, "subject")) {
1722 e->eb_subject = *ep;
1725 if (composing && !strcasecmp (*ap, "body")) {
1726 e->eb_body = getcpy (*ep);
1731 if (!e->eb_access) {
1733 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1734 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1747 InitApplication (CT ct)
1750 CI ci = &ct->c_ctinfo;
1753 for (kv = SubApplication; kv->kv_key; kv++)
1754 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1756 ct->c_subtype = kv->kv_value;
1763 * Set up structures for placing unencoded
1764 * content when building parts.
1768 init_decoded_content (CT ct)
1772 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1773 adios (NULL, "out of memory");
1776 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1777 ct->c_ceclosefnx = close_encoding;
1778 ct->c_cesizefnx = NULL; /* since unencoded */
1785 * TRANSFER ENCODINGS
1789 init_encoding (CT ct, OpenCEFunc openfnx)
1793 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1794 adios (NULL, "out of memory");
1797 ct->c_ceopenfnx = openfnx;
1798 ct->c_ceclosefnx = close_encoding;
1799 ct->c_cesizefnx = size_encoding;
1806 close_encoding (CT ct)
1810 if (!(ce = ct->c_cefile))
1820 static unsigned long
1821 size_encoding (CT ct)
1829 if (!(ce = ct->c_cefile))
1830 return (ct->c_end - ct->c_begin);
1832 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1833 return (long) st.st_size;
1836 if (stat (ce->ce_file, &st) != NOTOK)
1837 return (long) st.st_size;
1842 if (ct->c_encoding == CE_EXTERNAL)
1843 return (ct->c_end - ct->c_begin);
1846 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1847 return (ct->c_end - ct->c_begin);
1849 if (fstat (fd, &st) != NOTOK)
1850 size = (long) st.st_size;
1854 (*ct->c_ceclosefnx) (ct);
1863 static unsigned char b642nib[0x80] = {
1864 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1865 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1866 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1867 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1868 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1869 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1870 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1871 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1872 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1873 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1874 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1875 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1876 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1877 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1878 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1879 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1886 return init_encoding (ct, openBase64);
1891 openBase64 (CT ct, char **file)
1893 int bitno, cc, digested;
1896 unsigned char value, *b, *b1, *b2, *b3;
1897 char *cp, *ep, buffer[BUFSIZ];
1901 b = (unsigned char *) &bits;
1902 b1 = &b[endian > 0 ? 1 : 2];
1903 b2 = &b[endian > 0 ? 2 : 1];
1904 b3 = &b[endian > 0 ? 3 : 0];
1908 fseek (ce->ce_fp, 0L, SEEK_SET);
1913 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1914 content_error (ce->ce_file, ct, "unable to fopen for reading");
1920 if (*file == NULL) {
1921 ce->ce_file = add (m_scratch ("", tmp), NULL);
1924 ce->ce_file = add (*file, NULL);
1928 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1929 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1933 if ((len = ct->c_end - ct->c_begin) < 0)
1934 adios (NULL, "internal error(1)");
1936 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1937 content_error (ct->c_file, ct, "unable to open for reading");
1941 if ((digested = ct->c_digested))
1942 MD5Init (&mdContext);
1948 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1950 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1952 content_error (ct->c_file, ct, "error reading from");
1956 content_error (NULL, ct, "premature eof");
1964 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1969 if (skip || (*cp & 0x80)
1970 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1972 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1974 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1977 content_error (NULL, ct,
1978 "invalid BASE64 encoding -- continuing");
1982 bits |= value << bitno;
1984 if ((bitno -= 6) < 0) {
1985 putc ((char) *b1, ce->ce_fp);
1987 MD5Update (&mdContext, b1, 1);
1989 putc ((char) *b2, ce->ce_fp);
1991 MD5Update (&mdContext, b2, 1);
1993 putc ((char) *b3, ce->ce_fp);
1995 MD5Update (&mdContext, b3, 1);
1999 if (ferror (ce->ce_fp)) {
2000 content_error (ce->ce_file, ct,
2001 "error writing to");
2004 bitno = 18, bits = 0L, skip = 0;
2010 goto self_delimiting;
2019 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2021 content_error (NULL, ct, "invalid BASE64 encoding");
2026 fseek (ct->c_fp, 0L, SEEK_SET);
2028 if (fflush (ce->ce_fp)) {
2029 content_error (ce->ce_file, ct, "error writing to");
2034 unsigned char digest[16];
2036 MD5Final (digest, &mdContext);
2037 if (memcmp((char *) digest, (char *) ct->c_digest,
2038 sizeof(digest) / sizeof(digest[0])))
2039 content_error (NULL, ct,
2040 "content integrity suspect (digest mismatch) -- continuing");
2043 fprintf (stderr, "content integrity confirmed\n");
2046 fseek (ce->ce_fp, 0L, SEEK_SET);
2049 *file = ce->ce_file;
2050 return fileno (ce->ce_fp);
2053 free_encoding (ct, 0);
2062 static char hex2nib[0x80] = {
2063 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2064 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2065 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2066 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2067 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2068 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2069 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2070 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2071 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2072 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2073 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2074 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2075 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2076 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2077 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2078 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2085 return init_encoding (ct, openQuoted);
2090 openQuoted (CT ct, char **file)
2092 int cc, digested, len, quoted;
2094 char buffer[BUFSIZ];
2101 fseek (ce->ce_fp, 0L, SEEK_SET);
2106 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2107 content_error (ce->ce_file, ct, "unable to fopen for reading");
2113 if (*file == NULL) {
2114 ce->ce_file = add (m_scratch ("", tmp), NULL);
2117 ce->ce_file = add (*file, NULL);
2121 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2122 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2126 if ((len = ct->c_end - ct->c_begin) < 0)
2127 adios (NULL, "internal error(2)");
2129 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2130 content_error (ct->c_file, ct, "unable to open for reading");
2134 if ((digested = ct->c_digested))
2135 MD5Init (&mdContext);
2142 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2146 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2147 content_error (NULL, ct, "premature eof");
2151 if ((cc = strlen (buffer)) > len)
2155 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2160 for (; cp < ep; cp++) {
2163 if (!isxdigit (*cp)) {
2165 dp = "expecting hexidecimal-digit";
2166 goto invalid_encoding;
2169 mask |= hex2nib[*cp & 0x7f];
2170 putc (mask, ce->ce_fp);
2172 MD5Update (&mdContext, &mask, 1);
2176 putc (*cp, ce->ce_fp);
2178 MD5Update (&mdContext, (unsigned char *) ":", 1);
2182 if (!isxdigit (*cp))
2184 mask = hex2nib[*cp & 0x7f];
2190 if (ferror (ce->ce_fp)) {
2191 content_error (ce->ce_file, ct, "error writing to");
2200 if (*cp < '!' || *cp > '~') {
2202 dp = "expecting character in range [!..~]";
2205 i = strlen (invo_name) + 2;
2206 content_error (NULL, ct,
2207 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2215 putc (*cp, ce->ce_fp);
2218 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2220 MD5Update (&mdContext, (unsigned char *) cp, 1);
2222 if (ferror (ce->ce_fp)) {
2223 content_error (ce->ce_file, ct, "error writing to");
2229 if (*++cp != '\n') {
2238 content_error (NULL, ct,
2239 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2243 fseek (ct->c_fp, 0L, SEEK_SET);
2245 if (fflush (ce->ce_fp)) {
2246 content_error (ce->ce_file, ct, "error writing to");
2251 unsigned char digest[16];
2253 MD5Final (digest, &mdContext);
2254 if (memcmp((char *) digest, (char *) ct->c_digest,
2255 sizeof(digest) / sizeof(digest[0])))
2256 content_error (NULL, ct,
2257 "content integrity suspect (digest mismatch) -- continuing");
2260 fprintf (stderr, "content integrity confirmed\n");
2263 fseek (ce->ce_fp, 0L, SEEK_SET);
2266 *file = ce->ce_file;
2267 return fileno (ce->ce_fp);
2270 free_encoding (ct, 0);
2282 if (init_encoding (ct, open7Bit) == NOTOK)
2285 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2291 open7Bit (CT ct, char **file)
2294 char buffer[BUFSIZ];
2299 fseek (ce->ce_fp, 0L, SEEK_SET);
2304 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2305 content_error (ce->ce_file, ct, "unable to fopen for reading");
2311 if (*file == NULL) {
2312 ce->ce_file = add (m_scratch ("", tmp), NULL);
2315 ce->ce_file = add (*file, NULL);
2319 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2320 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2324 if (ct->c_type == CT_MULTIPART) {
2326 CI ci = &ct->c_ctinfo;
2329 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2330 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2331 + 1 + strlen (ci->ci_subtype);
2332 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2333 putc (';', ce->ce_fp);
2336 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2338 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2339 fputs ("\n\t", ce->ce_fp);
2342 putc (' ', ce->ce_fp);
2345 fprintf (ce->ce_fp, "%s", buffer);
2349 if (ci->ci_comment) {
2350 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2351 fputs ("\n\t", ce->ce_fp);
2355 putc (' ', ce->ce_fp);
2358 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2361 fprintf (ce->ce_fp, "\n");
2363 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2365 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2367 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2368 fprintf (ce->ce_fp, "\n");
2371 if ((len = ct->c_end - ct->c_begin) < 0)
2372 adios (NULL, "internal error(3)");
2374 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2375 content_error (ct->c_file, ct, "unable to open for reading");
2379 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2381 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2383 content_error (ct->c_file, ct, "error reading from");
2387 content_error (NULL, ct, "premature eof");
2395 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2396 if (ferror (ce->ce_fp)) {
2397 content_error (ce->ce_file, ct, "error writing to");
2402 fseek (ct->c_fp, 0L, SEEK_SET);
2404 if (fflush (ce->ce_fp)) {
2405 content_error (ce->ce_file, ct, "error writing to");
2409 fseek (ce->ce_fp, 0L, SEEK_SET);
2412 *file = ce->ce_file;
2413 return fileno (ce->ce_fp);
2416 free_encoding (ct, 0);
2426 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2428 char cachefile[BUFSIZ];
2431 fseek (ce->ce_fp, 0L, SEEK_SET);
2436 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2437 content_error (ce->ce_file, ct, "unable to fopen for reading");
2443 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2444 cachefile, sizeof(cachefile)) != NOTOK) {
2445 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2446 ce->ce_file = getcpy (cachefile);
2450 admonish (cachefile, "unable to fopen for reading");
2457 *file = ce->ce_file;
2458 *fd = fileno (ce->ce_fp);
2469 return init_encoding (ct, openFile);
2474 openFile (CT ct, char **file)
2477 char cachefile[BUFSIZ];
2478 struct exbody *e = ct->c_ctexbody;
2479 CE ce = ct->c_cefile;
2481 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2493 content_error (NULL, ct, "missing name parameter");
2497 ce->ce_file = getcpy (e->eb_name);
2500 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2501 content_error (ce->ce_file, ct, "unable to fopen for reading");
2505 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2506 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2507 cachefile, sizeof(cachefile)) != NOTOK) {
2511 mask = umask (cachetype ? ~m_gmprot () : 0222);
2512 if ((fp = fopen (cachefile, "w"))) {
2514 char buffer[BUFSIZ];
2515 FILE *gp = ce->ce_fp;
2517 fseek (gp, 0L, SEEK_SET);
2519 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2521 fwrite (buffer, sizeof(*buffer), cc, fp);
2525 admonish (ce->ce_file, "error reading");
2530 admonish (cachefile, "error writing");
2538 fseek (ce->ce_fp, 0L, SEEK_SET);
2539 *file = ce->ce_file;
2540 return fileno (ce->ce_fp);
2550 return init_encoding (ct, openFTP);
2555 openFTP (CT ct, char **file)
2557 int cachetype, caching, fd;
2559 char *bp, *ftp, *user, *pass;
2560 char buffer[BUFSIZ], cachefile[BUFSIZ];
2563 static char *username = NULL;
2564 static char *password = NULL;
2569 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2577 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2588 if (!e->eb_name || !e->eb_site) {
2589 content_error (NULL, ct, "missing %s parameter",
2590 e->eb_name ? "site": "name");
2597 pidcheck (pidwait (xpid, NOTOK));
2601 /* Get the buffer ready to go */
2603 buflen = sizeof(buffer);
2606 * Construct the query message for user
2608 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2614 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2620 snprintf (bp, buflen, "\n using %sFTP from site %s",
2621 e->eb_flags ? "anonymous " : "", e->eb_site);
2626 if (e->eb_size > 0) {
2627 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2632 snprintf (bp, buflen, "? ");
2635 * Now, check the answer
2637 if (!getanswer (buffer))
2642 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2645 ruserpass (e->eb_site, &username, &password);
2650 ce->ce_unlink = (*file == NULL);
2652 cachefile[0] = '\0';
2653 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2654 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2655 cachefile, sizeof(cachefile)) != NOTOK) {
2656 if (*file == NULL) {
2663 ce->ce_file = add (*file, NULL);
2665 ce->ce_file = add (cachefile, NULL);
2667 ce->ce_file = add (m_scratch ("", tmp), NULL);
2669 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2670 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2678 int child_id, i, vecp;
2682 vec[vecp++] = r1bindex (ftp, '/');
2683 vec[vecp++] = e->eb_site;
2686 vec[vecp++] = e->eb_dir;
2687 vec[vecp++] = e->eb_name;
2688 vec[vecp++] = ce->ce_file,
2689 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2690 ? "ascii" : "binary";
2695 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2699 adios ("fork", "unable to");
2703 close (fileno (ce->ce_fp));
2705 fprintf (stderr, "unable to exec ");
2711 if (pidXwait (child_id, NULL)) {
2715 username = password = NULL;
2724 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2726 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2733 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2738 mask = umask (cachetype ? ~m_gmprot () : 0222);
2739 if ((fp = fopen (cachefile, "w"))) {
2741 FILE *gp = ce->ce_fp;
2743 fseek (gp, 0L, SEEK_SET);
2745 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2747 fwrite (buffer, sizeof(*buffer), cc, fp);
2751 admonish (ce->ce_file, "error reading");
2756 admonish (cachefile, "error writing");
2765 fseek (ce->ce_fp, 0L, SEEK_SET);
2766 *file = ce->ce_file;
2767 return fileno (ce->ce_fp);
2778 return init_encoding (ct, openMail);
2783 openMail (CT ct, char **file)
2785 int child_id, fd, i, vecp;
2787 char *bp, buffer[BUFSIZ], *vec[7];
2788 struct exbody *e = ct->c_ctexbody;
2789 CE ce = ct->c_cefile;
2791 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2802 if (!e->eb_server) {
2803 content_error (NULL, ct, "missing server parameter");
2810 pidcheck (pidwait (xpid, NOTOK));
2814 /* Get buffer ready to go */
2816 buflen = sizeof(buffer);
2818 /* Now construct query message */
2819 snprintf (bp, buflen, "Retrieve content");
2825 snprintf (bp, buflen, " %s", e->eb_partno);
2831 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2833 e->eb_subject ? e->eb_subject : e->eb_body);
2835 /* Now, check answer */
2836 if (!getanswer (buffer))
2840 vec[vecp++] = r1bindex (mailproc, '/');
2841 vec[vecp++] = e->eb_server;
2842 vec[vecp++] = "-subject";
2843 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2844 vec[vecp++] = "-body";
2845 vec[vecp++] = e->eb_body;
2848 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2852 advise ("fork", "unable to");
2856 execvp (mailproc, vec);
2857 fprintf (stderr, "unable to exec ");
2863 if (pidXwait (child_id, NULL) == OK)
2864 advise (NULL, "request sent");
2868 if (*file == NULL) {
2869 ce->ce_file = add (m_scratch ("", tmp), NULL);
2872 ce->ce_file = add (*file, NULL);
2876 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2877 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2881 fseek (ce->ce_fp, 0L, SEEK_SET);
2882 *file = ce->ce_file;
2883 return fileno (ce->ce_fp);
2888 fgetstr (char *s, int n, FILE *stream)
2892 for (ep = (cp = s) + n; cp < ep; ) {
2895 if (!fgets (cp, n, stream))
2896 return (cp != s ? s : NULL);
2897 if (cp == s && *cp != '#')
2900 cp += (i = strlen (cp)) - 1;
2901 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2912 * Parse the composition draft for text and directives.
2913 * Do initial setup of Content structure.
2917 user_content (FILE *in, char *file, char *buf, CT *ctp)
2921 char buffer[BUFSIZ];
2922 struct multipart *m;
2925 struct str2init *s2i;
2930 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2935 /* allocate basic Content structure */
2936 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2937 adios (NULL, "out of memory");
2940 /* allocate basic structure for handling decoded content */
2941 init_decoded_content (ct);
2948 * Handle inline text. Check if line
2949 * is one of the following forms:
2951 * 1) doesn't begin with '#' (implicit directive)
2952 * 2) begins with "##" (implicit directive)
2953 * 3) begins with "#<"
2955 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2959 char content[BUFSIZ];
2962 /* use a temp file to collect the plain text lines */
2963 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2966 if ((out = fopen (ce->ce_file, "w")) == NULL)
2967 adios (ce->ce_file, "unable to open for writing");
2969 if (buf[0] == '#' && buf[1] == '<') {
2970 strncpy (content, buf + 2, sizeof(content));
2977 /* the directive is implicit */
2978 strncpy (content, "text/plain", sizeof(content));
2980 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2984 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2985 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2989 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2990 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2991 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2992 switch (buffer[0]) {
2999 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
3007 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
3008 && buffer[i = strlen (DISPO_FIELD)] == ':') {
3012 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
3013 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
3014 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
3015 switch (buffer[0]) {
3022 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
3030 if (headers != 1 || buffer[0] != '\n')
3031 fputs (buffer, out);
3036 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
3038 if (buffer[0] == '#') {
3041 if (buffer[1] != '#')
3043 for (cp = (bp = buffer) + 1; *cp; cp++)
3050 ct->c_end = ftell (out);
3053 /* parse content type */
3054 if (get_ctinfo (content, ct, inlineD) == NOTOK)
3057 for (s2i = str2cts; s2i->si_key; s2i++)
3058 if (!strcasecmp (ci->ci_type, s2i->si_key))
3060 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
3064 * check type specified (possibly implicitly)
3066 switch (ct->c_type = s2i->si_val) {
3068 if (!strcasecmp (ci->ci_subtype, "rfc822")) {
3069 ct->c_encoding = CE_7BIT;
3074 adios (NULL, "it doesn't make sense to define an in-line %s content",
3075 ct->c_type == CT_MESSAGE ? "message" : "multipart");
3080 if ((ct->c_ctinitfnx = s2i->si_init))
3081 (*ct->c_ctinitfnx) (ct);
3086 fseek (in, pos, SEEK_SET);
3091 * If we've reached this point, the next line
3092 * must be some type of explicit directive.
3095 /* check if directive is external-type */
3096 extrnal = (buf[1] == '@');
3098 /* parse directive */
3099 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3102 /* check directive against the list of MIME types */
3103 for (s2i = str2cts; s2i->si_key; s2i++)
3104 if (!strcasecmp (ci->ci_type, s2i->si_key))
3108 * Check if the directive specified a valid type.
3109 * This will happen if it was one of the following forms:
3111 * #type/subtype (or)
3115 if (!ci->ci_subtype)
3116 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3118 switch (ct->c_type = s2i->si_val) {
3120 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3121 ci->ci_type, ci->ci_subtype);
3125 if (!strcasecmp (ci->ci_subtype, "partial"))
3126 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3127 ci->ci_type, ci->ci_subtype);
3128 if (!strcasecmp (ci->ci_subtype, "external-body"))
3129 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3130 ci->ci_type, ci->ci_subtype);
3133 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3134 ci->ci_type, ci->ci_subtype);
3138 if ((ct->c_ctinitfnx = s2i->si_init))
3139 (*ct->c_ctinitfnx) (ct);
3144 * #@type/subtype (external types directive)
3151 adios (NULL, "need external information for \"#@%s/%s\"",
3152 ci->ci_type, ci->ci_subtype);
3155 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3156 free (ci->ci_magic);
3157 ci->ci_magic = NULL;
3160 * Since we are using the current Content structure to
3161 * hold information about the type of the external
3162 * reference, we need to create another Content structure
3163 * for the message/external-body to wrap it in.
3165 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3166 adios (NULL, "out of memory");
3169 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3171 ct->c_type = CT_MESSAGE;
3172 ct->c_subtype = MESSAGE_EXTERNAL;
3174 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3175 adios (NULL, "out of memory");
3176 ct->c_ctparams = (void *) e;
3182 if (params_external (ct, 1) == NOTOK)
3188 /* Handle [file] argument */
3190 /* check if specifies command to execute */
3191 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3192 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3195 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3196 cp = add (cp, NULL);
3197 free (ci->ci_magic);
3200 /* record filename of decoded contents */
3201 ce->ce_file = ci->ci_magic;
3202 if (access (ce->ce_file, R_OK) == NOTOK)
3203 adios ("reading", "unable to access %s for", ce->ce_file);
3204 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3205 ct->c_end = (long) st.st_size;
3206 ci->ci_magic = NULL;
3212 * No [file] argument, so check profile for
3213 * method to compose content.
3215 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3216 invo_name, ci->ci_type, ci->ci_subtype);
3217 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3218 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3219 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3220 content_error (NULL, ct, "don't know how to compose content");
3224 ci->ci_magic = add (cp, NULL);
3229 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3233 * #forw [+folder] [msgs]
3235 if (!strcasecmp (ci->ci_type, "forw")) {
3237 char *folder, *arguments[MAXARGS];
3241 ap = brkstring (ci->ci_magic, " ", "\n");
3242 copyip (ap, arguments, MAXARGS);
3244 arguments[0] = "cur";
3245 arguments[1] = NULL;
3249 /* search the arguments for a folder name */
3250 for (ap = arguments; *ap; ap++) {
3252 if (*cp == '+' || *cp == '@') {
3254 adios (NULL, "only one folder per #forw directive");
3256 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3260 /* else, use the current folder */
3262 folder = add (getfolder (1), NULL);
3264 if (!(mp = folder_read (folder)))
3265 adios (NULL, "unable to read folder %s", folder);
3266 for (ap = arguments; *ap; ap++) {
3268 if (*cp != '+' && *cp != '@')
3269 if (!m_convert (mp, cp))
3276 * If there is more than one message to include, make this
3277 * a content of type "multipart/digest" and insert each message
3278 * as a subpart. If there is only one message, then make this
3279 * a content of type "message/rfc822".
3281 if (mp->numsel > 1) {
3282 /* we are forwarding multiple messages */
3283 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3285 ct->c_type = CT_MULTIPART;
3286 ct->c_subtype = MULTI_DIGEST;
3288 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3289 adios (NULL, "out of memory");
3290 ct->c_ctparams = (void *) m;
3293 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3294 if (is_selected(mp, msgnum)) {
3299 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3300 adios (NULL, "out of memory");
3301 init_decoded_content (p);
3303 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3305 p->c_type = CT_MESSAGE;
3306 p->c_subtype = MESSAGE_RFC822;
3308 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3309 pe->ce_file = add (buffer, NULL);
3310 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3311 p->c_end = (long) st.st_size;
3313 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3314 adios (NULL, "out of memory");
3316 pp = &part->mp_next;
3321 /* we are forwarding one message */
3322 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3324 ct->c_type = CT_MESSAGE;
3325 ct->c_subtype = MESSAGE_RFC822;
3327 msgnum = mp->lowsel;
3328 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3329 ce->ce_file = add (buffer, NULL);
3330 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3331 ct->c_end = (long) st.st_size;
3334 folder_free (mp); /* free folder/message structure */
3341 if (!strcasecmp (ci->ci_type, "end")) {
3348 * #begin [ alternative | parallel ]
3350 if (!strcasecmp (ci->ci_type, "begin")) {
3351 if (!ci->ci_magic) {
3353 cp = SubMultiPart[vrsn - 1].kv_key;
3354 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3355 vrsn = MULTI_ALTERNATE;
3356 cp = SubMultiPart[vrsn - 1].kv_key;
3357 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3358 vrsn = MULTI_PARALLEL;
3359 cp = SubMultiPart[vrsn - 1].kv_key;
3360 } else if (uprf (ci->ci_magic, "digest")) {
3363 vrsn = MULTI_UNKNOWN;
3368 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3369 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3371 ct->c_type = CT_MULTIPART;
3372 ct->c_subtype = vrsn;
3374 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3375 adios (NULL, "out of memory");
3376 ct->c_ctparams = (void *) m;
3379 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3383 if (user_content (in, file, buffer, &p) == DONE) {
3385 adios (NULL, "empty \"#begin ... #end\" sequence");
3391 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3392 adios (NULL, "out of memory");
3394 pp = &part->mp_next;
3397 admonish (NULL, "premature end-of-file, missing #end");
3404 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3405 return NOTOK; /* NOT REACHED */
3410 set_id (CT ct, int top)
3414 static time_t clock = 0;
3415 static char *msgfmt;
3419 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3420 (int) getpid(), (long) clock, LocalName());
3422 msgfmt = getcpy(msgid);
3424 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3425 ct->c_id = getcpy (msgid);
3429 static char ebcdicsafe[0x100] = {
3430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3431 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3432 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3433 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3434 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3435 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3436 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3437 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3438 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3439 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3440 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3441 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3442 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3443 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3444 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3445 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3446 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3447 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3448 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3449 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3450 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3452 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3453 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3454 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3455 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3457 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3466 * Fill out, or expand the various contents in the composition
3467 * draft. Read-in any necessary files. Parse and execute any
3468 * commands specified by profile composition strings.
3472 compose_content (CT ct)
3474 CE ce = ct->c_cefile;
3476 switch (ct->c_type) {
3481 char partnam[BUFSIZ];
3482 struct multipart *m = (struct multipart *) ct->c_ctparams;
3486 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3487 pp = partnam + strlen (partnam);
3492 /* first, we call compose_content on all the subparts */
3493 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3494 CT p = part->mp_part;
3496 sprintf (pp, "%d", partnum);
3497 p->c_partno = add (partnam, NULL);
3498 if (compose_content (p) == NOTOK)
3503 * If the -rfc934mode switch is given, then check all
3504 * the subparts of a multipart/digest. If they are all
3505 * message/rfc822, then mark this content and all
3506 * subparts with the rfc934 compatibility mode flag.
3508 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3511 for (part = m->mp_parts; part; part = part->mp_next) {
3512 CT p = part->mp_part;
3514 if (p->c_subtype != MESSAGE_RFC822) {
3519 ct->c_rfc934 = is934;
3520 for (part = m->mp_parts; part; part = part->mp_next) {
3521 CT p = part->mp_part;
3523 if ((p->c_rfc934 = is934))
3529 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3533 for (part = m->mp_parts; part; part = part->mp_next)
3534 ct->c_end += part->mp_part->c_end + partnum;
3540 /* Nothing to do for type message */
3544 * Discrete types (text/application/audio/image/video)
3549 int i, xstdout, len, buflen;
3550 char *bp, **ap, *cp;
3551 char *vec[4], buffer[BUFSIZ];
3553 CI ci = &ct->c_ctinfo;
3555 if (!(cp = ci->ci_magic))
3556 adios (NULL, "internal error(5)");
3558 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3563 /* Get buffer ready to go */
3566 buflen = sizeof(buffer);
3569 * Parse composition string into buffer
3571 for ( ; *cp; cp++) {
3576 /* insert parameters from directive */
3580 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3581 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3591 /* %f, and stdout is not-redirected */
3597 * insert temporary filename where
3598 * content should be written
3600 snprintf (bp, buflen, "%s", ce->ce_file);
3604 /* insert content subtype */
3605 strncpy (bp, ci->ci_subtype, buflen);
3609 /* insert character % */
3630 printf ("composing content %s/%s from command\n\t%s\n",
3631 ci->ci_type, ci->ci_subtype, buffer);
3633 fflush (stdout); /* not sure if need for -noverbose */
3640 if ((out = fopen (ce->ce_file, "w")) == NULL)
3641 adios (ce->ce_file, "unable to open for writing");
3643 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3647 adios ("fork", "unable to fork");
3652 dup2 (fileno (out), 1);
3653 close (fileno (out));
3654 execvp ("/bin/sh", vec);
3655 fprintf (stderr, "unable to exec ");
3662 if (pidXwait(child_id, NULL))
3668 /* Check size of file */
3669 if (listsw && ct->c_end == 0L) {
3672 if (stat (ce->ce_file, &st) != NOTOK)
3673 ct->c_end = (long) st.st_size;
3685 * 1) choose a transfer encoding.
3686 * 2) check for clashes with multipart boundary string.
3687 * 3) for text content, figure out which character set is being used.
3689 * If there is a clash with one of the contents and the multipart boundary,
3690 * this function will exit with NOTOK. This will cause the scanning process
3691 * to be repeated with a different multipart boundary. It is possible
3692 * (although highly unlikely) that this scan will be repeated multiple times.
3696 scan_content (CT ct)
3699 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3700 int checklinelen, linelen = 0; /* check for long lines */
3701 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3702 int checklinespace, linespace = 0; /* check if any line ends with space */
3703 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3704 char *cp, buffer[BUFSIZ];
3707 CE ce = ct->c_cefile;
3710 * handle multipart by scanning all subparts
3711 * and then checking their encoding.
3713 if (ct->c_type == CT_MULTIPART) {
3714 struct multipart *m = (struct multipart *) ct->c_ctparams;
3717 /* initially mark the domain of enclosing multipart as 7bit */
3718 ct->c_encoding = CE_7BIT;
3720 for (part = m->mp_parts; part; part = part->mp_next) {
3721 CT p = part->mp_part;
3723 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3726 /* if necessary, enlarge encoding for enclosing multipart */
3727 if (p->c_encoding == CE_BINARY)
3728 ct->c_encoding = CE_BINARY;
3729 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3730 ct->c_encoding = CE_8BIT;
3737 * Decide what to check while scanning this content.
3739 switch (ct->c_type) {
3743 if (ct->c_subtype == TEXT_PLAIN) {
3748 checkebcdic = ebcdicsw;
3754 case CT_APPLICATION:
3756 checkebcdic = ebcdicsw;
3768 /* don't check anything for message/external */
3769 if (ct->c_subtype == MESSAGE_EXTERNAL)
3779 * Don't check anything for these types,
3780 * since we are forcing use of base64.
3791 * Scan the unencoded content
3793 if (check8bit || checklinelen || checklinespace || checkboundary) {
3794 if ((in = fopen (ce->ce_file, "r")) == NULL)
3795 adios (ce->ce_file, "unable to open for reading");
3796 len = strlen (prefix);
3798 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3800 * Check for 8bit data.
3803 for (cp = buffer; *cp; cp++) {
3804 if (!isascii (*cp)) {
3806 check8bit = 0; /* no need to keep checking */
3809 * Check if character is ebcdic-safe. We only check
3810 * this if also checking for 8bit data.
3812 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3814 checkebcdic = 0; /* no need to keep checking */
3820 * Check line length.
3822 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3824 checklinelen = 0; /* no need to keep checking */
3828 * Check if line ends with a space.
3830 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3832 checklinespace = 0; /* no need to keep checking */
3836 * Check if content contains a line that clashes
3837 * with our standard boundary for multipart messages.
3839 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3840 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3844 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3846 checkboundary = 0; /* no need to keep checking */
3854 * Decide which transfer encoding to use.
3856 switch (ct->c_type) {
3859 * If the text content didn't specify a character
3860 * set, we need to figure out which one was used.
3862 t = (struct text *) ct->c_ctparams;
3863 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3864 CI ci = &ct->c_ctinfo;
3867 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3871 t->tx_charset = CHARSET_UNKNOWN;
3872 *ap = concat ("charset=", write_charset_8bit(), NULL);
3874 t->tx_charset = CHARSET_USASCII;
3875 *ap = add ("charset=us-ascii", NULL);
3878 cp = strchr(*ap++, '=');
3884 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3885 ct->c_encoding = CE_QUOTED;
3887 ct->c_encoding = CE_7BIT;
3890 case CT_APPLICATION:
3891 /* For application type, use base64, except when postscript */
3892 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3893 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3894 ? CE_QUOTED : CE_BASE64;
3896 ct->c_encoding = CE_7BIT;
3900 ct->c_encoding = CE_7BIT;
3906 /* For audio, image, and video contents, just use base64 */
3907 ct->c_encoding = CE_BASE64;
3911 return (boundaryclash ? NOTOK : OK);
3916 * Scan the content structures, and build header
3917 * fields that will need to be output into the
3922 build_headers (CT ct)
3924 int cc, mailbody, len;
3926 char *np, *vp, buffer[BUFSIZ];
3927 CI ci = &ct->c_ctinfo;
3930 * If message is type multipart, then add the multipart
3931 * boundary to the list of attribute/value pairs.
3933 if (ct->c_type == CT_MULTIPART) {
3935 static int level = 0; /* store nesting level */
3939 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3940 cp = strchr(*ap++ = add (buffer, NULL), '=');
3947 * Skip the output of Content-Type, parameters, content
3948 * description and disposition, and Content-ID if the
3949 * content is of type "message" and the rfc934 compatibility
3950 * flag is set (which means we are inside multipart/digest
3951 * and the switch -rfc934mode was given).
3953 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3957 * output the content type and subtype
3959 np = add (TYPE_FIELD, NULL);
3960 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3962 /* keep track of length of line */
3963 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3964 + strlen (ci->ci_subtype) + 3;
3966 mailbody = ct->c_type == CT_MESSAGE
3967 && ct->c_subtype == MESSAGE_EXTERNAL
3968 && ((struct exbody *) ct->c_ctparams)->eb_body;
3971 * Append the attribute/value pairs to
3972 * the end of the Content-Type line.
3974 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3975 if (mailbody && !strcasecmp (*ap, "body"))
3981 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3982 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3983 vp = add ("\n\t", vp);
3989 vp = add (buffer, vp);
3994 * Append any RFC-822 comment to the end of
3995 * the Content-Type line.
3997 if (ci->ci_comment) {
3998 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3999 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
4000 vp = add ("\n\t", vp);
4006 vp = add (buffer, vp);
4009 vp = add ("\n", vp);
4010 add_header (ct, np, vp);
4013 * output the Content-ID, unless disabled by -nocontentid
4015 if (contentidsw && ct->c_id) {
4016 np = add (ID_FIELD, NULL);
4017 vp = concat (" ", ct->c_id, NULL);
4018 add_header (ct, np, vp);
4022 * output the Content-Description
4025 np = add (DESCR_FIELD, NULL);
4026 vp = concat (" ", ct->c_descr, NULL);
4027 add_header (ct, np, vp);
4031 * output the Content-Disposition
4034 np = add (DISPO_FIELD, NULL);
4035 vp = concat (" ", ct->c_dispo, NULL);
4036 add_header (ct, np, vp);
4041 * If this is the internal content structure for a
4042 * "message/external", then we are done with the
4043 * headers (since it has no body).
4049 * output the Content-MD5
4052 np = add (MD5_FIELD, NULL);
4053 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
4054 add_header (ct, np, vp);
4058 * output the Content-Transfer-Encoding
4060 switch (ct->c_encoding) {
4062 /* Nothing to output */
4064 np = add (ENCODING_FIELD, NULL);
4065 vp = concat (" ", "7bit", "\n", NULL);
4066 add_header (ct, np, vp);
4071 if (ct->c_type == CT_MESSAGE)
4072 adios (NULL, "internal error, invalid encoding");
4074 np = add (ENCODING_FIELD, NULL);
4075 vp = concat (" ", "8bit", "\n", NULL);
4076 add_header (ct, np, vp);
4080 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4081 adios (NULL, "internal error, invalid encoding");
4083 np = add (ENCODING_FIELD, NULL);
4084 vp = concat (" ", "quoted-printable", "\n", NULL);
4085 add_header (ct, np, vp);
4089 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4090 adios (NULL, "internal error, invalid encoding");
4092 np = add (ENCODING_FIELD, NULL);
4093 vp = concat (" ", "base64", "\n", NULL);
4094 add_header (ct, np, vp);
4098 if (ct->c_type == CT_MESSAGE)
4099 adios (NULL, "internal error, invalid encoding");
4101 np = add (ENCODING_FIELD, NULL);
4102 vp = concat (" ", "binary", "\n", NULL);
4103 add_header (ct, np, vp);
4107 adios (NULL, "unknown transfer encoding in content");
4112 * Additional content specific header processing
4114 switch (ct->c_type) {
4117 struct multipart *m;
4120 m = (struct multipart *) ct->c_ctparams;
4121 for (part = m->mp_parts; part; part = part->mp_next) {
4131 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4134 e = (struct exbody *) ct->c_ctparams;
4135 build_headers (e->eb_content);
4148 static char nib2b64[0x40+1] =
4149 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4152 calculate_digest (CT ct, int asciiP)
4155 char buffer[BUFSIZ], *vp, *op;
4157 unsigned char digest[16];
4158 unsigned char outbuf[25];
4161 CE ce = ct->c_cefile;
4164 if ((in = fopen (ce->ce_file, "r")) == NULL)
4165 adios (ce->ce_file, "unable to open for reading");
4167 /* Initialize md5 context */
4168 MD5Init (&mdContext);
4170 /* calculate md5 message digest */
4172 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4175 cp = buffer + strlen (buffer) - 1;
4176 if ((c = *cp) == '\n')
4179 MD5Update (&mdContext, (unsigned char *) buffer,
4180 (unsigned int) strlen (buffer));
4183 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4186 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4187 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4190 /* md5 finalization. Write digest and zero md5 context */
4191 MD5Final (digest, &mdContext);
4196 /* print debugging info */
4200 fprintf (stderr, "MD5 digest=");
4201 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4203 fprintf (stderr, "%02x", *dp & 0xff);
4204 fprintf (stderr, "\n");
4207 /* encode the digest using base64 */
4208 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4209 cc > 0; cc -= 3, op += 4) {
4213 bits = (*dp++ & 0xff) << 16;
4215 bits |= (*dp++ & 0xff) << 8;
4217 bits |= *dp++ & 0xff;
4220 for (bp = op + 4; bp > op; bits >>= 6)
4221 *--bp = nib2b64[bits & 0x3f];
4229 /* null terminate string */
4232 /* now make copy and return string */
4233 vp = concat (" ", outbuf, "\n", NULL);
4239 readDigest (CT ct, char *cp)
4244 unsigned char *dp, value, *ep;
4245 unsigned char *b, *b1, *b2, *b3;
4247 b = (unsigned char *) &bits,
4248 b1 = &b[endian > 0 ? 1 : 2],
4249 b2 = &b[endian > 0 ? 2 : 1],
4250 b3 = &b[endian > 0 ? 3 : 0];
4255 for (ep = (dp = ct->c_digest)
4256 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4261 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4263 fprintf (stderr, "invalid BASE64 encoding\n");
4267 bits |= value << bitno;
4269 if ((bitno -= 6) < 0) {
4270 if (dp + (3 - skip) > ep)
4271 goto invalid_digest;
4286 goto self_delimiting;
4291 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4301 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4309 fprintf (stderr, "MD5 digest=");
4310 for (dp = ct->c_digest; dp < ep; dp++)
4311 fprintf (stderr, "%02x", *dp & 0xff);
4312 fprintf (stderr, "\n");
4319 /* Make sure that buf contains at least one appearance of name,
4320 followed by =. If not, append both name and value. Note that name
4321 should not contain a trailing =. And quotes will be added around
4322 the value. Typical usage: make sure that a Content-Disposition
4323 header contains filename="foo". If it doesn't and value does, use
4326 incl_name_value (char *buf, char *name, char *value) {
4329 /* Assume that name is non-null. */
4331 char *name_plus_equal = concat (name, "=", NULL);
4333 if (! strstr (buf, name_plus_equal)) {
4337 /* Trim trailing space, esp. newline. */
4338 for (cp = &buf[strlen (buf) - 1]; cp >= buf && isspace (*cp); --cp) {
4342 appendage = concat ("; ", name, "=", "\"", value, "\"\n", NULL);
4343 newbuf = add (appendage, buf);
4347 free (name_plus_equal);
4354 /* Extract just name_suffix="foo", if any, from value. If there isn't
4355 one, return the entire value. Note that, for example, a name_suffix
4356 of name will match filename="foo", and return foo. */
4358 extract_name_value (char *name_suffix, char *value) {
4359 char *extracted_name_value = value;
4360 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
4361 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
4364 free (name_suffix_plus_quote);
4365 if (name_suffix_equals) {
4366 char *name_suffix_begin;
4368 /* Find first \". */
4369 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
4370 name_suffix_begin = ++cp;
4371 /* Find second \". */
4372 for (; *cp != '"'; ++cp) /* empty */;
4374 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
4375 memcpy (extracted_name_value, name_suffix_begin, cp - name_suffix_begin);
4376 extracted_name_value[cp - name_suffix_begin] = '\0';
4379 return extracted_name_value;