5d267ef70777b25076c02eeacca27dbfe2abafec
[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 write8Bit(CT, FILE *);
72 static int writeQuoted(CT, FILE *);
73 static int writeBase64(CT, FILE *);
74
75
76 /*
77 ** Main routine to output a MIME message contained
78 ** in a Content structure, to a file.  Any necessary
79 ** transfer encoding is added.
80 */
81
82 int
83 output_message_fp(CT ct, FILE *fp, char *file)
84 {
85         if (output_content(ct, fp) == NOTOK)
86                 return NOTOK;
87
88         if (fflush(fp)) {
89                 advise((file?file:"<FILE*>"), "error writing to");
90                 return NOTOK;
91         }
92         return OK;
93 }
94
95 int
96 output_message(CT ct, char *file)
97 {
98         FILE *fp;
99         int status;
100
101         if ((fp = fopen(file, "w")) == NULL) {
102                 advise(file, "unable to open for writing");
103                 return NOTOK;
104         }
105         status = output_message_fp(ct, fp, file);
106         fclose(fp);
107         return status;
108 }
109
110
111 /*
112 ** Output a Content structure to a file.
113 */
114
115 static int
116 output_content(CT ct, FILE *out)
117 {
118         int result = 0;
119         CI ci = &ct->c_ctinfo;
120
121         /*
122         ** Output all header fields for this content
123         */
124         output_headers(ct, out);
125
126         /*
127         ** Now output the content bodies.
128         */
129         switch (ct->c_type) {
130         case CT_MULTIPART:
131         {
132                 struct multipart *m;
133                 struct part *part;
134
135                 if (ct->c_rfc934)
136                         putc('\n', out);
137
138                 m = (struct multipart *) ct->c_ctparams;
139                 for (part = m->mp_parts; part; part = part->mp_next) {
140                         CT p = part->mp_part;
141
142                         fprintf(out, "\n--%s\n", ci->ci_values[0]);
143                         if (output_content(p, out) == NOTOK)
144                                 return NOTOK;
145                 }
146                 fprintf(out, "\n--%s--\n", ci->ci_values[0]);
147         }
148         break;
149
150         case CT_MESSAGE:
151                 putc('\n', out);
152                 result = write8Bit(ct, out);
153                 break;
154
155         /*
156         ** Handle discrete types (text/application/audio/image/video)
157         */
158         default:
159                 switch (ct->c_encoding) {
160                 case CE_7BIT:
161                         putc('\n', out);
162                         result = write8Bit(ct, out);
163                         break;
164
165                 case CE_8BIT:
166                         putc('\n', out);
167                         result = write8Bit(ct, out);
168                         break;
169
170                 case CE_QUOTED:
171                         putc('\n', out);
172                         result = writeQuoted(ct, out);
173                         break;
174
175                 case CE_BASE64:
176                         putc('\n', out);
177                         result = writeBase64(ct, out);
178                         break;
179
180                 case CE_BINARY:
181                         advise(NULL, "can't handle binary transfer encoding in content");
182                         result = NOTOK;
183                         break;
184
185                 default:
186                         advise(NULL, "unknown transfer encoding in content");
187                         result = NOTOK;
188                         break;
189                 }
190                 break;
191         }
192
193         return result;
194 }
195
196
197 /*
198 ** Output all the header fields for a content
199 */
200
201 static void
202 output_headers(CT ct, FILE *out)
203 {
204         HF hp;
205
206         hp = ct->c_first_hf;
207         while (hp) {
208                 fprintf(out, "%s:%s", hp->name, hp->value);
209                 hp = hp->next;
210         }
211 }
212
213
214 /*
215 ** Output a content without any transfer encoding
216 */
217
218 static int
219 write8Bit(CT ct, FILE *out)
220 {
221         int fd;
222         char c, *file, buffer[BUFSIZ];
223         CE ce = ct->c_cefile;
224
225         file = NULL;
226         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
227                 return NOTOK;
228
229         c = '\n';
230         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
231                 c = buffer[strlen(buffer) - 1];
232                 fputs(buffer, out);
233         }
234         if (c != '\n')
235                 putc('\n', out);
236
237         (*ct->c_ceclosefnx) (ct);
238         return OK;
239 }
240
241
242 /*
243 ** Output a content using quoted-printable
244 */
245
246 static int
247 writeQuoted(CT ct, FILE *out)
248 {
249         int fd;
250         char *cp, *file;
251         char c, buffer[BUFSIZ];
252         CE ce = ct->c_cefile;
253
254         file = NULL;
255         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
256                 return NOTOK;
257
258         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
259                 int n;
260
261                 cp = buffer + strlen(buffer) - 1;
262                 if ((c = *cp) == '\n')
263                         *cp = '\0';
264
265                 if (strncmp(cp = buffer, "From ", sizeof("From ") - 1) == 0) {
266                         fprintf(out, "=%02X", *cp++ & 0xff);
267                         n = 3;
268                 } else {
269                         n = 0;
270                 }
271                 for (; *cp; cp++) {
272                         if (n > CPERLIN - 3) {
273                                 fputs("=\n", out);
274                                 n = 0;
275                         }
276
277                         switch (*cp) {
278                         case ' ':
279                         case '\t':
280                                 putc(*cp, out);
281                                 n++;
282                                 break;
283
284                         default:
285                                 if (*cp < '!' || *cp > '~' || (ebcdicsw && !ebcdicsafe[*cp & 0xff]))
286                                         goto three_print;
287                                 putc(*cp, out);
288                                 n++;
289                                 break;
290
291                         case '=':
292 three_print:
293                                 fprintf(out, "=%02X", *cp & 0xff);
294                                 n += 3;
295                                 break;
296                         }
297                 }
298
299                 if (c == '\n') {
300                         if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
301                                 fputs("=\n", out);
302
303                         putc('\n', out);
304                 } else {
305                         fputs("=\n", out);
306                 }
307         }
308
309         (*ct->c_ceclosefnx) (ct);
310         return OK;
311 }
312
313
314 /*
315 ** Output a content using base64
316 */
317
318 static int
319 writeBase64(CT ct, FILE *out)
320 {
321         int fd, result;
322         char *file;
323         CE ce = ct->c_cefile;
324
325         file = NULL;
326         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
327                 return NOTOK;
328
329         result = writeBase64aux(ce->ce_fp, out);
330         (*ct->c_ceclosefnx) (ct);
331         return result;
332 }
333
334
335 int
336 writeBase64aux(FILE *in, FILE *out)
337 {
338         unsigned int cc, n;
339         char inbuf[3];
340
341         n = BPERLIN;
342         while ((cc = fread(inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
343                 unsigned long bits;
344                 char *bp;
345                 char outbuf[4];
346
347                 if (cc < sizeof(inbuf)) {
348                         inbuf[2] = 0;
349                         if (cc < sizeof(inbuf) - 1)
350                                 inbuf[1] = 0;
351                 }
352                 bits = (inbuf[0] & 0xff) << 16;
353                 bits |= (inbuf[1] & 0xff) << 8;
354                 bits |= inbuf[2] & 0xff;
355
356                 for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
357                         *--bp = nib2b64[bits & 0x3f];
358                 if (cc < sizeof(inbuf)) {
359                         outbuf[3] = '=';
360                         if (cc < sizeof inbuf - 1)
361                                 outbuf[2] = '=';
362                 }
363
364                 fwrite(outbuf, sizeof(*outbuf), sizeof(outbuf), out);
365
366                 if (cc < sizeof(inbuf)) {
367                         putc('\n', out);
368                         return OK;
369                 }
370
371                 if (--n <= 0) {
372                         n = BPERLIN;
373                         putc('\n', out);
374                 }
375         }
376         if (n != BPERLIN)
377                 putc('\n', out);
378
379         return OK;
380 }