Removed unnecessary #include of h/signals.h.
[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
165         hp = ct->c_first_hf;
166         while (hp) {
167                 fprintf(out, "%s:%s", hp->name, hp->value);
168                 hp = hp->next;
169         }
170 }
171
172
173 /*
174 ** Output a content without any transfer encoding
175 */
176
177 static int
178 write8Bit(CT ct, FILE *out)
179 {
180         int fd;
181         char c, *file, buffer[BUFSIZ];
182         CE ce = ct->c_cefile;
183
184         file = NULL;
185         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
186                 return NOTOK;
187
188         c = '\n';
189         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
190                 c = buffer[strlen(buffer) - 1];
191                 fputs(buffer, out);
192         }
193         if (c != '\n')
194                 putc('\n', out);
195
196         (*ct->c_ceclosefnx) (ct);
197         return OK;
198 }
199
200
201 /*
202 ** Output a content using quoted-printable
203 */
204
205 static int
206 writeQuoted(CT ct, FILE *out)
207 {
208         int fd;
209         char *cp, *file;
210         char c, buffer[BUFSIZ];
211         CE ce = ct->c_cefile;
212
213         file = NULL;
214         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
215                 return NOTOK;
216
217         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
218                 int n;
219
220                 cp = buffer + strlen(buffer) - 1;
221                 if ((c = *cp) == '\n')
222                         *cp = '\0';
223
224                 if (strncmp(cp = buffer, "From ", sizeof("From ") - 1) == 0) {
225                         fprintf(out, "=%02X", *cp++ & 0xff);
226                         n = 3;
227                 } else {
228                         n = 0;
229                 }
230                 for (; *cp; cp++) {
231                         if (n > CPERLIN - 3) {
232                                 fputs("=\n", out);
233                                 n = 0;
234                         }
235
236                         switch (*cp) {
237                         case ' ':
238                         case '\t':
239                                 putc(*cp, out);
240                                 n++;
241                                 break;
242
243                         default:
244                                 if (*cp < '!' || *cp > '~')
245                                         goto three_print;
246                                 putc(*cp, out);
247                                 n++;
248                                 break;
249
250                         case '=':
251 three_print:
252                                 fprintf(out, "=%02X", *cp & 0xff);
253                                 n += 3;
254                                 break;
255                         }
256                 }
257
258                 if (c == '\n') {
259                         if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
260                                 fputs("=\n", out);
261
262                         putc('\n', out);
263                 } else {
264                         fputs("=\n", out);
265                 }
266         }
267
268         (*ct->c_ceclosefnx) (ct);
269         return OK;
270 }
271
272
273 /*
274 ** Output a content using base64
275 */
276
277 static int
278 writeBase64(CT ct, FILE *out)
279 {
280         int fd, result;
281         char *file;
282         CE ce = ct->c_cefile;
283
284         file = NULL;
285         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
286                 return NOTOK;
287
288         result = writeBase64aux(ce->ce_fp, out);
289         (*ct->c_ceclosefnx) (ct);
290         return result;
291 }
292
293
294 static int
295 writeBase64aux(FILE *in, FILE *out)
296 {
297         unsigned int cc, n;
298         char inbuf[3];
299
300         n = BPERLIN;
301         while ((cc = fread(inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
302                 unsigned long bits;
303                 char *bp;
304                 char outbuf[4];
305
306                 if (cc < sizeof(inbuf)) {
307                         inbuf[2] = 0;
308                         if (cc < sizeof(inbuf) - 1)
309                                 inbuf[1] = 0;
310                 }
311                 bits = (inbuf[0] & 0xff) << 16;
312                 bits |= (inbuf[1] & 0xff) << 8;
313                 bits |= inbuf[2] & 0xff;
314
315                 for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
316                         *--bp = nib2b64[bits & 0x3f];
317                 if (cc < sizeof(inbuf)) {
318                         outbuf[3] = '=';
319                         if (cc < sizeof inbuf - 1)
320                                 outbuf[2] = '=';
321                 }
322
323                 fwrite(outbuf, sizeof(*outbuf), sizeof(outbuf), out);
324
325                 if (cc < sizeof(inbuf)) {
326                         putc('\n', out);
327                         return OK;
328                 }
329
330                 if (--n <= 0) {
331                         n = BPERLIN;
332                         putc('\n', out);
333                 }
334         }
335         if (n != BPERLIN)
336                 putc('\n', out);
337
338         return OK;
339 }