]> diplodocus.org Git - nmh/blob - uip/mhoutsbr.c
Add test for long + encoded line; does not pass right now.
[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 PM pm;
85 char *boundary = "";
86
87 for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
88 if (! strcasecmp ("boundary", pm->pm_name)) {
89 boundary = pm->pm_value;
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_first_pm != 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 *cp;
231 PM pm;
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 (pm = ci2->ci_first_pm; pm; pm = pm->pm_next)
252 if (!strcasecmp (pm->pm_name, "name")) {
253 fprintf (out, "%s", pm->pm_value);
254 break;
255 }
256 continue;
257
258 case 'T':
259 fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
260 for (pm = ci2->ci_first_pm; pm; pm = pm->pm_next)
261 fprintf (out, "; %s=\"%s\"", pm->pm_name, pm->pm_value);
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 size_t inbytes;
302 char c, *file, buffer[BUFSIZ];
303 CE ce = &ct->c_cefile;
304
305 file = NULL;
306 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
307 return NOTOK;
308
309 c = '\n';
310 while ((inbytes = fread (buffer, 1, sizeof buffer, ce->ce_fp)) > 0) {
311 c = buffer[inbytes - 1];
312 fwrite (buffer, 1, inbytes, out);
313 }
314 if (c != '\n')
315 putc ('\n', out);
316
317 (*ct->c_ceclosefnx) (ct);
318 return OK;
319 }
320
321
322 /*
323 * Output a content using quoted-printable
324 */
325
326 static int
327 writeQuoted (CT ct, FILE *out)
328 {
329 int fd;
330 char *cp, *file;
331 char c = '\0', buffer[BUFSIZ];
332 CE ce = &ct->c_cefile;
333 int n = 0;
334
335 file = NULL;
336 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
337 return NOTOK;
338
339 while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
340
341 cp = buffer + strlen (buffer) - 1;
342 if ((c = *cp) == '\n')
343 *cp = '\0';
344
345 if (strncmp (cp = buffer, "From ", sizeof("From ") - 1) == 0) {
346 fprintf (out, "=%02X", *cp++ & 0xff);
347 n += 3;
348 }
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 n = 0;
384 }
385 }
386
387 if (c != '\n')
388 putc ('\n', out);
389
390 (*ct->c_ceclosefnx) (ct);
391 return OK;
392 }
393
394
395 /*
396 * Output a content using base64
397 */
398
399 static int
400 writeBase64ct (CT ct, FILE *out)
401 {
402 int fd, result;
403 char *file;
404 CE ce = &ct->c_cefile;
405
406 file = NULL;
407 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
408 return NOTOK;
409
410 result = writeBase64aux (ce->ce_fp, out, (ct->c_type == CT_TEXT));
411 (*ct->c_ceclosefnx) (ct);
412 return result;
413 }