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