nail.h: add some OS_ constants
[s-mailx.git] / send.c
blobc1541960d130b6a526e4e8006fce3cea60ea2796
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Mail to mail folders and displays.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE send
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 enum pipeflags {
47 PIPE_NULL, /* No pipe- mimetype handler */
48 PIPE_COMM, /* Normal command */
49 PIPE_ASYNC, /* Normal command, run asynchronous */
50 PIPE_TEXT, /* @ special command to force treatment as text */
51 PIPE_MSG /* Display message (returned as command string) */
54 enum parseflags {
55 PARSE_DEFAULT = 0,
56 PARSE_DECRYPT = 01,
57 PARSE_PARTS = 02
60 static sigjmp_buf _send_pipejmp;
62 /* */
63 static struct mimepart *parsemsg(struct message *mp, enum parseflags pf);
64 static enum okay parsepart(struct message *zmp, struct mimepart *ip,
65 enum parseflags pf, int level);
66 static void parse822(struct message *zmp, struct mimepart *ip,
67 enum parseflags pf, int level);
68 #ifdef HAVE_SSL
69 static void parsepkcs7(struct message *zmp, struct mimepart *ip,
70 enum parseflags pf, int level);
71 #endif
72 static void _parsemultipart(struct message *zmp,
73 struct mimepart *ip, enum parseflags pf, int level);
74 static void __newpart(struct mimepart *ip, struct mimepart **np,
75 off_t offs, int *part);
76 static void __endpart(struct mimepart **np, off_t xoffs, long lines);
78 /* Going for user display, print Part: info string */
79 static void _print_part_info(FILE *obuf, struct mimepart const *mpp,
80 struct ignoretab *doign, int level,
81 struct quoteflt *qf, ui64_t *stats);
83 /* Query possible pipe command for MIME part */
84 static enum pipeflags _pipecmd(char const **result, struct mimepart const *mpp);
86 /* Create a pipe; if mpp is not NULL, place some NAILENV_* environment
87 * variables accordingly */
88 static FILE * _pipefile(char const *pipecomm, struct mimepart const *mpp,
89 FILE **qbuf, bool_t quote, bool_t async);
91 /* Call mime_write() as approbiate and adjust statistics */
92 SINLINE ssize_t _out(char const *buf, size_t len, FILE *fp,
93 enum conversion convert, enum sendaction action,
94 struct quoteflt *qf, ui64_t *stats, struct str *rest);
96 /* SIGPIPE handler */
97 static void _send_onpipe(int signo);
99 /* Send one part */
100 static int sendpart(struct message *zmp, struct mimepart *ip,
101 FILE *obuf, struct ignoretab *doign,
102 struct quoteflt *qf, enum sendaction action,
103 ui64_t *stats, int level);
105 /* Get a file for an attachment */
106 static FILE * newfile(struct mimepart *ip, int *ispipe);
108 static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
109 struct quoteflt *qf, ui64_t *stats);
111 /* Output a reasonable looking status field */
112 static void statusput(const struct message *mp, FILE *obuf,
113 struct quoteflt *qf, ui64_t *stats);
114 static void xstatusput(const struct message *mp, FILE *obuf,
115 struct quoteflt *qf, ui64_t *stats);
117 static void put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats);
119 static struct mimepart *
120 parsemsg(struct message *mp, enum parseflags pf)
122 struct mimepart *ip;
123 NYD_ENTER;
125 ip = csalloc(1, sizeof *ip);
126 ip->m_flag = mp->m_flag;
127 ip->m_have = mp->m_have;
128 ip->m_block = mp->m_block;
129 ip->m_offset = mp->m_offset;
130 ip->m_size = mp->m_size;
131 ip->m_xsize = mp->m_xsize;
132 ip->m_lines = mp->m_lines;
133 ip->m_xlines = mp->m_lines;
134 if (parsepart(mp, ip, pf, 0) != OKAY)
135 ip = NULL;
136 NYD_LEAVE;
137 return ip;
140 static enum okay
141 parsepart(struct message *zmp, struct mimepart *ip, enum parseflags pf,
142 int level)
144 char *cp_b, *cp;
145 enum okay rv = STOP;
146 NYD_ENTER;
148 ip->m_ct_type = hfield1("content-type", (struct message*)ip);
149 if (ip->m_ct_type != NULL) {
150 ip->m_ct_type_plain = cp_b = savestr(ip->m_ct_type);
151 if ((cp = strchr(cp_b, ';')) != NULL)
152 *cp = '\0';
153 cp = cp_b + strlen(cp_b);
154 while (cp > cp_b && blankchar(cp[-1]))
155 --cp;
156 *cp = '\0';
157 } else if (ip->m_parent != NULL &&
158 ip->m_parent->m_mimecontent == MIME_DIGEST)
159 ip->m_ct_type_plain = "message/rfc822";
160 else
161 ip->m_ct_type_plain = "text/plain";
162 ip->m_ct_type_usr_ovwr = NULL;
164 if (ip->m_ct_type != NULL)
165 ip->m_charset = mime_param_get("charset", ip->m_ct_type);
166 if (ip->m_charset == NULL)
167 ip->m_charset = charset_get_7bit();
169 if ((ip->m_ct_enc = hfield1("content-transfer-encoding",
170 (struct message*)ip)) == NULL)
171 ip->m_ct_enc = mime_enc_from_conversion(CONV_7BIT);
172 ip->m_mime_enc = mime_enc_from_ctehead(ip->m_ct_enc);
174 if (((cp = hfield1("content-disposition", (struct message*)ip)) == NULL ||
175 (ip->m_filename = mime_param_get("filename", cp)) == NULL) &&
176 ip->m_ct_type != NULL)
177 ip->m_filename = mime_param_get("name", ip->m_ct_type);
179 ip->m_mimecontent = mime_type_mimepart_content(ip);
181 if (pf & PARSE_PARTS) {
182 if (level > 9999) { /* TODO MAGIC */
183 fprintf(stderr, _("MIME content too deeply nested\n"));
184 goto jleave;
186 switch (ip->m_mimecontent) {
187 case MIME_PKCS7:
188 if (pf & PARSE_DECRYPT) {
189 #ifdef HAVE_SSL
190 parsepkcs7(zmp, ip, pf, level);
191 break;
192 #else
193 fprintf(stderr, _("No SSL support compiled in.\n"));
194 goto jleave;
195 #endif
197 /* FALLTHRU */
198 default:
199 break;
200 case MIME_MULTI:
201 case MIME_ALTERNATIVE:
202 case MIME_RELATED: /* TODO /related yet handled like /alternative */
203 case MIME_DIGEST:
204 _parsemultipart(zmp, ip, pf, level);
205 break;
206 case MIME_822:
207 parse822(zmp, ip, pf, level);
208 break;
211 rv = OKAY;
212 jleave:
213 NYD_LEAVE;
214 return rv;
217 static void
218 parse822(struct message *zmp, struct mimepart *ip, enum parseflags pf,
219 int level)
221 int c, lastc = '\n';
222 size_t cnt;
223 FILE *ibuf;
224 off_t offs;
225 struct mimepart *np;
226 long lines;
227 NYD_ENTER;
229 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
230 goto jleave;
232 cnt = ip->m_size;
233 lines = ip->m_lines;
234 while (cnt && ((c = getc(ibuf)) != EOF)) {
235 --cnt;
236 if (c == '\n') {
237 --lines;
238 if (lastc == '\n')
239 break;
241 lastc = c;
243 offs = ftell(ibuf);
245 np = csalloc(1, sizeof *np);
246 np->m_flag = MNOFROM;
247 np->m_have = HAVE_HEADER | HAVE_BODY;
248 np->m_block = mailx_blockof(offs);
249 np->m_offset = mailx_offsetof(offs);
250 np->m_size = np->m_xsize = cnt;
251 np->m_lines = np->m_xlines = lines;
252 np->m_partstring = ip->m_partstring;
253 np->m_parent = ip;
254 ip->m_multipart = np;
256 if (ok_blook(rfc822_body_from_)) {
257 substdate((struct message*)np);
258 np->m_from = fakefrom((struct message*)np);/* TODO strip MNOFROM flag? */
261 parsepart(zmp, np, pf, level + 1);
262 jleave:
263 NYD_LEAVE;
266 #ifdef HAVE_SSL
267 static void
268 parsepkcs7(struct message *zmp, struct mimepart *ip, enum parseflags pf,
269 int level)
271 struct message m, *xmp;
272 struct mimepart *np;
273 char *to, *cc;
274 NYD_ENTER;
276 memcpy(&m, ip, sizeof m);
277 to = hfield1("to", zmp);
278 cc = hfield1("cc", zmp);
280 if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) {
281 np = csalloc(1, sizeof *np);
282 np->m_flag = xmp->m_flag;
283 np->m_have = xmp->m_have;
284 np->m_block = xmp->m_block;
285 np->m_offset = xmp->m_offset;
286 np->m_size = xmp->m_size;
287 np->m_xsize = xmp->m_xsize;
288 np->m_lines = xmp->m_lines;
289 np->m_xlines = xmp->m_xlines;
290 np->m_partstring = ip->m_partstring;
292 if (parsepart(zmp, np, pf, level + 1) == OKAY) {
293 np->m_parent = ip;
294 ip->m_multipart = np;
297 NYD_LEAVE;
299 #endif
301 static void
302 _parsemultipart(struct message *zmp, struct mimepart *ip, enum parseflags pf,
303 int level)
305 /* TODO Instead of the recursive multiple run parse we have today,
306 * TODO the send/MIME layer rewrite must create a "tree" of parts with
307 * TODO a single-pass parse, then address each part directly as
308 * TODO necessary; since boundaries start with -- and the content
309 * TODO rather forms a stack this is pretty cheap indeed! */
310 struct mimepart *np = NULL;
311 char *boundary, *line = NULL;
312 size_t linesize = 0, linelen, cnt, boundlen;
313 FILE *ibuf;
314 off_t offs;
315 int part = 0;
316 long lines = 0;
317 NYD_ENTER;
319 if ((boundary = mime_param_boundary_get(ip->m_ct_type, &linelen)) == NULL)
320 goto jleave;
322 boundlen = linelen;
323 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
324 goto jleave;
326 cnt = ip->m_size;
327 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0))
328 if (line[0] == '\n')
329 break;
330 offs = ftell(ibuf);
332 __newpart(ip, &np, offs, NULL);
333 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
334 /* XXX linelen includes LF */
335 if (!((lines > 0 || part == 0) && linelen > boundlen &&
336 !strncmp(line, boundary, boundlen))) {
337 ++lines;
338 continue;
341 /* Subpart boundary? */
342 if (line[boundlen] == '\n') {
343 offs = ftell(ibuf);
344 if (part > 0) {
345 __endpart(&np, offs - boundlen - 2, lines);
346 __newpart(ip, &np, offs - boundlen - 2, NULL);
348 __endpart(&np, offs, 2);
349 __newpart(ip, &np, offs, &part);
350 lines = 0;
351 continue;
354 /* Final boundary? Be aware of cases where there is no separating
355 * newline in between boundaries, as has been seen in a message with
356 * "Content-Type: multipart/appledouble;" */
357 if (linelen < boundlen + 2)
358 continue;
359 linelen -= boundlen + 2;
360 if (line[boundlen] != '-' || line[boundlen + 1] != '-' ||
361 (linelen > 0 && line[boundlen + 2] != '\n'))
362 continue;
363 offs = ftell(ibuf);
364 if (part != 0) {
365 __endpart(&np, offs - boundlen - 4, lines);
366 __newpart(ip, &np, offs - boundlen - 4, NULL);
368 __endpart(&np, offs + cnt, 2);
369 break;
371 if (np) {
372 offs = ftell(ibuf);
373 __endpart(&np, offs, lines);
376 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart)
377 if (np->m_mimecontent != MIME_DISCARD)
378 parsepart(zmp, np, pf, level + 1);
379 free(line);
380 jleave:
381 NYD_LEAVE;
384 static void
385 __newpart(struct mimepart *ip, struct mimepart **np, off_t offs, int *part)
387 struct mimepart *pp;
388 size_t sz;
389 NYD_ENTER;
391 *np = csalloc(1, sizeof **np);
392 (*np)->m_flag = MNOFROM;
393 (*np)->m_have = HAVE_HEADER | HAVE_BODY;
394 (*np)->m_block = mailx_blockof(offs);
395 (*np)->m_offset = mailx_offsetof(offs);
397 if (part) {
398 ++(*part);
399 sz = (ip->m_partstring != NULL) ? strlen(ip->m_partstring) : 0;
400 sz += 20;
401 (*np)->m_partstring = salloc(sz);
402 if (ip->m_partstring)
403 snprintf((*np)->m_partstring, sz, "%s.%u", ip->m_partstring, *part);
404 else
405 snprintf((*np)->m_partstring, sz, "%u", *part);
406 } else
407 (*np)->m_mimecontent = MIME_DISCARD;
408 (*np)->m_parent = ip;
410 if (ip->m_multipart) {
411 for (pp = ip->m_multipart; pp->m_nextpart != NULL; pp = pp->m_nextpart)
413 pp->m_nextpart = *np;
414 } else
415 ip->m_multipart = *np;
416 NYD_LEAVE;
419 static void
420 __endpart(struct mimepart **np, off_t xoffs, long lines)
422 off_t offs;
423 NYD_ENTER;
425 offs = mailx_positionof((*np)->m_block, (*np)->m_offset);
426 (*np)->m_size = (*np)->m_xsize = xoffs - offs;
427 (*np)->m_lines = (*np)->m_xlines = lines;
428 *np = NULL;
429 NYD_LEAVE;
432 static void
433 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
434 struct ignoretab *doign, int level, struct quoteflt *qf, ui64_t *stats)
436 char buf[64];
437 struct str ti = {NULL, 0}, to;
438 struct str const *cpre, *csuf;
439 char const *cp;
440 NYD2_ENTER;
442 #ifdef HAVE_COLOUR
443 cpre = colour_get(COLOURSPEC_PARTINFO);
444 csuf = colour_get(COLOURSPEC_RESET);
445 #else
446 cpre = csuf = NULL;
447 #endif
449 /* Take care of "99.99", i.e., 5 */
450 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
451 cp = "?";
452 if (level || (cp[0] != '1' && cp[1] == '\0'))
453 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
454 if (cpre != NULL)
455 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
456 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
457 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
459 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
460 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
461 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
463 if ((cp = mpp->m_ct_type_usr_ovwr) != NULL)
464 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
465 else
466 cp = mpp->m_ct_type_plain;
467 if ((to.l = strlen(cp)) > 30 && is_asccaseprefix(cp, "application/")) {
468 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
469 size_t i = to.l - fl;
470 char *x = salloc(al + i +1);
472 memcpy(x, "appl../", al);
473 memcpy(x + al, cp + fl, i +1);
474 cp = x;
475 to.l = al + i;
477 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
479 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL) {
480 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
481 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
482 cp = "qu.-pr.";
483 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
486 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_charset) != NULL) {
487 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
488 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
491 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
492 if (csuf != NULL)
493 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
494 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
496 if (is_ign("content-disposition", 19, doign) && mpp->m_filename != NULL &&
497 *mpp->m_filename != '\0') {
498 makeprint(n_str_add_cp(&ti, mpp->m_filename), &to);
499 free(ti.s);
500 to.l = delctrl(to.s, to.l);
502 if (cpre != NULL)
503 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
504 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
505 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
506 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
507 if (csuf != NULL)
508 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
509 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
511 free(to.s);
513 NYD2_LEAVE;
516 static enum pipeflags
517 _pipecmd(char const **result, struct mimepart const *mpp)
519 enum pipeflags rv;
520 char const *cp;
521 NYD2_ENTER;
523 *result = NULL;
525 /* Do we have any handler for this part? */
526 if ((cp = mime_type_mimepart_handler(mpp)) == NULL)
527 rv = PIPE_NULL;
528 else if (cp == MIME_TYPE_HANDLER_TEXT)
529 rv = PIPE_TEXT;
530 else if (
531 #ifdef HAVE_FILTER_HTML_TAGSOUP
532 cp == MIME_TYPE_HANDLER_HTML ||
533 #endif
534 *cp != '@') {
535 *result = cp;
536 rv = PIPE_COMM;
537 } else if (!(pstate & PS_MSGLIST_DIRECT)) {
538 /* Viewing multiple messages in one go, don't block system */
539 *result = _("[Directly address message only to display this]\n");
540 rv = PIPE_MSG;
541 } else {
542 /* Viewing a single message only */
543 /* TODO send/MIME layer rewrite: when we have a single-pass parser
544 * TODO then the parsing phase and the send phase will be separated;
545 * TODO that allows us to ask a user *before* we start the send, i.e.,
546 * TODO *before* a pager pipe is setup */
547 if (*++cp == '&')
548 /* Asynchronous command, normal command line */
549 *result = ++cp, rv = PIPE_ASYNC;
550 else
551 *result = cp, rv = PIPE_COMM;
553 NYD2_LEAVE;
554 return rv;
557 static FILE *
558 _pipefile(char const *pipecomm, struct mimepart const *mpp, FILE **qbuf,
559 bool_t quote, bool_t async)
561 struct str s;
562 char const *env_addon[8], *cp, *sh;
563 FILE *rbuf;
564 NYD_ENTER;
566 rbuf = *qbuf;
568 if (quote) {
569 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER,
570 0600)) == NULL) {
571 perror(_("tmpfile"));
572 *qbuf = rbuf;
574 async = FAL0;
577 #ifdef HAVE_FILTER_HTML_TAGSOUP
578 if (pipecomm == MIME_TYPE_HANDLER_HTML) {
579 union {int (*ptf)(void); char const *sh;} u;
580 u.ptf = &htmlflt_process_main;
581 rbuf = Popen(MIME_TYPE_HANDLER_HTML, "W", u.sh, NULL, fileno(*qbuf));
582 pipecomm = "Builtin HTML tagsoup filter";
583 goto jafter_tagsoup_hack;
585 #endif
587 /* NAIL_FILENAME */
588 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
589 cp = "";
590 env_addon[0] = str_concat_csvl(&s, NAILENV_FILENAME, "=", cp, NULL)->s;
592 /* NAIL_FILENAME_GENERATED */
593 s.s = getrandstring(NAME_MAX);
594 if (mpp == NULL)
595 cp = s.s;
596 else if (*cp == '\0') {
597 if ( (((cp = mpp->m_ct_type_usr_ovwr) == NULL || *cp == '\0') &&
598 ((cp = mpp->m_ct_type_plain) == NULL || *cp == '\0')) ||
599 ((sh = strrchr(cp, '/')) == NULL || *++sh == '\0'))
600 cp = s.s;
601 else {
602 LCTA(NAME_MAX >= 8);
603 s.s[7] = '.';
604 cp = savecat(s.s, sh);
607 env_addon[1] = str_concat_csvl(&s, NAILENV_FILENAME_GENERATED, "=", cp,
608 NULL)->s;
610 /* NAIL_CONTENT{,_EVIDENCE} */
611 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
612 cp = "";
613 env_addon[2] = str_concat_csvl(&s, NAILENV_CONTENT, "=", cp, NULL)->s;
615 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
616 cp = mpp->m_ct_type_usr_ovwr;
617 env_addon[3] = str_concat_csvl(&s, NAILENV_CONTENT_EVIDENCE, "=", cp,
618 NULL)->s;
620 env_addon[4] = str_concat_csvl(&s, NAILENV_TMPDIR, "=", tempdir, NULL)->s;
621 env_addon[5] = str_concat_csvl(&s, "TMPDIR", "=", tempdir, NULL)->s;
623 env_addon[6] = NULL;
625 if ((sh = ok_vlook(SHELL)) == NULL)
626 sh = XSHELL;
628 rbuf = Popen(pipecomm, "W", sh, env_addon, (async ? -1 : fileno(*qbuf)));
629 #ifdef HAVE_FILTER_HTML_TAGSOUP
630 jafter_tagsoup_hack:
631 #endif
632 if (rbuf == NULL)
633 fprintf(stderr, _("Cannot run MIME type handler \"%s\": %s\n"),
634 pipecomm, strerror(errno));
635 else {
636 fflush(*qbuf);
637 if (*qbuf != stdout)
638 fflush(stdout);
640 NYD_LEAVE;
641 return rbuf;
644 SINLINE ssize_t
645 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
646 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *rest)
648 ssize_t sz = 0, n;
649 int flags;
650 NYD_ENTER;
652 #if 0
653 Well ... it turns out to not work like that since of course a valid
654 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
655 after an empty line has been seen, which cannot be detected that easily
656 right here!
657 ifdef HAVE_DEBUG /* TODO assert legacy */
658 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
659 * TODO other input situations handle RFC 4155 OR, if newly generated,
660 * TODO enforce quoted-printable if there is From_, as "required" by
661 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
662 * TODO if it may happen in this path, we should just treat decryption
663 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
664 if (action == SEND_MBOX || action == SEND_DECRYPT)
665 assert(!is_head(buf, len, TRU1));
666 #else
667 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
668 is_head(buf, len, TRU1)) {
669 putc('>', fp);
670 ++sz;
672 #endif
674 flags = ((int)action & _TD_EOF);
675 action &= ~_TD_EOF;
676 n = mime_write(buf, len, fp,
677 action == SEND_MBOX ? CONV_NONE : convert,
678 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
679 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
680 ? TD_ISPR | TD_ICONV
681 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
682 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
683 qf, rest);
684 if (n < 0)
685 sz = n;
686 else if (n > 0) {
687 sz += n;
688 if (stats != NULL)
689 *stats += sz;
691 NYD_LEAVE;
692 return sz;
695 static void
696 _send_onpipe(int signo)
698 NYD_X; /* Signal handler */
699 UNUSED(signo);
700 siglongjmp(_send_pipejmp, 1);
703 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
704 static int __sendp_sig; /* TODO someday.. */
705 static sighandler_type __sendp_opipe;
706 static void
707 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
709 NYD_X; /* Signal handler */
710 __sendp_sig = sig;
711 siglongjmp(__sendp_actjmp, 1);
714 static sigjmp_buf __sndalter_actjmp; /* TODO someday.. */
715 static int __sndalter_sig; /* TODO someday.. */
716 static void
717 __sndalter_onsig(int sig) /* TODO someday, we won't need it no more */
719 NYD_X; /* Signal handler */
720 __sndalter_sig = sig;
721 siglongjmp(__sndalter_actjmp, 1);
724 static int
725 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
726 struct ignoretab *doign, struct quoteflt *qf,
727 enum sendaction volatile action, ui64_t * volatile stats, int level)
729 int volatile ispipe, rv = 0;
730 struct str rest;
731 char *line = NULL, *cp, *cp2, *start;
732 char const *pipecomm = NULL;
733 size_t linesize = 0, linelen, cnt;
734 int dostat, infld = 0, ignoring = 1, isenc, c;
735 struct mimepart *volatile np;
736 FILE * volatile ibuf = NULL, * volatile pbuf = obuf, * volatile qbuf = obuf,
737 *origobuf = obuf;
738 enum conversion volatile convert;
739 sighandler_type volatile oldpipe = SIG_DFL;
740 long lineno = 0;
741 NYD_ENTER;
743 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
744 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
745 goto jskip;
747 dostat = 0;
748 if (level == 0) {
749 if (doign != NULL) {
750 if (!is_ign("status", 6, doign))
751 dostat |= 1;
752 if (!is_ign("x-status", 8, doign))
753 dostat |= 2;
754 } else
755 dostat = 3;
757 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
758 rv = -1;
759 goto jleave;
761 cnt = ip->m_size;
763 if (ip->m_mimecontent == MIME_DISCARD)
764 goto jskip;
766 if (!(ip->m_flag & MNOFROM))
767 while (cnt && (c = getc(ibuf)) != EOF) {
768 cnt--;
769 if (c == '\n')
770 break;
772 isenc = 0;
773 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
774 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
775 action == SEND_TOSRCH)
776 ? CONV_FROMHDR : CONV_NONE;
778 /* Work the headers */
779 quoteflt_reset(qf, obuf);
780 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
781 ++lineno;
782 if (line[0] == '\n') {
783 /* If line is blank, we've reached end of headers, so force out
784 * status: field and note that we are no longer in header fields */
785 if (dostat & 1)
786 statusput(zmp, obuf, qf, stats);
787 if (dostat & 2)
788 xstatusput(zmp, obuf, qf, stats);
789 if (doign != allignore)
790 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
791 break;
794 isenc &= ~1;
795 if (infld && blankchar(line[0])) {
796 /* If this line is a continuation (SP / HT) of a previous header
797 * field, determine if the start of the line is a MIME encoded word */
798 if (isenc & 2) {
799 for (cp = line; blankchar(*cp); ++cp);
800 if (cp > line && linelen - PTR2SIZE(cp - line) > 8 &&
801 cp[0] == '=' && cp[1] == '?')
802 isenc |= 1;
804 } else {
805 /* Pick up the header field if we have one */
806 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
808 cp2 = cp;
809 while (spacechar(*cp))
810 ++cp;
811 if (cp[0] != ':' && level == 0 && lineno == 1) {
812 /* Not a header line, force out status: This happens in uucp style
813 * mail where there are no headers at all */
814 if (dostat & 1)
815 statusput(zmp, obuf, qf, stats);
816 if (dostat & 2)
817 xstatusput(zmp, obuf, qf, stats);
818 if (doign != allignore)
819 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
820 break;
823 /* If it is an ignored field and we care about such things, skip it.
824 * Misuse dostat also for another bit xxx use a bitenum + for more */
825 if (ok_blook(keep_content_length))
826 dostat |= 1 << 2;
827 c = *cp2;
828 *cp2 = 0; /* temporarily null terminate */
829 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
830 (action == SEND_MBOX && !(dostat & (1 << 2)) &&
831 (!asccasecmp(line, "content-length") ||
832 !asccasecmp(line, "lines"))))
833 ignoring = 1;
834 else if (!asccasecmp(line, "status")) {
835 /* If field is "status," go compute and print real Status: field */
836 if (dostat & 1) {
837 statusput(zmp, obuf, qf, stats);
838 dostat &= ~1;
839 ignoring = 1;
841 } else if (!asccasecmp(line, "x-status")) {
842 /* If field is "status," go compute and print real Status: field */
843 if (dostat & 2) {
844 xstatusput(zmp, obuf, qf, stats);
845 dostat &= ~2;
846 ignoring = 1;
848 } else {
849 ignoring = 0;
850 /* For colourization we need the complete line, so save it */
851 /* XXX This is all temporary (colour belongs into backend), so
852 * XXX use pipecomm as a temporary storage in the meanwhile */
853 #ifdef HAVE_COLOUR
854 if (colour_table != NULL)
855 pipecomm = savestrbuf(line, PTR2SIZE(cp2 - line));
856 #endif
858 *cp2 = c;
859 dostat &= ~(1 << 2);
860 infld = 1;
863 /* Determine if the end of the line is a MIME encoded word */
864 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
865 * TODO with header follow lines, and it should be up to the backend
866 * TODO what happens and what not, i.e., it doesn't matter wether it's
867 * TODO a MIME-encoded word or not, as long as a single separating space
868 * TODO remains in between lines (the MIME stuff will correctly remove
869 * TODO whitespace in between multiple adjacent encoded words) */
870 isenc &= ~2;
871 if (cnt && (c = getc(ibuf)) != EOF) {
872 if (blankchar(c)) {
873 cp = line + linelen - 1;
874 if (linelen > 0 && *cp == '\n')
875 --cp;
876 while (cp >= line && whitechar(*cp))
877 --cp;
878 if (PTR2SIZE(cp - line > 8) && cp[0] == '=' && cp[-1] == '?')
879 isenc |= 2;
881 ungetc(c, ibuf);
884 if (!ignoring) {
885 size_t len = linelen;
886 start = line;
887 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
888 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
889 action == SEND_TOSRCH) {
890 /* Strip blank characters if two MIME-encoded words follow on
891 * continuing lines */
892 if (isenc & 1)
893 while (len > 0 && blankchar(*start)) {
894 ++start;
895 --len;
897 if (isenc & 2)
898 if (len > 0 && start[len - 1] == '\n')
899 --len;
900 while (len > 0 && blankchar(start[len - 1]))
901 --len;
903 #ifdef HAVE_COLOUR
905 bool_t colour_stripped = FAL0;
906 if (pipecomm != NULL) {
907 colour_put_header(obuf, pipecomm);
908 if (len > 0 && start[len - 1] == '\n') {
909 colour_stripped = TRU1;
910 --len;
913 #endif
914 _out(start, len, obuf, convert, action, qf, stats, NULL);
915 #ifdef HAVE_COLOUR
916 if (pipecomm != NULL) {
917 colour_reset(obuf); /* XXX reset after \n!! */
918 if (colour_stripped)
919 putc('\n', obuf);
922 #endif
923 if (ferror(obuf)) {
924 free(line);
925 rv = -1;
926 goto jleave;
930 quoteflt_flush(qf);
931 free(line);
932 line = NULL;
933 pipecomm = NULL;
935 jskip:
936 switch (ip->m_mimecontent) {
937 case MIME_822:
938 switch (action) {
939 case SEND_TODISP:
940 case SEND_TODISP_ALL:
941 case SEND_QUOTE:
942 case SEND_QUOTE_ALL:
943 if (ok_blook(rfc822_body_from_)) {
944 if (qf->qf_pfix_len > 0) {
945 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
946 qf->qf_pfix_len, obuf);
947 if (i == qf->qf_pfix_len && stats != NULL)
948 *stats += i;
950 put_from_(obuf, ip->m_multipart, stats);
952 /* FALLTHRU */
953 case SEND_TOSRCH:
954 case SEND_DECRYPT:
955 goto jmulti;
956 case SEND_TOFILE:
957 case SEND_TOPIPE:
958 if (ok_blook(rfc822_body_from_))
959 put_from_(obuf, ip->m_multipart, stats);
960 /* FALLTHRU */
961 case SEND_MBOX:
962 case SEND_RFC822:
963 case SEND_SHOW:
964 break;
966 break;
967 case MIME_TEXT_HTML:
968 case MIME_TEXT:
969 case MIME_TEXT_PLAIN:
970 switch (action) {
971 case SEND_TODISP:
972 case SEND_TODISP_ALL:
973 case SEND_QUOTE:
974 case SEND_QUOTE_ALL:
975 ispipe = TRU1;
976 switch (_pipecmd(&pipecomm, ip)) {
977 case PIPE_MSG:
978 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
979 stats, NULL);
980 /* We would print this as plain text, so better force going home */
981 goto jleave;
982 case PIPE_TEXT:
983 case PIPE_COMM:
984 case PIPE_NULL:
985 break;
986 case PIPE_ASYNC:
987 ispipe = FAL0;
988 break;
990 /* FALLTRHU */
991 default:
992 break;
994 break;
995 case MIME_DISCARD:
996 if (action != SEND_DECRYPT)
997 goto jleave;
998 break;
999 case MIME_PKCS7:
1000 if (action != SEND_MBOX && action != SEND_RFC822 &&
1001 action != SEND_SHOW && ip->m_multipart != NULL)
1002 goto jmulti;
1003 /* FALLTHRU */
1004 default:
1005 switch (action) {
1006 case SEND_TODISP:
1007 case SEND_TODISP_ALL:
1008 case SEND_QUOTE:
1009 case SEND_QUOTE_ALL:
1010 ispipe = TRU1;
1011 switch (_pipecmd(&pipecomm, ip)) {
1012 case PIPE_MSG:
1013 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
1014 stats, NULL);
1015 pipecomm = NULL;
1016 break;
1017 case PIPE_ASYNC:
1018 ispipe = FAL0;
1019 /* FALLTHRU */
1020 case PIPE_COMM:
1021 case PIPE_NULL:
1022 break;
1023 case PIPE_TEXT:
1024 goto jcopyout; /* break; break; */
1026 if (pipecomm != NULL)
1027 break;
1028 if (level == 0 && cnt) {
1029 char const *x = _("[Binary content]\n");
1030 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
1032 goto jleave;
1033 case SEND_TOFILE:
1034 case SEND_TOPIPE:
1035 case SEND_TOSRCH:
1036 case SEND_DECRYPT:
1037 case SEND_MBOX:
1038 case SEND_RFC822:
1039 case SEND_SHOW:
1040 break;
1042 break;
1043 case MIME_ALTERNATIVE:
1044 case MIME_RELATED:
1045 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
1046 !ok_blook(print_alternatives)) {
1047 /* XXX This (a) should not remain (b) should be own fun */
1048 struct mpstack {
1049 struct mpstack *outer;
1050 struct mimepart *mp;
1051 } outermost, * volatile curr = &outermost, * volatile mpsp;
1052 sighandler_type volatile opsh, oish, ohsh;
1053 size_t volatile partcnt = 0/* silence CC */;
1054 bool_t volatile neednl = FAL0;
1056 curr->outer = NULL;
1057 curr->mp = ip;
1059 __sndalter_sig = 0;
1060 opsh = safe_signal(SIGPIPE, &__sndalter_onsig);
1061 oish = safe_signal(SIGINT, &__sndalter_onsig);
1062 ohsh = safe_signal(SIGHUP, &__sndalter_onsig);
1063 if (sigsetjmp(__sndalter_actjmp, 1)) {
1064 rv = -1;
1065 goto jalter_unroll;
1068 for (np = ip->m_multipart;;) {
1069 partcnt = 0;
1070 jalter_redo:
1071 for (; np != NULL; np = np->m_nextpart) {
1072 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
1073 if (neednl)
1074 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
1075 _print_part_info(obuf, np, doign, level, qf, stats);
1077 neednl = TRU1;
1079 switch (np->m_mimecontent) {
1080 case MIME_ALTERNATIVE:
1081 case MIME_RELATED:
1082 case MIME_MULTI:
1083 case MIME_DIGEST:
1084 mpsp = ac_alloc(sizeof *mpsp);
1085 mpsp->outer = curr;
1086 mpsp->mp = np->m_multipart;
1087 curr->mp = np;
1088 curr = mpsp;
1089 np = mpsp->mp;
1090 neednl = FAL0;
1091 goto jalter_redo;
1092 default:
1093 switch (_pipecmd(&pipecomm, np)) {
1094 default:
1095 continue;
1096 case PIPE_TEXT:
1097 break;
1099 /* FALLTHRU */
1100 case MIME_TEXT_PLAIN:
1101 ++partcnt;
1102 if (action == SEND_QUOTE && partcnt > 1 &&
1103 ip->m_mimecontent == MIME_ALTERNATIVE)
1104 break;
1105 quoteflt_flush(qf);
1106 if (action == SEND_QUOTE && partcnt > 1) {
1107 struct quoteflt *dummy = quoteflt_dummy();
1108 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
1109 NULL);
1110 quoteflt_flush(dummy);
1112 neednl = FAL0;
1113 rv = sendpart(zmp, np, obuf, doign, qf, action, stats,
1114 level + 1);
1115 quoteflt_reset(qf, origobuf);
1117 if (rv < 0) {
1118 jalter_unroll:
1119 for (;; curr = mpsp) {
1120 if ((mpsp = curr->outer) == NULL)
1121 break;
1122 ac_free(curr);
1125 break;
1129 mpsp = curr->outer;
1130 if (mpsp == NULL)
1131 break;
1132 ac_free(curr);
1133 curr = mpsp;
1134 np = curr->mp->m_nextpart;
1136 safe_signal(SIGHUP, ohsh);
1137 safe_signal(SIGINT, oish);
1138 safe_signal(SIGPIPE, opsh);
1139 if (__sndalter_sig != 0)
1140 n_raise(__sndalter_sig);
1141 goto jleave;
1143 /* FALLTHRU */
1144 case MIME_MULTI:
1145 case MIME_DIGEST:
1146 switch (action) {
1147 case SEND_TODISP:
1148 case SEND_TODISP_ALL:
1149 case SEND_QUOTE:
1150 case SEND_QUOTE_ALL:
1151 case SEND_TOFILE:
1152 case SEND_TOPIPE:
1153 case SEND_TOSRCH:
1154 case SEND_DECRYPT:
1155 jmulti:
1156 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
1157 ip->m_multipart != NULL &&
1158 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
1159 ip->m_multipart->m_nextpart == NULL) {
1160 char const *x = _("[Missing multipart boundary - use \"show\" "
1161 "to display the raw message]\n");
1162 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
1165 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
1166 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
1167 continue;
1168 ispipe = FAL0;
1169 switch (action) {
1170 case SEND_TOFILE:
1171 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
1172 break;
1173 stats = NULL;
1174 if ((obuf = newfile(np, UNVOLATILE(&ispipe))) == NULL)
1175 continue;
1176 if (!ispipe)
1177 break;
1178 if (sigsetjmp(_send_pipejmp, 1)) {
1179 rv = -1;
1180 goto jpipe_close;
1182 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1183 break;
1184 case SEND_TODISP:
1185 case SEND_TODISP_ALL:
1186 case SEND_QUOTE_ALL:
1187 if (ip->m_mimecontent != MIME_MULTI &&
1188 ip->m_mimecontent != MIME_ALTERNATIVE &&
1189 ip->m_mimecontent != MIME_RELATED &&
1190 ip->m_mimecontent != MIME_DIGEST)
1191 break;
1192 _print_part_info(obuf, np, doign, level, qf, stats);
1193 break;
1194 case SEND_MBOX:
1195 case SEND_RFC822:
1196 case SEND_SHOW:
1197 case SEND_TOSRCH:
1198 case SEND_QUOTE:
1199 case SEND_DECRYPT:
1200 case SEND_TOPIPE:
1201 break;
1204 quoteflt_flush(qf);
1205 if (sendpart(zmp, np, obuf, doign, qf, action, stats, level+1) < 0)
1206 rv = -1;
1207 quoteflt_reset(qf, origobuf);
1209 if (action == SEND_QUOTE)
1210 break;
1211 if (action == SEND_TOFILE && obuf != origobuf) {
1212 if (!ispipe)
1213 Fclose(obuf);
1214 else {
1215 jpipe_close:
1216 safe_signal(SIGPIPE, SIG_IGN);
1217 Pclose(obuf, TRU1);
1218 safe_signal(SIGPIPE, oldpipe);
1222 goto jleave;
1223 case SEND_MBOX:
1224 case SEND_RFC822:
1225 case SEND_SHOW:
1226 break;
1230 /* Copy out message body */
1231 jcopyout:
1232 if (doign == allignore && level == 0) /* skip final blank line */
1233 --cnt;
1234 switch (ip->m_mime_enc) {
1235 case MIMEE_BIN:
1236 case MIMEE_7B:
1237 case MIMEE_8B:
1238 convert = CONV_NONE;
1239 break;
1240 case MIMEE_QP:
1241 convert = CONV_FROMQP;
1242 break;
1243 case MIMEE_B64:
1244 switch (ip->m_mimecontent) {
1245 case MIME_TEXT:
1246 case MIME_TEXT_PLAIN:
1247 case MIME_TEXT_HTML:
1248 convert = CONV_FROMB64_T;
1249 break;
1250 default:
1251 convert = CONV_FROMB64;
1253 break;
1254 default:
1255 convert = CONV_NONE;
1258 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1259 action == SEND_RFC822 || action == SEND_SHOW)
1260 convert = CONV_NONE;
1261 #ifdef HAVE_ICONV
1262 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
1263 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
1264 action == SEND_TOSRCH) &&
1265 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
1266 ip->m_mimecontent == MIME_TEXT_HTML ||
1267 ip->m_mimecontent == MIME_TEXT)) {
1268 char const *tcs = charset_get_lc();
1270 if (iconvd != (iconv_t)-1)
1271 n_iconv_close(iconvd);
1272 /* TODO Since Base64 has an odd 4:3 relation in between input
1273 * TODO and output an input line may end with a partial
1274 * TODO multibyte character; this is no problem at all unless
1275 * TODO we send to the display or whatever, i.e., ensure
1276 * TODO makeprint() or something; to avoid this trap, *force*
1277 * TODO iconv(), in which case this layer will handle leftovers
1278 * TODO correctly */
1279 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
1280 asccasecmp(charset_get_7bit(), ip->m_charset))) {
1281 iconvd = n_iconv_open(tcs, ip->m_charset);
1282 /* XXX Don't bail out if we cannot iconv(3) here;
1283 * XXX alternatively we could avoid trying to open
1284 * XXX if ip->m_charset is "unknown-8bit", which was
1285 * XXX the one that has bitten me?? */
1287 * TODO errors should DEFINETELY not be scrolled away!
1288 * TODO what about an error buffer (think old shsp(1)),
1289 * TODO re-dump errors since last snapshot when the
1290 * TODO command loop enters again? i.e., at least print
1291 * TODO "There were errors ?" before the next prompt,
1292 * TODO so that the user can look at the error buffer?
1294 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
1295 fprintf(stderr, _("Cannot convert from %s to %s\n"),
1296 ip->m_charset, tcs);
1297 /*rv = 1; goto jleave;*/
1301 #endif
1303 if (pipecomm != NULL && (action == SEND_TODISP ||
1304 action == SEND_TODISP_ALL || action == SEND_QUOTE ||
1305 action == SEND_QUOTE_ALL)) {
1306 qbuf = obuf;
1307 pbuf = _pipefile(pipecomm, ip, UNVOLATILE(&qbuf),
1308 (action == SEND_QUOTE || action == SEND_QUOTE_ALL), !ispipe);
1309 if (pbuf == NULL) {
1310 #ifdef HAVE_ICONV
1311 if (iconvd != (iconv_t)-1)
1312 n_iconv_close(iconvd);
1313 #endif
1314 rv = -1;
1315 goto jleave;
1317 action = SEND_TOPIPE;
1318 if (pbuf != qbuf) {
1319 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1320 if (sigsetjmp(_send_pipejmp, 1))
1321 goto jend;
1323 } else
1324 pbuf = qbuf = obuf;
1327 bool_t volatile eof;
1328 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1329 ui64_t *save_stats = stats;
1331 if (pbuf != origobuf) {
1332 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1333 stats = NULL;
1335 eof = FAL0;
1336 rest.s = NULL;
1337 rest.l = 0;
1339 if (pbuf == qbuf) {
1340 __sendp_sig = 0;
1341 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1342 if (sigsetjmp(__sendp_actjmp, 1)) {
1343 if (rest.s != NULL)
1344 free(rest.s);
1345 free(line);
1346 #ifdef HAVE_ICONV
1347 if (iconvd != (iconv_t)-1)
1348 n_iconv_close(iconvd);
1349 #endif
1350 safe_signal(SIGPIPE, __sendp_opipe);
1351 n_raise(__sendp_sig);
1355 quoteflt_reset(qf, pbuf);
1356 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1357 joutln:
1358 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1359 ferror(pbuf)) {
1360 rv = -1; /* XXX Should bail away?! */
1361 break;
1364 if (!eof && rest.l != 0) {
1365 linelen = 0;
1366 eof = TRU1;
1367 action |= _TD_EOF;
1368 goto joutln;
1370 if (pbuf == qbuf)
1371 safe_signal(SIGPIPE, __sendp_opipe);
1373 quoteflt_flush(qf);
1374 if (rest.s != NULL)
1375 free(rest.s);
1377 if (pbuf != origobuf) {
1378 qf->qf_pfix_len = save_qf_pfix_len;
1379 stats = save_stats;
1383 jend:
1384 if (line != NULL)
1385 free(line);
1386 if (pbuf != qbuf) {
1387 safe_signal(SIGPIPE, SIG_IGN);
1388 Pclose(pbuf, ispipe);
1389 safe_signal(SIGPIPE, oldpipe);
1390 if (qbuf != NULL && qbuf != obuf)
1391 pipecpy(qbuf, obuf, origobuf, qf, stats);
1393 #ifdef HAVE_ICONV
1394 if (iconvd != (iconv_t)-1)
1395 n_iconv_close(iconvd);
1396 #endif
1397 jleave:
1398 NYD_LEAVE;
1399 return rv;
1402 static FILE *
1403 newfile(struct mimepart *ip, int *ispipe)
1405 struct str in, out;
1406 char *f;
1407 FILE *fp;
1408 NYD_ENTER;
1410 f = ip->m_filename;
1411 *ispipe = 0;
1413 if (f != NULL && f != (char*)-1) {
1414 in.s = f;
1415 in.l = strlen(f);
1416 makeprint(&in, &out);
1417 out.l = delctrl(out.s, out.l);
1418 f = savestrbuf(out.s, out.l);
1419 free(out.s);
1422 if (options & OPT_INTERACTIVE) {
1423 char *f2, *f3;
1424 jgetname:
1425 printf(_("Enter filename for part %s (%s)"),
1426 (ip->m_partstring != NULL) ? ip->m_partstring : "?",
1427 ip->m_ct_type_plain);
1428 f2 = readstr_input(": ", (f != (char*)-1 && f != NULL)
1429 ? fexpand_nshell_quote(f) : NULL);
1430 if (f2 == NULL || *f2 == '\0') {
1431 fprintf(stderr, _("... skipping this\n"));
1432 fp = NULL;
1433 goto jleave;
1434 } else if (*f2 == '|')
1435 /* Pipes are expanded by the shell */
1436 f = f2;
1437 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NSHELL)) == NULL)
1438 /* (Error message written by fexpand()) */
1439 goto jgetname;
1440 else
1441 f = f3;
1443 if (f == NULL || f == (char*)-1) {
1444 fp = NULL;
1445 goto jleave;
1448 if (*f == '|') {
1449 char const *cp;
1450 cp = ok_vlook(SHELL);
1451 if (cp == NULL)
1452 cp = XSHELL;
1453 fp = Popen(f + 1, "w", cp, NULL, 1);
1454 if (!(*ispipe = (fp != NULL)))
1455 perror(f);
1456 } else {
1457 if ((fp = Fopen(f, "w")) == NULL)
1458 fprintf(stderr, _("Cannot open \"%s\"\n"), f);
1460 jleave:
1461 NYD_LEAVE;
1462 return fp;
1465 static void
1466 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1467 ui64_t *stats)
1469 char *line = NULL; /* TODO line pool */
1470 size_t linesize = 0, linelen, cnt;
1471 ssize_t all_sz, sz;
1472 NYD_ENTER;
1474 fflush(pipebuf);
1475 rewind(pipebuf);
1476 cnt = fsize(pipebuf);
1477 all_sz = 0;
1479 quoteflt_reset(qf, outbuf);
1480 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1481 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1482 break;
1483 all_sz += sz;
1485 if ((sz = quoteflt_flush(qf)) > 0)
1486 all_sz += sz;
1487 if (line)
1488 free(line);
1490 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1491 *stats += all_sz;
1492 fclose(pipebuf);
1493 NYD_LEAVE;
1496 static void
1497 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1498 ui64_t *stats)
1500 char statout[3], *cp = statout;
1501 NYD_ENTER;
1503 if (mp->m_flag & MREAD)
1504 *cp++ = 'R';
1505 if (!(mp->m_flag & MNEW))
1506 *cp++ = 'O';
1507 *cp = 0;
1508 if (statout[0]) {
1509 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1510 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1511 if (i > 0 && stats != NULL)
1512 *stats += i;
1514 NYD_LEAVE;
1517 static void
1518 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1519 ui64_t *stats)
1521 char xstatout[4];
1522 char *xp = xstatout;
1523 NYD_ENTER;
1525 if (mp->m_flag & MFLAGGED)
1526 *xp++ = 'F';
1527 if (mp->m_flag & MANSWERED)
1528 *xp++ = 'A';
1529 if (mp->m_flag & MDRAFTED)
1530 *xp++ = 'T';
1531 *xp = 0;
1532 if (xstatout[0]) {
1533 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1534 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1535 if (i > 0 && stats != NULL)
1536 *stats += i;
1538 NYD_LEAVE;
1541 static void
1542 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1544 char const *froma, *date, *nl;
1545 int i;
1546 NYD_ENTER;
1548 if (ip != NULL && ip->m_from != NULL) {
1549 froma = ip->m_from;
1550 date = fakedate(ip->m_time);
1551 nl = "\n";
1552 } else {
1553 froma = myname;
1554 date = time_current.tc_ctime;
1555 nl = "";
1558 colour_put(fp, COLOURSPEC_FROM_);
1559 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1560 colour_reset(fp);
1561 if (i > 0 && stats != NULL)
1562 *stats += i;
1563 NYD_LEAVE;
1566 FL int
1567 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1568 char const *prefix, enum sendaction action, ui64_t *stats)
1570 struct quoteflt qf;
1571 size_t cnt, sz, i;
1572 FILE *ibuf;
1573 enum parseflags pf;
1574 struct mimepart *ip;
1575 int rv = -1, c;
1576 NYD_ENTER;
1578 if (mp == dot && action != SEND_TOSRCH)
1579 pstate |= PS_DID_PRINT_DOT;
1580 if (stats != NULL)
1581 *stats = 0;
1582 quoteflt_init(&qf, prefix);
1584 /* First line is the From_ line, so no headers there to worry about */
1585 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1586 goto jleave;
1588 cnt = mp->m_size;
1589 sz = 0;
1591 struct str const *cpre, *csuf;
1592 #ifdef HAVE_COLOUR
1593 cpre = colour_get(COLOURSPEC_FROM_);
1594 csuf = colour_get(COLOURSPEC_RESET);
1595 #else
1596 cpre = csuf = NULL;
1597 #endif
1598 if (mp->m_flag & MNOFROM) {
1599 if (doign != allignore && doign != fwdignore && action != SEND_RFC822)
1600 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1601 (cpre != NULL ? cpre->s : ""),
1602 (int)qf.qf_pfix_len, (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""),
1603 fakefrom(mp), fakedate(mp->m_time),
1604 (csuf != NULL ? csuf->s : ""));
1605 } else {
1606 if (doign != allignore && doign != fwdignore && action != SEND_RFC822) {
1607 if (qf.qf_pfix_len > 0) {
1608 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1609 if (i != qf.qf_pfix_len)
1610 goto jleave;
1611 sz += i;
1613 #ifdef HAVE_COLOUR
1614 if (cpre != NULL) {
1615 fputs(cpre->s, obuf);
1616 cpre = (struct str const*)0x1;
1618 #endif
1621 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1622 if (doign != allignore && doign != fwdignore &&
1623 action != SEND_RFC822) {
1624 #ifdef HAVE_COLOUR
1625 if (c == '\n' && csuf != NULL) {
1626 cpre = (struct str const*)0x1;
1627 fputs(csuf->s, obuf);
1629 #endif
1630 putc(c, obuf);
1631 sz++;
1633 --cnt;
1634 if (c == '\n')
1635 break;
1638 #ifdef HAVE_COLOUR
1639 if (csuf != NULL && cpre != (struct str const*)0x1)
1640 fputs(csuf->s, obuf);
1641 #endif
1644 if (sz > 0 && stats != NULL)
1645 *stats += sz;
1647 pf = 0;
1648 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1649 pf |= PARSE_DECRYPT | PARSE_PARTS;
1650 if ((ip = parsemsg(mp, pf)) == NULL)
1651 goto jleave;
1653 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1654 jleave:
1655 quoteflt_destroy(&qf);
1656 NYD_LEAVE;
1657 return rv;
1660 /* s-it-mode */