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