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