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