3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
13 * This code was originally part of mhn.c. I split it into
14 * a separate program (mhbuild.c) and then later split it
15 * again (mhbuildsbr.c). But the code still has some of
16 * the mhn.c code in it. This program needs additional
17 * streamlining and removal of unneeded code.
22 #include <h/signals.h>
29 #include <h/mhparse.h>
32 #ifdef TIME_WITH_SYS_TIME
33 # include <sys/time.h>
36 # ifdef TM_IN_SYS_TIME
37 # include <sys/time.h>
43 #ifdef HAVE_SYS_WAIT_H
44 # include <sys/wait.h>
55 extern int endian; /* mhmisc.c */
58 extern int rcachesw; /* mhcachesbr.c */
59 extern int wcachesw; /* mhcachesbr.c */
61 int checksw = 0; /* Add Content-MD5 field */
64 * Directory to place tmp files. This must
65 * be set before these routines are called.
71 static char prefix[] = "----- =_aaaaaaaaaa";
74 * Structure for mapping types to their internal flags
82 * Structures for TEXT messages
84 static struct k2v SubText[] = {
85 { "plain", TEXT_PLAIN },
86 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
87 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
88 { NULL, TEXT_UNKNOWN } /* this one must be last! */
91 static struct k2v Charset[] = {
92 { "us-ascii", CHARSET_USASCII },
93 { "iso-8859-1", CHARSET_LATIN },
94 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
98 * Structures for MULTIPART messages
100 static struct k2v SubMultiPart[] = {
101 { "mixed", MULTI_MIXED },
102 { "alternative", MULTI_ALTERNATE },
103 { "digest", MULTI_DIGEST },
104 { "parallel", MULTI_PARALLEL },
105 { NULL, MULTI_UNKNOWN } /* this one must be last! */
109 * Structures for MESSAGE messages
111 static struct k2v SubMessage[] = {
112 { "rfc822", MESSAGE_RFC822 },
113 { "partial", MESSAGE_PARTIAL },
114 { "external-body", MESSAGE_EXTERNAL },
115 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
119 * Structure for APPLICATION messages
121 static struct k2v SubApplication[] = {
122 { "octet-stream", APPLICATION_OCTETS },
123 { "postscript", APPLICATION_POSTSCRIPT },
124 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
129 int make_intermediates (char *);
130 void content_error (char *, CT, char *, ...);
133 int find_cache (CT, int, int *, char *, char *, int);
136 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
139 void free_content (CT);
140 void free_ctinfo (CT);
141 void free_encoding (CT, int);
146 CT build_mime (char *);
152 static CT get_content (FILE *, char *, int);
153 static int add_header (CT, char *, char *);
154 static int get_ctinfo (char *, CT, int);
155 static int get_comment (CT, char **, int);
156 static int InitGeneric (CT);
157 static int InitText (CT);
158 static int InitMultiPart (CT);
159 static void reverse_parts (CT);
160 static int InitMessage (CT);
161 static int params_external (CT, int);
162 static int InitApplication (CT);
163 static int init_decoded_content (CT);
164 static int init_encoding (CT, OpenCEFunc);
165 static void close_encoding (CT);
166 static unsigned long size_encoding (CT);
167 static int InitBase64 (CT);
168 static int openBase64 (CT, char **);
169 static int InitQuoted (CT);
170 static int openQuoted (CT, char **);
171 static int Init7Bit (CT);
172 static int open7Bit (CT, char **);
173 static int openExternal (CT, CT, CE, char **, int *);
174 static int InitFile (CT);
175 static int openFile (CT, char **);
176 static int InitFTP (CT);
177 static int openFTP (CT, char **);
178 static int InitMail (CT);
179 static int openMail (CT, char **);
180 static char *fgetstr (char *, int, FILE *);
181 static int user_content (FILE *, char *, char *, CT *);
182 static void set_id (CT, int);
183 static int compose_content (CT);
184 static int scan_content (CT);
185 static int build_headers (CT);
186 static char *calculate_digest (CT, int);
187 static int readDigest (CT, char *);
190 * Structures for mapping (content) types to
191 * the functions to handle them.
199 static struct str2init str2cts[] = {
200 { "application", CT_APPLICATION, InitApplication },
201 { "audio", CT_AUDIO, InitGeneric },
202 { "image", CT_IMAGE, InitGeneric },
203 { "message", CT_MESSAGE, InitMessage },
204 { "multipart", CT_MULTIPART, InitMultiPart },
205 { "text", CT_TEXT, InitText },
206 { "video", CT_VIDEO, InitGeneric },
207 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
208 { NULL, CT_UNKNOWN, NULL },
211 static struct str2init str2ces[] = {
212 { "base64", CE_BASE64, InitBase64 },
213 { "quoted-printable", CE_QUOTED, InitQuoted },
214 { "8bit", CE_8BIT, Init7Bit },
215 { "7bit", CE_7BIT, Init7Bit },
216 { "binary", CE_BINARY, NULL },
217 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
218 { NULL, CE_UNKNOWN, NULL },
222 * NOTE WELL: si_key MUST NOT have value of NOTOK
224 * si_key is 1 if access method is anonymous.
226 static struct str2init str2methods[] = {
227 { "afs", 1, InitFile },
228 { "anon-ftp", 1, InitFTP },
229 { "ftp", 0, InitFTP },
230 { "local-file", 0, InitFile },
231 { "mail-server", 0, InitMail },
237 pidcheck (int status)
239 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
249 * Main routine for translating composition file
250 * into valid MIME message. It translates the draft
251 * into a content structure (actually a tree of content
252 * structures). This message then can be manipulated
253 * in various ways, including being output via
258 build_mime (char *infile)
261 char buf[BUFSIZ], name[NAMESZ];
268 umask (~m_gmprot ());
270 /* open the composition draft */
271 if ((in = fopen (infile, "r")) == NULL)
272 adios (infile, "unable to open for reading");
275 * Allocate space for primary (outside) content
277 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
278 adios (NULL, "out of memory");
281 * Allocate structure for handling decoded content
282 * for this part. We don't really need this, but
283 * allocate it to remain consistent.
285 init_decoded_content (ct);
288 * Parse some of the header fields in the composition
289 * draft into the linked list of header fields for
290 * the new MIME message.
292 for (compnum = 1, state = FLD;;) {
293 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
299 /* abort if draft has Mime-Version header field */
300 if (!strcasecmp (name, VRSN_FIELD))
301 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
303 /* abort if draft has Content-Transfer-Encoding header field */
304 if (!strcasecmp (name, ENCODING_FIELD))
305 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
307 /* ignore any Content-Type fields in the header */
308 if (!strcasecmp (name, TYPE_FIELD)) {
309 while (state == FLDPLUS)
310 state = m_getfld (state, name, buf, sizeof(buf), in);
314 /* get copies of the buffers */
315 np = add (name, NULL);
316 vp = add (buf, NULL);
318 /* if necessary, get rest of field */
319 while (state == FLDPLUS) {
320 state = m_getfld (state, name, buf, sizeof(buf), in);
321 vp = add (buf, vp); /* add to previous value */
324 /* Now add the header data to the list */
325 add_header (ct, np, vp);
328 /* if this wasn't the last header field, then continue */
334 adios (NULL, "draft has empty body -- no directives!");
339 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
344 adios (NULL, "message format error in component #%d", compnum);
347 adios (NULL, "getfld() returned %d", state);
353 * Now add the MIME-Version header field
354 * to the list of header fields.
356 np = add (VRSN_FIELD, NULL);
357 vp = concat (" ", VRSN_VALUE, "\n", NULL);
358 add_header (ct, np, vp);
361 * We initally assume we will find multiple contents in the
362 * draft. So create a multipart/mixed content to hold everything.
363 * We can remove this later, if it is not needed.
365 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
367 ct->c_type = CT_MULTIPART;
368 ct->c_subtype = MULTI_MIXED;
369 ct->c_file = add (infile, NULL);
371 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
372 adios (NULL, "out of memory");
373 ct->c_ctparams = (void *) m;
377 * read and parse the composition file
378 * and the directives it contains.
380 while (fgetstr (buf, sizeof(buf) - 1, in)) {
384 if (user_content (in, infile, buf, &p) == DONE) {
385 admonish (NULL, "ignoring spurious #end");
391 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
392 adios (NULL, "out of memory");
399 * close the composition draft since
400 * it's not needed any longer.
404 /* check if any contents were found */
406 adios (NULL, "no content directives found");
409 * If only one content was found, then remove and
410 * free the outer multipart content.
412 if (!m->mp_parts->mp_next) {
415 p = m->mp_parts->mp_part;
416 m->mp_parts->mp_part = NULL;
418 /* move header fields */
419 p->c_first_hf = ct->c_first_hf;
420 p->c_last_hf = ct->c_last_hf;
421 ct->c_first_hf = NULL;
422 ct->c_last_hf = NULL;
431 * Fill out, or expand directives. Parse and execute
432 * commands specified by profile composition strings.
434 compose_content (ct);
436 if ((cp = strchr(prefix, 'a')) == NULL)
437 adios (NULL, "internal error(4)");
440 * Scan the contents. Choose a transfer encoding, and
441 * check if prefix for multipart boundary clashes with
442 * any of the contents.
444 while (scan_content (ct) == NOTOK) {
449 adios (NULL, "giving up trying to find a unique delimiter string");
455 /* Build the rest of the header field structures */
463 * Main routine for reading/parsing the headers
464 * of a message content.
466 * toplevel = 1 # we are at the top level of the message
467 * toplevel = 0 # we are inside message type or multipart type
468 * # other than multipart/digest
469 * toplevel = -1 # we are inside multipart/digest
473 get_content (FILE *in, char *file, int toplevel)
476 char buf[BUFSIZ], name[NAMESZ];
479 if (!(ct = (CT) calloc (1, sizeof(*ct))))
480 adios (NULL, "out of memory");
483 ct->c_file = add (file, NULL);
484 ct->c_begin = ftell (ct->c_fp) + 1;
487 * Read the content headers
489 for (compnum = 1, state = FLD;;) {
490 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
496 /* Get MIME-Version field */
497 if (!strcasecmp (name, VRSN_FIELD)) {
501 cp = add (buf, NULL);
502 while (state == FLDPLUS) {
503 state = m_getfld (state, name, buf, sizeof(buf), in);
508 advise (NULL, "message %s has multiple %s: fields (%s)",
509 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
516 while (isspace (*cp))
518 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
520 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
525 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
527 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
530 for (dp = cp; istoken (*dp); dp++)
533 ucmp = !strcasecmp (cp, VRSN_VALUE);
537 "message %s has unknown value for %s: field (%s)",
538 ct->c_file, VRSN_FIELD, cp);
542 /* Get Content-Type field */
543 if (!strcasecmp (name, TYPE_FIELD)) {
545 struct str2init *s2i;
546 CI ci = &ct->c_ctinfo;
548 cp = add (buf, NULL);
549 while (state == FLDPLUS) {
550 state = m_getfld (state, name, buf, sizeof(buf), in);
554 /* Check if we've already seen a Content-Type header */
556 char *dp = trimcpy (cp);
558 advise (NULL, "message %s has multiple %s: fields (%s)",
559 ct->c_file, TYPE_FIELD, dp);
565 /* Parse the Content-Type field */
566 if (get_ctinfo (cp, ct, 0) == NOTOK)
570 * Set the Init function and the internal
571 * flag for this content type.
573 for (s2i = str2cts; s2i->si_key; s2i++)
574 if (!strcasecmp (ci->ci_type, s2i->si_key))
576 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
578 ct->c_type = s2i->si_val;
579 ct->c_ctinitfnx = s2i->si_init;
583 /* Get Content-Transfer-Encoding field */
584 if (!strcasecmp (name, ENCODING_FIELD)) {
587 struct str2init *s2i;
589 cp = add (buf, NULL);
590 while (state == FLDPLUS) {
591 state = m_getfld (state, name, buf, sizeof(buf), in);
596 * Check if we've already seen the
597 * Content-Transfer-Encoding field
600 advise (NULL, "message %s has multiple %s: fields (%s)",
601 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
607 ct->c_celine = cp; /* Save copy of this field */
608 while (isspace (*cp))
610 for (dp = cp; istoken (*dp); dp++)
616 * Find the internal flag and Init function
617 * for this transfer encoding.
619 for (s2i = str2ces; s2i->si_key; s2i++)
620 if (!strcasecmp (cp, s2i->si_key))
622 if (!s2i->si_key && !uprf (cp, "X-"))
625 ct->c_encoding = s2i->si_val;
627 /* Call the Init function for this encoding */
628 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
633 /* Get Content-ID field */
634 if (!strcasecmp (name, ID_FIELD)) {
635 ct->c_id = add (buf, ct->c_id);
636 while (state == FLDPLUS) {
637 state = m_getfld (state, name, buf, sizeof(buf), in);
638 ct->c_id = add (buf, ct->c_id);
643 /* Get Content-Description field */
644 if (!strcasecmp (name, DESCR_FIELD)) {
645 ct->c_descr = add (buf, ct->c_descr);
646 while (state == FLDPLUS) {
647 state = m_getfld (state, name, buf, sizeof(buf), in);
648 ct->c_descr = add (buf, ct->c_descr);
653 /* Get Content-MD5 field */
654 if (!strcasecmp (name, MD5_FIELD)) {
657 cp = add (buf, NULL);
658 while (state == FLDPLUS) {
659 state = m_getfld (state, name, buf, sizeof(buf), in);
668 if (ct->c_digested) {
669 advise (NULL, "message %s has multiple %s: fields (%s)",
670 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
677 while (isspace (*cp))
679 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
681 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
686 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
688 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
693 for (dp = cp; *dp && !isspace (*dp); dp++)
704 if (uprf (name, XXX_FIELD_PRF))
705 advise (NULL, "unknown field (%s) in message %s",
710 while (state == FLDPLUS)
711 state = m_getfld (state, name, buf, sizeof(buf), in);
714 if (state != FLDEOF) {
715 ct->c_begin = ftell (in) + 1;
722 ct->c_begin = ftell (in) - strlen (buf);
726 ct->c_begin = ftell (in);
731 adios (NULL, "message format error in component #%d", compnum);
734 adios (NULL, "getfld() returned %d", state);
740 * Check if we saw a Content-Type field.
741 * If not, then assign a default value for
742 * it, and the Init function.
746 * If we are inside a multipart/digest message,
747 * so default type is message/rfc822
750 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
752 ct->c_type = CT_MESSAGE;
753 ct->c_ctinitfnx = InitMessage;
756 * Else default type is text/plain
758 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
760 ct->c_type = CT_TEXT;
761 ct->c_ctinitfnx = InitText;
765 /* Use default Transfer-Encoding, if necessary */
767 ct->c_encoding = CE_7BIT;
780 * small routine to add header field to list
784 add_header (CT ct, char *name, char *value)
788 /* allocate header field structure */
789 hp = mh_xmalloc (sizeof(*hp));
791 /* link data into header structure */
796 /* link header structure into the list */
797 if (ct->c_first_hf == NULL) {
798 ct->c_first_hf = hp; /* this is the first */
801 ct->c_last_hf->next = hp; /* add it to the end */
810 * Used to parse both:
811 * 1) Content-Type line
812 * 2) composition directives
814 * and fills in the information of the CTinfo structure.
818 get_ctinfo (char *cp, CT ct, int magic)
821 char *dp, **ap, **ep;
826 i = strlen (invo_name) + 2;
828 /* store copy of Content-Type line */
829 cp = ct->c_ctline = add (cp, NULL);
831 while (isspace (*cp)) /* trim leading spaces */
834 /* change newlines to spaces */
835 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
838 /* trim trailing spaces */
839 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
845 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
847 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
850 for (dp = cp; istoken (*dp); dp++)
853 ci->ci_type = add (cp, NULL); /* store content type */
857 advise (NULL, "invalid %s: field in message %s (empty type)",
858 TYPE_FIELD, ct->c_file);
862 /* down case the content type string */
863 for (dp = ci->ci_type; *dp; dp++)
864 if (isalpha(*dp) && isupper (*dp))
867 while (isspace (*cp))
870 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
875 ci->ci_subtype = add ("", NULL);
880 while (isspace (*cp))
883 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
886 for (dp = cp; istoken (*dp); dp++)
889 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
892 if (!*ci->ci_subtype) {
894 "invalid %s: field in message %s (empty subtype for \"%s\")",
895 TYPE_FIELD, ct->c_file, ci->ci_type);
899 /* down case the content subtype string */
900 for (dp = ci->ci_subtype; *dp; dp++)
901 if (isalpha(*dp) && isupper (*dp))
905 while (isspace (*cp))
908 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
912 * Parse attribute/value pairs given with Content-Type
914 ep = (ap = ci->ci_attrs) + NPARMS;
920 "too many parameters in message %s's %s: field (%d max)",
921 ct->c_file, TYPE_FIELD, NPARMS);
926 while (isspace (*cp))
929 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
934 "extraneous trailing ';' in message %s's %s: parameter list",
935 ct->c_file, TYPE_FIELD);
939 /* down case the attribute name */
940 for (dp = cp; istoken (*dp); dp++)
941 if (isalpha(*dp) && isupper (*dp))
944 for (up = dp; isspace (*dp); )
946 if (dp == cp || *dp != '=') {
948 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
949 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
953 vp = (*ap = add (cp, NULL)) + (up - cp);
955 for (dp++; isspace (*dp); )
958 /* now add the attribute value */
959 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
962 for (cp = ++dp, dp = vp;;) {
967 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
968 ct->c_file, TYPE_FIELD, i, i, "", *ap);
973 if ((c = *cp++) == '\0')
988 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
994 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
995 ct->c_file, TYPE_FIELD, i, i, "", *ap);
1000 while (isspace (*cp))
1003 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1008 * Get any <Content-Id> given in buffer
1010 if (magic && *cp == '<') {
1015 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1016 advise (NULL, "invalid ID in message %s", ct->c_file);
1022 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1028 while (isspace (*cp))
1033 * Get any [Content-Description] given in buffer.
1035 if (magic && *cp == '[') {
1037 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1041 advise (NULL, "invalid description in message %s", ct->c_file);
1049 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1055 while (isspace (*cp))
1060 * Check if anything is left over
1064 ci->ci_magic = add (cp, NULL);
1067 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1068 ct->c_file, TYPE_FIELD, i, i, "", cp);
1076 get_comment (CT ct, char **ap, int istype)
1080 char c, buffer[BUFSIZ], *dp;
1089 switch (c = *cp++) {
1092 advise (NULL, "invalid comment in message %s's %s: field",
1093 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1098 if ((c = *cp++) == '\0')
1121 if ((dp = ci->ci_comment)) {
1122 ci->ci_comment = concat (dp, " ", buffer, NULL);
1125 ci->ci_comment = add (buffer, NULL);
1129 while (isspace (*cp))
1140 * Handles content types audio, image, and video.
1141 * There's not much to do right here.
1147 return OK; /* not much to do here */
1161 CI ci = &ct->c_ctinfo;
1163 /* check for missing subtype */
1164 if (!*ci->ci_subtype)
1165 ci->ci_subtype = add ("plain", ci->ci_subtype);
1168 for (kv = SubText; kv->kv_key; kv++)
1169 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1171 ct->c_subtype = kv->kv_value;
1173 /* allocate text character set structure */
1174 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1175 adios (NULL, "out of memory");
1176 ct->c_ctparams = (void *) t;
1178 /* initially mark character set as unspecified */
1179 t->tx_charset = CHARSET_UNSPECIFIED;
1181 /* scan for charset parameter */
1182 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1183 if (!strcasecmp (*ap, "charset"))
1186 /* check if content specified a character set */
1188 /* match character set or set to CHARSET_UNKNOWN */
1189 for (kv = Charset; kv->kv_key; kv++)
1190 if (!strcasecmp (*ep, kv->kv_key))
1192 t->tx_charset = kv->kv_value;
1204 InitMultiPart (CT ct)
1208 char *cp, *dp, **ap, **ep;
1209 char *bp, buffer[BUFSIZ];
1210 struct multipart *m;
1212 struct part *part, **next;
1213 CI ci = &ct->c_ctinfo;
1218 * The encoding for multipart messages must be either
1219 * 7bit, 8bit, or binary (per RFC2045).
1221 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1222 && ct->c_encoding != CE_BINARY) {
1224 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1225 ci->ci_type, ci->ci_subtype, ct->c_file);
1230 for (kv = SubMultiPart; kv->kv_key; kv++)
1231 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1233 ct->c_subtype = kv->kv_value;
1236 * Check for "boundary" parameter, which is
1237 * required for multipart messages.
1239 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1240 if (!strcasecmp (*ap, "boundary")) {
1246 /* complain if boundary parameter is missing */
1249 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1250 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1254 /* allocate primary structure for multipart info */
1255 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1256 adios (NULL, "out of memory");
1257 ct->c_ctparams = (void *) m;
1259 /* check if boundary parameter contains only whitespace characters */
1260 for (cp = bp; isspace (*cp); cp++)
1263 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1264 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1268 /* remove trailing whitespace from boundary parameter */
1269 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1274 /* record boundary separators */
1275 m->mp_start = concat (bp, "\n", NULL);
1276 m->mp_stop = concat (bp, "--\n", NULL);
1278 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1279 advise (ct->c_file, "unable to open for reading");
1283 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1285 next = &m->mp_parts;
1289 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1293 pos += strlen (buffer);
1294 if (buffer[0] != '-' || buffer[1] != '-')
1297 if (strcmp (buffer + 2, m->mp_start))
1300 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1301 adios (NULL, "out of memory");
1303 next = &part->mp_next;
1305 if (!(p = get_content (fp, ct->c_file,
1306 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1314 fseek (fp, pos, SEEK_SET);
1317 if (strcmp (buffer + 2, m->mp_start) == 0) {
1321 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1322 if (p->c_end < p->c_begin)
1323 p->c_begin = p->c_end;
1328 if (strcmp (buffer + 2, m->mp_stop) == 0)
1334 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1335 if (!inout && part) {
1337 p->c_end = ct->c_end;
1339 if (p->c_begin >= p->c_end) {
1340 for (next = &m->mp_parts; *next != part;
1341 next = &((*next)->mp_next))
1345 free ((char *) part);
1350 /* reverse the order of the parts for multipart/alternative */
1351 if (ct->c_subtype == MULTI_ALTERNATE)
1355 * label all subparts with part number, and
1356 * then initialize the content of the subpart.
1361 char partnam[BUFSIZ];
1364 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1365 pp = partnam + strlen (partnam);
1370 for (part = m->mp_parts, partnum = 1; part;
1371 part = part->mp_next, partnum++) {
1374 sprintf (pp, "%d", partnum);
1375 p->c_partno = add (partnam, NULL);
1377 /* initialize the content of the subparts */
1378 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1393 * reverse the order of the parts of a multipart
1397 reverse_parts (CT ct)
1400 struct multipart *m;
1401 struct part **base, **bmp, **next, *part;
1403 m = (struct multipart *) ct->c_ctparams;
1405 /* if only one part, just return */
1406 if (!m->mp_parts || !m->mp_parts->mp_next)
1409 /* count number of parts */
1411 for (part = m->mp_parts; part; part = part->mp_next)
1414 /* allocate array of pointers to the parts */
1415 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1416 adios (NULL, "out of memory");
1419 /* point at all the parts */
1420 for (part = m->mp_parts; part; part = part->mp_next)
1424 /* reverse the order of the parts */
1425 next = &m->mp_parts;
1426 for (bmp--; bmp >= base; bmp--) {
1429 next = &part->mp_next;
1433 /* free array of pointers */
1434 free ((char *) base);
1446 CI ci = &ct->c_ctinfo;
1448 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1450 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1451 ci->ci_type, ci->ci_subtype, ct->c_file);
1455 /* check for missing subtype */
1456 if (!*ci->ci_subtype)
1457 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1460 for (kv = SubMessage; kv->kv_key; kv++)
1461 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1463 ct->c_subtype = kv->kv_value;
1465 switch (ct->c_subtype) {
1466 case MESSAGE_RFC822:
1469 case MESSAGE_PARTIAL:
1474 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1475 adios (NULL, "out of memory");
1476 ct->c_ctparams = (void *) p;
1478 /* scan for parameters "id", "number", and "total" */
1479 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1480 if (!strcasecmp (*ap, "id")) {
1481 p->pm_partid = add (*ep, NULL);
1484 if (!strcasecmp (*ap, "number")) {
1485 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1486 || p->pm_partno < 1) {
1489 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1490 *ap, ci->ci_type, ci->ci_subtype,
1491 ct->c_file, TYPE_FIELD);
1496 if (!strcasecmp (*ap, "total")) {
1497 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1506 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1508 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1509 ci->ci_type, ci->ci_subtype,
1510 ct->c_file, TYPE_FIELD);
1516 case MESSAGE_EXTERNAL:
1523 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1524 adios (NULL, "out of memory");
1525 ct->c_ctparams = (void *) e;
1528 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1529 advise (ct->c_file, "unable to open for reading");
1533 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1535 if (!(p = get_content (fp, ct->c_file, 0))) {
1544 if ((exresult = params_external (ct, 0)) != NOTOK
1545 && p->c_ceopenfnx == openMail) {
1549 if ((size = ct->c_end - p->c_begin) <= 0) {
1551 content_error (NULL, ct,
1552 "empty body for access-type=mail-server");
1556 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1557 fseek (p->c_fp, p->c_begin, SEEK_SET);
1559 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1561 adios ("failed", "fread");
1564 adios (NULL, "unexpected EOF from fread");
1567 bp += cc, size -= cc;
1574 p->c_end = p->c_begin;
1579 if (exresult == NOTOK)
1581 if (e->eb_flags == NOTOK)
1584 switch (p->c_type) {
1589 if (p->c_subtype != MESSAGE_RFC822)
1593 e->eb_partno = ct->c_partno;
1595 (*p->c_ctinitfnx) (p);
1610 params_external (CT ct, int composing)
1613 struct exbody *e = (struct exbody *) ct->c_ctparams;
1614 CI ci = &ct->c_ctinfo;
1616 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1617 if (!strcasecmp (*ap, "access-type")) {
1618 struct str2init *s2i;
1619 CT p = e->eb_content;
1621 for (s2i = str2methods; s2i->si_key; s2i++)
1622 if (!strcasecmp (*ep, s2i->si_key))
1627 e->eb_flags = NOTOK;
1628 p->c_encoding = CE_EXTERNAL;
1631 e->eb_access = s2i->si_key;
1632 e->eb_flags = s2i->si_val;
1633 p->c_encoding = CE_EXTERNAL;
1635 /* Call the Init function for this external type */
1636 if ((*s2i->si_init)(p) == NOTOK)
1640 if (!strcasecmp (*ap, "name")) {
1644 if (!strcasecmp (*ap, "permission")) {
1645 e->eb_permission = *ep;
1648 if (!strcasecmp (*ap, "site")) {
1652 if (!strcasecmp (*ap, "directory")) {
1656 if (!strcasecmp (*ap, "mode")) {
1660 if (!strcasecmp (*ap, "size")) {
1661 sscanf (*ep, "%lu", &e->eb_size);
1664 if (!strcasecmp (*ap, "server")) {
1668 if (!strcasecmp (*ap, "subject")) {
1669 e->eb_subject = *ep;
1672 if (composing && !strcasecmp (*ap, "body")) {
1673 e->eb_body = getcpy (*ep);
1678 if (!e->eb_access) {
1680 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1681 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1694 InitApplication (CT ct)
1697 CI ci = &ct->c_ctinfo;
1700 for (kv = SubApplication; kv->kv_key; kv++)
1701 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1703 ct->c_subtype = kv->kv_value;
1710 * Set up structures for placing unencoded
1711 * content when building parts.
1715 init_decoded_content (CT ct)
1719 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1720 adios (NULL, "out of memory");
1723 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1724 ct->c_ceclosefnx = close_encoding;
1725 ct->c_cesizefnx = NULL; /* since unencoded */
1732 * TRANSFER ENCODINGS
1736 init_encoding (CT ct, OpenCEFunc openfnx)
1740 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1741 adios (NULL, "out of memory");
1744 ct->c_ceopenfnx = openfnx;
1745 ct->c_ceclosefnx = close_encoding;
1746 ct->c_cesizefnx = size_encoding;
1753 close_encoding (CT ct)
1757 if (!(ce = ct->c_cefile))
1767 static unsigned long
1768 size_encoding (CT ct)
1776 if (!(ce = ct->c_cefile))
1777 return (ct->c_end - ct->c_begin);
1779 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1780 return (long) st.st_size;
1783 if (stat (ce->ce_file, &st) != NOTOK)
1784 return (long) st.st_size;
1789 if (ct->c_encoding == CE_EXTERNAL)
1790 return (ct->c_end - ct->c_begin);
1793 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1794 return (ct->c_end - ct->c_begin);
1796 if (fstat (fd, &st) != NOTOK)
1797 size = (long) st.st_size;
1801 (*ct->c_ceclosefnx) (ct);
1810 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1816 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1817 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1818 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1819 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1820 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1821 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1822 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1823 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1824 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1825 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1826 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1833 return init_encoding (ct, openBase64);
1838 openBase64 (CT ct, char **file)
1840 int bitno, cc, digested;
1843 unsigned char value, *b, *b1, *b2, *b3;
1844 char *cp, *ep, buffer[BUFSIZ];
1848 b = (unsigned char *) &bits;
1849 b1 = &b[endian > 0 ? 1 : 2];
1850 b2 = &b[endian > 0 ? 2 : 1];
1851 b3 = &b[endian > 0 ? 3 : 0];
1855 fseek (ce->ce_fp, 0L, SEEK_SET);
1860 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1861 content_error (ce->ce_file, ct, "unable to fopen for reading");
1867 if (*file == NULL) {
1868 ce->ce_file = add (m_scratch ("", tmp), NULL);
1871 ce->ce_file = add (*file, NULL);
1875 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1876 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1880 if ((len = ct->c_end - ct->c_begin) < 0)
1881 adios (NULL, "internal error(1)");
1883 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1884 content_error (ct->c_file, ct, "unable to open for reading");
1888 if ((digested = ct->c_digested))
1889 MD5Init (&mdContext);
1895 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1897 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1899 content_error (ct->c_file, ct, "error reading from");
1903 content_error (NULL, ct, "premature eof");
1911 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1916 if (skip || (*cp & 0x80)
1917 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1919 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1921 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1924 content_error (NULL, ct,
1925 "invalid BASE64 encoding -- continuing");
1929 bits |= value << bitno;
1931 if ((bitno -= 6) < 0) {
1932 putc ((char) *b1, ce->ce_fp);
1934 MD5Update (&mdContext, b1, 1);
1936 putc ((char) *b2, ce->ce_fp);
1938 MD5Update (&mdContext, b2, 1);
1940 putc ((char) *b3, ce->ce_fp);
1942 MD5Update (&mdContext, b3, 1);
1946 if (ferror (ce->ce_fp)) {
1947 content_error (ce->ce_file, ct,
1948 "error writing to");
1951 bitno = 18, bits = 0L, skip = 0;
1957 goto self_delimiting;
1966 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1968 content_error (NULL, ct, "invalid BASE64 encoding");
1973 fseek (ct->c_fp, 0L, SEEK_SET);
1975 if (fflush (ce->ce_fp)) {
1976 content_error (ce->ce_file, ct, "error writing to");
1981 unsigned char digest[16];
1983 MD5Final (digest, &mdContext);
1984 if (memcmp((char *) digest, (char *) ct->c_digest,
1985 sizeof(digest) / sizeof(digest[0])))
1986 content_error (NULL, ct,
1987 "content integrity suspect (digest mismatch) -- continuing");
1990 fprintf (stderr, "content integrity confirmed\n");
1993 fseek (ce->ce_fp, 0L, SEEK_SET);
1996 *file = ce->ce_file;
1997 return fileno (ce->ce_fp);
2000 free_encoding (ct, 0);
2009 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2016 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2017 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2018 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2019 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2020 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2021 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2022 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2023 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2024 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2025 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2032 return init_encoding (ct, openQuoted);
2037 openQuoted (CT ct, char **file)
2039 int cc, digested, len, quoted;
2041 char buffer[BUFSIZ];
2048 fseek (ce->ce_fp, 0L, SEEK_SET);
2053 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2054 content_error (ce->ce_file, ct, "unable to fopen for reading");
2060 if (*file == NULL) {
2061 ce->ce_file = add (m_scratch ("", tmp), NULL);
2064 ce->ce_file = add (*file, NULL);
2068 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2069 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2073 if ((len = ct->c_end - ct->c_begin) < 0)
2074 adios (NULL, "internal error(2)");
2076 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2077 content_error (ct->c_file, ct, "unable to open for reading");
2081 if ((digested = ct->c_digested))
2082 MD5Init (&mdContext);
2089 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2093 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2094 content_error (NULL, ct, "premature eof");
2098 if ((cc = strlen (buffer)) > len)
2102 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2107 for (; cp < ep; cp++) {
2110 if (!isxdigit (*cp)) {
2112 dp = "expecting hexidecimal-digit";
2113 goto invalid_encoding;
2116 mask |= hex2nib[*cp & 0x7f];
2117 putc (mask, ce->ce_fp);
2119 MD5Update (&mdContext, &mask, 1);
2123 putc (*cp, ce->ce_fp);
2125 MD5Update (&mdContext, (unsigned char *) ":", 1);
2129 if (!isxdigit (*cp))
2131 mask = hex2nib[*cp & 0x7f];
2137 if (ferror (ce->ce_fp)) {
2138 content_error (ce->ce_file, ct, "error writing to");
2147 if (*cp < '!' || *cp > '~') {
2149 dp = "expecting character in range [!..~]";
2152 i = strlen (invo_name) + 2;
2153 content_error (NULL, ct,
2154 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2162 putc (*cp, ce->ce_fp);
2165 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2167 MD5Update (&mdContext, (unsigned char *) cp, 1);
2169 if (ferror (ce->ce_fp)) {
2170 content_error (ce->ce_file, ct, "error writing to");
2176 if (*++cp != '\n') {
2185 content_error (NULL, ct,
2186 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2190 fseek (ct->c_fp, 0L, SEEK_SET);
2192 if (fflush (ce->ce_fp)) {
2193 content_error (ce->ce_file, ct, "error writing to");
2198 unsigned char digest[16];
2200 MD5Final (digest, &mdContext);
2201 if (memcmp((char *) digest, (char *) ct->c_digest,
2202 sizeof(digest) / sizeof(digest[0])))
2203 content_error (NULL, ct,
2204 "content integrity suspect (digest mismatch) -- continuing");
2207 fprintf (stderr, "content integrity confirmed\n");
2210 fseek (ce->ce_fp, 0L, SEEK_SET);
2213 *file = ce->ce_file;
2214 return fileno (ce->ce_fp);
2217 free_encoding (ct, 0);
2229 if (init_encoding (ct, open7Bit) == NOTOK)
2232 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2238 open7Bit (CT ct, char **file)
2241 char buffer[BUFSIZ];
2246 fseek (ce->ce_fp, 0L, SEEK_SET);
2251 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2252 content_error (ce->ce_file, ct, "unable to fopen for reading");
2258 if (*file == NULL) {
2259 ce->ce_file = add (m_scratch ("", tmp), NULL);
2262 ce->ce_file = add (*file, NULL);
2266 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2267 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2271 if (ct->c_type == CT_MULTIPART) {
2273 CI ci = &ct->c_ctinfo;
2276 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2277 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2278 + 1 + strlen (ci->ci_subtype);
2279 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2280 putc (';', ce->ce_fp);
2283 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2285 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2286 fputs ("\n\t", ce->ce_fp);
2289 putc (' ', ce->ce_fp);
2292 fprintf (ce->ce_fp, "%s", buffer);
2296 if (ci->ci_comment) {
2297 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2298 fputs ("\n\t", ce->ce_fp);
2302 putc (' ', ce->ce_fp);
2305 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2308 fprintf (ce->ce_fp, "\n");
2310 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2312 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2313 fprintf (ce->ce_fp, "\n");
2316 if ((len = ct->c_end - ct->c_begin) < 0)
2317 adios (NULL, "internal error(3)");
2319 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2320 content_error (ct->c_file, ct, "unable to open for reading");
2324 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2326 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2328 content_error (ct->c_file, ct, "error reading from");
2332 content_error (NULL, ct, "premature eof");
2340 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2341 if (ferror (ce->ce_fp)) {
2342 content_error (ce->ce_file, ct, "error writing to");
2347 fseek (ct->c_fp, 0L, SEEK_SET);
2349 if (fflush (ce->ce_fp)) {
2350 content_error (ce->ce_file, ct, "error writing to");
2354 fseek (ce->ce_fp, 0L, SEEK_SET);
2357 *file = ce->ce_file;
2358 return fileno (ce->ce_fp);
2361 free_encoding (ct, 0);
2371 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2373 char cachefile[BUFSIZ];
2376 fseek (ce->ce_fp, 0L, SEEK_SET);
2381 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2382 content_error (ce->ce_file, ct, "unable to fopen for reading");
2388 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2389 cachefile, sizeof(cachefile)) != NOTOK) {
2390 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2391 ce->ce_file = getcpy (cachefile);
2395 admonish (cachefile, "unable to fopen for reading");
2402 *file = ce->ce_file;
2403 *fd = fileno (ce->ce_fp);
2414 return init_encoding (ct, openFile);
2419 openFile (CT ct, char **file)
2422 char cachefile[BUFSIZ];
2423 struct exbody *e = ct->c_ctexbody;
2424 CE ce = ct->c_cefile;
2426 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2438 content_error (NULL, ct, "missing name parameter");
2442 ce->ce_file = getcpy (e->eb_name);
2445 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2446 content_error (ce->ce_file, ct, "unable to fopen for reading");
2450 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2451 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2452 cachefile, sizeof(cachefile)) != NOTOK) {
2456 mask = umask (cachetype ? ~m_gmprot () : 0222);
2457 if ((fp = fopen (cachefile, "w"))) {
2459 char buffer[BUFSIZ];
2460 FILE *gp = ce->ce_fp;
2462 fseek (gp, 0L, SEEK_SET);
2464 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2466 fwrite (buffer, sizeof(*buffer), cc, fp);
2470 admonish (ce->ce_file, "error reading");
2475 admonish (cachefile, "error writing");
2483 fseek (ce->ce_fp, 0L, SEEK_SET);
2484 *file = ce->ce_file;
2485 return fileno (ce->ce_fp);
2495 return init_encoding (ct, openFTP);
2500 openFTP (CT ct, char **file)
2502 int cachetype, caching, fd;
2504 char *bp, *ftp, *user, *pass;
2505 char buffer[BUFSIZ], cachefile[BUFSIZ];
2508 static char *username = NULL;
2509 static char *password = NULL;
2514 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2522 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2533 if (!e->eb_name || !e->eb_site) {
2534 content_error (NULL, ct, "missing %s parameter",
2535 e->eb_name ? "site": "name");
2542 pidcheck (pidwait (xpid, NOTOK));
2546 /* Get the buffer ready to go */
2548 buflen = sizeof(buffer);
2551 * Construct the query message for user
2553 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2559 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2565 snprintf (bp, buflen, "\n using %sFTP from site %s",
2566 e->eb_flags ? "anonymous " : "", e->eb_site);
2571 if (e->eb_size > 0) {
2572 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2577 snprintf (bp, buflen, "? ");
2580 * Now, check the answer
2582 if (!getanswer (buffer))
2587 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2590 ruserpass (e->eb_site, &username, &password);
2595 ce->ce_unlink = (*file == NULL);
2597 cachefile[0] = '\0';
2598 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2599 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2600 cachefile, sizeof(cachefile)) != NOTOK) {
2601 if (*file == NULL) {
2608 ce->ce_file = add (*file, NULL);
2610 ce->ce_file = add (cachefile, NULL);
2612 ce->ce_file = add (m_scratch ("", tmp), NULL);
2614 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2615 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2623 int child_id, i, vecp;
2627 vec[vecp++] = r1bindex (ftp, '/');
2628 vec[vecp++] = e->eb_site;
2631 vec[vecp++] = e->eb_dir;
2632 vec[vecp++] = e->eb_name;
2633 vec[vecp++] = ce->ce_file,
2634 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2635 ? "ascii" : "binary";
2640 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2644 adios ("fork", "unable to");
2648 close (fileno (ce->ce_fp));
2650 fprintf (stderr, "unable to exec ");
2656 if (pidXwait (child_id, NULL)) {
2660 username = password = NULL;
2669 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2671 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2678 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2683 mask = umask (cachetype ? ~m_gmprot () : 0222);
2684 if ((fp = fopen (cachefile, "w"))) {
2686 FILE *gp = ce->ce_fp;
2688 fseek (gp, 0L, SEEK_SET);
2690 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2692 fwrite (buffer, sizeof(*buffer), cc, fp);
2696 admonish (ce->ce_file, "error reading");
2701 admonish (cachefile, "error writing");
2710 fseek (ce->ce_fp, 0L, SEEK_SET);
2711 *file = ce->ce_file;
2712 return fileno (ce->ce_fp);
2723 return init_encoding (ct, openMail);
2728 openMail (CT ct, char **file)
2730 int child_id, fd, i, vecp;
2732 char *bp, buffer[BUFSIZ], *vec[7];
2733 struct exbody *e = ct->c_ctexbody;
2734 CE ce = ct->c_cefile;
2736 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2747 if (!e->eb_server) {
2748 content_error (NULL, ct, "missing server parameter");
2755 pidcheck (pidwait (xpid, NOTOK));
2759 /* Get buffer ready to go */
2761 buflen = sizeof(buffer);
2763 /* Now construct query message */
2764 snprintf (bp, buflen, "Retrieve content");
2770 snprintf (bp, buflen, " %s", e->eb_partno);
2776 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2778 e->eb_subject ? e->eb_subject : e->eb_body);
2780 /* Now, check answer */
2781 if (!getanswer (buffer))
2785 vec[vecp++] = r1bindex (mailproc, '/');
2786 vec[vecp++] = e->eb_server;
2787 vec[vecp++] = "-subject";
2788 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2789 vec[vecp++] = "-body";
2790 vec[vecp++] = e->eb_body;
2793 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2797 advise ("fork", "unable to");
2801 execvp (mailproc, vec);
2802 fprintf (stderr, "unable to exec ");
2808 if (pidXwait (child_id, NULL) == OK)
2809 advise (NULL, "request sent");
2813 if (*file == NULL) {
2814 ce->ce_file = add (m_scratch ("", tmp), NULL);
2817 ce->ce_file = add (*file, NULL);
2821 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2822 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2826 fseek (ce->ce_fp, 0L, SEEK_SET);
2827 *file = ce->ce_file;
2828 return fileno (ce->ce_fp);
2833 fgetstr (char *s, int n, FILE *stream)
2837 for (ep = (cp = s) + n; cp < ep; ) {
2840 if (!fgets (cp, n, stream))
2841 return (cp != s ? s : NULL);
2842 if (cp == s && *cp != '#')
2845 cp += (i = strlen (cp)) - 1;
2846 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2857 * Parse the composition draft for text and directives.
2858 * Do initial setup of Content structure.
2862 user_content (FILE *in, char *file, char *buf, CT *ctp)
2866 char buffer[BUFSIZ];
2867 struct multipart *m;
2870 struct str2init *s2i;
2875 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2880 /* allocate basic Content structure */
2881 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2882 adios (NULL, "out of memory");
2885 /* allocate basic structure for handling decoded content */
2886 init_decoded_content (ct);
2893 * Handle inline text. Check if line
2894 * is one of the following forms:
2896 * 1) doesn't begin with '#' (implicit directive)
2897 * 2) begins with "##" (implicit directive)
2898 * 3) begins with "#<"
2900 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2904 char content[BUFSIZ];
2907 /* use a temp file to collect the plain text lines */
2908 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2911 if ((out = fopen (ce->ce_file, "w")) == NULL)
2912 adios (ce->ce_file, "unable to open for writing");
2914 if (buf[0] == '#' && buf[1] == '<') {
2915 strncpy (content, buf + 2, sizeof(content));
2922 /* the directive is implicit */
2923 strncpy (content, "text/plain", sizeof(content));
2925 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2929 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2930 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2934 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2935 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2936 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2937 switch (buffer[0]) {
2944 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
2952 if (headers != 1 || buffer[0] != '\n')
2953 fputs (buffer, out);
2958 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
2960 if (buffer[0] == '#') {
2963 if (buffer[1] != '#')
2965 for (cp = (bp = buffer) + 1; *cp; cp++)
2972 ct->c_end = ftell (out);
2975 /* parse content type */
2976 if (get_ctinfo (content, ct, inlineD) == NOTOK)
2979 for (s2i = str2cts; s2i->si_key; s2i++)
2980 if (!strcasecmp (ci->ci_type, s2i->si_key))
2982 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
2986 * check type specified (possibly implicitly)
2988 switch (ct->c_type = s2i->si_val) {
2990 if (!strcasecmp (ci->ci_subtype, "rfc822")) {
2991 ct->c_encoding = CE_7BIT;
2996 adios (NULL, "it doesn't make sense to define an in-line %s content",
2997 ct->c_type == CT_MESSAGE ? "message" : "multipart");
3002 if ((ct->c_ctinitfnx = s2i->si_init))
3003 (*ct->c_ctinitfnx) (ct);
3008 fseek (in, pos, SEEK_SET);
3013 * If we've reached this point, the next line
3014 * must be some type of explicit directive.
3017 /* check if directive is external-type */
3018 extrnal = (buf[1] == '@');
3020 /* parse directive */
3021 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3024 /* check directive against the list of MIME types */
3025 for (s2i = str2cts; s2i->si_key; s2i++)
3026 if (!strcasecmp (ci->ci_type, s2i->si_key))
3030 * Check if the directive specified a valid type.
3031 * This will happen if it was one of the following forms:
3033 * #type/subtype (or)
3037 if (!ci->ci_subtype)
3038 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3040 switch (ct->c_type = s2i->si_val) {
3042 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3043 ci->ci_type, ci->ci_subtype);
3047 if (!strcasecmp (ci->ci_subtype, "partial"))
3048 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3049 ci->ci_type, ci->ci_subtype);
3050 if (!strcasecmp (ci->ci_subtype, "external-body"))
3051 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3052 ci->ci_type, ci->ci_subtype);
3055 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3056 ci->ci_type, ci->ci_subtype);
3060 if ((ct->c_ctinitfnx = s2i->si_init))
3061 (*ct->c_ctinitfnx) (ct);
3066 * #@type/subtype (external types directive)
3073 adios (NULL, "need external information for \"#@%s/%s\"",
3074 ci->ci_type, ci->ci_subtype);
3077 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3078 free (ci->ci_magic);
3079 ci->ci_magic = NULL;
3082 * Since we are using the current Content structure to
3083 * hold information about the type of the external
3084 * reference, we need to create another Content structure
3085 * for the message/external-body to wrap it in.
3087 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3088 adios (NULL, "out of memory");
3091 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3093 ct->c_type = CT_MESSAGE;
3094 ct->c_subtype = MESSAGE_EXTERNAL;
3096 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3097 adios (NULL, "out of memory");
3098 ct->c_ctparams = (void *) e;
3104 if (params_external (ct, 1) == NOTOK)
3110 /* Handle [file] argument */
3112 /* check if specifies command to execute */
3113 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3114 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3117 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3118 cp = add (cp, NULL);
3119 free (ci->ci_magic);
3122 /* record filename of decoded contents */
3123 ce->ce_file = ci->ci_magic;
3124 if (access (ce->ce_file, R_OK) == NOTOK)
3125 adios ("reading", "unable to access %s for", ce->ce_file);
3126 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3127 ct->c_end = (long) st.st_size;
3128 ci->ci_magic = NULL;
3134 * No [file] argument, so check profile for
3135 * method to compose content.
3137 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3138 invo_name, ci->ci_type, ci->ci_subtype);
3139 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3140 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3141 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3142 content_error (NULL, ct, "don't know how to compose content");
3146 ci->ci_magic = add (cp, NULL);
3151 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3155 * #forw [+folder] [msgs]
3157 if (!strcasecmp (ci->ci_type, "forw")) {
3159 char *folder, *arguments[MAXARGS];
3163 ap = brkstring (ci->ci_magic, " ", "\n");
3164 copyip (ap, arguments, MAXARGS);
3166 arguments[0] = "cur";
3167 arguments[1] = NULL;
3171 /* search the arguments for a folder name */
3172 for (ap = arguments; *ap; ap++) {
3174 if (*cp == '+' || *cp == '@') {
3176 adios (NULL, "only one folder per #forw directive");
3178 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3182 /* else, use the current folder */
3184 folder = add (getfolder (1), NULL);
3186 if (!(mp = folder_read (folder)))
3187 adios (NULL, "unable to read folder %s", folder);
3188 for (ap = arguments; *ap; ap++) {
3190 if (*cp != '+' && *cp != '@')
3191 if (!m_convert (mp, cp))
3198 * If there is more than one message to include, make this
3199 * a content of type "multipart/digest" and insert each message
3200 * as a subpart. If there is only one message, then make this
3201 * a content of type "message/rfc822".
3203 if (mp->numsel > 1) {
3204 /* we are forwarding multiple messages */
3205 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3207 ct->c_type = CT_MULTIPART;
3208 ct->c_subtype = MULTI_DIGEST;
3210 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3211 adios (NULL, "out of memory");
3212 ct->c_ctparams = (void *) m;
3215 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3216 if (is_selected(mp, msgnum)) {
3221 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3222 adios (NULL, "out of memory");
3223 init_decoded_content (p);
3225 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3227 p->c_type = CT_MESSAGE;
3228 p->c_subtype = MESSAGE_RFC822;
3230 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3231 pe->ce_file = add (buffer, NULL);
3232 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3233 p->c_end = (long) st.st_size;
3235 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3236 adios (NULL, "out of memory");
3238 pp = &part->mp_next;
3243 /* we are forwarding one message */
3244 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3246 ct->c_type = CT_MESSAGE;
3247 ct->c_subtype = MESSAGE_RFC822;
3249 msgnum = mp->lowsel;
3250 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3251 ce->ce_file = add (buffer, NULL);
3252 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3253 ct->c_end = (long) st.st_size;
3256 folder_free (mp); /* free folder/message structure */
3263 if (!strcasecmp (ci->ci_type, "end")) {
3270 * #begin [ alternative | parallel ]
3272 if (!strcasecmp (ci->ci_type, "begin")) {
3273 if (!ci->ci_magic) {
3275 cp = SubMultiPart[vrsn - 1].kv_key;
3276 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3277 vrsn = MULTI_ALTERNATE;
3278 cp = SubMultiPart[vrsn - 1].kv_key;
3279 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3280 vrsn = MULTI_PARALLEL;
3281 cp = SubMultiPart[vrsn - 1].kv_key;
3282 } else if (uprf (ci->ci_magic, "digest")) {
3285 vrsn = MULTI_UNKNOWN;
3290 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3291 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3293 ct->c_type = CT_MULTIPART;
3294 ct->c_subtype = vrsn;
3296 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3297 adios (NULL, "out of memory");
3298 ct->c_ctparams = (void *) m;
3301 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3305 if (user_content (in, file, buffer, &p) == DONE) {
3307 adios (NULL, "empty \"#begin ... #end\" sequence");
3313 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3314 adios (NULL, "out of memory");
3316 pp = &part->mp_next;
3319 admonish (NULL, "premature end-of-file, missing #end");
3326 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3327 return NOTOK; /* NOT REACHED */
3332 set_id (CT ct, int top)
3336 static time_t clock = 0;
3337 static char *msgfmt;
3341 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3342 (int) getpid(), (long) clock, LocalName());
3344 msgfmt = getcpy(msgid);
3346 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3347 ct->c_id = getcpy (msgid);
3351 static char ebcdicsafe[0x100] = {
3352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3353 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3356 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3357 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3358 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3359 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3360 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3361 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3362 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3363 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3364 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3365 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3366 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3367 0x01, 0x01, 0x01, 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,
3383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3388 * Fill out, or expand the various contents in the composition
3389 * draft. Read-in any necessary files. Parse and execute any
3390 * commands specified by profile composition strings.
3394 compose_content (CT ct)
3396 CE ce = ct->c_cefile;
3398 switch (ct->c_type) {
3403 char partnam[BUFSIZ];
3404 struct multipart *m = (struct multipart *) ct->c_ctparams;
3408 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3409 pp = partnam + strlen (partnam);
3414 /* first, we call compose_content on all the subparts */
3415 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3416 CT p = part->mp_part;
3418 sprintf (pp, "%d", partnum);
3419 p->c_partno = add (partnam, NULL);
3420 if (compose_content (p) == NOTOK)
3425 * If the -rfc934mode switch is given, then check all
3426 * the subparts of a multipart/digest. If they are all
3427 * message/rfc822, then mark this content and all
3428 * subparts with the rfc934 compatibility mode flag.
3430 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3433 for (part = m->mp_parts; part; part = part->mp_next) {
3434 CT p = part->mp_part;
3436 if (p->c_subtype != MESSAGE_RFC822) {
3441 ct->c_rfc934 = is934;
3442 for (part = m->mp_parts; part; part = part->mp_next) {
3443 CT p = part->mp_part;
3445 if ((p->c_rfc934 = is934))
3451 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3455 for (part = m->mp_parts; part; part = part->mp_next)
3456 ct->c_end += part->mp_part->c_end + partnum;
3462 /* Nothing to do for type message */
3466 * Discrete types (text/application/audio/image/video)
3471 int i, xstdout, len, buflen;
3472 char *bp, **ap, *cp;
3473 char *vec[4], buffer[BUFSIZ];
3475 CI ci = &ct->c_ctinfo;
3477 if (!(cp = ci->ci_magic))
3478 adios (NULL, "internal error(5)");
3480 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3485 /* Get buffer ready to go */
3488 buflen = sizeof(buffer);
3491 * Parse composition string into buffer
3493 for ( ; *cp; cp++) {
3498 /* insert parameters from directive */
3502 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3503 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3513 /* %f, and stdout is not-redirected */
3519 * insert temporary filename where
3520 * content should be written
3522 snprintf (bp, buflen, "%s", ce->ce_file);
3526 /* insert content subtype */
3527 strncpy (bp, ci->ci_subtype, buflen);
3531 /* insert character % */
3552 printf ("composing content %s/%s from command\n\t%s\n",
3553 ci->ci_type, ci->ci_subtype, buffer);
3555 fflush (stdout); /* not sure if need for -noverbose */
3562 if ((out = fopen (ce->ce_file, "w")) == NULL)
3563 adios (ce->ce_file, "unable to open for writing");
3565 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3569 adios ("fork", "unable to fork");
3574 dup2 (fileno (out), 1);
3575 close (fileno (out));
3576 execvp ("/bin/sh", vec);
3577 fprintf (stderr, "unable to exec ");
3584 if (pidXwait(child_id, NULL))
3590 /* Check size of file */
3591 if (listsw && ct->c_end == 0L) {
3594 if (stat (ce->ce_file, &st) != NOTOK)
3595 ct->c_end = (long) st.st_size;
3607 * 1) choose a transfer encoding.
3608 * 2) check for clashes with multipart boundary string.
3609 * 3) for text content, figure out which character set is being used.
3611 * If there is a clash with one of the contents and the multipart boundary,
3612 * this function will exit with NOTOK. This will cause the scanning process
3613 * to be repeated with a different multipart boundary. It is possible
3614 * (although highly unlikely) that this scan will be repeated multiple times.
3618 scan_content (CT ct)
3621 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3622 int checklinelen, linelen = 0; /* check for long lines */
3623 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3624 int checklinespace, linespace = 0; /* check if any line ends with space */
3625 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3626 char *cp, buffer[BUFSIZ];
3629 CE ce = ct->c_cefile;
3632 * handle multipart by scanning all subparts
3633 * and then checking their encoding.
3635 if (ct->c_type == CT_MULTIPART) {
3636 struct multipart *m = (struct multipart *) ct->c_ctparams;
3639 /* initially mark the domain of enclosing multipart as 7bit */
3640 ct->c_encoding = CE_7BIT;
3642 for (part = m->mp_parts; part; part = part->mp_next) {
3643 CT p = part->mp_part;
3645 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3648 /* if necessary, enlarge encoding for enclosing multipart */
3649 if (p->c_encoding == CE_BINARY)
3650 ct->c_encoding = CE_BINARY;
3651 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3652 ct->c_encoding = CE_8BIT;
3659 * Decide what to check while scanning this content.
3661 switch (ct->c_type) {
3665 if (ct->c_subtype == TEXT_PLAIN) {
3670 checkebcdic = ebcdicsw;
3676 case CT_APPLICATION:
3678 checkebcdic = ebcdicsw;
3690 /* don't check anything for message/external */
3691 if (ct->c_subtype == MESSAGE_EXTERNAL)
3701 * Don't check anything for these types,
3702 * since we are forcing use of base64.
3713 * Scan the unencoded content
3715 if (check8bit || checklinelen || checklinespace || checkboundary) {
3716 if ((in = fopen (ce->ce_file, "r")) == NULL)
3717 adios (ce->ce_file, "unable to open for reading");
3718 len = strlen (prefix);
3720 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3722 * Check for 8bit data.
3725 for (cp = buffer; *cp; cp++) {
3726 if (!isascii (*cp)) {
3728 check8bit = 0; /* no need to keep checking */
3731 * Check if character is ebcdic-safe. We only check
3732 * this if also checking for 8bit data.
3734 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3736 checkebcdic = 0; /* no need to keep checking */
3742 * Check line length.
3744 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3746 checklinelen = 0; /* no need to keep checking */
3750 * Check if line ends with a space.
3752 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3754 checklinespace = 0; /* no need to keep checking */
3758 * Check if content contains a line that clashes
3759 * with our standard boundary for multipart messages.
3761 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3762 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3766 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3768 checkboundary = 0; /* no need to keep checking */
3776 * Decide which transfer encoding to use.
3778 switch (ct->c_type) {
3781 * If the text content didn't specify a character
3782 * set, we need to figure out which one was used.
3784 t = (struct text *) ct->c_ctparams;
3785 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3786 CI ci = &ct->c_ctinfo;
3789 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3793 t->tx_charset = CHARSET_UNKNOWN;
3794 *ap = concat ("charset=", write_charset_8bit(), NULL);
3796 t->tx_charset = CHARSET_USASCII;
3797 *ap = add ("charset=us-ascii", NULL);
3800 cp = strchr(*ap++, '=');
3806 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3807 ct->c_encoding = CE_QUOTED;
3809 ct->c_encoding = CE_7BIT;
3812 case CT_APPLICATION:
3813 /* For application type, use base64, except when postscript */
3814 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3815 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3816 ? CE_QUOTED : CE_BASE64;
3818 ct->c_encoding = CE_7BIT;
3822 ct->c_encoding = CE_7BIT;
3828 /* For audio, image, and video contents, just use base64 */
3829 ct->c_encoding = CE_BASE64;
3833 return (boundaryclash ? NOTOK : OK);
3838 * Scan the content structures, and build header
3839 * fields that will need to be output into the
3844 build_headers (CT ct)
3846 int cc, mailbody, len;
3848 char *np, *vp, buffer[BUFSIZ];
3849 CI ci = &ct->c_ctinfo;
3852 * If message is type multipart, then add the multipart
3853 * boundary to the list of attribute/value pairs.
3855 if (ct->c_type == CT_MULTIPART) {
3857 static int level = 0; /* store nesting level */
3861 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3862 cp = strchr(*ap++ = add (buffer, NULL), '=');
3869 * Skip the output of Content-Type, parameters, content
3870 * description, and Content-ID if the content is of type
3871 * "message" and the rfc934 compatibility flag is set
3872 * (which means we are inside multipart/digest and the
3873 * switch -rfc934mode was given).
3875 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3879 * output the content type and subtype
3881 np = add (TYPE_FIELD, NULL);
3882 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3884 /* keep track of length of line */
3885 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3886 + strlen (ci->ci_subtype) + 3;
3888 mailbody = ct->c_type == CT_MESSAGE
3889 && ct->c_subtype == MESSAGE_EXTERNAL
3890 && ((struct exbody *) ct->c_ctparams)->eb_body;
3893 * Append the attribute/value pairs to
3894 * the end of the Content-Type line.
3896 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3897 if (mailbody && !strcasecmp (*ap, "body"))
3903 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3904 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3905 vp = add ("\n\t", vp);
3911 vp = add (buffer, vp);
3916 * Append any RFC-822 comment to the end of
3917 * the Content-Type line.
3919 if (ci->ci_comment) {
3920 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3921 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3922 vp = add ("\n\t", vp);
3928 vp = add (buffer, vp);
3931 vp = add ("\n", vp);
3932 add_header (ct, np, vp);
3935 * output the Content-ID
3938 np = add (ID_FIELD, NULL);
3939 vp = concat (" ", ct->c_id, NULL);
3940 add_header (ct, np, vp);
3944 * output the Content-Description
3947 np = add (DESCR_FIELD, NULL);
3948 vp = concat (" ", ct->c_descr, NULL);
3949 add_header (ct, np, vp);
3954 * If this is the internal content structure for a
3955 * "message/external", then we are done with the
3956 * headers (since it has no body).
3962 * output the Content-MD5
3965 np = add (MD5_FIELD, NULL);
3966 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3967 add_header (ct, np, vp);
3971 * output the Content-Transfer-Encoding
3973 switch (ct->c_encoding) {
3975 /* Nothing to output */
3977 np = add (ENCODING_FIELD, NULL);
3978 vp = concat (" ", "7bit", "\n", NULL);
3979 add_header (ct, np, vp);
3984 if (ct->c_type == CT_MESSAGE)
3985 adios (NULL, "internal error, invalid encoding");
3987 np = add (ENCODING_FIELD, NULL);
3988 vp = concat (" ", "8bit", "\n", NULL);
3989 add_header (ct, np, vp);
3993 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3994 adios (NULL, "internal error, invalid encoding");
3996 np = add (ENCODING_FIELD, NULL);
3997 vp = concat (" ", "quoted-printable", "\n", NULL);
3998 add_header (ct, np, vp);
4002 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4003 adios (NULL, "internal error, invalid encoding");
4005 np = add (ENCODING_FIELD, NULL);
4006 vp = concat (" ", "base64", "\n", NULL);
4007 add_header (ct, np, vp);
4011 if (ct->c_type == CT_MESSAGE)
4012 adios (NULL, "internal error, invalid encoding");
4014 np = add (ENCODING_FIELD, NULL);
4015 vp = concat (" ", "binary", "\n", NULL);
4016 add_header (ct, np, vp);
4020 adios (NULL, "unknown transfer encoding in content");
4025 * Additional content specific header processing
4027 switch (ct->c_type) {
4030 struct multipart *m;
4033 m = (struct multipart *) ct->c_ctparams;
4034 for (part = m->mp_parts; part; part = part->mp_next) {
4044 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4047 e = (struct exbody *) ct->c_ctparams;
4048 build_headers (e->eb_content);
4061 static char nib2b64[0x40+1] =
4062 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4065 calculate_digest (CT ct, int asciiP)
4068 char buffer[BUFSIZ], *vp, *op;
4070 unsigned char digest[16];
4071 unsigned char outbuf[25];
4074 CE ce = ct->c_cefile;
4077 if ((in = fopen (ce->ce_file, "r")) == NULL)
4078 adios (ce->ce_file, "unable to open for reading");
4080 /* Initialize md5 context */
4081 MD5Init (&mdContext);
4083 /* calculate md5 message digest */
4085 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4088 cp = buffer + strlen (buffer) - 1;
4089 if ((c = *cp) == '\n')
4092 MD5Update (&mdContext, (unsigned char *) buffer,
4093 (unsigned int) strlen (buffer));
4096 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4099 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4100 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4103 /* md5 finalization. Write digest and zero md5 context */
4104 MD5Final (digest, &mdContext);
4109 /* print debugging info */
4113 fprintf (stderr, "MD5 digest=");
4114 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4116 fprintf (stderr, "%02x", *dp & 0xff);
4117 fprintf (stderr, "\n");
4120 /* encode the digest using base64 */
4121 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4122 cc > 0; cc -= 3, op += 4) {
4126 bits = (*dp++ & 0xff) << 16;
4128 bits |= (*dp++ & 0xff) << 8;
4130 bits |= *dp++ & 0xff;
4133 for (bp = op + 4; bp > op; bits >>= 6)
4134 *--bp = nib2b64[bits & 0x3f];
4142 /* null terminate string */
4145 /* now make copy and return string */
4146 vp = concat (" ", outbuf, "\n", NULL);
4152 readDigest (CT ct, char *cp)
4157 unsigned char *dp, value, *ep;
4158 unsigned char *b, *b1, *b2, *b3;
4160 b = (unsigned char *) &bits,
4161 b1 = &b[endian > 0 ? 1 : 2],
4162 b2 = &b[endian > 0 ? 2 : 1],
4163 b3 = &b[endian > 0 ? 3 : 0];
4168 for (ep = (dp = ct->c_digest)
4169 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4174 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4176 fprintf (stderr, "invalid BASE64 encoding\n");
4180 bits |= value << bitno;
4182 if ((bitno -= 6) < 0) {
4183 if (dp + (3 - skip) > ep)
4184 goto invalid_digest;
4199 goto self_delimiting;
4204 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4214 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4222 fprintf (stderr, "MD5 digest=");
4223 for (dp = ct->c_digest; dp < ep; dp++)
4224 fprintf (stderr, "%02x", *dp & 0xff);
4225 fprintf (stderr, "\n");