nail.1: more on sortability (Rudolf Sykora)..
[s-mailx.git] / send.c
blobc06747ae2b5f2dd621facc3bdcbba3dada5933d3
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 static sigjmp_buf _send_pipejmp;
44 /* Going for user display, print Part: info string */
45 static void _print_part_info(FILE *obuf, struct mimepart const *mpp,
46 struct ignoretab *doign, int level,
47 struct quoteflt *qf, ui64_t *stats);
49 /* Create a pipe; if mpp is not NULL, place some NAILENV_* environment
50 * variables accordingly */
51 static FILE * _pipefile(struct mime_handler *mhp,
52 struct mimepart const *mpp, FILE **qbuf,
53 char const *tmpfile, int term_infd);
55 /* Call mime_write() as approbiate and adjust statistics */
56 SINLINE ssize_t _out(char const *buf, size_t len, FILE *fp,
57 enum conversion convert, enum sendaction action,
58 struct quoteflt *qf, ui64_t *stats, struct str *rest);
60 /* SIGPIPE handler */
61 static void _send_onpipe(int signo);
63 /* Send one part */
64 static int sendpart(struct message *zmp, struct mimepart *ip,
65 FILE *obuf, struct ignoretab *doign,
66 struct quoteflt *qf, enum sendaction action,
67 ui64_t *stats, int level);
69 /* Get a file for an attachment */
70 static FILE * newfile(struct mimepart *ip, bool_t *ispipe);
72 static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
73 struct quoteflt *qf, ui64_t *stats);
75 /* Output a reasonable looking status field */
76 static void statusput(const struct message *mp, FILE *obuf,
77 struct quoteflt *qf, ui64_t *stats);
78 static void xstatusput(const struct message *mp, FILE *obuf,
79 struct quoteflt *qf, ui64_t *stats);
81 static void put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats);
83 static void
84 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
85 struct ignoretab *doign, int level, struct quoteflt *qf, ui64_t *stats)
87 char buf[64];
88 struct str ti = {NULL, 0}, to;
89 struct str const *cpre, *csuf;
90 char const *cp;
91 NYD2_ENTER;
93 #ifdef HAVE_COLOUR
94 cpre = colour_get(COLOURSPEC_PARTINFO);
95 csuf = colour_get(COLOURSPEC_RESET);
96 #else
97 cpre = csuf = NULL;
98 #endif
100 /* Take care of "99.99", i.e., 5 */
101 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
102 cp = "?";
103 if (level || (cp[0] != '1' && cp[1] == '\0'))
104 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
105 if (cpre != NULL)
106 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
107 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
108 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
110 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
111 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
112 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
114 if ((cp = mpp->m_ct_type_usr_ovwr) != NULL)
115 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
116 else
117 cp = mpp->m_ct_type_plain;
118 if ((to.l = strlen(cp)) > 30 && is_asccaseprefix(cp, "application/")) {
119 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
120 size_t i = to.l - fl;
121 char *x = salloc(al + i +1);
123 memcpy(x, "appl../", al);
124 memcpy(x + al, cp + fl, i +1);
125 cp = x;
126 to.l = al + i;
128 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
130 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL) {
131 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
132 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
133 cp = "qu.-pr.";
134 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
137 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_charset) != NULL) {
138 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
139 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
142 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
143 if (csuf != NULL)
144 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
145 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
147 if (is_ign("content-disposition", 19, doign) && mpp->m_filename != NULL &&
148 *mpp->m_filename != '\0') {
149 makeprint(n_str_add_cp(&ti, mpp->m_filename), &to);
150 free(ti.s);
151 to.l = delctrl(to.s, to.l);
153 if (cpre != NULL)
154 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
155 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
156 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
157 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
158 if (csuf != NULL)
159 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
160 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
162 free(to.s);
164 NYD2_LEAVE;
167 static FILE *
168 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
169 char const *tmpfile, int term_infd)
171 struct str s;
172 char const *env_addon[8], *cp, *sh;
173 FILE *rbuf;
174 NYD_ENTER;
176 rbuf = *qbuf;
178 if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
179 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
180 NULL) {
181 n_perr(_("tmpfile"), 0);
182 *qbuf = rbuf;
186 if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
187 union {int (*ptf)(void); char const *sh;} u;
189 fflush(*qbuf);
190 if (*qbuf != stdout) /* xxx never? v15: it'll be a filter anyway */
191 fflush(stdout);
193 u.ptf = mhp->mh_ptf;
194 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
195 goto jerror;
196 goto jleave;
199 /* NAIL_FILENAME */
200 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
201 cp = "";
202 env_addon[0] = str_concat_csvl(&s, NAILENV_FILENAME, "=", cp, NULL)->s;
204 /* NAIL_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
205 * TODO a file wherever he wants! *Do* create a zero-size temporary file
206 * TODO and give *that* path as NAIL_FILENAME_TEMPORARY, clean it up once
207 * TODO the pipe returns? Like this we *can* verify path/name issues! */
208 env_addon[1] = str_concat_csvl(&s, NAILENV_FILENAME_GENERATED, "=",
209 getrandstring(MIN(NAME_MAX / 4, 16)), NULL)->s;
211 /* NAIL_CONTENT{,_EVIDENCE} */
212 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
213 cp = "";
214 env_addon[2] = str_concat_csvl(&s, NAILENV_CONTENT, "=", cp, NULL)->s;
216 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
217 cp = mpp->m_ct_type_usr_ovwr;
218 env_addon[3] = str_concat_csvl(&s, NAILENV_CONTENT_EVIDENCE, "=", cp,
219 NULL)->s;
221 env_addon[4] = str_concat_csvl(&s, NAILENV_TMPDIR, "=", tempdir, NULL)->s;
222 env_addon[5] = str_concat_csvl(&s, "TMPDIR", "=", tempdir, NULL)->s;
224 env_addon[6] = NULL;
226 /* NAIL_FILENAME_TEMPORARY? */
227 if (tmpfile != NULL) {
228 env_addon[6] = str_concat_csvl(&s, NAILENV_FILENAME_TEMPORARY, "=",
229 tmpfile, NULL)->s;
230 env_addon[7] = NULL;
233 if ((sh = ok_vlook(SHELL)) == NULL)
234 sh = XSHELL;
236 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
237 sigset_t nset;
238 int pid;
240 sigemptyset(&nset);
241 pid = run_command(sh, NULL, term_infd, COMMAND_FD_PASS, "-c",
242 mhp->mh_shell_cmd, NULL, env_addon);
243 rbuf = (pid < 0) ? NULL : (FILE*)-1;
244 } else {
245 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
246 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
247 jerror:
248 if (rbuf == NULL)
249 n_err(_("Cannot run MIME type handler \"%s\": %s\n"),
250 mhp->mh_msg, strerror(errno));
251 else {
252 fflush(*qbuf);
253 if (*qbuf != stdout)
254 fflush(stdout);
257 jleave:
258 NYD_LEAVE;
259 return rbuf;
262 SINLINE ssize_t
263 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
264 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *rest)
266 ssize_t sz = 0, n;
267 int flags;
268 NYD_ENTER;
270 #if 0
271 Well ... it turns out to not work like that since of course a valid
272 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
273 after an empty line has been seen, which cannot be detected that easily
274 right here!
275 ifdef HAVE_DEBUG /* TODO assert legacy */
276 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
277 * TODO other input situations handle RFC 4155 OR, if newly generated,
278 * TODO enforce quoted-printable if there is From_, as "required" by
279 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
280 * TODO if it may happen in this path, we should just treat decryption
281 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
282 if (action == SEND_MBOX || action == SEND_DECRYPT)
283 assert(!is_head(buf, len, TRU1));
284 #else
285 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
286 is_head(buf, len, TRU1)) {
287 putc('>', fp);
288 ++sz;
290 #endif
292 flags = ((int)action & _TD_EOF);
293 action &= ~_TD_EOF;
294 n = mime_write(buf, len, fp,
295 action == SEND_MBOX ? CONV_NONE : convert,
296 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
297 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
298 ? TD_ISPR | TD_ICONV
299 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
300 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
301 qf, rest);
302 if (n < 0)
303 sz = n;
304 else if (n > 0) {
305 sz += n;
306 if (stats != NULL)
307 *stats += sz;
309 NYD_LEAVE;
310 return sz;
313 static void
314 _send_onpipe(int signo)
316 NYD_X; /* Signal handler */
317 UNUSED(signo);
318 siglongjmp(_send_pipejmp, 1);
321 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
322 static int __sendp_sig; /* TODO someday.. */
323 static sighandler_type __sendp_opipe;
324 static void
325 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
327 NYD_X; /* Signal handler */
328 __sendp_sig = sig;
329 siglongjmp(__sendp_actjmp, 1);
332 static sigjmp_buf __sndalter_actjmp; /* TODO someday.. */
333 static int __sndalter_sig; /* TODO someday.. */
334 static void
335 __sndalter_onsig(int sig) /* TODO someday, we won't need it no more */
337 NYD_X; /* Signal handler */
338 __sndalter_sig = sig;
339 siglongjmp(__sndalter_actjmp, 1);
342 static int
343 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
344 struct ignoretab *doign, struct quoteflt *qf,
345 enum sendaction volatile action, ui64_t * volatile stats, int level)
347 int volatile rv = 0;
348 struct mime_handler mh;
349 struct str rest;
350 char *line = NULL, *cp, *cp2, *start;
351 char const *tmpfile = NULL;
352 size_t linesize = 0, linelen, cnt;
353 int volatile term_infd;
354 int dostat, infld = 0, ignoring = 1, isenc, c;
355 struct mimepart *volatile np;
356 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
357 * volatile qbuf = obuf, *origobuf = obuf;
358 enum conversion volatile convert;
359 sighandler_type volatile oldpipe = SIG_DFL;
360 long lineno = 0;
361 NYD_ENTER;
363 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
364 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
365 goto jskip;
367 dostat = 0;
368 if (level == 0) {
369 if (doign != NULL) {
370 if (!is_ign("status", 6, doign))
371 dostat |= 1;
372 if (!is_ign("x-status", 8, doign))
373 dostat |= 2;
374 } else
375 dostat = 3;
377 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
378 rv = -1;
379 goto jleave;
381 cnt = ip->m_size;
383 if (ip->m_mimecontent == MIME_DISCARD)
384 goto jskip;
386 if (!(ip->m_flag & MNOFROM))
387 while (cnt && (c = getc(ibuf)) != EOF) {
388 cnt--;
389 if (c == '\n')
390 break;
392 isenc = 0;
393 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
394 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
395 action == SEND_TOSRCH)
396 ? CONV_FROMHDR : CONV_NONE;
398 /* Work the headers */
399 quoteflt_reset(qf, obuf);
400 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
401 ++lineno;
402 if (line[0] == '\n') {
403 /* If line is blank, we've reached end of headers, so force out
404 * status: field and note that we are no longer in header fields */
405 if (dostat & 1)
406 statusput(zmp, obuf, qf, stats);
407 if (dostat & 2)
408 xstatusput(zmp, obuf, qf, stats);
409 if (doign != allignore)
410 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
411 break;
414 isenc &= ~1;
415 if (infld && blankchar(line[0])) {
416 /* If this line is a continuation (SP / HT) of a previous header
417 * field, determine if the start of the line is a MIME encoded word */
418 if (isenc & 2) {
419 for (cp = line; blankchar(*cp); ++cp);
420 if (cp > line && linelen - PTR2SIZE(cp - line) > 8 &&
421 cp[0] == '=' && cp[1] == '?')
422 isenc |= 1;
424 } else {
425 /* Pick up the header field if we have one */
426 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
428 cp2 = cp;
429 while (spacechar(*cp))
430 ++cp;
431 if (cp[0] != ':' && level == 0 && lineno == 1) {
432 /* Not a header line, force out status: This happens in uucp style
433 * mail where there are no headers at all */
434 if (dostat & 1)
435 statusput(zmp, obuf, qf, stats);
436 if (dostat & 2)
437 xstatusput(zmp, obuf, qf, stats);
438 if (doign != allignore)
439 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
440 break;
443 /* If it is an ignored field and we care about such things, skip it.
444 * Misuse dostat also for another bit xxx use a bitenum + for more */
445 if (ok_blook(keep_content_length))
446 dostat |= 1 << 2;
447 c = *cp2;
448 *cp2 = 0; /* temporarily null terminate */
449 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
450 (action == SEND_MBOX && !(dostat & (1 << 2)) &&
451 (!asccasecmp(line, "content-length") ||
452 !asccasecmp(line, "lines"))))
453 ignoring = 1;
454 else if (!asccasecmp(line, "status")) {
455 /* If field is "status," go compute and print real Status: field */
456 if (dostat & 1) {
457 statusput(zmp, obuf, qf, stats);
458 dostat &= ~1;
459 ignoring = 1;
461 } else if (!asccasecmp(line, "x-status")) {
462 /* If field is "status," go compute and print real Status: field */
463 if (dostat & 2) {
464 xstatusput(zmp, obuf, qf, stats);
465 dostat &= ~2;
466 ignoring = 1;
468 } else {
469 ignoring = 0;
470 /* For colourization we need the complete line, so save it */
471 /* XXX This is all temporary (colour belongs into backend), so
472 * XXX use tmpfile as a temporary storage in the meanwhile */
473 #ifdef HAVE_COLOUR
474 if (colour_table != NULL)
475 tmpfile = savestrbuf(line, PTR2SIZE(cp2 - line));
476 #endif
478 *cp2 = c;
479 dostat &= ~(1 << 2);
480 infld = 1;
483 /* Determine if the end of the line is a MIME encoded word */
484 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
485 * TODO with header follow lines, and it should be up to the backend
486 * TODO what happens and what not, i.e., it doesn't matter wether it's
487 * TODO a MIME-encoded word or not, as long as a single separating space
488 * TODO remains in between lines (the MIME stuff will correctly remove
489 * TODO whitespace in between multiple adjacent encoded words) */
490 isenc &= ~2;
491 if (cnt && (c = getc(ibuf)) != EOF) {
492 if (blankchar(c)) {
493 cp = line + linelen - 1;
494 if (linelen > 0 && *cp == '\n')
495 --cp;
496 while (cp >= line && whitechar(*cp))
497 --cp;
498 if (PTR2SIZE(cp - line > 8) && cp[0] == '=' && cp[-1] == '?')
499 isenc |= 2;
501 ungetc(c, ibuf);
504 if (!ignoring) {
505 size_t len = linelen;
506 start = line;
507 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
508 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
509 action == SEND_TOSRCH) {
510 /* Strip blank characters if two MIME-encoded words follow on
511 * continuing lines */
512 if (isenc & 1)
513 while (len > 0 && blankchar(*start)) {
514 ++start;
515 --len;
517 if (isenc & 2)
518 if (len > 0 && start[len - 1] == '\n')
519 --len;
520 while (len > 0 && blankchar(start[len - 1]))
521 --len;
523 #ifdef HAVE_COLOUR
525 bool_t colour_stripped = FAL0;
526 if (tmpfile != NULL) {
527 colour_put_header(obuf, tmpfile);
528 if (len > 0 && start[len - 1] == '\n') {
529 colour_stripped = TRU1;
530 --len;
533 #endif
534 _out(start, len, obuf, convert, action, qf, stats, NULL);
535 #ifdef HAVE_COLOUR
536 if (tmpfile != NULL) {
537 colour_reset(obuf); /* XXX reset after \n!! */
538 if (colour_stripped)
539 putc('\n', obuf);
542 #endif
543 if (ferror(obuf)) {
544 free(line);
545 rv = -1;
546 goto jleave;
550 quoteflt_flush(qf);
551 free(line);
552 line = NULL;
553 tmpfile = NULL;
555 jskip:
556 memset(&mh, 0, sizeof mh);
558 switch (ip->m_mimecontent) {
559 case MIME_822:
560 switch (action) {
561 case SEND_TODISP:
562 case SEND_TODISP_ALL:
563 case SEND_QUOTE:
564 case SEND_QUOTE_ALL:
565 if (ok_blook(rfc822_body_from_)) {
566 if (qf->qf_pfix_len > 0) {
567 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
568 qf->qf_pfix_len, obuf);
569 if (i == qf->qf_pfix_len && stats != NULL)
570 *stats += i;
572 put_from_(obuf, ip->m_multipart, stats);
574 /* FALLTHRU */
575 case SEND_TOSRCH:
576 case SEND_DECRYPT:
577 goto jmulti;
578 case SEND_TOFILE:
579 case SEND_TOPIPE:
580 if (ok_blook(rfc822_body_from_))
581 put_from_(obuf, ip->m_multipart, stats);
582 /* FALLTHRU */
583 case SEND_MBOX:
584 case SEND_RFC822:
585 case SEND_SHOW:
586 break;
588 break;
589 case MIME_TEXT_HTML:
590 case MIME_TEXT:
591 case MIME_TEXT_PLAIN:
592 switch (action) {
593 case SEND_TODISP:
594 case SEND_TODISP_ALL:
595 case SEND_QUOTE:
596 case SEND_QUOTE_ALL:
597 switch (mime_type_handler(&mh, ip, action)) {
598 case MIME_HDL_MSG:
599 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
600 stats, NULL);
601 /* We would print this as plain text, so better force going home */
602 goto jleave;
603 default:
604 break;
606 /* FALLTRHU */
607 default:
608 break;
610 break;
611 case MIME_DISCARD:
612 if (action != SEND_DECRYPT)
613 goto jleave;
614 break;
615 case MIME_PKCS7:
616 if (action != SEND_MBOX && action != SEND_RFC822 &&
617 action != SEND_SHOW && ip->m_multipart != NULL)
618 goto jmulti;
619 /* FALLTHRU */
620 default:
621 switch (action) {
622 case SEND_TODISP:
623 case SEND_TODISP_ALL:
624 case SEND_QUOTE:
625 case SEND_QUOTE_ALL:
626 switch (mime_type_handler(&mh, ip, action)) {
627 case MIME_HDL_MSG:
628 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
629 stats, NULL);
630 /* We would print this as plain text, so better force going home */
631 goto jleave;
632 case MIME_HDL_CMD:
633 /* FIXME WE NEED TO DO THAT IF WE ARE THE ONLY MAIL
634 * FIXME CONTENT !! */
635 case MIME_HDL_TEXT:
636 break;
637 default:
638 case MIME_HDL_NULL:
639 if (level == 0 && cnt) {
640 char const *x = _("[-- Binary content --]\n");
641 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
643 goto jleave;
645 break;
646 case SEND_TOFILE:
647 case SEND_TOPIPE:
648 case SEND_TOSRCH:
649 case SEND_DECRYPT:
650 case SEND_MBOX:
651 case SEND_RFC822:
652 case SEND_SHOW:
653 break;
655 break;
656 case MIME_ALTERNATIVE:
657 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
658 !ok_blook(print_alternatives)) {
659 /* XXX This (a) should not remain (b) should be own fun
660 * TODO (despite the fact that v15 will do this completely differently
661 * TODO by having an action-specific "manager" that will traverse the
662 * TODO parsed MIME tree and decide for each part wether it'll be
663 * TODO displayed or not *before* we walk the tree for doing action */
664 struct mpstack {
665 struct mpstack *outer;
666 struct mimepart *mp;
667 } outermost, * volatile curr, * volatile mpsp;
668 bool_t volatile neednl, hadpart;
669 sighandler_type volatile opsh, oish, ohsh;
671 (curr = &outermost)->outer = NULL;
672 curr->mp = ip;
673 neednl = hadpart = FAL0;
675 __sndalter_sig = 0;
676 opsh = safe_signal(SIGPIPE, &__sndalter_onsig);
677 oish = safe_signal(SIGINT, &__sndalter_onsig);
678 ohsh = safe_signal(SIGHUP, &__sndalter_onsig);
679 if (sigsetjmp(__sndalter_actjmp, 1)) {
680 rv = -1;
681 goto jalter_unroll;
684 for (np = ip->m_multipart;;) {
685 jalter_redo:
686 for (; np != NULL; np = np->m_nextpart) {
687 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
688 if (neednl)
689 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
690 _print_part_info(obuf, np, doign, level, qf, stats);
692 neednl = TRU1;
694 switch (np->m_mimecontent) {
695 case MIME_ALTERNATIVE:
696 case MIME_RELATED:
697 case MIME_MULTI:
698 case MIME_DIGEST:
699 mpsp = salloc(sizeof *mpsp);
700 mpsp->outer = curr;
701 mpsp->mp = np->m_multipart;
702 curr->mp = np;
703 curr = mpsp;
704 np = mpsp->mp;
705 neednl = FAL0;
706 goto jalter_redo;
707 default:
708 if (hadpart)
709 break;
710 switch (mime_type_handler(&mh, np, action)) {
711 default:
712 mh.mh_flags = MIME_HDL_NULL;
713 continue; /* break; break; */
714 case MIME_HDL_PTF:
715 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
716 struct mimepart *x = np;
718 while ((x = x->m_nextpart) != NULL) {
719 struct mime_handler mhx;
721 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
722 mime_type_handler(&mhx, x, action) ==
723 MIME_HDL_TEXT)
724 break;
726 if (x != NULL)
727 continue; /* break; break; */
728 goto jalter_plain;
730 /* FALLTHRU */
731 case MIME_HDL_TEXT:
732 break;
734 /* FALLTHRU */
735 case MIME_TEXT_PLAIN:
736 if (hadpart)
737 break;
738 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
739 struct mimepart *x = np;
741 /* TODO twice TODO, we should dive into /related and
742 * TODO check wether that has rich parts! */
743 while ((x = x->m_nextpart) != NULL) {
744 struct mime_handler mhx;
746 switch (mime_type_handler(&mhx, x, action)) {
747 case MIME_HDL_PTF:
748 break;
749 default:
750 continue;
752 break;
754 if (x != NULL)
755 continue; /* break; break; */
757 jalter_plain:
758 quoteflt_flush(qf);
759 if (action == SEND_QUOTE && hadpart) {
760 struct quoteflt *dummy = quoteflt_dummy();
761 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
762 NULL);
763 quoteflt_flush(dummy);
765 hadpart = TRU1;
766 neednl = FAL0;
767 rv = sendpart(zmp, np, obuf, doign, qf, action, stats,
768 level + 1);
769 quoteflt_reset(qf, origobuf);
771 if (rv < 0)
772 jalter_unroll:
773 curr = &outermost; /* Cause overall loop termination */
774 break;
778 mpsp = curr->outer;
779 if (mpsp == NULL)
780 break;
781 curr = mpsp;
782 np = curr->mp->m_nextpart;
784 safe_signal(SIGHUP, ohsh);
785 safe_signal(SIGINT, oish);
786 safe_signal(SIGPIPE, opsh);
787 if (__sndalter_sig != 0)
788 n_raise(__sndalter_sig);
789 goto jleave;
791 /* FALLTHRU */
792 case MIME_MULTI:
793 case MIME_DIGEST:
794 case MIME_RELATED:
795 switch (action) {
796 case SEND_TODISP:
797 case SEND_TODISP_ALL:
798 case SEND_QUOTE:
799 case SEND_QUOTE_ALL:
800 case SEND_TOFILE:
801 case SEND_TOPIPE:
802 case SEND_TOSRCH:
803 case SEND_DECRYPT:
804 jmulti:
805 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
806 ip->m_multipart != NULL &&
807 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
808 ip->m_multipart->m_nextpart == NULL) {
809 char const *x = _("[Missing multipart boundary - use \"show\" "
810 "to display the raw message]\n");
811 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
814 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
815 bool_t ispipe;
817 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
818 continue;
820 ispipe = FAL0;
821 switch (action) {
822 case SEND_TOFILE:
823 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
824 break;
825 stats = NULL;
826 /* TODO Always open multipart on /dev/null, it's a hack to be
827 * TODO able to dive into that structure, and still better
828 * TODO than asking the user for something stupid.
829 * TODO oh, wait, we did ask for a filename for this MIME mail,
830 * TODO and that outer container is useless anyway ;-P */
831 if (np->m_multipart != NULL) {
832 if ((obuf = Fopen("/dev/null", "w")) == NULL)
833 continue;
834 } else if ((obuf = newfile(np, &ispipe)) == NULL)
835 continue;
836 if (!ispipe)
837 break;
838 if (sigsetjmp(_send_pipejmp, 1)) {
839 rv = -1;
840 goto jpipe_close;
842 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
843 break;
844 case SEND_TODISP:
845 case SEND_TODISP_ALL:
846 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
847 ip->m_mimecontent != MIME_RELATED &&
848 ip->m_mimecontent != MIME_DIGEST &&
849 ip->m_mimecontent != MIME_MULTI)
850 break;
851 _print_part_info(obuf, np, doign, level, qf, stats);
852 break;
853 case SEND_QUOTE:
854 case SEND_QUOTE_ALL:
855 case SEND_MBOX:
856 case SEND_RFC822:
857 case SEND_SHOW:
858 case SEND_TOSRCH:
859 case SEND_DECRYPT:
860 case SEND_TOPIPE:
861 break;
864 quoteflt_flush(qf);
865 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
866 np->m_multipart == NULL && ip->m_parent != NULL) {
867 struct quoteflt *dummy = quoteflt_dummy();
868 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
869 NULL);
870 quoteflt_flush(dummy);
872 if (sendpart(zmp, np, obuf, doign, qf, action, stats, level+1) < 0)
873 rv = -1;
874 quoteflt_reset(qf, origobuf);
876 if (action == SEND_QUOTE) {
877 if (ip->m_mimecontent != MIME_RELATED)
878 break;
880 if (action == SEND_TOFILE && obuf != origobuf) {
881 if (!ispipe)
882 Fclose(obuf);
883 else {
884 jpipe_close:
885 safe_signal(SIGPIPE, SIG_IGN);
886 Pclose(obuf, TRU1);
887 safe_signal(SIGPIPE, oldpipe);
891 goto jleave;
892 case SEND_MBOX:
893 case SEND_RFC822:
894 case SEND_SHOW:
895 break;
897 break;
900 /* Copy out message body */
901 if (doign == allignore && level == 0) /* skip final blank line */
902 --cnt;
903 switch (ip->m_mime_enc) {
904 case MIMEE_BIN:
905 case MIMEE_7B:
906 case MIMEE_8B:
907 convert = CONV_NONE;
908 break;
909 case MIMEE_QP:
910 convert = CONV_FROMQP;
911 break;
912 case MIMEE_B64:
913 switch (ip->m_mimecontent) {
914 case MIME_TEXT:
915 case MIME_TEXT_PLAIN:
916 case MIME_TEXT_HTML:
917 convert = CONV_FROMB64_T;
918 break;
919 default:
920 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
921 case MIME_HDL_TEXT:
922 case MIME_HDL_PTF:
923 convert = CONV_FROMB64_T;
924 break;
925 default:
926 convert = CONV_FROMB64;
927 break;
929 break;
931 break;
932 default:
933 convert = CONV_NONE;
936 if (action == SEND_DECRYPT || action == SEND_MBOX ||
937 action == SEND_RFC822 || action == SEND_SHOW)
938 convert = CONV_NONE;
939 #ifdef HAVE_ICONV
940 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
941 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
942 action == SEND_TOSRCH) &&
943 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
944 ip->m_mimecontent == MIME_TEXT_HTML ||
945 ip->m_mimecontent == MIME_TEXT ||
946 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
947 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
948 char const *tcs = charset_get_lc();
950 if (iconvd != (iconv_t)-1)
951 n_iconv_close(iconvd);
952 /* TODO Since Base64 has an odd 4:3 relation in between input
953 * TODO and output an input line may end with a partial
954 * TODO multibyte character; this is no problem at all unless
955 * TODO we send to the display or whatever, i.e., ensure
956 * TODO makeprint() or something; to avoid this trap, *force*
957 * TODO iconv(), in which case this layer will handle leftovers
958 * TODO correctly */
959 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
960 asccasecmp(charset_get_7bit(), ip->m_charset))) {
961 iconvd = n_iconv_open(tcs, ip->m_charset);
963 * TODO errors should DEFINETELY not be scrolled away!
964 * TODO what about an error buffer (think old shsp(1)),
965 * TODO re-dump errors since last snapshot when the
966 * TODO command loop enters again? i.e., at least print
967 * TODO "There were errors ?" before the next prompt,
968 * TODO so that the user can look at the error buffer?
970 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
971 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
972 /*rv = 1; goto jleave;*/
976 #endif
978 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
979 case MIME_HDL_CMD:
980 case MIME_HDL_PTF:
981 tmpfile = NULL;
982 qbuf = obuf;
984 term_infd = COMMAND_FD_PASS;
985 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
986 enum oflags of;
988 of = OF_RDWR | OF_REGISTER;
989 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
990 term_infd = 0;
991 mh.mh_flags |= MIME_HDL_TMPF_FILL;
992 of |= OF_UNLINK;
993 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
994 of |= OF_REGISTER_UNLINK;
996 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
997 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
998 of)) == NULL)
999 goto jesend;
1001 if (mh.mh_flags & MIME_HDL_TMPF) {
1002 tmpfile = savestr(cp);
1003 Ftmp_free(&cp);
1006 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1007 if (term_infd == 0)
1008 term_infd = fileno(pbuf);
1009 goto jsend;
1013 jpipe_for_real:
1014 pbuf = _pipefile(&mh, ip, UNVOLATILE(&qbuf), tmpfile, term_infd);
1015 if (pbuf == NULL) {
1016 jesend:
1017 pbuf = qbuf = NULL;
1018 rv = -1;
1019 goto jend;
1020 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1021 pbuf = qbuf = NULL;
1022 goto jend;
1024 tmpfile = NULL;
1025 action = SEND_TOPIPE;
1026 if (pbuf != qbuf) {
1027 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1028 if (sigsetjmp(_send_pipejmp, 1))
1029 goto jend;
1031 break;
1033 default:
1034 mh.mh_flags = MIME_HDL_NULL;
1035 pbuf = qbuf = obuf;
1036 break;
1039 jsend:
1041 bool_t volatile eof;
1042 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1043 ui64_t *save_stats = stats;
1045 if (pbuf != origobuf) {
1046 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1047 stats = NULL;
1049 eof = FAL0;
1050 rest.s = NULL;
1051 rest.l = 0;
1053 if (pbuf == qbuf) {
1054 __sendp_sig = 0;
1055 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1056 if (sigsetjmp(__sendp_actjmp, 1)) {
1057 if (rest.s != NULL)
1058 free(rest.s);
1059 free(line);
1060 #ifdef HAVE_ICONV
1061 if (iconvd != (iconv_t)-1)
1062 n_iconv_close(iconvd);
1063 #endif
1064 safe_signal(SIGPIPE, __sendp_opipe);
1065 n_raise(__sendp_sig);
1069 quoteflt_reset(qf, pbuf);
1070 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1071 joutln:
1072 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1073 ferror(pbuf)) {
1074 rv = -1; /* XXX Should bail away?! */
1075 break;
1078 if (!eof && rest.l != 0) {
1079 linelen = 0;
1080 eof = TRU1;
1081 action |= _TD_EOF;
1082 goto joutln;
1084 quoteflt_flush(qf);
1086 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1087 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1088 fflush(pbuf);
1089 really_rewind(pbuf);
1090 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1091 goto jpipe_for_real;
1094 if (pbuf == qbuf)
1095 safe_signal(SIGPIPE, __sendp_opipe);
1097 if (rest.s != NULL)
1098 free(rest.s);
1100 if (pbuf != origobuf) {
1101 qf->qf_pfix_len = save_qf_pfix_len;
1102 stats = save_stats;
1106 jend:
1107 if (line != NULL)
1108 free(line);
1109 if (pbuf != qbuf) {
1110 safe_signal(SIGPIPE, SIG_IGN);
1111 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1112 safe_signal(SIGPIPE, oldpipe);
1113 if (qbuf != NULL && qbuf != obuf)
1114 pipecpy(qbuf, obuf, origobuf, qf, stats);
1116 #ifdef HAVE_ICONV
1117 if (iconvd != (iconv_t)-1)
1118 n_iconv_close(iconvd);
1119 #endif
1120 jleave:
1121 NYD_LEAVE;
1122 return rv;
1125 static FILE *
1126 newfile(struct mimepart *ip, bool_t *ispipe)
1128 struct str in, out;
1129 char *f;
1130 FILE *fp;
1131 NYD_ENTER;
1133 f = ip->m_filename;
1134 *ispipe = FAL0;
1136 if (f != NULL && f != (char*)-1) {
1137 in.s = f;
1138 in.l = strlen(f);
1139 makeprint(&in, &out);
1140 out.l = delctrl(out.s, out.l);
1141 f = savestrbuf(out.s, out.l);
1142 free(out.s);
1145 if (options & OPT_INTERACTIVE) {
1146 struct str prompt;
1147 char *f2, *f3;
1149 /* TODO Generic function which asks for filename.
1150 * TODO If the current part is the first textpart the target
1151 * TODO is implicit from outer `write' etc! */
1152 /* I18N: Filename input prompt with file type indication */
1153 str_concat_csvl(&prompt, _("Enter filename for part "),
1154 (ip->m_partstring != NULL) ? ip->m_partstring : _("?"),
1155 _(" ("), ip->m_ct_type_plain, _("): "), NULL);
1156 jgetname:
1157 f2 = n_input_cp_addhist(prompt.s, ((f != (char*)-1 && f != NULL)
1158 ? fexpand_nshell_quote(f) : NULL), TRU1);
1159 if (f2 == NULL || *f2 == '\0') {
1160 if (options & OPT_D_V)
1161 n_err(_("... skipping this\n"));
1162 fp = NULL;
1163 goto jleave;
1164 } else if (*f2 == '|')
1165 /* Pipes are expanded by the shell */
1166 f = f2;
1167 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NSHELL)) == NULL)
1168 /* (Error message written by fexpand()) */
1169 goto jgetname;
1170 else
1171 f = f3;
1173 if (f == NULL || f == (char*)-1) {
1174 fp = NULL;
1175 goto jleave;
1178 if (*f == '|') {
1179 char const *cp;
1180 cp = ok_vlook(SHELL);
1181 if (cp == NULL)
1182 cp = XSHELL;
1183 fp = Popen(f + 1, "w", cp, NULL, 1);
1184 if (!(*ispipe = (fp != NULL)))
1185 n_perr(f, 0);
1186 } else {
1187 if ((fp = Fopen(f, "w")) == NULL)
1188 n_err(_("Cannot open \"%s\"\n"), f);
1190 jleave:
1191 NYD_LEAVE;
1192 return fp;
1195 static void
1196 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1197 ui64_t *stats)
1199 char *line = NULL; /* TODO line pool */
1200 size_t linesize = 0, linelen, cnt;
1201 ssize_t all_sz, sz;
1202 NYD_ENTER;
1204 fflush(pipebuf);
1205 rewind(pipebuf);
1206 cnt = fsize(pipebuf);
1207 all_sz = 0;
1209 quoteflt_reset(qf, outbuf);
1210 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1211 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1212 break;
1213 all_sz += sz;
1215 if ((sz = quoteflt_flush(qf)) > 0)
1216 all_sz += sz;
1217 if (line)
1218 free(line);
1220 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1221 *stats += all_sz;
1222 Fclose(pipebuf);
1223 NYD_LEAVE;
1226 static void
1227 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1228 ui64_t *stats)
1230 char statout[3], *cp = statout;
1231 NYD_ENTER;
1233 if (mp->m_flag & MREAD)
1234 *cp++ = 'R';
1235 if (!(mp->m_flag & MNEW))
1236 *cp++ = 'O';
1237 *cp = 0;
1238 if (statout[0]) {
1239 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1240 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1241 if (i > 0 && stats != NULL)
1242 *stats += i;
1244 NYD_LEAVE;
1247 static void
1248 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1249 ui64_t *stats)
1251 char xstatout[4];
1252 char *xp = xstatout;
1253 NYD_ENTER;
1255 if (mp->m_flag & MFLAGGED)
1256 *xp++ = 'F';
1257 if (mp->m_flag & MANSWERED)
1258 *xp++ = 'A';
1259 if (mp->m_flag & MDRAFTED)
1260 *xp++ = 'T';
1261 *xp = 0;
1262 if (xstatout[0]) {
1263 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1264 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1265 if (i > 0 && stats != NULL)
1266 *stats += i;
1268 NYD_LEAVE;
1271 static void
1272 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1274 char const *froma, *date, *nl;
1275 int i;
1276 NYD_ENTER;
1278 if (ip != NULL && ip->m_from != NULL) {
1279 froma = ip->m_from;
1280 date = fakedate(ip->m_time);
1281 nl = "\n";
1282 } else {
1283 froma = myname;
1284 date = time_current.tc_ctime;
1285 nl = "";
1288 colour_put(fp, COLOURSPEC_FROM_);
1289 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1290 colour_reset(fp);
1291 if (i > 0 && stats != NULL)
1292 *stats += i;
1293 NYD_LEAVE;
1296 FL int
1297 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1298 char const *prefix, enum sendaction action, ui64_t *stats)
1300 struct quoteflt qf;
1301 size_t cnt, sz, i;
1302 FILE *ibuf;
1303 enum mime_parse_flags mpf;
1304 struct mimepart *ip;
1305 int rv = -1, c;
1306 NYD_ENTER;
1308 if (mp == dot && action != SEND_TOSRCH)
1309 pstate |= PS_DID_PRINT_DOT;
1310 if (stats != NULL)
1311 *stats = 0;
1312 quoteflt_init(&qf, prefix);
1314 /* First line is the From_ line, so no headers there to worry about */
1315 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1316 goto jleave;
1318 cnt = mp->m_size;
1319 sz = 0;
1321 struct str const *cpre, *csuf;
1322 #ifdef HAVE_COLOUR
1323 cpre = colour_get(COLOURSPEC_FROM_);
1324 csuf = colour_get(COLOURSPEC_RESET);
1325 #else
1326 cpre = csuf = NULL;
1327 #endif
1328 if (mp->m_flag & MNOFROM) {
1329 if (doign != allignore && doign != fwdignore && action != SEND_RFC822)
1330 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1331 (cpre != NULL ? cpre->s : ""),
1332 (int)qf.qf_pfix_len, (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""),
1333 fakefrom(mp), fakedate(mp->m_time),
1334 (csuf != NULL ? csuf->s : ""));
1335 } else if (doign != allignore && doign != fwdignore &&
1336 action != SEND_RFC822) {
1337 if (qf.qf_pfix_len > 0) {
1338 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1339 if (i != qf.qf_pfix_len)
1340 goto jleave;
1341 sz += i;
1343 #ifdef HAVE_COLOUR
1344 if (cpre != NULL) {
1345 fputs(cpre->s, obuf);
1346 cpre = (struct str const*)0x1;
1348 #endif
1350 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1351 #ifdef HAVE_COLOUR
1352 if (c == '\n' && csuf != NULL) {
1353 cpre = (struct str const*)0x1;
1354 fputs(csuf->s, obuf);
1356 #endif
1357 putc(c, obuf);
1358 ++sz;
1359 --cnt;
1360 if (c == '\n')
1361 break;
1364 #ifdef HAVE_COLOUR
1365 if (csuf != NULL && cpre != (struct str const*)0x1)
1366 fputs(csuf->s, obuf);
1367 #endif
1368 } else {
1369 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1370 --cnt;
1371 if (c == '\n')
1372 break;
1376 if (sz > 0 && stats != NULL)
1377 *stats += sz;
1379 mpf = MIME_PARSE_NONE;
1380 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1381 mpf |= MIME_PARSE_DECRYPT | MIME_PARSE_PARTS;
1382 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1383 goto jleave;
1385 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1386 jleave:
1387 quoteflt_destroy(&qf);
1388 NYD_LEAVE;
1389 return rv;
1392 /* s-it-mode */