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 *);
191 * Structures for mapping (content) types to
192 * the functions to handle them.
200 static struct str2init str2cts[] = {
201 { "application", CT_APPLICATION, InitApplication },
202 { "audio", CT_AUDIO, InitGeneric },
203 { "image", CT_IMAGE, InitGeneric },
204 { "message", CT_MESSAGE, InitMessage },
205 { "multipart", CT_MULTIPART, InitMultiPart },
206 { "text", CT_TEXT, InitText },
207 { "video", CT_VIDEO, InitGeneric },
208 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
209 { NULL, CT_UNKNOWN, NULL },
212 static struct str2init str2ces[] = {
213 { "base64", CE_BASE64, InitBase64 },
214 { "quoted-printable", CE_QUOTED, InitQuoted },
215 { "8bit", CE_8BIT, Init7Bit },
216 { "7bit", CE_7BIT, Init7Bit },
217 { "binary", CE_BINARY, NULL },
218 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
219 { NULL, CE_UNKNOWN, NULL },
223 * NOTE WELL: si_key MUST NOT have value of NOTOK
225 * si_key is 1 if access method is anonymous.
227 static struct str2init str2methods[] = {
228 { "afs", 1, InitFile },
229 { "anon-ftp", 1, InitFTP },
230 { "ftp", 0, InitFTP },
231 { "local-file", 0, InitFile },
232 { "mail-server", 0, InitMail },
238 pidcheck (int status)
240 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
250 * Main routine for translating composition file
251 * into valid MIME message. It translates the draft
252 * into a content structure (actually a tree of content
253 * structures). This message then can be manipulated
254 * in various ways, including being output via
259 build_mime (char *infile)
262 char buf[BUFSIZ], name[NAMESZ];
269 umask (~m_gmprot ());
271 /* open the composition draft */
272 if ((in = fopen (infile, "r")) == NULL)
273 adios (infile, "unable to open for reading");
276 * Allocate space for primary (outside) content
278 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
279 adios (NULL, "out of memory");
282 * Allocate structure for handling decoded content
283 * for this part. We don't really need this, but
284 * allocate it to remain consistent.
286 init_decoded_content (ct);
289 * Parse some of the header fields in the composition
290 * draft into the linked list of header fields for
291 * the new MIME message.
293 for (compnum = 1, state = FLD;;) {
294 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
300 /* abort if draft has Mime-Version header field */
301 if (!strcasecmp (name, VRSN_FIELD))
302 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
304 /* abort if draft has Content-Transfer-Encoding header field */
305 if (!strcasecmp (name, ENCODING_FIELD))
306 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
308 /* ignore any Content-Type fields in the header */
309 if (!strcasecmp (name, TYPE_FIELD)) {
310 while (state == FLDPLUS)
311 state = m_getfld (state, name, buf, sizeof(buf), in);
315 /* get copies of the buffers */
316 np = add (name, NULL);
317 vp = add (buf, NULL);
319 /* if necessary, get rest of field */
320 while (state == FLDPLUS) {
321 state = m_getfld (state, name, buf, sizeof(buf), in);
322 vp = add (buf, vp); /* add to previous value */
325 /* Now add the header data to the list */
326 add_header (ct, np, vp);
329 /* if this wasn't the last header field, then continue */
335 adios (NULL, "draft has empty body -- no directives!");
340 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
345 adios (NULL, "message format error in component #%d", compnum);
348 adios (NULL, "getfld() returned %d", state);
354 * Now add the MIME-Version header field
355 * to the list of header fields.
357 np = add (VRSN_FIELD, NULL);
358 vp = concat (" ", VRSN_VALUE, "\n", NULL);
359 add_header (ct, np, vp);
362 * We initally assume we will find multiple contents in the
363 * draft. So create a multipart/mixed content to hold everything.
364 * We can remove this later, if it is not needed.
366 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
368 ct->c_type = CT_MULTIPART;
369 ct->c_subtype = MULTI_MIXED;
370 ct->c_file = add (infile, NULL);
372 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
373 adios (NULL, "out of memory");
374 ct->c_ctparams = (void *) m;
378 * read and parse the composition file
379 * and the directives it contains.
381 while (fgetstr (buf, sizeof(buf) - 1, in)) {
385 if (user_content (in, infile, buf, &p) == DONE) {
386 admonish (NULL, "ignoring spurious #end");
392 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
393 adios (NULL, "out of memory");
400 * close the composition draft since
401 * it's not needed any longer.
405 /* check if any contents were found */
407 adios (NULL, "no content directives found");
410 * If only one content was found, then remove and
411 * free the outer multipart content.
413 if (!m->mp_parts->mp_next) {
416 p = m->mp_parts->mp_part;
417 m->mp_parts->mp_part = NULL;
419 /* move header fields */
420 p->c_first_hf = ct->c_first_hf;
421 p->c_last_hf = ct->c_last_hf;
422 ct->c_first_hf = NULL;
423 ct->c_last_hf = NULL;
432 * Fill out, or expand directives. Parse and execute
433 * commands specified by profile composition strings.
435 compose_content (ct);
437 if ((cp = strchr(prefix, 'a')) == NULL)
438 adios (NULL, "internal error(4)");
441 * Scan the contents. Choose a transfer encoding, and
442 * check if prefix for multipart boundary clashes with
443 * any of the contents.
445 while (scan_content (ct) == NOTOK) {
450 adios (NULL, "giving up trying to find a unique delimiter string");
456 /* Build the rest of the header field structures */
464 * Main routine for reading/parsing the headers
465 * of a message content.
467 * toplevel = 1 # we are at the top level of the message
468 * toplevel = 0 # we are inside message type or multipart type
469 * # other than multipart/digest
470 * toplevel = -1 # we are inside multipart/digest
474 get_content (FILE *in, char *file, int toplevel)
477 char buf[BUFSIZ], name[NAMESZ];
480 if (!(ct = (CT) calloc (1, sizeof(*ct))))
481 adios (NULL, "out of memory");
484 ct->c_file = add (file, NULL);
485 ct->c_begin = ftell (ct->c_fp) + 1;
488 * Read the content headers
490 for (compnum = 1, state = FLD;;) {
491 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
497 /* Get MIME-Version field */
498 if (!strcasecmp (name, VRSN_FIELD)) {
502 cp = add (buf, NULL);
503 while (state == FLDPLUS) {
504 state = m_getfld (state, name, buf, sizeof(buf), in);
509 advise (NULL, "message %s has multiple %s: fields (%s)",
510 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
517 while (isspace (*cp))
519 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
521 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
526 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
528 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
531 for (dp = cp; istoken (*dp); dp++)
534 ucmp = !strcasecmp (cp, VRSN_VALUE);
538 "message %s has unknown value for %s: field (%s)",
539 ct->c_file, VRSN_FIELD, cp);
543 /* Get Content-Type field */
544 if (!strcasecmp (name, TYPE_FIELD)) {
546 struct str2init *s2i;
547 CI ci = &ct->c_ctinfo;
549 cp = add (buf, NULL);
550 while (state == FLDPLUS) {
551 state = m_getfld (state, name, buf, sizeof(buf), in);
555 /* Check if we've already seen a Content-Type header */
557 char *dp = trimcpy (cp);
559 advise (NULL, "message %s has multiple %s: fields (%s)",
560 ct->c_file, TYPE_FIELD, dp);
566 /* Parse the Content-Type field */
567 if (get_ctinfo (cp, ct, 0) == NOTOK)
571 * Set the Init function and the internal
572 * flag for this content type.
574 for (s2i = str2cts; s2i->si_key; s2i++)
575 if (!strcasecmp (ci->ci_type, s2i->si_key))
577 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
579 ct->c_type = s2i->si_val;
580 ct->c_ctinitfnx = s2i->si_init;
584 /* Get Content-Transfer-Encoding field */
585 if (!strcasecmp (name, ENCODING_FIELD)) {
588 struct str2init *s2i;
590 cp = add (buf, NULL);
591 while (state == FLDPLUS) {
592 state = m_getfld (state, name, buf, sizeof(buf), in);
597 * Check if we've already seen the
598 * Content-Transfer-Encoding field
601 advise (NULL, "message %s has multiple %s: fields (%s)",
602 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
608 ct->c_celine = cp; /* Save copy of this field */
609 while (isspace (*cp))
611 for (dp = cp; istoken (*dp); dp++)
617 * Find the internal flag and Init function
618 * for this transfer encoding.
620 for (s2i = str2ces; s2i->si_key; s2i++)
621 if (!strcasecmp (cp, s2i->si_key))
623 if (!s2i->si_key && !uprf (cp, "X-"))
626 ct->c_encoding = s2i->si_val;
628 /* Call the Init function for this encoding */
629 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
634 /* Get Content-ID field */
635 if (!strcasecmp (name, ID_FIELD)) {
636 ct->c_id = add (buf, ct->c_id);
637 while (state == FLDPLUS) {
638 state = m_getfld (state, name, buf, sizeof(buf), in);
639 ct->c_id = add (buf, ct->c_id);
644 /* Get Content-Description field */
645 if (!strcasecmp (name, DESCR_FIELD)) {
646 ct->c_descr = add (buf, ct->c_descr);
647 while (state == FLDPLUS) {
648 state = m_getfld (state, name, buf, sizeof(buf), in);
649 ct->c_descr = add (buf, ct->c_descr);
654 /* Get Content-MD5 field */
655 if (!strcasecmp (name, MD5_FIELD)) {
658 cp = add (buf, NULL);
659 while (state == FLDPLUS) {
660 state = m_getfld (state, name, buf, sizeof(buf), in);
669 if (ct->c_digested) {
670 advise (NULL, "message %s has multiple %s: fields (%s)",
671 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
678 while (isspace (*cp))
680 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
682 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
687 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
689 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
694 for (dp = cp; *dp && !isspace (*dp); dp++)
705 if (uprf (name, XXX_FIELD_PRF))
706 advise (NULL, "unknown field (%s) in message %s",
711 while (state == FLDPLUS)
712 state = m_getfld (state, name, buf, sizeof(buf), in);
715 if (state != FLDEOF) {
716 ct->c_begin = ftell (in) + 1;
723 ct->c_begin = ftell (in) - strlen (buf);
727 ct->c_begin = ftell (in);
732 adios (NULL, "message format error in component #%d", compnum);
735 adios (NULL, "getfld() returned %d", state);
741 * Check if we saw a Content-Type field.
742 * If not, then assign a default value for
743 * it, and the Init function.
747 * If we are inside a multipart/digest message,
748 * so default type is message/rfc822
751 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
753 ct->c_type = CT_MESSAGE;
754 ct->c_ctinitfnx = InitMessage;
757 * Else default type is text/plain
759 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
761 ct->c_type = CT_TEXT;
762 ct->c_ctinitfnx = InitText;
766 /* Use default Transfer-Encoding, if necessary */
768 ct->c_encoding = CE_7BIT;
781 * small routine to add header field to list
785 add_header (CT ct, char *name, char *value)
789 /* allocate header field structure */
790 hp = mh_xmalloc (sizeof(*hp));
792 /* link data into header structure */
797 /* link header structure into the list */
798 if (ct->c_first_hf == NULL) {
799 ct->c_first_hf = hp; /* this is the first */
802 ct->c_last_hf->next = hp; /* add it to the end */
811 * Used to parse both:
812 * 1) Content-Type line
813 * 2) composition directives
815 * and fills in the information of the CTinfo structure.
819 get_ctinfo (char *cp, CT ct, int magic)
822 char *dp, **ap, **ep;
827 i = strlen (invo_name) + 2;
829 /* store copy of Content-Type line */
830 cp = ct->c_ctline = add (cp, NULL);
832 while (isspace (*cp)) /* trim leading spaces */
835 /* change newlines to spaces */
836 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
839 /* trim trailing spaces */
840 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
846 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
848 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
851 for (dp = cp; istoken (*dp); dp++)
854 ci->ci_type = add (cp, NULL); /* store content type */
858 advise (NULL, "invalid %s: field in message %s (empty type)",
859 TYPE_FIELD, ct->c_file);
863 /* down case the content type string */
864 for (dp = ci->ci_type; *dp; dp++)
865 if (isalpha(*dp) && isupper (*dp))
868 while (isspace (*cp))
871 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
876 ci->ci_subtype = add ("", NULL);
881 while (isspace (*cp))
884 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
887 for (dp = cp; istoken (*dp); dp++)
890 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
893 if (!*ci->ci_subtype) {
895 "invalid %s: field in message %s (empty subtype for \"%s\")",
896 TYPE_FIELD, ct->c_file, ci->ci_type);
900 /* down case the content subtype string */
901 for (dp = ci->ci_subtype; *dp; dp++)
902 if (isalpha(*dp) && isupper (*dp))
906 while (isspace (*cp))
909 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
913 * Parse attribute/value pairs given with Content-Type
915 ep = (ap = ci->ci_attrs) + NPARMS;
921 "too many parameters in message %s's %s: field (%d max)",
922 ct->c_file, TYPE_FIELD, NPARMS);
927 while (isspace (*cp))
930 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
935 "extraneous trailing ';' in message %s's %s: parameter list",
936 ct->c_file, TYPE_FIELD);
940 /* down case the attribute name */
941 for (dp = cp; istoken (*dp); dp++)
942 if (isalpha(*dp) && isupper (*dp))
945 for (up = dp; isspace (*dp); )
947 if (dp == cp || *dp != '=') {
949 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
950 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
954 vp = (*ap = add (cp, NULL)) + (up - cp);
956 for (dp++; isspace (*dp); )
959 /* now add the attribute value */
960 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
963 for (cp = ++dp, dp = vp;;) {
968 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
969 ct->c_file, TYPE_FIELD, i, i, "", *ap);
974 if ((c = *cp++) == '\0')
989 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
995 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
996 ct->c_file, TYPE_FIELD, i, i, "", *ap);
1001 while (isspace (*cp))
1004 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1009 * Get any <Content-Id> given in buffer
1011 if (magic && *cp == '<') {
1016 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1017 advise (NULL, "invalid ID in message %s", ct->c_file);
1023 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1029 while (isspace (*cp))
1034 * Get any [Content-Description] given in buffer.
1036 if (magic && *cp == '[') {
1038 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1042 advise (NULL, "invalid description in message %s", ct->c_file);
1050 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1056 while (isspace (*cp))
1061 * Check if anything is left over
1065 ci->ci_magic = add (cp, NULL);
1068 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1069 ct->c_file, TYPE_FIELD, i, i, "", cp);
1077 get_comment (CT ct, char **ap, int istype)
1081 char c, buffer[BUFSIZ], *dp;
1090 switch (c = *cp++) {
1093 advise (NULL, "invalid comment in message %s's %s: field",
1094 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1099 if ((c = *cp++) == '\0')
1122 if ((dp = ci->ci_comment)) {
1123 ci->ci_comment = concat (dp, " ", buffer, NULL);
1126 ci->ci_comment = add (buffer, NULL);
1130 while (isspace (*cp))
1141 * Handles content types audio, image, and video.
1142 * There's not much to do right here.
1148 return OK; /* not much to do here */
1162 CI ci = &ct->c_ctinfo;
1164 /* check for missing subtype */
1165 if (!*ci->ci_subtype)
1166 ci->ci_subtype = add ("plain", ci->ci_subtype);
1169 for (kv = SubText; kv->kv_key; kv++)
1170 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1172 ct->c_subtype = kv->kv_value;
1174 /* allocate text character set structure */
1175 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1176 adios (NULL, "out of memory");
1177 ct->c_ctparams = (void *) t;
1179 /* initially mark character set as unspecified */
1180 t->tx_charset = CHARSET_UNSPECIFIED;
1182 /* scan for charset parameter */
1183 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1184 if (!strcasecmp (*ap, "charset"))
1187 /* check if content specified a character set */
1189 /* match character set or set to CHARSET_UNKNOWN */
1190 for (kv = Charset; kv->kv_key; kv++)
1191 if (!strcasecmp (*ep, kv->kv_key))
1193 t->tx_charset = kv->kv_value;
1205 InitMultiPart (CT ct)
1209 char *cp, *dp, **ap, **ep;
1210 char *bp, buffer[BUFSIZ];
1211 struct multipart *m;
1213 struct part *part, **next;
1214 CI ci = &ct->c_ctinfo;
1219 * The encoding for multipart messages must be either
1220 * 7bit, 8bit, or binary (per RFC2045).
1222 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1223 && ct->c_encoding != CE_BINARY) {
1225 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1226 ci->ci_type, ci->ci_subtype, ct->c_file);
1231 for (kv = SubMultiPart; kv->kv_key; kv++)
1232 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1234 ct->c_subtype = kv->kv_value;
1237 * Check for "boundary" parameter, which is
1238 * required for multipart messages.
1240 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1241 if (!strcasecmp (*ap, "boundary")) {
1247 /* complain if boundary parameter is missing */
1250 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1251 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1255 /* allocate primary structure for multipart info */
1256 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1257 adios (NULL, "out of memory");
1258 ct->c_ctparams = (void *) m;
1260 /* check if boundary parameter contains only whitespace characters */
1261 for (cp = bp; isspace (*cp); cp++)
1264 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1265 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1269 /* remove trailing whitespace from boundary parameter */
1270 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1275 /* record boundary separators */
1276 m->mp_start = concat (bp, "\n", NULL);
1277 m->mp_stop = concat (bp, "--\n", NULL);
1279 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1280 advise (ct->c_file, "unable to open for reading");
1284 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1286 next = &m->mp_parts;
1290 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1294 pos += strlen (buffer);
1295 if (buffer[0] != '-' || buffer[1] != '-')
1298 if (strcmp (buffer + 2, m->mp_start))
1301 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1302 adios (NULL, "out of memory");
1304 next = &part->mp_next;
1306 if (!(p = get_content (fp, ct->c_file,
1307 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1315 fseek (fp, pos, SEEK_SET);
1318 if (strcmp (buffer + 2, m->mp_start) == 0) {
1322 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1323 if (p->c_end < p->c_begin)
1324 p->c_begin = p->c_end;
1329 if (strcmp (buffer + 2, m->mp_stop) == 0)
1335 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1336 if (!inout && part) {
1338 p->c_end = ct->c_end;
1340 if (p->c_begin >= p->c_end) {
1341 for (next = &m->mp_parts; *next != part;
1342 next = &((*next)->mp_next))
1346 free ((char *) part);
1351 /* reverse the order of the parts for multipart/alternative */
1352 if (ct->c_subtype == MULTI_ALTERNATE)
1356 * label all subparts with part number, and
1357 * then initialize the content of the subpart.
1362 char partnam[BUFSIZ];
1365 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1366 pp = partnam + strlen (partnam);
1371 for (part = m->mp_parts, partnum = 1; part;
1372 part = part->mp_next, partnum++) {
1375 sprintf (pp, "%d", partnum);
1376 p->c_partno = add (partnam, NULL);
1378 /* initialize the content of the subparts */
1379 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1394 * reverse the order of the parts of a multipart
1398 reverse_parts (CT ct)
1401 struct multipart *m;
1402 struct part **base, **bmp, **next, *part;
1404 m = (struct multipart *) ct->c_ctparams;
1406 /* if only one part, just return */
1407 if (!m->mp_parts || !m->mp_parts->mp_next)
1410 /* count number of parts */
1412 for (part = m->mp_parts; part; part = part->mp_next)
1415 /* allocate array of pointers to the parts */
1416 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1417 adios (NULL, "out of memory");
1420 /* point at all the parts */
1421 for (part = m->mp_parts; part; part = part->mp_next)
1425 /* reverse the order of the parts */
1426 next = &m->mp_parts;
1427 for (bmp--; bmp >= base; bmp--) {
1430 next = &part->mp_next;
1434 /* free array of pointers */
1435 free ((char *) base);
1447 CI ci = &ct->c_ctinfo;
1449 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1451 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1452 ci->ci_type, ci->ci_subtype, ct->c_file);
1456 /* check for missing subtype */
1457 if (!*ci->ci_subtype)
1458 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1461 for (kv = SubMessage; kv->kv_key; kv++)
1462 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1464 ct->c_subtype = kv->kv_value;
1466 switch (ct->c_subtype) {
1467 case MESSAGE_RFC822:
1470 case MESSAGE_PARTIAL:
1475 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1476 adios (NULL, "out of memory");
1477 ct->c_ctparams = (void *) p;
1479 /* scan for parameters "id", "number", and "total" */
1480 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1481 if (!strcasecmp (*ap, "id")) {
1482 p->pm_partid = add (*ep, NULL);
1485 if (!strcasecmp (*ap, "number")) {
1486 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1487 || p->pm_partno < 1) {
1490 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1491 *ap, ci->ci_type, ci->ci_subtype,
1492 ct->c_file, TYPE_FIELD);
1497 if (!strcasecmp (*ap, "total")) {
1498 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1507 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1509 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1510 ci->ci_type, ci->ci_subtype,
1511 ct->c_file, TYPE_FIELD);
1517 case MESSAGE_EXTERNAL:
1524 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1525 adios (NULL, "out of memory");
1526 ct->c_ctparams = (void *) e;
1529 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1530 advise (ct->c_file, "unable to open for reading");
1534 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1536 if (!(p = get_content (fp, ct->c_file, 0))) {
1545 if ((exresult = params_external (ct, 0)) != NOTOK
1546 && p->c_ceopenfnx == openMail) {
1550 if ((size = ct->c_end - p->c_begin) <= 0) {
1552 content_error (NULL, ct,
1553 "empty body for access-type=mail-server");
1557 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1558 fseek (p->c_fp, p->c_begin, SEEK_SET);
1560 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1562 adios ("failed", "fread");
1565 adios (NULL, "unexpected EOF from fread");
1568 bp += cc, size -= cc;
1575 p->c_end = p->c_begin;
1580 if (exresult == NOTOK)
1582 if (e->eb_flags == NOTOK)
1585 switch (p->c_type) {
1590 if (p->c_subtype != MESSAGE_RFC822)
1594 e->eb_partno = ct->c_partno;
1596 (*p->c_ctinitfnx) (p);
1611 params_external (CT ct, int composing)
1614 struct exbody *e = (struct exbody *) ct->c_ctparams;
1615 CI ci = &ct->c_ctinfo;
1617 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1618 if (!strcasecmp (*ap, "access-type")) {
1619 struct str2init *s2i;
1620 CT p = e->eb_content;
1622 for (s2i = str2methods; s2i->si_key; s2i++)
1623 if (!strcasecmp (*ep, s2i->si_key))
1628 e->eb_flags = NOTOK;
1629 p->c_encoding = CE_EXTERNAL;
1632 e->eb_access = s2i->si_key;
1633 e->eb_flags = s2i->si_val;
1634 p->c_encoding = CE_EXTERNAL;
1636 /* Call the Init function for this external type */
1637 if ((*s2i->si_init)(p) == NOTOK)
1641 if (!strcasecmp (*ap, "name")) {
1645 if (!strcasecmp (*ap, "permission")) {
1646 e->eb_permission = *ep;
1649 if (!strcasecmp (*ap, "site")) {
1653 if (!strcasecmp (*ap, "directory")) {
1657 if (!strcasecmp (*ap, "mode")) {
1661 if (!strcasecmp (*ap, "size")) {
1662 sscanf (*ep, "%lu", &e->eb_size);
1665 if (!strcasecmp (*ap, "server")) {
1669 if (!strcasecmp (*ap, "subject")) {
1670 e->eb_subject = *ep;
1673 if (composing && !strcasecmp (*ap, "body")) {
1674 e->eb_body = getcpy (*ep);
1679 if (!e->eb_access) {
1681 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1682 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1695 InitApplication (CT ct)
1698 CI ci = &ct->c_ctinfo;
1701 for (kv = SubApplication; kv->kv_key; kv++)
1702 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1704 ct->c_subtype = kv->kv_value;
1711 * Set up structures for placing unencoded
1712 * content when building parts.
1716 init_decoded_content (CT ct)
1720 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1721 adios (NULL, "out of memory");
1724 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1725 ct->c_ceclosefnx = close_encoding;
1726 ct->c_cesizefnx = NULL; /* since unencoded */
1733 * TRANSFER ENCODINGS
1737 init_encoding (CT ct, OpenCEFunc openfnx)
1741 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1742 adios (NULL, "out of memory");
1745 ct->c_ceopenfnx = openfnx;
1746 ct->c_ceclosefnx = close_encoding;
1747 ct->c_cesizefnx = size_encoding;
1754 close_encoding (CT ct)
1758 if (!(ce = ct->c_cefile))
1768 static unsigned long
1769 size_encoding (CT ct)
1777 if (!(ce = ct->c_cefile))
1778 return (ct->c_end - ct->c_begin);
1780 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1781 return (long) st.st_size;
1784 if (stat (ce->ce_file, &st) != NOTOK)
1785 return (long) st.st_size;
1790 if (ct->c_encoding == CE_EXTERNAL)
1791 return (ct->c_end - ct->c_begin);
1794 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1795 return (ct->c_end - ct->c_begin);
1797 if (fstat (fd, &st) != NOTOK)
1798 size = (long) st.st_size;
1802 (*ct->c_ceclosefnx) (ct);
1811 static unsigned char b642nib[0x80] = {
1812 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1813 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1814 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1815 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1816 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1817 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1818 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1819 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1820 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1821 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1822 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1823 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1824 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1825 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1826 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1827 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1834 return init_encoding (ct, openBase64);
1839 openBase64 (CT ct, char **file)
1841 int bitno, cc, digested;
1844 unsigned char value, *b, *b1, *b2, *b3;
1845 char *cp, *ep, buffer[BUFSIZ];
1849 b = (unsigned char *) &bits;
1850 b1 = &b[endian > 0 ? 1 : 2];
1851 b2 = &b[endian > 0 ? 2 : 1];
1852 b3 = &b[endian > 0 ? 3 : 0];
1856 fseek (ce->ce_fp, 0L, SEEK_SET);
1861 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1862 content_error (ce->ce_file, ct, "unable to fopen for reading");
1868 if (*file == NULL) {
1869 ce->ce_file = add (m_scratch ("", tmp), NULL);
1872 ce->ce_file = add (*file, NULL);
1876 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1877 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1881 if ((len = ct->c_end - ct->c_begin) < 0)
1882 adios (NULL, "internal error(1)");
1884 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1885 content_error (ct->c_file, ct, "unable to open for reading");
1889 if ((digested = ct->c_digested))
1890 MD5Init (&mdContext);
1896 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1898 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1900 content_error (ct->c_file, ct, "error reading from");
1904 content_error (NULL, ct, "premature eof");
1912 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1917 if (skip || (*cp & 0x80)
1918 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1920 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1922 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1925 content_error (NULL, ct,
1926 "invalid BASE64 encoding -- continuing");
1930 bits |= value << bitno;
1932 if ((bitno -= 6) < 0) {
1933 putc ((char) *b1, ce->ce_fp);
1935 MD5Update (&mdContext, b1, 1);
1937 putc ((char) *b2, ce->ce_fp);
1939 MD5Update (&mdContext, b2, 1);
1941 putc ((char) *b3, ce->ce_fp);
1943 MD5Update (&mdContext, b3, 1);
1947 if (ferror (ce->ce_fp)) {
1948 content_error (ce->ce_file, ct,
1949 "error writing to");
1952 bitno = 18, bits = 0L, skip = 0;
1958 goto self_delimiting;
1967 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1969 content_error (NULL, ct, "invalid BASE64 encoding");
1974 fseek (ct->c_fp, 0L, SEEK_SET);
1976 if (fflush (ce->ce_fp)) {
1977 content_error (ce->ce_file, ct, "error writing to");
1982 unsigned char digest[16];
1984 MD5Final (digest, &mdContext);
1985 if (memcmp((char *) digest, (char *) ct->c_digest,
1986 sizeof(digest) / sizeof(digest[0])))
1987 content_error (NULL, ct,
1988 "content integrity suspect (digest mismatch) -- continuing");
1991 fprintf (stderr, "content integrity confirmed\n");
1994 fseek (ce->ce_fp, 0L, SEEK_SET);
1997 *file = ce->ce_file;
1998 return fileno (ce->ce_fp);
2001 free_encoding (ct, 0);
2010 static char hex2nib[0x80] = {
2011 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2012 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2013 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2014 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2015 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2016 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2017 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2018 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2019 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2020 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2021 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2022 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2023 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2024 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2025 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2026 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2033 return init_encoding (ct, openQuoted);
2038 openQuoted (CT ct, char **file)
2040 int cc, digested, len, quoted;
2042 char buffer[BUFSIZ];
2049 fseek (ce->ce_fp, 0L, SEEK_SET);
2054 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2055 content_error (ce->ce_file, ct, "unable to fopen for reading");
2061 if (*file == NULL) {
2062 ce->ce_file = add (m_scratch ("", tmp), NULL);
2065 ce->ce_file = add (*file, NULL);
2069 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2070 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2074 if ((len = ct->c_end - ct->c_begin) < 0)
2075 adios (NULL, "internal error(2)");
2077 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2078 content_error (ct->c_file, ct, "unable to open for reading");
2082 if ((digested = ct->c_digested))
2083 MD5Init (&mdContext);
2090 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2094 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2095 content_error (NULL, ct, "premature eof");
2099 if ((cc = strlen (buffer)) > len)
2103 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2108 for (; cp < ep; cp++) {
2111 if (!isxdigit (*cp)) {
2113 dp = "expecting hexidecimal-digit";
2114 goto invalid_encoding;
2117 mask |= hex2nib[*cp & 0x7f];
2118 putc (mask, ce->ce_fp);
2120 MD5Update (&mdContext, &mask, 1);
2124 putc (*cp, ce->ce_fp);
2126 MD5Update (&mdContext, (unsigned char *) ":", 1);
2130 if (!isxdigit (*cp))
2132 mask = hex2nib[*cp & 0x7f];
2138 if (ferror (ce->ce_fp)) {
2139 content_error (ce->ce_file, ct, "error writing to");
2148 if (*cp < '!' || *cp > '~') {
2150 dp = "expecting character in range [!..~]";
2153 i = strlen (invo_name) + 2;
2154 content_error (NULL, ct,
2155 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2163 putc (*cp, ce->ce_fp);
2166 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2168 MD5Update (&mdContext, (unsigned char *) cp, 1);
2170 if (ferror (ce->ce_fp)) {
2171 content_error (ce->ce_file, ct, "error writing to");
2177 if (*++cp != '\n') {
2186 content_error (NULL, ct,
2187 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2191 fseek (ct->c_fp, 0L, SEEK_SET);
2193 if (fflush (ce->ce_fp)) {
2194 content_error (ce->ce_file, ct, "error writing to");
2199 unsigned char digest[16];
2201 MD5Final (digest, &mdContext);
2202 if (memcmp((char *) digest, (char *) ct->c_digest,
2203 sizeof(digest) / sizeof(digest[0])))
2204 content_error (NULL, ct,
2205 "content integrity suspect (digest mismatch) -- continuing");
2208 fprintf (stderr, "content integrity confirmed\n");
2211 fseek (ce->ce_fp, 0L, SEEK_SET);
2214 *file = ce->ce_file;
2215 return fileno (ce->ce_fp);
2218 free_encoding (ct, 0);
2230 if (init_encoding (ct, open7Bit) == NOTOK)
2233 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2239 open7Bit (CT ct, char **file)
2242 char buffer[BUFSIZ];
2247 fseek (ce->ce_fp, 0L, SEEK_SET);
2252 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2253 content_error (ce->ce_file, ct, "unable to fopen for reading");
2259 if (*file == NULL) {
2260 ce->ce_file = add (m_scratch ("", tmp), NULL);
2263 ce->ce_file = add (*file, NULL);
2267 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2268 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2272 if (ct->c_type == CT_MULTIPART) {
2274 CI ci = &ct->c_ctinfo;
2277 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2278 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2279 + 1 + strlen (ci->ci_subtype);
2280 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2281 putc (';', ce->ce_fp);
2284 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2286 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2287 fputs ("\n\t", ce->ce_fp);
2290 putc (' ', ce->ce_fp);
2293 fprintf (ce->ce_fp, "%s", buffer);
2297 if (ci->ci_comment) {
2298 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2299 fputs ("\n\t", ce->ce_fp);
2303 putc (' ', ce->ce_fp);
2306 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2309 fprintf (ce->ce_fp, "\n");
2311 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2313 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2314 fprintf (ce->ce_fp, "\n");
2317 if ((len = ct->c_end - ct->c_begin) < 0)
2318 adios (NULL, "internal error(3)");
2320 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2321 content_error (ct->c_file, ct, "unable to open for reading");
2325 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2327 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2329 content_error (ct->c_file, ct, "error reading from");
2333 content_error (NULL, ct, "premature eof");
2341 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2342 if (ferror (ce->ce_fp)) {
2343 content_error (ce->ce_file, ct, "error writing to");
2348 fseek (ct->c_fp, 0L, SEEK_SET);
2350 if (fflush (ce->ce_fp)) {
2351 content_error (ce->ce_file, ct, "error writing to");
2355 fseek (ce->ce_fp, 0L, SEEK_SET);
2358 *file = ce->ce_file;
2359 return fileno (ce->ce_fp);
2362 free_encoding (ct, 0);
2372 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2374 char cachefile[BUFSIZ];
2377 fseek (ce->ce_fp, 0L, SEEK_SET);
2382 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2383 content_error (ce->ce_file, ct, "unable to fopen for reading");
2389 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2390 cachefile, sizeof(cachefile)) != NOTOK) {
2391 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2392 ce->ce_file = getcpy (cachefile);
2396 admonish (cachefile, "unable to fopen for reading");
2403 *file = ce->ce_file;
2404 *fd = fileno (ce->ce_fp);
2415 return init_encoding (ct, openFile);
2420 openFile (CT ct, char **file)
2423 char cachefile[BUFSIZ];
2424 struct exbody *e = ct->c_ctexbody;
2425 CE ce = ct->c_cefile;
2427 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2439 content_error (NULL, ct, "missing name parameter");
2443 ce->ce_file = getcpy (e->eb_name);
2446 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2447 content_error (ce->ce_file, ct, "unable to fopen for reading");
2451 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2452 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2453 cachefile, sizeof(cachefile)) != NOTOK) {
2457 mask = umask (cachetype ? ~m_gmprot () : 0222);
2458 if ((fp = fopen (cachefile, "w"))) {
2460 char buffer[BUFSIZ];
2461 FILE *gp = ce->ce_fp;
2463 fseek (gp, 0L, SEEK_SET);
2465 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2467 fwrite (buffer, sizeof(*buffer), cc, fp);
2471 admonish (ce->ce_file, "error reading");
2476 admonish (cachefile, "error writing");
2484 fseek (ce->ce_fp, 0L, SEEK_SET);
2485 *file = ce->ce_file;
2486 return fileno (ce->ce_fp);
2496 return init_encoding (ct, openFTP);
2501 openFTP (CT ct, char **file)
2503 int cachetype, caching, fd;
2505 char *bp, *ftp, *user, *pass;
2506 char buffer[BUFSIZ], cachefile[BUFSIZ];
2509 static char *username = NULL;
2510 static char *password = NULL;
2515 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2523 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2534 if (!e->eb_name || !e->eb_site) {
2535 content_error (NULL, ct, "missing %s parameter",
2536 e->eb_name ? "site": "name");
2543 pidcheck (pidwait (xpid, NOTOK));
2547 /* Get the buffer ready to go */
2549 buflen = sizeof(buffer);
2552 * Construct the query message for user
2554 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2560 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2566 snprintf (bp, buflen, "\n using %sFTP from site %s",
2567 e->eb_flags ? "anonymous " : "", e->eb_site);
2572 if (e->eb_size > 0) {
2573 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2578 snprintf (bp, buflen, "? ");
2581 * Now, check the answer
2583 if (!getanswer (buffer))
2588 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2591 ruserpass (e->eb_site, &username, &password);
2596 ce->ce_unlink = (*file == NULL);
2598 cachefile[0] = '\0';
2599 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2600 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2601 cachefile, sizeof(cachefile)) != NOTOK) {
2602 if (*file == NULL) {
2609 ce->ce_file = add (*file, NULL);
2611 ce->ce_file = add (cachefile, NULL);
2613 ce->ce_file = add (m_scratch ("", tmp), NULL);
2615 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2616 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2624 int child_id, i, vecp;
2628 vec[vecp++] = r1bindex (ftp, '/');
2629 vec[vecp++] = e->eb_site;
2632 vec[vecp++] = e->eb_dir;
2633 vec[vecp++] = e->eb_name;
2634 vec[vecp++] = ce->ce_file,
2635 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2636 ? "ascii" : "binary";
2641 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2645 adios ("fork", "unable to");
2649 close (fileno (ce->ce_fp));
2651 fprintf (stderr, "unable to exec ");
2657 if (pidXwait (child_id, NULL)) {
2661 username = password = NULL;
2670 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2672 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2679 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2684 mask = umask (cachetype ? ~m_gmprot () : 0222);
2685 if ((fp = fopen (cachefile, "w"))) {
2687 FILE *gp = ce->ce_fp;
2689 fseek (gp, 0L, SEEK_SET);
2691 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2693 fwrite (buffer, sizeof(*buffer), cc, fp);
2697 admonish (ce->ce_file, "error reading");
2702 admonish (cachefile, "error writing");
2711 fseek (ce->ce_fp, 0L, SEEK_SET);
2712 *file = ce->ce_file;
2713 return fileno (ce->ce_fp);
2724 return init_encoding (ct, openMail);
2729 openMail (CT ct, char **file)
2731 int child_id, fd, i, vecp;
2733 char *bp, buffer[BUFSIZ], *vec[7];
2734 struct exbody *e = ct->c_ctexbody;
2735 CE ce = ct->c_cefile;
2737 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2748 if (!e->eb_server) {
2749 content_error (NULL, ct, "missing server parameter");
2756 pidcheck (pidwait (xpid, NOTOK));
2760 /* Get buffer ready to go */
2762 buflen = sizeof(buffer);
2764 /* Now construct query message */
2765 snprintf (bp, buflen, "Retrieve content");
2771 snprintf (bp, buflen, " %s", e->eb_partno);
2777 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2779 e->eb_subject ? e->eb_subject : e->eb_body);
2781 /* Now, check answer */
2782 if (!getanswer (buffer))
2786 vec[vecp++] = r1bindex (mailproc, '/');
2787 vec[vecp++] = e->eb_server;
2788 vec[vecp++] = "-subject";
2789 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2790 vec[vecp++] = "-body";
2791 vec[vecp++] = e->eb_body;
2794 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2798 advise ("fork", "unable to");
2802 execvp (mailproc, vec);
2803 fprintf (stderr, "unable to exec ");
2809 if (pidXwait (child_id, NULL) == OK)
2810 advise (NULL, "request sent");
2814 if (*file == NULL) {
2815 ce->ce_file = add (m_scratch ("", tmp), NULL);
2818 ce->ce_file = add (*file, NULL);
2822 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2823 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2827 fseek (ce->ce_fp, 0L, SEEK_SET);
2828 *file = ce->ce_file;
2829 return fileno (ce->ce_fp);
2834 fgetstr (char *s, int n, FILE *stream)
2838 for (ep = (cp = s) + n; cp < ep; ) {
2841 if (!fgets (cp, n, stream))
2842 return (cp != s ? s : NULL);
2843 if (cp == s && *cp != '#')
2846 cp += (i = strlen (cp)) - 1;
2847 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2858 * Parse the composition draft for text and directives.
2859 * Do initial setup of Content structure.
2863 user_content (FILE *in, char *file, char *buf, CT *ctp)
2867 char buffer[BUFSIZ];
2868 struct multipart *m;
2871 struct str2init *s2i;
2876 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2881 /* allocate basic Content structure */
2882 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2883 adios (NULL, "out of memory");
2886 /* allocate basic structure for handling decoded content */
2887 init_decoded_content (ct);
2894 * Handle inline text. Check if line
2895 * is one of the following forms:
2897 * 1) doesn't begin with '#' (implicit directive)
2898 * 2) begins with "##" (implicit directive)
2899 * 3) begins with "#<"
2901 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2905 char content[BUFSIZ];
2908 /* use a temp file to collect the plain text lines */
2909 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2912 if ((out = fopen (ce->ce_file, "w")) == NULL)
2913 adios (ce->ce_file, "unable to open for writing");
2915 if (buf[0] == '#' && buf[1] == '<') {
2916 strncpy (content, buf + 2, sizeof(content));
2923 /* the directive is implicit */
2924 strncpy (content, "text/plain", sizeof(content));
2926 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2930 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2931 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2935 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2936 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2937 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2938 switch (buffer[0]) {
2945 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
2953 if (headers != 1 || buffer[0] != '\n')
2954 fputs (buffer, out);
2959 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
2961 if (buffer[0] == '#') {
2964 if (buffer[1] != '#')
2966 for (cp = (bp = buffer) + 1; *cp; cp++)
2973 ct->c_end = ftell (out);
2976 /* parse content type */
2977 if (get_ctinfo (content, ct, inlineD) == NOTOK)
2980 for (s2i = str2cts; s2i->si_key; s2i++)
2981 if (!strcasecmp (ci->ci_type, s2i->si_key))
2983 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
2987 * check type specified (possibly implicitly)
2989 switch (ct->c_type = s2i->si_val) {
2991 if (!strcasecmp (ci->ci_subtype, "rfc822")) {
2992 ct->c_encoding = CE_7BIT;
2997 adios (NULL, "it doesn't make sense to define an in-line %s content",
2998 ct->c_type == CT_MESSAGE ? "message" : "multipart");
3003 if ((ct->c_ctinitfnx = s2i->si_init))
3004 (*ct->c_ctinitfnx) (ct);
3009 fseek (in, pos, SEEK_SET);
3014 * If we've reached this point, the next line
3015 * must be some type of explicit directive.
3018 /* check if directive is external-type */
3019 extrnal = (buf[1] == '@');
3021 /* parse directive */
3022 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3025 /* check directive against the list of MIME types */
3026 for (s2i = str2cts; s2i->si_key; s2i++)
3027 if (!strcasecmp (ci->ci_type, s2i->si_key))
3031 * Check if the directive specified a valid type.
3032 * This will happen if it was one of the following forms:
3034 * #type/subtype (or)
3038 if (!ci->ci_subtype)
3039 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3041 switch (ct->c_type = s2i->si_val) {
3043 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3044 ci->ci_type, ci->ci_subtype);
3048 if (!strcasecmp (ci->ci_subtype, "partial"))
3049 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3050 ci->ci_type, ci->ci_subtype);
3051 if (!strcasecmp (ci->ci_subtype, "external-body"))
3052 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3053 ci->ci_type, ci->ci_subtype);
3056 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3057 ci->ci_type, ci->ci_subtype);
3061 if ((ct->c_ctinitfnx = s2i->si_init))
3062 (*ct->c_ctinitfnx) (ct);
3067 * #@type/subtype (external types directive)
3074 adios (NULL, "need external information for \"#@%s/%s\"",
3075 ci->ci_type, ci->ci_subtype);
3078 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3079 free (ci->ci_magic);
3080 ci->ci_magic = NULL;
3083 * Since we are using the current Content structure to
3084 * hold information about the type of the external
3085 * reference, we need to create another Content structure
3086 * for the message/external-body to wrap it in.
3088 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3089 adios (NULL, "out of memory");
3092 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3094 ct->c_type = CT_MESSAGE;
3095 ct->c_subtype = MESSAGE_EXTERNAL;
3097 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3098 adios (NULL, "out of memory");
3099 ct->c_ctparams = (void *) e;
3105 if (params_external (ct, 1) == NOTOK)
3111 /* Handle [file] argument */
3113 /* check if specifies command to execute */
3114 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3115 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3118 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3119 cp = add (cp, NULL);
3120 free (ci->ci_magic);
3123 /* record filename of decoded contents */
3124 ce->ce_file = ci->ci_magic;
3125 if (access (ce->ce_file, R_OK) == NOTOK)
3126 adios ("reading", "unable to access %s for", ce->ce_file);
3127 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3128 ct->c_end = (long) st.st_size;
3129 ci->ci_magic = NULL;
3135 * No [file] argument, so check profile for
3136 * method to compose content.
3138 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3139 invo_name, ci->ci_type, ci->ci_subtype);
3140 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3141 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3142 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3143 content_error (NULL, ct, "don't know how to compose content");
3147 ci->ci_magic = add (cp, NULL);
3152 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3156 * #forw [+folder] [msgs]
3158 if (!strcasecmp (ci->ci_type, "forw")) {
3160 char *folder, *arguments[MAXARGS];
3164 ap = brkstring (ci->ci_magic, " ", "\n");
3165 copyip (ap, arguments, MAXARGS);
3167 arguments[0] = "cur";
3168 arguments[1] = NULL;
3172 /* search the arguments for a folder name */
3173 for (ap = arguments; *ap; ap++) {
3175 if (*cp == '+' || *cp == '@') {
3177 adios (NULL, "only one folder per #forw directive");
3179 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3183 /* else, use the current folder */
3185 folder = add (getfolder (1), NULL);
3187 if (!(mp = folder_read (folder)))
3188 adios (NULL, "unable to read folder %s", folder);
3189 for (ap = arguments; *ap; ap++) {
3191 if (*cp != '+' && *cp != '@')
3192 if (!m_convert (mp, cp))
3199 * If there is more than one message to include, make this
3200 * a content of type "multipart/digest" and insert each message
3201 * as a subpart. If there is only one message, then make this
3202 * a content of type "message/rfc822".
3204 if (mp->numsel > 1) {
3205 /* we are forwarding multiple messages */
3206 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3208 ct->c_type = CT_MULTIPART;
3209 ct->c_subtype = MULTI_DIGEST;
3211 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3212 adios (NULL, "out of memory");
3213 ct->c_ctparams = (void *) m;
3216 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3217 if (is_selected(mp, msgnum)) {
3222 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3223 adios (NULL, "out of memory");
3224 init_decoded_content (p);
3226 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3228 p->c_type = CT_MESSAGE;
3229 p->c_subtype = MESSAGE_RFC822;
3231 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3232 pe->ce_file = add (buffer, NULL);
3233 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3234 p->c_end = (long) st.st_size;
3236 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3237 adios (NULL, "out of memory");
3239 pp = &part->mp_next;
3244 /* we are forwarding one message */
3245 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3247 ct->c_type = CT_MESSAGE;
3248 ct->c_subtype = MESSAGE_RFC822;
3250 msgnum = mp->lowsel;
3251 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3252 ce->ce_file = add (buffer, NULL);
3253 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3254 ct->c_end = (long) st.st_size;
3257 folder_free (mp); /* free folder/message structure */
3264 if (!strcasecmp (ci->ci_type, "end")) {
3271 * #begin [ alternative | parallel ]
3273 if (!strcasecmp (ci->ci_type, "begin")) {
3274 if (!ci->ci_magic) {
3276 cp = SubMultiPart[vrsn - 1].kv_key;
3277 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3278 vrsn = MULTI_ALTERNATE;
3279 cp = SubMultiPart[vrsn - 1].kv_key;
3280 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3281 vrsn = MULTI_PARALLEL;
3282 cp = SubMultiPart[vrsn - 1].kv_key;
3283 } else if (uprf (ci->ci_magic, "digest")) {
3286 vrsn = MULTI_UNKNOWN;
3291 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3292 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3294 ct->c_type = CT_MULTIPART;
3295 ct->c_subtype = vrsn;
3297 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3298 adios (NULL, "out of memory");
3299 ct->c_ctparams = (void *) m;
3302 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3306 if (user_content (in, file, buffer, &p) == DONE) {
3308 adios (NULL, "empty \"#begin ... #end\" sequence");
3314 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3315 adios (NULL, "out of memory");
3317 pp = &part->mp_next;
3320 admonish (NULL, "premature end-of-file, missing #end");
3327 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3328 return NOTOK; /* NOT REACHED */
3333 set_id (CT ct, int top)
3337 static time_t clock = 0;
3338 static char *msgfmt;
3342 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3343 (int) getpid(), (long) clock, LocalName());
3345 msgfmt = getcpy(msgid);
3347 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3348 ct->c_id = getcpy (msgid);
3352 static char ebcdicsafe[0x100] = {
3353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3354 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3356 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3357 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3358 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3359 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3360 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3361 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3362 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3363 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3364 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3365 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3366 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3367 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3368 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3371 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3379 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3380 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3389 * Fill out, or expand the various contents in the composition
3390 * draft. Read-in any necessary files. Parse and execute any
3391 * commands specified by profile composition strings.
3395 compose_content (CT ct)
3397 CE ce = ct->c_cefile;
3399 switch (ct->c_type) {
3404 char partnam[BUFSIZ];
3405 struct multipart *m = (struct multipart *) ct->c_ctparams;
3409 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3410 pp = partnam + strlen (partnam);
3415 /* first, we call compose_content on all the subparts */
3416 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3417 CT p = part->mp_part;
3419 sprintf (pp, "%d", partnum);
3420 p->c_partno = add (partnam, NULL);
3421 if (compose_content (p) == NOTOK)
3426 * If the -rfc934mode switch is given, then check all
3427 * the subparts of a multipart/digest. If they are all
3428 * message/rfc822, then mark this content and all
3429 * subparts with the rfc934 compatibility mode flag.
3431 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3434 for (part = m->mp_parts; part; part = part->mp_next) {
3435 CT p = part->mp_part;
3437 if (p->c_subtype != MESSAGE_RFC822) {
3442 ct->c_rfc934 = is934;
3443 for (part = m->mp_parts; part; part = part->mp_next) {
3444 CT p = part->mp_part;
3446 if ((p->c_rfc934 = is934))
3452 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3456 for (part = m->mp_parts; part; part = part->mp_next)
3457 ct->c_end += part->mp_part->c_end + partnum;
3463 /* Nothing to do for type message */
3467 * Discrete types (text/application/audio/image/video)
3472 int i, xstdout, len, buflen;
3473 char *bp, **ap, *cp;
3474 char *vec[4], buffer[BUFSIZ];
3476 CI ci = &ct->c_ctinfo;
3478 if (!(cp = ci->ci_magic))
3479 adios (NULL, "internal error(5)");
3481 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3486 /* Get buffer ready to go */
3489 buflen = sizeof(buffer);
3492 * Parse composition string into buffer
3494 for ( ; *cp; cp++) {
3499 /* insert parameters from directive */
3503 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3504 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3514 /* %f, and stdout is not-redirected */
3520 * insert temporary filename where
3521 * content should be written
3523 snprintf (bp, buflen, "%s", ce->ce_file);
3527 /* insert content subtype */
3528 strncpy (bp, ci->ci_subtype, buflen);
3532 /* insert character % */
3553 printf ("composing content %s/%s from command\n\t%s\n",
3554 ci->ci_type, ci->ci_subtype, buffer);
3556 fflush (stdout); /* not sure if need for -noverbose */
3563 if ((out = fopen (ce->ce_file, "w")) == NULL)
3564 adios (ce->ce_file, "unable to open for writing");
3566 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3570 adios ("fork", "unable to fork");
3575 dup2 (fileno (out), 1);
3576 close (fileno (out));
3577 execvp ("/bin/sh", vec);
3578 fprintf (stderr, "unable to exec ");
3585 if (pidXwait(child_id, NULL))
3591 /* Check size of file */
3592 if (listsw && ct->c_end == 0L) {
3595 if (stat (ce->ce_file, &st) != NOTOK)
3596 ct->c_end = (long) st.st_size;
3608 * 1) choose a transfer encoding.
3609 * 2) check for clashes with multipart boundary string.
3610 * 3) for text content, figure out which character set is being used.
3612 * If there is a clash with one of the contents and the multipart boundary,
3613 * this function will exit with NOTOK. This will cause the scanning process
3614 * to be repeated with a different multipart boundary. It is possible
3615 * (although highly unlikely) that this scan will be repeated multiple times.
3619 scan_content (CT ct)
3622 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3623 int checklinelen, linelen = 0; /* check for long lines */
3624 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3625 int checklinespace, linespace = 0; /* check if any line ends with space */
3626 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3627 char *cp, buffer[BUFSIZ];
3630 CE ce = ct->c_cefile;
3633 * handle multipart by scanning all subparts
3634 * and then checking their encoding.
3636 if (ct->c_type == CT_MULTIPART) {
3637 struct multipart *m = (struct multipart *) ct->c_ctparams;
3640 /* initially mark the domain of enclosing multipart as 7bit */
3641 ct->c_encoding = CE_7BIT;
3643 for (part = m->mp_parts; part; part = part->mp_next) {
3644 CT p = part->mp_part;
3646 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3649 /* if necessary, enlarge encoding for enclosing multipart */
3650 if (p->c_encoding == CE_BINARY)
3651 ct->c_encoding = CE_BINARY;
3652 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3653 ct->c_encoding = CE_8BIT;
3660 * Decide what to check while scanning this content.
3662 switch (ct->c_type) {
3666 if (ct->c_subtype == TEXT_PLAIN) {
3671 checkebcdic = ebcdicsw;
3677 case CT_APPLICATION:
3679 checkebcdic = ebcdicsw;
3691 /* don't check anything for message/external */
3692 if (ct->c_subtype == MESSAGE_EXTERNAL)
3702 * Don't check anything for these types,
3703 * since we are forcing use of base64.
3714 * Scan the unencoded content
3716 if (check8bit || checklinelen || checklinespace || checkboundary) {
3717 if ((in = fopen (ce->ce_file, "r")) == NULL)
3718 adios (ce->ce_file, "unable to open for reading");
3719 len = strlen (prefix);
3721 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3723 * Check for 8bit data.
3726 for (cp = buffer; *cp; cp++) {
3727 if (!isascii (*cp)) {
3729 check8bit = 0; /* no need to keep checking */
3732 * Check if character is ebcdic-safe. We only check
3733 * this if also checking for 8bit data.
3735 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3737 checkebcdic = 0; /* no need to keep checking */
3743 * Check line length.
3745 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3747 checklinelen = 0; /* no need to keep checking */
3751 * Check if line ends with a space.
3753 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3755 checklinespace = 0; /* no need to keep checking */
3759 * Check if content contains a line that clashes
3760 * with our standard boundary for multipart messages.
3762 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3763 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3767 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3769 checkboundary = 0; /* no need to keep checking */
3777 * Decide which transfer encoding to use.
3779 switch (ct->c_type) {
3782 * If the text content didn't specify a character
3783 * set, we need to figure out which one was used.
3785 t = (struct text *) ct->c_ctparams;
3786 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3787 CI ci = &ct->c_ctinfo;
3790 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3794 t->tx_charset = CHARSET_UNKNOWN;
3795 *ap = concat ("charset=", write_charset_8bit(), NULL);
3797 t->tx_charset = CHARSET_USASCII;
3798 *ap = add ("charset=us-ascii", NULL);
3801 cp = strchr(*ap++, '=');
3807 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3808 ct->c_encoding = CE_QUOTED;
3810 ct->c_encoding = CE_7BIT;
3813 case CT_APPLICATION:
3814 /* For application type, use base64, except when postscript */
3815 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3816 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3817 ? CE_QUOTED : CE_BASE64;
3819 ct->c_encoding = CE_7BIT;
3823 ct->c_encoding = CE_7BIT;
3829 /* For audio, image, and video contents, just use base64 */
3830 ct->c_encoding = CE_BASE64;
3834 return (boundaryclash ? NOTOK : OK);
3839 * Scan the content structures, and build header
3840 * fields that will need to be output into the
3845 build_headers (CT ct)
3847 int cc, mailbody, len;
3849 char *np, *vp, buffer[BUFSIZ];
3850 CI ci = &ct->c_ctinfo;
3853 * If message is type multipart, then add the multipart
3854 * boundary to the list of attribute/value pairs.
3856 if (ct->c_type == CT_MULTIPART) {
3858 static int level = 0; /* store nesting level */
3862 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3863 cp = strchr(*ap++ = add (buffer, NULL), '=');
3870 * Skip the output of Content-Type, parameters, content
3871 * description, and Content-ID if the content is of type
3872 * "message" and the rfc934 compatibility flag is set
3873 * (which means we are inside multipart/digest and the
3874 * switch -rfc934mode was given).
3876 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3880 * output the content type and subtype
3882 np = add (TYPE_FIELD, NULL);
3883 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3885 /* keep track of length of line */
3886 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3887 + strlen (ci->ci_subtype) + 3;
3889 mailbody = ct->c_type == CT_MESSAGE
3890 && ct->c_subtype == MESSAGE_EXTERNAL
3891 && ((struct exbody *) ct->c_ctparams)->eb_body;
3894 * Append the attribute/value pairs to
3895 * the end of the Content-Type line.
3897 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3898 if (mailbody && !strcasecmp (*ap, "body"))
3904 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3905 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3906 vp = add ("\n\t", vp);
3912 vp = add (buffer, vp);
3917 * Append any RFC-822 comment to the end of
3918 * the Content-Type line.
3920 if (ci->ci_comment) {
3921 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3922 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3923 vp = add ("\n\t", vp);
3929 vp = add (buffer, vp);
3932 vp = add ("\n", vp);
3933 add_header (ct, np, vp);
3936 * output the Content-ID, unless disabled by -nocontentid
3938 if (contentidsw && ct->c_id) {
3939 np = add (ID_FIELD, NULL);
3940 vp = concat (" ", ct->c_id, NULL);
3941 add_header (ct, np, vp);
3945 * output the Content-Description
3948 np = add (DESCR_FIELD, NULL);
3949 vp = concat (" ", ct->c_descr, NULL);
3950 add_header (ct, np, vp);
3955 * If this is the internal content structure for a
3956 * "message/external", then we are done with the
3957 * headers (since it has no body).
3963 * output the Content-MD5
3966 np = add (MD5_FIELD, NULL);
3967 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3968 add_header (ct, np, vp);
3972 * output the Content-Transfer-Encoding
3974 switch (ct->c_encoding) {
3976 /* Nothing to output */
3978 np = add (ENCODING_FIELD, NULL);
3979 vp = concat (" ", "7bit", "\n", NULL);
3980 add_header (ct, np, vp);
3985 if (ct->c_type == CT_MESSAGE)
3986 adios (NULL, "internal error, invalid encoding");
3988 np = add (ENCODING_FIELD, NULL);
3989 vp = concat (" ", "8bit", "\n", NULL);
3990 add_header (ct, np, vp);
3994 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3995 adios (NULL, "internal error, invalid encoding");
3997 np = add (ENCODING_FIELD, NULL);
3998 vp = concat (" ", "quoted-printable", "\n", NULL);
3999 add_header (ct, np, vp);
4003 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4004 adios (NULL, "internal error, invalid encoding");
4006 np = add (ENCODING_FIELD, NULL);
4007 vp = concat (" ", "base64", "\n", NULL);
4008 add_header (ct, np, vp);
4012 if (ct->c_type == CT_MESSAGE)
4013 adios (NULL, "internal error, invalid encoding");
4015 np = add (ENCODING_FIELD, NULL);
4016 vp = concat (" ", "binary", "\n", NULL);
4017 add_header (ct, np, vp);
4021 adios (NULL, "unknown transfer encoding in content");
4026 * Additional content specific header processing
4028 switch (ct->c_type) {
4031 struct multipart *m;
4034 m = (struct multipart *) ct->c_ctparams;
4035 for (part = m->mp_parts; part; part = part->mp_next) {
4045 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4048 e = (struct exbody *) ct->c_ctparams;
4049 build_headers (e->eb_content);
4062 static char nib2b64[0x40+1] =
4063 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4066 calculate_digest (CT ct, int asciiP)
4069 char buffer[BUFSIZ], *vp, *op;
4071 unsigned char digest[16];
4072 unsigned char outbuf[25];
4075 CE ce = ct->c_cefile;
4078 if ((in = fopen (ce->ce_file, "r")) == NULL)
4079 adios (ce->ce_file, "unable to open for reading");
4081 /* Initialize md5 context */
4082 MD5Init (&mdContext);
4084 /* calculate md5 message digest */
4086 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4089 cp = buffer + strlen (buffer) - 1;
4090 if ((c = *cp) == '\n')
4093 MD5Update (&mdContext, (unsigned char *) buffer,
4094 (unsigned int) strlen (buffer));
4097 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4100 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4101 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4104 /* md5 finalization. Write digest and zero md5 context */
4105 MD5Final (digest, &mdContext);
4110 /* print debugging info */
4114 fprintf (stderr, "MD5 digest=");
4115 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4117 fprintf (stderr, "%02x", *dp & 0xff);
4118 fprintf (stderr, "\n");
4121 /* encode the digest using base64 */
4122 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4123 cc > 0; cc -= 3, op += 4) {
4127 bits = (*dp++ & 0xff) << 16;
4129 bits |= (*dp++ & 0xff) << 8;
4131 bits |= *dp++ & 0xff;
4134 for (bp = op + 4; bp > op; bits >>= 6)
4135 *--bp = nib2b64[bits & 0x3f];
4143 /* null terminate string */
4146 /* now make copy and return string */
4147 vp = concat (" ", outbuf, "\n", NULL);
4153 readDigest (CT ct, char *cp)
4158 unsigned char *dp, value, *ep;
4159 unsigned char *b, *b1, *b2, *b3;
4161 b = (unsigned char *) &bits,
4162 b1 = &b[endian > 0 ? 1 : 2],
4163 b2 = &b[endian > 0 ? 2 : 1],
4164 b3 = &b[endian > 0 ? 3 : 0];
4169 for (ep = (dp = ct->c_digest)
4170 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4175 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4177 fprintf (stderr, "invalid BASE64 encoding\n");
4181 bits |= value << bitno;
4183 if ((bitno -= 6) < 0) {
4184 if (dp + (3 - skip) > ep)
4185 goto invalid_digest;
4200 goto self_delimiting;
4205 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4215 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4223 fprintf (stderr, "MD5 digest=");
4224 for (dp = ct->c_digest; dp < ep; dp++)
4225 fprintf (stderr, "%02x", *dp & 0xff);
4226 fprintf (stderr, "\n");