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