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