]>
diplodocus.org Git - nmh/blob - uip/mhbuildsbr.c
3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
11 * This code was originally part of mhn.c. I split it into
12 * a separate program (mhbuild.c) and then later split it
13 * again (mhbuildsbr.c). But the code still has some of
14 * the mhn.c code in it. This program needs additional
15 * streamlining and removal of unneeded code.
20 #include <h/signals.h>
27 #include <h/mhparse.h>
30 #ifdef HAVE_SYS_TIME_H
31 # include <sys/time.h>
42 extern int contentidsw
;
45 extern int rcachesw
; /* mhcachesbr.c */
46 extern int wcachesw
; /* mhcachesbr.c */
49 * Directory to place tmp files. This must
50 * be set before these routines are called.
56 static char prefix
[] = "----- =_aaaaaaaaaa";
60 void content_error (char *, CT
, char *, ...);
63 int find_cache (CT
, int, int *, char *, char *, int);
66 void free_content (CT
);
67 void free_ctinfo (CT
);
68 void free_encoding (CT
, int);
73 CT
build_mime (char *, int);
78 static int init_decoded_content (CT
);
79 static char *fgetstr (char *, int, FILE *);
80 static int user_content (FILE *, char *, char *, CT
*);
81 static void set_id (CT
, int);
82 static int compose_content (CT
);
83 static int scan_content (CT
);
84 static int build_headers (CT
);
85 static char *calculate_digest (CT
, int);
88 static unsigned char directives_stack
[32];
89 static unsigned int directives_index
;
91 static int do_direct(void)
93 return directives_stack
[directives_index
];
96 static void directive_onoff(int onoff
)
98 if (directives_index
>= sizeof(directives_stack
) - 1) {
99 fprintf(stderr
, "mhbuild: #on/off overflow, continuing\n");
102 directives_stack
[++directives_index
] = onoff
;
105 static void directive_init(int onoff
)
107 directives_index
= 0;
108 directives_stack
[0] = onoff
;
111 static void directive_pop(void)
113 if (directives_index
> 0)
116 fprintf(stderr
, "mhbuild: #pop underflow, continuing\n");
120 * Main routine for translating composition file
121 * into valid MIME message. It translates the draft
122 * into a content structure (actually a tree of content
123 * structures). This message then can be manipulated
124 * in various ways, including being output via
129 build_mime (char *infile
, int directives
)
132 char buf
[BUFSIZ
], name
[NAMESZ
];
139 directive_init(directives
);
141 umask (~m_gmprot ());
143 /* open the composition draft */
144 if ((in
= fopen (infile
, "r")) == NULL
)
145 adios (infile
, "unable to open for reading");
148 * Allocate space for primary (outside) content
150 if ((ct
= (CT
) calloc (1, sizeof(*ct
))) == NULL
)
151 adios (NULL
, "out of memory");
154 * Allocate structure for handling decoded content
155 * for this part. We don't really need this, but
156 * allocate it to remain consistent.
158 init_decoded_content (ct
);
161 * Parse some of the header fields in the composition
162 * draft into the linked list of header fields for
163 * the new MIME message.
165 for (compnum
= 1, state
= FLD
;;) {
166 int bufsz
= sizeof buf
;
167 switch (state
= m_getfld (state
, name
, buf
, &bufsz
, in
)) {
173 /* abort if draft has Mime-Version header field */
174 if (!mh_strcasecmp (name
, VRSN_FIELD
))
175 adios (NULL
, "draft shouldn't contain %s: field", VRSN_FIELD
);
177 /* abort if draft has Content-Transfer-Encoding header field */
178 if (!mh_strcasecmp (name
, ENCODING_FIELD
))
179 adios (NULL
, "draft shouldn't contain %s: field", ENCODING_FIELD
);
181 /* ignore any Content-Type fields in the header */
182 if (!mh_strcasecmp (name
, TYPE_FIELD
)) {
183 while (state
== FLDPLUS
) {
185 state
= m_getfld (state
, name
, buf
, &bufsz
, in
);
190 /* get copies of the buffers */
191 np
= add (name
, NULL
);
192 vp
= add (buf
, NULL
);
194 /* if necessary, get rest of field */
195 while (state
== FLDPLUS
) {
197 state
= m_getfld (state
, name
, buf
, &bufsz
, in
);
198 vp
= add (buf
, vp
); /* add to previous value */
201 /* Now add the header data to the list */
202 add_header (ct
, np
, vp
);
205 /* if this wasn't the last header field, then continue */
211 adios (NULL
, "draft has empty body -- no directives!");
216 fseek (in
, (long) (-strlen (buf
)), SEEK_CUR
);
221 adios (NULL
, "message format error in component #%d", compnum
);
224 adios (NULL
, "getfld() returned %d", state
);
230 * Now add the MIME-Version header field
231 * to the list of header fields.
233 np
= add (VRSN_FIELD
, NULL
);
234 vp
= concat (" ", VRSN_VALUE
, "\n", NULL
);
235 add_header (ct
, np
, vp
);
238 * We initally assume we will find multiple contents in the
239 * draft. So create a multipart/mixed content to hold everything.
240 * We can remove this later, if it is not needed.
242 if (get_ctinfo ("multipart/mixed", ct
, 0) == NOTOK
)
244 ct
->c_type
= CT_MULTIPART
;
245 ct
->c_subtype
= MULTI_MIXED
;
246 ct
->c_file
= add (infile
, NULL
);
248 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
249 adios (NULL
, "out of memory");
250 ct
->c_ctparams
= (void *) m
;
254 * read and parse the composition file
255 * and the directives it contains.
257 while (fgetstr (buf
, sizeof(buf
) - 1, in
)) {
261 if (user_content (in
, infile
, buf
, &p
) == DONE
) {
262 admonish (NULL
, "ignoring spurious #end");
268 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
269 adios (NULL
, "out of memory");
276 * close the composition draft since
277 * it's not needed any longer.
281 /* check if any contents were found */
283 adios (NULL
, "no content directives found");
286 * If only one content was found, then remove and
287 * free the outer multipart content.
289 if (!m
->mp_parts
->mp_next
) {
292 p
= m
->mp_parts
->mp_part
;
293 m
->mp_parts
->mp_part
= NULL
;
295 /* move header fields */
296 p
->c_first_hf
= ct
->c_first_hf
;
297 p
->c_last_hf
= ct
->c_last_hf
;
298 ct
->c_first_hf
= NULL
;
299 ct
->c_last_hf
= NULL
;
308 * Fill out, or expand directives. Parse and execute
309 * commands specified by profile composition strings.
311 compose_content (ct
);
313 if ((cp
= strchr(prefix
, 'a')) == NULL
)
314 adios (NULL
, "internal error(4)");
317 * Scan the contents. Choose a transfer encoding, and
318 * check if prefix for multipart boundary clashes with
319 * any of the contents.
321 while (scan_content (ct
) == NOTOK
) {
326 adios (NULL
, "giving up trying to find a unique delimiter string");
332 /* Build the rest of the header field structures */
340 * Set up structures for placing unencoded
341 * content when building parts.
345 init_decoded_content (CT ct
)
349 if ((ce
= (CE
) calloc (1, sizeof(*ce
))) == NULL
)
350 adios (NULL
, "out of memory");
353 ct
->c_ceopenfnx
= open7Bit
; /* since unencoded */
354 ct
->c_ceclosefnx
= close_encoding
;
355 ct
->c_cesizefnx
= NULL
; /* since unencoded */
362 fgetstr (char *s
, int n
, FILE *stream
)
368 for (ep
= (cp
= s
) + o_n
; cp
< ep
; ) {
371 if (!fgets (cp
, n
, stream
))
372 return (cp
!= s
? s
: NULL
);
374 if (cp
== s
&& *cp
!= '#')
377 cp
+= (i
= strlen (cp
)) - 1;
378 if (i
<= 1 || *cp
-- != '\n' || *cp
!= '\\')
384 if (strcmp(s
, "#on\n") == 0) {
386 } else if (strcmp(s
, "#off\n") == 0) {
388 } else if (strcmp(s
, "#pop\n") == 0) {
400 * Parse the composition draft for text and directives.
401 * Do initial setup of Content structure.
405 user_content (FILE *in
, char *file
, char *buf
, CT
*ctp
)
414 struct str2init
*s2i
;
419 if (buf
[0] == '\n' || (do_direct() && strcmp (buf
, "#\n") == 0)) {
424 /* allocate basic Content structure */
425 if ((ct
= (CT
) calloc (1, sizeof(*ct
))) == NULL
)
426 adios (NULL
, "out of memory");
429 /* allocate basic structure for handling decoded content */
430 init_decoded_content (ct
);
437 * Handle inline text. Check if line
438 * is one of the following forms:
440 * 1) doesn't begin with '#' (implicit directive)
441 * 2) begins with "##" (implicit directive)
442 * 3) begins with "#<"
444 if (!do_direct() || buf
[0] != '#' || buf
[1] == '#' || buf
[1] == '<') {
448 char content
[BUFSIZ
];
452 cp
= m_mktemp2(NULL
, invo_name
, NULL
, &out
);
453 if (cp
== NULL
) adios("mhbuildsbr", "unable to create temporary file");
455 /* use a temp file to collect the plain text lines */
456 ce
->ce_file
= add (cp
, NULL
);
459 if (do_direct() && (buf
[0] == '#' && buf
[1] == '<')) {
460 strncpy (content
, buf
+ 2, sizeof(content
));
467 /* the directive is implicit */
468 strncpy (content
, "text/plain", sizeof(content
));
470 strncpy (buffer
, (!do_direct() || buf
[0] != '#') ? buf
: buf
+ 1, sizeof(buffer
));
474 if (headers
>= 0 && do_direct() && uprf (buffer
, DESCR_FIELD
)
475 && buffer
[i
= strlen (DESCR_FIELD
)] == ':') {
479 ct
->c_descr
= add (buffer
+ i
+ 1, ct
->c_descr
);
480 if (!fgetstr (buffer
, sizeof(buffer
) - 1, in
))
481 adios (NULL
, "end-of-file after %s: field in plaintext", DESCR_FIELD
);
489 adios (NULL
, "#-directive after %s: field in plaintext", DESCR_FIELD
);
497 if (headers
>= 0 && do_direct() && uprf (buffer
, DISPO_FIELD
)
498 && buffer
[i
= strlen (DISPO_FIELD
)] == ':') {
502 ct
->c_dispo
= add (buffer
+ i
+ 1, ct
->c_dispo
);
503 if (!fgetstr (buffer
, sizeof(buffer
) - 1, in
))
504 adios (NULL
, "end-of-file after %s: field in plaintext", DISPO_FIELD
);
512 adios (NULL
, "#-directive after %s: field in plaintext", DISPO_FIELD
);
520 if (headers
!= 1 || buffer
[0] != '\n')
526 if ((cp
= fgetstr (buffer
, sizeof(buffer
) - 1, in
)) == NULL
)
528 if (do_direct() && buffer
[0] == '#') {
531 if (buffer
[1] != '#')
533 for (cp
= (bp
= buffer
) + 1; *cp
; cp
++)
540 ct
->c_end
= ftell (out
);
543 /* parse content type */
544 if (get_ctinfo (content
, ct
, inlineD
) == NOTOK
)
547 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
548 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
550 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
554 * check type specified (possibly implicitly)
556 switch (ct
->c_type
= s2i
->si_val
) {
558 if (!mh_strcasecmp (ci
->ci_subtype
, "rfc822")) {
559 ct
->c_encoding
= CE_7BIT
;
564 adios (NULL
, "it doesn't make sense to define an in-line %s content",
565 ct
->c_type
== CT_MESSAGE
? "message" : "multipart");
570 if ((ct
->c_ctinitfnx
= s2i
->si_init
))
571 (*ct
->c_ctinitfnx
) (ct
);
576 fseek (in
, pos
, SEEK_SET
);
581 * If we've reached this point, the next line
582 * must be some type of explicit directive.
585 /* check if directive is external-type */
586 extrnal
= (buf
[1] == '@');
588 /* parse directive */
589 if (get_ctinfo (buf
+ (extrnal
? 2 : 1), ct
, 1) == NOTOK
)
592 /* check directive against the list of MIME types */
593 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
594 if (!mh_strcasecmp (ci
->ci_type
, s2i
->si_key
))
598 * Check if the directive specified a valid type.
599 * This will happen if it was one of the following forms:
606 adios (NULL
, "missing subtype in \"#%s\"", ci
->ci_type
);
608 switch (ct
->c_type
= s2i
->si_val
) {
610 adios (NULL
, "use \"#begin ... #end\" instead of \"#%s/%s\"",
611 ci
->ci_type
, ci
->ci_subtype
);
615 if (!mh_strcasecmp (ci
->ci_subtype
, "partial"))
616 adios (NULL
, "sorry, \"#%s/%s\" isn't supported",
617 ci
->ci_type
, ci
->ci_subtype
);
618 if (!mh_strcasecmp (ci
->ci_subtype
, "external-body"))
619 adios (NULL
, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
620 ci
->ci_type
, ci
->ci_subtype
);
623 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
624 ci
->ci_type
, ci
->ci_subtype
);
628 if ((ct
->c_ctinitfnx
= s2i
->si_init
))
629 (*ct
->c_ctinitfnx
) (ct
);
634 * #@type/subtype (external types directive)
641 adios (NULL
, "need external information for \"#@%s/%s\"",
642 ci
->ci_type
, ci
->ci_subtype
);
645 snprintf (buffer
, sizeof(buffer
), "message/external-body; %s", ci
->ci_magic
);
650 * Since we are using the current Content structure to
651 * hold information about the type of the external
652 * reference, we need to create another Content structure
653 * for the message/external-body to wrap it in.
655 if ((ct
= (CT
) calloc (1, sizeof(*ct
))) == NULL
)
656 adios (NULL
, "out of memory");
659 if (get_ctinfo (buffer
, ct
, 0) == NOTOK
)
661 ct
->c_type
= CT_MESSAGE
;
662 ct
->c_subtype
= MESSAGE_EXTERNAL
;
664 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
665 adios (NULL
, "out of memory");
666 ct
->c_ctparams
= (void *) e
;
672 if (params_external (ct
, 1) == NOTOK
)
678 /* Handle [file] argument */
680 /* check if specifies command to execute */
681 if (*ci
->ci_magic
== '|' || *ci
->ci_magic
== '!') {
682 for (cp
= ci
->ci_magic
+ 1; isspace (*cp
); cp
++)
685 adios (NULL
, "empty pipe command for #%s directive", ci
->ci_type
);
690 /* record filename of decoded contents */
691 ce
->ce_file
= ci
->ci_magic
;
692 if (access (ce
->ce_file
, R_OK
) == NOTOK
)
693 adios ("reading", "unable to access %s for", ce
->ce_file
);
694 if (listsw
&& stat (ce
->ce_file
, &st
) != NOTOK
)
695 ct
->c_end
= (long) st
.st_size
;
702 * No [file] argument, so check profile for
703 * method to compose content.
705 snprintf (buffer
, sizeof(buffer
), "%s-compose-%s/%s",
706 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
707 if ((cp
= context_find (buffer
)) == NULL
|| *cp
== '\0') {
708 snprintf (buffer
, sizeof(buffer
), "%s-compose-%s", invo_name
, ci
->ci_type
);
709 if ((cp
= context_find (buffer
)) == NULL
|| *cp
== '\0') {
710 content_error (NULL
, ct
, "don't know how to compose content");
714 ci
->ci_magic
= add (cp
, NULL
);
719 adios (NULL
, "external definition not allowed for \"#%s\"", ci
->ci_type
);
723 * #forw [+folder] [msgs]
725 if (!mh_strcasecmp (ci
->ci_type
, "forw")) {
727 char *folder
, *arguments
[MAXARGS
];
731 ap
= brkstring (ci
->ci_magic
, " ", "\n");
732 copyip (ap
, arguments
, MAXARGS
);
734 arguments
[0] = "cur";
739 /* search the arguments for a folder name */
740 for (ap
= arguments
; *ap
; ap
++) {
742 if (*cp
== '+' || *cp
== '@') {
744 adios (NULL
, "only one folder per #forw directive");
746 folder
= pluspath (cp
);
750 /* else, use the current folder */
752 folder
= add (getfolder (1), NULL
);
754 if (!(mp
= folder_read (folder
)))
755 adios (NULL
, "unable to read folder %s", folder
);
756 for (ap
= arguments
; *ap
; ap
++) {
758 if (*cp
!= '+' && *cp
!= '@')
759 if (!m_convert (mp
, cp
))
766 * If there is more than one message to include, make this
767 * a content of type "multipart/digest" and insert each message
768 * as a subpart. If there is only one message, then make this
769 * a content of type "message/rfc822".
771 if (mp
->numsel
> 1) {
772 /* we are forwarding multiple messages */
773 if (get_ctinfo ("multipart/digest", ct
, 0) == NOTOK
)
775 ct
->c_type
= CT_MULTIPART
;
776 ct
->c_subtype
= MULTI_DIGEST
;
778 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
779 adios (NULL
, "out of memory");
780 ct
->c_ctparams
= (void *) m
;
783 for (msgnum
= mp
->lowsel
; msgnum
<= mp
->hghsel
; msgnum
++) {
784 if (is_selected(mp
, msgnum
)) {
789 if ((p
= (CT
) calloc (1, sizeof(*p
))) == NULL
)
790 adios (NULL
, "out of memory");
791 init_decoded_content (p
);
793 if (get_ctinfo ("message/rfc822", p
, 0) == NOTOK
)
795 p
->c_type
= CT_MESSAGE
;
796 p
->c_subtype
= MESSAGE_RFC822
;
798 snprintf (buffer
, sizeof(buffer
), "%s/%d", mp
->foldpath
, msgnum
);
799 pe
->ce_file
= add (buffer
, NULL
);
800 if (listsw
&& stat (pe
->ce_file
, &st
) != NOTOK
)
801 p
->c_end
= (long) st
.st_size
;
803 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
804 adios (NULL
, "out of memory");
811 /* we are forwarding one message */
812 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
814 ct
->c_type
= CT_MESSAGE
;
815 ct
->c_subtype
= MESSAGE_RFC822
;
818 snprintf (buffer
, sizeof(buffer
), "%s/%d", mp
->foldpath
, msgnum
);
819 ce
->ce_file
= add (buffer
, NULL
);
820 if (listsw
&& stat (ce
->ce_file
, &st
) != NOTOK
)
821 ct
->c_end
= (long) st
.st_size
;
824 folder_free (mp
); /* free folder/message structure */
831 if (!mh_strcasecmp (ci
->ci_type
, "end")) {
838 * #begin [ alternative | parallel ]
840 if (!mh_strcasecmp (ci
->ci_type
, "begin")) {
843 cp
= SubMultiPart
[vrsn
- 1].kv_key
;
844 } else if (!mh_strcasecmp (ci
->ci_magic
, "alternative")) {
845 vrsn
= MULTI_ALTERNATE
;
846 cp
= SubMultiPart
[vrsn
- 1].kv_key
;
847 } else if (!mh_strcasecmp (ci
->ci_magic
, "parallel")) {
848 vrsn
= MULTI_PARALLEL
;
849 cp
= SubMultiPart
[vrsn
- 1].kv_key
;
850 } else if (uprf (ci
->ci_magic
, "digest")) {
853 vrsn
= MULTI_UNKNOWN
;
858 snprintf (buffer
, sizeof(buffer
), "multipart/%s", cp
);
859 if (get_ctinfo (buffer
, ct
, 0) == NOTOK
)
861 ct
->c_type
= CT_MULTIPART
;
862 ct
->c_subtype
= vrsn
;
864 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
865 adios (NULL
, "out of memory");
866 ct
->c_ctparams
= (void *) m
;
869 while (fgetstr (buffer
, sizeof(buffer
) - 1, in
)) {
873 if (user_content (in
, file
, buffer
, &p
) == DONE
) {
875 adios (NULL
, "empty \"#begin ... #end\" sequence");
881 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
882 adios (NULL
, "out of memory");
887 admonish (NULL
, "premature end-of-file, missing #end");
894 adios (NULL
, "unknown directive \"#%s\"", ci
->ci_type
);
895 return NOTOK
; /* NOT REACHED */
900 set_id (CT ct
, int top
)
902 char contentid
[BUFSIZ
];
904 static time_t clock
= 0;
909 snprintf (contentid
, sizeof(contentid
), "%s\n", message_id (clock
, 1));
911 msgfmt
= getcpy(contentid
);
913 snprintf (contentid
, sizeof(contentid
), msgfmt
, top
? 0 : ++partno
);
914 ct
->c_id
= getcpy (contentid
);
918 static char ebcdicsafe
[0x100] = {
919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
920 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
923 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
924 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
925 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
926 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
927 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
928 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
929 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
930 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
931 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
932 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
933 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
934 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
955 * Fill out, or expand the various contents in the composition
956 * draft. Read-in any necessary files. Parse and execute any
957 * commands specified by profile composition strings.
961 compose_content (CT ct
)
963 CE ce
= ct
->c_cefile
;
965 switch (ct
->c_type
) {
970 char partnam
[BUFSIZ
];
971 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
975 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
976 pp
= partnam
+ strlen (partnam
);
981 /* first, we call compose_content on all the subparts */
982 for (part
= m
->mp_parts
, partnum
= 1; part
; part
= part
->mp_next
, partnum
++) {
983 CT p
= part
->mp_part
;
985 sprintf (pp
, "%d", partnum
);
986 p
->c_partno
= add (partnam
, NULL
);
987 if (compose_content (p
) == NOTOK
)
992 * If the -rfc934mode switch is given, then check all
993 * the subparts of a multipart/digest. If they are all
994 * message/rfc822, then mark this content and all
995 * subparts with the rfc934 compatibility mode flag.
997 if (rfc934sw
&& ct
->c_subtype
== MULTI_DIGEST
) {
1000 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1001 CT p
= part
->mp_part
;
1003 if (p
->c_subtype
!= MESSAGE_RFC822
) {
1008 ct
->c_rfc934
= is934
;
1009 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1010 CT p
= part
->mp_part
;
1012 if ((p
->c_rfc934
= is934
))
1018 ct
->c_end
= (partnum
= strlen (prefix
) + 2) + 2;
1022 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1023 ct
->c_end
+= part
->mp_part
->c_end
+ partnum
;
1029 /* Nothing to do for type message */
1033 * Discrete types (text/application/audio/image/video)
1038 int i
, xstdout
, len
, buflen
;
1039 char *bp
, **ap
, *cp
;
1040 char *vec
[4], buffer
[BUFSIZ
];
1042 CI ci
= &ct
->c_ctinfo
;
1045 if (!(cp
= ci
->ci_magic
))
1046 adios (NULL
, "internal error(5)");
1048 tfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
);
1049 if (tfile
== NULL
) {
1050 adios("mhbuildsbr", "unable to create temporary file");
1052 ce
->ce_file
= add (tfile
, NULL
);
1057 /* Get buffer ready to go */
1060 buflen
= sizeof(buffer
);
1063 * Parse composition string into buffer
1065 for ( ; *cp
; cp
++) {
1070 /* insert parameters from directive */
1074 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1075 snprintf (bp
, buflen
, "%s%s=\"%s\"", s
, *ap
, *ep
);
1085 /* %f, and stdout is not-redirected */
1091 * insert temporary filename where
1092 * content should be written
1094 snprintf (bp
, buflen
, "%s", ce
->ce_file
);
1098 /* insert content subtype */
1099 strncpy (bp
, ci
->ci_subtype
, buflen
);
1103 /* insert character % */
1124 printf ("composing content %s/%s from command\n\t%s\n",
1125 ci
->ci_type
, ci
->ci_subtype
, buffer
);
1127 fflush (stdout
); /* not sure if need for -noverbose */
1134 if ((out
= fopen (ce
->ce_file
, "w")) == NULL
)
1135 adios (ce
->ce_file
, "unable to open for writing");
1137 for (i
= 0; (child_id
= vfork()) == NOTOK
&& i
> 5; i
++)
1141 adios ("fork", "unable to fork");
1146 dup2 (fileno (out
), 1);
1147 close (fileno (out
));
1148 execvp ("/bin/sh", vec
);
1149 fprintf (stderr
, "unable to exec ");
1156 if (pidXwait(child_id
, NULL
))
1162 /* Check size of file */
1163 if (listsw
&& ct
->c_end
== 0L) {
1166 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1167 ct
->c_end
= (long) st
.st_size
;
1179 * 1) choose a transfer encoding.
1180 * 2) check for clashes with multipart boundary string.
1181 * 3) for text content, figure out which character set is being used.
1183 * If there is a clash with one of the contents and the multipart boundary,
1184 * this function will exit with NOTOK. This will cause the scanning process
1185 * to be repeated with a different multipart boundary. It is possible
1186 * (although highly unlikely) that this scan will be repeated multiple times.
1190 scan_content (CT ct
)
1193 int check8bit
= 0, contains8bit
= 0; /* check if contains 8bit data */
1194 int checklinelen
= 0, linelen
= 0; /* check for long lines */
1195 int checkboundary
= 0, boundaryclash
= 0; /* check if clashes with multipart boundary */
1196 int checklinespace
= 0, linespace
= 0; /* check if any line ends with space */
1197 int checkebcdic
= 0, ebcdicunsafe
= 0; /* check if contains ebcdic unsafe characters */
1198 unsigned char *cp
= NULL
, buffer
[BUFSIZ
];
1199 struct text
*t
= NULL
;
1201 CE ce
= ct
->c_cefile
;
1204 * handle multipart by scanning all subparts
1205 * and then checking their encoding.
1207 if (ct
->c_type
== CT_MULTIPART
) {
1208 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1211 /* initially mark the domain of enclosing multipart as 7bit */
1212 ct
->c_encoding
= CE_7BIT
;
1214 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1215 CT p
= part
->mp_part
;
1217 if (scan_content (p
) == NOTOK
) /* choose encoding for subpart */
1220 /* if necessary, enlarge encoding for enclosing multipart */
1221 if (p
->c_encoding
== CE_BINARY
)
1222 ct
->c_encoding
= CE_BINARY
;
1223 if (p
->c_encoding
== CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
)
1224 ct
->c_encoding
= CE_8BIT
;
1231 * Decide what to check while scanning this content.
1233 switch (ct
->c_type
) {
1237 if (ct
->c_subtype
== TEXT_PLAIN
) {
1242 checkebcdic
= ebcdicsw
;
1248 case CT_APPLICATION
:
1250 checkebcdic
= ebcdicsw
;
1262 /* don't check anything for message/external */
1263 if (ct
->c_subtype
== MESSAGE_EXTERNAL
)
1273 * Don't check anything for these types,
1274 * since we are forcing use of base64.
1285 * Scan the unencoded content
1287 if (check8bit
|| checklinelen
|| checklinespace
|| checkboundary
) {
1288 if ((in
= fopen (ce
->ce_file
, "r")) == NULL
)
1289 adios (ce
->ce_file
, "unable to open for reading");
1290 len
= strlen (prefix
);
1292 while (fgets (buffer
, sizeof(buffer
) - 1, in
)) {
1294 * Check for 8bit data.
1297 for (cp
= buffer
; *cp
; cp
++) {
1298 if (!isascii (*cp
)) {
1300 check8bit
= 0; /* no need to keep checking */
1303 * Check if character is ebcdic-safe. We only check
1304 * this if also checking for 8bit data.
1306 if (checkebcdic
&& !ebcdicsafe
[*cp
& 0xff]) {
1308 checkebcdic
= 0; /* no need to keep checking */
1314 * Check line length.
1316 if (checklinelen
&& (strlen (buffer
) > CPERLIN
+ 1)) {
1318 checklinelen
= 0; /* no need to keep checking */
1322 * Check if line ends with a space.
1324 if (checklinespace
&& (cp
= buffer
+ strlen (buffer
) - 2) > buffer
&& isspace (*cp
)) {
1326 checklinespace
= 0; /* no need to keep checking */
1330 * Check if content contains a line that clashes
1331 * with our standard boundary for multipart messages.
1333 if (checkboundary
&& buffer
[0] == '-' && buffer
[1] == '-') {
1334 for (cp
= buffer
+ strlen (buffer
) - 1; cp
>= buffer
; cp
--)
1338 if (!strncmp(buffer
+ 2, prefix
, len
) && isdigit(buffer
[2 + len
])) {
1340 checkboundary
= 0; /* no need to keep checking */
1348 * Decide which transfer encoding to use.
1350 switch (ct
->c_type
) {
1353 * If the text content didn't specify a character
1354 * set, we need to figure out which one was used.
1356 t
= (struct text
*) ct
->c_ctparams
;
1357 if (t
->tx_charset
== CHARSET_UNSPECIFIED
) {
1358 CI ci
= &ct
->c_ctinfo
;
1361 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1365 *ap
= concat ("charset=", write_charset_8bit(), NULL
);
1367 *ap
= add ("charset=us-ascii", NULL
);
1369 t
->tx_charset
= CHARSET_SPECIFIED
;
1371 cp
= strchr(*ap
++, '=');
1377 if (contains8bit
|| ebcdicunsafe
|| linelen
|| linespace
|| checksw
)
1378 ct
->c_encoding
= CE_QUOTED
;
1380 ct
->c_encoding
= CE_7BIT
;
1383 case CT_APPLICATION
:
1384 /* For application type, use base64, except when postscript */
1385 if (contains8bit
|| ebcdicunsafe
|| linelen
|| linespace
|| checksw
)
1386 ct
->c_encoding
= (ct
->c_subtype
== APPLICATION_POSTSCRIPT
)
1387 ? CE_QUOTED
: CE_BASE64
;
1389 ct
->c_encoding
= CE_7BIT
;
1393 ct
->c_encoding
= CE_7BIT
;
1399 /* For audio, image, and video contents, just use base64 */
1400 ct
->c_encoding
= CE_BASE64
;
1404 return (boundaryclash
? NOTOK
: OK
);
1409 * Scan the content structures, and build header
1410 * fields that will need to be output into the
1415 build_headers (CT ct
)
1417 int cc
, mailbody
, len
;
1419 char *np
, *vp
, buffer
[BUFSIZ
];
1420 CI ci
= &ct
->c_ctinfo
;
1423 * If message is type multipart, then add the multipart
1424 * boundary to the list of attribute/value pairs.
1426 if (ct
->c_type
== CT_MULTIPART
) {
1428 static int level
= 0; /* store nesting level */
1432 snprintf (buffer
, sizeof(buffer
), "boundary=%s%d", prefix
, level
++);
1433 cp
= strchr(*ap
++ = add (buffer
, NULL
), '=');
1440 * Skip the output of Content-Type, parameters, content
1441 * description and disposition, and Content-ID if the
1442 * content is of type "message" and the rfc934 compatibility
1443 * flag is set (which means we are inside multipart/digest
1444 * and the switch -rfc934mode was given).
1446 if (ct
->c_type
== CT_MESSAGE
&& ct
->c_rfc934
)
1450 * output the content type and subtype
1452 np
= add (TYPE_FIELD
, NULL
);
1453 vp
= concat (" ", ci
->ci_type
, "/", ci
->ci_subtype
, NULL
);
1455 /* keep track of length of line */
1456 len
= strlen (TYPE_FIELD
) + strlen (ci
->ci_type
)
1457 + strlen (ci
->ci_subtype
) + 3;
1459 mailbody
= ct
->c_type
== CT_MESSAGE
1460 && ct
->c_subtype
== MESSAGE_EXTERNAL
1461 && ((struct exbody
*) ct
->c_ctparams
)->eb_body
;
1464 * Append the attribute/value pairs to
1465 * the end of the Content-Type line.
1467 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1468 if (mailbody
&& !mh_strcasecmp (*ap
, "body"))
1474 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
1475 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
1476 vp
= add ("\n\t", vp
);
1482 vp
= add (buffer
, vp
);
1487 * Append any RFC-822 comment to the end of
1488 * the Content-Type line.
1490 if (ci
->ci_comment
) {
1491 snprintf (buffer
, sizeof(buffer
), "(%s)", ci
->ci_comment
);
1492 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
1493 vp
= add ("\n\t", vp
);
1499 vp
= add (buffer
, vp
);
1502 vp
= add ("\n", vp
);
1503 add_header (ct
, np
, vp
);
1506 * output the Content-ID, unless disabled by -nocontentid
1508 if (contentidsw
&& ct
->c_id
) {
1509 np
= add (ID_FIELD
, NULL
);
1510 vp
= concat (" ", ct
->c_id
, NULL
);
1511 add_header (ct
, np
, vp
);
1515 * output the Content-Description
1518 np
= add (DESCR_FIELD
, NULL
);
1519 vp
= concat (" ", ct
->c_descr
, NULL
);
1520 add_header (ct
, np
, vp
);
1524 * output the Content-Disposition
1527 np
= add (DISPO_FIELD
, NULL
);
1528 vp
= concat (" ", ct
->c_dispo
, NULL
);
1529 add_header (ct
, np
, vp
);
1534 * If this is the internal content structure for a
1535 * "message/external", then we are done with the
1536 * headers (since it has no body).
1542 * output the Content-MD5
1545 np
= add (MD5_FIELD
, NULL
);
1546 vp
= calculate_digest (ct
, (ct
->c_encoding
== CE_QUOTED
) ? 1 : 0);
1547 add_header (ct
, np
, vp
);
1551 * output the Content-Transfer-Encoding
1553 switch (ct
->c_encoding
) {
1555 /* Nothing to output */
1559 if (ct
->c_type
== CT_MESSAGE
)
1560 adios (NULL
, "internal error, invalid encoding");
1562 np
= add (ENCODING_FIELD
, NULL
);
1563 vp
= concat (" ", "8bit", "\n", NULL
);
1564 add_header (ct
, np
, vp
);
1568 if (ct
->c_type
== CT_MESSAGE
|| ct
->c_type
== CT_MULTIPART
)
1569 adios (NULL
, "internal error, invalid encoding");
1571 np
= add (ENCODING_FIELD
, NULL
);
1572 vp
= concat (" ", "quoted-printable", "\n", NULL
);
1573 add_header (ct
, np
, vp
);
1577 if (ct
->c_type
== CT_MESSAGE
|| ct
->c_type
== CT_MULTIPART
)
1578 adios (NULL
, "internal error, invalid encoding");
1580 np
= add (ENCODING_FIELD
, NULL
);
1581 vp
= concat (" ", "base64", "\n", NULL
);
1582 add_header (ct
, np
, vp
);
1586 if (ct
->c_type
== CT_MESSAGE
)
1587 adios (NULL
, "internal error, invalid encoding");
1589 np
= add (ENCODING_FIELD
, NULL
);
1590 vp
= concat (" ", "binary", "\n", NULL
);
1591 add_header (ct
, np
, vp
);
1595 adios (NULL
, "unknown transfer encoding in content");
1600 * Additional content specific header processing
1602 switch (ct
->c_type
) {
1605 struct multipart
*m
;
1608 m
= (struct multipart
*) ct
->c_ctparams
;
1609 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1619 if (ct
->c_subtype
== MESSAGE_EXTERNAL
) {
1622 e
= (struct exbody
*) ct
->c_ctparams
;
1623 build_headers (e
->eb_content
);
1636 static char nib2b64
[0x40+1] =
1637 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1640 calculate_digest (CT ct
, int asciiP
)
1643 char buffer
[BUFSIZ
], *vp
, *op
;
1645 unsigned char digest
[16];
1646 unsigned char outbuf
[25];
1648 CE ce
= ct
->c_cefile
;
1649 char *infilename
= ce
->ce_file
? ce
->ce_file
: ct
->c_file
;
1653 if ((in
= fopen (infilename
, "r")) == NULL
)
1654 adios (infilename
, "unable to open for reading");
1656 /* Initialize md5 context */
1657 MD5Init (&mdContext
);
1659 /* calculate md5 message digest */
1661 while (fgets (buffer
, sizeof(buffer
) - 1, in
)) {
1664 cp
= buffer
+ strlen (buffer
) - 1;
1665 if ((c
= *cp
) == '\n')
1668 MD5Update (&mdContext
, (unsigned char *) buffer
,
1669 (unsigned int) strlen (buffer
));
1672 MD5Update (&mdContext
, (unsigned char *) "\r\n", 2);
1675 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), in
)) > 0)
1676 MD5Update (&mdContext
, (unsigned char *) buffer
, (unsigned int) cc
);
1679 /* md5 finalization. Write digest and zero md5 context */
1680 MD5Final (digest
, &mdContext
);
1685 /* print debugging info */
1689 fprintf (stderr
, "MD5 digest=");
1690 for (ep
= (dp
= digest
) + sizeof(digest
) / sizeof(digest
[0]);
1692 fprintf (stderr
, "%02x", *dp
& 0xff);
1693 fprintf (stderr
, "\n");
1696 /* encode the digest using base64 */
1697 for (dp
= digest
, op
= outbuf
, cc
= sizeof(digest
) / sizeof(digest
[0]);
1698 cc
> 0; cc
-= 3, op
+= 4) {
1702 bits
= (*dp
++ & 0xff) << 16;
1704 bits
|= (*dp
++ & 0xff) << 8;
1706 bits
|= *dp
++ & 0xff;
1709 for (bp
= op
+ 4; bp
> op
; bits
>>= 6)
1710 *--bp
= nib2b64
[bits
& 0x3f];
1718 /* null terminate string */
1721 /* now make copy and return string */
1722 vp
= concat (" ", outbuf
, "\n", NULL
);