c2e080566b57c9357b8adfca3bc68ed06f40a23f
[mmh] / uip / mhoutsbr.c
1
2 /*
3  * mhoutsbr.c -- routines to output MIME messages
4  *            -- given a Content structure
5  *
6  * $Id$
7  */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/signals.h>
12 #include <h/md5.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <zotnet/mts/mts.h>
16 #include <h/tws.h>
17 #include <h/mime.h>
18 #include <h/mhparse.h>
19
20 #ifdef HAVE_SYS_WAIT_H
21 # include <sys/wait.h>
22 #endif
23
24
25 extern int errno;
26 extern int ebcdicsw;
27
28 static char ebcdicsafe[0x100] = {
29     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30     0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
31     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33     0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
34     0x01, 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     0x00, 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     0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 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, 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     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 };
62
63 static char nib2b64[0x40+1] =
64         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
65
66 /*
67  * prototypes
68  */
69 int output_message (CT, char *);
70 int writeBase64aux (FILE *, FILE *);
71
72 /*
73  * static prototypes
74  */
75 static int output_content (CT, FILE *);
76 static void output_headers (CT, FILE *);
77 static int writeExternalBody (CT, FILE *);
78 static int write8Bit (CT, FILE *);
79 static int writeQuoted (CT, FILE *);
80 static int writeBase64 (CT, FILE *);
81
82
83 /*
84  * Main routine to output a MIME message contained
85  * in a Content structure, to a file.  Any necessary
86  * transfer encoding is added.
87  */
88
89 int
90 output_message (CT ct, char *file)
91 {
92     FILE *fp;
93
94     if ((fp = fopen (file, "w")) == NULL) {
95         advise (file, "unable to open for writing");
96         return NOTOK;
97     }
98
99     if (output_content (ct, fp) == NOTOK)
100         return NOTOK;
101
102     if (fflush (fp)) {
103         advise (file, "error writing to");
104         return NOTOK;
105     }
106     fclose (fp);
107
108     return OK;
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 = writeBase64 (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 (!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 writeBase64 (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 }
424
425
426 int
427 writeBase64aux (FILE *in, FILE *out)
428 {
429     int cc, n;
430     char inbuf[3];
431
432     n = BPERLIN;
433     while ((cc = fread (inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
434         unsigned long bits;
435         char *bp;
436         char outbuf[4];
437
438         if (cc < sizeof(inbuf)) {
439             inbuf[2] = 0;
440             if (cc < sizeof(inbuf) - 1)
441                 inbuf[1] = 0;
442         }
443         bits = (inbuf[0] & 0xff) << 16;
444         bits |= (inbuf[1] & 0xff) << 8;
445         bits |= inbuf[2] & 0xff;
446
447         for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
448             *--bp = nib2b64[bits & 0x3f];
449         if (cc < sizeof(inbuf)) {
450             outbuf[3] = '=';
451             if (cc < sizeof inbuf - 1)
452                 outbuf[2] = '=';
453         }
454
455         fwrite (outbuf, sizeof(*outbuf), sizeof(outbuf), out);
456
457         if (cc < sizeof(inbuf)) {
458             putc ('\n', out);
459             return OK;
460         }
461
462         if (--n <= 0) {
463             n = BPERLIN;
464             putc ('\n', out);
465         }
466     }
467     if (n != BPERLIN)
468         putc ('\n', out);
469
470     return OK;
471 }