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