Generalize usage of shell and tilde expansion..
[s-mailx.git] / send.c
blob10595891c01df43e08bf429897a2a684edc59161
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE send
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 enum pipe_flags {
43 PIPE_NULL, /* No pipe- mimetype handler, go away */
44 PIPE_CMD, /* Normal command */
45 PIPE_TEXT, /* @ special command to force treatment as text */
46 PIPE_MSG, /* Display message (returned as command string) */
47 PIPE_TYPE_MASK = 7,
48 PIPE_ISQUOTE = 1<<4, /* Is quote action (we have the info, keep it!) */
49 PIPE_NOQUOTE = 1<<5, /* No MIME for quoting */
50 PIPE_ALWAYS = 1<<6, /* Handler shall run even for multi-msg actions */
51 PIPE_ASYNC = 1<<7, /* Should run asynchronously */
52 PIPE_NEEDSTERM = 1<<8, /* Takes over terminal */
53 PIPE_TMPFILE = 1<<9, /* Create temporary file (zero-sized) */
54 PIPE_TMPFILE_FILL = 1<<10, /* Fill it in with the message body content */
55 PIPE_TMPFILE_UNLINK = 1<<11 /* Delete that later again */
58 static sigjmp_buf _send_pipejmp;
60 /* Going for user display, print Part: info string */
61 static void _print_part_info(FILE *obuf, struct mimepart const *mpp,
62 struct ignoretab *doign, int level,
63 struct quoteflt *qf, ui64_t *stats);
65 /* Query possible pipe command for MIME part */
66 static enum pipe_flags _pipecmd(char const **result, struct mimepart const *mpp,
67 bool_t isquote);
69 /* Create a pipe; if mpp is not NULL, place some NAILENV_* environment
70 * variables accordingly */
71 static FILE * _pipefile(enum pipe_flags pipeflags, char const *pipecomm,
72 struct mimepart const *mpp, FILE **qbuf,
73 char const *tmpfile, int term_infd);
75 /* Call mime_write() as approbiate and adjust statistics */
76 SINLINE ssize_t _out(char const *buf, size_t len, FILE *fp,
77 enum conversion convert, enum sendaction action,
78 struct quoteflt *qf, ui64_t *stats, struct str *rest);
80 /* SIGPIPE handler */
81 static void _send_onpipe(int signo);
83 /* Send one part */
84 static int sendpart(struct message *zmp, struct mimepart *ip,
85 FILE *obuf, struct ignoretab *doign,
86 struct quoteflt *qf, enum sendaction action,
87 ui64_t *stats, int level);
89 /* Get a file for an attachment */
90 static FILE * newfile(struct mimepart *ip, bool_t *ispipe);
92 static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
93 struct quoteflt *qf, ui64_t *stats);
95 /* Output a reasonable looking status field */
96 static void statusput(const struct message *mp, FILE *obuf,
97 struct quoteflt *qf, ui64_t *stats);
98 static void xstatusput(const struct message *mp, FILE *obuf,
99 struct quoteflt *qf, ui64_t *stats);
101 static void put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats);
103 static void
104 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
105 struct ignoretab *doign, int level, struct quoteflt *qf, ui64_t *stats)
107 char buf[64];
108 struct str ti = {NULL, 0}, to;
109 struct str const *cpre, *csuf;
110 char const *cp;
111 NYD2_ENTER;
113 #ifdef HAVE_COLOUR
114 cpre = colour_get(COLOURSPEC_PARTINFO);
115 csuf = colour_get(COLOURSPEC_RESET);
116 #else
117 cpre = csuf = NULL;
118 #endif
120 /* Take care of "99.99", i.e., 5 */
121 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
122 cp = "?";
123 if (level || (cp[0] != '1' && cp[1] == '\0'))
124 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
125 if (cpre != NULL)
126 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
127 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
128 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
130 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
131 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
132 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
134 if ((cp = mpp->m_ct_type_usr_ovwr) != NULL)
135 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
136 else
137 cp = mpp->m_ct_type_plain;
138 if ((to.l = strlen(cp)) > 30 && is_asccaseprefix(cp, "application/")) {
139 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
140 size_t i = to.l - fl;
141 char *x = salloc(al + i +1);
143 memcpy(x, "appl../", al);
144 memcpy(x + al, cp + fl, i +1);
145 cp = x;
146 to.l = al + i;
148 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
150 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL) {
151 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
152 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
153 cp = "qu.-pr.";
154 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
157 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_charset) != NULL) {
158 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
159 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
162 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
163 if (csuf != NULL)
164 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
165 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
167 if (is_ign("content-disposition", 19, doign) && mpp->m_filename != NULL &&
168 *mpp->m_filename != '\0') {
169 makeprint(n_str_add_cp(&ti, mpp->m_filename), &to);
170 free(ti.s);
171 to.l = delctrl(to.s, to.l);
173 if (cpre != NULL)
174 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
175 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
176 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
177 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
178 if (csuf != NULL)
179 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
180 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
182 free(to.s);
184 NYD2_LEAVE;
187 static enum pipe_flags
188 _pipecmd(char const **result, struct mimepart const *mpp, bool_t isquote)
190 enum pipe_flags rv;
191 char const *cp;
192 NYD2_ENTER;
194 *result = NULL;
195 rv = isquote ? PIPE_ISQUOTE : PIPE_NULL;
197 /* Do we have any handler for this part? */
198 if ((cp = mime_type_mimepart_handler(mpp)) == NULL) {
200 } else if (cp == MIME_TYPE_HANDLER_TEXT)
201 rv |= PIPE_TEXT;
202 else if (
203 #ifdef HAVE_FILTER_HTML_TAGSOUP
204 cp == MIME_TYPE_HANDLER_HTML ||
205 #endif
206 *cp != '@') {
207 *result = cp;
208 rv |= PIPE_CMD;
209 } else if (*++cp == '\0') {
210 /* Treat as plain text */
211 rv |= PIPE_TEXT;
212 } else {
213 rv |= PIPE_CMD;
214 jnextc:
215 switch (*cp) {
216 case '*': rv |= PIPE_ALWAYS; ++cp; goto jnextc;
217 case '#': rv |= PIPE_NOQUOTE; ++cp; goto jnextc;
218 case '&': rv |= PIPE_ASYNC; ++cp; goto jnextc;
219 case '!': rv |= PIPE_NEEDSTERM;++cp; goto jnextc;
220 case '+':
221 if (rv & PIPE_TMPFILE)
222 rv |= PIPE_TMPFILE_UNLINK;
223 rv |= PIPE_TMPFILE;
224 ++cp;
225 goto jnextc;
226 case '=':
227 rv |= PIPE_TMPFILE_FILL;
228 ++cp;
229 goto jnextc;
230 case '@':
231 ++cp;
232 /* FALLTHRU */
233 default:
234 break;
237 /* Implications */
238 if (rv & PIPE_TMPFILE_FILL)
239 rv |= PIPE_TMPFILE;
241 /* Exceptions */
242 if (rv & PIPE_ISQUOTE) {
243 if (rv & PIPE_NOQUOTE) {
244 rv = PIPE_NULL | PIPE_ISQUOTE;
245 goto jleave;
248 /* Cannot fetch data back from asynchronous process */
249 if (rv & PIPE_ASYNC) {
250 rv = PIPE_NULL | PIPE_ISQUOTE;
251 goto jleave;
254 /* TODO Can't use a "needsterminal" program for quoting */
255 if (rv & PIPE_NEEDSTERM) {
256 rv = PIPE_NULL | PIPE_ISQUOTE;
257 goto jleave;
261 if (rv & PIPE_NEEDSTERM) {
262 if (rv & PIPE_ASYNC) {
263 n_err(_("MIME type handlers: can't use \"needsterminal\" and "
264 "\"x-nail-async\" together\n"));
265 rv = PIPE_NULL;
266 goto jleave;
269 /* needsterminal needs a terminal */
270 if (!(options & OPT_INTERACTIVE)) {
271 rv = PIPE_NULL;
272 goto jleave;
276 if (!(rv & PIPE_ALWAYS) && !(pstate & PS_MSGLIST_DIRECT)) {
277 /* Viewing multiple messages in one go, don't block system */
278 *result = _("[-- Directly address message only to display this --]\n");
279 rv &= ~PIPE_TYPE_MASK;
280 rv |= PIPE_MSG;
281 goto jleave;
284 *result = cp;
286 jleave:
287 NYD2_LEAVE;
288 return rv;
291 static FILE *
292 _pipefile(enum pipe_flags pipeflags, char const *pipecomm,
293 struct mimepart const *mpp, FILE **qbuf, char const *tmpfile, int term_infd)
295 struct str s;
296 char const *env_addon[8], *cp, *sh;
297 FILE *rbuf;
298 NYD_ENTER;
300 rbuf = *qbuf;
302 if (pipeflags & PIPE_ISQUOTE) {
303 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER,
304 0600)) == NULL) {
305 n_perr(_("tmpfile"), 0);
306 *qbuf = rbuf;
310 #ifdef HAVE_FILTER_HTML_TAGSOUP
311 if (pipecomm == MIME_TYPE_HANDLER_HTML) {
312 union {int (*ptf)(void); char const *sh;} u;
314 fflush(*qbuf);
315 if (*qbuf != stdout) /* xxx never? v15: it'll be a filter anyway */
316 fflush(stdout);
318 pipecomm = "Builtin HTML tagsoup filter";
319 u.ptf = &htmlflt_process_main;
320 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
321 goto jerror;
322 goto jleave;
324 #endif
326 /* NAIL_FILENAME */
327 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
328 cp = "";
329 env_addon[0] = str_concat_csvl(&s, NAILENV_FILENAME, "=", cp, NULL)->s;
331 /* NAIL_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
332 * TODO a file wherever he wants! *Do* create a zero-size temporary file
333 * TODO and give *that* path as NAIL_FILENAME_TEMPORARY, clean it up once
334 * TODO the pipe returns? Like this we *can* verify path/name issues! */
335 env_addon[1] = str_concat_csvl(&s, NAILENV_FILENAME_GENERATED, "=",
336 getrandstring(MIN(NAME_MAX / 4, 16)), NULL)->s;
338 /* NAIL_CONTENT{,_EVIDENCE} */
339 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
340 cp = "";
341 env_addon[2] = str_concat_csvl(&s, NAILENV_CONTENT, "=", cp, NULL)->s;
343 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
344 cp = mpp->m_ct_type_usr_ovwr;
345 env_addon[3] = str_concat_csvl(&s, NAILENV_CONTENT_EVIDENCE, "=", cp,
346 NULL)->s;
348 env_addon[4] = str_concat_csvl(&s, NAILENV_TMPDIR, "=", tempdir, NULL)->s;
349 env_addon[5] = str_concat_csvl(&s, "TMPDIR", "=", tempdir, NULL)->s;
351 env_addon[6] = NULL;
353 /* NAIL_FILENAME_TEMPORARY? */
354 if (tmpfile != NULL) {
355 env_addon[6] = str_concat_csvl(&s, NAILENV_FILENAME_TEMPORARY, "=",
356 tmpfile, NULL)->s;
357 env_addon[7] = NULL;
360 if ((sh = ok_vlook(SHELL)) == NULL)
361 sh = XSHELL;
363 if (pipeflags & PIPE_NEEDSTERM) {
364 sigset_t nset;
365 int pid;
367 sigemptyset(&nset);
368 pid = run_command(sh, NULL, term_infd, COMMAND_FD_PASS, "-c",
369 pipecomm, NULL, env_addon);
370 rbuf = (pid < 0) ? NULL : (FILE*)-1;
371 } else {
372 rbuf = Popen(pipecomm, "W", sh, env_addon,
373 (pipeflags & PIPE_ASYNC ? -1 : fileno(*qbuf)));
374 jerror:
375 if (rbuf == NULL)
376 n_err(_("Cannot run MIME type handler \"%s\": %s\n"),
377 pipecomm, strerror(errno));
378 else {
379 fflush(*qbuf);
380 if (*qbuf != stdout)
381 fflush(stdout);
384 #ifdef HAVE_FILTER_HTML_TAGSOUP
385 jleave:
386 #endif
387 NYD_LEAVE;
388 return rbuf;
391 SINLINE ssize_t
392 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
393 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *rest)
395 ssize_t sz = 0, n;
396 int flags;
397 NYD_ENTER;
399 #if 0
400 Well ... it turns out to not work like that since of course a valid
401 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
402 after an empty line has been seen, which cannot be detected that easily
403 right here!
404 ifdef HAVE_DEBUG /* TODO assert legacy */
405 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
406 * TODO other input situations handle RFC 4155 OR, if newly generated,
407 * TODO enforce quoted-printable if there is From_, as "required" by
408 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
409 * TODO if it may happen in this path, we should just treat decryption
410 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
411 if (action == SEND_MBOX || action == SEND_DECRYPT)
412 assert(!is_head(buf, len, TRU1));
413 #else
414 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
415 is_head(buf, len, TRU1)) {
416 putc('>', fp);
417 ++sz;
419 #endif
421 flags = ((int)action & _TD_EOF);
422 action &= ~_TD_EOF;
423 n = mime_write(buf, len, fp,
424 action == SEND_MBOX ? CONV_NONE : convert,
425 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
426 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
427 ? TD_ISPR | TD_ICONV
428 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
429 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
430 qf, rest);
431 if (n < 0)
432 sz = n;
433 else if (n > 0) {
434 sz += n;
435 if (stats != NULL)
436 *stats += sz;
438 NYD_LEAVE;
439 return sz;
442 static void
443 _send_onpipe(int signo)
445 NYD_X; /* Signal handler */
446 UNUSED(signo);
447 siglongjmp(_send_pipejmp, 1);
450 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
451 static int __sendp_sig; /* TODO someday.. */
452 static sighandler_type __sendp_opipe;
453 static void
454 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
456 NYD_X; /* Signal handler */
457 __sendp_sig = sig;
458 siglongjmp(__sendp_actjmp, 1);
461 static sigjmp_buf __sndalter_actjmp; /* TODO someday.. */
462 static int __sndalter_sig; /* TODO someday.. */
463 static void
464 __sndalter_onsig(int sig) /* TODO someday, we won't need it no more */
466 NYD_X; /* Signal handler */
467 __sndalter_sig = sig;
468 siglongjmp(__sndalter_actjmp, 1);
471 static int
472 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
473 struct ignoretab *doign, struct quoteflt *qf,
474 enum sendaction volatile action, ui64_t * volatile stats, int level)
476 int volatile rv = 0;
477 struct str rest;
478 char *line = NULL, *cp, *cp2, *start;
479 enum pipe_flags pipeflags;
480 char const *pipecomm = NULL, *tmpfile = NULL;
481 size_t linesize = 0, linelen, cnt;
482 int volatile term_infd;
483 int dostat, infld = 0, ignoring = 1, isenc, c;
484 struct mimepart *volatile np;
485 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
486 * volatile qbuf = obuf, *origobuf = obuf;
487 enum conversion volatile convert;
488 sighandler_type volatile oldpipe = SIG_DFL;
489 long lineno = 0;
490 NYD_ENTER;
492 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
493 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
494 goto jskip;
496 dostat = 0;
497 if (level == 0) {
498 if (doign != NULL) {
499 if (!is_ign("status", 6, doign))
500 dostat |= 1;
501 if (!is_ign("x-status", 8, doign))
502 dostat |= 2;
503 } else
504 dostat = 3;
506 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
507 rv = -1;
508 goto jleave;
510 cnt = ip->m_size;
512 if (ip->m_mimecontent == MIME_DISCARD)
513 goto jskip;
515 if (!(ip->m_flag & MNOFROM))
516 while (cnt && (c = getc(ibuf)) != EOF) {
517 cnt--;
518 if (c == '\n')
519 break;
521 isenc = 0;
522 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
523 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
524 action == SEND_TOSRCH)
525 ? CONV_FROMHDR : CONV_NONE;
527 /* Work the headers */
528 quoteflt_reset(qf, obuf);
529 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
530 ++lineno;
531 if (line[0] == '\n') {
532 /* If line is blank, we've reached end of headers, so force out
533 * status: field and note that we are no longer in header fields */
534 if (dostat & 1)
535 statusput(zmp, obuf, qf, stats);
536 if (dostat & 2)
537 xstatusput(zmp, obuf, qf, stats);
538 if (doign != allignore)
539 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
540 break;
543 isenc &= ~1;
544 if (infld && blankchar(line[0])) {
545 /* If this line is a continuation (SP / HT) of a previous header
546 * field, determine if the start of the line is a MIME encoded word */
547 if (isenc & 2) {
548 for (cp = line; blankchar(*cp); ++cp);
549 if (cp > line && linelen - PTR2SIZE(cp - line) > 8 &&
550 cp[0] == '=' && cp[1] == '?')
551 isenc |= 1;
553 } else {
554 /* Pick up the header field if we have one */
555 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
557 cp2 = cp;
558 while (spacechar(*cp))
559 ++cp;
560 if (cp[0] != ':' && level == 0 && lineno == 1) {
561 /* Not a header line, force out status: This happens in uucp style
562 * mail where there are no headers at all */
563 if (dostat & 1)
564 statusput(zmp, obuf, qf, stats);
565 if (dostat & 2)
566 xstatusput(zmp, obuf, qf, stats);
567 if (doign != allignore)
568 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
569 break;
572 /* If it is an ignored field and we care about such things, skip it.
573 * Misuse dostat also for another bit xxx use a bitenum + for more */
574 if (ok_blook(keep_content_length))
575 dostat |= 1 << 2;
576 c = *cp2;
577 *cp2 = 0; /* temporarily null terminate */
578 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
579 (action == SEND_MBOX && !(dostat & (1 << 2)) &&
580 (!asccasecmp(line, "content-length") ||
581 !asccasecmp(line, "lines"))))
582 ignoring = 1;
583 else if (!asccasecmp(line, "status")) {
584 /* If field is "status," go compute and print real Status: field */
585 if (dostat & 1) {
586 statusput(zmp, obuf, qf, stats);
587 dostat &= ~1;
588 ignoring = 1;
590 } else if (!asccasecmp(line, "x-status")) {
591 /* If field is "status," go compute and print real Status: field */
592 if (dostat & 2) {
593 xstatusput(zmp, obuf, qf, stats);
594 dostat &= ~2;
595 ignoring = 1;
597 } else {
598 ignoring = 0;
599 /* For colourization we need the complete line, so save it */
600 /* XXX This is all temporary (colour belongs into backend), so
601 * XXX use pipecomm as a temporary storage in the meanwhile */
602 #ifdef HAVE_COLOUR
603 if (colour_table != NULL)
604 pipecomm = savestrbuf(line, PTR2SIZE(cp2 - line));
605 #endif
607 *cp2 = c;
608 dostat &= ~(1 << 2);
609 infld = 1;
612 /* Determine if the end of the line is a MIME encoded word */
613 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
614 * TODO with header follow lines, and it should be up to the backend
615 * TODO what happens and what not, i.e., it doesn't matter wether it's
616 * TODO a MIME-encoded word or not, as long as a single separating space
617 * TODO remains in between lines (the MIME stuff will correctly remove
618 * TODO whitespace in between multiple adjacent encoded words) */
619 isenc &= ~2;
620 if (cnt && (c = getc(ibuf)) != EOF) {
621 if (blankchar(c)) {
622 cp = line + linelen - 1;
623 if (linelen > 0 && *cp == '\n')
624 --cp;
625 while (cp >= line && whitechar(*cp))
626 --cp;
627 if (PTR2SIZE(cp - line > 8) && cp[0] == '=' && cp[-1] == '?')
628 isenc |= 2;
630 ungetc(c, ibuf);
633 if (!ignoring) {
634 size_t len = linelen;
635 start = line;
636 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
637 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
638 action == SEND_TOSRCH) {
639 /* Strip blank characters if two MIME-encoded words follow on
640 * continuing lines */
641 if (isenc & 1)
642 while (len > 0 && blankchar(*start)) {
643 ++start;
644 --len;
646 if (isenc & 2)
647 if (len > 0 && start[len - 1] == '\n')
648 --len;
649 while (len > 0 && blankchar(start[len - 1]))
650 --len;
652 #ifdef HAVE_COLOUR
654 bool_t colour_stripped = FAL0;
655 if (pipecomm != NULL) {
656 colour_put_header(obuf, pipecomm);
657 if (len > 0 && start[len - 1] == '\n') {
658 colour_stripped = TRU1;
659 --len;
662 #endif
663 _out(start, len, obuf, convert, action, qf, stats, NULL);
664 #ifdef HAVE_COLOUR
665 if (pipecomm != NULL) {
666 colour_reset(obuf); /* XXX reset after \n!! */
667 if (colour_stripped)
668 putc('\n', obuf);
671 #endif
672 if (ferror(obuf)) {
673 free(line);
674 rv = -1;
675 goto jleave;
679 quoteflt_flush(qf);
680 free(line);
681 line = NULL;
682 pipecomm = NULL;
684 jskip:
685 pipeflags = PIPE_NULL;
687 switch (ip->m_mimecontent) {
688 case MIME_822:
689 switch (action) {
690 case SEND_TODISP:
691 case SEND_TODISP_ALL:
692 case SEND_QUOTE:
693 case SEND_QUOTE_ALL:
694 if (ok_blook(rfc822_body_from_)) {
695 if (qf->qf_pfix_len > 0) {
696 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
697 qf->qf_pfix_len, obuf);
698 if (i == qf->qf_pfix_len && stats != NULL)
699 *stats += i;
701 put_from_(obuf, ip->m_multipart, stats);
703 /* FALLTHRU */
704 case SEND_TOSRCH:
705 case SEND_DECRYPT:
706 goto jmulti;
707 case SEND_TOFILE:
708 case SEND_TOPIPE:
709 if (ok_blook(rfc822_body_from_))
710 put_from_(obuf, ip->m_multipart, stats);
711 /* FALLTHRU */
712 case SEND_MBOX:
713 case SEND_RFC822:
714 case SEND_SHOW:
715 break;
717 break;
718 case MIME_TEXT_HTML:
719 case MIME_TEXT:
720 case MIME_TEXT_PLAIN:
721 switch (action) {
722 case SEND_TODISP:
723 case SEND_TODISP_ALL:
724 case SEND_QUOTE:
725 case SEND_QUOTE_ALL:
726 switch ((pipeflags = _pipecmd(&pipecomm, ip,
727 (action == SEND_QUOTE || action == SEND_QUOTE_ALL))
728 ) & PIPE_TYPE_MASK) {
729 case PIPE_MSG:
730 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
731 stats, NULL);
732 /* We would print this as plain text, so better force going home */
733 goto jleave;
734 default:
735 break;
737 /* FALLTRHU */
738 default:
739 break;
741 break;
742 case MIME_DISCARD:
743 if (action != SEND_DECRYPT)
744 goto jleave;
745 break;
746 case MIME_PKCS7:
747 if (action != SEND_MBOX && action != SEND_RFC822 &&
748 action != SEND_SHOW && ip->m_multipart != NULL)
749 goto jmulti;
750 /* FALLTHRU */
751 default:
752 switch (action) {
753 case SEND_TODISP:
754 case SEND_TODISP_ALL:
755 case SEND_QUOTE:
756 case SEND_QUOTE_ALL:
757 switch ((pipeflags = _pipecmd(&pipecomm, ip,
758 (action == SEND_QUOTE || action == SEND_QUOTE_ALL))
759 ) & PIPE_TYPE_MASK) {
760 case PIPE_MSG:
761 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
762 stats, NULL);
763 /* We would print this as plain text, so better force going home */
764 goto jleave;
765 case PIPE_CMD:
766 /* FIXME WE NEED TO DO THAT IF WE ARE THE ONLY MAIL
767 * FIXME CONTENT !! */
768 case PIPE_TEXT:
769 break;
770 case PIPE_NULL:
771 if (level == 0 && cnt) {
772 char const *x = _("[-- Binary content --]\n");
773 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
775 goto jleave;
777 break;
778 case SEND_TOFILE:
779 case SEND_TOPIPE:
780 case SEND_TOSRCH:
781 case SEND_DECRYPT:
782 case SEND_MBOX:
783 case SEND_RFC822:
784 case SEND_SHOW:
785 break;
787 break;
788 case MIME_ALTERNATIVE:
789 case MIME_RELATED:
790 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
791 !ok_blook(print_alternatives)) {
792 /* XXX This (a) should not remain (b) should be own fun */
793 struct mpstack {
794 struct mpstack *outer;
795 struct mimepart *mp;
796 } outermost, * volatile curr = &outermost, * volatile mpsp;
797 sighandler_type volatile opsh, oish, ohsh;
798 size_t volatile partcnt = 0/* silence CC */;
799 bool_t volatile neednl = FAL0;
801 curr->outer = NULL;
802 curr->mp = ip;
804 __sndalter_sig = 0;
805 opsh = safe_signal(SIGPIPE, &__sndalter_onsig);
806 oish = safe_signal(SIGINT, &__sndalter_onsig);
807 ohsh = safe_signal(SIGHUP, &__sndalter_onsig);
808 if (sigsetjmp(__sndalter_actjmp, 1)) {
809 rv = -1;
810 goto jalter_unroll;
813 for (np = ip->m_multipart;;) {
814 partcnt = 0;
815 jalter_redo:
816 for (; np != NULL; np = np->m_nextpart) {
817 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
818 if (neednl)
819 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
820 _print_part_info(obuf, np, doign, level, qf, stats);
822 neednl = TRU1;
824 switch (np->m_mimecontent) {
825 case MIME_ALTERNATIVE:
826 case MIME_RELATED:
827 case MIME_MULTI:
828 case MIME_DIGEST:
829 mpsp = ac_alloc(sizeof *mpsp);
830 mpsp->outer = curr;
831 mpsp->mp = np->m_multipart;
832 curr->mp = np;
833 curr = mpsp;
834 np = mpsp->mp;
835 neednl = FAL0;
836 goto jalter_redo;
837 default:
838 switch ((pipeflags = _pipecmd(&pipecomm, np,
839 (action == SEND_QUOTE || action == SEND_QUOTE_ALL))
840 ) & PIPE_TYPE_MASK) {
841 default:
842 pipeflags = PIPE_NULL;
843 continue;
844 case PIPE_TEXT:
845 break;
847 /* FALLTHRU */
848 case MIME_TEXT_PLAIN:
849 ++partcnt;
850 if (action == SEND_QUOTE && partcnt > 1 &&
851 ip->m_mimecontent == MIME_ALTERNATIVE)
852 break;
853 quoteflt_flush(qf);
854 if (action == SEND_QUOTE && partcnt > 1) {
855 struct quoteflt *dummy = quoteflt_dummy();
856 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
857 NULL);
858 quoteflt_flush(dummy);
860 neednl = FAL0;
861 rv = sendpart(zmp, np, obuf, doign, qf, action, stats,
862 level + 1);
863 quoteflt_reset(qf, origobuf);
865 if (rv < 0) {
866 jalter_unroll:
867 for (;; curr = mpsp) {
868 if ((mpsp = curr->outer) == NULL)
869 break;
870 ac_free(curr);
873 break;
877 mpsp = curr->outer;
878 if (mpsp == NULL)
879 break;
880 ac_free(curr);
881 curr = mpsp;
882 np = curr->mp->m_nextpart;
884 safe_signal(SIGHUP, ohsh);
885 safe_signal(SIGINT, oish);
886 safe_signal(SIGPIPE, opsh);
887 if (__sndalter_sig != 0)
888 n_raise(__sndalter_sig);
889 goto jleave;
891 /* FALLTHRU */
892 case MIME_MULTI:
893 case MIME_DIGEST:
894 switch (action) {
895 case SEND_TODISP:
896 case SEND_TODISP_ALL:
897 case SEND_QUOTE:
898 case SEND_QUOTE_ALL:
899 case SEND_TOFILE:
900 case SEND_TOPIPE:
901 case SEND_TOSRCH:
902 case SEND_DECRYPT:
903 jmulti:
904 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
905 ip->m_multipart != NULL &&
906 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
907 ip->m_multipart->m_nextpart == NULL) {
908 char const *x = _("[Missing multipart boundary - use \"show\" "
909 "to display the raw message]\n");
910 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
913 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
914 bool_t ispipe;
916 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
917 continue;
919 ispipe = FAL0;
920 switch (action) {
921 case SEND_TOFILE:
922 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
923 break;
924 stats = NULL;
925 /* TODO Always open multipart on /dev/null, it's a hack to be
926 * TODO able to dive into that structure, and still better
927 * TODO than asking the user for something stupid.
928 * TODO oh, wait, we did ask for a filename for this MIME mail,
929 * TODO and that outer container is useless anyway ;-P */
930 if (np->m_multipart != NULL) {
931 if ((obuf = Fopen("/dev/null", "w")) == NULL)
932 continue;
933 } else if ((obuf = newfile(np, &ispipe)) == NULL)
934 continue;
935 if (!ispipe)
936 break;
937 if (sigsetjmp(_send_pipejmp, 1)) {
938 rv = -1;
939 goto jpipe_close;
941 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
942 break;
943 case SEND_TODISP:
944 case SEND_TODISP_ALL:
945 case SEND_QUOTE_ALL:
946 if (ip->m_mimecontent != MIME_MULTI &&
947 ip->m_mimecontent != MIME_ALTERNATIVE &&
948 ip->m_mimecontent != MIME_RELATED &&
949 ip->m_mimecontent != MIME_DIGEST)
950 break;
951 _print_part_info(obuf, np, doign, level, qf, stats);
952 break;
953 case SEND_MBOX:
954 case SEND_RFC822:
955 case SEND_SHOW:
956 case SEND_TOSRCH:
957 case SEND_QUOTE:
958 case SEND_DECRYPT:
959 case SEND_TOPIPE:
960 break;
963 quoteflt_flush(qf);
964 if (sendpart(zmp, np, obuf, doign, qf, action, stats, level+1) < 0)
965 rv = -1;
966 quoteflt_reset(qf, origobuf);
968 if (action == SEND_QUOTE)
969 break;
970 if (action == SEND_TOFILE && obuf != origobuf) {
971 if (!ispipe)
972 Fclose(obuf);
973 else {
974 jpipe_close:
975 safe_signal(SIGPIPE, SIG_IGN);
976 Pclose(obuf, TRU1);
977 safe_signal(SIGPIPE, oldpipe);
981 goto jleave;
982 case SEND_MBOX:
983 case SEND_RFC822:
984 case SEND_SHOW:
985 break;
989 /* Copy out message body */
990 if (doign == allignore && level == 0) /* skip final blank line */
991 --cnt;
992 switch (ip->m_mime_enc) {
993 case MIMEE_BIN:
994 case MIMEE_7B:
995 case MIMEE_8B:
996 convert = CONV_NONE;
997 break;
998 case MIMEE_QP:
999 convert = CONV_FROMQP;
1000 break;
1001 case MIMEE_B64:
1002 switch (ip->m_mimecontent) {
1003 case MIME_TEXT:
1004 case MIME_TEXT_PLAIN:
1005 case MIME_TEXT_HTML:
1006 convert = CONV_FROMB64_T;
1007 break;
1008 default:
1009 convert = CONV_FROMB64;
1011 break;
1012 default:
1013 convert = CONV_NONE;
1016 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1017 action == SEND_RFC822 || action == SEND_SHOW)
1018 convert = CONV_NONE;
1019 #ifdef HAVE_ICONV
1020 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
1021 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
1022 action == SEND_TOSRCH) &&
1023 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
1024 ip->m_mimecontent == MIME_TEXT_HTML ||
1025 ip->m_mimecontent == MIME_TEXT)) {
1026 char const *tcs = charset_get_lc();
1028 if (iconvd != (iconv_t)-1)
1029 n_iconv_close(iconvd);
1030 /* TODO Since Base64 has an odd 4:3 relation in between input
1031 * TODO and output an input line may end with a partial
1032 * TODO multibyte character; this is no problem at all unless
1033 * TODO we send to the display or whatever, i.e., ensure
1034 * TODO makeprint() or something; to avoid this trap, *force*
1035 * TODO iconv(), in which case this layer will handle leftovers
1036 * TODO correctly */
1037 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
1038 asccasecmp(charset_get_7bit(), ip->m_charset))) {
1039 iconvd = n_iconv_open(tcs, ip->m_charset);
1041 * TODO errors should DEFINETELY not be scrolled away!
1042 * TODO what about an error buffer (think old shsp(1)),
1043 * TODO re-dump errors since last snapshot when the
1044 * TODO command loop enters again? i.e., at least print
1045 * TODO "There were errors ?" before the next prompt,
1046 * TODO so that the user can look at the error buffer?
1048 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
1049 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
1050 /*rv = 1; goto jleave;*/
1054 #endif
1056 tmpfile = NULL;
1057 if ((pipeflags & PIPE_TYPE_MASK) == PIPE_CMD) {
1058 qbuf = obuf;
1060 term_infd = COMMAND_FD_PASS;
1061 if (pipeflags & (PIPE_TMPFILE | PIPE_NEEDSTERM)) {
1062 enum oflags of;
1064 of = OF_RDWR | OF_REGISTER;
1065 if (!(pipeflags & PIPE_TMPFILE)) {
1066 term_infd = 0;
1067 pipeflags |= PIPE_TMPFILE_FILL;
1068 of |= OF_UNLINK;
1069 } else if (pipeflags & PIPE_TMPFILE_UNLINK)
1070 of |= OF_REGISTER_UNLINK;
1072 if ((pbuf = Ftmp((pipeflags & PIPE_TMPFILE ? &cp : NULL),
1073 (pipeflags & PIPE_TMPFILE_FILL ? "mimehdlfill" : "mimehdl"),
1074 of, 0600)) == NULL)
1075 goto jesend;
1077 if (pipeflags & PIPE_TMPFILE) {
1078 tmpfile = savestr(cp);
1079 Ftmp_free(&cp);
1082 if (pipeflags & PIPE_TMPFILE_FILL) {
1083 if (term_infd == 0)
1084 term_infd = fileno(pbuf);
1085 goto jsend;
1089 jpipe_for_real:
1090 pbuf = _pipefile(pipeflags, pipecomm, ip, UNVOLATILE(&qbuf), tmpfile,
1091 term_infd);
1092 if (pbuf == NULL) {
1093 jesend:
1094 pbuf = qbuf = NULL;
1095 rv = -1;
1096 goto jend;
1097 } else if ((pipeflags & PIPE_NEEDSTERM) && pbuf == (FILE*)-1) {
1098 pbuf = qbuf = NULL;
1099 goto jend;
1101 tmpfile = NULL;
1102 action = SEND_TOPIPE;
1103 if (pbuf != qbuf) {
1104 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1105 if (sigsetjmp(_send_pipejmp, 1))
1106 goto jend;
1108 } else {
1109 pipeflags = PIPE_NULL;
1110 pbuf = qbuf = obuf;
1113 jsend:
1115 bool_t volatile eof;
1116 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1117 ui64_t *save_stats = stats;
1119 if (pbuf != origobuf) {
1120 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1121 stats = NULL;
1123 eof = FAL0;
1124 rest.s = NULL;
1125 rest.l = 0;
1127 if (pbuf == qbuf) {
1128 __sendp_sig = 0;
1129 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1130 if (sigsetjmp(__sendp_actjmp, 1)) {
1131 if (rest.s != NULL)
1132 free(rest.s);
1133 free(line);
1134 #ifdef HAVE_ICONV
1135 if (iconvd != (iconv_t)-1)
1136 n_iconv_close(iconvd);
1137 #endif
1138 safe_signal(SIGPIPE, __sendp_opipe);
1139 n_raise(__sendp_sig);
1143 quoteflt_reset(qf, pbuf);
1144 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1145 joutln:
1146 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1147 ferror(pbuf)) {
1148 rv = -1; /* XXX Should bail away?! */
1149 break;
1152 if (!eof && rest.l != 0) {
1153 linelen = 0;
1154 eof = TRU1;
1155 action |= _TD_EOF;
1156 goto joutln;
1158 quoteflt_flush(qf);
1160 if (pipeflags & PIPE_TMPFILE_FILL) {
1161 fflush(pbuf);
1162 really_rewind(pbuf);
1163 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1164 goto jpipe_for_real;
1167 if (pbuf == qbuf)
1168 safe_signal(SIGPIPE, __sendp_opipe);
1170 if (rest.s != NULL)
1171 free(rest.s);
1173 if (pbuf != origobuf) {
1174 qf->qf_pfix_len = save_qf_pfix_len;
1175 stats = save_stats;
1179 jend:
1180 if (line != NULL)
1181 free(line);
1182 if (pbuf != qbuf) {
1183 safe_signal(SIGPIPE, SIG_IGN);
1184 Pclose(pbuf, !(pipeflags & PIPE_ASYNC));
1185 safe_signal(SIGPIPE, oldpipe);
1186 if (qbuf != NULL && qbuf != obuf)
1187 pipecpy(qbuf, obuf, origobuf, qf, stats);
1189 #ifdef HAVE_ICONV
1190 if (iconvd != (iconv_t)-1)
1191 n_iconv_close(iconvd);
1192 #endif
1193 jleave:
1194 NYD_LEAVE;
1195 return rv;
1198 static FILE *
1199 newfile(struct mimepart *ip, bool_t *ispipe)
1201 struct str in, out;
1202 char *f;
1203 FILE *fp;
1204 NYD_ENTER;
1206 f = ip->m_filename;
1207 *ispipe = FAL0;
1209 if (f != NULL && f != (char*)-1) {
1210 in.s = f;
1211 in.l = strlen(f);
1212 makeprint(&in, &out);
1213 out.l = delctrl(out.s, out.l);
1214 f = savestrbuf(out.s, out.l);
1215 free(out.s);
1218 if (options & OPT_INTERACTIVE) {
1219 struct str prompt;
1220 char *f2, *f3;
1222 /* TODO Generic function which asks for filename.
1223 * TODO If the current part is the first textpart the target
1224 * TODO is implicit from outer `write' etc! */
1225 /* I18N: Filename input prompt with file type indication */
1226 str_concat_csvl(&prompt, _("Enter filename for part "),
1227 (ip->m_partstring != NULL) ? ip->m_partstring : _("?"),
1228 _(" ("), ip->m_ct_type_plain, _("): "), NULL);
1229 jgetname:
1230 f2 = n_input_cp_addhist(prompt.s, ((f != (char*)-1 && f != NULL)
1231 ? fexpand_nshell_quote(f) : NULL), TRU1);
1232 if (f2 == NULL || *f2 == '\0') {
1233 if (options & OPT_D_V)
1234 n_err(_("... skipping this\n"));
1235 fp = NULL;
1236 goto jleave;
1237 } else if (*f2 == '|')
1238 /* Pipes are expanded by the shell */
1239 f = f2;
1240 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NSHELL)) == NULL)
1241 /* (Error message written by fexpand()) */
1242 goto jgetname;
1243 else
1244 f = f3;
1246 if (f == NULL || f == (char*)-1) {
1247 fp = NULL;
1248 goto jleave;
1251 if (*f == '|') {
1252 char const *cp;
1253 cp = ok_vlook(SHELL);
1254 if (cp == NULL)
1255 cp = XSHELL;
1256 fp = Popen(f + 1, "w", cp, NULL, 1);
1257 if (!(*ispipe = (fp != NULL)))
1258 n_perr(f, 0);
1259 } else {
1260 if ((fp = Fopen(f, "w")) == NULL)
1261 n_err(_("Cannot open \"%s\"\n"), f);
1263 jleave:
1264 NYD_LEAVE;
1265 return fp;
1268 static void
1269 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1270 ui64_t *stats)
1272 char *line = NULL; /* TODO line pool */
1273 size_t linesize = 0, linelen, cnt;
1274 ssize_t all_sz, sz;
1275 NYD_ENTER;
1277 fflush(pipebuf);
1278 rewind(pipebuf);
1279 cnt = fsize(pipebuf);
1280 all_sz = 0;
1282 quoteflt_reset(qf, outbuf);
1283 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1284 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1285 break;
1286 all_sz += sz;
1288 if ((sz = quoteflt_flush(qf)) > 0)
1289 all_sz += sz;
1290 if (line)
1291 free(line);
1293 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1294 *stats += all_sz;
1295 Fclose(pipebuf);
1296 NYD_LEAVE;
1299 static void
1300 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1301 ui64_t *stats)
1303 char statout[3], *cp = statout;
1304 NYD_ENTER;
1306 if (mp->m_flag & MREAD)
1307 *cp++ = 'R';
1308 if (!(mp->m_flag & MNEW))
1309 *cp++ = 'O';
1310 *cp = 0;
1311 if (statout[0]) {
1312 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1313 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1314 if (i > 0 && stats != NULL)
1315 *stats += i;
1317 NYD_LEAVE;
1320 static void
1321 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1322 ui64_t *stats)
1324 char xstatout[4];
1325 char *xp = xstatout;
1326 NYD_ENTER;
1328 if (mp->m_flag & MFLAGGED)
1329 *xp++ = 'F';
1330 if (mp->m_flag & MANSWERED)
1331 *xp++ = 'A';
1332 if (mp->m_flag & MDRAFTED)
1333 *xp++ = 'T';
1334 *xp = 0;
1335 if (xstatout[0]) {
1336 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1337 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1338 if (i > 0 && stats != NULL)
1339 *stats += i;
1341 NYD_LEAVE;
1344 static void
1345 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1347 char const *froma, *date, *nl;
1348 int i;
1349 NYD_ENTER;
1351 if (ip != NULL && ip->m_from != NULL) {
1352 froma = ip->m_from;
1353 date = fakedate(ip->m_time);
1354 nl = "\n";
1355 } else {
1356 froma = myname;
1357 date = time_current.tc_ctime;
1358 nl = "";
1361 colour_put(fp, COLOURSPEC_FROM_);
1362 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1363 colour_reset(fp);
1364 if (i > 0 && stats != NULL)
1365 *stats += i;
1366 NYD_LEAVE;
1369 FL int
1370 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1371 char const *prefix, enum sendaction action, ui64_t *stats)
1373 struct quoteflt qf;
1374 size_t cnt, sz, i;
1375 FILE *ibuf;
1376 enum mime_parse_flags mpf;
1377 struct mimepart *ip;
1378 int rv = -1, c;
1379 NYD_ENTER;
1381 if (mp == dot && action != SEND_TOSRCH)
1382 pstate |= PS_DID_PRINT_DOT;
1383 if (stats != NULL)
1384 *stats = 0;
1385 quoteflt_init(&qf, prefix);
1387 /* First line is the From_ line, so no headers there to worry about */
1388 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1389 goto jleave;
1391 cnt = mp->m_size;
1392 sz = 0;
1394 struct str const *cpre, *csuf;
1395 #ifdef HAVE_COLOUR
1396 cpre = colour_get(COLOURSPEC_FROM_);
1397 csuf = colour_get(COLOURSPEC_RESET);
1398 #else
1399 cpre = csuf = NULL;
1400 #endif
1401 if (mp->m_flag & MNOFROM) {
1402 if (doign != allignore && doign != fwdignore && action != SEND_RFC822)
1403 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1404 (cpre != NULL ? cpre->s : ""),
1405 (int)qf.qf_pfix_len, (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""),
1406 fakefrom(mp), fakedate(mp->m_time),
1407 (csuf != NULL ? csuf->s : ""));
1408 } else if (doign != allignore && doign != fwdignore &&
1409 action != SEND_RFC822) {
1410 if (qf.qf_pfix_len > 0) {
1411 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1412 if (i != qf.qf_pfix_len)
1413 goto jleave;
1414 sz += i;
1416 #ifdef HAVE_COLOUR
1417 if (cpre != NULL) {
1418 fputs(cpre->s, obuf);
1419 cpre = (struct str const*)0x1;
1421 #endif
1423 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1424 #ifdef HAVE_COLOUR
1425 if (c == '\n' && csuf != NULL) {
1426 cpre = (struct str const*)0x1;
1427 fputs(csuf->s, obuf);
1429 #endif
1430 putc(c, obuf);
1431 ++sz;
1432 --cnt;
1433 if (c == '\n')
1434 break;
1437 #ifdef HAVE_COLOUR
1438 if (csuf != NULL && cpre != (struct str const*)0x1)
1439 fputs(csuf->s, obuf);
1440 #endif
1441 } else {
1442 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1443 --cnt;
1444 if (c == '\n')
1445 break;
1449 if (sz > 0 && stats != NULL)
1450 *stats += sz;
1452 mpf = MIME_PARSE_NONE;
1453 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1454 mpf |= MIME_PARSE_DECRYPT | MIME_PARSE_PARTS;
1455 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1456 goto jleave;
1458 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1459 jleave:
1460 quoteflt_destroy(&qf);
1461 NYD_LEAVE;
1462 return rv;
1465 /* s-it-mode */