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