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