Added -messageid switch to send(1) and post(8). This allows selection
[mmh] / uip / mhoutsbr.c
1
2 /*
3  * mhoutsbr.c -- routines to output MIME messages
4  *            -- given a Content structure
5  *
6  * This code is Copyright (c) 2002, by the authors of nmh.  See the
7  * COPYRIGHT file in the root directory of the nmh distribution for
8  * complete copyright information.
9  */
10
11 #include <h/mh.h>
12 #include <fcntl.h>
13 #include <h/signals.h>
14 #include <h/md5.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <h/mts.h>
18 #include <h/tws.h>
19 #include <h/mime.h>
20 #include <h/mhparse.h>
21
22
23 extern int ebcdicsw;
24
25 static char ebcdicsafe[0x100] = {
26     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27     0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
28     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30     0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
31     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
32     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
33     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
34     0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
35     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
36     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
37     0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
38     0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
39     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
40     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
41     0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
42     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
58 };
59
60 /*
61  * prototypes
62  */
63 int output_message (CT, char *);
64 int output_message_fp (CT, FILE *, char *);
65
66 /*
67  * static prototypes
68  */
69 static int output_content (CT, FILE *);
70 static void output_headers (CT, FILE *);
71 static int writeExternalBody (CT, FILE *);
72 static int write8Bit (CT, FILE *);
73 static int writeQuoted (CT, FILE *);
74 static int writeBase64ct (CT, FILE *);
75
76
77 /*
78  * Main routine to output a MIME message contained
79  * in a Content structure, to a file.  Any necessary
80  * transfer encoding is added.
81  */
82
83 int
84 output_message_fp (CT ct, FILE *fp, char *file)
85 {
86     if (output_content (ct, fp) == NOTOK)
87         return NOTOK;
88
89     if (fflush (fp)) {
90         advise ((file?file:"<FILE*>"), "error writing to");
91         return NOTOK;
92     }
93     return OK;
94 }
95
96 int
97 output_message (CT ct, char *file)
98 {
99     FILE *fp;
100     int status;
101
102     if ((fp = fopen (file, "w")) == NULL) {
103         advise (file, "unable to open for writing");
104         return NOTOK;
105     }
106     status = output_message_fp(ct, fp, file);
107     fclose(fp);
108     return status;
109 }
110
111
112 /*
113  * Output a Content structure to a file.
114  */
115
116 static int
117 output_content (CT ct, FILE *out)
118 {
119     int result = 0;
120     CI ci = &ct->c_ctinfo;
121
122     /*
123      * Output all header fields for this content
124      */
125     output_headers (ct, out);
126
127     /*
128      * If this is the internal content structure for a
129      * "message/external", then we are done with the
130      * headers (since it has no body).
131      */
132     if (ct->c_ctexbody)
133         return OK;
134
135     /*
136      * Now output the content bodies.
137      */
138     switch (ct->c_type) {
139     case CT_MULTIPART:
140     {
141         struct multipart *m;
142         struct part *part;
143
144         if (ct->c_rfc934)
145             putc ('\n', out);
146
147         m = (struct multipart *) ct->c_ctparams;
148         for (part = m->mp_parts; part; part = part->mp_next) {
149             CT p = part->mp_part;
150
151             fprintf (out, "\n--%s\n", ci->ci_values[0]);
152             if (output_content (p, out) == NOTOK)
153                 return NOTOK;
154         }
155         fprintf (out, "\n--%s--\n", ci->ci_values[0]);
156     }
157     break;
158
159     case CT_MESSAGE:
160         putc ('\n', out);
161         if (ct->c_subtype == MESSAGE_EXTERNAL) {
162             struct exbody *e;
163
164             e = (struct exbody *) ct->c_ctparams;
165             if (output_content (e->eb_content, out) == NOTOK)
166                 return NOTOK;
167
168             /* output phantom body for access-type "mail-server" */
169             if (e->eb_body)
170                 writeExternalBody (ct, out);
171         } else {
172             result = write8Bit (ct, out);
173         }
174         break;
175
176     /*
177      * Handle discrete types (text/application/audio/image/video)
178      */
179     default:
180         switch (ct->c_encoding) {
181         case CE_7BIT:
182             putc ('\n', out);
183             result = write8Bit (ct, out);
184             break;
185
186         case CE_8BIT:
187             putc ('\n', out);
188             result = write8Bit (ct, out);
189             break;
190
191         case CE_QUOTED:
192             putc ('\n', out);
193             result = writeQuoted (ct, out);
194             break;
195
196         case CE_BASE64:
197             putc ('\n', out);
198             result = writeBase64ct (ct, out);
199             break;
200
201         case CE_BINARY:
202             advise (NULL, "can't handle binary transfer encoding in content");
203             result = NOTOK;
204             break;
205
206         default:
207             advise (NULL, "unknown transfer encoding in content");
208             result = NOTOK;
209             break;
210         }
211         break;
212     }
213
214     return result;
215 }
216
217
218 /*
219  * Output all the header fields for a content
220  */
221
222 static void
223 output_headers (CT ct, FILE *out)
224 {
225     HF hp;
226
227     hp = ct->c_first_hf;
228     while (hp) {
229         fprintf (out, "%s:%s", hp->name, hp->value);
230         hp = hp->next;
231     }
232 }
233
234
235 /*
236  * Write the phantom body for access-type "mail-server".
237  */
238
239 static int
240 writeExternalBody (CT ct, FILE *out)
241 {
242     char **ap, **ep, *cp;
243     struct exbody *e = (struct exbody *) ct->c_ctparams;
244                 
245     putc ('\n', out);
246     for (cp = e->eb_body; *cp; cp++) {
247         CT ct2 = e->eb_content;
248         CI ci2 = &ct2->c_ctinfo;
249
250         if (*cp == '\\') {
251             switch (*++cp) {
252             case 'I':
253                 if (ct2->c_id) {
254                     char *dp = trimcpy (ct2->c_id);
255
256                     fputs (dp, out);
257                     free (dp);
258                 }
259                 continue;
260
261             case 'N':
262                 for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
263                     if (!mh_strcasecmp (*ap, "name")) {
264                         fprintf (out, "%s", *ep);
265                         break;
266                     }
267                 continue;
268
269             case 'T':
270                 fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
271                 for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
272                     fprintf (out, "; %s=\"%s\"", *ap, *ep);
273                 continue;
274
275             case 'n':
276                 putc ('\n', out);
277                 continue;
278
279             case 't':
280                 putc ('\t', out);
281                 continue;
282
283             case '\0':
284                 cp--;
285                 break;
286
287             case '\\':
288             case '"':
289                 break;
290
291             default:
292                 putc ('\\', out);
293                 break;
294             }
295         }
296         putc (*cp, out);
297     }
298     putc ('\n', out);
299
300     return OK;
301 }
302
303
304 /*
305  * Output a content without any transfer encoding
306  */
307
308 static int
309 write8Bit (CT ct, FILE *out)
310 {
311     int fd;
312     char c, *file, buffer[BUFSIZ];
313     CE ce = ct->c_cefile;
314
315     file = NULL;
316     if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
317         return NOTOK;
318
319     c = '\n';
320     while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
321         c = buffer[strlen (buffer) - 1];
322         fputs (buffer, out);
323     }
324     if (c != '\n')
325         putc ('\n', out);
326
327     (*ct->c_ceclosefnx) (ct);
328     return OK;
329 }
330
331
332 /*
333  * Output a content using quoted-printable
334  */
335
336 static int
337 writeQuoted (CT ct, FILE *out)
338 {
339     int fd;
340     char *cp, *file;
341     char c, buffer[BUFSIZ];
342     CE ce = ct->c_cefile;
343
344     file = NULL;
345     if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
346         return NOTOK;
347
348     while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
349         int n;
350
351         cp = buffer + strlen (buffer) - 1;
352         if ((c = *cp) == '\n')
353             *cp = '\0';
354
355         if (strncmp (cp = buffer, "From ", sizeof("From ") - 1) == 0) {
356             fprintf (out, "=%02X", *cp++ & 0xff);
357             n = 3;
358         } else {
359             n = 0;
360         }
361         for (; *cp; cp++) {
362             if (n > CPERLIN - 3) {
363                 fputs ("=\n", out);
364                 n = 0;
365             }
366
367             switch (*cp) {
368                 case ' ':
369                 case '\t':
370                     putc (*cp, out);
371                     n++;
372                     break;
373
374                 default:
375                     if (*cp < '!' || *cp > '~'
376                             || (ebcdicsw && !ebcdicsafe[*cp & 0xff]))
377                         goto three_print;
378                     putc (*cp, out);
379                     n++;
380                     break;
381
382                 case '=':
383 three_print:
384                     fprintf (out, "=%02X", *cp & 0xff);
385                     n += 3;
386                     break;
387             }
388         }
389
390         if (c == '\n') {
391             if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
392                 fputs ("=\n", out);
393
394             putc ('\n', out);
395         } else {
396             fputs ("=\n", out);
397         }
398     }
399
400     (*ct->c_ceclosefnx) (ct);
401     return OK;
402 }
403
404
405 /*
406  * Output a content using base64
407  */
408
409 static int
410 writeBase64ct (CT ct, FILE *out)
411 {
412     int fd, result;
413     char *file;
414     CE ce = ct->c_cefile;
415
416     file = NULL;
417     if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
418         return NOTOK;
419
420     result = writeBase64aux (ce->ce_fp, out);
421     (*ct->c_ceclosefnx) (ct);
422     return result;
423 }