Removed the space between function names and the opening parenthesis.
[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 <h/md5.h>
14 #include <errno.h>
15 #include <signal.h>
16 #include <h/mts.h>
17 #include <h/tws.h>
18 #include <h/mime.h>
19 #include <h/mhparse.h>
20
21 #ifdef HAVE_SYS_WAIT_H
22 # include <sys/wait.h>
23 #endif
24
25
26 extern int ebcdicsw;
27
28 static char ebcdicsafe[0x100] = {
29         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30         0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
31         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33         0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
34         0x01, 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         0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
38         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
39         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
40         0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
41         0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
42         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
43         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
44         0x01, 0x01, 0x01, 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         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
61 };
62
63 static char nib2b64[0x40+1] =
64         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
65
66 /*
67 ** prototypes
68 */
69 int output_message(CT, char *);
70 int output_message_fp(CT, FILE *, char *);
71 int writeBase64aux(FILE *, FILE *);
72
73 /*
74 ** static prototypes
75 */
76 static int output_content(CT, FILE *);
77 static void output_headers(CT, FILE *);
78 static int writeExternalBody(CT, FILE *);
79 static int write8Bit(CT, FILE *);
80 static int writeQuoted(CT, FILE *);
81 static int writeBase64(CT, FILE *);
82
83
84 /*
85 ** Main routine to output a MIME message contained
86 ** in a Content structure, to a file.  Any necessary
87 ** transfer encoding is added.
88 */
89
90 int
91 output_message_fp(CT ct, FILE *fp, char *file)
92 {
93         if (output_content(ct, fp) == NOTOK)
94                 return NOTOK;
95
96         if (fflush(fp)) {
97                 advise((file?file:"<FILE*>"), "error writing to");
98                 return NOTOK;
99         }
100         return OK;
101 }
102
103 int
104 output_message(CT ct, char *file)
105 {
106         FILE *fp;
107         int status;
108
109         if ((fp = fopen(file, "w")) == NULL) {
110                 advise(file, "unable to open for writing");
111                 return NOTOK;
112         }
113         status = output_message_fp(ct, fp, file);
114         fclose(fp);
115         return status;
116 }
117
118
119 /*
120 ** Output a Content structure to a file.
121 */
122
123 static int
124 output_content(CT ct, FILE *out)
125 {
126         int result = 0;
127         CI ci = &ct->c_ctinfo;
128
129         /*
130         ** Output all header fields for this content
131         */
132         output_headers(ct, out);
133
134         /*
135         ** If this is the internal content structure for a
136         ** "message/external", then we are done with the
137         ** headers (since it has no body).
138         */
139         if (ct->c_ctexbody)
140                 return OK;
141
142         /*
143         ** Now output the content bodies.
144         */
145         switch (ct->c_type) {
146         case CT_MULTIPART:
147         {
148                 struct multipart *m;
149                 struct part *part;
150
151                 if (ct->c_rfc934)
152                         putc('\n', out);
153
154                 m = (struct multipart *) ct->c_ctparams;
155                 for (part = m->mp_parts; part; part = part->mp_next) {
156                         CT p = part->mp_part;
157
158                         fprintf(out, "\n--%s\n", ci->ci_values[0]);
159                         if (output_content(p, out) == NOTOK)
160                                 return NOTOK;
161                 }
162                 fprintf(out, "\n--%s--\n", ci->ci_values[0]);
163         }
164         break;
165
166         case CT_MESSAGE:
167                 putc('\n', out);
168                 if (ct->c_subtype == MESSAGE_EXTERNAL) {
169                         struct exbody *e;
170
171                         e = (struct exbody *) ct->c_ctparams;
172                         if (output_content(e->eb_content, out) == NOTOK)
173                                 return NOTOK;
174
175                         /* output phantom body for access-type "mail-server" */
176                         if (e->eb_body)
177                                 writeExternalBody(ct, out);
178                 } else {
179                         result = write8Bit(ct, out);
180                 }
181                 break;
182
183         /*
184         ** Handle discrete types (text/application/audio/image/video)
185         */
186         default:
187                 switch (ct->c_encoding) {
188                 case CE_7BIT:
189                         putc('\n', out);
190                         result = write8Bit(ct, out);
191                         break;
192
193                 case CE_8BIT:
194                         putc('\n', out);
195                         result = write8Bit(ct, out);
196                         break;
197
198                 case CE_QUOTED:
199                         putc('\n', out);
200                         result = writeQuoted(ct, out);
201                         break;
202
203                 case CE_BASE64:
204                         putc('\n', out);
205                         result = writeBase64(ct, out);
206                         break;
207
208                 case CE_BINARY:
209                         advise(NULL, "can't handle binary transfer encoding in content");
210                         result = NOTOK;
211                         break;
212
213                 default:
214                         advise(NULL, "unknown transfer encoding in content");
215                         result = NOTOK;
216                         break;
217                 }
218                 break;
219         }
220
221         return result;
222 }
223
224
225 /*
226 ** Output all the header fields for a content
227 */
228
229 static void
230 output_headers(CT ct, FILE *out)
231 {
232         HF hp;
233
234         hp = ct->c_first_hf;
235         while (hp) {
236                 fprintf(out, "%s:%s", hp->name, hp->value);
237                 hp = hp->next;
238         }
239 }
240
241
242 /*
243 ** Write the phantom body for access-type "mail-server".
244 */
245
246 static int
247 writeExternalBody(CT ct, FILE *out)
248 {
249         char **ap, **ep, *cp;
250         struct exbody *e = (struct exbody *) ct->c_ctparams;
251
252         putc('\n', out);
253         for (cp = e->eb_body; *cp; cp++) {
254                 CT ct2 = e->eb_content;
255                 CI ci2 = &ct2->c_ctinfo;
256
257                 if (*cp == '\\') {
258                         switch (*++cp) {
259                         case 'I':
260                                 if (ct2->c_id) {
261                                         char *dp = trimcpy(ct2->c_id);
262
263                                         fputs(dp, out);
264                                         free(dp);
265                                 }
266                                 continue;
267
268                         case 'N':
269                                 for (ap = ci2->ci_attrs, ep = ci2->ci_values;
270                                                 *ap; ap++, ep++)
271                                         if (!mh_strcasecmp(*ap, "name")) {
272                                                 fprintf(out, "%s", *ep);
273                                                 break;
274                                         }
275                                 continue;
276
277                         case 'T':
278                                 fprintf(out, "%s/%s", ci2->ci_type,
279                                                 ci2->ci_subtype);
280                                 for (ap = ci2->ci_attrs, ep = ci2->ci_values;
281                                                 *ap; ap++, ep++)
282                                         fprintf(out, "; %s=\"%s\"", *ap, *ep);
283                                 continue;
284
285                         case 'n':
286                                 putc('\n', out);
287                                 continue;
288
289                         case 't':
290                                 putc('\t', out);
291                                 continue;
292
293                         case '\0':
294                                 cp--;
295                                 break;
296
297                         case '\\':
298                         case '"':
299                                 break;
300
301                         default:
302                                 putc('\\', out);
303                                 break;
304                         }
305                 }
306                 putc(*cp, out);
307         }
308         putc('\n', out);
309
310         return OK;
311 }
312
313
314 /*
315 ** Output a content without any transfer encoding
316 */
317
318 static int
319 write8Bit(CT ct, FILE *out)
320 {
321         int fd;
322         char c, *file, buffer[BUFSIZ];
323         CE ce = ct->c_cefile;
324
325         file = NULL;
326         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
327                 return NOTOK;
328
329         c = '\n';
330         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
331                 c = buffer[strlen(buffer) - 1];
332                 fputs(buffer, out);
333         }
334         if (c != '\n')
335                 putc('\n', out);
336
337         (*ct->c_ceclosefnx) (ct);
338         return OK;
339 }
340
341
342 /*
343 ** Output a content using quoted-printable
344 */
345
346 static int
347 writeQuoted(CT ct, FILE *out)
348 {
349         int fd;
350         char *cp, *file;
351         char c, buffer[BUFSIZ];
352         CE ce = ct->c_cefile;
353
354         file = NULL;
355         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
356                 return NOTOK;
357
358         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
359                 int n;
360
361                 cp = buffer + strlen(buffer) - 1;
362                 if ((c = *cp) == '\n')
363                         *cp = '\0';
364
365                 if (strncmp(cp = buffer, "From ", sizeof("From ") - 1) == 0) {
366                         fprintf(out, "=%02X", *cp++ & 0xff);
367                         n = 3;
368                 } else {
369                         n = 0;
370                 }
371                 for (; *cp; cp++) {
372                         if (n > CPERLIN - 3) {
373                                 fputs("=\n", out);
374                                 n = 0;
375                         }
376
377                         switch (*cp) {
378                                 case ' ':
379                                 case '\t':
380                                         putc(*cp, out);
381                                         n++;
382                                         break;
383
384                                 default:
385                                         if (*cp < '!' || *cp > '~' || (ebcdicsw && !ebcdicsafe[*cp & 0xff]))
386                                                 goto three_print;
387                                         putc(*cp, out);
388                                         n++;
389                                         break;
390
391                                 case '=':
392 three_print:
393                                         fprintf(out, "=%02X", *cp & 0xff);
394                                         n += 3;
395                                         break;
396                         }
397                 }
398
399                 if (c == '\n') {
400                         if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
401                                 fputs("=\n", out);
402
403                         putc('\n', out);
404                 } else {
405                         fputs("=\n", out);
406                 }
407         }
408
409         (*ct->c_ceclosefnx) (ct);
410         return OK;
411 }
412
413
414 /*
415 ** Output a content using base64
416 */
417
418 static int
419 writeBase64(CT ct, FILE *out)
420 {
421         int fd, result;
422         char *file;
423         CE ce = ct->c_cefile;
424
425         file = NULL;
426         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
427                 return NOTOK;
428
429         result = writeBase64aux(ce->ce_fp, out);
430         (*ct->c_ceclosefnx) (ct);
431         return result;
432 }
433
434
435 int
436 writeBase64aux(FILE *in, FILE *out)
437 {
438         int cc, n;
439         char inbuf[3];
440
441         n = BPERLIN;
442         while ((cc = fread(inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
443                 unsigned long bits;
444                 char *bp;
445                 char outbuf[4];
446
447                 if (cc < sizeof(inbuf)) {
448                         inbuf[2] = 0;
449                         if (cc < sizeof(inbuf) - 1)
450                                 inbuf[1] = 0;
451                 }
452                 bits = (inbuf[0] & 0xff) << 16;
453                 bits |= (inbuf[1] & 0xff) << 8;
454                 bits |= inbuf[2] & 0xff;
455
456                 for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
457                         *--bp = nib2b64[bits & 0x3f];
458                 if (cc < sizeof(inbuf)) {
459                         outbuf[3] = '=';
460                         if (cc < sizeof inbuf - 1)
461                                 outbuf[2] = '=';
462                 }
463
464                 fwrite(outbuf, sizeof(*outbuf), sizeof(outbuf), out);
465
466                 if (cc < sizeof(inbuf)) {
467                         putc('\n', out);
468                         return OK;
469                 }
470
471                 if (--n <= 0) {
472                         n = BPERLIN;
473                         putc('\n', out);
474                 }
475         }
476         if (n != BPERLIN)
477                 putc('\n', out);
478
479         return OK;
480 }