]> diplodocus.org Git - nmh/blob - uip/mhoutsbr.c
Finished replacing mh_strcasecmp() with strcasecmp(). Removed
[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 (! 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 (!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 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, buffer[BUFSIZ];
332 CE ce = &ct->c_cefile;
333
334 file = NULL;
335 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
336 return NOTOK;
337
338 while (fgets (buffer, sizeof(buffer) - 1, ce->ce_fp)) {
339 int n;
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 } else {
349 n = 0;
350 }
351 for (; *cp; cp++) {
352 if (n > CPERLIN - 3) {
353 fputs ("=\n", out);
354 n = 0;
355 }
356
357 switch (*cp) {
358 case ' ':
359 case '\t':
360 putc (*cp, out);
361 n++;
362 break;
363
364 default:
365 if (*cp < '!' || *cp > '~')
366 goto three_print;
367 putc (*cp, out);
368 n++;
369 break;
370
371 case '=':
372 three_print:
373 fprintf (out, "=%02X", *cp & 0xff);
374 n += 3;
375 break;
376 }
377 }
378
379 if (c == '\n') {
380 if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
381 fputs ("=\n", out);
382
383 putc ('\n', out);
384 } else {
385 fputs ("=\n", out);
386 }
387 }
388
389 (*ct->c_ceclosefnx) (ct);
390 return OK;
391 }
392
393
394 /*
395 * Output a content using base64
396 */
397
398 static int
399 writeBase64ct (CT ct, FILE *out)
400 {
401 int fd, result;
402 char *file;
403 CE ce = &ct->c_cefile;
404
405 file = NULL;
406 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
407 return NOTOK;
408
409 result = writeBase64aux (ce->ce_fp, out);
410 (*ct->c_ceclosefnx) (ct);
411 return result;
412 }