]> diplodocus.org Git - nmh/blob - uip/mhoutsbr.c
change mhlist to use decimal math when abbreviating sizes
[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';
340 CE ce = &ct->c_cefile;
341 int n = 0;
342 char *bufp = NULL;
343 size_t buflen;
344 ssize_t gotlen;
345
346 file = NULL;
347 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
348 return NOTOK;
349
350 while ((gotlen = getline(&bufp, &buflen, ce->ce_fp)) != -1) {
351
352 cp = bufp + gotlen - 1;
353 if ((c = *cp) == '\n')
354 gotlen--;
355
356 /*
357 * if the line starts with "From ", encode the 'F' so it
358 * doesn't falsely match an mbox delimiter.
359 */
360 cp = bufp;
361 if (gotlen >= 5 && strncmp (cp, "From ", 5) == 0) {
362 fprintf (out, "=%02X", 'F');
363 cp++;
364 n += 3;
365 }
366
367 for (; cp < bufp + gotlen; cp++) {
368 if (n > CPERLIN - 3) {
369 fputs ("=\n", out);
370 n = 0;
371 }
372
373 switch (*cp) {
374 case ' ':
375 case '\t':
376 putc (*cp, out);
377 n++;
378 break;
379
380 default:
381 if (*cp < '!' || *cp > '~')
382 goto three_print;
383 putc (*cp, out);
384 n++;
385 break;
386
387 case '=':
388 three_print:
389 fprintf (out, "=%02X", *cp & 0xff);
390 n += 3;
391 break;
392 }
393 }
394
395 if (c == '\n') {
396 if (cp > bufp && (*--cp == ' ' || *cp == '\t'))
397 fputs ("=\n", out);
398
399 putc ('\n', out);
400 n = 0;
401 }
402 }
403
404 if (c != '\n')
405 putc ('\n', out);
406
407 (*ct->c_ceclosefnx) (ct);
408 return OK;
409 }
410
411
412 /*
413 * Output a content using base64
414 */
415
416 static int
417 writeBase64ct (CT ct, FILE *out)
418 {
419 int fd, result;
420 char *file;
421 CE ce = &ct->c_cefile;
422
423 file = NULL;
424 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
425 return NOTOK;
426
427 result = writeBase64aux (ce->ce_fp, out, (ct->c_type == CT_TEXT));
428 (*ct->c_ceclosefnx) (ct);
429 return result;
430 }