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