mhoutsbr.c: writeBase64aux() needs not to be exported.
[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
29 /*
30 ** static prototypes
31 */
32 static int output_content(CT, FILE *);
33 static void output_headers(CT, FILE *);
34 static int write8Bit(CT, FILE *);
35 static int writeQuoted(CT, FILE *);
36 static int writeBase64(CT, FILE *);
37 static int writeBase64aux(FILE *, 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                 m = (struct multipart *) ct->c_ctparams;
100                 for (part = m->mp_parts; part; part = part->mp_next) {
101                         CT p = part->mp_part;
102
103                         fprintf(out, "\n--%s\n", ci->ci_values[0]);
104                         if (output_content(p, out) == NOTOK)
105                                 return NOTOK;
106                 }
107                 fprintf(out, "\n--%s--\n", ci->ci_values[0]);
108         }
109         break;
110
111         case CT_MESSAGE:
112                 putc('\n', out);
113                 result = write8Bit(ct, out);
114                 break;
115
116         /*
117         ** Handle discrete types (text/application/audio/image/video)
118         */
119         default:
120                 switch (ct->c_encoding) {
121                 case CE_7BIT:
122                         putc('\n', out);
123                         result = write8Bit(ct, out);
124                         break;
125
126                 case CE_8BIT:
127                         putc('\n', out);
128                         result = write8Bit(ct, out);
129                         break;
130
131                 case CE_QUOTED:
132                         putc('\n', out);
133                         result = writeQuoted(ct, out);
134                         break;
135
136                 case CE_BASE64:
137                         putc('\n', out);
138                         result = writeBase64(ct, out);
139                         break;
140
141                 case CE_BINARY:
142                         advise(NULL, "can't handle binary transfer encoding in content");
143                         result = NOTOK;
144                         break;
145
146                 default:
147                         advise(NULL, "unknown transfer encoding in content");
148                         result = NOTOK;
149                         break;
150                 }
151                 break;
152         }
153
154         return result;
155 }
156
157
158 /*
159 ** Output all the header fields for a content
160 */
161
162 static void
163 output_headers(CT ct, FILE *out)
164 {
165         HF hp;
166
167         hp = ct->c_first_hf;
168         while (hp) {
169                 fprintf(out, "%s:%s", hp->name, hp->value);
170                 hp = hp->next;
171         }
172 }
173
174
175 /*
176 ** Output a content without any transfer encoding
177 */
178
179 static int
180 write8Bit(CT ct, FILE *out)
181 {
182         int fd;
183         char c, *file, buffer[BUFSIZ];
184         CE ce = ct->c_cefile;
185
186         file = NULL;
187         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
188                 return NOTOK;
189
190         c = '\n';
191         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
192                 c = buffer[strlen(buffer) - 1];
193                 fputs(buffer, out);
194         }
195         if (c != '\n')
196                 putc('\n', out);
197
198         (*ct->c_ceclosefnx) (ct);
199         return OK;
200 }
201
202
203 /*
204 ** Output a content using quoted-printable
205 */
206
207 static int
208 writeQuoted(CT ct, FILE *out)
209 {
210         int fd;
211         char *cp, *file;
212         char c, buffer[BUFSIZ];
213         CE ce = ct->c_cefile;
214
215         file = NULL;
216         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
217                 return NOTOK;
218
219         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
220                 int n;
221
222                 cp = buffer + strlen(buffer) - 1;
223                 if ((c = *cp) == '\n')
224                         *cp = '\0';
225
226                 if (strncmp(cp = buffer, "From ", sizeof("From ") - 1) == 0) {
227                         fprintf(out, "=%02X", *cp++ & 0xff);
228                         n = 3;
229                 } else {
230                         n = 0;
231                 }
232                 for (; *cp; cp++) {
233                         if (n > CPERLIN - 3) {
234                                 fputs("=\n", out);
235                                 n = 0;
236                         }
237
238                         switch (*cp) {
239                         case ' ':
240                         case '\t':
241                                 putc(*cp, out);
242                                 n++;
243                                 break;
244
245                         default:
246                                 if (*cp < '!' || *cp > '~')
247                                         goto three_print;
248                                 putc(*cp, out);
249                                 n++;
250                                 break;
251
252                         case '=':
253 three_print:
254                                 fprintf(out, "=%02X", *cp & 0xff);
255                                 n += 3;
256                                 break;
257                         }
258                 }
259
260                 if (c == '\n') {
261                         if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
262                                 fputs("=\n", out);
263
264                         putc('\n', out);
265                 } else {
266                         fputs("=\n", out);
267                 }
268         }
269
270         (*ct->c_ceclosefnx) (ct);
271         return OK;
272 }
273
274
275 /*
276 ** Output a content using base64
277 */
278
279 static int
280 writeBase64(CT ct, FILE *out)
281 {
282         int fd, result;
283         char *file;
284         CE ce = ct->c_cefile;
285
286         file = NULL;
287         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
288                 return NOTOK;
289
290         result = writeBase64aux(ce->ce_fp, out);
291         (*ct->c_ceclosefnx) (ct);
292         return result;
293 }
294
295
296 static int
297 writeBase64aux(FILE *in, FILE *out)
298 {
299         unsigned int cc, n;
300         char inbuf[3];
301
302         n = BPERLIN;
303         while ((cc = fread(inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
304                 unsigned long bits;
305                 char *bp;
306                 char outbuf[4];
307
308                 if (cc < sizeof(inbuf)) {
309                         inbuf[2] = 0;
310                         if (cc < sizeof(inbuf) - 1)
311                                 inbuf[1] = 0;
312                 }
313                 bits = (inbuf[0] & 0xff) << 16;
314                 bits |= (inbuf[1] & 0xff) << 8;
315                 bits |= inbuf[2] & 0xff;
316
317                 for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
318                         *--bp = nib2b64[bits & 0x3f];
319                 if (cc < sizeof(inbuf)) {
320                         outbuf[3] = '=';
321                         if (cc < sizeof inbuf - 1)
322                                 outbuf[2] = '=';
323                 }
324
325                 fwrite(outbuf, sizeof(*outbuf), sizeof(outbuf), out);
326
327                 if (cc < sizeof(inbuf)) {
328                         putc('\n', out);
329                         return OK;
330                 }
331
332                 if (--n <= 0) {
333                         n = BPERLIN;
334                         putc('\n', out);
335                 }
336         }
337         if (n != BPERLIN)
338                 putc('\n', out);
339
340         return OK;
341 }