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