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