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