3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
9 * This code was originally part of mhn.c. I split it into
10 * a separate program (mhbuild.c) and then later split it
11 * again (mhbuildsbr.c). But the code still has some of
12 * the mhn.c code in it. This program needs additional
13 * streamlining and removal of unneeded code.
18 #include <h/signals.h>
22 #include <zotnet/mts/mts.h>
23 #include <zotnet/tws/tws.h>
25 #include <h/mhparse.h>
27 #ifdef HAVE_SYS_WAIT_H
28 # include <sys/wait.h>
41 extern int endian; /* mhmisc.c */
44 extern int rcachesw; /* mhcachesbr.c */
45 extern int wcachesw; /* mhcachesbr.c */
47 int checksw = 0; /* Add Content-MD5 field */
50 * Directory to place tmp files. This must
51 * be set before these routines are called.
57 static char prefix[] = "----- =_aaaaaaaaaa";
60 * Structure for mapping types to their internal flags
68 * Structures for TEXT messages
70 static struct k2v SubText[] = {
71 { "plain", TEXT_PLAIN },
72 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
73 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
74 { NULL, TEXT_UNKNOWN } /* this one must be last! */
77 static struct k2v Charset[] = {
78 { "us-ascii", CHARSET_USASCII },
79 { "iso-8859-1", CHARSET_LATIN },
80 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
84 * Structures for MULTIPART messages
86 static struct k2v SubMultiPart[] = {
87 { "mixed", MULTI_MIXED },
88 { "alternative", MULTI_ALTERNATE },
89 { "digest", MULTI_DIGEST },
90 { "parallel", MULTI_PARALLEL },
91 { NULL, MULTI_UNKNOWN } /* this one must be last! */
95 * Structures for MESSAGE messages
97 static struct k2v SubMessage[] = {
98 { "rfc822", MESSAGE_RFC822 },
99 { "partial", MESSAGE_PARTIAL },
100 { "external-body", MESSAGE_EXTERNAL },
101 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
105 * Structure for APPLICATION messages
107 static struct k2v SubApplication[] = {
108 { "octet-stream", APPLICATION_OCTETS },
109 { "postscript", APPLICATION_POSTSCRIPT },
110 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
115 int make_intermediates (char *);
116 void content_error (char *, CT, char *, ...);
119 int find_cache (CT, int, int *, char *, char *, int);
122 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
125 void free_content (CT);
126 void free_ctinfo (CT);
127 void free_encoding (CT, int);
132 CT build_mime (char *);
138 static CT get_content (FILE *, char *, int);
139 static int add_header (CT, char *, char *);
140 static int get_ctinfo (char *, CT, int);
141 static int get_comment (CT, char **, int);
142 static int InitGeneric (CT);
143 static int InitText (CT);
144 static int InitMultiPart (CT);
145 static void reverse_parts (CT);
146 static int InitMessage (CT);
147 static int params_external (CT, int);
148 static int InitApplication (CT);
149 static int init_decoded_content (CT);
150 static int init_encoding (CT, OpenCEFunc);
151 static void close_encoding (CT);
152 static unsigned long size_encoding (CT);
153 static int InitBase64 (CT);
154 static int openBase64 (CT, char **);
155 static int InitQuoted (CT);
156 static int openQuoted (CT, char **);
157 static int Init7Bit (CT);
158 static int open7Bit (CT, char **);
159 static int openExternal (CT, CT, CE, char **, int *);
160 static int InitFile (CT);
161 static int openFile (CT, char **);
162 static int InitFTP (CT);
163 static int openFTP (CT, char **);
164 static int InitMail (CT);
165 static int openMail (CT, char **);
166 static char *fgetstr (char *, int, FILE *);
167 static int user_content (FILE *, char *, char *, CT *);
168 static void set_id (CT, int);
169 static int compose_content (CT);
170 static int scan_content (CT);
171 static int build_headers (CT);
172 static char *calculate_digest (CT, int);
173 static int readDigest (CT, char *);
176 * Structures for mapping (content) types to
177 * the functions to handle them.
185 static struct str2init str2cts[] = {
186 { "application", CT_APPLICATION, InitApplication },
187 { "audio", CT_AUDIO, InitGeneric },
188 { "image", CT_IMAGE, InitGeneric },
189 { "message", CT_MESSAGE, InitMessage },
190 { "multipart", CT_MULTIPART, InitMultiPart },
191 { "text", CT_TEXT, InitText },
192 { "video", CT_VIDEO, InitGeneric },
193 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
194 { NULL, CT_UNKNOWN, NULL },
197 static struct str2init str2ces[] = {
198 { "base64", CE_BASE64, InitBase64 },
199 { "quoted-printable", CE_QUOTED, InitQuoted },
200 { "8bit", CE_8BIT, Init7Bit },
201 { "7bit", CE_7BIT, Init7Bit },
202 { "binary", CE_BINARY, NULL },
203 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
204 { NULL, CE_UNKNOWN, NULL },
208 * NOTE WELL: si_key MUST NOT have value of NOTOK
210 * si_key is 1 if access method is anonymous.
212 static struct str2init str2methods[] = {
213 { "afs", 1, InitFile },
214 { "anon-ftp", 1, InitFTP },
215 { "ftp", 0, InitFTP },
216 { "local-file", 0, InitFile },
217 { "mail-server", 0, InitMail },
223 pidcheck (int status)
225 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
235 * Main routine for translating composition file
236 * into valid MIME message. It translates the draft
237 * into a content structure (actually a tree of content
238 * structures). This message then can be manipulated
239 * in various ways, including being output via
244 build_mime (char *infile)
247 char buf[BUFSIZ], name[NAMESZ];
254 umask (~m_gmprot ());
256 /* open the composition draft */
257 if ((in = fopen (infile, "r")) == NULL)
258 adios (infile, "unable to open for reading");
261 * Allocate space for primary (outside) content
263 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
264 adios (NULL, "out of memory");
267 * Allocate structure for handling decoded content
268 * for this part. We don't really need this, but
269 * allocate it to remain consistent.
271 init_decoded_content (ct);
274 * Parse some of the header fields in the composition
275 * draft into the linked list of header fields for
276 * the new MIME message.
278 for (compnum = 1, state = FLD;;) {
279 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
285 /* abort if draft has Mime-Version header field */
286 if (!strcasecmp (name, VRSN_FIELD))
287 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
289 /* abort if draft has Content-Transfer-Encoding header field */
290 if (!strcasecmp (name, ENCODING_FIELD))
291 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
293 /* ignore any Content-Type fields in the header */
294 if (!strcasecmp (name, TYPE_FIELD)) {
295 while (state == FLDPLUS)
296 state = m_getfld (state, name, buf, sizeof(buf), in);
300 /* get copies of the buffers */
301 np = add (name, NULL);
302 vp = add (buf, NULL);
304 /* if necessary, get rest of field */
305 while (state == FLDPLUS) {
306 state = m_getfld (state, name, buf, sizeof(buf), in);
307 vp = add (buf, vp); /* add to previous value */
310 /* Now add the header data to the list */
311 add_header (ct, np, vp);
314 /* if this wasn't the last header field, then continue */
320 adios (NULL, "draft has empty body -- no directives!");
325 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
330 adios (NULL, "message format error in component #%d", compnum);
333 adios (NULL, "getfld() returned %d", state);
339 * Now add the MIME-Version header field
340 * to the list of header fields.
342 np = add (VRSN_FIELD, NULL);
343 vp = concat (" ", VRSN_VALUE, "\n", NULL);
344 add_header (ct, np, vp);
347 * We initally assume we will find multiple contents in the
348 * draft. So create a multipart/mixed content to hold everything.
349 * We can remove this later, if it is not needed.
351 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
353 ct->c_type = CT_MULTIPART;
354 ct->c_subtype = MULTI_MIXED;
355 ct->c_file = add (infile, NULL);
357 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
358 adios (NULL, "out of memory");
359 ct->c_ctparams = (void *) m;
363 * read and parse the composition file
364 * and the directives it contains.
366 while (fgetstr (buf, sizeof(buf) - 1, in)) {
370 if (user_content (in, infile, buf, &p) == DONE) {
371 admonish (NULL, "ignoring spurious #end");
377 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
378 adios (NULL, "out of memory");
385 * close the composition draft since
386 * it's not needed any longer.
390 /* check if any contents were found */
392 adios (NULL, "no content directives found");
395 * If only one content was found, then remove and
396 * free the outer multipart content.
398 if (!m->mp_parts->mp_next) {
401 p = m->mp_parts->mp_part;
402 m->mp_parts->mp_part = NULL;
404 /* move header fields */
405 p->c_first_hf = ct->c_first_hf;
406 p->c_last_hf = ct->c_last_hf;
407 ct->c_first_hf = NULL;
408 ct->c_last_hf = NULL;
417 * Fill out, or expand directives. Parse and execute
418 * commands specified by profile composition strings.
420 compose_content (ct);
422 if ((cp = strchr(prefix, 'a')) == NULL)
423 adios (NULL, "internal error(4)");
426 * Scan the contents. Choose a transfer encoding, and
427 * check if prefix for multipart boundary clashes with
428 * any of the contents.
430 while (scan_content (ct) == NOTOK) {
435 adios (NULL, "giving up trying to find a unique delimiter string");
441 /* Build the rest of the header field structures */
449 * Main routine for reading/parsing the headers
450 * of a message content.
452 * toplevel = 1 # we are at the top level of the message
453 * toplevel = 0 # we are inside message type or multipart type
454 * # other than multipart/digest
455 * toplevel = -1 # we are inside multipart/digest
459 get_content (FILE *in, char *file, int toplevel)
462 char buf[BUFSIZ], name[NAMESZ];
465 if (!(ct = (CT) calloc (1, sizeof(*ct))))
466 adios (NULL, "out of memory");
469 ct->c_file = add (file, NULL);
470 ct->c_begin = ftell (ct->c_fp) + 1;
473 * Read the content headers
475 for (compnum = 1, state = FLD;;) {
476 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
482 /* Get MIME-Version field */
483 if (!strcasecmp (name, VRSN_FIELD)) {
487 cp = add (buf, NULL);
488 while (state == FLDPLUS) {
489 state = m_getfld (state, name, buf, sizeof(buf), in);
494 advise (NULL, "message %s has multiple %s: fields (%s)",
495 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
502 while (isspace (*cp))
504 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
506 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
511 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
513 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
516 for (dp = cp; istoken (*dp); dp++)
519 ucmp = !strcasecmp (cp, VRSN_VALUE);
523 "message %s has unknown value for %s: field (%s)",
524 ct->c_file, VRSN_FIELD, cp);
528 /* Get Content-Type field */
529 if (!strcasecmp (name, TYPE_FIELD)) {
531 struct str2init *s2i;
532 CI ci = &ct->c_ctinfo;
534 cp = add (buf, NULL);
535 while (state == FLDPLUS) {
536 state = m_getfld (state, name, buf, sizeof(buf), in);
540 /* Check if we've already seen a Content-Type header */
542 char *dp = trimcpy (cp);
544 advise (NULL, "message %s has multiple %s: fields (%s)",
545 ct->c_file, TYPE_FIELD, dp);
551 /* Parse the Content-Type field */
552 if (get_ctinfo (cp, ct, 0) == NOTOK)
556 * Set the Init function and the internal
557 * flag for this content type.
559 for (s2i = str2cts; s2i->si_key; s2i++)
560 if (!strcasecmp (ci->ci_type, s2i->si_key))
562 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
564 ct->c_type = s2i->si_val;
565 ct->c_ctinitfnx = s2i->si_init;
569 /* Get Content-Transfer-Encoding field */
570 if (!strcasecmp (name, ENCODING_FIELD)) {
573 struct str2init *s2i;
575 cp = add (buf, NULL);
576 while (state == FLDPLUS) {
577 state = m_getfld (state, name, buf, sizeof(buf), in);
582 * Check if we've already seen the
583 * Content-Transfer-Encoding field
586 advise (NULL, "message %s has multiple %s: fields (%s)",
587 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
593 ct->c_celine = cp; /* Save copy of this field */
594 while (isspace (*cp))
596 for (dp = cp; istoken (*dp); dp++)
602 * Find the internal flag and Init function
603 * for this transfer encoding.
605 for (s2i = str2ces; s2i->si_key; s2i++)
606 if (!strcasecmp (cp, s2i->si_key))
608 if (!s2i->si_key && !uprf (cp, "X-"))
611 ct->c_encoding = s2i->si_val;
613 /* Call the Init function for this encoding */
614 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
619 /* Get Content-ID field */
620 if (!strcasecmp (name, ID_FIELD)) {
621 ct->c_id = add (buf, ct->c_id);
622 while (state == FLDPLUS) {
623 state = m_getfld (state, name, buf, sizeof(buf), in);
624 ct->c_id = add (buf, ct->c_id);
629 /* Get Content-Description field */
630 if (!strcasecmp (name, DESCR_FIELD)) {
631 ct->c_descr = add (buf, ct->c_descr);
632 while (state == FLDPLUS) {
633 state = m_getfld (state, name, buf, sizeof(buf), in);
634 ct->c_descr = add (buf, ct->c_descr);
639 /* Get Content-MD5 field */
640 if (!strcasecmp (name, MD5_FIELD)) {
643 cp = add (buf, NULL);
644 while (state == FLDPLUS) {
645 state = m_getfld (state, name, buf, sizeof(buf), in);
654 if (ct->c_digested) {
655 advise (NULL, "message %s has multiple %s: fields (%s)",
656 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
663 while (isspace (*cp))
665 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
667 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
672 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
674 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
679 for (dp = cp; *dp && !isspace (*dp); dp++)
690 if (uprf (name, XXX_FIELD_PRF))
691 advise (NULL, "unknown field (%s) in message %s",
696 while (state == FLDPLUS)
697 state = m_getfld (state, name, buf, sizeof(buf), in);
700 if (state != FLDEOF) {
701 ct->c_begin = ftell (in) + 1;
708 ct->c_begin = ftell (in) - strlen (buf);
712 ct->c_begin = ftell (in);
717 adios (NULL, "message format error in component #%d", compnum);
720 adios (NULL, "getfld() returned %d", state);
726 * Check if we saw a Content-Type field.
727 * If not, then assign a default value for
728 * it, and the Init function.
732 * If we are inside a multipart/digest message,
733 * so default type is message/rfc822
736 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
738 ct->c_type = CT_MESSAGE;
739 ct->c_ctinitfnx = InitMessage;
742 * Else default type is text/plain
744 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
746 ct->c_type = CT_TEXT;
747 ct->c_ctinitfnx = InitText;
751 /* Use default Transfer-Encoding, if necessary */
753 ct->c_encoding = CE_7BIT;
766 * small routine to add header field to list
770 add_header (CT ct, char *name, char *value)
774 /* allocate header field structure */
775 if (!(hp = malloc (sizeof(*hp))))
776 adios (NULL, "out of memory");
778 /* link data into header structure */
783 /* link header structure into the list */
784 if (ct->c_first_hf == NULL) {
785 ct->c_first_hf = hp; /* this is the first */
788 ct->c_last_hf->next = hp; /* add it to the end */
797 * Used to parse both:
798 * 1) Content-Type line
799 * 2) composition directives
801 * and fills in the information of the CTinfo structure.
805 get_ctinfo (char *cp, CT ct, int magic)
808 char *dp, **ap, **ep;
813 i = strlen (invo_name) + 2;
815 /* store copy of Content-Type line */
816 cp = ct->c_ctline = add (cp, NULL);
818 while (isspace (*cp)) /* trim leading spaces */
821 /* change newlines to spaces */
822 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
825 /* trim trailing spaces */
826 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
832 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
834 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
837 for (dp = cp; istoken (*dp); dp++)
840 ci->ci_type = add (cp, NULL); /* store content type */
844 advise (NULL, "invalid %s: field in message %s (empty type)",
845 TYPE_FIELD, ct->c_file);
849 /* down case the content type string */
850 for (dp = ci->ci_type; *dp; dp++)
851 if (isalpha(*dp) && isupper (*dp))
854 while (isspace (*cp))
857 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
862 ci->ci_subtype = add ("", NULL);
867 while (isspace (*cp))
870 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
873 for (dp = cp; istoken (*dp); dp++)
876 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
879 if (!*ci->ci_subtype) {
881 "invalid %s: field in message %s (empty subtype for \"%s\")",
882 TYPE_FIELD, ct->c_file, ci->ci_type);
886 /* down case the content subtype string */
887 for (dp = ci->ci_subtype; *dp; dp++)
888 if (isalpha(*dp) && isupper (*dp))
892 while (isspace (*cp))
895 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
899 * Parse attribute/value pairs given with Content-Type
901 ep = (ap = ci->ci_attrs) + NPARMS;
907 "too many parameters in message %s's %s: field (%d max)",
908 ct->c_file, TYPE_FIELD, NPARMS);
913 while (isspace (*cp))
916 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
921 "extraneous trailing ';' in message %s's %s: parameter list",
922 ct->c_file, TYPE_FIELD);
926 /* down case the attribute name */
927 for (dp = cp; istoken (*dp); dp++)
928 if (isalpha(*dp) && isupper (*dp))
931 for (up = dp; isspace (*dp); )
933 if (dp == cp || *dp != '=') {
935 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
936 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
940 vp = (*ap = add (cp, NULL)) + (up - cp);
942 for (dp++; isspace (*dp); )
945 /* now add the attribute value */
946 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
949 for (cp = ++dp, dp = vp;;) {
954 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
955 ct->c_file, TYPE_FIELD, i, i, "", *ap);
960 if ((c = *cp++) == '\0')
975 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
981 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
982 ct->c_file, TYPE_FIELD, i, i, "", *ap);
987 while (isspace (*cp))
990 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
995 * Get any <Content-Id> given in buffer
997 if (magic && *cp == '<') {
1002 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1003 advise (NULL, "invalid ID in message %s", ct->c_file);
1009 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1015 while (isspace (*cp))
1020 * Get any [Content-Description] given in buffer.
1022 if (magic && *cp == '[') {
1024 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1028 advise (NULL, "invalid description in message %s", ct->c_file);
1036 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1042 while (isspace (*cp))
1047 * Check if anything is left over
1051 ci->ci_magic = add (cp, NULL);
1054 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1055 ct->c_file, TYPE_FIELD, i, i, "", cp);
1063 get_comment (CT ct, char **ap, int istype)
1067 char c, buffer[BUFSIZ], *dp;
1076 switch (c = *cp++) {
1079 advise (NULL, "invalid comment in message %s's %s: field",
1080 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1085 if ((c = *cp++) == '\0')
1108 if ((dp = ci->ci_comment)) {
1109 ci->ci_comment = concat (dp, " ", buffer, NULL);
1112 ci->ci_comment = add (buffer, NULL);
1116 while (isspace (*cp))
1127 * Handles content types audio, image, and video.
1128 * There's not much to do right here.
1134 return OK; /* not much to do here */
1148 CI ci = &ct->c_ctinfo;
1150 /* check for missing subtype */
1151 if (!*ci->ci_subtype)
1152 ci->ci_subtype = add ("plain", ci->ci_subtype);
1155 for (kv = SubText; kv->kv_key; kv++)
1156 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1158 ct->c_subtype = kv->kv_value;
1160 /* allocate text character set structure */
1161 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1162 adios (NULL, "out of memory");
1163 ct->c_ctparams = (void *) t;
1165 /* initially mark character set as unspecified */
1166 t->tx_charset = CHARSET_UNSPECIFIED;
1168 /* scan for charset parameter */
1169 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1170 if (!strcasecmp (*ap, "charset"))
1173 /* check if content specified a character set */
1175 /* match character set or set to CHARSET_UNKNOWN */
1176 for (kv = Charset; kv->kv_key; kv++)
1177 if (!strcasecmp (*ep, kv->kv_key))
1179 t->tx_charset = kv->kv_value;
1191 InitMultiPart (CT ct)
1195 char *cp, *dp, **ap, **ep;
1196 char *bp, buffer[BUFSIZ];
1197 struct multipart *m;
1199 struct part *part, **next;
1200 CI ci = &ct->c_ctinfo;
1205 * The encoding for multipart messages must be either
1206 * 7bit, 8bit, or binary (per RFC2045).
1208 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1209 && ct->c_encoding != CE_BINARY) {
1211 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1212 ci->ci_type, ci->ci_subtype, ct->c_file);
1217 for (kv = SubMultiPart; kv->kv_key; kv++)
1218 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1220 ct->c_subtype = kv->kv_value;
1223 * Check for "boundary" parameter, which is
1224 * required for multipart messages.
1226 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1227 if (!strcasecmp (*ap, "boundary")) {
1233 /* complain if boundary parameter is missing */
1236 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1237 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1241 /* allocate primary structure for multipart info */
1242 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1243 adios (NULL, "out of memory");
1244 ct->c_ctparams = (void *) m;
1246 /* check if boundary parameter contains only whitespace characters */
1247 for (cp = bp; isspace (*cp); cp++)
1250 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1251 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1255 /* remove trailing whitespace from boundary parameter */
1256 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1261 /* record boundary separators */
1262 m->mp_start = concat (bp, "\n", NULL);
1263 m->mp_stop = concat (bp, "--\n", NULL);
1265 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1266 advise (ct->c_file, "unable to open for reading");
1270 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1272 next = &m->mp_parts;
1276 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1280 pos += strlen (buffer);
1281 if (buffer[0] != '-' || buffer[1] != '-')
1284 if (strcmp (buffer + 2, m->mp_start))
1287 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1288 adios (NULL, "out of memory");
1290 next = &part->mp_next;
1292 if (!(p = get_content (fp, ct->c_file,
1293 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1301 fseek (fp, pos, SEEK_SET);
1304 if (strcmp (buffer + 2, m->mp_start) == 0) {
1308 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1309 if (p->c_end < p->c_begin)
1310 p->c_begin = p->c_end;
1315 if (strcmp (buffer + 2, m->mp_stop) == 0)
1321 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1322 if (!inout && part) {
1324 p->c_end = ct->c_end;
1326 if (p->c_begin >= p->c_end) {
1327 for (next = &m->mp_parts; *next != part;
1328 next = &((*next)->mp_next))
1332 free ((char *) part);
1337 /* reverse the order of the parts for multipart/alternative */
1338 if (ct->c_subtype == MULTI_ALTERNATE)
1342 * label all subparts with part number, and
1343 * then initialize the content of the subpart.
1348 char partnam[BUFSIZ];
1351 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1352 pp = partnam + strlen (partnam);
1357 for (part = m->mp_parts, partnum = 1; part;
1358 part = part->mp_next, partnum++) {
1361 sprintf (pp, "%d", partnum);
1362 p->c_partno = add (partnam, NULL);
1364 /* initialize the content of the subparts */
1365 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1380 * reverse the order of the parts of a multipart
1384 reverse_parts (CT ct)
1387 struct multipart *m;
1388 struct part **base, **bmp, **next, *part;
1390 m = (struct multipart *) ct->c_ctparams;
1392 /* if only one part, just return */
1393 if (!m->mp_parts || !m->mp_parts->mp_next)
1396 /* count number of parts */
1398 for (part = m->mp_parts; part; part = part->mp_next)
1401 /* allocate array of pointers to the parts */
1402 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1403 adios (NULL, "out of memory");
1406 /* point at all the parts */
1407 for (part = m->mp_parts; part; part = part->mp_next)
1411 /* reverse the order of the parts */
1412 next = &m->mp_parts;
1413 for (bmp--; bmp >= base; bmp--) {
1416 next = &part->mp_next;
1420 /* free array of pointers */
1421 free ((char *) base);
1433 CI ci = &ct->c_ctinfo;
1435 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1437 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1438 ci->ci_type, ci->ci_subtype, ct->c_file);
1442 /* check for missing subtype */
1443 if (!*ci->ci_subtype)
1444 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1447 for (kv = SubMessage; kv->kv_key; kv++)
1448 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1450 ct->c_subtype = kv->kv_value;
1452 switch (ct->c_subtype) {
1453 case MESSAGE_RFC822:
1456 case MESSAGE_PARTIAL:
1461 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1462 adios (NULL, "out of memory");
1463 ct->c_ctparams = (void *) p;
1465 /* scan for parameters "id", "number", and "total" */
1466 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1467 if (!strcasecmp (*ap, "id")) {
1468 p->pm_partid = add (*ep, NULL);
1471 if (!strcasecmp (*ap, "number")) {
1472 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1473 || p->pm_partno < 1) {
1476 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1477 *ap, ci->ci_type, ci->ci_subtype,
1478 ct->c_file, TYPE_FIELD);
1483 if (!strcasecmp (*ap, "total")) {
1484 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1493 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1495 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1496 ci->ci_type, ci->ci_subtype,
1497 ct->c_file, TYPE_FIELD);
1503 case MESSAGE_EXTERNAL:
1510 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1511 adios (NULL, "out of memory");
1512 ct->c_ctparams = (void *) e;
1515 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1516 advise (ct->c_file, "unable to open for reading");
1520 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1522 if (!(p = get_content (fp, ct->c_file, 0))) {
1531 if ((exresult = params_external (ct, 0)) != NOTOK
1532 && p->c_ceopenfnx == openMail) {
1536 if ((size = ct->c_end - p->c_begin) <= 0) {
1538 content_error (NULL, ct,
1539 "empty body for access-type=mail-server");
1543 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1544 adios (NULL, "out of memory");
1545 fseek (p->c_fp, p->c_begin, SEEK_SET);
1547 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1549 adios ("failed", "fread");
1552 adios (NULL, "unexpected EOF from fread");
1555 bp += cc, size -= cc;
1562 p->c_end = p->c_begin;
1567 if (exresult == NOTOK)
1569 if (e->eb_flags == NOTOK)
1572 switch (p->c_type) {
1577 if (p->c_subtype != MESSAGE_RFC822)
1581 e->eb_partno = ct->c_partno;
1583 (*p->c_ctinitfnx) (p);
1598 params_external (CT ct, int composing)
1601 struct exbody *e = (struct exbody *) ct->c_ctparams;
1602 CI ci = &ct->c_ctinfo;
1604 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1605 if (!strcasecmp (*ap, "access-type")) {
1606 struct str2init *s2i;
1607 CT p = e->eb_content;
1609 for (s2i = str2methods; s2i->si_key; s2i++)
1610 if (!strcasecmp (*ep, s2i->si_key))
1615 e->eb_flags = NOTOK;
1616 p->c_encoding = CE_EXTERNAL;
1619 e->eb_access = s2i->si_key;
1620 e->eb_flags = s2i->si_val;
1621 p->c_encoding = CE_EXTERNAL;
1623 /* Call the Init function for this external type */
1624 if ((*s2i->si_init)(p) == NOTOK)
1628 if (!strcasecmp (*ap, "name")) {
1632 if (!strcasecmp (*ap, "permission")) {
1633 e->eb_permission = *ep;
1636 if (!strcasecmp (*ap, "site")) {
1640 if (!strcasecmp (*ap, "directory")) {
1644 if (!strcasecmp (*ap, "mode")) {
1648 if (!strcasecmp (*ap, "size")) {
1649 sscanf (*ep, "%lu", &e->eb_size);
1652 if (!strcasecmp (*ap, "server")) {
1656 if (!strcasecmp (*ap, "subject")) {
1657 e->eb_subject = *ep;
1660 if (composing && !strcasecmp (*ap, "body")) {
1661 e->eb_body = getcpy (*ep);
1666 if (!e->eb_access) {
1668 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1669 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1682 InitApplication (CT ct)
1685 CI ci = &ct->c_ctinfo;
1688 for (kv = SubApplication; kv->kv_key; kv++)
1689 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1691 ct->c_subtype = kv->kv_value;
1698 * Set up structures for placing unencoded
1699 * content when building parts.
1703 init_decoded_content (CT ct)
1707 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1708 adios (NULL, "out of memory");
1711 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1712 ct->c_ceclosefnx = close_encoding;
1713 ct->c_cesizefnx = NULL; /* since unencoded */
1720 * TRANSFER ENCODINGS
1724 init_encoding (CT ct, OpenCEFunc openfnx)
1728 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1729 adios (NULL, "out of memory");
1732 ct->c_ceopenfnx = openfnx;
1733 ct->c_ceclosefnx = close_encoding;
1734 ct->c_cesizefnx = size_encoding;
1741 close_encoding (CT ct)
1745 if (!(ce = ct->c_cefile))
1755 static unsigned long
1756 size_encoding (CT ct)
1764 if (!(ce = ct->c_cefile))
1765 return (ct->c_end - ct->c_begin);
1767 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1768 return (long) st.st_size;
1771 if (stat (ce->ce_file, &st) != NOTOK)
1772 return (long) st.st_size;
1777 if (ct->c_encoding == CE_EXTERNAL)
1778 return (ct->c_end - ct->c_begin);
1781 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1782 return (ct->c_end - ct->c_begin);
1784 if (fstat (fd, &st) != NOTOK)
1785 size = (long) st.st_size;
1789 (*ct->c_ceclosefnx) (ct);
1798 static unsigned char b642nib[0x80] = {
1799 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1800 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1801 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1802 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1803 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1804 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1805 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1806 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1807 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1808 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1809 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1810 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1811 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1812 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1813 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1814 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1821 return init_encoding (ct, openBase64);
1826 openBase64 (CT ct, char **file)
1828 int bitno, cc, digested;
1831 unsigned char value, *b, *b1, *b2, *b3;
1832 char *cp, *ep, buffer[BUFSIZ];
1836 b = (unsigned char *) &bits;
1837 b1 = &b[endian > 0 ? 1 : 2];
1838 b2 = &b[endian > 0 ? 2 : 1];
1839 b3 = &b[endian > 0 ? 3 : 0];
1843 fseek (ce->ce_fp, 0L, SEEK_SET);
1848 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1849 content_error (ce->ce_file, ct, "unable to fopen for reading");
1855 if (*file == NULL) {
1856 ce->ce_file = add (m_scratch ("", tmp), NULL);
1859 ce->ce_file = add (*file, NULL);
1863 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1864 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1868 if ((len = ct->c_end - ct->c_begin) < 0)
1869 adios (NULL, "internal error(1)");
1871 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1872 content_error (ct->c_file, ct, "unable to open for reading");
1876 if ((digested = ct->c_digested))
1877 MD5Init (&mdContext);
1883 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1885 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1887 content_error (ct->c_file, ct, "error reading from");
1891 content_error (NULL, ct, "premature eof");
1899 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1904 if (skip || (*cp & 0x80)
1905 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1907 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1909 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1912 content_error (NULL, ct,
1913 "invalid BASE64 encoding -- continuing");
1917 bits |= value << bitno;
1919 if ((bitno -= 6) < 0) {
1920 putc ((char) *b1, ce->ce_fp);
1922 MD5Update (&mdContext, b1, 1);
1924 putc ((char) *b2, ce->ce_fp);
1926 MD5Update (&mdContext, b2, 1);
1928 putc ((char) *b3, ce->ce_fp);
1930 MD5Update (&mdContext, b3, 1);
1934 if (ferror (ce->ce_fp)) {
1935 content_error (ce->ce_file, ct,
1936 "error writing to");
1939 bitno = 18, bits = 0L, skip = 0;
1945 goto self_delimiting;
1954 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1956 content_error (NULL, ct, "invalid BASE64 encoding");
1961 fseek (ct->c_fp, 0L, SEEK_SET);
1963 if (fflush (ce->ce_fp)) {
1964 content_error (ce->ce_file, ct, "error writing to");
1969 unsigned char digest[16];
1971 MD5Final (digest, &mdContext);
1972 if (memcmp((char *) digest, (char *) ct->c_digest,
1973 sizeof(digest) / sizeof(digest[0])))
1974 content_error (NULL, ct,
1975 "content integrity suspect (digest mismatch) -- continuing");
1978 fprintf (stderr, "content integrity confirmed\n");
1981 fseek (ce->ce_fp, 0L, SEEK_SET);
1984 *file = ce->ce_file;
1985 return fileno (ce->ce_fp);
1988 free_encoding (ct, 0);
1997 static char hex2nib[0x80] = {
1998 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1999 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2000 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2001 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2002 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2003 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2004 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2005 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2006 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2007 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2008 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2009 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2010 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
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
2020 return init_encoding (ct, openQuoted);
2025 openQuoted (CT ct, char **file)
2027 int cc, digested, len, quoted;
2029 char buffer[BUFSIZ];
2036 fseek (ce->ce_fp, 0L, SEEK_SET);
2041 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2042 content_error (ce->ce_file, ct, "unable to fopen for reading");
2048 if (*file == NULL) {
2049 ce->ce_file = add (m_scratch ("", tmp), NULL);
2052 ce->ce_file = add (*file, NULL);
2056 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2057 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2061 if ((len = ct->c_end - ct->c_begin) < 0)
2062 adios (NULL, "internal error(2)");
2064 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2065 content_error (ct->c_file, ct, "unable to open for reading");
2069 if ((digested = ct->c_digested))
2070 MD5Init (&mdContext);
2077 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2081 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2082 content_error (NULL, ct, "premature eof");
2086 if ((cc = strlen (buffer)) > len)
2090 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2095 for (; cp < ep; cp++) {
2098 if (!isxdigit (*cp)) {
2100 dp = "expecting hexidecimal-digit";
2101 goto invalid_encoding;
2104 mask |= hex2nib[*cp & 0x7f];
2105 putc (mask, ce->ce_fp);
2107 MD5Update (&mdContext, &mask, 1);
2111 putc (*cp, ce->ce_fp);
2113 MD5Update (&mdContext, (unsigned char *) ":", 1);
2117 if (!isxdigit (*cp))
2119 mask = hex2nib[*cp & 0x7f];
2125 if (ferror (ce->ce_fp)) {
2126 content_error (ce->ce_file, ct, "error writing to");
2135 if (*cp < '!' || *cp > '~') {
2137 dp = "expecting character in range [!..~]";
2140 i = strlen (invo_name) + 2;
2141 content_error (NULL, ct,
2142 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2150 putc (*cp, ce->ce_fp);
2153 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2155 MD5Update (&mdContext, (unsigned char *) cp, 1);
2157 if (ferror (ce->ce_fp)) {
2158 content_error (ce->ce_file, ct, "error writing to");
2164 if (*++cp != '\n') {
2173 content_error (NULL, ct,
2174 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2178 fseek (ct->c_fp, 0L, SEEK_SET);
2180 if (fflush (ce->ce_fp)) {
2181 content_error (ce->ce_file, ct, "error writing to");
2186 unsigned char digest[16];
2188 MD5Final (digest, &mdContext);
2189 if (memcmp((char *) digest, (char *) ct->c_digest,
2190 sizeof(digest) / sizeof(digest[0])))
2191 content_error (NULL, ct,
2192 "content integrity suspect (digest mismatch) -- continuing");
2195 fprintf (stderr, "content integrity confirmed\n");
2198 fseek (ce->ce_fp, 0L, SEEK_SET);
2201 *file = ce->ce_file;
2202 return fileno (ce->ce_fp);
2205 free_encoding (ct, 0);
2217 if (init_encoding (ct, open7Bit) == NOTOK)
2220 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2226 open7Bit (CT ct, char **file)
2229 char buffer[BUFSIZ];
2234 fseek (ce->ce_fp, 0L, SEEK_SET);
2239 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2240 content_error (ce->ce_file, ct, "unable to fopen for reading");
2246 if (*file == NULL) {
2247 ce->ce_file = add (m_scratch ("", tmp), NULL);
2250 ce->ce_file = add (*file, NULL);
2254 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2255 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2259 if (ct->c_type == CT_MULTIPART) {
2261 CI ci = &ct->c_ctinfo;
2264 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2265 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2266 + 1 + strlen (ci->ci_subtype);
2267 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2268 putc (';', ce->ce_fp);
2271 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2273 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2274 fputs ("\n\t", ce->ce_fp);
2277 putc (' ', ce->ce_fp);
2280 fprintf (ce->ce_fp, "%s", buffer);
2284 if (ci->ci_comment) {
2285 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2286 fputs ("\n\t", ce->ce_fp);
2290 putc (' ', ce->ce_fp);
2293 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2296 fprintf (ce->ce_fp, "\n");
2298 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2300 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2301 fprintf (ce->ce_fp, "\n");
2304 if ((len = ct->c_end - ct->c_begin) < 0)
2305 adios (NULL, "internal error(3)");
2307 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2308 content_error (ct->c_file, ct, "unable to open for reading");
2312 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2314 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2316 content_error (ct->c_file, ct, "error reading from");
2320 content_error (NULL, ct, "premature eof");
2328 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2329 if (ferror (ce->ce_fp)) {
2330 content_error (ce->ce_file, ct, "error writing to");
2335 fseek (ct->c_fp, 0L, SEEK_SET);
2337 if (fflush (ce->ce_fp)) {
2338 content_error (ce->ce_file, ct, "error writing to");
2342 fseek (ce->ce_fp, 0L, SEEK_SET);
2345 *file = ce->ce_file;
2346 return fileno (ce->ce_fp);
2349 free_encoding (ct, 0);
2359 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2361 char cachefile[BUFSIZ];
2364 fseek (ce->ce_fp, 0L, SEEK_SET);
2369 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2370 content_error (ce->ce_file, ct, "unable to fopen for reading");
2376 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2377 cachefile, sizeof(cachefile)) != NOTOK) {
2378 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2379 ce->ce_file = getcpy (cachefile);
2383 admonish (cachefile, "unable to fopen for reading");
2390 *file = ce->ce_file;
2391 *fd = fileno (ce->ce_fp);
2402 return init_encoding (ct, openFile);
2407 openFile (CT ct, char **file)
2410 char cachefile[BUFSIZ];
2411 struct exbody *e = ct->c_ctexbody;
2412 CE ce = ct->c_cefile;
2414 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2426 content_error (NULL, ct, "missing name parameter");
2430 ce->ce_file = getcpy (e->eb_name);
2433 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2434 content_error (ce->ce_file, ct, "unable to fopen for reading");
2438 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2439 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2440 cachefile, sizeof(cachefile)) != NOTOK) {
2444 mask = umask (cachetype ? ~m_gmprot () : 0222);
2445 if ((fp = fopen (cachefile, "w"))) {
2447 char buffer[BUFSIZ];
2448 FILE *gp = ce->ce_fp;
2450 fseek (gp, 0L, SEEK_SET);
2452 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2454 fwrite (buffer, sizeof(*buffer), cc, fp);
2458 admonish (ce->ce_file, "error reading");
2463 admonish (cachefile, "error writing");
2471 fseek (ce->ce_fp, 0L, SEEK_SET);
2472 *file = ce->ce_file;
2473 return fileno (ce->ce_fp);
2483 return init_encoding (ct, openFTP);
2488 openFTP (CT ct, char **file)
2490 int cachetype, caching, fd;
2492 char *bp, *ftp, *user, *pass;
2493 char buffer[BUFSIZ], cachefile[BUFSIZ];
2496 static char *username = NULL;
2497 static char *password = NULL;
2502 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2510 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2521 if (!e->eb_name || !e->eb_site) {
2522 content_error (NULL, ct, "missing %s parameter",
2523 e->eb_name ? "site": "name");
2530 pidcheck (pidwait (xpid, NOTOK));
2534 /* Get the buffer ready to go */
2536 buflen = sizeof(buffer);
2539 * Construct the query message for user
2541 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2547 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2553 snprintf (bp, buflen, "\n using %sFTP from site %s",
2554 e->eb_flags ? "anonymous " : "", e->eb_site);
2559 if (e->eb_size > 0) {
2560 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2565 snprintf (bp, buflen, "? ");
2568 * Now, check the answer
2570 if (!getanswer (buffer))
2575 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2578 ruserpass (e->eb_site, &username, &password);
2583 ce->ce_unlink = (*file == NULL);
2585 cachefile[0] = '\0';
2586 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2587 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2588 cachefile, sizeof(cachefile)) != NOTOK) {
2589 if (*file == NULL) {
2596 ce->ce_file = add (*file, NULL);
2598 ce->ce_file = add (cachefile, NULL);
2600 ce->ce_file = add (m_scratch ("", tmp), NULL);
2602 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2603 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2611 int child_id, i, vecp;
2615 vec[vecp++] = r1bindex (ftp, '/');
2616 vec[vecp++] = e->eb_site;
2619 vec[vecp++] = e->eb_dir;
2620 vec[vecp++] = e->eb_name;
2621 vec[vecp++] = ce->ce_file,
2622 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2623 ? "ascii" : "binary";
2628 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2632 adios ("fork", "unable to");
2636 close (fileno (ce->ce_fp));
2638 fprintf (stderr, "unable to exec ");
2644 if (pidXwait (child_id, NULL)) {
2648 username = password = NULL;
2657 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2659 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2666 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2671 mask = umask (cachetype ? ~m_gmprot () : 0222);
2672 if ((fp = fopen (cachefile, "w"))) {
2674 FILE *gp = ce->ce_fp;
2676 fseek (gp, 0L, SEEK_SET);
2678 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2680 fwrite (buffer, sizeof(*buffer), cc, fp);
2684 admonish (ce->ce_file, "error reading");
2689 admonish (cachefile, "error writing");
2698 fseek (ce->ce_fp, 0L, SEEK_SET);
2699 *file = ce->ce_file;
2700 return fileno (ce->ce_fp);
2711 return init_encoding (ct, openMail);
2716 openMail (CT ct, char **file)
2718 int child_id, fd, i, vecp;
2720 char *bp, buffer[BUFSIZ], *vec[7];
2721 struct exbody *e = ct->c_ctexbody;
2722 CE ce = ct->c_cefile;
2724 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2735 if (!e->eb_server) {
2736 content_error (NULL, ct, "missing server parameter");
2743 pidcheck (pidwait (xpid, NOTOK));
2747 /* Get buffer ready to go */
2749 buflen = sizeof(buffer);
2751 /* Now construct query message */
2752 snprintf (bp, buflen, "Retrieve content");
2758 snprintf (bp, buflen, " %s", e->eb_partno);
2764 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2766 e->eb_subject ? e->eb_subject : e->eb_body);
2768 /* Now, check answer */
2769 if (!getanswer (buffer))
2773 vec[vecp++] = r1bindex (mailproc, '/');
2774 vec[vecp++] = e->eb_server;
2775 vec[vecp++] = "-subject";
2776 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2777 vec[vecp++] = "-body";
2778 vec[vecp++] = e->eb_body;
2781 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2785 advise ("fork", "unable to");
2789 execvp (mailproc, vec);
2790 fprintf (stderr, "unable to exec ");
2796 if (pidXwait (child_id, NULL) == OK)
2797 advise (NULL, "request sent");
2801 if (*file == NULL) {
2802 ce->ce_file = add (m_scratch ("", tmp), NULL);
2805 ce->ce_file = add (*file, NULL);
2809 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2810 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2814 fseek (ce->ce_fp, 0L, SEEK_SET);
2815 *file = ce->ce_file;
2816 return fileno (ce->ce_fp);
2821 fgetstr (char *s, int n, FILE *stream)
2825 for (ep = (cp = s) + n; cp < ep; ) {
2828 if (!fgets (cp, n, stream))
2829 return (cp != s ? s : NULL);
2830 if (cp == s && *cp != '#')
2833 cp += (i = strlen (cp)) - 1;
2834 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2845 * Parse the composition draft for text and directives.
2846 * Do initial setup of Content structure.
2850 user_content (FILE *in, char *file, char *buf, CT *ctp)
2854 char buffer[BUFSIZ];
2855 struct multipart *m;
2858 struct str2init *s2i;
2863 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2868 /* allocate basic Content structure */
2869 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2870 adios (NULL, "out of memory");
2873 /* allocate basic structure for handling decoded content */
2874 init_decoded_content (ct);
2881 * Handle inline text. Check if line
2882 * is one of the following forms:
2884 * 1) doesn't begin with '#' (implicit directive)
2885 * 2) begins with "##" (implicit directive)
2886 * 3) begins with "#<"
2888 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2892 char content[BUFSIZ];
2895 /* use a temp file to collect the plain text lines */
2896 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2899 if ((out = fopen (ce->ce_file, "w")) == NULL)
2900 adios (ce->ce_file, "unable to open for writing");
2902 if (buf[0] == '#' && buf[1] == '<') {
2903 strncpy (content, buf + 2, sizeof(content));
2910 /* the directive is implicit */
2911 strncpy (content, "text/plain", sizeof(content));
2913 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2917 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2918 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2922 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2923 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2924 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2925 switch (buffer[0]) {
2932 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
2940 if (headers != 1 || buffer[0] != '\n')
2941 fputs (buffer, out);
2946 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
2948 if (buffer[0] == '#') {
2951 if (buffer[1] != '#')
2953 for (cp = (bp = buffer) + 1; *cp; cp++)
2960 ct->c_end = ftell (out);
2963 /* parse content type */
2964 if (get_ctinfo (content, ct, inlineD) == NOTOK)
2967 for (s2i = str2cts; s2i->si_key; s2i++)
2968 if (!strcasecmp (ci->ci_type, s2i->si_key))
2970 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
2974 * check type specified (possibly implicitly)
2976 switch (ct->c_type = s2i->si_val) {
2978 if (!strcasecmp (ci->ci_subtype, "rfc822")) {
2979 ct->c_encoding = CE_7BIT;
2984 adios (NULL, "it doesn't make sense to define an in-line %s content",
2985 ct->c_type == CT_MESSAGE ? "message" : "multipart");
2990 if ((ct->c_ctinitfnx = s2i->si_init))
2991 (*ct->c_ctinitfnx) (ct);
2996 fseek (in, pos, SEEK_SET);
3001 * If we've reached this point, the next line
3002 * must be some type of explicit directive.
3005 /* check if directive is external-type */
3006 extrnal = (buf[1] == '@');
3008 /* parse directive */
3009 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3012 /* check directive against the list of MIME types */
3013 for (s2i = str2cts; s2i->si_key; s2i++)
3014 if (!strcasecmp (ci->ci_type, s2i->si_key))
3018 * Check if the directive specified a valid type.
3019 * This will happen if it was one of the following forms:
3021 * #type/subtype (or)
3025 if (!ci->ci_subtype)
3026 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3028 switch (ct->c_type = s2i->si_val) {
3030 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3031 ci->ci_type, ci->ci_subtype);
3035 if (!strcasecmp (ci->ci_subtype, "partial"))
3036 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3037 ci->ci_type, ci->ci_subtype);
3038 if (!strcasecmp (ci->ci_subtype, "external-body"))
3039 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3040 ci->ci_type, ci->ci_subtype);
3043 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3044 ci->ci_type, ci->ci_subtype);
3048 if ((ct->c_ctinitfnx = s2i->si_init))
3049 (*ct->c_ctinitfnx) (ct);
3054 * #@type/subtype (external types directive)
3061 adios (NULL, "need external information for \"#@%s/%s\"",
3062 ci->ci_type, ci->ci_subtype);
3065 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3066 free (ci->ci_magic);
3067 ci->ci_magic = NULL;
3070 * Since we are using the current Content structure to
3071 * hold information about the type of the external
3072 * reference, we need to create another Content structure
3073 * for the message/external-body to wrap it in.
3075 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3076 adios (NULL, "out of memory");
3079 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3081 ct->c_type = CT_MESSAGE;
3082 ct->c_subtype = MESSAGE_EXTERNAL;
3084 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3085 adios (NULL, "out of memory");
3086 ct->c_ctparams = (void *) e;
3092 if (params_external (ct, 1) == NOTOK)
3098 /* Handle [file] argument */
3100 /* check if specifies command to execute */
3101 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3102 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3105 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3106 cp = add (cp, NULL);
3107 free (ci->ci_magic);
3110 /* record filename of decoded contents */
3111 ce->ce_file = ci->ci_magic;
3112 if (access (ce->ce_file, R_OK) == NOTOK)
3113 adios ("reading", "unable to access %s for", ce->ce_file);
3114 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3115 ct->c_end = (long) st.st_size;
3116 ci->ci_magic = NULL;
3122 * No [file] argument, so check profile for
3123 * method to compose content.
3125 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3126 invo_name, ci->ci_type, ci->ci_subtype);
3127 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3128 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3129 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3130 content_error (NULL, ct, "don't know how to compose content");
3134 ci->ci_magic = add (cp, NULL);
3139 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3143 * #forw [+folder] [msgs]
3145 if (!strcasecmp (ci->ci_type, "forw")) {
3147 char *folder, *arguments[MAXARGS];
3151 ap = brkstring (ci->ci_magic, " ", "\n");
3152 copyip (ap, arguments, MAXARGS);
3154 arguments[0] = "cur";
3155 arguments[1] = NULL;
3159 /* search the arguments for a folder name */
3160 for (ap = arguments; *ap; ap++) {
3162 if (*cp == '+' || *cp == '@') {
3164 adios (NULL, "only one folder per #forw directive");
3166 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3170 /* else, use the current folder */
3172 folder = add (getfolder (1), NULL);
3174 if (!(mp = folder_read (folder)))
3175 adios (NULL, "unable to read folder %s", folder);
3176 for (ap = arguments; *ap; ap++) {
3178 if (*cp != '+' && *cp != '@')
3179 if (!m_convert (mp, cp))
3186 * If there is more than one message to include, make this
3187 * a content of type "multipart/digest" and insert each message
3188 * as a subpart. If there is only one message, then make this
3189 * a content of type "message/rfc822".
3191 if (mp->numsel > 1) {
3192 /* we are forwarding multiple messages */
3193 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3195 ct->c_type = CT_MULTIPART;
3196 ct->c_subtype = MULTI_DIGEST;
3198 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3199 adios (NULL, "out of memory");
3200 ct->c_ctparams = (void *) m;
3203 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3204 if (is_selected(mp, msgnum)) {
3209 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3210 adios (NULL, "out of memory");
3211 init_decoded_content (p);
3213 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3215 p->c_type = CT_MESSAGE;
3216 p->c_subtype = MESSAGE_RFC822;
3218 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3219 pe->ce_file = add (buffer, NULL);
3220 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3221 p->c_end = (long) st.st_size;
3223 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3224 adios (NULL, "out of memory");
3226 pp = &part->mp_next;
3231 /* we are forwarding one message */
3232 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3234 ct->c_type = CT_MESSAGE;
3235 ct->c_subtype = MESSAGE_RFC822;
3237 msgnum = mp->lowsel;
3238 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3239 ce->ce_file = add (buffer, NULL);
3240 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3241 ct->c_end = (long) st.st_size;
3244 folder_free (mp); /* free folder/message structure */
3251 if (!strcasecmp (ci->ci_type, "end")) {
3258 * #begin [ alternative | parallel ]
3260 if (!strcasecmp (ci->ci_type, "begin")) {
3261 if (!ci->ci_magic) {
3263 cp = SubMultiPart[vrsn - 1].kv_key;
3264 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3265 vrsn = MULTI_ALTERNATE;
3266 cp = SubMultiPart[vrsn - 1].kv_key;
3267 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3268 vrsn = MULTI_PARALLEL;
3269 cp = SubMultiPart[vrsn - 1].kv_key;
3270 } else if (uprf (ci->ci_magic, "digest")) {
3273 vrsn = MULTI_UNKNOWN;
3278 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3279 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3281 ct->c_type = CT_MULTIPART;
3282 ct->c_subtype = vrsn;
3284 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3285 adios (NULL, "out of memory");
3286 ct->c_ctparams = (void *) m;
3289 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3293 if (user_content (in, file, buffer, &p) == DONE) {
3295 adios (NULL, "empty \"#begin ... #end\" sequence");
3301 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3302 adios (NULL, "out of memory");
3304 pp = &part->mp_next;
3307 admonish (NULL, "premature end-of-file, missing #end");
3314 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3315 return NOTOK; /* NOT REACHED */
3320 set_id (CT ct, int top)
3324 static time_t clock = 0;
3325 static char *msgfmt;
3329 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3330 (int) getpid(), (long) clock, LocalName());
3332 msgfmt = getcpy(msgid);
3334 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3335 ct->c_id = getcpy (msgid);
3339 static char ebcdicsafe[0x100] = {
3340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3341 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3344 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3345 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3346 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3347 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3348 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3349 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3350 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3351 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3352 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3353 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3354 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3355 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3356 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3358 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3359 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3360 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3361 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3362 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3363 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3364 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3365 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3367 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3368 0x00, 0x00, 0x00, 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
3376 * Fill out, or expand the various contents in the composition
3377 * draft. Read-in any necessary files. Parse and execute any
3378 * commands specified by profile composition strings.
3382 compose_content (CT ct)
3384 CE ce = ct->c_cefile;
3386 switch (ct->c_type) {
3391 char partnam[BUFSIZ];
3392 struct multipart *m = (struct multipart *) ct->c_ctparams;
3396 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3397 pp = partnam + strlen (partnam);
3402 /* first, we call compose_content on all the subparts */
3403 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3404 CT p = part->mp_part;
3406 sprintf (pp, "%d", partnum);
3407 p->c_partno = add (partnam, NULL);
3408 if (compose_content (p) == NOTOK)
3413 * If the -rfc934mode switch is given, then check all
3414 * the subparts of a multipart/digest. If they are all
3415 * message/rfc822, then mark this content and all
3416 * subparts with the rfc934 compatibility mode flag.
3418 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3421 for (part = m->mp_parts; part; part = part->mp_next) {
3422 CT p = part->mp_part;
3424 if (p->c_subtype != MESSAGE_RFC822) {
3429 ct->c_rfc934 = is934;
3430 for (part = m->mp_parts; part; part = part->mp_next) {
3431 CT p = part->mp_part;
3433 if ((p->c_rfc934 = is934))
3439 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3443 for (part = m->mp_parts; part; part = part->mp_next)
3444 ct->c_end += part->mp_part->c_end + partnum;
3450 /* Nothing to do for type message */
3454 * Discrete types (text/application/audio/image/video)
3459 int i, xstdout, len, buflen;
3460 char *bp, **ap, *cp;
3461 char *vec[4], buffer[BUFSIZ];
3463 CI ci = &ct->c_ctinfo;
3465 if (!(cp = ci->ci_magic))
3466 adios (NULL, "internal error(5)");
3468 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3473 /* Get buffer ready to go */
3476 buflen = sizeof(buffer);
3479 * Parse composition string into buffer
3481 for ( ; *cp; cp++) {
3486 /* insert parameters from directive */
3490 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3491 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3501 /* %f, and stdout is not-redirected */
3507 * insert temporary filename where
3508 * content should be written
3510 snprintf (bp, buflen, "%s", ce->ce_file);
3514 /* insert content subtype */
3515 strncpy (bp, ci->ci_subtype, buflen);
3519 /* insert character % */
3540 printf ("composing content %s/%s from command\n\t%s\n",
3541 ci->ci_type, ci->ci_subtype, buffer);
3543 fflush (stdout); /* not sure if need for -noverbose */
3550 if ((out = fopen (ce->ce_file, "w")) == NULL)
3551 adios (ce->ce_file, "unable to open for writing");
3553 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3557 adios ("fork", "unable to fork");
3562 dup2 (fileno (out), 1);
3563 close (fileno (out));
3564 execvp ("/bin/sh", vec);
3565 fprintf (stderr, "unable to exec ");
3572 if (pidXwait(child_id, NULL))
3578 /* Check size of file */
3579 if (listsw && ct->c_end == 0L) {
3582 if (stat (ce->ce_file, &st) != NOTOK)
3583 ct->c_end = (long) st.st_size;
3595 * 1) choose a transfer encoding.
3596 * 2) check for clashes with multipart boundary string.
3597 * 3) for text content, figure out which character set is being used.
3599 * If there is a clash with one of the contents and the multipart boundary,
3600 * this function will exit with NOTOK. This will cause the scanning process
3601 * to be repeated with a different multipart boundary. It is possible
3602 * (although highly unlikely) that this scan will be repeated multiple times.
3606 scan_content (CT ct)
3609 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3610 int checklinelen, linelen = 0; /* check for long lines */
3611 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3612 int checklinespace, linespace = 0; /* check if any line ends with space */
3613 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3614 char *cp, buffer[BUFSIZ];
3617 CE ce = ct->c_cefile;
3620 * handle multipart by scanning all subparts
3621 * and then checking their encoding.
3623 if (ct->c_type == CT_MULTIPART) {
3624 struct multipart *m = (struct multipart *) ct->c_ctparams;
3627 /* initially mark the domain of enclosing multipart as 7bit */
3628 ct->c_encoding = CE_7BIT;
3630 for (part = m->mp_parts; part; part = part->mp_next) {
3631 CT p = part->mp_part;
3633 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3636 /* if necessary, enlarge encoding for enclosing multipart */
3637 if (p->c_encoding == CE_BINARY)
3638 ct->c_encoding = CE_BINARY;
3639 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3640 ct->c_encoding = CE_8BIT;
3647 * Decide what to check while scanning this content.
3649 switch (ct->c_type) {
3653 if (ct->c_subtype == TEXT_PLAIN) {
3658 checkebcdic = ebcdicsw;
3664 case CT_APPLICATION:
3666 checkebcdic = ebcdicsw;
3678 /* don't check anything for message/external */
3679 if (ct->c_subtype == MESSAGE_EXTERNAL)
3689 * Don't check anything for these types,
3690 * since we are forcing use of base64.
3701 * Scan the unencoded content
3703 if (check8bit || checklinelen || checklinespace || checkboundary) {
3704 if ((in = fopen (ce->ce_file, "r")) == NULL)
3705 adios (ce->ce_file, "unable to open for reading");
3706 len = strlen (prefix);
3708 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3710 * Check for 8bit data.
3713 for (cp = buffer; *cp; cp++) {
3714 if (!isascii (*cp)) {
3716 check8bit = 0; /* no need to keep checking */
3719 * Check if character is ebcdic-safe. We only check
3720 * this if also checking for 8bit data.
3722 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3724 checkebcdic = 0; /* no need to keep checking */
3730 * Check line length.
3732 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3734 checklinelen = 0; /* no need to keep checking */
3738 * Check if line ends with a space.
3740 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3742 checklinespace = 0; /* no need to keep checking */
3746 * Check if content contains a line that clashes
3747 * with our standard boundary for multipart messages.
3749 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3750 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3754 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3756 checkboundary = 0; /* no need to keep checking */
3764 * Decide which transfer encoding to use.
3766 switch (ct->c_type) {
3769 * If the text content didn't specify a character
3770 * set, we need to figure out which one was used.
3772 t = (struct text *) ct->c_ctparams;
3773 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3774 CI ci = &ct->c_ctinfo;
3777 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3781 t->tx_charset = CHARSET_UNKNOWN;
3782 *ap = concat ("charset=", write_charset_8bit(), NULL);
3784 t->tx_charset = CHARSET_USASCII;
3785 *ap = add ("charset=us-ascii", NULL);
3788 cp = strchr(*ap++, '=');
3794 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3795 ct->c_encoding = CE_QUOTED;
3797 ct->c_encoding = CE_7BIT;
3800 case CT_APPLICATION:
3801 /* For application type, use base64, except when postscript */
3802 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3803 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3804 ? CE_QUOTED : CE_BASE64;
3806 ct->c_encoding = CE_7BIT;
3810 ct->c_encoding = CE_7BIT;
3816 /* For audio, image, and video contents, just use base64 */
3817 ct->c_encoding = CE_BASE64;
3821 return (boundaryclash ? NOTOK : OK);
3826 * Scan the content structures, and build header
3827 * fields that will need to be output into the
3832 build_headers (CT ct)
3834 int cc, mailbody, len;
3836 char *np, *vp, buffer[BUFSIZ];
3837 CI ci = &ct->c_ctinfo;
3840 * If message is type multipart, then add the multipart
3841 * boundary to the list of attribute/value pairs.
3843 if (ct->c_type == CT_MULTIPART) {
3845 static int level = 0; /* store nesting level */
3849 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3850 cp = strchr(*ap++ = add (buffer, NULL), '=');
3857 * Skip the output of Content-Type, parameters, content
3858 * description, and Content-ID if the content is of type
3859 * "message" and the rfc934 compatibility flag is set
3860 * (which means we are inside multipart/digest and the
3861 * switch -rfc934mode was given).
3863 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3867 * output the content type and subtype
3869 np = add (TYPE_FIELD, NULL);
3870 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3872 /* keep track of length of line */
3873 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3874 + strlen (ci->ci_subtype) + 3;
3876 mailbody = ct->c_type == CT_MESSAGE
3877 && ct->c_subtype == MESSAGE_EXTERNAL
3878 && ((struct exbody *) ct->c_ctparams)->eb_body;
3881 * Append the attribute/value pairs to
3882 * the end of the Content-Type line.
3884 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3885 if (mailbody && !strcasecmp (*ap, "body"))
3891 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3892 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3893 vp = add ("\n\t", vp);
3899 vp = add (buffer, vp);
3904 * Append any RFC-822 comment to the end of
3905 * the Content-Type line.
3907 if (ci->ci_comment) {
3908 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3909 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3910 vp = add ("\n\t", vp);
3916 vp = add (buffer, vp);
3919 vp = add ("\n", vp);
3920 add_header (ct, np, vp);
3923 * output the Content-ID
3926 np = add (ID_FIELD, NULL);
3927 vp = concat (" ", ct->c_id, NULL);
3928 add_header (ct, np, vp);
3932 * output the Content-Description
3935 np = add (DESCR_FIELD, NULL);
3936 vp = concat (" ", ct->c_descr, NULL);
3937 add_header (ct, np, vp);
3942 * If this is the internal content structure for a
3943 * "message/external", then we are done with the
3944 * headers (since it has no body).
3950 * output the Content-MD5
3953 np = add (MD5_FIELD, NULL);
3954 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3955 add_header (ct, np, vp);
3959 * output the Content-Transfer-Encoding
3961 switch (ct->c_encoding) {
3963 /* Nothing to output */
3965 np = add (ENCODING_FIELD, NULL);
3966 vp = concat (" ", "7bit", "\n", NULL);
3967 add_header (ct, np, vp);
3972 if (ct->c_type == CT_MESSAGE)
3973 adios (NULL, "internal error, invalid encoding");
3975 np = add (ENCODING_FIELD, NULL);
3976 vp = concat (" ", "8bit", "\n", NULL);
3977 add_header (ct, np, vp);
3981 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3982 adios (NULL, "internal error, invalid encoding");
3984 np = add (ENCODING_FIELD, NULL);
3985 vp = concat (" ", "quoted-printable", "\n", NULL);
3986 add_header (ct, np, vp);
3990 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3991 adios (NULL, "internal error, invalid encoding");
3993 np = add (ENCODING_FIELD, NULL);
3994 vp = concat (" ", "base64", "\n", NULL);
3995 add_header (ct, np, vp);
3999 if (ct->c_type == CT_MESSAGE)
4000 adios (NULL, "internal error, invalid encoding");
4002 np = add (ENCODING_FIELD, NULL);
4003 vp = concat (" ", "binary", "\n", NULL);
4004 add_header (ct, np, vp);
4008 adios (NULL, "unknown transfer encoding in content");
4013 * Additional content specific header processing
4015 switch (ct->c_type) {
4018 struct multipart *m;
4021 m = (struct multipart *) ct->c_ctparams;
4022 for (part = m->mp_parts; part; part = part->mp_next) {
4032 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4035 e = (struct exbody *) ct->c_ctparams;
4036 build_headers (e->eb_content);
4049 static char nib2b64[0x40+1] =
4050 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4053 calculate_digest (CT ct, int asciiP)
4056 char buffer[BUFSIZ], *vp, *op;
4058 unsigned char digest[16];
4059 unsigned char outbuf[25];
4062 CE ce = ct->c_cefile;
4065 if ((in = fopen (ce->ce_file, "r")) == NULL)
4066 adios (ce->ce_file, "unable to open for reading");
4068 /* Initialize md5 context */
4069 MD5Init (&mdContext);
4071 /* calculate md5 message digest */
4073 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4076 cp = buffer + strlen (buffer) - 1;
4077 if ((c = *cp) == '\n')
4080 MD5Update (&mdContext, (unsigned char *) buffer,
4081 (unsigned int) strlen (buffer));
4084 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4087 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4088 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4091 /* md5 finalization. Write digest and zero md5 context */
4092 MD5Final (digest, &mdContext);
4097 /* print debugging info */
4101 fprintf (stderr, "MD5 digest=");
4102 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4104 fprintf (stderr, "%02x", *dp & 0xff);
4105 fprintf (stderr, "\n");
4108 /* encode the digest using base64 */
4109 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4110 cc > 0; cc -= 3, op += 4) {
4114 bits = (*dp++ & 0xff) << 16;
4116 bits |= (*dp++ & 0xff) << 8;
4118 bits |= *dp++ & 0xff;
4121 for (bp = op + 4; bp > op; bits >>= 6)
4122 *--bp = nib2b64[bits & 0x3f];
4130 /* null terminate string */
4133 /* now make copy and return string */
4134 vp = concat (" ", outbuf, "\n", NULL);
4140 readDigest (CT ct, char *cp)
4145 unsigned char *dp, value, *ep;
4146 unsigned char *b, *b1, *b2, *b3;
4148 b = (unsigned char *) &bits,
4149 b1 = &b[endian > 0 ? 1 : 2],
4150 b2 = &b[endian > 0 ? 2 : 1],
4151 b3 = &b[endian > 0 ? 3 : 0];
4156 for (ep = (dp = ct->c_digest)
4157 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4162 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4164 fprintf (stderr, "invalid BASE64 encoding\n");
4168 bits |= value << bitno;
4170 if ((bitno -= 6) < 0) {
4171 if (dp + (3 - skip) > ep)
4172 goto invalid_digest;
4187 goto self_delimiting;
4192 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4202 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4210 fprintf (stderr, "MD5 digest=");
4211 for (dp = ct->c_digest; dp < ep; dp++)
4212 fprintf (stderr, "%02x", *dp & 0xff);
4213 fprintf (stderr, "\n");