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