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