Removed RFC 1864 (Content-MD5) support. I.e. -check switches.
[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 <errno.h>
14 #include <signal.h>
15 #include <h/tws.h>
16 #include <h/mime.h>
17 #include <h/mhparse.h>
18
19 extern int ebcdicsw;
20
21 static char ebcdicsafe[0x100] = {
22         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
23         0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
24         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26         0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
27         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
28         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
29         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
30         0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
31         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
32         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
33         0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
34         0x00, 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         0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
38         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43         0x00, 0x00, 0x00, 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 };
55
56 static char nib2b64[0x40+1] =
57         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
58
59 /*
60 ** prototypes
61 */
62 int output_message(CT, char *);
63 int output_message_fp(CT, FILE *, char *);
64 int writeBase64aux(FILE *, FILE *);
65
66 /*
67 ** static prototypes
68 */
69 static int output_content(CT, FILE *);
70 static void output_headers(CT, FILE *);
71 static int writeExternalBody(CT, FILE *);
72 static int write8Bit(CT, FILE *);
73 static int writeQuoted(CT, FILE *);
74 static int writeBase64(CT, FILE *);
75
76
77 /*
78 ** Main routine to output a MIME message contained
79 ** in a Content structure, to a file.  Any necessary
80 ** transfer encoding is added.
81 */
82
83 int
84 output_message_fp(CT ct, FILE *fp, char *file)
85 {
86         if (output_content(ct, fp) == NOTOK)
87                 return NOTOK;
88
89         if (fflush(fp)) {
90                 advise((file?file:"<FILE*>"), "error writing to");
91                 return NOTOK;
92         }
93         return OK;
94 }
95
96 int
97 output_message(CT ct, char *file)
98 {
99         FILE *fp;
100         int status;
101
102         if ((fp = fopen(file, "w")) == NULL) {
103                 advise(file, "unable to open for writing");
104                 return NOTOK;
105         }
106         status = output_message_fp(ct, fp, file);
107         fclose(fp);
108         return status;
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;
263                                                 *ap; ap++, ep++)
264                                         if (!mh_strcasecmp(*ap, "name")) {
265                                                 fprintf(out, "%s", *ep);
266                                                 break;
267                                         }
268                                 continue;
269
270                         case 'T':
271                                 fprintf(out, "%s/%s", ci2->ci_type,
272                                                 ci2->ci_subtype);
273                                 for (ap = ci2->ci_attrs, ep = ci2->ci_values;
274                                                 *ap; ap++, ep++)
275                                         fprintf(out, "; %s=\"%s\"", *ap, *ep);
276                                 continue;
277
278                         case 'n':
279                                 putc('\n', out);
280                                 continue;
281
282                         case 't':
283                                 putc('\t', out);
284                                 continue;
285
286                         case '\0':
287                                 cp--;
288                                 break;
289
290                         case '\\':
291                         case '"':
292                                 break;
293
294                         default:
295                                 putc('\\', out);
296                                 break;
297                         }
298                 }
299                 putc(*cp, out);
300         }
301         putc('\n', out);
302
303         return OK;
304 }
305
306
307 /*
308 ** Output a content without any transfer encoding
309 */
310
311 static int
312 write8Bit(CT ct, FILE *out)
313 {
314         int fd;
315         char c, *file, buffer[BUFSIZ];
316         CE ce = ct->c_cefile;
317
318         file = NULL;
319         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
320                 return NOTOK;
321
322         c = '\n';
323         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
324                 c = buffer[strlen(buffer) - 1];
325                 fputs(buffer, out);
326         }
327         if (c != '\n')
328                 putc('\n', out);
329
330         (*ct->c_ceclosefnx) (ct);
331         return OK;
332 }
333
334
335 /*
336 ** Output a content using quoted-printable
337 */
338
339 static int
340 writeQuoted(CT ct, FILE *out)
341 {
342         int fd;
343         char *cp, *file;
344         char c, buffer[BUFSIZ];
345         CE ce = ct->c_cefile;
346
347         file = NULL;
348         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
349                 return NOTOK;
350
351         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
352                 int n;
353
354                 cp = buffer + strlen(buffer) - 1;
355                 if ((c = *cp) == '\n')
356                         *cp = '\0';
357
358                 if (strncmp(cp = buffer, "From ", sizeof("From ") - 1) == 0) {
359                         fprintf(out, "=%02X", *cp++ & 0xff);
360                         n = 3;
361                 } else {
362                         n = 0;
363                 }
364                 for (; *cp; cp++) {
365                         if (n > CPERLIN - 3) {
366                                 fputs("=\n", out);
367                                 n = 0;
368                         }
369
370                         switch (*cp) {
371                         case ' ':
372                         case '\t':
373                                 putc(*cp, out);
374                                 n++;
375                                 break;
376
377                         default:
378                                 if (*cp < '!' || *cp > '~' || (ebcdicsw && !ebcdicsafe[*cp & 0xff]))
379                                         goto three_print;
380                                 putc(*cp, out);
381                                 n++;
382                                 break;
383
384                         case '=':
385 three_print:
386                                 fprintf(out, "=%02X", *cp & 0xff);
387                                 n += 3;
388                                 break;
389                         }
390                 }
391
392                 if (c == '\n') {
393                         if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
394                                 fputs("=\n", out);
395
396                         putc('\n', out);
397                 } else {
398                         fputs("=\n", out);
399                 }
400         }
401
402         (*ct->c_ceclosefnx) (ct);
403         return OK;
404 }
405
406
407 /*
408 ** Output a content using base64
409 */
410
411 static int
412 writeBase64(CT ct, FILE *out)
413 {
414         int fd, result;
415         char *file;
416         CE ce = ct->c_cefile;
417
418         file = NULL;
419         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
420                 return NOTOK;
421
422         result = writeBase64aux(ce->ce_fp, out);
423         (*ct->c_ceclosefnx) (ct);
424         return result;
425 }
426
427
428 int
429 writeBase64aux(FILE *in, FILE *out)
430 {
431         unsigned int cc, n;
432         char inbuf[3];
433
434         n = BPERLIN;
435         while ((cc = fread(inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
436                 unsigned long bits;
437                 char *bp;
438                 char outbuf[4];
439
440                 if (cc < sizeof(inbuf)) {
441                         inbuf[2] = 0;
442                         if (cc < sizeof(inbuf) - 1)
443                                 inbuf[1] = 0;
444                 }
445                 bits = (inbuf[0] & 0xff) << 16;
446                 bits |= (inbuf[1] & 0xff) << 8;
447                 bits |= inbuf[2] & 0xff;
448
449                 for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
450                         *--bp = nib2b64[bits & 0x3f];
451                 if (cc < sizeof(inbuf)) {
452                         outbuf[3] = '=';
453                         if (cc < sizeof inbuf - 1)
454                                 outbuf[2] = '=';
455                 }
456
457                 fwrite(outbuf, sizeof(*outbuf), sizeof(outbuf), out);
458
459                 if (cc < sizeof(inbuf)) {
460                         putc('\n', out);
461                         return OK;
462                 }
463
464                 if (--n <= 0) {
465                         n = BPERLIN;
466                         putc('\n', out);
467                 }
468         }
469         if (n != BPERLIN)
470                 putc('\n', out);
471
472         return OK;
473 }