]>
diplodocus.org Git - nmh/blob - uip/mhbuildsbr.c
53c06e956accef467846d3e308508e8769aff77b
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>
25 #include <h/mhparse.h>
28 #ifdef HAVE_SYS_TIME_H
29 # include <sys/time.h>
39 extern int contentidsw
;
42 extern int rcachesw
; /* mhcachesbr.c */
43 extern int wcachesw
; /* mhcachesbr.c */
46 * Directory to place tmp files. This must
47 * be set before these routines are called.
53 static char prefix
[] = "----- =_aaaaaaaaaa";
57 struct attach_list
*next
;
61 * Maximum size of URL token in message/external-body
64 #define MAXURLTOKEN 40
68 void content_error (char *, CT
, char *, ...);
71 int find_cache (CT
, int, int *, char *, char *, int);
74 void free_ctinfo (CT
);
75 void free_encoding (CT
, int);
80 static int init_decoded_content (CT
);
81 static void setup_attach_content(CT
, char *);
82 static char *fgetstr (char *, int, FILE *);
83 static int user_content (FILE *, char *, char *, CT
*);
84 static void set_id (CT
, int);
85 static int compose_content (CT
);
86 static int scan_content (CT
);
87 static int build_headers (CT
);
88 static char *calculate_digest (CT
, int);
91 static unsigned char directives_stack
[32];
92 static unsigned int directives_index
;
94 static int do_direct(void)
96 return directives_stack
[directives_index
];
99 static void directive_onoff(int onoff
)
101 if (directives_index
>= sizeof(directives_stack
) - 1) {
102 fprintf(stderr
, "mhbuild: #on/off overflow, continuing\n");
105 directives_stack
[++directives_index
] = onoff
;
108 static void directive_init(int onoff
)
110 directives_index
= 0;
111 directives_stack
[0] = onoff
;
114 static void directive_pop(void)
116 if (directives_index
> 0)
119 fprintf(stderr
, "mhbuild: #pop underflow, continuing\n");
123 * Main routine for translating composition file
124 * into valid MIME message. It translates the draft
125 * into a content structure (actually a tree of content
126 * structures). This message then can be manipulated
127 * in various ways, including being output via
132 build_mime (char *infile
, int directives
, int header_encoding
)
135 char buf
[BUFSIZ
], name
[NAMESZ
];
142 m_getfld_state_t gstate
= 0;
143 struct attach_list
*attach_head
= NULL
, *attach_tail
= NULL
, *at_entry
;
145 directive_init(directives
);
147 umask (~m_gmprot ());
149 /* open the composition draft */
150 if ((in
= fopen (infile
, "r")) == NULL
)
151 adios (infile
, "unable to open for reading");
154 * Allocate space for primary (outside) content
156 if ((ct
= (CT
) calloc (1, sizeof(*ct
))) == NULL
)
157 adios (NULL
, "out of memory");
160 * Allocate structure for handling decoded content
161 * for this part. We don't really need this, but
162 * allocate it to remain consistent.
164 init_decoded_content (ct
);
167 * Parse some of the header fields in the composition
168 * draft into the linked list of header fields for
169 * the new MIME message.
171 m_getfld_track_filepos (&gstate
, in
);
172 for (compnum
= 1;;) {
173 int bufsz
= sizeof buf
;
174 switch (state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
)) {
179 /* abort if draft has Mime-Version header field */
180 if (!strcasecmp (name
, VRSN_FIELD
))
181 adios (NULL
, "draft shouldn't contain %s: field", VRSN_FIELD
);
183 /* abort if draft has Content-Transfer-Encoding header field */
184 if (!strcasecmp (name
, ENCODING_FIELD
))
185 adios (NULL
, "draft shouldn't contain %s: field", ENCODING_FIELD
);
187 /* ignore any Content-Type fields in the header */
188 if (!strcasecmp (name
, TYPE_FIELD
)) {
189 while (state
== FLDPLUS
) {
191 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
196 /* get copies of the buffers */
197 np
= add (name
, NULL
);
198 vp
= add (buf
, NULL
);
200 /* if necessary, get rest of field */
201 while (state
== FLDPLUS
) {
203 state
= m_getfld (&gstate
, name
, buf
, &bufsz
, in
);
204 vp
= add (buf
, vp
); /* add to previous value */
208 * Now add the header data to the list, unless it's an attach
209 * header; in that case, add it to our attach list
212 if (strcasecmp(ATTACH_FIELD
, np
) == 0) {
213 struct attach_list
*entry
;
214 char *s
= vp
, *e
= vp
+ strlen(vp
) - 1;
218 * Make sure we can find the start of this filename.
219 * If it's blank, we skip completely. Otherwise, strip
220 * off any leading spaces and trailing newlines.
223 while (isspace((unsigned char) *s
))
226 while (e
> s
&& *e
== '\n')
234 entry
= mh_xmalloc(sizeof(*entry
));
235 entry
->filename
= getcpy(s
);
238 attach_tail
->next
= entry
;
241 attach_head
= attach_tail
= entry
;
244 add_header (ct
, np
, vp
);
248 /* if this wasn't the last header field, then continue */
252 fseek (in
, (long) (-strlen (buf
)), SEEK_CUR
);
259 adios (NULL
, "message format error in component #%d", compnum
);
262 adios (NULL
, "getfld() returned %d", state
);
266 m_getfld_state_destroy (&gstate
);
269 * Iterate through the list of headers and call the function to MIME-ify
273 for (hp
= ct
->c_first_hf
; hp
!= NULL
; hp
= hp
->next
) {
274 if (encode_rfc2047(hp
->name
, &hp
->value
, header_encoding
, NULL
)) {
275 adios(NULL
, "Unable to encode header \"%s\"", hp
->name
);
280 * Now add the MIME-Version header field
281 * to the list of header fields.
283 np
= add (VRSN_FIELD
, NULL
);
284 vp
= concat (" ", VRSN_VALUE
, "\n", NULL
);
285 add_header (ct
, np
, vp
);
288 * We initally assume we will find multiple contents in the
289 * draft. So create a multipart/mixed content to hold everything.
290 * We can remove this later, if it is not needed.
292 if (get_ctinfo ("multipart/mixed", ct
, 0) == NOTOK
)
294 ct
->c_type
= CT_MULTIPART
;
295 ct
->c_subtype
= MULTI_MIXED
;
296 ct
->c_file
= add (infile
, NULL
);
298 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
299 adios (NULL
, "out of memory");
300 ct
->c_ctparams
= (void *) m
;
304 * read and parse the composition file
305 * and the directives it contains.
307 while (fgetstr (buf
, sizeof(buf
) - 1, in
)) {
311 if (user_content (in
, infile
, buf
, &p
) == DONE
) {
312 admonish (NULL
, "ignoring spurious #end");
318 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
319 adios (NULL
, "out of memory");
326 * close the composition draft since
327 * it's not needed any longer.
332 * Add any Attach headers to the list of MIME parts at the end of the
336 for (at_entry
= attach_head
; at_entry
; ) {
337 struct attach_list
*at_prev
= at_entry
;
341 if (access(at_entry
->filename
, R_OK
) != 0) {
342 adios("reading", "Unable to open %s for", at_entry
->filename
);
345 if ((p
= (CT
) calloc (1, sizeof(*p
))) == NULL
)
346 adios(NULL
, "out of memory");
348 init_decoded_content(p
);
351 * Initialize our content structure based on the filename,
352 * and fill in all of the relevant fields. Also place MIME
353 * parameters in the attributes array.
356 setup_attach_content(p
, at_entry
->filename
);
358 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
359 adios (NULL
, "out of memory");
364 at_entry
= at_entry
->next
;
365 free(at_prev
->filename
);
369 /* check if any contents were found */
371 adios (NULL
, "no content directives found");
374 * If only one content was found, then remove and
375 * free the outer multipart content.
377 if (!m
->mp_parts
->mp_next
) {
380 p
= m
->mp_parts
->mp_part
;
381 m
->mp_parts
->mp_part
= NULL
;
383 /* move header fields */
384 p
->c_first_hf
= ct
->c_first_hf
;
385 p
->c_last_hf
= ct
->c_last_hf
;
386 ct
->c_first_hf
= NULL
;
387 ct
->c_last_hf
= NULL
;
396 * Fill out, or expand directives. Parse and execute
397 * commands specified by profile composition strings.
399 compose_content (ct
);
401 if ((cp
= strchr(prefix
, 'a')) == NULL
)
402 adios (NULL
, "internal error(4)");
405 * Scan the contents. Choose a transfer encoding, and
406 * check if prefix for multipart boundary clashes with
407 * any of the contents.
409 while (scan_content (ct
) == NOTOK
) {
414 adios (NULL
, "giving up trying to find a unique delimiter string");
420 /* Build the rest of the header field structures */
428 * Set up structures for placing unencoded
429 * content when building parts.
433 init_decoded_content (CT ct
)
435 ct
->c_ceopenfnx
= open7Bit
; /* since unencoded */
436 ct
->c_ceclosefnx
= close_encoding
;
437 ct
->c_cesizefnx
= NULL
; /* since unencoded */
444 fgetstr (char *s
, int n
, FILE *stream
)
450 for (ep
= (cp
= s
) + o_n
; cp
< ep
; ) {
453 if (!fgets (cp
, n
, stream
))
454 return (cp
!= s
? s
: NULL
);
456 if (cp
== s
&& *cp
!= '#')
459 cp
+= (i
= strlen (cp
)) - 1;
460 if (i
<= 1 || *cp
-- != '\n' || *cp
!= '\\')
466 if (strcmp(s
, "#on\n") == 0) {
468 } else if (strcmp(s
, "#off\n") == 0) {
470 } else if (strcmp(s
, "#pop\n") == 0) {
482 * Parse the composition draft for text and directives.
483 * Do initial setup of Content structure.
487 user_content (FILE *in
, char *file
, char *buf
, CT
*ctp
)
495 struct str2init
*s2i
;
500 if (buf
[0] == '\n' || (do_direct() && strcmp (buf
, "#\n") == 0)) {
505 /* allocate basic Content structure */
506 if ((ct
= (CT
) calloc (1, sizeof(*ct
))) == NULL
)
507 adios (NULL
, "out of memory");
510 /* allocate basic structure for handling decoded content */
511 init_decoded_content (ct
);
518 * Handle inline text. Check if line
519 * is one of the following forms:
521 * 1) doesn't begin with '#' (implicit directive)
522 * 2) begins with "##" (implicit directive)
523 * 3) begins with "#<"
525 if (!do_direct() || buf
[0] != '#' || buf
[1] == '#' || buf
[1] == '<') {
529 char content
[BUFSIZ
];
533 cp
= m_mktemp2(NULL
, invo_name
, NULL
, &out
);
534 if (cp
== NULL
) adios("mhbuildsbr", "unable to create temporary file");
536 /* use a temp file to collect the plain text lines */
537 ce
->ce_file
= add (cp
, NULL
);
540 if (do_direct() && (buf
[0] == '#' && buf
[1] == '<')) {
541 strncpy (content
, buf
+ 2, sizeof(content
));
548 /* the directive is implicit */
549 strncpy (content
, "text/plain", sizeof(content
));
551 strncpy (buffer
, (!do_direct() || buf
[0] != '#') ? buf
: buf
+ 1, sizeof(buffer
));
555 if (headers
>= 0 && do_direct() && uprf (buffer
, DESCR_FIELD
)
556 && buffer
[i
= strlen (DESCR_FIELD
)] == ':') {
560 ct
->c_descr
= add (buffer
+ i
+ 1, ct
->c_descr
);
561 if (!fgetstr (buffer
, sizeof(buffer
) - 1, in
))
562 adios (NULL
, "end-of-file after %s: field in plaintext", DESCR_FIELD
);
570 adios (NULL
, "#-directive after %s: field in plaintext", DESCR_FIELD
);
578 if (headers
>= 0 && do_direct() && uprf (buffer
, DISPO_FIELD
)
579 && buffer
[i
= strlen (DISPO_FIELD
)] == ':') {
583 ct
->c_dispo
= add (buffer
+ i
+ 1, ct
->c_dispo
);
584 if (!fgetstr (buffer
, sizeof(buffer
) - 1, in
))
585 adios (NULL
, "end-of-file after %s: field in plaintext", DISPO_FIELD
);
593 adios (NULL
, "#-directive after %s: field in plaintext", DISPO_FIELD
);
601 if (headers
!= 1 || buffer
[0] != '\n')
607 if ((cp
= fgetstr (buffer
, sizeof(buffer
) - 1, in
)) == NULL
)
609 if (do_direct() && buffer
[0] == '#') {
612 if (buffer
[1] != '#')
614 for (cp
= (bp
= buffer
) + 1; *cp
; cp
++)
621 ct
->c_end
= ftell (out
);
624 /* parse content type */
625 if (get_ctinfo (content
, ct
, inlineD
) == NOTOK
)
628 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
629 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
631 if (!s2i
->si_key
&& !uprf (ci
->ci_type
, "X-"))
635 * check type specified (possibly implicitly)
637 switch (ct
->c_type
= s2i
->si_val
) {
639 if (!strcasecmp (ci
->ci_subtype
, "rfc822")) {
640 ct
->c_encoding
= CE_7BIT
;
645 adios (NULL
, "it doesn't make sense to define an in-line %s content",
646 ct
->c_type
== CT_MESSAGE
? "message" : "multipart");
651 if ((ct
->c_ctinitfnx
= s2i
->si_init
))
652 (*ct
->c_ctinitfnx
) (ct
);
657 fseek (in
, pos
, SEEK_SET
);
662 * If we've reached this point, the next line
663 * must be some type of explicit directive.
666 /* check if directive is external-type */
667 extrnal
= (buf
[1] == '@');
669 /* parse directive */
670 if (get_ctinfo (buf
+ (extrnal
? 2 : 1), ct
, 1) == NOTOK
)
673 /* check directive against the list of MIME types */
674 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
675 if (!strcasecmp (ci
->ci_type
, s2i
->si_key
))
679 * Check if the directive specified a valid type.
680 * This will happen if it was one of the following forms:
687 adios (NULL
, "missing subtype in \"#%s\"", ci
->ci_type
);
689 switch (ct
->c_type
= s2i
->si_val
) {
691 adios (NULL
, "use \"#begin ... #end\" instead of \"#%s/%s\"",
692 ci
->ci_type
, ci
->ci_subtype
);
696 if (!strcasecmp (ci
->ci_subtype
, "partial"))
697 adios (NULL
, "sorry, \"#%s/%s\" isn't supported",
698 ci
->ci_type
, ci
->ci_subtype
);
699 if (!strcasecmp (ci
->ci_subtype
, "external-body"))
700 adios (NULL
, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
701 ci
->ci_type
, ci
->ci_subtype
);
704 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
705 ci
->ci_type
, ci
->ci_subtype
);
709 if ((ct
->c_ctinitfnx
= s2i
->si_init
))
710 (*ct
->c_ctinitfnx
) (ct
);
715 * #@type/subtype (external types directive)
722 adios (NULL
, "need external information for \"#@%s/%s\"",
723 ci
->ci_type
, ci
->ci_subtype
);
726 snprintf (buffer
, sizeof(buffer
), "message/external-body; %s", ci
->ci_magic
);
731 * Since we are using the current Content structure to
732 * hold information about the type of the external
733 * reference, we need to create another Content structure
734 * for the message/external-body to wrap it in.
736 if ((ct
= (CT
) calloc (1, sizeof(*ct
))) == NULL
)
737 adios (NULL
, "out of memory");
740 if (get_ctinfo (buffer
, ct
, 0) == NOTOK
)
742 ct
->c_type
= CT_MESSAGE
;
743 ct
->c_subtype
= MESSAGE_EXTERNAL
;
745 if ((e
= (struct exbody
*) calloc (1, sizeof(*e
))) == NULL
)
746 adios (NULL
, "out of memory");
747 ct
->c_ctparams
= (void *) e
;
753 if (params_external (ct
, 1) == NOTOK
)
759 /* Handle [file] argument */
761 /* check if specifies command to execute */
762 if (*ci
->ci_magic
== '|' || *ci
->ci_magic
== '!') {
763 for (cp
= ci
->ci_magic
+ 1; isspace ((unsigned char) *cp
); cp
++)
766 adios (NULL
, "empty pipe command for #%s directive", ci
->ci_type
);
771 /* record filename of decoded contents */
772 ce
->ce_file
= ci
->ci_magic
;
773 if (access (ce
->ce_file
, R_OK
) == NOTOK
)
774 adios ("reading", "unable to access %s for", ce
->ce_file
);
775 if (listsw
&& stat (ce
->ce_file
, &st
) != NOTOK
)
776 ct
->c_end
= (long) st
.st_size
;
783 * No [file] argument, so check profile for
784 * method to compose content.
786 snprintf (buffer
, sizeof(buffer
), "%s-compose-%s/%s",
787 invo_name
, ci
->ci_type
, ci
->ci_subtype
);
788 if ((cp
= context_find (buffer
)) == NULL
|| *cp
== '\0') {
789 snprintf (buffer
, sizeof(buffer
), "%s-compose-%s", invo_name
, ci
->ci_type
);
790 if ((cp
= context_find (buffer
)) == NULL
|| *cp
== '\0') {
791 content_error (NULL
, ct
, "don't know how to compose content");
795 ci
->ci_magic
= add (cp
, NULL
);
800 adios (NULL
, "external definition not allowed for \"#%s\"", ci
->ci_type
);
804 * #forw [+folder] [msgs]
806 if (!strcasecmp (ci
->ci_type
, "forw")) {
808 char *folder
, *arguments
[MAXARGS
];
812 ap
= brkstring (ci
->ci_magic
, " ", "\n");
813 copyip (ap
, arguments
, MAXARGS
);
815 arguments
[0] = "cur";
820 /* search the arguments for a folder name */
821 for (ap
= arguments
; *ap
; ap
++) {
823 if (*cp
== '+' || *cp
== '@') {
825 adios (NULL
, "only one folder per #forw directive");
827 folder
= pluspath (cp
);
831 /* else, use the current folder */
833 folder
= add (getfolder (1), NULL
);
835 if (!(mp
= folder_read (folder
, 0)))
836 adios (NULL
, "unable to read folder %s", folder
);
837 for (ap
= arguments
; *ap
; ap
++) {
839 if (*cp
!= '+' && *cp
!= '@')
840 if (!m_convert (mp
, cp
))
847 * If there is more than one message to include, make this
848 * a content of type "multipart/digest" and insert each message
849 * as a subpart. If there is only one message, then make this
850 * a content of type "message/rfc822".
852 if (mp
->numsel
> 1) {
853 /* we are forwarding multiple messages */
854 if (get_ctinfo ("multipart/digest", ct
, 0) == NOTOK
)
856 ct
->c_type
= CT_MULTIPART
;
857 ct
->c_subtype
= MULTI_DIGEST
;
859 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
860 adios (NULL
, "out of memory");
861 ct
->c_ctparams
= (void *) m
;
864 for (msgnum
= mp
->lowsel
; msgnum
<= mp
->hghsel
; msgnum
++) {
865 if (is_selected(mp
, msgnum
)) {
870 if ((p
= (CT
) calloc (1, sizeof(*p
))) == NULL
)
871 adios (NULL
, "out of memory");
872 init_decoded_content (p
);
874 if (get_ctinfo ("message/rfc822", p
, 0) == NOTOK
)
876 p
->c_type
= CT_MESSAGE
;
877 p
->c_subtype
= MESSAGE_RFC822
;
879 snprintf (buffer
, sizeof(buffer
), "%s/%d", mp
->foldpath
, msgnum
);
880 pe
->ce_file
= add (buffer
, NULL
);
881 if (listsw
&& stat (pe
->ce_file
, &st
) != NOTOK
)
882 p
->c_end
= (long) st
.st_size
;
884 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
885 adios (NULL
, "out of memory");
892 /* we are forwarding one message */
893 if (get_ctinfo ("message/rfc822", ct
, 0) == NOTOK
)
895 ct
->c_type
= CT_MESSAGE
;
896 ct
->c_subtype
= MESSAGE_RFC822
;
899 snprintf (buffer
, sizeof(buffer
), "%s/%d", mp
->foldpath
, msgnum
);
900 ce
->ce_file
= add (buffer
, NULL
);
901 if (listsw
&& stat (ce
->ce_file
, &st
) != NOTOK
)
902 ct
->c_end
= (long) st
.st_size
;
905 folder_free (mp
); /* free folder/message structure */
912 if (!strcasecmp (ci
->ci_type
, "end")) {
919 * #begin [ alternative | parallel ]
921 if (!strcasecmp (ci
->ci_type
, "begin")) {
924 cp
= SubMultiPart
[vrsn
- 1].kv_key
;
925 } else if (!strcasecmp (ci
->ci_magic
, "alternative")) {
926 vrsn
= MULTI_ALTERNATE
;
927 cp
= SubMultiPart
[vrsn
- 1].kv_key
;
928 } else if (!strcasecmp (ci
->ci_magic
, "parallel")) {
929 vrsn
= MULTI_PARALLEL
;
930 cp
= SubMultiPart
[vrsn
- 1].kv_key
;
931 } else if (uprf (ci
->ci_magic
, "digest")) {
934 vrsn
= MULTI_UNKNOWN
;
939 snprintf (buffer
, sizeof(buffer
), "multipart/%s", cp
);
940 if (get_ctinfo (buffer
, ct
, 0) == NOTOK
)
942 ct
->c_type
= CT_MULTIPART
;
943 ct
->c_subtype
= vrsn
;
945 if ((m
= (struct multipart
*) calloc (1, sizeof(*m
))) == NULL
)
946 adios (NULL
, "out of memory");
947 ct
->c_ctparams
= (void *) m
;
950 while (fgetstr (buffer
, sizeof(buffer
) - 1, in
)) {
954 if (user_content (in
, file
, buffer
, &p
) == DONE
) {
956 adios (NULL
, "empty \"#begin ... #end\" sequence");
962 if ((part
= (struct part
*) calloc (1, sizeof(*part
))) == NULL
)
963 adios (NULL
, "out of memory");
968 admonish (NULL
, "premature end-of-file, missing #end");
975 adios (NULL
, "unknown directive \"#%s\"", ci
->ci_type
);
976 return NOTOK
; /* NOT REACHED */
981 set_id (CT ct
, int top
)
983 char contentid
[BUFSIZ
];
985 static time_t clock
= 0;
990 snprintf (contentid
, sizeof(contentid
), "%s\n", message_id (clock
, 1));
992 msgfmt
= getcpy(contentid
);
994 snprintf (contentid
, sizeof(contentid
), msgfmt
, top
? 0 : ++partno
);
995 ct
->c_id
= getcpy (contentid
);
1000 * Fill out, or expand the various contents in the composition
1001 * draft. Read-in any necessary files. Parse and execute any
1002 * commands specified by profile composition strings.
1006 compose_content (CT ct
)
1008 CE ce
= &ct
->c_cefile
;
1010 switch (ct
->c_type
) {
1015 char partnam
[BUFSIZ
];
1016 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1020 snprintf (partnam
, sizeof(partnam
), "%s.", ct
->c_partno
);
1021 pp
= partnam
+ strlen (partnam
);
1026 /* first, we call compose_content on all the subparts */
1027 for (part
= m
->mp_parts
, partnum
= 1; part
; part
= part
->mp_next
, partnum
++) {
1028 CT p
= part
->mp_part
;
1030 sprintf (pp
, "%d", partnum
);
1031 p
->c_partno
= add (partnam
, NULL
);
1032 if (compose_content (p
) == NOTOK
)
1037 * If the -rfc934mode switch is given, then check all
1038 * the subparts of a multipart/digest. If they are all
1039 * message/rfc822, then mark this content and all
1040 * subparts with the rfc934 compatibility mode flag.
1042 if (rfc934sw
&& ct
->c_subtype
== MULTI_DIGEST
) {
1045 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1046 CT p
= part
->mp_part
;
1048 if (p
->c_subtype
!= MESSAGE_RFC822
) {
1053 ct
->c_rfc934
= is934
;
1054 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1055 CT p
= part
->mp_part
;
1057 if ((p
->c_rfc934
= is934
))
1063 ct
->c_end
= (partnum
= strlen (prefix
) + 2) + 2;
1067 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
)
1068 ct
->c_end
+= part
->mp_part
->c_end
+ partnum
;
1074 /* Nothing to do for type message */
1078 * Discrete types (text/application/audio/image/video)
1083 int i
, xstdout
, len
, buflen
;
1084 char *bp
, **ap
, *cp
;
1085 char *vec
[4], buffer
[BUFSIZ
];
1087 CI ci
= &ct
->c_ctinfo
;
1090 if (!(cp
= ci
->ci_magic
))
1091 adios (NULL
, "internal error(5)");
1093 tfile
= m_mktemp2(NULL
, invo_name
, NULL
, NULL
);
1094 if (tfile
== NULL
) {
1095 adios("mhbuildsbr", "unable to create temporary file");
1097 ce
->ce_file
= add (tfile
, NULL
);
1102 /* Get buffer ready to go */
1105 buflen
= sizeof(buffer
);
1108 * Parse composition string into buffer
1110 for ( ; *cp
; cp
++) {
1115 /* insert parameters from directive */
1119 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1120 snprintf (bp
, buflen
, "%s%s=\"%s\"", s
, *ap
, *ep
);
1130 /* %f, and stdout is not-redirected */
1136 * insert temporary filename where
1137 * content should be written
1139 snprintf (bp
, buflen
, "%s", ce
->ce_file
);
1143 /* insert content subtype */
1144 strncpy (bp
, ci
->ci_subtype
, buflen
);
1148 /* insert character % */
1169 printf ("composing content %s/%s from command\n\t%s\n",
1170 ci
->ci_type
, ci
->ci_subtype
, buffer
);
1172 fflush (stdout
); /* not sure if need for -noverbose */
1179 if ((out
= fopen (ce
->ce_file
, "w")) == NULL
)
1180 adios (ce
->ce_file
, "unable to open for writing");
1182 for (i
= 0; (child_id
= fork()) == NOTOK
&& i
> 5; i
++)
1186 adios ("fork", "unable to fork");
1191 dup2 (fileno (out
), 1);
1192 close (fileno (out
));
1193 execvp ("/bin/sh", vec
);
1194 fprintf (stderr
, "unable to exec ");
1201 if (pidXwait(child_id
, NULL
))
1207 /* Check size of file */
1208 if (listsw
&& ct
->c_end
== 0L) {
1211 if (stat (ce
->ce_file
, &st
) != NOTOK
)
1212 ct
->c_end
= (long) st
.st_size
;
1224 * 1) choose a transfer encoding.
1225 * 2) check for clashes with multipart boundary string.
1226 * 3) for text content, figure out which character set is being used.
1228 * If there is a clash with one of the contents and the multipart boundary,
1229 * this function will exit with NOTOK. This will cause the scanning process
1230 * to be repeated with a different multipart boundary. It is possible
1231 * (although highly unlikely) that this scan will be repeated multiple times.
1235 scan_content (CT ct
)
1238 int check8bit
= 0, contains8bit
= 0; /* check if contains 8bit data */
1239 int checklinelen
= 0, linelen
= 0; /* check for long lines */
1240 int checkboundary
= 0, boundaryclash
= 0; /* check if clashes with multipart boundary */
1241 int checklinespace
= 0, linespace
= 0; /* check if any line ends with space */
1242 char *cp
= NULL
, buffer
[BUFSIZ
];
1243 struct text
*t
= NULL
;
1245 CE ce
= &ct
->c_cefile
;
1248 * handle multipart by scanning all subparts
1249 * and then checking their encoding.
1251 if (ct
->c_type
== CT_MULTIPART
) {
1252 struct multipart
*m
= (struct multipart
*) ct
->c_ctparams
;
1255 /* initially mark the domain of enclosing multipart as 7bit */
1256 ct
->c_encoding
= CE_7BIT
;
1258 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1259 CT p
= part
->mp_part
;
1261 if (scan_content (p
) == NOTOK
) /* choose encoding for subpart */
1264 /* if necessary, enlarge encoding for enclosing multipart */
1265 if (p
->c_encoding
== CE_BINARY
)
1266 ct
->c_encoding
= CE_BINARY
;
1267 if (p
->c_encoding
== CE_8BIT
&& ct
->c_encoding
!= CE_BINARY
)
1268 ct
->c_encoding
= CE_8BIT
;
1275 * Decide what to check while scanning this content.
1277 switch (ct
->c_type
) {
1281 if (ct
->c_subtype
== TEXT_PLAIN
) {
1290 case CT_APPLICATION
:
1302 /* don't check anything for message/external */
1303 if (ct
->c_subtype
== MESSAGE_EXTERNAL
)
1313 * Don't check anything for these types,
1314 * since we are forcing use of base64.
1324 * Scan the unencoded content
1326 if (check8bit
|| checklinelen
|| checklinespace
|| checkboundary
) {
1327 if ((in
= fopen (ce
->ce_file
, "r")) == NULL
)
1328 adios (ce
->ce_file
, "unable to open for reading");
1329 len
= strlen (prefix
);
1331 while (fgets (buffer
, sizeof(buffer
) - 1, in
)) {
1333 * Check for 8bit data.
1336 for (cp
= buffer
; *cp
; cp
++) {
1337 if (!isascii ((unsigned char) *cp
)) {
1339 check8bit
= 0; /* no need to keep checking */
1345 * Check line length.
1347 if (checklinelen
&& (strlen (buffer
) > CPERLIN
+ 1)) {
1349 checklinelen
= 0; /* no need to keep checking */
1353 * Check if line ends with a space.
1355 if (checklinespace
&& (cp
= buffer
+ strlen (buffer
) - 2) > buffer
&& isspace ((unsigned char) *cp
)) {
1357 checklinespace
= 0; /* no need to keep checking */
1361 * Check if content contains a line that clashes
1362 * with our standard boundary for multipart messages.
1364 if (checkboundary
&& buffer
[0] == '-' && buffer
[1] == '-') {
1365 for (cp
= buffer
+ strlen (buffer
) - 1; cp
>= buffer
; cp
--)
1366 if (!isspace ((unsigned char) *cp
))
1369 if (!strncmp(buffer
+ 2, prefix
, len
) && isdigit((unsigned char) buffer
[2 + len
])) {
1371 checkboundary
= 0; /* no need to keep checking */
1379 * Decide which transfer encoding to use.
1381 switch (ct
->c_type
) {
1384 * If the text content didn't specify a character
1385 * set, we need to figure out which one was used.
1387 t
= (struct text
*) ct
->c_ctparams
;
1388 if (t
->tx_charset
== CHARSET_UNSPECIFIED
) {
1389 CI ci
= &ct
->c_ctinfo
;
1392 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++)
1396 *ap
= concat ("charset=", write_charset_8bit(), NULL
);
1398 *ap
= add ("charset=us-ascii", NULL
);
1400 t
->tx_charset
= CHARSET_SPECIFIED
;
1402 cp
= strchr(*ap
++, '=');
1408 if (contains8bit
|| linelen
|| linespace
|| checksw
)
1409 ct
->c_encoding
= CE_QUOTED
;
1411 ct
->c_encoding
= CE_7BIT
;
1414 case CT_APPLICATION
:
1415 /* For application type, use base64, except when postscript */
1416 if (contains8bit
|| linelen
|| linespace
|| checksw
)
1417 ct
->c_encoding
= (ct
->c_subtype
== APPLICATION_POSTSCRIPT
)
1418 ? CE_QUOTED
: CE_BASE64
;
1420 ct
->c_encoding
= CE_7BIT
;
1424 ct
->c_encoding
= CE_7BIT
;
1430 /* For audio, image, and video contents, just use base64 */
1431 ct
->c_encoding
= CE_BASE64
;
1435 return (boundaryclash
? NOTOK
: OK
);
1440 * Scan the content structures, and build header
1441 * fields that will need to be output into the
1446 build_headers (CT ct
)
1448 int cc
, mailbody
, extbody
, len
;
1450 char *np
, *vp
, buffer
[BUFSIZ
];
1451 CI ci
= &ct
->c_ctinfo
;
1454 * If message is type multipart, then add the multipart
1455 * boundary to the list of attribute/value pairs.
1457 if (ct
->c_type
== CT_MULTIPART
) {
1459 static int level
= 0; /* store nesting level */
1463 snprintf (buffer
, sizeof(buffer
), "boundary=%s%d", prefix
, level
++);
1464 cp
= strchr(*ap
++ = add (buffer
, NULL
), '=');
1471 * Skip the output of Content-Type, parameters, content
1472 * description and disposition, and Content-ID if the
1473 * content is of type "message" and the rfc934 compatibility
1474 * flag is set (which means we are inside multipart/digest
1475 * and the switch -rfc934mode was given).
1477 if (ct
->c_type
== CT_MESSAGE
&& ct
->c_rfc934
)
1481 * output the content type and subtype
1483 np
= add (TYPE_FIELD
, NULL
);
1484 vp
= concat (" ", ci
->ci_type
, "/", ci
->ci_subtype
, NULL
);
1486 /* keep track of length of line */
1487 len
= strlen (TYPE_FIELD
) + strlen (ci
->ci_type
)
1488 + strlen (ci
->ci_subtype
) + 3;
1490 extbody
= ct
->c_type
== CT_MESSAGE
&& ct
->c_subtype
== MESSAGE_EXTERNAL
;
1491 mailbody
= extbody
&& ((struct exbody
*) ct
->c_ctparams
)->eb_body
;
1494 * Append the attribute/value pairs to
1495 * the end of the Content-Type line.
1497 for (ap
= ci
->ci_attrs
, ep
= ci
->ci_values
; *ap
; ap
++, ep
++) {
1498 if (mailbody
&& !strcasecmp (*ap
, "body"))
1505 * According to RFC 2017, if we have a URL longer than 40 characters
1506 * we have to break it across multiple lines
1509 if (extbody
&& strcasecmp (*ap
, "url") == 0) {
1512 /* 7 here refers to " url=\"\"" */
1513 if (len
+ 1 + (cc
= (min(MAXURLTOKEN
, strlen(value
)) + 7)) >=
1515 vp
= add ("\n\t", vp
);
1522 vp
= add ("url=\"", vp
);
1525 while (strlen(value
) > MAXURLTOKEN
) {
1526 strncpy(buffer
, value
, MAXURLTOKEN
);
1527 buffer
[MAXURLTOKEN
] = '\0';
1528 vp
= add (buffer
, vp
);
1529 vp
= add ("\n\t", vp
);
1530 value
+= MAXURLTOKEN
;
1534 vp
= add (value
, vp
);
1535 vp
= add ("\"", vp
);
1536 len
+= strlen(value
) + 1;
1540 snprintf (buffer
, sizeof(buffer
), "%s=\"%s\"", *ap
, *ep
);
1541 if (len
+ 1 + (cc
= strlen (buffer
)) >= CPERLIN
) {
1542 vp
= add ("\n\t", vp
);
1548 vp
= add (buffer
, vp
);
1553 * Append any RFC-822 comment to the end of
1554 * the Content-Type line.
1556 if (ci
->ci_comment
) {
1557 snprintf (buffer
, sizeof(buffer
), "(%s)", ci
->ci_comment
);
1558 if (len
+ 1 + (cc
= 2 + strlen (ci
->ci_comment
)) >= CPERLIN
) {
1559 vp
= add ("\n\t", vp
);
1565 vp
= add (buffer
, vp
);
1568 vp
= add ("\n", vp
);
1569 add_header (ct
, np
, vp
);
1572 * output the Content-ID, unless disabled by -nocontentid
1574 if (contentidsw
&& ct
->c_id
) {
1575 np
= add (ID_FIELD
, NULL
);
1576 vp
= concat (" ", ct
->c_id
, NULL
);
1577 add_header (ct
, np
, vp
);
1581 * output the Content-Description
1584 np
= add (DESCR_FIELD
, NULL
);
1585 vp
= concat (" ", ct
->c_descr
, NULL
);
1586 add_header (ct
, np
, vp
);
1590 * output the Content-Disposition
1593 np
= add (DISPO_FIELD
, NULL
);
1594 vp
= concat (" ", ct
->c_dispo
, NULL
);
1595 add_header (ct
, np
, vp
);
1600 * If this is the internal content structure for a
1601 * "message/external", then we are done with the
1602 * headers (since it has no body).
1608 * output the Content-MD5
1611 np
= add (MD5_FIELD
, NULL
);
1612 vp
= calculate_digest (ct
, (ct
->c_encoding
== CE_QUOTED
) ? 1 : 0);
1613 add_header (ct
, np
, vp
);
1617 * output the Content-Transfer-Encoding
1619 switch (ct
->c_encoding
) {
1621 /* Nothing to output */
1625 if (ct
->c_type
== CT_MESSAGE
)
1626 adios (NULL
, "internal error, invalid encoding");
1628 np
= add (ENCODING_FIELD
, NULL
);
1629 vp
= concat (" ", "8bit", "\n", NULL
);
1630 add_header (ct
, np
, vp
);
1634 if (ct
->c_type
== CT_MESSAGE
|| ct
->c_type
== CT_MULTIPART
)
1635 adios (NULL
, "internal error, invalid encoding");
1637 np
= add (ENCODING_FIELD
, NULL
);
1638 vp
= concat (" ", "quoted-printable", "\n", NULL
);
1639 add_header (ct
, np
, vp
);
1643 if (ct
->c_type
== CT_MESSAGE
|| ct
->c_type
== CT_MULTIPART
)
1644 adios (NULL
, "internal error, invalid encoding");
1646 np
= add (ENCODING_FIELD
, NULL
);
1647 vp
= concat (" ", "base64", "\n", NULL
);
1648 add_header (ct
, np
, vp
);
1652 if (ct
->c_type
== CT_MESSAGE
)
1653 adios (NULL
, "internal error, invalid encoding");
1655 np
= add (ENCODING_FIELD
, NULL
);
1656 vp
= concat (" ", "binary", "\n", NULL
);
1657 add_header (ct
, np
, vp
);
1661 adios (NULL
, "unknown transfer encoding in content");
1666 * Additional content specific header processing
1668 switch (ct
->c_type
) {
1671 struct multipart
*m
;
1674 m
= (struct multipart
*) ct
->c_ctparams
;
1675 for (part
= m
->mp_parts
; part
; part
= part
->mp_next
) {
1685 if (ct
->c_subtype
== MESSAGE_EXTERNAL
) {
1688 e
= (struct exbody
*) ct
->c_ctparams
;
1689 build_headers (e
->eb_content
);
1702 static char nib2b64
[0x40+1] =
1703 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1706 calculate_digest (CT ct
, int asciiP
)
1709 char buffer
[BUFSIZ
], *vp
, *op
;
1711 unsigned char digest
[16];
1712 unsigned char outbuf
[25];
1714 CE ce
= &ct
->c_cefile
;
1715 char *infilename
= ce
->ce_file
? ce
->ce_file
: ct
->c_file
;
1719 if ((in
= fopen (infilename
, "r")) == NULL
)
1720 adios (infilename
, "unable to open for reading");
1722 /* Initialize md5 context */
1723 MD5Init (&mdContext
);
1725 /* calculate md5 message digest */
1727 while (fgets (buffer
, sizeof(buffer
) - 1, in
)) {
1730 cp
= buffer
+ strlen (buffer
) - 1;
1731 if ((c
= *cp
) == '\n')
1734 MD5Update (&mdContext
, (unsigned char *) buffer
,
1735 (unsigned int) strlen (buffer
));
1738 MD5Update (&mdContext
, (unsigned char *) "\r\n", 2);
1741 while ((cc
= fread (buffer
, sizeof(*buffer
), sizeof(buffer
), in
)) > 0)
1742 MD5Update (&mdContext
, (unsigned char *) buffer
, (unsigned int) cc
);
1745 /* md5 finalization. Write digest and zero md5 context */
1746 MD5Final (digest
, &mdContext
);
1751 /* print debugging info */
1755 fprintf (stderr
, "MD5 digest=");
1756 for (ep
= (dp
= digest
) + sizeof(digest
) / sizeof(digest
[0]);
1758 fprintf (stderr
, "%02x", *dp
& 0xff);
1759 fprintf (stderr
, "\n");
1762 /* encode the digest using base64 */
1763 for (dp
= digest
, op
= (char *) outbuf
,
1764 cc
= sizeof(digest
) / sizeof(digest
[0]);
1765 cc
> 0; cc
-= 3, op
+= 4) {
1769 bits
= (*dp
++ & 0xff) << 16;
1771 bits
|= (*dp
++ & 0xff) << 8;
1773 bits
|= *dp
++ & 0xff;
1776 for (bp
= op
+ 4; bp
> op
; bits
>>= 6)
1777 *--bp
= nib2b64
[bits
& 0x3f];
1785 /* null terminate string */
1788 /* now make copy and return string */
1789 vp
= concat (" ", outbuf
, "\n", NULL
);
1794 * Set things up for the content structure for file "filename" that
1799 setup_attach_content(CT ct
, char *filename
)
1801 char *type
, **ap
, **ep
, *simplename
= r1bindex(filename
, '/');
1802 struct str2init
*s2i
;
1804 if (! (type
= mime_type(filename
))) {
1805 adios(NULL
, "Unable to determine MIME type of \"%s\"", filename
);
1809 * Parse the Content-Type. get_ctinfo() parses MIME parameters, but
1810 * since we're just feeding it a MIME type we have to add those ourself.
1811 * Map that to a valid content-type label and call any initialization
1815 if (get_ctinfo(type
, ct
, 0) == NOTOK
)
1820 for (s2i
= str2cts
; s2i
->si_key
; s2i
++)
1821 if (strcasecmp(ct
->c_ctinfo
.ci_type
, s2i
->si_key
) == 0)
1823 if (!s2i
->si_key
&& !uprf(ct
->c_ctinfo
.ci_type
, "X-"))
1827 * Make sure the type isn't incompatible with what we can handle
1830 switch (ct
->c_type
= s2i
->si_val
) {
1832 adios (NULL
, "multipart types must be specified by mhbuild directives");
1836 if (strcasecmp(ct
->c_ctinfo
.ci_subtype
, "partial") == 0)
1837 adios(NULL
, "Sorry, %s/%s isn't supported", ct
->c_ctinfo
.ci_type
,
1838 ct
->c_ctinfo
.ci_subtype
);
1839 if (strcasecmp(ct
->c_ctinfo
.ci_subtype
, "external-body") == 0)
1840 adios(NULL
, "external-body messages must be specified "
1841 "by mhbuild directives");
1846 * This sets the subtype, if it's significant
1848 if ((ct
->c_ctinitfnx
= s2i
->si_init
))
1849 (*ct
->c_ctinitfnx
)(ct
);
1854 * Feed in a few attributes; specifically, the name attribute, the
1855 * content-description, and the content-disposition.
1858 for (ap
= ct
->c_ctinfo
.ci_attrs
, ep
= ct
->c_ctinfo
.ci_values
; *ap
;
1860 if (strcasecmp(*ap
, "name") == 0) {
1863 *ep
= getcpy(simplename
);
1869 *ap
= getcpy("name");
1870 *ep
= getcpy(simplename
);
1873 ct
->c_descr
= getcpy(simplename
);
1874 ct
->c_descr
= add("\n", ct
->c_descr
);
1875 ct
->c_cefile
.ce_file
= getcpy(filename
);
1878 * If it's a text/calendar, we need to make sure it's an inline,
1879 * otherwise it won't work with some calendar programs. Otherwise
1883 if (strcasecmp(ct
->c_ctinfo
.ci_type
, "text") == 0 &&
1884 strcasecmp(ct
->c_ctinfo
.ci_subtype
, "calendar") == 0) {
1885 ct
->c_dispo
= getcpy("inline; filename=\"");
1887 ct
->c_dispo
= getcpy("attachment; filename=\"");
1890 ct
->c_dispo
= add(simplename
, ct
->c_dispo
);
1891 ct
->c_dispo
= add("\"\n", ct
->c_dispo
);