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>
31 #ifdef TIME_WITH_SYS_TIME
32 # include <sys/time.h>
35 # ifdef TM_IN_SYS_TIME
36 # include <sys/time.h>
42 #ifdef HAVE_SYS_WAIT_H
43 # include <sys/wait.h>
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 if (!(hp = malloc (sizeof(*hp))))
791 adios (NULL, "out of memory");
793 /* link data into header structure */
798 /* link header structure into the list */
799 if (ct->c_first_hf == NULL) {
800 ct->c_first_hf = hp; /* this is the first */
803 ct->c_last_hf->next = hp; /* add it to the end */
812 * Used to parse both:
813 * 1) Content-Type line
814 * 2) composition directives
816 * and fills in the information of the CTinfo structure.
820 get_ctinfo (char *cp, CT ct, int magic)
823 char *dp, **ap, **ep;
828 i = strlen (invo_name) + 2;
830 /* store copy of Content-Type line */
831 cp = ct->c_ctline = add (cp, NULL);
833 while (isspace (*cp)) /* trim leading spaces */
836 /* change newlines to spaces */
837 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
840 /* trim trailing spaces */
841 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
847 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
849 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
852 for (dp = cp; istoken (*dp); dp++)
855 ci->ci_type = add (cp, NULL); /* store content type */
859 advise (NULL, "invalid %s: field in message %s (empty type)",
860 TYPE_FIELD, ct->c_file);
864 /* down case the content type string */
865 for (dp = ci->ci_type; *dp; dp++)
866 if (isalpha(*dp) && isupper (*dp))
869 while (isspace (*cp))
872 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
877 ci->ci_subtype = add ("", NULL);
882 while (isspace (*cp))
885 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
888 for (dp = cp; istoken (*dp); dp++)
891 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
894 if (!*ci->ci_subtype) {
896 "invalid %s: field in message %s (empty subtype for \"%s\")",
897 TYPE_FIELD, ct->c_file, ci->ci_type);
901 /* down case the content subtype string */
902 for (dp = ci->ci_subtype; *dp; dp++)
903 if (isalpha(*dp) && isupper (*dp))
907 while (isspace (*cp))
910 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
914 * Parse attribute/value pairs given with Content-Type
916 ep = (ap = ci->ci_attrs) + NPARMS;
922 "too many parameters in message %s's %s: field (%d max)",
923 ct->c_file, TYPE_FIELD, NPARMS);
928 while (isspace (*cp))
931 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
936 "extraneous trailing ';' in message %s's %s: parameter list",
937 ct->c_file, TYPE_FIELD);
941 /* down case the attribute name */
942 for (dp = cp; istoken (*dp); dp++)
943 if (isalpha(*dp) && isupper (*dp))
946 for (up = dp; isspace (*dp); )
948 if (dp == cp || *dp != '=') {
950 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
951 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
955 vp = (*ap = add (cp, NULL)) + (up - cp);
957 for (dp++; isspace (*dp); )
960 /* now add the attribute value */
961 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
964 for (cp = ++dp, dp = vp;;) {
969 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
970 ct->c_file, TYPE_FIELD, i, i, "", *ap);
975 if ((c = *cp++) == '\0')
990 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
996 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
997 ct->c_file, TYPE_FIELD, i, i, "", *ap);
1002 while (isspace (*cp))
1005 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1010 * Get any <Content-Id> given in buffer
1012 if (magic && *cp == '<') {
1017 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1018 advise (NULL, "invalid ID in message %s", ct->c_file);
1024 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1030 while (isspace (*cp))
1035 * Get any [Content-Description] given in buffer.
1037 if (magic && *cp == '[') {
1039 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1043 advise (NULL, "invalid description in message %s", ct->c_file);
1051 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1057 while (isspace (*cp))
1062 * Check if anything is left over
1066 ci->ci_magic = add (cp, NULL);
1069 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1070 ct->c_file, TYPE_FIELD, i, i, "", cp);
1078 get_comment (CT ct, char **ap, int istype)
1082 char c, buffer[BUFSIZ], *dp;
1091 switch (c = *cp++) {
1094 advise (NULL, "invalid comment in message %s's %s: field",
1095 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1100 if ((c = *cp++) == '\0')
1123 if ((dp = ci->ci_comment)) {
1124 ci->ci_comment = concat (dp, " ", buffer, NULL);
1127 ci->ci_comment = add (buffer, NULL);
1131 while (isspace (*cp))
1142 * Handles content types audio, image, and video.
1143 * There's not much to do right here.
1149 return OK; /* not much to do here */
1163 CI ci = &ct->c_ctinfo;
1165 /* check for missing subtype */
1166 if (!*ci->ci_subtype)
1167 ci->ci_subtype = add ("plain", ci->ci_subtype);
1170 for (kv = SubText; kv->kv_key; kv++)
1171 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1173 ct->c_subtype = kv->kv_value;
1175 /* allocate text character set structure */
1176 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1177 adios (NULL, "out of memory");
1178 ct->c_ctparams = (void *) t;
1180 /* initially mark character set as unspecified */
1181 t->tx_charset = CHARSET_UNSPECIFIED;
1183 /* scan for charset parameter */
1184 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1185 if (!strcasecmp (*ap, "charset"))
1188 /* check if content specified a character set */
1190 /* match character set or set to CHARSET_UNKNOWN */
1191 for (kv = Charset; kv->kv_key; kv++)
1192 if (!strcasecmp (*ep, kv->kv_key))
1194 t->tx_charset = kv->kv_value;
1206 InitMultiPart (CT ct)
1210 char *cp, *dp, **ap, **ep;
1211 char *bp, buffer[BUFSIZ];
1212 struct multipart *m;
1214 struct part *part, **next;
1215 CI ci = &ct->c_ctinfo;
1220 * The encoding for multipart messages must be either
1221 * 7bit, 8bit, or binary (per RFC2045).
1223 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1224 && ct->c_encoding != CE_BINARY) {
1226 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1227 ci->ci_type, ci->ci_subtype, ct->c_file);
1232 for (kv = SubMultiPart; kv->kv_key; kv++)
1233 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1235 ct->c_subtype = kv->kv_value;
1238 * Check for "boundary" parameter, which is
1239 * required for multipart messages.
1241 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1242 if (!strcasecmp (*ap, "boundary")) {
1248 /* complain if boundary parameter is missing */
1251 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1252 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1256 /* allocate primary structure for multipart info */
1257 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1258 adios (NULL, "out of memory");
1259 ct->c_ctparams = (void *) m;
1261 /* check if boundary parameter contains only whitespace characters */
1262 for (cp = bp; isspace (*cp); cp++)
1265 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1266 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1270 /* remove trailing whitespace from boundary parameter */
1271 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1276 /* record boundary separators */
1277 m->mp_start = concat (bp, "\n", NULL);
1278 m->mp_stop = concat (bp, "--\n", NULL);
1280 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1281 advise (ct->c_file, "unable to open for reading");
1285 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1287 next = &m->mp_parts;
1291 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1295 pos += strlen (buffer);
1296 if (buffer[0] != '-' || buffer[1] != '-')
1299 if (strcmp (buffer + 2, m->mp_start))
1302 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1303 adios (NULL, "out of memory");
1305 next = &part->mp_next;
1307 if (!(p = get_content (fp, ct->c_file,
1308 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1316 fseek (fp, pos, SEEK_SET);
1319 if (strcmp (buffer + 2, m->mp_start) == 0) {
1323 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1324 if (p->c_end < p->c_begin)
1325 p->c_begin = p->c_end;
1330 if (strcmp (buffer + 2, m->mp_stop) == 0)
1336 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1337 if (!inout && part) {
1339 p->c_end = ct->c_end;
1341 if (p->c_begin >= p->c_end) {
1342 for (next = &m->mp_parts; *next != part;
1343 next = &((*next)->mp_next))
1347 free ((char *) part);
1352 /* reverse the order of the parts for multipart/alternative */
1353 if (ct->c_subtype == MULTI_ALTERNATE)
1357 * label all subparts with part number, and
1358 * then initialize the content of the subpart.
1363 char partnam[BUFSIZ];
1366 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1367 pp = partnam + strlen (partnam);
1372 for (part = m->mp_parts, partnum = 1; part;
1373 part = part->mp_next, partnum++) {
1376 sprintf (pp, "%d", partnum);
1377 p->c_partno = add (partnam, NULL);
1379 /* initialize the content of the subparts */
1380 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1395 * reverse the order of the parts of a multipart
1399 reverse_parts (CT ct)
1402 struct multipart *m;
1403 struct part **base, **bmp, **next, *part;
1405 m = (struct multipart *) ct->c_ctparams;
1407 /* if only one part, just return */
1408 if (!m->mp_parts || !m->mp_parts->mp_next)
1411 /* count number of parts */
1413 for (part = m->mp_parts; part; part = part->mp_next)
1416 /* allocate array of pointers to the parts */
1417 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1418 adios (NULL, "out of memory");
1421 /* point at all the parts */
1422 for (part = m->mp_parts; part; part = part->mp_next)
1426 /* reverse the order of the parts */
1427 next = &m->mp_parts;
1428 for (bmp--; bmp >= base; bmp--) {
1431 next = &part->mp_next;
1435 /* free array of pointers */
1436 free ((char *) base);
1448 CI ci = &ct->c_ctinfo;
1450 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1452 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1453 ci->ci_type, ci->ci_subtype, ct->c_file);
1457 /* check for missing subtype */
1458 if (!*ci->ci_subtype)
1459 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1462 for (kv = SubMessage; kv->kv_key; kv++)
1463 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1465 ct->c_subtype = kv->kv_value;
1467 switch (ct->c_subtype) {
1468 case MESSAGE_RFC822:
1471 case MESSAGE_PARTIAL:
1476 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1477 adios (NULL, "out of memory");
1478 ct->c_ctparams = (void *) p;
1480 /* scan for parameters "id", "number", and "total" */
1481 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1482 if (!strcasecmp (*ap, "id")) {
1483 p->pm_partid = add (*ep, NULL);
1486 if (!strcasecmp (*ap, "number")) {
1487 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1488 || p->pm_partno < 1) {
1491 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1492 *ap, ci->ci_type, ci->ci_subtype,
1493 ct->c_file, TYPE_FIELD);
1498 if (!strcasecmp (*ap, "total")) {
1499 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1508 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1510 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1511 ci->ci_type, ci->ci_subtype,
1512 ct->c_file, TYPE_FIELD);
1518 case MESSAGE_EXTERNAL:
1525 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1526 adios (NULL, "out of memory");
1527 ct->c_ctparams = (void *) e;
1530 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1531 advise (ct->c_file, "unable to open for reading");
1535 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1537 if (!(p = get_content (fp, ct->c_file, 0))) {
1546 if ((exresult = params_external (ct, 0)) != NOTOK
1547 && p->c_ceopenfnx == openMail) {
1551 if ((size = ct->c_end - p->c_begin) <= 0) {
1553 content_error (NULL, ct,
1554 "empty body for access-type=mail-server");
1558 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1559 adios (NULL, "out of memory");
1560 fseek (p->c_fp, p->c_begin, SEEK_SET);
1562 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1564 adios ("failed", "fread");
1567 adios (NULL, "unexpected EOF from fread");
1570 bp += cc, size -= cc;
1577 p->c_end = p->c_begin;
1582 if (exresult == NOTOK)
1584 if (e->eb_flags == NOTOK)
1587 switch (p->c_type) {
1592 if (p->c_subtype != MESSAGE_RFC822)
1596 e->eb_partno = ct->c_partno;
1598 (*p->c_ctinitfnx) (p);
1613 params_external (CT ct, int composing)
1616 struct exbody *e = (struct exbody *) ct->c_ctparams;
1617 CI ci = &ct->c_ctinfo;
1619 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1620 if (!strcasecmp (*ap, "access-type")) {
1621 struct str2init *s2i;
1622 CT p = e->eb_content;
1624 for (s2i = str2methods; s2i->si_key; s2i++)
1625 if (!strcasecmp (*ep, s2i->si_key))
1630 e->eb_flags = NOTOK;
1631 p->c_encoding = CE_EXTERNAL;
1634 e->eb_access = s2i->si_key;
1635 e->eb_flags = s2i->si_val;
1636 p->c_encoding = CE_EXTERNAL;
1638 /* Call the Init function for this external type */
1639 if ((*s2i->si_init)(p) == NOTOK)
1643 if (!strcasecmp (*ap, "name")) {
1647 if (!strcasecmp (*ap, "permission")) {
1648 e->eb_permission = *ep;
1651 if (!strcasecmp (*ap, "site")) {
1655 if (!strcasecmp (*ap, "directory")) {
1659 if (!strcasecmp (*ap, "mode")) {
1663 if (!strcasecmp (*ap, "size")) {
1664 sscanf (*ep, "%lu", &e->eb_size);
1667 if (!strcasecmp (*ap, "server")) {
1671 if (!strcasecmp (*ap, "subject")) {
1672 e->eb_subject = *ep;
1675 if (composing && !strcasecmp (*ap, "body")) {
1676 e->eb_body = getcpy (*ep);
1681 if (!e->eb_access) {
1683 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1684 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1697 InitApplication (CT ct)
1700 CI ci = &ct->c_ctinfo;
1703 for (kv = SubApplication; kv->kv_key; kv++)
1704 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1706 ct->c_subtype = kv->kv_value;
1713 * Set up structures for placing unencoded
1714 * content when building parts.
1718 init_decoded_content (CT ct)
1722 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1723 adios (NULL, "out of memory");
1726 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1727 ct->c_ceclosefnx = close_encoding;
1728 ct->c_cesizefnx = NULL; /* since unencoded */
1735 * TRANSFER ENCODINGS
1739 init_encoding (CT ct, OpenCEFunc openfnx)
1743 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1744 adios (NULL, "out of memory");
1747 ct->c_ceopenfnx = openfnx;
1748 ct->c_ceclosefnx = close_encoding;
1749 ct->c_cesizefnx = size_encoding;
1756 close_encoding (CT ct)
1760 if (!(ce = ct->c_cefile))
1770 static unsigned long
1771 size_encoding (CT ct)
1779 if (!(ce = ct->c_cefile))
1780 return (ct->c_end - ct->c_begin);
1782 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1783 return (long) st.st_size;
1786 if (stat (ce->ce_file, &st) != NOTOK)
1787 return (long) st.st_size;
1792 if (ct->c_encoding == CE_EXTERNAL)
1793 return (ct->c_end - ct->c_begin);
1796 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1797 return (ct->c_end - ct->c_begin);
1799 if (fstat (fd, &st) != NOTOK)
1800 size = (long) st.st_size;
1804 (*ct->c_ceclosefnx) (ct);
1813 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1818 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1819 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1820 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1821 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1822 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1823 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1824 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1825 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1826 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1827 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1828 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1829 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1836 return init_encoding (ct, openBase64);
1841 openBase64 (CT ct, char **file)
1843 int bitno, cc, digested;
1846 unsigned char value, *b, *b1, *b2, *b3;
1847 char *cp, *ep, buffer[BUFSIZ];
1851 b = (unsigned char *) &bits;
1852 b1 = &b[endian > 0 ? 1 : 2];
1853 b2 = &b[endian > 0 ? 2 : 1];
1854 b3 = &b[endian > 0 ? 3 : 0];
1858 fseek (ce->ce_fp, 0L, SEEK_SET);
1863 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1864 content_error (ce->ce_file, ct, "unable to fopen for reading");
1870 if (*file == NULL) {
1871 ce->ce_file = add (m_scratch ("", tmp), NULL);
1874 ce->ce_file = add (*file, NULL);
1878 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1879 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1883 if ((len = ct->c_end - ct->c_begin) < 0)
1884 adios (NULL, "internal error(1)");
1886 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1887 content_error (ct->c_file, ct, "unable to open for reading");
1891 if ((digested = ct->c_digested))
1892 MD5Init (&mdContext);
1898 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1900 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1902 content_error (ct->c_file, ct, "error reading from");
1906 content_error (NULL, ct, "premature eof");
1914 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1919 if (skip || (*cp & 0x80)
1920 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1922 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1924 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1927 content_error (NULL, ct,
1928 "invalid BASE64 encoding -- continuing");
1932 bits |= value << bitno;
1934 if ((bitno -= 6) < 0) {
1935 putc ((char) *b1, ce->ce_fp);
1937 MD5Update (&mdContext, b1, 1);
1939 putc ((char) *b2, ce->ce_fp);
1941 MD5Update (&mdContext, b2, 1);
1943 putc ((char) *b3, ce->ce_fp);
1945 MD5Update (&mdContext, b3, 1);
1949 if (ferror (ce->ce_fp)) {
1950 content_error (ce->ce_file, ct,
1951 "error writing to");
1954 bitno = 18, bits = 0L, skip = 0;
1960 goto self_delimiting;
1969 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1971 content_error (NULL, ct, "invalid BASE64 encoding");
1976 fseek (ct->c_fp, 0L, SEEK_SET);
1978 if (fflush (ce->ce_fp)) {
1979 content_error (ce->ce_file, ct, "error writing to");
1984 unsigned char digest[16];
1986 MD5Final (digest, &mdContext);
1987 if (memcmp((char *) digest, (char *) ct->c_digest,
1988 sizeof(digest) / sizeof(digest[0])))
1989 content_error (NULL, ct,
1990 "content integrity suspect (digest mismatch) -- continuing");
1993 fprintf (stderr, "content integrity confirmed\n");
1996 fseek (ce->ce_fp, 0L, SEEK_SET);
1999 *file = ce->ce_file;
2000 return fileno (ce->ce_fp);
2003 free_encoding (ct, 0);
2012 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2018 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2019 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2020 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2021 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2022 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2023 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2024 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2025 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2026 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2027 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2028 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2035 return init_encoding (ct, openQuoted);
2040 openQuoted (CT ct, char **file)
2042 int cc, digested, len, quoted;
2044 char buffer[BUFSIZ];
2051 fseek (ce->ce_fp, 0L, SEEK_SET);
2056 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2057 content_error (ce->ce_file, ct, "unable to fopen for reading");
2063 if (*file == NULL) {
2064 ce->ce_file = add (m_scratch ("", tmp), NULL);
2067 ce->ce_file = add (*file, NULL);
2071 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2072 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2076 if ((len = ct->c_end - ct->c_begin) < 0)
2077 adios (NULL, "internal error(2)");
2079 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2080 content_error (ct->c_file, ct, "unable to open for reading");
2084 if ((digested = ct->c_digested))
2085 MD5Init (&mdContext);
2092 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2096 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2097 content_error (NULL, ct, "premature eof");
2101 if ((cc = strlen (buffer)) > len)
2105 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2110 for (; cp < ep; cp++) {
2113 if (!isxdigit (*cp)) {
2115 dp = "expecting hexidecimal-digit";
2116 goto invalid_encoding;
2119 mask |= hex2nib[*cp & 0x7f];
2120 putc (mask, ce->ce_fp);
2122 MD5Update (&mdContext, &mask, 1);
2126 putc (*cp, ce->ce_fp);
2128 MD5Update (&mdContext, (unsigned char *) ":", 1);
2132 if (!isxdigit (*cp))
2134 mask = hex2nib[*cp & 0x7f];
2140 if (ferror (ce->ce_fp)) {
2141 content_error (ce->ce_file, ct, "error writing to");
2150 if (*cp < '!' || *cp > '~') {
2152 dp = "expecting character in range [!..~]";
2155 i = strlen (invo_name) + 2;
2156 content_error (NULL, ct,
2157 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2165 putc (*cp, ce->ce_fp);
2168 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2170 MD5Update (&mdContext, (unsigned char *) cp, 1);
2172 if (ferror (ce->ce_fp)) {
2173 content_error (ce->ce_file, ct, "error writing to");
2179 if (*++cp != '\n') {
2188 content_error (NULL, ct,
2189 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2193 fseek (ct->c_fp, 0L, SEEK_SET);
2195 if (fflush (ce->ce_fp)) {
2196 content_error (ce->ce_file, ct, "error writing to");
2201 unsigned char digest[16];
2203 MD5Final (digest, &mdContext);
2204 if (memcmp((char *) digest, (char *) ct->c_digest,
2205 sizeof(digest) / sizeof(digest[0])))
2206 content_error (NULL, ct,
2207 "content integrity suspect (digest mismatch) -- continuing");
2210 fprintf (stderr, "content integrity confirmed\n");
2213 fseek (ce->ce_fp, 0L, SEEK_SET);
2216 *file = ce->ce_file;
2217 return fileno (ce->ce_fp);
2220 free_encoding (ct, 0);
2232 if (init_encoding (ct, open7Bit) == NOTOK)
2235 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2241 open7Bit (CT ct, char **file)
2244 char buffer[BUFSIZ];
2249 fseek (ce->ce_fp, 0L, SEEK_SET);
2254 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2255 content_error (ce->ce_file, ct, "unable to fopen for reading");
2261 if (*file == NULL) {
2262 ce->ce_file = add (m_scratch ("", tmp), NULL);
2265 ce->ce_file = add (*file, NULL);
2269 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2270 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2274 if (ct->c_type == CT_MULTIPART) {
2276 CI ci = &ct->c_ctinfo;
2279 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2280 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2281 + 1 + strlen (ci->ci_subtype);
2282 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2283 putc (';', ce->ce_fp);
2286 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2288 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2289 fputs ("\n\t", ce->ce_fp);
2292 putc (' ', ce->ce_fp);
2295 fprintf (ce->ce_fp, "%s", buffer);
2299 if (ci->ci_comment) {
2300 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2301 fputs ("\n\t", ce->ce_fp);
2305 putc (' ', ce->ce_fp);
2308 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2311 fprintf (ce->ce_fp, "\n");
2313 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2315 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2316 fprintf (ce->ce_fp, "\n");
2319 if ((len = ct->c_end - ct->c_begin) < 0)
2320 adios (NULL, "internal error(3)");
2322 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2323 content_error (ct->c_file, ct, "unable to open for reading");
2327 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2329 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2331 content_error (ct->c_file, ct, "error reading from");
2335 content_error (NULL, ct, "premature eof");
2343 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2344 if (ferror (ce->ce_fp)) {
2345 content_error (ce->ce_file, ct, "error writing to");
2350 fseek (ct->c_fp, 0L, SEEK_SET);
2352 if (fflush (ce->ce_fp)) {
2353 content_error (ce->ce_file, ct, "error writing to");
2357 fseek (ce->ce_fp, 0L, SEEK_SET);
2360 *file = ce->ce_file;
2361 return fileno (ce->ce_fp);
2364 free_encoding (ct, 0);
2374 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2376 char cachefile[BUFSIZ];
2379 fseek (ce->ce_fp, 0L, SEEK_SET);
2384 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2385 content_error (ce->ce_file, ct, "unable to fopen for reading");
2391 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2392 cachefile, sizeof(cachefile)) != NOTOK) {
2393 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2394 ce->ce_file = getcpy (cachefile);
2398 admonish (cachefile, "unable to fopen for reading");
2405 *file = ce->ce_file;
2406 *fd = fileno (ce->ce_fp);
2417 return init_encoding (ct, openFile);
2422 openFile (CT ct, char **file)
2425 char cachefile[BUFSIZ];
2426 struct exbody *e = ct->c_ctexbody;
2427 CE ce = ct->c_cefile;
2429 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2441 content_error (NULL, ct, "missing name parameter");
2445 ce->ce_file = getcpy (e->eb_name);
2448 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2449 content_error (ce->ce_file, ct, "unable to fopen for reading");
2453 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2454 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2455 cachefile, sizeof(cachefile)) != NOTOK) {
2459 mask = umask (cachetype ? ~m_gmprot () : 0222);
2460 if ((fp = fopen (cachefile, "w"))) {
2462 char buffer[BUFSIZ];
2463 FILE *gp = ce->ce_fp;
2465 fseek (gp, 0L, SEEK_SET);
2467 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2469 fwrite (buffer, sizeof(*buffer), cc, fp);
2473 admonish (ce->ce_file, "error reading");
2478 admonish (cachefile, "error writing");
2486 fseek (ce->ce_fp, 0L, SEEK_SET);
2487 *file = ce->ce_file;
2488 return fileno (ce->ce_fp);
2498 return init_encoding (ct, openFTP);
2503 openFTP (CT ct, char **file)
2505 int cachetype, caching, fd;
2507 char *bp, *ftp, *user, *pass;
2508 char buffer[BUFSIZ], cachefile[BUFSIZ];
2511 static char *username = NULL;
2512 static char *password = NULL;
2517 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2525 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2536 if (!e->eb_name || !e->eb_site) {
2537 content_error (NULL, ct, "missing %s parameter",
2538 e->eb_name ? "site": "name");
2545 pidcheck (pidwait (xpid, NOTOK));
2549 /* Get the buffer ready to go */
2551 buflen = sizeof(buffer);
2554 * Construct the query message for user
2556 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2562 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2568 snprintf (bp, buflen, "\n using %sFTP from site %s",
2569 e->eb_flags ? "anonymous " : "", e->eb_site);
2574 if (e->eb_size > 0) {
2575 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2580 snprintf (bp, buflen, "? ");
2583 * Now, check the answer
2585 if (!getanswer (buffer))
2590 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2593 ruserpass (e->eb_site, &username, &password);
2598 ce->ce_unlink = (*file == NULL);
2600 cachefile[0] = '\0';
2601 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2602 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2603 cachefile, sizeof(cachefile)) != NOTOK) {
2604 if (*file == NULL) {
2611 ce->ce_file = add (*file, NULL);
2613 ce->ce_file = add (cachefile, NULL);
2615 ce->ce_file = add (m_scratch ("", tmp), NULL);
2617 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2618 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2626 int child_id, i, vecp;
2630 vec[vecp++] = r1bindex (ftp, '/');
2631 vec[vecp++] = e->eb_site;
2634 vec[vecp++] = e->eb_dir;
2635 vec[vecp++] = e->eb_name;
2636 vec[vecp++] = ce->ce_file,
2637 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2638 ? "ascii" : "binary";
2643 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2647 adios ("fork", "unable to");
2651 close (fileno (ce->ce_fp));
2653 fprintf (stderr, "unable to exec ");
2659 if (pidXwait (child_id, NULL)) {
2663 username = password = NULL;
2672 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2674 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2681 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2686 mask = umask (cachetype ? ~m_gmprot () : 0222);
2687 if ((fp = fopen (cachefile, "w"))) {
2689 FILE *gp = ce->ce_fp;
2691 fseek (gp, 0L, SEEK_SET);
2693 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2695 fwrite (buffer, sizeof(*buffer), cc, fp);
2699 admonish (ce->ce_file, "error reading");
2704 admonish (cachefile, "error writing");
2713 fseek (ce->ce_fp, 0L, SEEK_SET);
2714 *file = ce->ce_file;
2715 return fileno (ce->ce_fp);
2726 return init_encoding (ct, openMail);
2731 openMail (CT ct, char **file)
2733 int child_id, fd, i, vecp;
2735 char *bp, buffer[BUFSIZ], *vec[7];
2736 struct exbody *e = ct->c_ctexbody;
2737 CE ce = ct->c_cefile;
2739 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2750 if (!e->eb_server) {
2751 content_error (NULL, ct, "missing server parameter");
2758 pidcheck (pidwait (xpid, NOTOK));
2762 /* Get buffer ready to go */
2764 buflen = sizeof(buffer);
2766 /* Now construct query message */
2767 snprintf (bp, buflen, "Retrieve content");
2773 snprintf (bp, buflen, " %s", e->eb_partno);
2779 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2781 e->eb_subject ? e->eb_subject : e->eb_body);
2783 /* Now, check answer */
2784 if (!getanswer (buffer))
2788 vec[vecp++] = r1bindex (mailproc, '/');
2789 vec[vecp++] = e->eb_server;
2790 vec[vecp++] = "-subject";
2791 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2792 vec[vecp++] = "-body";
2793 vec[vecp++] = e->eb_body;
2796 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2800 advise ("fork", "unable to");
2804 execvp (mailproc, vec);
2805 fprintf (stderr, "unable to exec ");
2811 if (pidXwait (child_id, NULL) == OK)
2812 advise (NULL, "request sent");
2816 if (*file == NULL) {
2817 ce->ce_file = add (m_scratch ("", tmp), NULL);
2820 ce->ce_file = add (*file, NULL);
2824 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2825 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2829 fseek (ce->ce_fp, 0L, SEEK_SET);
2830 *file = ce->ce_file;
2831 return fileno (ce->ce_fp);
2836 fgetstr (char *s, int n, FILE *stream)
2840 for (ep = (cp = s) + n; cp < ep; ) {
2843 if (!fgets (cp, n, stream))
2844 return (cp != s ? s : NULL);
2845 if (cp == s && *cp != '#')
2848 cp += (i = strlen (cp)) - 1;
2849 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2860 * Parse the composition draft for text and directives.
2861 * Do initial setup of Content structure.
2865 user_content (FILE *in, char *file, char *buf, CT *ctp)
2869 char buffer[BUFSIZ];
2870 struct multipart *m;
2873 struct str2init *s2i;
2878 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2883 /* allocate basic Content structure */
2884 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2885 adios (NULL, "out of memory");
2888 /* allocate basic structure for handling decoded content */
2889 init_decoded_content (ct);
2896 * Handle inline text. Check if line
2897 * is one of the following forms:
2899 * 1) doesn't begin with '#' (implicit directive)
2900 * 2) begins with "##" (implicit directive)
2901 * 3) begins with "#<"
2903 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2907 char content[BUFSIZ];
2910 /* use a temp file to collect the plain text lines */
2911 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2914 if ((out = fopen (ce->ce_file, "w")) == NULL)
2915 adios (ce->ce_file, "unable to open for writing");
2917 if (buf[0] == '#' && buf[1] == '<') {
2918 strncpy (content, buf + 2, sizeof(content));
2925 /* the directive is implicit */
2926 strncpy (content, "text/plain", sizeof(content));
2928 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2932 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2933 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2937 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2938 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2939 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2940 switch (buffer[0]) {
2947 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
2955 if (headers != 1 || buffer[0] != '\n')
2956 fputs (buffer, out);
2961 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
2963 if (buffer[0] == '#') {
2966 if (buffer[1] != '#')
2968 for (cp = (bp = buffer) + 1; *cp; cp++)
2975 ct->c_end = ftell (out);
2978 /* parse content type */
2979 if (get_ctinfo (content, ct, inlineD) == NOTOK)
2982 for (s2i = str2cts; s2i->si_key; s2i++)
2983 if (!strcasecmp (ci->ci_type, s2i->si_key))
2985 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
2989 * check type specified (possibly implicitly)
2991 switch (ct->c_type = s2i->si_val) {
2993 if (!strcasecmp (ci->ci_subtype, "rfc822")) {
2994 ct->c_encoding = CE_7BIT;
2999 adios (NULL, "it doesn't make sense to define an in-line %s content",
3000 ct->c_type == CT_MESSAGE ? "message" : "multipart");
3005 if ((ct->c_ctinitfnx = s2i->si_init))
3006 (*ct->c_ctinitfnx) (ct);
3011 fseek (in, pos, SEEK_SET);
3016 * If we've reached this point, the next line
3017 * must be some type of explicit directive.
3020 /* check if directive is external-type */
3021 extrnal = (buf[1] == '@');
3023 /* parse directive */
3024 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3027 /* check directive against the list of MIME types */
3028 for (s2i = str2cts; s2i->si_key; s2i++)
3029 if (!strcasecmp (ci->ci_type, s2i->si_key))
3033 * Check if the directive specified a valid type.
3034 * This will happen if it was one of the following forms:
3036 * #type/subtype (or)
3040 if (!ci->ci_subtype)
3041 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3043 switch (ct->c_type = s2i->si_val) {
3045 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3046 ci->ci_type, ci->ci_subtype);
3050 if (!strcasecmp (ci->ci_subtype, "partial"))
3051 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3052 ci->ci_type, ci->ci_subtype);
3053 if (!strcasecmp (ci->ci_subtype, "external-body"))
3054 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3055 ci->ci_type, ci->ci_subtype);
3058 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3059 ci->ci_type, ci->ci_subtype);
3063 if ((ct->c_ctinitfnx = s2i->si_init))
3064 (*ct->c_ctinitfnx) (ct);
3069 * #@type/subtype (external types directive)
3076 adios (NULL, "need external information for \"#@%s/%s\"",
3077 ci->ci_type, ci->ci_subtype);
3080 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3081 free (ci->ci_magic);
3082 ci->ci_magic = NULL;
3085 * Since we are using the current Content structure to
3086 * hold information about the type of the external
3087 * reference, we need to create another Content structure
3088 * for the message/external-body to wrap it in.
3090 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3091 adios (NULL, "out of memory");
3094 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3096 ct->c_type = CT_MESSAGE;
3097 ct->c_subtype = MESSAGE_EXTERNAL;
3099 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3100 adios (NULL, "out of memory");
3101 ct->c_ctparams = (void *) e;
3107 if (params_external (ct, 1) == NOTOK)
3113 /* Handle [file] argument */
3115 /* check if specifies command to execute */
3116 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3117 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3120 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3121 cp = add (cp, NULL);
3122 free (ci->ci_magic);
3125 /* record filename of decoded contents */
3126 ce->ce_file = ci->ci_magic;
3127 if (access (ce->ce_file, R_OK) == NOTOK)
3128 adios ("reading", "unable to access %s for", ce->ce_file);
3129 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3130 ct->c_end = (long) st.st_size;
3131 ci->ci_magic = NULL;
3137 * No [file] argument, so check profile for
3138 * method to compose content.
3140 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3141 invo_name, ci->ci_type, ci->ci_subtype);
3142 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3143 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3144 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3145 content_error (NULL, ct, "don't know how to compose content");
3149 ci->ci_magic = add (cp, NULL);
3154 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3158 * #forw [+folder] [msgs]
3160 if (!strcasecmp (ci->ci_type, "forw")) {
3162 char *folder, *arguments[MAXARGS];
3166 ap = brkstring (ci->ci_magic, " ", "\n");
3167 copyip (ap, arguments, MAXARGS);
3169 arguments[0] = "cur";
3170 arguments[1] = NULL;
3174 /* search the arguments for a folder name */
3175 for (ap = arguments; *ap; ap++) {
3177 if (*cp == '+' || *cp == '@') {
3179 adios (NULL, "only one folder per #forw directive");
3181 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3185 /* else, use the current folder */
3187 folder = add (getfolder (1), NULL);
3189 if (!(mp = folder_read (folder)))
3190 adios (NULL, "unable to read folder %s", folder);
3191 for (ap = arguments; *ap; ap++) {
3193 if (*cp != '+' && *cp != '@')
3194 if (!m_convert (mp, cp))
3201 * If there is more than one message to include, make this
3202 * a content of type "multipart/digest" and insert each message
3203 * as a subpart. If there is only one message, then make this
3204 * a content of type "message/rfc822".
3206 if (mp->numsel > 1) {
3207 /* we are forwarding multiple messages */
3208 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3210 ct->c_type = CT_MULTIPART;
3211 ct->c_subtype = MULTI_DIGEST;
3213 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3214 adios (NULL, "out of memory");
3215 ct->c_ctparams = (void *) m;
3218 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3219 if (is_selected(mp, msgnum)) {
3224 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3225 adios (NULL, "out of memory");
3226 init_decoded_content (p);
3228 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3230 p->c_type = CT_MESSAGE;
3231 p->c_subtype = MESSAGE_RFC822;
3233 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3234 pe->ce_file = add (buffer, NULL);
3235 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3236 p->c_end = (long) st.st_size;
3238 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3239 adios (NULL, "out of memory");
3241 pp = &part->mp_next;
3246 /* we are forwarding one message */
3247 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3249 ct->c_type = CT_MESSAGE;
3250 ct->c_subtype = MESSAGE_RFC822;
3252 msgnum = mp->lowsel;
3253 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3254 ce->ce_file = add (buffer, NULL);
3255 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3256 ct->c_end = (long) st.st_size;
3259 folder_free (mp); /* free folder/message structure */
3266 if (!strcasecmp (ci->ci_type, "end")) {
3273 * #begin [ alternative | parallel ]
3275 if (!strcasecmp (ci->ci_type, "begin")) {
3276 if (!ci->ci_magic) {
3278 cp = SubMultiPart[vrsn - 1].kv_key;
3279 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3280 vrsn = MULTI_ALTERNATE;
3281 cp = SubMultiPart[vrsn - 1].kv_key;
3282 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3283 vrsn = MULTI_PARALLEL;
3284 cp = SubMultiPart[vrsn - 1].kv_key;
3285 } else if (uprf (ci->ci_magic, "digest")) {
3288 vrsn = MULTI_UNKNOWN;
3293 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3294 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3296 ct->c_type = CT_MULTIPART;
3297 ct->c_subtype = vrsn;
3299 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3300 adios (NULL, "out of memory");
3301 ct->c_ctparams = (void *) m;
3304 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3308 if (user_content (in, file, buffer, &p) == DONE) {
3310 adios (NULL, "empty \"#begin ... #end\" sequence");
3316 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3317 adios (NULL, "out of memory");
3319 pp = &part->mp_next;
3322 admonish (NULL, "premature end-of-file, missing #end");
3329 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3330 return NOTOK; /* NOT REACHED */
3335 set_id (CT ct, int top)
3339 static time_t clock = 0;
3340 static char *msgfmt;
3344 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3345 (int) getpid(), (long) clock, LocalName());
3347 msgfmt = getcpy(msgid);
3349 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3350 ct->c_id = getcpy (msgid);
3354 static char ebcdicsafe[0x100] = {
3355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3356 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3358 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3359 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3360 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3361 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3362 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3363 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3364 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3365 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3366 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3367 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3368 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3369 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3370 0x01, 0x01, 0x01, 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,
3385 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3386 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3391 * Fill out, or expand the various contents in the composition
3392 * draft. Read-in any necessary files. Parse and execute any
3393 * commands specified by profile composition strings.
3397 compose_content (CT ct)
3399 CE ce = ct->c_cefile;
3401 switch (ct->c_type) {
3406 char partnam[BUFSIZ];
3407 struct multipart *m = (struct multipart *) ct->c_ctparams;
3411 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3412 pp = partnam + strlen (partnam);
3417 /* first, we call compose_content on all the subparts */
3418 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3419 CT p = part->mp_part;
3421 sprintf (pp, "%d", partnum);
3422 p->c_partno = add (partnam, NULL);
3423 if (compose_content (p) == NOTOK)
3428 * If the -rfc934mode switch is given, then check all
3429 * the subparts of a multipart/digest. If they are all
3430 * message/rfc822, then mark this content and all
3431 * subparts with the rfc934 compatibility mode flag.
3433 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3436 for (part = m->mp_parts; part; part = part->mp_next) {
3437 CT p = part->mp_part;
3439 if (p->c_subtype != MESSAGE_RFC822) {
3444 ct->c_rfc934 = is934;
3445 for (part = m->mp_parts; part; part = part->mp_next) {
3446 CT p = part->mp_part;
3448 if ((p->c_rfc934 = is934))
3454 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3458 for (part = m->mp_parts; part; part = part->mp_next)
3459 ct->c_end += part->mp_part->c_end + partnum;
3465 /* Nothing to do for type message */
3469 * Discrete types (text/application/audio/image/video)
3474 int i, xstdout, len, buflen;
3475 char *bp, **ap, *cp;
3476 char *vec[4], buffer[BUFSIZ];
3478 CI ci = &ct->c_ctinfo;
3480 if (!(cp = ci->ci_magic))
3481 adios (NULL, "internal error(5)");
3483 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3488 /* Get buffer ready to go */
3491 buflen = sizeof(buffer);
3494 * Parse composition string into buffer
3496 for ( ; *cp; cp++) {
3501 /* insert parameters from directive */
3505 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3506 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3516 /* %f, and stdout is not-redirected */
3522 * insert temporary filename where
3523 * content should be written
3525 snprintf (bp, buflen, "%s", ce->ce_file);
3529 /* insert content subtype */
3530 strncpy (bp, ci->ci_subtype, buflen);
3534 /* insert character % */
3555 printf ("composing content %s/%s from command\n\t%s\n",
3556 ci->ci_type, ci->ci_subtype, buffer);
3558 fflush (stdout); /* not sure if need for -noverbose */
3565 if ((out = fopen (ce->ce_file, "w")) == NULL)
3566 adios (ce->ce_file, "unable to open for writing");
3568 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3572 adios ("fork", "unable to fork");
3577 dup2 (fileno (out), 1);
3578 close (fileno (out));
3579 execvp ("/bin/sh", vec);
3580 fprintf (stderr, "unable to exec ");
3587 if (pidXwait(child_id, NULL))
3593 /* Check size of file */
3594 if (listsw && ct->c_end == 0L) {
3597 if (stat (ce->ce_file, &st) != NOTOK)
3598 ct->c_end = (long) st.st_size;
3610 * 1) choose a transfer encoding.
3611 * 2) check for clashes with multipart boundary string.
3612 * 3) for text content, figure out which character set is being used.
3614 * If there is a clash with one of the contents and the multipart boundary,
3615 * this function will exit with NOTOK. This will cause the scanning process
3616 * to be repeated with a different multipart boundary. It is possible
3617 * (although highly unlikely) that this scan will be repeated multiple times.
3621 scan_content (CT ct)
3624 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3625 int checklinelen, linelen = 0; /* check for long lines */
3626 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3627 int checklinespace, linespace = 0; /* check if any line ends with space */
3628 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3629 char *cp, buffer[BUFSIZ];
3632 CE ce = ct->c_cefile;
3635 * handle multipart by scanning all subparts
3636 * and then checking their encoding.
3638 if (ct->c_type == CT_MULTIPART) {
3639 struct multipart *m = (struct multipart *) ct->c_ctparams;
3642 /* initially mark the domain of enclosing multipart as 7bit */
3643 ct->c_encoding = CE_7BIT;
3645 for (part = m->mp_parts; part; part = part->mp_next) {
3646 CT p = part->mp_part;
3648 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3651 /* if necessary, enlarge encoding for enclosing multipart */
3652 if (p->c_encoding == CE_BINARY)
3653 ct->c_encoding = CE_BINARY;
3654 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3655 ct->c_encoding = CE_8BIT;
3662 * Decide what to check while scanning this content.
3664 switch (ct->c_type) {
3668 if (ct->c_subtype == TEXT_PLAIN) {
3673 checkebcdic = ebcdicsw;
3679 case CT_APPLICATION:
3681 checkebcdic = ebcdicsw;
3693 /* don't check anything for message/external */
3694 if (ct->c_subtype == MESSAGE_EXTERNAL)
3704 * Don't check anything for these types,
3705 * since we are forcing use of base64.
3716 * Scan the unencoded content
3718 if (check8bit || checklinelen || checklinespace || checkboundary) {
3719 if ((in = fopen (ce->ce_file, "r")) == NULL)
3720 adios (ce->ce_file, "unable to open for reading");
3721 len = strlen (prefix);
3723 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3725 * Check for 8bit data.
3728 for (cp = buffer; *cp; cp++) {
3729 if (!isascii (*cp)) {
3731 check8bit = 0; /* no need to keep checking */
3734 * Check if character is ebcdic-safe. We only check
3735 * this if also checking for 8bit data.
3737 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3739 checkebcdic = 0; /* no need to keep checking */
3745 * Check line length.
3747 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3749 checklinelen = 0; /* no need to keep checking */
3753 * Check if line ends with a space.
3755 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3757 checklinespace = 0; /* no need to keep checking */
3761 * Check if content contains a line that clashes
3762 * with our standard boundary for multipart messages.
3764 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3765 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3769 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3771 checkboundary = 0; /* no need to keep checking */
3779 * Decide which transfer encoding to use.
3781 switch (ct->c_type) {
3784 * If the text content didn't specify a character
3785 * set, we need to figure out which one was used.
3787 t = (struct text *) ct->c_ctparams;
3788 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3789 CI ci = &ct->c_ctinfo;
3792 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3796 t->tx_charset = CHARSET_UNKNOWN;
3797 *ap = concat ("charset=", write_charset_8bit(), NULL);
3799 t->tx_charset = CHARSET_USASCII;
3800 *ap = add ("charset=us-ascii", NULL);
3803 cp = strchr(*ap++, '=');
3809 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3810 ct->c_encoding = CE_QUOTED;
3812 ct->c_encoding = CE_7BIT;
3815 case CT_APPLICATION:
3816 /* For application type, use base64, except when postscript */
3817 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3818 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3819 ? CE_QUOTED : CE_BASE64;
3821 ct->c_encoding = CE_7BIT;
3825 ct->c_encoding = CE_7BIT;
3831 /* For audio, image, and video contents, just use base64 */
3832 ct->c_encoding = CE_BASE64;
3836 return (boundaryclash ? NOTOK : OK);
3841 * Scan the content structures, and build header
3842 * fields that will need to be output into the
3847 build_headers (CT ct)
3849 int cc, mailbody, len;
3851 char *np, *vp, buffer[BUFSIZ];
3852 CI ci = &ct->c_ctinfo;
3855 * If message is type multipart, then add the multipart
3856 * boundary to the list of attribute/value pairs.
3858 if (ct->c_type == CT_MULTIPART) {
3860 static int level = 0; /* store nesting level */
3864 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3865 cp = strchr(*ap++ = add (buffer, NULL), '=');
3872 * Skip the output of Content-Type, parameters, content
3873 * description, and Content-ID if the content is of type
3874 * "message" and the rfc934 compatibility flag is set
3875 * (which means we are inside multipart/digest and the
3876 * switch -rfc934mode was given).
3878 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3882 * output the content type and subtype
3884 np = add (TYPE_FIELD, NULL);
3885 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3887 /* keep track of length of line */
3888 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3889 + strlen (ci->ci_subtype) + 3;
3891 mailbody = ct->c_type == CT_MESSAGE
3892 && ct->c_subtype == MESSAGE_EXTERNAL
3893 && ((struct exbody *) ct->c_ctparams)->eb_body;
3896 * Append the attribute/value pairs to
3897 * the end of the Content-Type line.
3899 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3900 if (mailbody && !strcasecmp (*ap, "body"))
3906 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3907 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3908 vp = add ("\n\t", vp);
3914 vp = add (buffer, vp);
3919 * Append any RFC-822 comment to the end of
3920 * the Content-Type line.
3922 if (ci->ci_comment) {
3923 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3924 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3925 vp = add ("\n\t", vp);
3931 vp = add (buffer, vp);
3934 vp = add ("\n", vp);
3935 add_header (ct, np, vp);
3938 * output the Content-ID
3941 np = add (ID_FIELD, NULL);
3942 vp = concat (" ", ct->c_id, NULL);
3943 add_header (ct, np, vp);
3947 * output the Content-Description
3950 np = add (DESCR_FIELD, NULL);
3951 vp = concat (" ", ct->c_descr, NULL);
3952 add_header (ct, np, vp);
3957 * If this is the internal content structure for a
3958 * "message/external", then we are done with the
3959 * headers (since it has no body).
3965 * output the Content-MD5
3968 np = add (MD5_FIELD, NULL);
3969 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3970 add_header (ct, np, vp);
3974 * output the Content-Transfer-Encoding
3976 switch (ct->c_encoding) {
3978 /* Nothing to output */
3980 np = add (ENCODING_FIELD, NULL);
3981 vp = concat (" ", "7bit", "\n", NULL);
3982 add_header (ct, np, vp);
3987 if (ct->c_type == CT_MESSAGE)
3988 adios (NULL, "internal error, invalid encoding");
3990 np = add (ENCODING_FIELD, NULL);
3991 vp = concat (" ", "8bit", "\n", NULL);
3992 add_header (ct, np, vp);
3996 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3997 adios (NULL, "internal error, invalid encoding");
3999 np = add (ENCODING_FIELD, NULL);
4000 vp = concat (" ", "quoted-printable", "\n", NULL);
4001 add_header (ct, np, vp);
4005 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4006 adios (NULL, "internal error, invalid encoding");
4008 np = add (ENCODING_FIELD, NULL);
4009 vp = concat (" ", "base64", "\n", NULL);
4010 add_header (ct, np, vp);
4014 if (ct->c_type == CT_MESSAGE)
4015 adios (NULL, "internal error, invalid encoding");
4017 np = add (ENCODING_FIELD, NULL);
4018 vp = concat (" ", "binary", "\n", NULL);
4019 add_header (ct, np, vp);
4023 adios (NULL, "unknown transfer encoding in content");
4028 * Additional content specific header processing
4030 switch (ct->c_type) {
4033 struct multipart *m;
4036 m = (struct multipart *) ct->c_ctparams;
4037 for (part = m->mp_parts; part; part = part->mp_next) {
4047 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4050 e = (struct exbody *) ct->c_ctparams;
4051 build_headers (e->eb_content);
4064 static char nib2b64[0x40+1] =
4065 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4068 calculate_digest (CT ct, int asciiP)
4071 char buffer[BUFSIZ], *vp, *op;
4073 unsigned char digest[16];
4074 unsigned char outbuf[25];
4077 CE ce = ct->c_cefile;
4080 if ((in = fopen (ce->ce_file, "r")) == NULL)
4081 adios (ce->ce_file, "unable to open for reading");
4083 /* Initialize md5 context */
4084 MD5Init (&mdContext);
4086 /* calculate md5 message digest */
4088 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4091 cp = buffer + strlen (buffer) - 1;
4092 if ((c = *cp) == '\n')
4095 MD5Update (&mdContext, (unsigned char *) buffer,
4096 (unsigned int) strlen (buffer));
4099 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4102 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4103 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4106 /* md5 finalization. Write digest and zero md5 context */
4107 MD5Final (digest, &mdContext);
4112 /* print debugging info */
4116 fprintf (stderr, "MD5 digest=");
4117 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4119 fprintf (stderr, "%02x", *dp & 0xff);
4120 fprintf (stderr, "\n");
4123 /* encode the digest using base64 */
4124 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4125 cc > 0; cc -= 3, op += 4) {
4129 bits = (*dp++ & 0xff) << 16;
4131 bits |= (*dp++ & 0xff) << 8;
4133 bits |= *dp++ & 0xff;
4136 for (bp = op + 4; bp > op; bits >>= 6)
4137 *--bp = nib2b64[bits & 0x3f];
4145 /* null terminate string */
4148 /* now make copy and return string */
4149 vp = concat (" ", outbuf, "\n", NULL);
4155 readDigest (CT ct, char *cp)
4160 unsigned char *dp, value, *ep;
4161 unsigned char *b, *b1, *b2, *b3;
4163 b = (unsigned char *) &bits,
4164 b1 = &b[endian > 0 ? 1 : 2],
4165 b2 = &b[endian > 0 ? 2 : 1],
4166 b3 = &b[endian > 0 ? 3 : 0];
4171 for (ep = (dp = ct->c_digest)
4172 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4177 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4179 fprintf (stderr, "invalid BASE64 encoding\n");
4183 bits |= value << bitno;
4185 if ((bitno -= 6) < 0) {
4186 if (dp + (3 - skip) > ep)
4187 goto invalid_digest;
4202 goto self_delimiting;
4207 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4217 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4225 fprintf (stderr, "MD5 digest=");
4226 for (dp = ct->c_digest; dp < ep; dp++)
4227 fprintf (stderr, "%02x", *dp & 0xff);
4228 fprintf (stderr, "\n");