]> diplodocus.org Git - nmh/blob - uip/mhoutsbr.c
Added context_find_prefix().
[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/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 (CT, char *);
24 int output_message_fp (CT, FILE *, char *);
25
26 /*
27 * static prototypes
28 */
29 static int output_content (CT, FILE *);
30 static void output_headers (CT, FILE *);
31 static int writeExternalBody (CT, FILE *);
32 static int write8Bit (CT, FILE *);
33 static int writeQuoted (CT, FILE *);
34 static int writeBase64ct (CT, FILE *);
35
36
37 /*
38 * Main routine to output a MIME message contained
39 * in a Content structure, to a file. Any necessary
40 * transfer encoding is added.
41 */
42
43 int
44 output_message_fp (CT ct, FILE *fp, char *file)
45 {
46 if (output_content (ct, fp) == NOTOK)
47 return NOTOK;
48
49 if (fflush (fp)) {
50 advise ((file?file:"<FILE*>"), "error writing to");
51 return NOTOK;
52 }
53 return OK;
54 }
55
56 int
57 output_message (CT ct, char *file)
58 {
59 FILE *fp;
60 int status;
61
62 if (! strcmp (file, "-")) {
63 fp = stdout;
64 } else if ((fp = fopen (file, "w")) == NULL) {
65 advise (file, "unable to open for writing");
66 return NOTOK;
67 }
68 status = output_message_fp(ct, fp, file);
69 if (strcmp (file, "-")) fclose(fp);
70 return status;
71 }
72
73
74 /*
75 * Output a Content structure to a file.
76 */
77
78 static int
79 output_content (CT ct, FILE *out)
80 {
81 int result = 0;
82 CI ci = &ct->c_ctinfo;
83 char *boundary = "", *cp;
84
85 if ((cp = get_param(ci->ci_first_pm, "boundary", '-', 0)))
86 boundary = cp;
87
88 /*
89 * Output all header fields for this content
90 */
91 output_headers (ct, out);
92
93 /*
94 * If this is the internal content structure for a
95 * "message/external", then we are done with the
96 * headers (since it has no body).
97 */
98 if (ct->c_ctexbody) {
99 if (boundary && *boundary != '\0')
100 free(boundary);
101 return OK;
102 }
103
104 /*
105 * Now output the content bodies.
106 */
107 switch (ct->c_type) {
108 case CT_MULTIPART:
109 {
110 struct multipart *m;
111 struct part *part;
112
113 if (ct->c_rfc934)
114 putc ('\n', out);
115
116 m = (struct multipart *) ct->c_ctparams;
117
118 if (m->mp_content_before) {
119 fprintf (out, "%s", m->mp_content_before);
120 }
121
122 for (part = m->mp_parts; part; part = part->mp_next) {
123 CT p = part->mp_part;
124
125 fprintf (out, "\n--%s\n", boundary);
126 if (output_content (p, out) == NOTOK) {
127 if (boundary && *boundary != '\0')
128 free(boundary);
129 return NOTOK;
130 }
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_first_pm != NULL || ct->c_begin == 0 ||
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 if (boundary && *boundary != '\0')
203 free(boundary);
204
205 return result;
206 }
207
208
209 /*
210 * Output all the header fields for a content
211 */
212
213 static void
214 output_headers (CT ct, FILE *out)
215 {
216 HF hp;
217
218 hp = ct->c_first_hf;
219 while (hp) {
220 fprintf (out, "%s:%s", hp->name, hp->value);
221 hp = hp->next;
222 }
223 }
224
225
226 /*
227 * Write the phantom body for access-type "mail-server".
228 */
229
230 static int
231 writeExternalBody (CT ct, FILE *out)
232 {
233 char *cp, *dp;
234 struct exbody *e = (struct exbody *) ct->c_ctparams;
235
236 putc ('\n', out);
237 for (cp = e->eb_body; *cp; cp++) {
238 CT ct2 = e->eb_content;
239 CI ci2 = &ct2->c_ctinfo;
240
241 if (*cp == '\\') {
242 switch (*++cp) {
243 case 'I':
244 if (ct2->c_id) {
245 dp = trimcpy (ct2->c_id);
246
247 fputs (dp, out);
248 free (dp);
249 }
250 continue;
251
252 case 'N':
253 dp = get_param(ci2->ci_first_pm, "name", '_', 0);
254 if (dp) {
255 fputs (dp, out);
256 free (dp);
257 }
258 continue;
259
260 case 'T':
261 fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
262 dp = output_params(strlen(ci2->ci_type) +
263 strlen(ci2->ci_subtype) + 1,
264 ci2->ci_first_pm, NULL, 0);
265 if (dp) {
266 fputs (dp, out);
267 free (dp);
268 }
269 continue;
270
271 case 'n':
272 putc ('\n', out);
273 continue;
274
275 case 't':
276 putc ('\t', out);
277 continue;
278
279 case '\0':
280 cp--;
281 break;
282
283 case '\\':
284 case '"':
285 break;
286
287 default:
288 putc ('\\', out);
289 break;
290 }
291 }
292 putc (*cp, out);
293 }
294 putc ('\n', out);
295
296 return OK;
297 }
298
299
300 /*
301 * Output a content without any transfer encoding
302 */
303
304 static int
305 write8Bit (CT ct, FILE *out)
306 {
307 int fd;
308 size_t inbytes;
309 char c, *file, buffer[BUFSIZ];
310 CE ce = &ct->c_cefile;
311
312 file = NULL;
313 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
314 return NOTOK;
315
316 c = '\n';
317 while ((inbytes = fread (buffer, 1, sizeof buffer, ce->ce_fp)) > 0) {
318 c = buffer[inbytes - 1];
319 if (fwrite (buffer, 1, inbytes, out) < inbytes) {
320 advise ("write8Bit", "fwrite");
321 }
322 }
323 if (c != '\n')
324 putc ('\n', out);
325
326 (*ct->c_ceclosefnx) (ct);
327 return OK;
328 }
329
330
331 /*
332 * Output a content using quoted-printable
333 */
334
335 static int
336 writeQuoted (CT ct, FILE *out)
337 {
338 int fd;
339 char *cp, *file;
340 char c = '\0';
341 CE ce = &ct->c_cefile;
342 int n = 0;
343 char *bufp = NULL;
344 size_t buflen;
345 ssize_t gotlen;
346
347 file = NULL;
348 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
349 return NOTOK;
350
351 while ((gotlen = getline(&bufp, &buflen, ce->ce_fp)) != -1) {
352
353 cp = bufp + gotlen - 1;
354 if ((c = *cp) == '\n')
355 gotlen--;
356
357 /*
358 * if the line starts with "From ", encode the 'F' so it
359 * doesn't falsely match an mbox delimiter.
360 */
361 cp = bufp;
362 if (gotlen >= 5 && strncmp (cp, "From ", 5) == 0) {
363 fprintf (out, "=%02X", 'F');
364 cp++;
365 n += 3;
366 }
367
368 for (; cp < bufp + gotlen; cp++) {
369 if (n > CPERLIN - 3) {
370 fputs ("=\n", out);
371 n = 0;
372 }
373
374 switch (*cp) {
375 case ' ':
376 case '\t':
377 putc (*cp, out);
378 n++;
379 break;
380
381 default:
382 if (*cp < '!' || *cp > '~')
383 goto three_print;
384 putc (*cp, out);
385 n++;
386 break;
387
388 case '=':
389 three_print:
390 fprintf (out, "=%02X", *cp & 0xff);
391 n += 3;
392 break;
393 }
394 }
395
396 if (c == '\n') {
397 if (cp > bufp && (*--cp == ' ' || *cp == '\t'))
398 fputs ("=\n", out);
399
400 putc ('\n', out);
401 n = 0;
402 }
403 }
404
405 if (c != '\n')
406 putc ('\n', out);
407
408 (*ct->c_ceclosefnx) (ct);
409 free (bufp);
410 return OK;
411 }
412
413
414 /*
415 * Output a content using base64
416 */
417
418 static int
419 writeBase64ct (CT ct, FILE *out)
420 {
421 int fd, result;
422 char *file;
423 CE ce = &ct->c_cefile;
424
425 file = NULL;
426 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
427 return NOTOK;
428
429 result = writeBase64aux (ce->ce_fp, out,
430 ct->c_type == CT_TEXT && ct->c_ctparams
431 ? ((struct text *) ct->c_ctparams)->lf_line_endings == 0
432 : 0);
433 (*ct->c_ceclosefnx) (ct);
434 return result;
435 }