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)
236 * Main routine for translating composition file
237 * into valid MIME message. It translates the draft
238 * into a content structure (actually a tree of content
239 * structures). This message then can be manipulated
240 * in various ways, including being output via
245 build_mime (char *infile)
248 char buf[BUFSIZ], name[NAMESZ];
255 umask (~m_gmprot ());
257 /* open the composition draft */
258 if ((in = fopen (infile, "r")) == NULL)
259 adios (infile, "unable to open for reading");
262 * Allocate space for primary (outside) content
264 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
265 adios (NULL, "out of memory");
268 * Allocate structure for handling decoded content
269 * for this part. We don't really need this, but
270 * allocate it to remain consistent.
272 init_decoded_content (ct);
275 * Parse some of the header fields in the composition
276 * draft into the linked list of header fields for
277 * the new MIME message.
279 for (compnum = 1, state = FLD;;) {
280 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
286 /* abort if draft has Mime-Version header field */
287 if (!strcasecmp (name, VRSN_FIELD))
288 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
290 /* abort if draft has Content-Transfer-Encoding header field */
291 if (!strcasecmp (name, ENCODING_FIELD))
292 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
294 /* ignore any Content-Type fields in the header */
295 if (!strcasecmp (name, TYPE_FIELD)) {
296 while (state == FLDPLUS)
297 state = m_getfld (state, name, buf, sizeof(buf), in);
301 /* get copies of the buffers */
302 np = add (name, NULL);
303 vp = add (buf, NULL);
305 /* if necessary, get rest of field */
306 while (state == FLDPLUS) {
307 state = m_getfld (state, name, buf, sizeof(buf), in);
308 vp = add (buf, vp); /* add to previous value */
311 /* Now add the header data to the list */
312 add_header (ct, np, vp);
315 /* if this wasn't the last header field, then continue */
321 adios (NULL, "draft has empty body -- no directives!");
326 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
331 adios (NULL, "message format error in component #%d", compnum);
334 adios (NULL, "getfld() returned %d", state);
340 * Now add the MIME-Version header field
341 * to the list of header fields.
343 np = add (VRSN_FIELD, NULL);
344 vp = concat (" ", VRSN_VALUE, "\n", NULL);
345 add_header (ct, np, vp);
348 * We initally assume we will find multiple contents in the
349 * draft. So create a multipart/mixed content to hold everything.
350 * We can remove this later, if it is not needed.
352 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
354 ct->c_type = CT_MULTIPART;
355 ct->c_subtype = MULTI_MIXED;
356 ct->c_file = add (infile, NULL);
358 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
359 adios (NULL, "out of memory");
360 ct->c_ctparams = (void *) m;
364 * read and parse the composition file
365 * and the directives it contains.
367 while (fgetstr (buf, sizeof(buf) - 1, in)) {
371 if (user_content (in, infile, buf, &p) == DONE) {
372 admonish (NULL, "ignoring spurious #end");
378 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
379 adios (NULL, "out of memory");
386 * close the composition draft since
387 * it's not needed any longer.
391 /* check if any contents were found */
393 adios (NULL, "no content directives found");
396 * If only one content was found, then remove and
397 * free the outer multipart content.
399 if (!m->mp_parts->mp_next) {
402 p = m->mp_parts->mp_part;
403 m->mp_parts->mp_part = NULL;
405 /* move header fields */
406 p->c_first_hf = ct->c_first_hf;
407 p->c_last_hf = ct->c_last_hf;
408 ct->c_first_hf = NULL;
409 ct->c_last_hf = NULL;
418 * Fill out, or expand directives. Parse and execute
419 * commands specified by profile composition strings.
421 compose_content (ct);
423 if ((cp = strchr(prefix, 'a')) == NULL)
424 adios (NULL, "internal error(4)");
427 * Scan the contents. Choose a transfer encoding, and
428 * check if prefix for multipart boundary clashes with
429 * any of the contents.
431 while (scan_content (ct) == NOTOK) {
436 adios (NULL, "giving up trying to find a unique delimiter string");
442 /* Build the rest of the header field structures */
450 * Main routine for reading/parsing the headers
451 * of a message content.
453 * toplevel = 1 # we are at the top level of the message
454 * toplevel = 0 # we are inside message type or multipart type
455 * # other than multipart/digest
456 * toplevel = -1 # we are inside multipart/digest
460 get_content (FILE *in, char *file, int toplevel)
463 char buf[BUFSIZ], name[NAMESZ];
466 if (!(ct = (CT) calloc (1, sizeof(*ct))))
467 adios (NULL, "out of memory");
470 ct->c_file = add (file, NULL);
471 ct->c_begin = ftell (ct->c_fp) + 1;
474 * Read the content headers
476 for (compnum = 1, state = FLD;;) {
477 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
483 /* Get MIME-Version field */
484 if (!strcasecmp (name, VRSN_FIELD)) {
488 cp = add (buf, NULL);
489 while (state == FLDPLUS) {
490 state = m_getfld (state, name, buf, sizeof(buf), in);
495 advise (NULL, "message %s has multiple %s: fields (%s)",
496 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
503 while (isspace (*cp))
505 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
507 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
512 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
514 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
517 for (dp = cp; istoken (*dp); dp++)
520 ucmp = !strcasecmp (cp, VRSN_VALUE);
524 "message %s has unknown value for %s: field (%s)",
525 ct->c_file, VRSN_FIELD, cp);
529 /* Get Content-Type field */
530 if (!strcasecmp (name, TYPE_FIELD)) {
532 struct str2init *s2i;
533 CI ci = &ct->c_ctinfo;
535 cp = add (buf, NULL);
536 while (state == FLDPLUS) {
537 state = m_getfld (state, name, buf, sizeof(buf), in);
541 /* Check if we've already seen a Content-Type header */
543 char *dp = trimcpy (cp);
545 advise (NULL, "message %s has multiple %s: fields (%s)",
546 ct->c_file, TYPE_FIELD, dp);
552 /* Parse the Content-Type field */
553 if (get_ctinfo (cp, ct, 0) == NOTOK)
557 * Set the Init function and the internal
558 * flag for this content type.
560 for (s2i = str2cts; s2i->si_key; s2i++)
561 if (!strcasecmp (ci->ci_type, s2i->si_key))
563 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
565 ct->c_type = s2i->si_val;
566 ct->c_ctinitfnx = s2i->si_init;
570 /* Get Content-Transfer-Encoding field */
571 if (!strcasecmp (name, ENCODING_FIELD)) {
574 struct str2init *s2i;
576 cp = add (buf, NULL);
577 while (state == FLDPLUS) {
578 state = m_getfld (state, name, buf, sizeof(buf), in);
583 * Check if we've already seen the
584 * Content-Transfer-Encoding field
587 advise (NULL, "message %s has multiple %s: fields (%s)",
588 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
594 ct->c_celine = cp; /* Save copy of this field */
595 while (isspace (*cp))
597 for (dp = cp; istoken (*dp); dp++)
603 * Find the internal flag and Init function
604 * for this transfer encoding.
606 for (s2i = str2ces; s2i->si_key; s2i++)
607 if (!strcasecmp (cp, s2i->si_key))
609 if (!s2i->si_key && !uprf (cp, "X-"))
612 ct->c_encoding = s2i->si_val;
614 /* Call the Init function for this encoding */
615 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
620 /* Get Content-ID field */
621 if (!strcasecmp (name, ID_FIELD)) {
622 ct->c_id = add (buf, ct->c_id);
623 while (state == FLDPLUS) {
624 state = m_getfld (state, name, buf, sizeof(buf), in);
625 ct->c_id = add (buf, ct->c_id);
630 /* Get Content-Description field */
631 if (!strcasecmp (name, DESCR_FIELD)) {
632 ct->c_descr = add (buf, ct->c_descr);
633 while (state == FLDPLUS) {
634 state = m_getfld (state, name, buf, sizeof(buf), in);
635 ct->c_descr = add (buf, ct->c_descr);
640 /* Get Content-MD5 field */
641 if (!strcasecmp (name, MD5_FIELD)) {
644 cp = add (buf, NULL);
645 while (state == FLDPLUS) {
646 state = m_getfld (state, name, buf, sizeof(buf), in);
655 if (ct->c_digested) {
656 advise (NULL, "message %s has multiple %s: fields (%s)",
657 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
664 while (isspace (*cp))
666 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
668 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
673 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
675 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
680 for (dp = cp; *dp && !isspace (*dp); dp++)
691 if (uprf (name, XXX_FIELD_PRF))
692 advise (NULL, "unknown field (%s) in message %s",
697 while (state == FLDPLUS)
698 state = m_getfld (state, name, buf, sizeof(buf), in);
701 if (state != FLDEOF) {
702 ct->c_begin = ftell (in) + 1;
709 ct->c_begin = ftell (in) - strlen (buf);
713 ct->c_begin = ftell (in);
718 adios (NULL, "message format error in component #%d", compnum);
721 adios (NULL, "getfld() returned %d", state);
727 * Check if we saw a Content-Type field.
728 * If not, then assign a default value for
729 * it, and the Init function.
733 * If we are inside a multipart/digest message,
734 * so default type is message/rfc822
737 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
739 ct->c_type = CT_MESSAGE;
740 ct->c_ctinitfnx = InitMessage;
743 * Else default type is text/plain
745 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
747 ct->c_type = CT_TEXT;
748 ct->c_ctinitfnx = InitText;
752 /* Use default Transfer-Encoding, if necessary */
754 ct->c_encoding = CE_7BIT;
767 * small routine to add header field to list
771 add_header (CT ct, char *name, char *value)
775 /* allocate header field structure */
776 if (!(hp = malloc (sizeof(*hp))))
777 adios (NULL, "out of memory");
779 /* link data into header structure */
784 /* link header structure into the list */
785 if (ct->c_first_hf == NULL) {
786 ct->c_first_hf = hp; /* this is the first */
789 ct->c_last_hf->next = hp; /* add it to the end */
798 * Used to parse both:
799 * 1) Content-Type line
800 * 2) composition directives
802 * and fills in the information of the CTinfo structure.
806 get_ctinfo (char *cp, CT ct, int magic)
809 char *dp, **ap, **ep;
814 i = strlen (invo_name) + 2;
816 /* store copy of Content-Type line */
817 cp = ct->c_ctline = add (cp, NULL);
819 while (isspace (*cp)) /* trim leading spaces */
822 /* change newlines to spaces */
823 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
826 /* trim trailing spaces */
827 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
833 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
835 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
838 for (dp = cp; istoken (*dp); dp++)
841 ci->ci_type = add (cp, NULL); /* store content type */
845 advise (NULL, "invalid %s: field in message %s (empty type)",
846 TYPE_FIELD, ct->c_file);
850 /* down case the content type string */
851 for (dp = ci->ci_type; *dp; dp++)
852 if (isalpha(*dp) && isupper (*dp))
855 while (isspace (*cp))
858 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
863 ci->ci_subtype = add ("", NULL);
868 while (isspace (*cp))
871 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
874 for (dp = cp; istoken (*dp); dp++)
877 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
880 if (!*ci->ci_subtype) {
882 "invalid %s: field in message %s (empty subtype for \"%s\")",
883 TYPE_FIELD, ct->c_file, ci->ci_type);
887 /* down case the content subtype string */
888 for (dp = ci->ci_subtype; *dp; dp++)
889 if (isalpha(*dp) && isupper (*dp))
893 while (isspace (*cp))
896 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
900 * Parse attribute/value pairs given with Content-Type
902 ep = (ap = ci->ci_attrs) + NPARMS;
908 "too many parameters in message %s's %s: field (%d max)",
909 ct->c_file, TYPE_FIELD, NPARMS);
914 while (isspace (*cp))
917 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
922 "extraneous trailing ';' in message %s's %s: parameter list",
923 ct->c_file, TYPE_FIELD);
927 /* down case the attribute name */
928 for (dp = cp; istoken (*dp); dp++)
929 if (isalpha(*dp) && isupper (*dp))
932 for (up = dp; isspace (*dp); )
934 if (dp == cp || *dp != '=') {
936 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
937 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
941 vp = (*ap = add (cp, NULL)) + (up - cp);
943 for (dp++; isspace (*dp); )
946 /* now add the attribute value */
947 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
950 for (cp = ++dp, dp = vp;;) {
955 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
956 ct->c_file, TYPE_FIELD, i, i, "", *ap);
961 if ((c = *cp++) == '\0')
976 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
982 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
983 ct->c_file, TYPE_FIELD, i, i, "", *ap);
988 while (isspace (*cp))
991 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
996 * Get any <Content-Id> given in buffer
998 if (magic && *cp == '<') {
1003 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1004 advise (NULL, "invalid ID in message %s", ct->c_file);
1010 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1016 while (isspace (*cp))
1021 * Get any [Content-Description] given in buffer.
1023 if (magic && *cp == '[') {
1025 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1029 advise (NULL, "invalid description in message %s", ct->c_file);
1037 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1043 while (isspace (*cp))
1048 * Check if anything is left over
1052 ci->ci_magic = add (cp, NULL);
1055 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1056 ct->c_file, TYPE_FIELD, i, i, "", cp);
1064 get_comment (CT ct, char **ap, int istype)
1068 char c, buffer[BUFSIZ], *dp;
1077 switch (c = *cp++) {
1080 advise (NULL, "invalid comment in message %s's %s: field",
1081 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1086 if ((c = *cp++) == '\0')
1109 if ((dp = ci->ci_comment)) {
1110 ci->ci_comment = concat (dp, " ", buffer, NULL);
1113 ci->ci_comment = add (buffer, NULL);
1117 while (isspace (*cp))
1128 * Handles content types audio, image, and video.
1129 * There's not much to do right here.
1135 return OK; /* not much to do here */
1149 CI ci = &ct->c_ctinfo;
1151 /* check for missing subtype */
1152 if (!*ci->ci_subtype)
1153 ci->ci_subtype = add ("plain", ci->ci_subtype);
1156 for (kv = SubText; kv->kv_key; kv++)
1157 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1159 ct->c_subtype = kv->kv_value;
1161 /* allocate text character set structure */
1162 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1163 adios (NULL, "out of memory");
1164 ct->c_ctparams = (void *) t;
1166 /* initially mark character set as unspecified */
1167 t->tx_charset = CHARSET_UNSPECIFIED;
1169 /* scan for charset parameter */
1170 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1171 if (!strcasecmp (*ap, "charset"))
1174 /* check if content specified a character set */
1176 /* match character set or set to CHARSET_UNKNOWN */
1177 for (kv = Charset; kv->kv_key; kv++)
1178 if (!strcasecmp (*ep, kv->kv_key))
1180 t->tx_charset = kv->kv_value;
1192 InitMultiPart (CT ct)
1196 char *cp, *dp, **ap, **ep;
1197 char *bp, buffer[BUFSIZ];
1198 struct multipart *m;
1200 struct part *part, **next;
1201 CI ci = &ct->c_ctinfo;
1206 * The encoding for multipart messages must be either
1207 * 7bit, 8bit, or binary (per RFC2045).
1209 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1210 && ct->c_encoding != CE_BINARY) {
1212 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1213 ci->ci_type, ci->ci_subtype, ct->c_file);
1218 for (kv = SubMultiPart; kv->kv_key; kv++)
1219 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1221 ct->c_subtype = kv->kv_value;
1224 * Check for "boundary" parameter, which is
1225 * required for multipart messages.
1227 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1228 if (!strcasecmp (*ap, "boundary")) {
1234 /* complain if boundary parameter is missing */
1237 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1238 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1242 /* allocate primary structure for multipart info */
1243 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1244 adios (NULL, "out of memory");
1245 ct->c_ctparams = (void *) m;
1247 /* check if boundary parameter contains only whitespace characters */
1248 for (cp = bp; isspace (*cp); cp++)
1251 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1252 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1256 /* remove trailing whitespace from boundary parameter */
1257 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1262 /* record boundary separators */
1263 m->mp_start = concat (bp, "\n", NULL);
1264 m->mp_stop = concat (bp, "--\n", NULL);
1266 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1267 advise (ct->c_file, "unable to open for reading");
1271 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1273 next = &m->mp_parts;
1277 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1281 pos += strlen (buffer);
1282 if (buffer[0] != '-' || buffer[1] != '-')
1285 if (strcmp (buffer + 2, m->mp_start))
1288 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1289 adios (NULL, "out of memory");
1291 next = &part->mp_next;
1293 if (!(p = get_content (fp, ct->c_file,
1294 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1302 fseek (fp, pos, SEEK_SET);
1305 if (strcmp (buffer + 2, m->mp_start) == 0) {
1309 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1310 if (p->c_end < p->c_begin)
1311 p->c_begin = p->c_end;
1316 if (strcmp (buffer + 2, m->mp_stop) == 0)
1322 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1323 if (!inout && part) {
1325 p->c_end = ct->c_end;
1327 if (p->c_begin >= p->c_end) {
1328 for (next = &m->mp_parts; *next != part;
1329 next = &((*next)->mp_next))
1333 free ((char *) part);
1338 /* reverse the order of the parts for multipart/alternative */
1339 if (ct->c_subtype == MULTI_ALTERNATE)
1343 * label all subparts with part number, and
1344 * then initialize the content of the subpart.
1349 char partnam[BUFSIZ];
1352 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1353 pp = partnam + strlen (partnam);
1358 for (part = m->mp_parts, partnum = 1; part;
1359 part = part->mp_next, partnum++) {
1362 sprintf (pp, "%d", partnum);
1363 p->c_partno = add (partnam, NULL);
1365 /* initialize the content of the subparts */
1366 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1381 * reverse the order of the parts of a multipart
1385 reverse_parts (CT ct)
1388 struct multipart *m;
1389 struct part **base, **bmp, **next, *part;
1391 m = (struct multipart *) ct->c_ctparams;
1393 /* if only one part, just return */
1394 if (!m->mp_parts || !m->mp_parts->mp_next)
1397 /* count number of parts */
1399 for (part = m->mp_parts; part; part = part->mp_next)
1402 /* allocate array of pointers to the parts */
1403 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1404 adios (NULL, "out of memory");
1407 /* point at all the parts */
1408 for (part = m->mp_parts; part; part = part->mp_next)
1412 /* reverse the order of the parts */
1413 next = &m->mp_parts;
1414 for (bmp--; bmp >= base; bmp--) {
1417 next = &part->mp_next;
1421 /* free array of pointers */
1422 free ((char *) base);
1434 CI ci = &ct->c_ctinfo;
1436 if (ct->c_encoding != CE_7BIT) {
1438 "\"%s/%s\" type in message %s should be encoded in 7bit",
1439 ci->ci_type, ci->ci_subtype, ct->c_file);
1443 /* check for missing subtype */
1444 if (!*ci->ci_subtype)
1445 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1448 for (kv = SubMessage; kv->kv_key; kv++)
1449 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1451 ct->c_subtype = kv->kv_value;
1453 switch (ct->c_subtype) {
1454 case MESSAGE_RFC822:
1457 case MESSAGE_PARTIAL:
1462 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1463 adios (NULL, "out of memory");
1464 ct->c_ctparams = (void *) p;
1466 /* scan for parameters "id", "number", and "total" */
1467 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1468 if (!strcasecmp (*ap, "id")) {
1469 p->pm_partid = add (*ep, NULL);
1472 if (!strcasecmp (*ap, "number")) {
1473 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1474 || p->pm_partno < 1) {
1477 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1478 *ap, ci->ci_type, ci->ci_subtype,
1479 ct->c_file, TYPE_FIELD);
1484 if (!strcasecmp (*ap, "total")) {
1485 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1494 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1496 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1497 ci->ci_type, ci->ci_subtype,
1498 ct->c_file, TYPE_FIELD);
1504 case MESSAGE_EXTERNAL:
1511 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1512 adios (NULL, "out of memory");
1513 ct->c_ctparams = (void *) e;
1516 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1517 advise (ct->c_file, "unable to open for reading");
1521 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1523 if (!(p = get_content (fp, ct->c_file, 0))) {
1532 if ((exresult = params_external (ct, 0)) != NOTOK
1533 && p->c_ceopenfnx == openMail) {
1537 if ((size = ct->c_end - p->c_begin) <= 0) {
1539 content_error (NULL, ct,
1540 "empty body for access-type=mail-server");
1544 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1545 adios (NULL, "out of memory");
1546 fseek (p->c_fp, p->c_begin, SEEK_SET);
1548 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1550 adios ("failed", "fread");
1553 adios (NULL, "unexpected EOF from fread");
1556 bp += cc, size -= cc;
1563 p->c_end = p->c_begin;
1568 if (exresult == NOTOK)
1570 if (e->eb_flags == NOTOK)
1573 switch (p->c_type) {
1578 if (p->c_subtype != MESSAGE_RFC822)
1582 e->eb_partno = ct->c_partno;
1584 (*p->c_ctinitfnx) (p);
1599 params_external (CT ct, int composing)
1602 struct exbody *e = (struct exbody *) ct->c_ctparams;
1603 CI ci = &ct->c_ctinfo;
1605 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1606 if (!strcasecmp (*ap, "access-type")) {
1607 struct str2init *s2i;
1608 CT p = e->eb_content;
1610 for (s2i = str2methods; s2i->si_key; s2i++)
1611 if (!strcasecmp (*ep, s2i->si_key))
1616 e->eb_flags = NOTOK;
1617 p->c_encoding = CE_EXTERNAL;
1620 e->eb_access = s2i->si_key;
1621 e->eb_flags = s2i->si_val;
1622 p->c_encoding = CE_EXTERNAL;
1624 /* Call the Init function for this external type */
1625 if ((*s2i->si_init)(p) == NOTOK)
1629 if (!strcasecmp (*ap, "name")) {
1633 if (!strcasecmp (*ap, "permission")) {
1634 e->eb_permission = *ep;
1637 if (!strcasecmp (*ap, "site")) {
1641 if (!strcasecmp (*ap, "directory")) {
1645 if (!strcasecmp (*ap, "mode")) {
1649 if (!strcasecmp (*ap, "size")) {
1650 sscanf (*ep, "%lu", &e->eb_size);
1653 if (!strcasecmp (*ap, "server")) {
1657 if (!strcasecmp (*ap, "subject")) {
1658 e->eb_subject = *ep;
1661 if (composing && !strcasecmp (*ap, "body")) {
1662 e->eb_body = getcpy (*ep);
1667 if (!e->eb_access) {
1669 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1670 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1683 InitApplication (CT ct)
1686 CI ci = &ct->c_ctinfo;
1689 for (kv = SubApplication; kv->kv_key; kv++)
1690 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1692 ct->c_subtype = kv->kv_value;
1699 * Set up structures for placing unencoded
1700 * content when building parts.
1704 init_decoded_content (CT ct)
1708 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1709 adios (NULL, "out of memory");
1712 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1713 ct->c_ceclosefnx = close_encoding;
1714 ct->c_cesizefnx = NULL; /* since unencoded */
1721 * TRANSFER ENCODINGS
1725 init_encoding (CT ct, OpenCEFunc openfnx)
1729 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1730 adios (NULL, "out of memory");
1733 ct->c_ceopenfnx = openfnx;
1734 ct->c_ceclosefnx = close_encoding;
1735 ct->c_cesizefnx = size_encoding;
1742 close_encoding (CT ct)
1746 if (!(ce = ct->c_cefile))
1756 static unsigned long
1757 size_encoding (CT ct)
1765 if (!(ce = ct->c_cefile))
1766 return (ct->c_end - ct->c_begin);
1768 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1769 return (long) st.st_size;
1772 if (stat (ce->ce_file, &st) != NOTOK)
1773 return (long) st.st_size;
1778 if (ct->c_encoding == CE_EXTERNAL)
1779 return (ct->c_end - ct->c_begin);
1782 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1783 return (ct->c_end - ct->c_begin);
1785 if (fstat (fd, &st) != NOTOK)
1786 size = (long) st.st_size;
1790 (*ct->c_ceclosefnx) (ct);
1799 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1805 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1806 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1807 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1808 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1809 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1810 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1811 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1812 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1813 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1814 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1815 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1822 return init_encoding (ct, openBase64);
1827 openBase64 (CT ct, char **file)
1829 int bitno, cc, digested;
1832 unsigned char value, *b, *b1, *b2, *b3;
1833 char *cp, *ep, buffer[BUFSIZ];
1837 b = (unsigned char *) &bits;
1838 b1 = &b[endian > 0 ? 1 : 2];
1839 b2 = &b[endian > 0 ? 2 : 1];
1840 b3 = &b[endian > 0 ? 3 : 0];
1844 fseek (ce->ce_fp, 0L, SEEK_SET);
1849 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1850 content_error (ce->ce_file, ct, "unable to fopen for reading");
1856 if (*file == NULL) {
1857 ce->ce_file = add (m_scratch ("", tmp), NULL);
1860 ce->ce_file = add (*file, NULL);
1864 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1865 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1869 if ((len = ct->c_end - ct->c_begin) < 0)
1870 adios (NULL, "internal error(1)");
1872 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1873 content_error (ct->c_file, ct, "unable to open for reading");
1877 if ((digested = ct->c_digested))
1878 MD5Init (&mdContext);
1884 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1886 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1888 content_error (ct->c_file, ct, "error reading from");
1892 content_error (NULL, ct, "premature eof");
1900 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1905 if (skip || (*cp & 0x80)
1906 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1908 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1910 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1913 content_error (NULL, ct,
1914 "invalid BASE64 encoding -- continuing");
1918 bits |= value << bitno;
1920 if ((bitno -= 6) < 0) {
1921 putc ((char) *b1, ce->ce_fp);
1923 MD5Update (&mdContext, b1, 1);
1925 putc ((char) *b2, ce->ce_fp);
1927 MD5Update (&mdContext, b2, 1);
1929 putc ((char) *b3, ce->ce_fp);
1931 MD5Update (&mdContext, b3, 1);
1935 if (ferror (ce->ce_fp)) {
1936 content_error (ce->ce_file, ct,
1937 "error writing to");
1940 bitno = 18, bits = 0L, skip = 0;
1946 goto self_delimiting;
1955 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1957 content_error (NULL, ct, "invalid BASE64 encoding");
1962 fseek (ct->c_fp, 0L, SEEK_SET);
1964 if (fflush (ce->ce_fp)) {
1965 content_error (ce->ce_file, ct, "error writing to");
1970 unsigned char digest[16];
1972 MD5Final (digest, &mdContext);
1973 if (memcmp((char *) digest, (char *) ct->c_digest,
1974 sizeof(digest) / sizeof(digest[0])))
1975 content_error (NULL, ct,
1976 "content integrity suspect (digest mismatch) -- continuing");
1979 fprintf (stderr, "content integrity confirmed\n");
1982 fseek (ce->ce_fp, 0L, SEEK_SET);
1985 *file = ce->ce_file;
1986 return fileno (ce->ce_fp);
1989 free_encoding (ct, 0);
1998 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2005 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2006 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2007 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2008 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2009 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2010 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2011 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2012 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2013 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2014 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2021 return init_encoding (ct, openQuoted);
2026 openQuoted (CT ct, char **file)
2028 int cc, digested, len, quoted;
2030 char buffer[BUFSIZ];
2037 fseek (ce->ce_fp, 0L, SEEK_SET);
2042 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2043 content_error (ce->ce_file, ct, "unable to fopen for reading");
2049 if (*file == NULL) {
2050 ce->ce_file = add (m_scratch ("", tmp), NULL);
2053 ce->ce_file = add (*file, NULL);
2057 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2058 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2062 if ((len = ct->c_end - ct->c_begin) < 0)
2063 adios (NULL, "internal error(2)");
2065 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2066 content_error (ct->c_file, ct, "unable to open for reading");
2070 if ((digested = ct->c_digested))
2071 MD5Init (&mdContext);
2078 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2082 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2083 content_error (NULL, ct, "premature eof");
2087 if ((cc = strlen (buffer)) > len)
2091 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2096 for (; cp < ep; cp++) {
2099 if (!isxdigit (*cp)) {
2101 dp = "expecting hexidecimal-digit";
2102 goto invalid_encoding;
2105 mask |= hex2nib[*cp & 0x7f];
2106 putc (mask, ce->ce_fp);
2108 MD5Update (&mdContext, &mask, 1);
2112 putc (*cp, ce->ce_fp);
2114 MD5Update (&mdContext, (unsigned char *) ":", 1);
2118 if (!isxdigit (*cp))
2120 mask = hex2nib[*cp & 0x7f];
2126 if (ferror (ce->ce_fp)) {
2127 content_error (ce->ce_file, ct, "error writing to");
2136 if (*cp < '!' || *cp > '~') {
2138 dp = "expecting character in range [!..~]";
2141 i = strlen (invo_name) + 2;
2142 content_error (NULL, ct,
2143 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2151 putc (*cp, ce->ce_fp);
2154 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2156 MD5Update (&mdContext, (unsigned char *) cp, 1);
2158 if (ferror (ce->ce_fp)) {
2159 content_error (ce->ce_file, ct, "error writing to");
2165 if (*++cp != '\n') {
2174 content_error (NULL, ct,
2175 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2179 fseek (ct->c_fp, 0L, SEEK_SET);
2181 if (fflush (ce->ce_fp)) {
2182 content_error (ce->ce_file, ct, "error writing to");
2187 unsigned char digest[16];
2189 MD5Final (digest, &mdContext);
2190 if (memcmp((char *) digest, (char *) ct->c_digest,
2191 sizeof(digest) / sizeof(digest[0])))
2192 content_error (NULL, ct,
2193 "content integrity suspect (digest mismatch) -- continuing");
2196 fprintf (stderr, "content integrity confirmed\n");
2199 fseek (ce->ce_fp, 0L, SEEK_SET);
2202 *file = ce->ce_file;
2203 return fileno (ce->ce_fp);
2206 free_encoding (ct, 0);
2218 if (init_encoding (ct, open7Bit) == NOTOK)
2221 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2227 open7Bit (CT ct, char **file)
2230 char buffer[BUFSIZ];
2235 fseek (ce->ce_fp, 0L, SEEK_SET);
2240 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2241 content_error (ce->ce_file, ct, "unable to fopen for reading");
2247 if (*file == NULL) {
2248 ce->ce_file = add (m_scratch ("", tmp), NULL);
2251 ce->ce_file = add (*file, NULL);
2255 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2256 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2260 if (ct->c_type == CT_MULTIPART) {
2262 CI ci = &ct->c_ctinfo;
2265 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2266 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2267 + 1 + strlen (ci->ci_subtype);
2268 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2269 putc (';', ce->ce_fp);
2272 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2274 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2275 fputs ("\n\t", ce->ce_fp);
2278 putc (' ', ce->ce_fp);
2281 fprintf (ce->ce_fp, "%s", buffer);
2285 if (ci->ci_comment) {
2286 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2287 fputs ("\n\t", ce->ce_fp);
2291 putc (' ', ce->ce_fp);
2294 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2297 fprintf (ce->ce_fp, "\n");
2299 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2301 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2302 fprintf (ce->ce_fp, "\n");
2305 if ((len = ct->c_end - ct->c_begin) < 0)
2306 adios (NULL, "internal error(3)");
2308 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2309 content_error (ct->c_file, ct, "unable to open for reading");
2313 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2315 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2317 content_error (ct->c_file, ct, "error reading from");
2321 content_error (NULL, ct, "premature eof");
2329 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2330 if (ferror (ce->ce_fp)) {
2331 content_error (ce->ce_file, ct, "error writing to");
2336 fseek (ct->c_fp, 0L, SEEK_SET);
2338 if (fflush (ce->ce_fp)) {
2339 content_error (ce->ce_file, ct, "error writing to");
2343 fseek (ce->ce_fp, 0L, SEEK_SET);
2346 *file = ce->ce_file;
2347 return fileno (ce->ce_fp);
2350 free_encoding (ct, 0);
2360 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2362 char cachefile[BUFSIZ];
2365 fseek (ce->ce_fp, 0L, SEEK_SET);
2370 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2371 content_error (ce->ce_file, ct, "unable to fopen for reading");
2377 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2378 cachefile, sizeof(cachefile)) != NOTOK) {
2379 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2380 ce->ce_file = getcpy (cachefile);
2384 admonish (cachefile, "unable to fopen for reading");
2391 *file = ce->ce_file;
2392 *fd = fileno (ce->ce_fp);
2403 return init_encoding (ct, openFile);
2408 openFile (CT ct, char **file)
2411 char cachefile[BUFSIZ];
2412 struct exbody *e = ct->c_ctexbody;
2413 CE ce = ct->c_cefile;
2415 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2427 content_error (NULL, ct, "missing name parameter");
2431 ce->ce_file = getcpy (e->eb_name);
2434 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2435 content_error (ce->ce_file, ct, "unable to fopen for reading");
2439 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2440 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2441 cachefile, sizeof(cachefile)) != NOTOK) {
2445 mask = umask (cachetype ? ~m_gmprot () : 0222);
2446 if ((fp = fopen (cachefile, "w"))) {
2448 char buffer[BUFSIZ];
2449 FILE *gp = ce->ce_fp;
2451 fseek (gp, 0L, SEEK_SET);
2453 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2455 fwrite (buffer, sizeof(*buffer), cc, fp);
2459 admonish (ce->ce_file, "error reading");
2464 admonish (cachefile, "error writing");
2472 fseek (ce->ce_fp, 0L, SEEK_SET);
2473 *file = ce->ce_file;
2474 return fileno (ce->ce_fp);
2484 return init_encoding (ct, openFTP);
2489 openFTP (CT ct, char **file)
2491 int cachetype, caching, fd;
2493 char *bp, *ftp, *user, *pass;
2494 char buffer[BUFSIZ], cachefile[BUFSIZ];
2497 static char *username = NULL;
2498 static char *password = NULL;
2503 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2511 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2522 if (!e->eb_name || !e->eb_site) {
2523 content_error (NULL, ct, "missing %s parameter",
2524 e->eb_name ? "site": "name");
2531 pidcheck (pidwait (xpid, NOTOK));
2535 /* Get the buffer ready to go */
2537 buflen = sizeof(buffer);
2540 * Construct the query message for user
2542 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2548 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2554 snprintf (bp, buflen, "\n using %sFTP from site %s",
2555 e->eb_flags ? "anonymous " : "", e->eb_site);
2560 if (e->eb_size > 0) {
2561 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2566 snprintf (bp, buflen, "? ");
2569 * Now, check the answer
2571 if (!getanswer (buffer))
2576 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2579 ruserpass (e->eb_site, &username, &password);
2584 ce->ce_unlink = (*file == NULL);
2586 cachefile[0] = '\0';
2587 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2588 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2589 cachefile, sizeof(cachefile)) != NOTOK) {
2590 if (*file == NULL) {
2597 ce->ce_file = add (*file, NULL);
2599 ce->ce_file = add (cachefile, NULL);
2601 ce->ce_file = add (m_scratch ("", tmp), NULL);
2603 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2604 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2612 int child_id, i, vecp;
2616 vec[vecp++] = r1bindex (ftp, '/');
2617 vec[vecp++] = e->eb_site;
2620 vec[vecp++] = e->eb_dir;
2621 vec[vecp++] = e->eb_name;
2622 vec[vecp++] = ce->ce_file,
2623 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2624 ? "ascii" : "binary";
2629 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2633 adios ("fork", "unable to");
2637 close (fileno (ce->ce_fp));
2639 fprintf (stderr, "unable to exec ");
2645 if (pidXwait (child_id, NULL)) {
2649 username = password = NULL;
2658 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2660 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2667 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2672 mask = umask (cachetype ? ~m_gmprot () : 0222);
2673 if ((fp = fopen (cachefile, "w"))) {
2675 FILE *gp = ce->ce_fp;
2677 fseek (gp, 0L, SEEK_SET);
2679 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2681 fwrite (buffer, sizeof(*buffer), cc, fp);
2685 admonish (ce->ce_file, "error reading");
2690 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);
3169 /* else, use the current folder */
3171 folder = add (getfolder (1), NULL);
3173 if (!(mp = folder_read (folder)))
3174 adios (NULL, "unable to read folder %s", folder);
3175 for (ap = arguments; *ap; ap++) {
3177 if (*cp != '+' && *cp != '@')
3178 if (!m_convert (mp, cp))
3185 * If there is more than one message to include, make this
3186 * a content of type "multipart/digest" and insert each message
3187 * as a subpart. If there is only one message, then make this
3188 * a content of type "message/rfc822".
3190 if (mp->numsel > 1) {
3191 /* we are forwarding multiple messages */
3192 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3194 ct->c_type = CT_MULTIPART;
3195 ct->c_subtype = MULTI_DIGEST;
3197 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3198 adios (NULL, "out of memory");
3199 ct->c_ctparams = (void *) m;
3202 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3203 if (is_selected(mp, msgnum)) {
3208 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3209 adios (NULL, "out of memory");
3210 init_decoded_content (p);
3212 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3214 p->c_type = CT_MESSAGE;
3215 p->c_subtype = MESSAGE_RFC822;
3217 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3218 pe->ce_file = add (buffer, NULL);
3219 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3220 p->c_end = (long) st.st_size;
3222 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3223 adios (NULL, "out of memory");
3225 pp = &part->mp_next;
3230 /* we are forwarding one message */
3231 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3233 ct->c_type = CT_MESSAGE;
3234 ct->c_subtype = MESSAGE_RFC822;
3236 msgnum = mp->lowsel;
3237 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3238 ce->ce_file = add (buffer, NULL);
3239 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3240 ct->c_end = (long) st.st_size;
3243 folder_free (mp); /* free folder/message structure */
3250 if (!strcasecmp (ci->ci_type, "end")) {
3257 * #begin [ alternative | parallel ]
3259 if (!strcasecmp (ci->ci_type, "begin")) {
3260 if (!ci->ci_magic) {
3262 cp = SubMultiPart[vrsn - 1].kv_key;
3263 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3264 vrsn = MULTI_ALTERNATE;
3265 cp = SubMultiPart[vrsn - 1].kv_key;
3266 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3267 vrsn = MULTI_PARALLEL;
3268 cp = SubMultiPart[vrsn - 1].kv_key;
3269 } else if (uprf (ci->ci_magic, "digest")) {
3272 vrsn = MULTI_UNKNOWN;
3277 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3278 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3280 ct->c_type = CT_MULTIPART;
3281 ct->c_subtype = vrsn;
3283 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3284 adios (NULL, "out of memory");
3285 ct->c_ctparams = (void *) m;
3288 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3292 if (user_content (in, file, buffer, &p) == DONE) {
3294 adios (NULL, "empty \"#begin ... #end\" sequence");
3300 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3301 adios (NULL, "out of memory");
3303 pp = &part->mp_next;
3306 admonish (NULL, "premature end-of-file, missing #end");
3313 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3314 return NOTOK; /* NOT REACHED */
3319 set_id (CT ct, int top)
3323 static time_t clock = 0;
3324 static char *msgfmt;
3328 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3329 (int) getpid(), (long) clock, LocalName());
3331 msgfmt = getcpy(msgid);
3333 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3334 ct->c_id = getcpy (msgid);
3338 static char ebcdicsafe[0x100] = {
3339 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3340 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3341 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3343 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3344 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3345 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3346 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3347 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3348 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3349 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3350 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3351 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3352 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3353 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3354 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3356 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3357 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
3375 * Fill out, or expand the various contents in the composition
3376 * draft. Read-in any necessary files. Parse and execute any
3377 * commands specified by profile composition strings.
3381 compose_content (CT ct)
3383 CE ce = ct->c_cefile;
3385 switch (ct->c_type) {
3390 char partnam[BUFSIZ];
3391 struct multipart *m = (struct multipart *) ct->c_ctparams;
3395 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3396 pp = partnam + strlen (partnam);
3401 /* first, we call compose_content on all the subparts */
3402 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3403 CT p = part->mp_part;
3405 sprintf (pp, "%d", partnum);
3406 p->c_partno = add (partnam, NULL);
3407 if (compose_content (p) == NOTOK)
3412 * If the -rfc934mode switch is given, then check all
3413 * the subparts of a multipart/digest. If they are all
3414 * message/rfc822, then mark this content and all
3415 * subparts with the rfc934 compatibility mode flag.
3417 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3420 for (part = m->mp_parts; part; part = part->mp_next) {
3421 CT p = part->mp_part;
3423 if (p->c_subtype != MESSAGE_RFC822) {
3428 ct->c_rfc934 = is934;
3429 for (part = m->mp_parts; part; part = part->mp_next) {
3430 CT p = part->mp_part;
3432 if ((p->c_rfc934 = is934))
3438 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3442 for (part = m->mp_parts; part; part = part->mp_next)
3443 ct->c_end += part->mp_part->c_end + partnum;
3449 /* Nothing to do for type message */
3453 * Discrete types (text/application/audio/image/video)
3458 int i, xstdout, len, buflen;
3459 char *bp, **ap, *cp;
3460 char *vec[4], buffer[BUFSIZ];
3462 CI ci = &ct->c_ctinfo;
3464 if (!(cp = ci->ci_magic))
3465 adios (NULL, "internal error(5)");
3467 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3472 /* Get buffer ready to go */
3475 buflen = sizeof(buffer);
3478 * Parse composition string into buffer
3480 for ( ; *cp; cp++) {
3485 /* insert parameters from directive */
3489 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3490 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3500 /* %f, and stdout is not-redirected */
3506 * insert temporary filename where
3507 * content should be written
3509 snprintf (bp, buflen, "%s", ce->ce_file);
3513 /* insert content subtype */
3514 strncpy (bp, ci->ci_subtype, buflen);
3518 /* insert character % */
3539 printf ("composing content %s/%s from command\n\t%s\n",
3540 ci->ci_type, ci->ci_subtype, buffer);
3542 fflush (stdout); /* not sure if need for -noverbose */
3549 if ((out = fopen (ce->ce_file, "w")) == NULL)
3550 adios (ce->ce_file, "unable to open for writing");
3552 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3556 adios ("fork", "unable to fork");
3561 dup2 (fileno (out), 1);
3562 close (fileno (out));
3563 execvp ("/bin/sh", vec);
3564 fprintf (stderr, "unable to exec ");
3571 if (pidXwait(child_id, NULL))
3577 /* Check size of file */
3578 if (listsw && ct->c_end == 0L) {
3581 if (stat (ce->ce_file, &st) != NOTOK)
3582 ct->c_end = (long) st.st_size;
3594 * 1) choose a transfer encoding.
3595 * 2) check for clashes with multipart boundary string.
3596 * 3) for text content, figure out which character set is being used.
3598 * If there is a clash with one of the contents and the multipart boundary,
3599 * this function will exit with NOTOK. This will cause the scanning process
3600 * to be repeated with a different multipart boundary. It is possible
3601 * (although highly unlikely) that this scan will be repeated multiple times.
3605 scan_content (CT ct)
3608 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3609 int checklinelen, linelen = 0; /* check for long lines */
3610 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3611 int checklinespace, linespace = 0; /* check if any line ends with space */
3612 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3613 char *cp, buffer[BUFSIZ];
3616 CE ce = ct->c_cefile;
3619 * handle multipart by scanning all subparts
3620 * and then checking their encoding.
3622 if (ct->c_type == CT_MULTIPART) {
3623 struct multipart *m = (struct multipart *) ct->c_ctparams;
3626 /* initially mark the domain of enclosing multipart as 7bit */
3627 ct->c_encoding = CE_7BIT;
3629 for (part = m->mp_parts; part; part = part->mp_next) {
3630 CT p = part->mp_part;
3632 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3635 /* if necessary, enlarge encoding for enclosing multipart */
3636 if (p->c_encoding == CE_BINARY)
3637 ct->c_encoding = CE_BINARY;
3638 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3639 ct->c_encoding = CE_8BIT;
3646 * Decide what to check while scanning this content.
3648 switch (ct->c_type) {
3652 if (ct->c_subtype == TEXT_PLAIN) {
3657 checkebcdic = ebcdicsw;
3663 case CT_APPLICATION:
3665 checkebcdic = ebcdicsw;
3677 /* don't check anything for message/external */
3678 if (ct->c_subtype == MESSAGE_EXTERNAL)
3688 * Don't check anything for these types,
3689 * since we are forcing use of base64.
3700 * Scan the unencoded content
3702 if (check8bit || checklinelen || checklinespace || checkboundary) {
3703 if ((in = fopen (ce->ce_file, "r")) == NULL)
3704 adios (ce->ce_file, "unable to open for reading");
3705 len = strlen (prefix);
3707 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3709 * Check for 8bit data.
3712 for (cp = buffer; *cp; cp++) {
3713 if (!isascii (*cp)) {
3715 check8bit = 0; /* no need to keep checking */
3718 * Check if character is ebcdic-safe. We only check
3719 * this if also checking for 8bit data.
3721 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3723 checkebcdic = 0; /* no need to keep checking */
3729 * Check line length.
3731 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3733 checklinelen = 0; /* no need to keep checking */
3737 * Check if line ends with a space.
3739 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3741 checklinespace = 0; /* no need to keep checking */
3745 * Check if content contains a line that clashes
3746 * with our standard boundary for multipart messages.
3748 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3749 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3753 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3755 checkboundary = 0; /* no need to keep checking */
3763 * Decide which transfer encoding to use.
3765 switch (ct->c_type) {
3768 * If the text content didn't specify a character
3769 * set, we need to figure out which one was used.
3771 t = (struct text *) ct->c_ctparams;
3772 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3773 CI ci = &ct->c_ctinfo;
3776 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3780 t->tx_charset = CHARSET_UNKNOWN;
3781 *ap = concat ("charset=", write_charset_8bit(), NULL);
3783 t->tx_charset = CHARSET_USASCII;
3784 *ap = add ("charset=us-ascii", NULL);
3787 cp = strchr(*ap++, '=');
3793 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3794 ct->c_encoding = CE_QUOTED;
3796 ct->c_encoding = CE_7BIT;
3799 case CT_APPLICATION:
3800 /* For application type, use base64, except when postscript */
3801 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3802 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3803 ? CE_QUOTED : CE_BASE64;
3805 ct->c_encoding = CE_7BIT;
3809 ct->c_encoding = CE_7BIT;
3815 /* For audio, image, and video contents, just use base64 */
3816 ct->c_encoding = CE_BASE64;
3820 return (boundaryclash ? NOTOK : OK);
3825 * Scan the content structures, and build header
3826 * fields that will need to be output into the
3831 build_headers (CT ct)
3833 int cc, mailbody, len;
3835 char *np, *vp, buffer[BUFSIZ];
3836 CI ci = &ct->c_ctinfo;
3839 * If message is type multipart, then add the multipart
3840 * boundary to the list of attribute/value pairs.
3842 if (ct->c_type == CT_MULTIPART) {
3844 static int level = 0; /* store nesting level */
3848 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3849 cp = strchr(*ap++ = add (buffer, NULL), '=');
3856 * Skip the output of Content-Type, parameters, content
3857 * description, and Content-ID if the content is of type
3858 * "message" and the rfc934 compatibility flag is set
3859 * (which means we are inside multipart/digest and the
3860 * switch -rfc934mode was given).
3862 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3866 * output the content type and subtype
3868 np = add (TYPE_FIELD, NULL);
3869 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3871 /* keep track of length of line */
3872 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3873 + strlen (ci->ci_subtype) + 3;
3875 mailbody = ct->c_type == CT_MESSAGE
3876 && ct->c_subtype == MESSAGE_EXTERNAL
3877 && ((struct exbody *) ct->c_ctparams)->eb_body;
3880 * Append the attribute/value pairs to
3881 * the end of the Content-Type line.
3883 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3884 if (mailbody && !strcasecmp (*ap, "body"))
3890 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3891 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3892 vp = add ("\n\t", vp);
3898 vp = add (buffer, vp);
3903 * Append any RFC-822 comment to the end of
3904 * the Content-Type line.
3906 if (ci->ci_comment) {
3907 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3908 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3909 vp = add ("\n\t", vp);
3915 vp = add (buffer, vp);
3918 vp = add ("\n", vp);
3919 add_header (ct, np, vp);
3922 * output the Content-ID
3925 np = add (ID_FIELD, NULL);
3926 vp = concat (" ", ct->c_id, NULL);
3927 add_header (ct, np, vp);
3931 * output the Content-Description
3934 np = add (DESCR_FIELD, NULL);
3935 vp = concat (" ", ct->c_descr, NULL);
3936 add_header (ct, np, vp);
3941 * If this is the internal content structure for a
3942 * "message/external", then we are done with the
3943 * headers (since it has no body).
3949 * output the Content-MD5
3952 np = add (MD5_FIELD, NULL);
3953 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3954 add_header (ct, np, vp);
3958 * output the Content-Transfer-Encoding
3960 switch (ct->c_encoding) {
3962 /* Nothing to output */
3964 np = add (ENCODING_FIELD, NULL);
3965 vp = concat (" ", "7bit", "\n", NULL);
3966 add_header (ct, np, vp);
3971 if (ct->c_type == CT_MESSAGE)
3972 adios (NULL, "internal error, invalid encoding");
3974 np = add (ENCODING_FIELD, NULL);
3975 vp = concat (" ", "8bit", "\n", NULL);
3976 add_header (ct, np, vp);
3980 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3981 adios (NULL, "internal error, invalid encoding");
3983 np = add (ENCODING_FIELD, NULL);
3984 vp = concat (" ", "quoted-printable", "\n", NULL);
3985 add_header (ct, np, vp);
3989 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3990 adios (NULL, "internal error, invalid encoding");
3992 np = add (ENCODING_FIELD, NULL);
3993 vp = concat (" ", "base64", "\n", NULL);
3994 add_header (ct, np, vp);
3998 if (ct->c_type == CT_MESSAGE)
3999 adios (NULL, "internal error, invalid encoding");
4001 np = add (ENCODING_FIELD, NULL);
4002 vp = concat (" ", "binary", "\n", NULL);
4003 add_header (ct, np, vp);
4007 adios (NULL, "unknown transfer encoding in content");
4012 * Additional content specific header processing
4014 switch (ct->c_type) {
4017 struct multipart *m;
4020 m = (struct multipart *) ct->c_ctparams;
4021 for (part = m->mp_parts; part; part = part->mp_next) {
4031 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4034 e = (struct exbody *) ct->c_ctparams;
4035 build_headers (e->eb_content);
4048 static char nib2b64[0x40+1] =
4049 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4052 calculate_digest (CT ct, int asciiP)
4055 char buffer[BUFSIZ], *vp, *op;
4057 unsigned char digest[16];
4058 unsigned char outbuf[25];
4061 CE ce = ct->c_cefile;
4064 if ((in = fopen (ce->ce_file, "r")) == NULL)
4065 adios (ce->ce_file, "unable to open for reading");
4067 /* Initialize md5 context */
4068 MD5Init (&mdContext);
4070 /* calculate md5 message digest */
4072 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4075 cp = buffer + strlen (buffer) - 1;
4076 if ((c = *cp) == '\n')
4079 MD5Update (&mdContext, (unsigned char *) buffer,
4080 (unsigned int) strlen (buffer));
4083 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4086 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4087 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4090 /* md5 finalization. Write digest and zero md5 context */
4091 MD5Final (digest, &mdContext);
4096 /* print debugging info */
4100 fprintf (stderr, "MD5 digest=");
4101 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4103 fprintf (stderr, "%02x", *dp & 0xff);
4104 fprintf (stderr, "\n");
4107 /* encode the digest using base64 */
4108 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4109 cc > 0; cc -= 3, op += 4) {
4113 bits = (*dp++ & 0xff) << 16;
4115 bits |= (*dp++ & 0xff) << 8;
4117 bits |= *dp++ & 0xff;
4120 for (bp = op + 4; bp > op; bits >>= 6)
4121 *--bp = nib2b64[bits & 0x3f];
4129 /* null terminate string */
4132 /* now make copy and return string */
4133 vp = concat (" ", outbuf, "\n", NULL);
4139 readDigest (CT ct, char *cp)
4144 unsigned char *dp, value, *ep;
4145 unsigned char *b, *b1, *b2, *b3;
4147 b = (unsigned char *) &bits,
4148 b1 = &b[endian > 0 ? 1 : 2],
4149 b2 = &b[endian > 0 ? 2 : 1],
4150 b3 = &b[endian > 0 ? 3 : 0];
4155 for (ep = (dp = ct->c_digest)
4156 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4161 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4163 fprintf (stderr, "invalid BASE64 encoding\n");
4167 bits |= value << bitno;
4169 if ((bitno -= 6) < 0) {
4170 if (dp + (3 - skip) > ep)
4171 goto invalid_digest;
4186 goto self_delimiting;
4191 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4201 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4209 fprintf (stderr, "MD5 digest=");
4210 for (dp = ct->c_digest; dp < ep; dp++)
4211 fprintf (stderr, "%02x", *dp & 0xff);
4212 fprintf (stderr, "\n");