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