]> diplodocus.org Git - nmh/blob - uip/mhoutsbr.c
246d99b49d16d4b8c28af362d0929dfb3330a710
[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 <signal.h>
16 #include <h/mts.h>
17 #include <h/tws.h>
18 #include <h/mime.h>
19 #include <h/mhparse.h>
20
21
22 /*
23 * prototypes
24 */
25 int output_message (CT, char *);
26 int output_message_fp (CT, FILE *, char *);
27
28 /*
29 * static prototypes
30 */
31 static int output_content (CT, FILE *);
32 static void output_headers (CT, FILE *);
33 static int writeExternalBody (CT, FILE *);
34 static int write8Bit (CT, FILE *);
35 static int writeQuoted (CT, FILE *);
36 static int writeBase64ct (CT, FILE *);
37
38
39 /*
40 * Main routine to output a MIME message contained
41 * in a Content structure, to a file. Any necessary
42 * transfer encoding is added.
43 */
44
45 int
46 output_message_fp (CT ct, FILE *fp, char *file)
47 {
48 if (output_content (ct, fp) == NOTOK)
49 return NOTOK;
50
51 if (fflush (fp)) {
52 advise ((file?file:"<FILE*>"), "error writing to");
53 return NOTOK;
54 }
55 return OK;
56 }
57
58 int
59 output_message (CT ct, char *file)
60 {
61 FILE *fp;
62 int status;
63
64 if (! strcmp (file, "-")) {
65 fp = stdout;
66 } else if ((fp = fopen (file, "w")) == NULL) {
67 advise (file, "unable to open for writing");
68 return NOTOK;
69 }
70 status = output_message_fp(ct, fp, file);
71 if (strcmp (file, "-")) fclose(fp);
72 return status;
73 }
74
75
76 /*
77 * Output a Content structure to a file.
78 */
79
80 static int
81 output_content (CT ct, FILE *out)
82 {
83 int result = 0;
84 CI ci = &ct->c_ctinfo;
85 char *boundary = ci->ci_values[0], **ap, **vp;
86
87 for (ap = ci->ci_attrs, vp = ci->ci_values; *ap; ++ap, ++vp) {
88 if (! strcasecmp ("boundary", *ap)) {
89 boundary = *vp;
90 break;
91 }
92 }
93
94 /*
95 * Output all header fields for this content
96 */
97 output_headers (ct, out);
98
99 /*
100 * If this is the internal content structure for a
101 * "message/external", then we are done with the
102 * headers (since it has no body).
103 */
104 if (ct->c_ctexbody)
105 return OK;
106
107 /*
108 * Now output the content bodies.
109 */
110 switch (ct->c_type) {
111 case CT_MULTIPART:
112 {
113 struct multipart *m;
114 struct part *part;
115
116 if (ct->c_rfc934)
117 putc ('\n', out);
118
119 m = (struct multipart *) ct->c_ctparams;
120
121 if (m->mp_content_before) {
122 fprintf (out, "%s", m->mp_content_before);
123 }
124
125 for (part = m->mp_parts; part; part = part->mp_next) {
126 CT p = part->mp_part;
127
128 fprintf (out, "\n--%s\n", boundary);
129 if (output_content (p, out) == NOTOK)
130 return NOTOK;
131 }
132 fprintf (out, "\n--%s--\n", boundary);
133
134 if (m->mp_content_after) {
135 fprintf (out, "%s", m->mp_content_after);
136 }
137 }
138 break;
139
140 case CT_MESSAGE:
141 putc ('\n', out);
142 if (ct->c_subtype == MESSAGE_EXTERNAL) {
143 struct exbody *e;
144
145 e = (struct exbody *) ct->c_ctparams;
146 if (output_content (e->eb_content, out) == NOTOK)
147 return NOTOK;
148
149 /* output phantom body for access-type "mail-server" */
150 if (e->eb_body)
151 writeExternalBody (ct, out);
152 } else {
153 result = write8Bit (ct, out);
154 }
155 break;
156
157 /*
158 * Handle discrete types (text/application/audio/image/video)
159 */
160 default:
161 switch (ct->c_encoding) {
162 case CE_7BIT:
163 /* Special case: if this is a non-MIME message with no
164 body, don't emit the newline that would appear between
165 the headers and body. In that case, the call to
166 write8Bit() shouldn't be needed, but is harmless. */
167 if (ct->c_ctinfo.ci_attrs[0] != NULL ||
168 ct->c_begin != ct->c_end) {
169 putc ('\n', out);
170 }
171 result = write8Bit (ct, out);
172 break;
173
174 case CE_8BIT:
175 putc ('\n', out);
176 result = write8Bit (ct, out);
177 break;
178
179 case CE_QUOTED:
180 putc ('\n', out);
181 result = writeQuoted (ct, out);
182 break;
183
184 case CE_BASE64:
185 putc ('\n', out);
186 result = writeBase64ct (ct, out);
187 break;
188
189 case CE_BINARY:
190 advise (NULL, "can't handle binary transfer encoding in content");
191 result = NOTOK;
192 break;
193
194 default:
195 advise (NULL, "unknown transfer encoding in content");
196 result = NOTOK;
197 break;
198 }
199 break;
200 }
201
202 return result;
203 }
204
205
206 /*
207 * Output all the header fields for a content
208 */
209
210 static void
211 output_headers (CT ct, FILE *out)
212 {
213 HF hp;
214
215 hp = ct->c_first_hf;
216 while (hp) {
217 fprintf (out, "%s:%s", hp->name, hp->value);
218 hp = hp->next;
219 }
220 }
221
222
223 /*
224 * Write the phantom body for access-type "mail-server".
225 */
226
227 static int
228 writeExternalBody (CT ct, FILE *out)
229 {
230 char **ap, **ep, *cp;
231 struct exbody *e = (struct exbody *) ct->c_ctparams;
232
233 putc ('\n', out);
234 for (cp = e->eb_body; *cp; cp++) {
235 CT ct2 = e->eb_content;
236 CI ci2 = &ct2->c_ctinfo;
237
238 if (*cp == '\\') {
239 switch (*++cp) {
240 case 'I':
241 if (ct2->c_id) {
242 char *dp = trimcpy (ct2->c_id);
243
244 fputs (dp, out);
245 free (dp);
246 }
247 continue;
248
249 case 'N':
250 for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
251 if (!strcasecmp (*ap, "name")) {
252 fprintf (out, "%s", *ep);
253 break;
254 }
255 continue;
256
257 case 'T':
258 fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
259 for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
260 fprintf (out, "; %s=\"%s\"", *ap, *ep);
261 continue;
262
263 case 'n':
264 putc ('\n', out);
265 continue;
266
267 case 't':
268 putc ('\t', out);
269 continue;
270
271 case '\0':
272 cp--;
273 break;
274
275 case '\\':
276 case '"':
277 break;
278
279 default:
280 putc ('\\', out);
281 break;
282 }
283 }
284 putc (*cp, out);
285 }
286 putc ('\n', out);
287
288 return OK;
289 }
290
291
292 /*
293 * Output a content without any transfer encoding
294 */
295
296 static int
297 write8Bit (CT ct, FILE *out)
298 {
299 int fd;
300 size_t inbytes;
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 ((inbytes = fread (buffer, 1, sizeof buffer, ce->ce_fp)) > 0) {
310 c = buffer[inbytes - 1];
311 fwrite (buffer, 1, inbytes, 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 }