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>
25 #include <h/mhparse.h>
27 #ifdef TIME_WITH_SYS_TIME
28 # include <sys/time.h>
31 # ifdef TM_IN_SYS_TIME
32 # include <sys/time.h>
38 #ifdef HAVE_SYS_WAIT_H
39 # include <sys/wait.h>
52 extern int endian; /* mhmisc.c */
55 extern int rcachesw; /* mhcachesbr.c */
56 extern int wcachesw; /* mhcachesbr.c */
58 int checksw = 0; /* Add Content-MD5 field */
61 * Directory to place tmp files. This must
62 * be set before these routines are called.
68 static char prefix[] = "----- =_aaaaaaaaaa";
71 * Structure for mapping types to their internal flags
79 * Structures for TEXT messages
81 static struct k2v SubText[] = {
82 { "plain", TEXT_PLAIN },
83 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
84 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
85 { NULL, TEXT_UNKNOWN } /* this one must be last! */
88 static struct k2v Charset[] = {
89 { "us-ascii", CHARSET_USASCII },
90 { "iso-8859-1", CHARSET_LATIN },
91 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
95 * Structures for MULTIPART messages
97 static struct k2v SubMultiPart[] = {
98 { "mixed", MULTI_MIXED },
99 { "alternative", MULTI_ALTERNATE },
100 { "digest", MULTI_DIGEST },
101 { "parallel", MULTI_PARALLEL },
102 { NULL, MULTI_UNKNOWN } /* this one must be last! */
106 * Structures for MESSAGE messages
108 static struct k2v SubMessage[] = {
109 { "rfc822", MESSAGE_RFC822 },
110 { "partial", MESSAGE_PARTIAL },
111 { "external-body", MESSAGE_EXTERNAL },
112 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
116 * Structure for APPLICATION messages
118 static struct k2v SubApplication[] = {
119 { "octet-stream", APPLICATION_OCTETS },
120 { "postscript", APPLICATION_POSTSCRIPT },
121 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
126 int make_intermediates (char *);
127 void content_error (char *, CT, char *, ...);
130 int find_cache (CT, int, int *, char *, char *, int);
133 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
136 void free_content (CT);
137 void free_ctinfo (CT);
138 void free_encoding (CT, int);
143 CT build_mime (char *);
149 static CT get_content (FILE *, char *, int);
150 static int add_header (CT, char *, char *);
151 static int get_ctinfo (char *, CT, int);
152 static int get_comment (CT, char **, int);
153 static int InitGeneric (CT);
154 static int InitText (CT);
155 static int InitMultiPart (CT);
156 static void reverse_parts (CT);
157 static int InitMessage (CT);
158 static int params_external (CT, int);
159 static int InitApplication (CT);
160 static int init_decoded_content (CT);
161 static int init_encoding (CT, OpenCEFunc);
162 static void close_encoding (CT);
163 static unsigned long size_encoding (CT);
164 static int InitBase64 (CT);
165 static int openBase64 (CT, char **);
166 static int InitQuoted (CT);
167 static int openQuoted (CT, char **);
168 static int Init7Bit (CT);
169 static int open7Bit (CT, char **);
170 static int openExternal (CT, CT, CE, char **, int *);
171 static int InitFile (CT);
172 static int openFile (CT, char **);
173 static int InitFTP (CT);
174 static int openFTP (CT, char **);
175 static int InitMail (CT);
176 static int openMail (CT, char **);
177 static char *fgetstr (char *, int, FILE *);
178 static int user_content (FILE *, char *, char *, CT *);
179 static void set_id (CT, int);
180 static int compose_content (CT);
181 static int scan_content (CT);
182 static int build_headers (CT);
183 static char *calculate_digest (CT, int);
184 static int readDigest (CT, char *);
187 * Structures for mapping (content) types to
188 * the functions to handle them.
196 static struct str2init str2cts[] = {
197 { "application", CT_APPLICATION, InitApplication },
198 { "audio", CT_AUDIO, InitGeneric },
199 { "image", CT_IMAGE, InitGeneric },
200 { "message", CT_MESSAGE, InitMessage },
201 { "multipart", CT_MULTIPART, InitMultiPart },
202 { "text", CT_TEXT, InitText },
203 { "video", CT_VIDEO, InitGeneric },
204 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
205 { NULL, CT_UNKNOWN, NULL },
208 static struct str2init str2ces[] = {
209 { "base64", CE_BASE64, InitBase64 },
210 { "quoted-printable", CE_QUOTED, InitQuoted },
211 { "8bit", CE_8BIT, Init7Bit },
212 { "7bit", CE_7BIT, Init7Bit },
213 { "binary", CE_BINARY, NULL },
214 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
215 { NULL, CE_UNKNOWN, NULL },
219 * NOTE WELL: si_key MUST NOT have value of NOTOK
221 * si_key is 1 if access method is anonymous.
223 static struct str2init str2methods[] = {
224 { "afs", 1, InitFile },
225 { "anon-ftp", 1, InitFTP },
226 { "ftp", 0, InitFTP },
227 { "local-file", 0, InitFile },
228 { "mail-server", 0, InitMail },
234 pidcheck (int status)
236 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
246 * Main routine for translating composition file
247 * into valid MIME message. It translates the draft
248 * into a content structure (actually a tree of content
249 * structures). This message then can be manipulated
250 * in various ways, including being output via
255 build_mime (char *infile)
258 char buf[BUFSIZ], name[NAMESZ];
265 umask (~m_gmprot ());
267 /* open the composition draft */
268 if ((in = fopen (infile, "r")) == NULL)
269 adios (infile, "unable to open for reading");
272 * Allocate space for primary (outside) content
274 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
275 adios (NULL, "out of memory");
278 * Allocate structure for handling decoded content
279 * for this part. We don't really need this, but
280 * allocate it to remain consistent.
282 init_decoded_content (ct);
285 * Parse some of the header fields in the composition
286 * draft into the linked list of header fields for
287 * the new MIME message.
289 for (compnum = 1, state = FLD;;) {
290 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
296 /* abort if draft has Mime-Version header field */
297 if (!strcasecmp (name, VRSN_FIELD))
298 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
300 /* abort if draft has Content-Transfer-Encoding header field */
301 if (!strcasecmp (name, ENCODING_FIELD))
302 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
304 /* ignore any Content-Type fields in the header */
305 if (!strcasecmp (name, TYPE_FIELD)) {
306 while (state == FLDPLUS)
307 state = m_getfld (state, name, buf, sizeof(buf), in);
311 /* get copies of the buffers */
312 np = add (name, NULL);
313 vp = add (buf, NULL);
315 /* if necessary, get rest of field */
316 while (state == FLDPLUS) {
317 state = m_getfld (state, name, buf, sizeof(buf), in);
318 vp = add (buf, vp); /* add to previous value */
321 /* Now add the header data to the list */
322 add_header (ct, np, vp);
325 /* if this wasn't the last header field, then continue */
331 adios (NULL, "draft has empty body -- no directives!");
336 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
341 adios (NULL, "message format error in component #%d", compnum);
344 adios (NULL, "getfld() returned %d", state);
350 * Now add the MIME-Version header field
351 * to the list of header fields.
353 np = add (VRSN_FIELD, NULL);
354 vp = concat (" ", VRSN_VALUE, "\n", NULL);
355 add_header (ct, np, vp);
358 * We initally assume we will find multiple contents in the
359 * draft. So create a multipart/mixed content to hold everything.
360 * We can remove this later, if it is not needed.
362 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
364 ct->c_type = CT_MULTIPART;
365 ct->c_subtype = MULTI_MIXED;
366 ct->c_file = add (infile, NULL);
368 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
369 adios (NULL, "out of memory");
370 ct->c_ctparams = (void *) m;
374 * read and parse the composition file
375 * and the directives it contains.
377 while (fgetstr (buf, sizeof(buf) - 1, in)) {
381 if (user_content (in, infile, buf, &p) == DONE) {
382 admonish (NULL, "ignoring spurious #end");
388 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
389 adios (NULL, "out of memory");
396 * close the composition draft since
397 * it's not needed any longer.
401 /* check if any contents were found */
403 adios (NULL, "no content directives found");
406 * If only one content was found, then remove and
407 * free the outer multipart content.
409 if (!m->mp_parts->mp_next) {
412 p = m->mp_parts->mp_part;
413 m->mp_parts->mp_part = NULL;
415 /* move header fields */
416 p->c_first_hf = ct->c_first_hf;
417 p->c_last_hf = ct->c_last_hf;
418 ct->c_first_hf = NULL;
419 ct->c_last_hf = NULL;
428 * Fill out, or expand directives. Parse and execute
429 * commands specified by profile composition strings.
431 compose_content (ct);
433 if ((cp = strchr(prefix, 'a')) == NULL)
434 adios (NULL, "internal error(4)");
437 * Scan the contents. Choose a transfer encoding, and
438 * check if prefix for multipart boundary clashes with
439 * any of the contents.
441 while (scan_content (ct) == NOTOK) {
446 adios (NULL, "giving up trying to find a unique delimiter string");
452 /* Build the rest of the header field structures */
460 * Main routine for reading/parsing the headers
461 * of a message content.
463 * toplevel = 1 # we are at the top level of the message
464 * toplevel = 0 # we are inside message type or multipart type
465 * # other than multipart/digest
466 * toplevel = -1 # we are inside multipart/digest
470 get_content (FILE *in, char *file, int toplevel)
473 char buf[BUFSIZ], name[NAMESZ];
476 if (!(ct = (CT) calloc (1, sizeof(*ct))))
477 adios (NULL, "out of memory");
480 ct->c_file = add (file, NULL);
481 ct->c_begin = ftell (ct->c_fp) + 1;
484 * Read the content headers
486 for (compnum = 1, state = FLD;;) {
487 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
493 /* Get MIME-Version field */
494 if (!strcasecmp (name, VRSN_FIELD)) {
498 cp = add (buf, NULL);
499 while (state == FLDPLUS) {
500 state = m_getfld (state, name, buf, sizeof(buf), in);
505 advise (NULL, "message %s has multiple %s: fields (%s)",
506 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
513 while (isspace (*cp))
515 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
517 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
522 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
524 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
527 for (dp = cp; istoken (*dp); dp++)
530 ucmp = !strcasecmp (cp, VRSN_VALUE);
534 "message %s has unknown value for %s: field (%s)",
535 ct->c_file, VRSN_FIELD, cp);
539 /* Get Content-Type field */
540 if (!strcasecmp (name, TYPE_FIELD)) {
542 struct str2init *s2i;
543 CI ci = &ct->c_ctinfo;
545 cp = add (buf, NULL);
546 while (state == FLDPLUS) {
547 state = m_getfld (state, name, buf, sizeof(buf), in);
551 /* Check if we've already seen a Content-Type header */
553 char *dp = trimcpy (cp);
555 advise (NULL, "message %s has multiple %s: fields (%s)",
556 ct->c_file, TYPE_FIELD, dp);
562 /* Parse the Content-Type field */
563 if (get_ctinfo (cp, ct, 0) == NOTOK)
567 * Set the Init function and the internal
568 * flag for this content type.
570 for (s2i = str2cts; s2i->si_key; s2i++)
571 if (!strcasecmp (ci->ci_type, s2i->si_key))
573 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
575 ct->c_type = s2i->si_val;
576 ct->c_ctinitfnx = s2i->si_init;
580 /* Get Content-Transfer-Encoding field */
581 if (!strcasecmp (name, ENCODING_FIELD)) {
584 struct str2init *s2i;
586 cp = add (buf, NULL);
587 while (state == FLDPLUS) {
588 state = m_getfld (state, name, buf, sizeof(buf), in);
593 * Check if we've already seen the
594 * Content-Transfer-Encoding field
597 advise (NULL, "message %s has multiple %s: fields (%s)",
598 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
604 ct->c_celine = cp; /* Save copy of this field */
605 while (isspace (*cp))
607 for (dp = cp; istoken (*dp); dp++)
613 * Find the internal flag and Init function
614 * for this transfer encoding.
616 for (s2i = str2ces; s2i->si_key; s2i++)
617 if (!strcasecmp (cp, s2i->si_key))
619 if (!s2i->si_key && !uprf (cp, "X-"))
622 ct->c_encoding = s2i->si_val;
624 /* Call the Init function for this encoding */
625 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
630 /* Get Content-ID field */
631 if (!strcasecmp (name, ID_FIELD)) {
632 ct->c_id = add (buf, ct->c_id);
633 while (state == FLDPLUS) {
634 state = m_getfld (state, name, buf, sizeof(buf), in);
635 ct->c_id = add (buf, ct->c_id);
640 /* Get Content-Description field */
641 if (!strcasecmp (name, DESCR_FIELD)) {
642 ct->c_descr = add (buf, ct->c_descr);
643 while (state == FLDPLUS) {
644 state = m_getfld (state, name, buf, sizeof(buf), in);
645 ct->c_descr = add (buf, ct->c_descr);
650 /* Get Content-MD5 field */
651 if (!strcasecmp (name, MD5_FIELD)) {
654 cp = add (buf, NULL);
655 while (state == FLDPLUS) {
656 state = m_getfld (state, name, buf, sizeof(buf), in);
665 if (ct->c_digested) {
666 advise (NULL, "message %s has multiple %s: fields (%s)",
667 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
674 while (isspace (*cp))
676 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
678 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
683 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
685 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
690 for (dp = cp; *dp && !isspace (*dp); dp++)
701 if (uprf (name, XXX_FIELD_PRF))
702 advise (NULL, "unknown field (%s) in message %s",
707 while (state == FLDPLUS)
708 state = m_getfld (state, name, buf, sizeof(buf), in);
711 if (state != FLDEOF) {
712 ct->c_begin = ftell (in) + 1;
719 ct->c_begin = ftell (in) - strlen (buf);
723 ct->c_begin = ftell (in);
728 adios (NULL, "message format error in component #%d", compnum);
731 adios (NULL, "getfld() returned %d", state);
737 * Check if we saw a Content-Type field.
738 * If not, then assign a default value for
739 * it, and the Init function.
743 * If we are inside a multipart/digest message,
744 * so default type is message/rfc822
747 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
749 ct->c_type = CT_MESSAGE;
750 ct->c_ctinitfnx = InitMessage;
753 * Else default type is text/plain
755 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
757 ct->c_type = CT_TEXT;
758 ct->c_ctinitfnx = InitText;
762 /* Use default Transfer-Encoding, if necessary */
764 ct->c_encoding = CE_7BIT;
777 * small routine to add header field to list
781 add_header (CT ct, char *name, char *value)
785 /* allocate header field structure */
786 if (!(hp = malloc (sizeof(*hp))))
787 adios (NULL, "out of memory");
789 /* link data into header structure */
794 /* link header structure into the list */
795 if (ct->c_first_hf == NULL) {
796 ct->c_first_hf = hp; /* this is the first */
799 ct->c_last_hf->next = hp; /* add it to the end */
808 * Used to parse both:
809 * 1) Content-Type line
810 * 2) composition directives
812 * and fills in the information of the CTinfo structure.
816 get_ctinfo (char *cp, CT ct, int magic)
819 char *dp, **ap, **ep;
824 i = strlen (invo_name) + 2;
826 /* store copy of Content-Type line */
827 cp = ct->c_ctline = add (cp, NULL);
829 while (isspace (*cp)) /* trim leading spaces */
832 /* change newlines to spaces */
833 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
836 /* trim trailing spaces */
837 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
843 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
845 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
848 for (dp = cp; istoken (*dp); dp++)
851 ci->ci_type = add (cp, NULL); /* store content type */
855 advise (NULL, "invalid %s: field in message %s (empty type)",
856 TYPE_FIELD, ct->c_file);
860 /* down case the content type string */
861 for (dp = ci->ci_type; *dp; dp++)
862 if (isalpha(*dp) && isupper (*dp))
865 while (isspace (*cp))
868 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
873 ci->ci_subtype = add ("", NULL);
878 while (isspace (*cp))
881 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
884 for (dp = cp; istoken (*dp); dp++)
887 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
890 if (!*ci->ci_subtype) {
892 "invalid %s: field in message %s (empty subtype for \"%s\")",
893 TYPE_FIELD, ct->c_file, ci->ci_type);
897 /* down case the content subtype string */
898 for (dp = ci->ci_subtype; *dp; dp++)
899 if (isalpha(*dp) && isupper (*dp))
903 while (isspace (*cp))
906 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
910 * Parse attribute/value pairs given with Content-Type
912 ep = (ap = ci->ci_attrs) + NPARMS;
918 "too many parameters in message %s's %s: field (%d max)",
919 ct->c_file, TYPE_FIELD, NPARMS);
924 while (isspace (*cp))
927 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
932 "extraneous trailing ';' in message %s's %s: parameter list",
933 ct->c_file, TYPE_FIELD);
937 /* down case the attribute name */
938 for (dp = cp; istoken (*dp); dp++)
939 if (isalpha(*dp) && isupper (*dp))
942 for (up = dp; isspace (*dp); )
944 if (dp == cp || *dp != '=') {
946 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
947 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
951 vp = (*ap = add (cp, NULL)) + (up - cp);
953 for (dp++; isspace (*dp); )
956 /* now add the attribute value */
957 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
960 for (cp = ++dp, dp = vp;;) {
965 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
966 ct->c_file, TYPE_FIELD, i, i, "", *ap);
971 if ((c = *cp++) == '\0')
986 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
992 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
993 ct->c_file, TYPE_FIELD, i, i, "", *ap);
998 while (isspace (*cp))
1001 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1006 * Get any <Content-Id> given in buffer
1008 if (magic && *cp == '<') {
1013 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1014 advise (NULL, "invalid ID in message %s", ct->c_file);
1020 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1026 while (isspace (*cp))
1031 * Get any [Content-Description] given in buffer.
1033 if (magic && *cp == '[') {
1035 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1039 advise (NULL, "invalid description in message %s", ct->c_file);
1047 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1053 while (isspace (*cp))
1058 * Check if anything is left over
1062 ci->ci_magic = add (cp, NULL);
1065 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1066 ct->c_file, TYPE_FIELD, i, i, "", cp);
1074 get_comment (CT ct, char **ap, int istype)
1078 char c, buffer[BUFSIZ], *dp;
1087 switch (c = *cp++) {
1090 advise (NULL, "invalid comment in message %s's %s: field",
1091 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1096 if ((c = *cp++) == '\0')
1119 if ((dp = ci->ci_comment)) {
1120 ci->ci_comment = concat (dp, " ", buffer, NULL);
1123 ci->ci_comment = add (buffer, NULL);
1127 while (isspace (*cp))
1138 * Handles content types audio, image, and video.
1139 * There's not much to do right here.
1145 return OK; /* not much to do here */
1159 CI ci = &ct->c_ctinfo;
1161 /* check for missing subtype */
1162 if (!*ci->ci_subtype)
1163 ci->ci_subtype = add ("plain", ci->ci_subtype);
1166 for (kv = SubText; kv->kv_key; kv++)
1167 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1169 ct->c_subtype = kv->kv_value;
1171 /* allocate text character set structure */
1172 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1173 adios (NULL, "out of memory");
1174 ct->c_ctparams = (void *) t;
1176 /* initially mark character set as unspecified */
1177 t->tx_charset = CHARSET_UNSPECIFIED;
1179 /* scan for charset parameter */
1180 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1181 if (!strcasecmp (*ap, "charset"))
1184 /* check if content specified a character set */
1186 /* match character set or set to CHARSET_UNKNOWN */
1187 for (kv = Charset; kv->kv_key; kv++)
1188 if (!strcasecmp (*ep, kv->kv_key))
1190 t->tx_charset = kv->kv_value;
1202 InitMultiPart (CT ct)
1206 char *cp, *dp, **ap, **ep;
1207 char *bp, buffer[BUFSIZ];
1208 struct multipart *m;
1210 struct part *part, **next;
1211 CI ci = &ct->c_ctinfo;
1216 * The encoding for multipart messages must be either
1217 * 7bit, 8bit, or binary (per RFC2045).
1219 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1220 && ct->c_encoding != CE_BINARY) {
1222 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1223 ci->ci_type, ci->ci_subtype, ct->c_file);
1228 for (kv = SubMultiPart; kv->kv_key; kv++)
1229 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1231 ct->c_subtype = kv->kv_value;
1234 * Check for "boundary" parameter, which is
1235 * required for multipart messages.
1237 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1238 if (!strcasecmp (*ap, "boundary")) {
1244 /* complain if boundary parameter is missing */
1247 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1248 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1252 /* allocate primary structure for multipart info */
1253 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1254 adios (NULL, "out of memory");
1255 ct->c_ctparams = (void *) m;
1257 /* check if boundary parameter contains only whitespace characters */
1258 for (cp = bp; isspace (*cp); cp++)
1261 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1262 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1266 /* remove trailing whitespace from boundary parameter */
1267 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1272 /* record boundary separators */
1273 m->mp_start = concat (bp, "\n", NULL);
1274 m->mp_stop = concat (bp, "--\n", NULL);
1276 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1277 advise (ct->c_file, "unable to open for reading");
1281 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1283 next = &m->mp_parts;
1287 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1291 pos += strlen (buffer);
1292 if (buffer[0] != '-' || buffer[1] != '-')
1295 if (strcmp (buffer + 2, m->mp_start))
1298 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1299 adios (NULL, "out of memory");
1301 next = &part->mp_next;
1303 if (!(p = get_content (fp, ct->c_file,
1304 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1312 fseek (fp, pos, SEEK_SET);
1315 if (strcmp (buffer + 2, m->mp_start) == 0) {
1319 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1320 if (p->c_end < p->c_begin)
1321 p->c_begin = p->c_end;
1326 if (strcmp (buffer + 2, m->mp_stop) == 0)
1332 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1333 if (!inout && part) {
1335 p->c_end = ct->c_end;
1337 if (p->c_begin >= p->c_end) {
1338 for (next = &m->mp_parts; *next != part;
1339 next = &((*next)->mp_next))
1343 free ((char *) part);
1348 /* reverse the order of the parts for multipart/alternative */
1349 if (ct->c_subtype == MULTI_ALTERNATE)
1353 * label all subparts with part number, and
1354 * then initialize the content of the subpart.
1359 char partnam[BUFSIZ];
1362 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1363 pp = partnam + strlen (partnam);
1368 for (part = m->mp_parts, partnum = 1; part;
1369 part = part->mp_next, partnum++) {
1372 sprintf (pp, "%d", partnum);
1373 p->c_partno = add (partnam, NULL);
1375 /* initialize the content of the subparts */
1376 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1391 * reverse the order of the parts of a multipart
1395 reverse_parts (CT ct)
1398 struct multipart *m;
1399 struct part **base, **bmp, **next, *part;
1401 m = (struct multipart *) ct->c_ctparams;
1403 /* if only one part, just return */
1404 if (!m->mp_parts || !m->mp_parts->mp_next)
1407 /* count number of parts */
1409 for (part = m->mp_parts; part; part = part->mp_next)
1412 /* allocate array of pointers to the parts */
1413 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1414 adios (NULL, "out of memory");
1417 /* point at all the parts */
1418 for (part = m->mp_parts; part; part = part->mp_next)
1422 /* reverse the order of the parts */
1423 next = &m->mp_parts;
1424 for (bmp--; bmp >= base; bmp--) {
1427 next = &part->mp_next;
1431 /* free array of pointers */
1432 free ((char *) base);
1444 CI ci = &ct->c_ctinfo;
1446 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1448 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1449 ci->ci_type, ci->ci_subtype, ct->c_file);
1453 /* check for missing subtype */
1454 if (!*ci->ci_subtype)
1455 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1458 for (kv = SubMessage; kv->kv_key; kv++)
1459 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1461 ct->c_subtype = kv->kv_value;
1463 switch (ct->c_subtype) {
1464 case MESSAGE_RFC822:
1467 case MESSAGE_PARTIAL:
1472 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1473 adios (NULL, "out of memory");
1474 ct->c_ctparams = (void *) p;
1476 /* scan for parameters "id", "number", and "total" */
1477 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1478 if (!strcasecmp (*ap, "id")) {
1479 p->pm_partid = add (*ep, NULL);
1482 if (!strcasecmp (*ap, "number")) {
1483 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1484 || p->pm_partno < 1) {
1487 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1488 *ap, ci->ci_type, ci->ci_subtype,
1489 ct->c_file, TYPE_FIELD);
1494 if (!strcasecmp (*ap, "total")) {
1495 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1504 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1506 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1507 ci->ci_type, ci->ci_subtype,
1508 ct->c_file, TYPE_FIELD);
1514 case MESSAGE_EXTERNAL:
1521 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1522 adios (NULL, "out of memory");
1523 ct->c_ctparams = (void *) e;
1526 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1527 advise (ct->c_file, "unable to open for reading");
1531 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1533 if (!(p = get_content (fp, ct->c_file, 0))) {
1542 if ((exresult = params_external (ct, 0)) != NOTOK
1543 && p->c_ceopenfnx == openMail) {
1547 if ((size = ct->c_end - p->c_begin) <= 0) {
1549 content_error (NULL, ct,
1550 "empty body for access-type=mail-server");
1554 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1555 adios (NULL, "out of memory");
1556 fseek (p->c_fp, p->c_begin, SEEK_SET);
1558 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1560 adios ("failed", "fread");
1563 adios (NULL, "unexpected EOF from fread");
1566 bp += cc, size -= cc;
1573 p->c_end = p->c_begin;
1578 if (exresult == NOTOK)
1580 if (e->eb_flags == NOTOK)
1583 switch (p->c_type) {
1588 if (p->c_subtype != MESSAGE_RFC822)
1592 e->eb_partno = ct->c_partno;
1594 (*p->c_ctinitfnx) (p);
1609 params_external (CT ct, int composing)
1612 struct exbody *e = (struct exbody *) ct->c_ctparams;
1613 CI ci = &ct->c_ctinfo;
1615 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1616 if (!strcasecmp (*ap, "access-type")) {
1617 struct str2init *s2i;
1618 CT p = e->eb_content;
1620 for (s2i = str2methods; s2i->si_key; s2i++)
1621 if (!strcasecmp (*ep, s2i->si_key))
1626 e->eb_flags = NOTOK;
1627 p->c_encoding = CE_EXTERNAL;
1630 e->eb_access = s2i->si_key;
1631 e->eb_flags = s2i->si_val;
1632 p->c_encoding = CE_EXTERNAL;
1634 /* Call the Init function for this external type */
1635 if ((*s2i->si_init)(p) == NOTOK)
1639 if (!strcasecmp (*ap, "name")) {
1643 if (!strcasecmp (*ap, "permission")) {
1644 e->eb_permission = *ep;
1647 if (!strcasecmp (*ap, "site")) {
1651 if (!strcasecmp (*ap, "directory")) {
1655 if (!strcasecmp (*ap, "mode")) {
1659 if (!strcasecmp (*ap, "size")) {
1660 sscanf (*ep, "%lu", &e->eb_size);
1663 if (!strcasecmp (*ap, "server")) {
1667 if (!strcasecmp (*ap, "subject")) {
1668 e->eb_subject = *ep;
1671 if (composing && !strcasecmp (*ap, "body")) {
1672 e->eb_body = getcpy (*ep);
1677 if (!e->eb_access) {
1679 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1680 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1693 InitApplication (CT ct)
1696 CI ci = &ct->c_ctinfo;
1699 for (kv = SubApplication; kv->kv_key; kv++)
1700 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1702 ct->c_subtype = kv->kv_value;
1709 * Set up structures for placing unencoded
1710 * content when building parts.
1714 init_decoded_content (CT ct)
1718 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1719 adios (NULL, "out of memory");
1722 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1723 ct->c_ceclosefnx = close_encoding;
1724 ct->c_cesizefnx = NULL; /* since unencoded */
1731 * TRANSFER ENCODINGS
1735 init_encoding (CT ct, OpenCEFunc openfnx)
1739 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1740 adios (NULL, "out of memory");
1743 ct->c_ceopenfnx = openfnx;
1744 ct->c_ceclosefnx = close_encoding;
1745 ct->c_cesizefnx = size_encoding;
1752 close_encoding (CT ct)
1756 if (!(ce = ct->c_cefile))
1766 static unsigned long
1767 size_encoding (CT ct)
1775 if (!(ce = ct->c_cefile))
1776 return (ct->c_end - ct->c_begin);
1778 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1779 return (long) st.st_size;
1782 if (stat (ce->ce_file, &st) != NOTOK)
1783 return (long) st.st_size;
1788 if (ct->c_encoding == CE_EXTERNAL)
1789 return (ct->c_end - ct->c_begin);
1792 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1793 return (ct->c_end - ct->c_begin);
1795 if (fstat (fd, &st) != NOTOK)
1796 size = (long) st.st_size;
1800 (*ct->c_ceclosefnx) (ct);
1809 static unsigned char b642nib[0x80] = {
1810 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1811 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1812 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1813 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1814 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1815 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1816 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1817 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1818 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1819 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1820 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1821 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1822 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1823 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1824 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1825 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1832 return init_encoding (ct, openBase64);
1837 openBase64 (CT ct, char **file)
1839 int bitno, cc, digested;
1842 unsigned char value, *b, *b1, *b2, *b3;
1843 char *cp, *ep, buffer[BUFSIZ];
1847 b = (unsigned char *) &bits;
1848 b1 = &b[endian > 0 ? 1 : 2];
1849 b2 = &b[endian > 0 ? 2 : 1];
1850 b3 = &b[endian > 0 ? 3 : 0];
1854 fseek (ce->ce_fp, 0L, SEEK_SET);
1859 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1860 content_error (ce->ce_file, ct, "unable to fopen for reading");
1866 if (*file == NULL) {
1867 ce->ce_file = add (m_scratch ("", tmp), NULL);
1870 ce->ce_file = add (*file, NULL);
1874 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1875 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1879 if ((len = ct->c_end - ct->c_begin) < 0)
1880 adios (NULL, "internal error(1)");
1882 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1883 content_error (ct->c_file, ct, "unable to open for reading");
1887 if ((digested = ct->c_digested))
1888 MD5Init (&mdContext);
1894 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1896 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1898 content_error (ct->c_file, ct, "error reading from");
1902 content_error (NULL, ct, "premature eof");
1910 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1915 if (skip || (*cp & 0x80)
1916 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1918 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1920 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1923 content_error (NULL, ct,
1924 "invalid BASE64 encoding -- continuing");
1928 bits |= value << bitno;
1930 if ((bitno -= 6) < 0) {
1931 putc ((char) *b1, ce->ce_fp);
1933 MD5Update (&mdContext, b1, 1);
1935 putc ((char) *b2, ce->ce_fp);
1937 MD5Update (&mdContext, b2, 1);
1939 putc ((char) *b3, ce->ce_fp);
1941 MD5Update (&mdContext, b3, 1);
1945 if (ferror (ce->ce_fp)) {
1946 content_error (ce->ce_file, ct,
1947 "error writing to");
1950 bitno = 18, bits = 0L, skip = 0;
1956 goto self_delimiting;
1965 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1967 content_error (NULL, ct, "invalid BASE64 encoding");
1972 fseek (ct->c_fp, 0L, SEEK_SET);
1974 if (fflush (ce->ce_fp)) {
1975 content_error (ce->ce_file, ct, "error writing to");
1980 unsigned char digest[16];
1982 MD5Final (digest, &mdContext);
1983 if (memcmp((char *) digest, (char *) ct->c_digest,
1984 sizeof(digest) / sizeof(digest[0])))
1985 content_error (NULL, ct,
1986 "content integrity suspect (digest mismatch) -- continuing");
1989 fprintf (stderr, "content integrity confirmed\n");
1992 fseek (ce->ce_fp, 0L, SEEK_SET);
1995 *file = ce->ce_file;
1996 return fileno (ce->ce_fp);
1999 free_encoding (ct, 0);
2008 static char hex2nib[0x80] = {
2009 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2010 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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,
2014 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2015 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2016 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2017 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2018 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2019 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2020 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2021 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2022 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2023 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2024 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2031 return init_encoding (ct, openQuoted);
2036 openQuoted (CT ct, char **file)
2038 int cc, digested, len, quoted;
2040 char buffer[BUFSIZ];
2047 fseek (ce->ce_fp, 0L, SEEK_SET);
2052 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2053 content_error (ce->ce_file, ct, "unable to fopen for reading");
2059 if (*file == NULL) {
2060 ce->ce_file = add (m_scratch ("", tmp), NULL);
2063 ce->ce_file = add (*file, NULL);
2067 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2068 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2072 if ((len = ct->c_end - ct->c_begin) < 0)
2073 adios (NULL, "internal error(2)");
2075 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2076 content_error (ct->c_file, ct, "unable to open for reading");
2080 if ((digested = ct->c_digested))
2081 MD5Init (&mdContext);
2088 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2092 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2093 content_error (NULL, ct, "premature eof");
2097 if ((cc = strlen (buffer)) > len)
2101 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2106 for (; cp < ep; cp++) {
2109 if (!isxdigit (*cp)) {
2111 dp = "expecting hexidecimal-digit";
2112 goto invalid_encoding;
2115 mask |= hex2nib[*cp & 0x7f];
2116 putc (mask, ce->ce_fp);
2118 MD5Update (&mdContext, &mask, 1);
2122 putc (*cp, ce->ce_fp);
2124 MD5Update (&mdContext, (unsigned char *) ":", 1);
2128 if (!isxdigit (*cp))
2130 mask = hex2nib[*cp & 0x7f];
2136 if (ferror (ce->ce_fp)) {
2137 content_error (ce->ce_file, ct, "error writing to");
2146 if (*cp < '!' || *cp > '~') {
2148 dp = "expecting character in range [!..~]";
2151 i = strlen (invo_name) + 2;
2152 content_error (NULL, ct,
2153 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2161 putc (*cp, ce->ce_fp);
2164 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2166 MD5Update (&mdContext, (unsigned char *) cp, 1);
2168 if (ferror (ce->ce_fp)) {
2169 content_error (ce->ce_file, ct, "error writing to");
2175 if (*++cp != '\n') {
2184 content_error (NULL, ct,
2185 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2189 fseek (ct->c_fp, 0L, SEEK_SET);
2191 if (fflush (ce->ce_fp)) {
2192 content_error (ce->ce_file, ct, "error writing to");
2197 unsigned char digest[16];
2199 MD5Final (digest, &mdContext);
2200 if (memcmp((char *) digest, (char *) ct->c_digest,
2201 sizeof(digest) / sizeof(digest[0])))
2202 content_error (NULL, ct,
2203 "content integrity suspect (digest mismatch) -- continuing");
2206 fprintf (stderr, "content integrity confirmed\n");
2209 fseek (ce->ce_fp, 0L, SEEK_SET);
2212 *file = ce->ce_file;
2213 return fileno (ce->ce_fp);
2216 free_encoding (ct, 0);
2228 if (init_encoding (ct, open7Bit) == NOTOK)
2231 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2237 open7Bit (CT ct, char **file)
2240 char buffer[BUFSIZ];
2245 fseek (ce->ce_fp, 0L, SEEK_SET);
2250 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2251 content_error (ce->ce_file, ct, "unable to fopen for reading");
2257 if (*file == NULL) {
2258 ce->ce_file = add (m_scratch ("", tmp), NULL);
2261 ce->ce_file = add (*file, NULL);
2265 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2266 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2270 if (ct->c_type == CT_MULTIPART) {
2272 CI ci = &ct->c_ctinfo;
2275 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2276 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2277 + 1 + strlen (ci->ci_subtype);
2278 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2279 putc (';', ce->ce_fp);
2282 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2284 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2285 fputs ("\n\t", ce->ce_fp);
2288 putc (' ', ce->ce_fp);
2291 fprintf (ce->ce_fp, "%s", buffer);
2295 if (ci->ci_comment) {
2296 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2297 fputs ("\n\t", ce->ce_fp);
2301 putc (' ', ce->ce_fp);
2304 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2307 fprintf (ce->ce_fp, "\n");
2309 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2311 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2312 fprintf (ce->ce_fp, "\n");
2315 if ((len = ct->c_end - ct->c_begin) < 0)
2316 adios (NULL, "internal error(3)");
2318 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2319 content_error (ct->c_file, ct, "unable to open for reading");
2323 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2325 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2327 content_error (ct->c_file, ct, "error reading from");
2331 content_error (NULL, ct, "premature eof");
2339 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2340 if (ferror (ce->ce_fp)) {
2341 content_error (ce->ce_file, ct, "error writing to");
2346 fseek (ct->c_fp, 0L, SEEK_SET);
2348 if (fflush (ce->ce_fp)) {
2349 content_error (ce->ce_file, ct, "error writing to");
2353 fseek (ce->ce_fp, 0L, SEEK_SET);
2356 *file = ce->ce_file;
2357 return fileno (ce->ce_fp);
2360 free_encoding (ct, 0);
2370 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2372 char cachefile[BUFSIZ];
2375 fseek (ce->ce_fp, 0L, SEEK_SET);
2380 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2381 content_error (ce->ce_file, ct, "unable to fopen for reading");
2387 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2388 cachefile, sizeof(cachefile)) != NOTOK) {
2389 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2390 ce->ce_file = getcpy (cachefile);
2394 admonish (cachefile, "unable to fopen for reading");
2401 *file = ce->ce_file;
2402 *fd = fileno (ce->ce_fp);
2413 return init_encoding (ct, openFile);
2418 openFile (CT ct, char **file)
2421 char cachefile[BUFSIZ];
2422 struct exbody *e = ct->c_ctexbody;
2423 CE ce = ct->c_cefile;
2425 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2437 content_error (NULL, ct, "missing name parameter");
2441 ce->ce_file = getcpy (e->eb_name);
2444 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2445 content_error (ce->ce_file, ct, "unable to fopen for reading");
2449 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2450 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2451 cachefile, sizeof(cachefile)) != NOTOK) {
2455 mask = umask (cachetype ? ~m_gmprot () : 0222);
2456 if ((fp = fopen (cachefile, "w"))) {
2458 char buffer[BUFSIZ];
2459 FILE *gp = ce->ce_fp;
2461 fseek (gp, 0L, SEEK_SET);
2463 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2465 fwrite (buffer, sizeof(*buffer), cc, fp);
2469 admonish (ce->ce_file, "error reading");
2474 admonish (cachefile, "error writing");
2482 fseek (ce->ce_fp, 0L, SEEK_SET);
2483 *file = ce->ce_file;
2484 return fileno (ce->ce_fp);
2494 return init_encoding (ct, openFTP);
2499 openFTP (CT ct, char **file)
2501 int cachetype, caching, fd;
2503 char *bp, *ftp, *user, *pass;
2504 char buffer[BUFSIZ], cachefile[BUFSIZ];
2507 static char *username = NULL;
2508 static char *password = NULL;
2513 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2521 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2532 if (!e->eb_name || !e->eb_site) {
2533 content_error (NULL, ct, "missing %s parameter",
2534 e->eb_name ? "site": "name");
2541 pidcheck (pidwait (xpid, NOTOK));
2545 /* Get the buffer ready to go */
2547 buflen = sizeof(buffer);
2550 * Construct the query message for user
2552 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2558 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2564 snprintf (bp, buflen, "\n using %sFTP from site %s",
2565 e->eb_flags ? "anonymous " : "", e->eb_site);
2570 if (e->eb_size > 0) {
2571 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2576 snprintf (bp, buflen, "? ");
2579 * Now, check the answer
2581 if (!getanswer (buffer))
2586 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2589 ruserpass (e->eb_site, &username, &password);
2594 ce->ce_unlink = (*file == NULL);
2596 cachefile[0] = '\0';
2597 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2598 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2599 cachefile, sizeof(cachefile)) != NOTOK) {
2600 if (*file == NULL) {
2607 ce->ce_file = add (*file, NULL);
2609 ce->ce_file = add (cachefile, NULL);
2611 ce->ce_file = add (m_scratch ("", tmp), NULL);
2613 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2614 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2622 int child_id, i, vecp;
2626 vec[vecp++] = r1bindex (ftp, '/');
2627 vec[vecp++] = e->eb_site;
2630 vec[vecp++] = e->eb_dir;
2631 vec[vecp++] = e->eb_name;
2632 vec[vecp++] = ce->ce_file,
2633 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2634 ? "ascii" : "binary";
2639 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2643 adios ("fork", "unable to");
2647 close (fileno (ce->ce_fp));
2649 fprintf (stderr, "unable to exec ");
2655 if (pidXwait (child_id, NULL)) {
2659 username = password = NULL;
2668 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2670 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2677 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2682 mask = umask (cachetype ? ~m_gmprot () : 0222);
2683 if ((fp = fopen (cachefile, "w"))) {
2685 FILE *gp = ce->ce_fp;
2687 fseek (gp, 0L, SEEK_SET);
2689 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2691 fwrite (buffer, sizeof(*buffer), cc, fp);
2695 admonish (ce->ce_file, "error reading");
2700 admonish (cachefile, "error writing");
2709 fseek (ce->ce_fp, 0L, SEEK_SET);
2710 *file = ce->ce_file;
2711 return fileno (ce->ce_fp);
2722 return init_encoding (ct, openMail);
2727 openMail (CT ct, char **file)
2729 int child_id, fd, i, vecp;
2731 char *bp, buffer[BUFSIZ], *vec[7];
2732 struct exbody *e = ct->c_ctexbody;
2733 CE ce = ct->c_cefile;
2735 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2746 if (!e->eb_server) {
2747 content_error (NULL, ct, "missing server parameter");
2754 pidcheck (pidwait (xpid, NOTOK));
2758 /* Get buffer ready to go */
2760 buflen = sizeof(buffer);
2762 /* Now construct query message */
2763 snprintf (bp, buflen, "Retrieve content");
2769 snprintf (bp, buflen, " %s", e->eb_partno);
2775 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2777 e->eb_subject ? e->eb_subject : e->eb_body);
2779 /* Now, check answer */
2780 if (!getanswer (buffer))
2784 vec[vecp++] = r1bindex (mailproc, '/');
2785 vec[vecp++] = e->eb_server;
2786 vec[vecp++] = "-subject";
2787 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2788 vec[vecp++] = "-body";
2789 vec[vecp++] = e->eb_body;
2792 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2796 advise ("fork", "unable to");
2800 execvp (mailproc, vec);
2801 fprintf (stderr, "unable to exec ");
2807 if (pidXwait (child_id, NULL) == OK)
2808 advise (NULL, "request sent");
2812 if (*file == NULL) {
2813 ce->ce_file = add (m_scratch ("", tmp), NULL);
2816 ce->ce_file = add (*file, NULL);
2820 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2821 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2825 fseek (ce->ce_fp, 0L, SEEK_SET);
2826 *file = ce->ce_file;
2827 return fileno (ce->ce_fp);
2832 fgetstr (char *s, int n, FILE *stream)
2836 for (ep = (cp = s) + n; cp < ep; ) {
2839 if (!fgets (cp, n, stream))
2840 return (cp != s ? s : NULL);
2841 if (cp == s && *cp != '#')
2844 cp += (i = strlen (cp)) - 1;
2845 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2856 * Parse the composition draft for text and directives.
2857 * Do initial setup of Content structure.
2861 user_content (FILE *in, char *file, char *buf, CT *ctp)
2865 char buffer[BUFSIZ];
2866 struct multipart *m;
2869 struct str2init *s2i;
2874 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2879 /* allocate basic Content structure */
2880 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2881 adios (NULL, "out of memory");
2884 /* allocate basic structure for handling decoded content */
2885 init_decoded_content (ct);
2892 * Handle inline text. Check if line
2893 * is one of the following forms:
2895 * 1) doesn't begin with '#' (implicit directive)
2896 * 2) begins with "##" (implicit directive)
2897 * 3) begins with "#<"
2899 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2903 char content[BUFSIZ];
2906 /* use a temp file to collect the plain text lines */
2907 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2910 if ((out = fopen (ce->ce_file, "w")) == NULL)
2911 adios (ce->ce_file, "unable to open for writing");
2913 if (buf[0] == '#' && buf[1] == '<') {
2914 strncpy (content, buf + 2, sizeof(content));
2921 /* the directive is implicit */
2922 strncpy (content, "text/plain", sizeof(content));
2924 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2928 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2929 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2933 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2934 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2935 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2936 switch (buffer[0]) {
2943 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
2951 if (headers != 1 || buffer[0] != '\n')
2952 fputs (buffer, out);
2957 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
2959 if (buffer[0] == '#') {
2962 if (buffer[1] != '#')
2964 for (cp = (bp = buffer) + 1; *cp; cp++)
2971 ct->c_end = ftell (out);
2974 /* parse content type */
2975 if (get_ctinfo (content, ct, inlineD) == NOTOK)
2978 for (s2i = str2cts; s2i->si_key; s2i++)
2979 if (!strcasecmp (ci->ci_type, s2i->si_key))
2981 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
2985 * check type specified (possibly implicitly)
2987 switch (ct->c_type = s2i->si_val) {
2989 if (!strcasecmp (ci->ci_subtype, "rfc822")) {
2990 ct->c_encoding = CE_7BIT;
2995 adios (NULL, "it doesn't make sense to define an in-line %s content",
2996 ct->c_type == CT_MESSAGE ? "message" : "multipart");
3001 if ((ct->c_ctinitfnx = s2i->si_init))
3002 (*ct->c_ctinitfnx) (ct);
3007 fseek (in, pos, SEEK_SET);
3012 * If we've reached this point, the next line
3013 * must be some type of explicit directive.
3016 /* check if directive is external-type */
3017 extrnal = (buf[1] == '@');
3019 /* parse directive */
3020 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3023 /* check directive against the list of MIME types */
3024 for (s2i = str2cts; s2i->si_key; s2i++)
3025 if (!strcasecmp (ci->ci_type, s2i->si_key))
3029 * Check if the directive specified a valid type.
3030 * This will happen if it was one of the following forms:
3032 * #type/subtype (or)
3036 if (!ci->ci_subtype)
3037 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3039 switch (ct->c_type = s2i->si_val) {
3041 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3042 ci->ci_type, ci->ci_subtype);
3046 if (!strcasecmp (ci->ci_subtype, "partial"))
3047 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3048 ci->ci_type, ci->ci_subtype);
3049 if (!strcasecmp (ci->ci_subtype, "external-body"))
3050 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3051 ci->ci_type, ci->ci_subtype);
3054 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3055 ci->ci_type, ci->ci_subtype);
3059 if ((ct->c_ctinitfnx = s2i->si_init))
3060 (*ct->c_ctinitfnx) (ct);
3065 * #@type/subtype (external types directive)
3072 adios (NULL, "need external information for \"#@%s/%s\"",
3073 ci->ci_type, ci->ci_subtype);
3076 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3077 free (ci->ci_magic);
3078 ci->ci_magic = NULL;
3081 * Since we are using the current Content structure to
3082 * hold information about the type of the external
3083 * reference, we need to create another Content structure
3084 * for the message/external-body to wrap it in.
3086 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3087 adios (NULL, "out of memory");
3090 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3092 ct->c_type = CT_MESSAGE;
3093 ct->c_subtype = MESSAGE_EXTERNAL;
3095 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3096 adios (NULL, "out of memory");
3097 ct->c_ctparams = (void *) e;
3103 if (params_external (ct, 1) == NOTOK)
3109 /* Handle [file] argument */
3111 /* check if specifies command to execute */
3112 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3113 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3116 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3117 cp = add (cp, NULL);
3118 free (ci->ci_magic);
3121 /* record filename of decoded contents */
3122 ce->ce_file = ci->ci_magic;
3123 if (access (ce->ce_file, R_OK) == NOTOK)
3124 adios ("reading", "unable to access %s for", ce->ce_file);
3125 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3126 ct->c_end = (long) st.st_size;
3127 ci->ci_magic = NULL;
3133 * No [file] argument, so check profile for
3134 * method to compose content.
3136 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3137 invo_name, ci->ci_type, ci->ci_subtype);
3138 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3139 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3140 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3141 content_error (NULL, ct, "don't know how to compose content");
3145 ci->ci_magic = add (cp, NULL);
3150 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3154 * #forw [+folder] [msgs]
3156 if (!strcasecmp (ci->ci_type, "forw")) {
3158 char *folder, *arguments[MAXARGS];
3162 ap = brkstring (ci->ci_magic, " ", "\n");
3163 copyip (ap, arguments, MAXARGS);
3165 arguments[0] = "cur";
3166 arguments[1] = NULL;
3170 /* search the arguments for a folder name */
3171 for (ap = arguments; *ap; ap++) {
3173 if (*cp == '+' || *cp == '@') {
3175 adios (NULL, "only one folder per #forw directive");
3177 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3181 /* else, use the current folder */
3183 folder = add (getfolder (1), NULL);
3185 if (!(mp = folder_read (folder)))
3186 adios (NULL, "unable to read folder %s", folder);
3187 for (ap = arguments; *ap; ap++) {
3189 if (*cp != '+' && *cp != '@')
3190 if (!m_convert (mp, cp))
3197 * If there is more than one message to include, make this
3198 * a content of type "multipart/digest" and insert each message
3199 * as a subpart. If there is only one message, then make this
3200 * a content of type "message/rfc822".
3202 if (mp->numsel > 1) {
3203 /* we are forwarding multiple messages */
3204 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3206 ct->c_type = CT_MULTIPART;
3207 ct->c_subtype = MULTI_DIGEST;
3209 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3210 adios (NULL, "out of memory");
3211 ct->c_ctparams = (void *) m;
3214 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3215 if (is_selected(mp, msgnum)) {
3220 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3221 adios (NULL, "out of memory");
3222 init_decoded_content (p);
3224 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3226 p->c_type = CT_MESSAGE;
3227 p->c_subtype = MESSAGE_RFC822;
3229 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3230 pe->ce_file = add (buffer, NULL);
3231 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3232 p->c_end = (long) st.st_size;
3234 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3235 adios (NULL, "out of memory");
3237 pp = &part->mp_next;
3242 /* we are forwarding one message */
3243 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3245 ct->c_type = CT_MESSAGE;
3246 ct->c_subtype = MESSAGE_RFC822;
3248 msgnum = mp->lowsel;
3249 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3250 ce->ce_file = add (buffer, NULL);
3251 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3252 ct->c_end = (long) st.st_size;
3255 folder_free (mp); /* free folder/message structure */
3262 if (!strcasecmp (ci->ci_type, "end")) {
3269 * #begin [ alternative | parallel ]
3271 if (!strcasecmp (ci->ci_type, "begin")) {
3272 if (!ci->ci_magic) {
3274 cp = SubMultiPart[vrsn - 1].kv_key;
3275 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3276 vrsn = MULTI_ALTERNATE;
3277 cp = SubMultiPart[vrsn - 1].kv_key;
3278 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3279 vrsn = MULTI_PARALLEL;
3280 cp = SubMultiPart[vrsn - 1].kv_key;
3281 } else if (uprf (ci->ci_magic, "digest")) {
3284 vrsn = MULTI_UNKNOWN;
3289 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3290 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3292 ct->c_type = CT_MULTIPART;
3293 ct->c_subtype = vrsn;
3295 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3296 adios (NULL, "out of memory");
3297 ct->c_ctparams = (void *) m;
3300 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3304 if (user_content (in, file, buffer, &p) == DONE) {
3306 adios (NULL, "empty \"#begin ... #end\" sequence");
3312 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3313 adios (NULL, "out of memory");
3315 pp = &part->mp_next;
3318 admonish (NULL, "premature end-of-file, missing #end");
3325 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3326 return NOTOK; /* NOT REACHED */
3331 set_id (CT ct, int top)
3335 static time_t clock = 0;
3336 static char *msgfmt;
3340 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3341 (int) getpid(), (long) clock, LocalName());
3343 msgfmt = getcpy(msgid);
3345 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3346 ct->c_id = getcpy (msgid);
3350 static char ebcdicsafe[0x100] = {
3351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3352 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3355 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3356 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3357 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3358 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3359 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3360 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3361 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3362 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3363 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3364 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3365 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3366 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 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,
3372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3379 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3380 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3387 * Fill out, or expand the various contents in the composition
3388 * draft. Read-in any necessary files. Parse and execute any
3389 * commands specified by profile composition strings.
3393 compose_content (CT ct)
3395 CE ce = ct->c_cefile;
3397 switch (ct->c_type) {
3402 char partnam[BUFSIZ];
3403 struct multipart *m = (struct multipart *) ct->c_ctparams;
3407 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3408 pp = partnam + strlen (partnam);
3413 /* first, we call compose_content on all the subparts */
3414 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3415 CT p = part->mp_part;
3417 sprintf (pp, "%d", partnum);
3418 p->c_partno = add (partnam, NULL);
3419 if (compose_content (p) == NOTOK)
3424 * If the -rfc934mode switch is given, then check all
3425 * the subparts of a multipart/digest. If they are all
3426 * message/rfc822, then mark this content and all
3427 * subparts with the rfc934 compatibility mode flag.
3429 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3432 for (part = m->mp_parts; part; part = part->mp_next) {
3433 CT p = part->mp_part;
3435 if (p->c_subtype != MESSAGE_RFC822) {
3440 ct->c_rfc934 = is934;
3441 for (part = m->mp_parts; part; part = part->mp_next) {
3442 CT p = part->mp_part;
3444 if ((p->c_rfc934 = is934))
3450 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3454 for (part = m->mp_parts; part; part = part->mp_next)
3455 ct->c_end += part->mp_part->c_end + partnum;
3461 /* Nothing to do for type message */
3465 * Discrete types (text/application/audio/image/video)
3470 int i, xstdout, len, buflen;
3471 char *bp, **ap, *cp;
3472 char *vec[4], buffer[BUFSIZ];
3474 CI ci = &ct->c_ctinfo;
3476 if (!(cp = ci->ci_magic))
3477 adios (NULL, "internal error(5)");
3479 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3484 /* Get buffer ready to go */
3487 buflen = sizeof(buffer);
3490 * Parse composition string into buffer
3492 for ( ; *cp; cp++) {
3497 /* insert parameters from directive */
3501 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3502 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3512 /* %f, and stdout is not-redirected */
3518 * insert temporary filename where
3519 * content should be written
3521 snprintf (bp, buflen, "%s", ce->ce_file);
3525 /* insert content subtype */
3526 strncpy (bp, ci->ci_subtype, buflen);
3530 /* insert character % */
3551 printf ("composing content %s/%s from command\n\t%s\n",
3552 ci->ci_type, ci->ci_subtype, buffer);
3554 fflush (stdout); /* not sure if need for -noverbose */
3561 if ((out = fopen (ce->ce_file, "w")) == NULL)
3562 adios (ce->ce_file, "unable to open for writing");
3564 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3568 adios ("fork", "unable to fork");
3573 dup2 (fileno (out), 1);
3574 close (fileno (out));
3575 execvp ("/bin/sh", vec);
3576 fprintf (stderr, "unable to exec ");
3583 if (pidXwait(child_id, NULL))
3589 /* Check size of file */
3590 if (listsw && ct->c_end == 0L) {
3593 if (stat (ce->ce_file, &st) != NOTOK)
3594 ct->c_end = (long) st.st_size;
3606 * 1) choose a transfer encoding.
3607 * 2) check for clashes with multipart boundary string.
3608 * 3) for text content, figure out which character set is being used.
3610 * If there is a clash with one of the contents and the multipart boundary,
3611 * this function will exit with NOTOK. This will cause the scanning process
3612 * to be repeated with a different multipart boundary. It is possible
3613 * (although highly unlikely) that this scan will be repeated multiple times.
3617 scan_content (CT ct)
3620 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3621 int checklinelen, linelen = 0; /* check for long lines */
3622 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3623 int checklinespace, linespace = 0; /* check if any line ends with space */
3624 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3625 char *cp, buffer[BUFSIZ];
3628 CE ce = ct->c_cefile;
3631 * handle multipart by scanning all subparts
3632 * and then checking their encoding.
3634 if (ct->c_type == CT_MULTIPART) {
3635 struct multipart *m = (struct multipart *) ct->c_ctparams;
3638 /* initially mark the domain of enclosing multipart as 7bit */
3639 ct->c_encoding = CE_7BIT;
3641 for (part = m->mp_parts; part; part = part->mp_next) {
3642 CT p = part->mp_part;
3644 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3647 /* if necessary, enlarge encoding for enclosing multipart */
3648 if (p->c_encoding == CE_BINARY)
3649 ct->c_encoding = CE_BINARY;
3650 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3651 ct->c_encoding = CE_8BIT;
3658 * Decide what to check while scanning this content.
3660 switch (ct->c_type) {
3664 if (ct->c_subtype == TEXT_PLAIN) {
3669 checkebcdic = ebcdicsw;
3675 case CT_APPLICATION:
3677 checkebcdic = ebcdicsw;
3689 /* don't check anything for message/external */
3690 if (ct->c_subtype == MESSAGE_EXTERNAL)
3700 * Don't check anything for these types,
3701 * since we are forcing use of base64.
3712 * Scan the unencoded content
3714 if (check8bit || checklinelen || checklinespace || checkboundary) {
3715 if ((in = fopen (ce->ce_file, "r")) == NULL)
3716 adios (ce->ce_file, "unable to open for reading");
3717 len = strlen (prefix);
3719 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3721 * Check for 8bit data.
3724 for (cp = buffer; *cp; cp++) {
3725 if (!isascii (*cp)) {
3727 check8bit = 0; /* no need to keep checking */
3730 * Check if character is ebcdic-safe. We only check
3731 * this if also checking for 8bit data.
3733 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3735 checkebcdic = 0; /* no need to keep checking */
3741 * Check line length.
3743 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3745 checklinelen = 0; /* no need to keep checking */
3749 * Check if line ends with a space.
3751 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3753 checklinespace = 0; /* no need to keep checking */
3757 * Check if content contains a line that clashes
3758 * with our standard boundary for multipart messages.
3760 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3761 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3765 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3767 checkboundary = 0; /* no need to keep checking */
3775 * Decide which transfer encoding to use.
3777 switch (ct->c_type) {
3780 * If the text content didn't specify a character
3781 * set, we need to figure out which one was used.
3783 t = (struct text *) ct->c_ctparams;
3784 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3785 CI ci = &ct->c_ctinfo;
3788 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3792 t->tx_charset = CHARSET_UNKNOWN;
3793 *ap = concat ("charset=", write_charset_8bit(), NULL);
3795 t->tx_charset = CHARSET_USASCII;
3796 *ap = add ("charset=us-ascii", NULL);
3799 cp = strchr(*ap++, '=');
3805 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3806 ct->c_encoding = CE_QUOTED;
3808 ct->c_encoding = CE_7BIT;
3811 case CT_APPLICATION:
3812 /* For application type, use base64, except when postscript */
3813 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3814 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3815 ? CE_QUOTED : CE_BASE64;
3817 ct->c_encoding = CE_7BIT;
3821 ct->c_encoding = CE_7BIT;
3827 /* For audio, image, and video contents, just use base64 */
3828 ct->c_encoding = CE_BASE64;
3832 return (boundaryclash ? NOTOK : OK);
3837 * Scan the content structures, and build header
3838 * fields that will need to be output into the
3843 build_headers (CT ct)
3845 int cc, mailbody, len;
3847 char *np, *vp, buffer[BUFSIZ];
3848 CI ci = &ct->c_ctinfo;
3851 * If message is type multipart, then add the multipart
3852 * boundary to the list of attribute/value pairs.
3854 if (ct->c_type == CT_MULTIPART) {
3856 static int level = 0; /* store nesting level */
3860 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3861 cp = strchr(*ap++ = add (buffer, NULL), '=');
3868 * Skip the output of Content-Type, parameters, content
3869 * description, and Content-ID if the content is of type
3870 * "message" and the rfc934 compatibility flag is set
3871 * (which means we are inside multipart/digest and the
3872 * switch -rfc934mode was given).
3874 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3878 * output the content type and subtype
3880 np = add (TYPE_FIELD, NULL);
3881 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3883 /* keep track of length of line */
3884 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3885 + strlen (ci->ci_subtype) + 3;
3887 mailbody = ct->c_type == CT_MESSAGE
3888 && ct->c_subtype == MESSAGE_EXTERNAL
3889 && ((struct exbody *) ct->c_ctparams)->eb_body;
3892 * Append the attribute/value pairs to
3893 * the end of the Content-Type line.
3895 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3896 if (mailbody && !strcasecmp (*ap, "body"))
3902 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3903 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3904 vp = add ("\n\t", vp);
3910 vp = add (buffer, vp);
3915 * Append any RFC-822 comment to the end of
3916 * the Content-Type line.
3918 if (ci->ci_comment) {
3919 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3920 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3921 vp = add ("\n\t", vp);
3927 vp = add (buffer, vp);
3930 vp = add ("\n", vp);
3931 add_header (ct, np, vp);
3934 * output the Content-ID
3937 np = add (ID_FIELD, NULL);
3938 vp = concat (" ", ct->c_id, NULL);
3939 add_header (ct, np, vp);
3943 * output the Content-Description
3946 np = add (DESCR_FIELD, NULL);
3947 vp = concat (" ", ct->c_descr, NULL);
3948 add_header (ct, np, vp);
3953 * If this is the internal content structure for a
3954 * "message/external", then we are done with the
3955 * headers (since it has no body).
3961 * output the Content-MD5
3964 np = add (MD5_FIELD, NULL);
3965 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3966 add_header (ct, np, vp);
3970 * output the Content-Transfer-Encoding
3972 switch (ct->c_encoding) {
3974 /* Nothing to output */
3976 np = add (ENCODING_FIELD, NULL);
3977 vp = concat (" ", "7bit", "\n", NULL);
3978 add_header (ct, np, vp);
3983 if (ct->c_type == CT_MESSAGE)
3984 adios (NULL, "internal error, invalid encoding");
3986 np = add (ENCODING_FIELD, NULL);
3987 vp = concat (" ", "8bit", "\n", NULL);
3988 add_header (ct, np, vp);
3992 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3993 adios (NULL, "internal error, invalid encoding");
3995 np = add (ENCODING_FIELD, NULL);
3996 vp = concat (" ", "quoted-printable", "\n", NULL);
3997 add_header (ct, np, vp);
4001 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4002 adios (NULL, "internal error, invalid encoding");
4004 np = add (ENCODING_FIELD, NULL);
4005 vp = concat (" ", "base64", "\n", NULL);
4006 add_header (ct, np, vp);
4010 if (ct->c_type == CT_MESSAGE)
4011 adios (NULL, "internal error, invalid encoding");
4013 np = add (ENCODING_FIELD, NULL);
4014 vp = concat (" ", "binary", "\n", NULL);
4015 add_header (ct, np, vp);
4019 adios (NULL, "unknown transfer encoding in content");
4024 * Additional content specific header processing
4026 switch (ct->c_type) {
4029 struct multipart *m;
4032 m = (struct multipart *) ct->c_ctparams;
4033 for (part = m->mp_parts; part; part = part->mp_next) {
4043 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4046 e = (struct exbody *) ct->c_ctparams;
4047 build_headers (e->eb_content);
4060 static char nib2b64[0x40+1] =
4061 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4064 calculate_digest (CT ct, int asciiP)
4067 char buffer[BUFSIZ], *vp, *op;
4069 unsigned char digest[16];
4070 unsigned char outbuf[25];
4073 CE ce = ct->c_cefile;
4076 if ((in = fopen (ce->ce_file, "r")) == NULL)
4077 adios (ce->ce_file, "unable to open for reading");
4079 /* Initialize md5 context */
4080 MD5Init (&mdContext);
4082 /* calculate md5 message digest */
4084 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4087 cp = buffer + strlen (buffer) - 1;
4088 if ((c = *cp) == '\n')
4091 MD5Update (&mdContext, (unsigned char *) buffer,
4092 (unsigned int) strlen (buffer));
4095 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4098 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4099 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4102 /* md5 finalization. Write digest and zero md5 context */
4103 MD5Final (digest, &mdContext);
4108 /* print debugging info */
4112 fprintf (stderr, "MD5 digest=");
4113 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4115 fprintf (stderr, "%02x", *dp & 0xff);
4116 fprintf (stderr, "\n");
4119 /* encode the digest using base64 */
4120 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4121 cc > 0; cc -= 3, op += 4) {
4125 bits = (*dp++ & 0xff) << 16;
4127 bits |= (*dp++ & 0xff) << 8;
4129 bits |= *dp++ & 0xff;
4132 for (bp = op + 4; bp > op; bits >>= 6)
4133 *--bp = nib2b64[bits & 0x3f];
4141 /* null terminate string */
4144 /* now make copy and return string */
4145 vp = concat (" ", outbuf, "\n", NULL);
4151 readDigest (CT ct, char *cp)
4156 unsigned char *dp, value, *ep;
4157 unsigned char *b, *b1, *b2, *b3;
4159 b = (unsigned char *) &bits,
4160 b1 = &b[endian > 0 ? 1 : 2],
4161 b2 = &b[endian > 0 ? 2 : 1],
4162 b3 = &b[endian > 0 ? 3 : 0];
4167 for (ep = (dp = ct->c_digest)
4168 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4173 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4175 fprintf (stderr, "invalid BASE64 encoding\n");
4179 bits |= value << bitno;
4181 if ((bitno -= 6) < 0) {
4182 if (dp + (3 - skip) > ep)
4183 goto invalid_digest;
4198 goto self_delimiting;
4203 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4213 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4221 fprintf (stderr, "MD5 digest=");
4222 for (dp = ct->c_digest; dp < ep; dp++)
4223 fprintf (stderr, "%02x", *dp & 0xff);
4224 fprintf (stderr, "\n");