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