Merged h/mts.h into h/prototypes.h.
[mmh] / uip / mhoutsbr.c
1 /*
2 ** mhoutsbr.c -- routines to output MIME messages
3 **            -- given a Content structure
4 **
5 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
6 ** COPYRIGHT file in the root directory of the nmh distribution for
7 ** complete copyright information.
8 */
9
10 #include <h/mh.h>
11 #include <fcntl.h>
12 #include <h/signals.h>
13 #include <h/md5.h>
14 #include <errno.h>
15 #include <signal.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 ebcdicsw;
26
27 static char ebcdicsafe[0x100] = {
28         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29         0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
30         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32         0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
33         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
34         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
35         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
36         0x00, 0x01, 0x01, 0x01, 0x01, 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, 0x00, 0x00, 0x00, 0x00, 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, 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         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
60 };
61
62 static char nib2b64[0x40+1] =
63         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
64
65 /*
66 ** prototypes
67 */
68 int output_message(CT, char *);
69 int output_message_fp(CT, FILE *, 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_fp(CT ct, FILE *fp, char *file)
91 {
92         if (output_content(ct, fp) == NOTOK)
93                 return NOTOK;
94
95         if (fflush(fp)) {
96                 advise((file?file:"<FILE*>"), "error writing to");
97                 return NOTOK;
98         }
99         return OK;
100 }
101
102 int
103 output_message(CT ct, char *file)
104 {
105         FILE *fp;
106         int status;
107
108         if ((fp = fopen(file, "w")) == NULL) {
109                 advise(file, "unable to open for writing");
110                 return NOTOK;
111         }
112         status = output_message_fp(ct, fp, file);
113         fclose(fp);
114         return status;
115 }
116
117
118 /*
119 ** Output a Content structure to a file.
120 */
121
122 static int
123 output_content(CT ct, FILE *out)
124 {
125         int result = 0;
126         CI ci = &ct->c_ctinfo;
127
128         /*
129         ** Output all header fields for this content
130         */
131         output_headers(ct, out);
132
133         /*
134         ** If this is the internal content structure for a
135         ** "message/external", then we are done with the
136         ** headers (since it has no body).
137         */
138         if (ct->c_ctexbody)
139                 return OK;
140
141         /*
142         ** Now output the content bodies.
143         */
144         switch (ct->c_type) {
145         case CT_MULTIPART:
146         {
147                 struct multipart *m;
148                 struct part *part;
149
150                 if (ct->c_rfc934)
151                         putc('\n', out);
152
153                 m = (struct multipart *) ct->c_ctparams;
154                 for (part = m->mp_parts; part; part = part->mp_next) {
155                         CT p = part->mp_part;
156
157                         fprintf(out, "\n--%s\n", ci->ci_values[0]);
158                         if (output_content(p, out) == NOTOK)
159                                 return NOTOK;
160                 }
161                 fprintf(out, "\n--%s--\n", ci->ci_values[0]);
162         }
163         break;
164
165         case CT_MESSAGE:
166                 putc('\n', out);
167                 if (ct->c_subtype == MESSAGE_EXTERNAL) {
168                         struct exbody *e;
169
170                         e = (struct exbody *) ct->c_ctparams;
171                         if (output_content(e->eb_content, out) == NOTOK)
172                                 return NOTOK;
173
174                         /* output phantom body for access-type "mail-server" */
175                         if (e->eb_body)
176                                 writeExternalBody(ct, out);
177                 } else {
178                         result = write8Bit(ct, out);
179                 }
180                 break;
181
182         /*
183         ** Handle discrete types (text/application/audio/image/video)
184         */
185         default:
186                 switch (ct->c_encoding) {
187                 case CE_7BIT:
188                         putc('\n', out);
189                         result = write8Bit(ct, out);
190                         break;
191
192                 case CE_8BIT:
193                         putc('\n', out);
194                         result = write8Bit(ct, out);
195                         break;
196
197                 case CE_QUOTED:
198                         putc('\n', out);
199                         result = writeQuoted(ct, out);
200                         break;
201
202                 case CE_BASE64:
203                         putc('\n', out);
204                         result = writeBase64(ct, out);
205                         break;
206
207                 case CE_BINARY:
208                         advise(NULL, "can't handle binary transfer encoding in content");
209                         result = NOTOK;
210                         break;
211
212                 default:
213                         advise(NULL, "unknown transfer encoding in content");
214                         result = NOTOK;
215                         break;
216                 }
217                 break;
218         }
219
220         return result;
221 }
222
223
224 /*
225 ** Output all the header fields for a content
226 */
227
228 static void
229 output_headers(CT ct, FILE *out)
230 {
231         HF hp;
232
233         hp = ct->c_first_hf;
234         while (hp) {
235                 fprintf(out, "%s:%s", hp->name, hp->value);
236                 hp = hp->next;
237         }
238 }
239
240
241 /*
242 ** Write the phantom body for access-type "mail-server".
243 */
244
245 static int
246 writeExternalBody(CT ct, FILE *out)
247 {
248         char **ap, **ep, *cp;
249         struct exbody *e = (struct exbody *) ct->c_ctparams;
250
251         putc('\n', out);
252         for (cp = e->eb_body; *cp; cp++) {
253                 CT ct2 = e->eb_content;
254                 CI ci2 = &ct2->c_ctinfo;
255
256                 if (*cp == '\\') {
257                         switch (*++cp) {
258                         case 'I':
259                                 if (ct2->c_id) {
260                                         char *dp = trimcpy(ct2->c_id);
261
262                                         fputs(dp, out);
263                                         free(dp);
264                                 }
265                                 continue;
266
267                         case 'N':
268                                 for (ap = ci2->ci_attrs, ep = ci2->ci_values;
269                                                 *ap; ap++, ep++)
270                                         if (!mh_strcasecmp(*ap, "name")) {
271                                                 fprintf(out, "%s", *ep);
272                                                 break;
273                                         }
274                                 continue;
275
276                         case 'T':
277                                 fprintf(out, "%s/%s", ci2->ci_type,
278                                                 ci2->ci_subtype);
279                                 for (ap = ci2->ci_attrs, ep = ci2->ci_values;
280                                                 *ap; ap++, ep++)
281                                         fprintf(out, "; %s=\"%s\"", *ap, *ep);
282                                 continue;
283
284                         case 'n':
285                                 putc('\n', out);
286                                 continue;
287
288                         case 't':
289                                 putc('\t', out);
290                                 continue;
291
292                         case '\0':
293                                 cp--;
294                                 break;
295
296                         case '\\':
297                         case '"':
298                                 break;
299
300                         default:
301                                 putc('\\', out);
302                                 break;
303                         }
304                 }
305                 putc(*cp, out);
306         }
307         putc('\n', out);
308
309         return OK;
310 }
311
312
313 /*
314 ** Output a content without any transfer encoding
315 */
316
317 static int
318 write8Bit(CT ct, FILE *out)
319 {
320         int fd;
321         char c, *file, buffer[BUFSIZ];
322         CE ce = ct->c_cefile;
323
324         file = NULL;
325         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
326                 return NOTOK;
327
328         c = '\n';
329         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
330                 c = buffer[strlen(buffer) - 1];
331                 fputs(buffer, out);
332         }
333         if (c != '\n')
334                 putc('\n', out);
335
336         (*ct->c_ceclosefnx) (ct);
337         return OK;
338 }
339
340
341 /*
342 ** Output a content using quoted-printable
343 */
344
345 static int
346 writeQuoted(CT ct, FILE *out)
347 {
348         int fd;
349         char *cp, *file;
350         char c, buffer[BUFSIZ];
351         CE ce = ct->c_cefile;
352
353         file = NULL;
354         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
355                 return NOTOK;
356
357         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
358                 int n;
359
360                 cp = buffer + strlen(buffer) - 1;
361                 if ((c = *cp) == '\n')
362                         *cp = '\0';
363
364                 if (strncmp(cp = buffer, "From ", sizeof("From ") - 1) == 0) {
365                         fprintf(out, "=%02X", *cp++ & 0xff);
366                         n = 3;
367                 } else {
368                         n = 0;
369                 }
370                 for (; *cp; cp++) {
371                         if (n > CPERLIN - 3) {
372                                 fputs("=\n", out);
373                                 n = 0;
374                         }
375
376                         switch (*cp) {
377                         case ' ':
378                         case '\t':
379                                 putc(*cp, out);
380                                 n++;
381                                 break;
382
383                         default:
384                                 if (*cp < '!' || *cp > '~' || (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 }