]> diplodocus.org Git - nmh/blob - uip/mhoutsbr.c
Remove unused NCWD and NPWD #defines.
[nmh] / uip / mhoutsbr.c
1
2 /*
3 * mhoutsbr.c -- routines to output MIME messages
4 * -- given a Content structure
5 *
6 * This code is Copyright (c) 2002, by the authors of nmh. See the
7 * COPYRIGHT file in the root directory of the nmh distribution for
8 * complete copyright information.
9 */
10
11 #include <h/mh.h>
12 #include <fcntl.h>
13 #include <h/utils.h>
14 #include <h/md5.h>
15 #include <h/mts.h>
16 #include <h/tws.h>
17 #include <h/mime.h>
18 #include <h/mhparse.h>
19
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 writeExternalBody (CT, FILE *);
33 static int write8Bit (CT, FILE *);
34 static int writeQuoted (CT, FILE *);
35 static int writeBase64ct (CT, 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 (! strcmp (file, "-")) {
64 fp = stdout;
65 } else 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 if (strcmp (file, "-")) 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 char *boundary = "", *cp;
85
86 if ((cp = get_param(ci->ci_first_pm, "boundary", '-', 0)))
87 boundary = cp;
88
89 /*
90 * Output all header fields for this content
91 */
92 output_headers (ct, out);
93
94 /*
95 * If this is the internal content structure for a
96 * "message/external", then we are done with the
97 * headers (since it has no body).
98 */
99 if (ct->c_ctexbody) {
100 if (*boundary != '\0')
101 free(boundary);
102 return OK;
103 }
104
105 /*
106 * Now output the content bodies.
107 */
108 switch (ct->c_type) {
109 case CT_MULTIPART:
110 {
111 struct multipart *m;
112 struct part *part;
113
114 if (ct->c_rfc934)
115 putc ('\n', out);
116
117 m = (struct multipart *) ct->c_ctparams;
118
119 if (m->mp_content_before) {
120 fprintf (out, "%s", m->mp_content_before);
121 }
122
123 for (part = m->mp_parts; part; part = part->mp_next) {
124 CT p = part->mp_part;
125
126 fprintf (out, "\n--%s\n", boundary);
127 if (output_content (p, out) == NOTOK) {
128 if (*boundary != '\0')
129 free(boundary);
130 return NOTOK;
131 }
132 }
133 fprintf (out, "\n--%s--\n", boundary);
134
135 if (m->mp_content_after) {
136 fprintf (out, "%s", m->mp_content_after);
137 }
138 }
139 break;
140
141 case CT_MESSAGE:
142 putc ('\n', out);
143 if (ct->c_subtype == MESSAGE_EXTERNAL) {
144 struct exbody *e;
145
146 e = (struct exbody *) ct->c_ctparams;
147 if (output_content (e->eb_content, out) == NOTOK)
148 return NOTOK;
149
150 /* output phantom body for access-type "mail-server" */
151 if (e->eb_body)
152 writeExternalBody (ct, out);
153 } else {
154 result = write8Bit (ct, out);
155 }
156 break;
157
158 /*
159 * Handle discrete types (text/application/audio/image/video)
160 */
161 default:
162 switch (ct->c_encoding) {
163 case CE_7BIT:
164 /* Special case: if this is a non-MIME message with no
165 body, don't emit the newline that would appear between
166 the headers and body. In that case, the call to
167 write8Bit() shouldn't be needed, but is harmless. */
168 if (ct->c_ctinfo.ci_first_pm != NULL || ct->c_begin == 0 ||
169 ct->c_begin != ct->c_end) {
170 putc ('\n', out);
171 }
172 result = write8Bit (ct, out);
173 break;
174
175 case CE_8BIT:
176 putc ('\n', out);
177 result = write8Bit (ct, out);
178 break;
179
180 case CE_QUOTED:
181 putc ('\n', out);
182 result = writeQuoted (ct, out);
183 break;
184
185 case CE_BASE64:
186 putc ('\n', out);
187 result = writeBase64ct (ct, out);
188 break;
189
190 case CE_BINARY:
191 if (ct->c_type == CT_TEXT) {
192 /* So that mhfixmsg can decode to binary text. */
193 putc ('\n', out);
194 result = write8Bit (ct, out);
195 } else {
196 advise (NULL, "can't handle binary transfer encoding in content");
197 result = NOTOK;
198 }
199 break;
200
201 default:
202 advise (NULL, "unknown transfer encoding in content");
203 result = NOTOK;
204 break;
205 }
206 break;
207 }
208
209 if (*boundary != '\0')
210 free(boundary);
211
212 return result;
213 }
214
215
216 /*
217 * Output all the header fields for a content
218 */
219
220 static void
221 output_headers (CT ct, FILE *out)
222 {
223 HF hp;
224
225 hp = ct->c_first_hf;
226 while (hp) {
227 fprintf (out, "%s:%s", hp->name, hp->value);
228 hp = hp->next;
229 }
230 }
231
232
233 /*
234 * Write the phantom body for access-type "mail-server".
235 */
236
237 static int
238 writeExternalBody (CT ct, FILE *out)
239 {
240 char *cp, *dp;
241 struct exbody *e = (struct exbody *) ct->c_ctparams;
242
243 putc ('\n', out);
244 for (cp = e->eb_body; *cp; cp++) {
245 CT ct2 = e->eb_content;
246 CI ci2 = &ct2->c_ctinfo;
247
248 if (*cp == '\\') {
249 switch (*++cp) {
250 case 'I':
251 if (ct2->c_id) {
252 dp = trimcpy (ct2->c_id);
253
254 fputs (dp, out);
255 free (dp);
256 }
257 continue;
258
259 case 'N':
260 dp = get_param(ci2->ci_first_pm, "name", '_', 0);
261 if (dp) {
262 fputs (dp, out);
263 free (dp);
264 }
265 continue;
266
267 case 'T':
268 fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
269 dp = output_params(strlen(ci2->ci_type) +
270 strlen(ci2->ci_subtype) + 1,
271 ci2->ci_first_pm, NULL, 0);
272 if (dp) {
273 fputs (dp, out);
274 free (dp);
275 }
276 continue;
277
278 case 'n':
279 putc ('\n', out);
280 continue;
281
282 case 't':
283 putc ('\t', out);
284 continue;
285
286 case '\0':
287 cp--;
288 break;
289
290 case '\\':
291 case '"':
292 break;
293
294 default:
295 putc ('\\', out);
296 break;
297 }
298 }
299 putc (*cp, out);
300 }
301 putc ('\n', out);
302
303 return OK;
304 }
305
306
307 /*
308 * Output a content without any transfer encoding
309 */
310
311 static int
312 write8Bit (CT ct, FILE *out)
313 {
314 int fd;
315 size_t inbytes;
316 char c, *file, buffer[BUFSIZ];
317 CE ce = &ct->c_cefile;
318
319 file = NULL;
320 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
321 return NOTOK;
322
323 c = '\n';
324 while ((inbytes = fread (buffer, 1, sizeof buffer, ce->ce_fp)) > 0) {
325 c = buffer[inbytes - 1];
326 if (fwrite (buffer, 1, inbytes, out) < inbytes) {
327 advise ("write8Bit", "fwrite");
328 }
329 }
330 if (c != '\n')
331 putc ('\n', out);
332
333 (*ct->c_ceclosefnx) (ct);
334 return OK;
335 }
336
337
338 /*
339 * Output a content using quoted-printable
340 */
341
342 static int
343 writeQuoted (CT ct, FILE *out)
344 {
345 int fd;
346 char *cp, *file;
347 char c = '\0';
348 CE ce = &ct->c_cefile;
349 int n = 0;
350 char *bufp = NULL;
351 size_t buflen;
352 ssize_t gotlen;
353
354 file = NULL;
355 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
356 return NOTOK;
357
358 while ((gotlen = getline(&bufp, &buflen, ce->ce_fp)) != -1) {
359
360 cp = bufp + gotlen - 1;
361 if ((c = *cp) == '\n')
362 gotlen--;
363
364 /*
365 * if the line starts with "From ", encode the 'F' so it
366 * doesn't falsely match an mbox delimiter.
367 */
368 cp = bufp;
369 if (gotlen >= 5 && HasPrefix(cp, "From ")) {
370 fprintf (out, "=%02X", 'F');
371 cp++;
372 n += 3;
373 }
374
375 for (; cp < bufp + gotlen; cp++) {
376 if (n > CPERLIN - 3) {
377 fputs ("=\n", out);
378 n = 0;
379 }
380
381 switch (*cp) {
382 case ' ':
383 case '\t':
384 putc (*cp, out);
385 n++;
386 break;
387
388 default:
389 if (*cp < '!' || *cp > '~')
390 goto three_print;
391 putc (*cp, out);
392 n++;
393 break;
394
395 case '=':
396 three_print:
397 fprintf (out, "=%02X", *cp & 0xff);
398 n += 3;
399 break;
400 }
401 }
402
403 if (c == '\n') {
404 if (cp > bufp && (*--cp == ' ' || *cp == '\t'))
405 fputs ("=\n", out);
406
407 putc ('\n', out);
408 n = 0;
409 }
410 }
411
412 if (c != '\n')
413 putc ('\n', out);
414
415 (*ct->c_ceclosefnx) (ct);
416 free (bufp);
417 return OK;
418 }
419
420
421 /*
422 * Output a content using base64
423 */
424
425 static int
426 writeBase64ct (CT ct, FILE *out)
427 {
428 int fd, result;
429 char *file;
430 CE ce = &ct->c_cefile;
431
432 file = NULL;
433 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
434 return NOTOK;
435
436 result = writeBase64aux (ce->ce_fp, out,
437 ct->c_type == CT_TEXT && ct->c_ctparams
438 ? ((struct text *) ct->c_ctparams)->lf_line_endings == 0
439 : 0);
440 (*ct->c_ceclosefnx) (ct);
441 return result;
442 }