]> diplodocus.org Git - nmh/blob - uip/mhoutsbr.c
Escape literal leading full stop in man/new.man.
[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/utils.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_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
57 /*
58 * Output a Content structure to a file.
59 */
60
61 static int
62 output_content (CT ct, FILE *out)
63 {
64 int result = 0;
65 CI ci = &ct->c_ctinfo;
66 char *boundary = "", *cp;
67
68 if ((cp = get_param(ci->ci_first_pm, "boundary", '-', 0)))
69 boundary = cp;
70
71 /*
72 * Output all header fields for this content
73 */
74 output_headers (ct, out);
75
76 /*
77 * If this is the internal content structure for a
78 * "message/external", then we are done with the
79 * headers (since it has no body).
80 */
81 if (ct->c_ctexbody) {
82 if (*boundary != '\0')
83 free(boundary);
84 return OK;
85 }
86
87 /*
88 * Now output the content bodies.
89 */
90 switch (ct->c_type) {
91 case CT_MULTIPART:
92 {
93 struct multipart *m;
94 struct part *part;
95
96 if (ct->c_rfc934)
97 putc ('\n', out);
98
99 m = (struct multipart *) ct->c_ctparams;
100
101 if (m->mp_content_before) {
102 fputs(m->mp_content_before, out);
103 }
104
105 for (part = m->mp_parts; part; part = part->mp_next) {
106 CT p = part->mp_part;
107
108 fprintf (out, "\n--%s\n", boundary);
109 if (output_content (p, out) == NOTOK) {
110 if (*boundary != '\0')
111 free(boundary);
112 return NOTOK;
113 }
114 }
115 fprintf (out, "\n--%s--\n", boundary);
116
117 if (m->mp_content_after) {
118 fputs(m->mp_content_after, out);
119 }
120 }
121 break;
122
123 case CT_MESSAGE:
124 putc ('\n', out);
125 if (ct->c_subtype == MESSAGE_EXTERNAL) {
126 struct exbody *e;
127
128 e = (struct exbody *) ct->c_ctparams;
129 if (output_content (e->eb_content, out) == NOTOK)
130 return NOTOK;
131
132 /* output phantom body for access-type "mail-server" */
133 if (e->eb_body)
134 writeExternalBody (ct, out);
135 } else {
136 result = write8Bit (ct, out);
137 }
138 break;
139
140 /*
141 * Handle discrete types (text/application/audio/image/video)
142 */
143 default:
144 switch (ct->c_encoding) {
145 case CE_7BIT:
146 /* Special case: if this is a non-MIME message with no
147 body, don't emit the newline that would appear between
148 the headers and body. In that case, the call to
149 write8Bit() shouldn't be needed, but is harmless. */
150 if (ct->c_ctinfo.ci_first_pm != NULL || ct->c_begin == 0 ||
151 ct->c_begin != ct->c_end) {
152 putc ('\n', out);
153 }
154 result = write8Bit (ct, out);
155 break;
156
157 case CE_8BIT:
158 putc ('\n', out);
159 result = write8Bit (ct, out);
160 break;
161
162 case CE_QUOTED:
163 putc ('\n', out);
164 result = writeQuoted (ct, out);
165 break;
166
167 case CE_BASE64:
168 putc ('\n', out);
169 result = writeBase64ct (ct, out);
170 break;
171
172 case CE_BINARY:
173 if (ct->c_type == CT_TEXT) {
174 /* So that mhfixmsg can decode to binary text. */
175 putc ('\n', out);
176 result = write8Bit (ct, out);
177 } else {
178 advise (NULL, "can't handle binary transfer encoding in content");
179 result = NOTOK;
180 }
181 break;
182
183 default:
184 advise (NULL, "unknown transfer encoding in content");
185 result = NOTOK;
186 break;
187 }
188 break;
189 }
190
191 if (*boundary != '\0')
192 free(boundary);
193
194 return result;
195 }
196
197
198 /*
199 * Output all the header fields for a content
200 */
201
202 static void
203 output_headers (CT ct, FILE *out)
204 {
205 HF hp;
206
207 hp = ct->c_first_hf;
208 while (hp) {
209 fprintf (out, "%s:%s", hp->name, hp->value);
210 hp = hp->next;
211 }
212 }
213
214
215 /*
216 * Write the phantom body for access-type "mail-server".
217 */
218
219 static int
220 writeExternalBody (CT ct, FILE *out)
221 {
222 char *cp, *dp;
223 struct exbody *e = (struct exbody *) ct->c_ctparams;
224
225 putc ('\n', out);
226 for (cp = e->eb_body; *cp; cp++) {
227 CT ct2 = e->eb_content;
228 CI ci2 = &ct2->c_ctinfo;
229
230 if (*cp == '\\') {
231 switch (*++cp) {
232 case 'I':
233 if (ct2->c_id) {
234 dp = trimcpy (ct2->c_id);
235
236 fputs (dp, out);
237 free (dp);
238 }
239 continue;
240
241 case 'N':
242 dp = get_param(ci2->ci_first_pm, "name", '_', 0);
243 if (dp) {
244 fputs (dp, out);
245 free (dp);
246 }
247 continue;
248
249 case 'T':
250 fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
251 dp = output_params(strlen(ci2->ci_type) +
252 strlen(ci2->ci_subtype) + 1,
253 ci2->ci_first_pm, NULL, 0);
254 if (dp) {
255 fputs (dp, out);
256 free (dp);
257 }
258 continue;
259
260 case 'n':
261 putc ('\n', out);
262 continue;
263
264 case 't':
265 putc ('\t', out);
266 continue;
267
268 case '\0':
269 cp--;
270 break;
271
272 case '\\':
273 case '"':
274 break;
275
276 default:
277 putc ('\\', out);
278 break;
279 }
280 }
281 putc (*cp, out);
282 }
283 putc ('\n', out);
284
285 return OK;
286 }
287
288
289 /*
290 * Output a content without any transfer encoding
291 */
292
293 static int
294 write8Bit (CT ct, FILE *out)
295 {
296 int fd;
297 size_t inbytes;
298 char c, *file, buffer[BUFSIZ];
299 CE ce = &ct->c_cefile;
300
301 file = NULL;
302 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
303 return NOTOK;
304
305 c = '\n';
306 while ((inbytes = fread (buffer, 1, sizeof buffer, ce->ce_fp)) > 0) {
307 c = buffer[inbytes - 1];
308 if (fwrite (buffer, 1, inbytes, out) < inbytes) {
309 advise ("write8Bit", "fwrite");
310 }
311 }
312 if (c != '\n')
313 putc ('\n', out);
314
315 (*ct->c_ceclosefnx) (ct);
316 return OK;
317 }
318
319
320 /*
321 * Output a content using quoted-printable
322 */
323
324 static int
325 writeQuoted (CT ct, FILE *out)
326 {
327 int fd;
328 char *cp, *file;
329 char c = '\0';
330 CE ce = &ct->c_cefile;
331 int n = 0;
332 char *bufp = NULL;
333 size_t buflen;
334 ssize_t gotlen;
335
336 file = NULL;
337 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
338 return NOTOK;
339
340 while ((gotlen = getline(&bufp, &buflen, ce->ce_fp)) != -1) {
341
342 cp = bufp + gotlen - 1;
343 if ((c = *cp) == '\n')
344 gotlen--;
345
346 /*
347 * if the line starts with "From ", encode the 'F' so it
348 * doesn't falsely match an mbox delimiter.
349 */
350 cp = bufp;
351 if (gotlen >= 5 && has_prefix(cp, "From ")) {
352 fprintf (out, "=%02X", 'F');
353 cp++;
354 n += 3;
355 }
356
357 for (; cp < bufp + gotlen; cp++) {
358 if (n > CPERLIN - 3) {
359 fputs ("=\n", out);
360 n = 0;
361 }
362
363 switch (*cp) {
364 case ' ':
365 case '\t':
366 putc (*cp, out);
367 n++;
368 break;
369
370 default:
371 if (*cp < '!' || *cp > '~')
372 goto three_print;
373 putc (*cp, out);
374 n++;
375 break;
376
377 case '=':
378 three_print:
379 fprintf (out, "=%02X", *cp & 0xff);
380 n += 3;
381 break;
382 }
383 }
384
385 if (c == '\n') {
386 if (cp > bufp && (*--cp == ' ' || *cp == '\t'))
387 fputs ("=\n", out);
388
389 putc ('\n', out);
390 n = 0;
391 }
392 }
393
394 if (c != '\n')
395 putc ('\n', out);
396
397 (*ct->c_ceclosefnx) (ct);
398 free (bufp);
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,
419 ct->c_type == CT_TEXT && ct->c_ctparams
420 ? ((struct text *) ct->c_ctparams)->lf_line_endings == 0
421 : 0);
422 (*ct->c_ceclosefnx) (ct);
423 return result;
424 }