Move string dope from strings.c to the new memory.c..
[s-mailx.git] / send.c
blob9019189f2916b63560fbb67166f563cb25d89d03
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 *tmpname, 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
95 struct n_colour_pen *cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_PARTINFO,
96 NULL);
97 if ((cpre = n_colour_pen_to_str(cpen)) != NULL)
98 csuf = n_colour_reset_to_str();
99 else
100 csuf = NULL;
102 #else
103 cpre = csuf = NULL;
104 #endif
106 /* Take care of "99.99", i.e., 5 */
107 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
108 cp = "?";
109 if (level || (cp[0] != '1' && cp[1] == '\0'))
110 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
111 if (cpre != NULL)
112 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
113 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
114 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
116 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
117 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
118 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
120 if ((cp = mpp->m_ct_type_usr_ovwr) != NULL)
121 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
122 else
123 cp = mpp->m_ct_type_plain;
124 if ((to.l = strlen(cp)) > 30 && is_asccaseprefix(cp, "application/")) {
125 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
126 size_t i = to.l - fl;
127 char *x = salloc(al + i +1);
129 memcpy(x, "appl../", al);
130 memcpy(x + al, cp + fl, i +1);
131 cp = x;
132 to.l = al + i;
134 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
136 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL) {
137 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
138 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
139 cp = "qu.-pr.";
140 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
143 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_charset) != NULL) {
144 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
145 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
148 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
149 if (csuf != NULL)
150 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
151 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
153 if (is_ign("content-disposition", 19, doign) && mpp->m_filename != NULL &&
154 *mpp->m_filename != '\0') {
155 makeprint(n_str_add_cp(&ti, mpp->m_filename), &to);
156 free(ti.s);
157 to.l = delctrl(to.s, to.l);
159 if (cpre != NULL)
160 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
161 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
162 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
163 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
164 if (csuf != NULL)
165 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
166 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
168 free(to.s);
170 NYD2_LEAVE;
173 static FILE *
174 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
175 char const *tmpname, int term_infd)
177 struct str s;
178 char const *env_addon[8], *cp, *sh;
179 FILE *rbuf;
180 NYD_ENTER;
182 rbuf = *qbuf;
184 if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
185 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
186 NULL) {
187 n_perr(_("tmpfile"), 0);
188 *qbuf = rbuf;
192 if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
193 union {int (*ptf)(void); char const *sh;} u;
195 fflush(*qbuf);
196 if (*qbuf != stdout) /* xxx never? v15: it'll be a filter anyway */
197 fflush(stdout);
199 u.ptf = mhp->mh_ptf;
200 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
201 goto jerror;
202 goto jleave;
205 /* NAIL_FILENAME */
206 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
207 cp = "";
208 env_addon[0] = str_concat_csvl(&s, NAILENV_FILENAME, "=", cp, NULL)->s;
210 /* NAIL_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
211 * TODO a file wherever he wants! *Do* create a zero-size temporary file
212 * TODO and give *that* path as NAIL_FILENAME_TEMPORARY, clean it up once
213 * TODO the pipe returns? Like this we *can* verify path/name issues! */
214 env_addon[1] = str_concat_csvl(&s, NAILENV_FILENAME_GENERATED, "=",
215 getrandstring(MIN(NAME_MAX / 4, 16)), NULL)->s;
217 /* NAIL_CONTENT{,_EVIDENCE} */
218 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
219 cp = "";
220 env_addon[2] = str_concat_csvl(&s, NAILENV_CONTENT, "=", cp, NULL)->s;
222 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
223 cp = mpp->m_ct_type_usr_ovwr;
224 env_addon[3] = str_concat_csvl(&s, NAILENV_CONTENT_EVIDENCE, "=", cp,
225 NULL)->s;
227 env_addon[4] = str_concat_csvl(&s, NAILENV_TMPDIR, "=", tempdir, NULL)->s;
228 env_addon[5] = str_concat_csvl(&s, "TMPDIR", "=", tempdir, NULL)->s;
230 env_addon[6] = NULL;
232 /* NAIL_FILENAME_TEMPORARY? */
233 if (tmpname != NULL) {
234 env_addon[6] = str_concat_csvl(&s, NAILENV_FILENAME_TEMPORARY, "=",
235 tmpname, NULL)->s;
236 env_addon[7] = NULL;
239 if ((sh = ok_vlook(SHELL)) == NULL)
240 sh = XSHELL;
242 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
243 sigset_t nset;
244 int pid;
246 sigemptyset(&nset);
247 pid = run_command(sh, NULL, term_infd, COMMAND_FD_PASS, "-c",
248 mhp->mh_shell_cmd, NULL, env_addon);
249 rbuf = (pid < 0) ? NULL : (FILE*)-1;
250 } else {
251 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
252 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
253 jerror:
254 if (rbuf == NULL)
255 n_err(_("Cannot run MIME type handler \"%s\": %s\n"),
256 mhp->mh_msg, strerror(errno));
257 else {
258 fflush(*qbuf);
259 if (*qbuf != stdout)
260 fflush(stdout);
263 jleave:
264 NYD_LEAVE;
265 return rbuf;
268 SINLINE ssize_t
269 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
270 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *rest)
272 ssize_t sz = 0, n;
273 int flags;
274 NYD_ENTER;
276 #if 0
277 Well ... it turns out to not work like that since of course a valid
278 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
279 after an empty line has been seen, which cannot be detected that easily
280 right here!
281 ifdef HAVE_DEBUG /* TODO assert legacy */
282 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
283 * TODO other input situations handle RFC 4155 OR, if newly generated,
284 * TODO enforce quoted-printable if there is From_, as "required" by
285 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
286 * TODO if it may happen in this path, we should just treat decryption
287 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
288 if (action == SEND_MBOX || action == SEND_DECRYPT)
289 assert(!is_head(buf, len, TRU1));
290 #else
291 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
292 is_head(buf, len, TRU1)) {
293 putc('>', fp);
294 ++sz;
296 #endif
298 flags = ((int)action & _TD_EOF);
299 action &= ~_TD_EOF;
300 n = mime_write(buf, len, fp,
301 action == SEND_MBOX ? CONV_NONE : convert,
302 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
303 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
304 ? TD_ISPR | TD_ICONV
305 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
306 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
307 qf, rest);
308 if (n < 0)
309 sz = n;
310 else if (n > 0) {
311 sz += n;
312 if (stats != NULL)
313 *stats += sz;
315 NYD_LEAVE;
316 return sz;
319 static void
320 _send_onpipe(int signo)
322 NYD_X; /* Signal handler */
323 UNUSED(signo);
324 siglongjmp(_send_pipejmp, 1);
327 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
328 static int __sendp_sig; /* TODO someday.. */
329 static sighandler_type __sendp_opipe;
330 static void
331 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
333 NYD_X; /* Signal handler */
334 __sendp_sig = sig;
335 siglongjmp(__sendp_actjmp, 1);
338 static sigjmp_buf __sndalter_actjmp; /* TODO someday.. */
339 static int __sndalter_sig; /* TODO someday.. */
340 static void
341 __sndalter_onsig(int sig) /* TODO someday, we won't need it no more */
343 NYD_X; /* Signal handler */
344 __sndalter_sig = sig;
345 siglongjmp(__sndalter_actjmp, 1);
348 static int
349 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
350 struct ignoretab *doign, struct quoteflt *qf,
351 enum sendaction volatile action, ui64_t * volatile stats, int level)
353 int volatile rv = 0;
354 struct mime_handler mh;
355 struct str rest;
356 char *line = NULL, *cp, *cp2, *start;
357 char const *tmpname = NULL;
358 size_t linesize = 0, linelen, cnt;
359 int volatile term_infd;
360 int dostat, c;
361 struct mimepart *volatile np;
362 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
363 * volatile qbuf = obuf, *origobuf = obuf;
364 enum conversion volatile convert;
365 sighandler_type volatile oldpipe = SIG_DFL;
366 NYD_ENTER;
368 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
369 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
370 goto jskip;
372 dostat = 0;
373 if (level == 0) {
374 if (doign != NULL) {
375 if (!is_ign("status", 6, doign))
376 dostat |= 1;
377 if (!is_ign("x-status", 8, doign))
378 dostat |= 2;
379 } else
380 dostat = 3;
382 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
383 rv = -1;
384 goto jleave;
386 cnt = ip->m_size;
388 if (ip->m_mimecontent == MIME_DISCARD)
389 goto jskip;
391 if (!(ip->m_flag & MNOFROM))
392 while (cnt && (c = getc(ibuf)) != EOF) {
393 cnt--;
394 if (c == '\n')
395 break;
397 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
398 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
399 action == SEND_TOSRCH)
400 ? CONV_FROMHDR : CONV_NONE;
402 /* Work the headers */
403 quoteflt_reset(qf, obuf);
404 /* C99 */{
405 enum {
406 HPS_NONE = 0,
407 HPS_IN_FIELD = 1<<0,
408 HPS_IGNORE = 1<<1,
409 HPS_ISENC_1 = 1<<2,
410 HPS_ISENC_2 = 1<<3
411 } hps = HPS_NONE;
412 size_t lineno = 0;
414 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
415 ++lineno;
416 if (line[0] == '\n') {
417 /* If line is blank, we've reached end of headers, so force out
418 * status: field and note that we are no longer in header fields */
419 if (dostat & 1)
420 statusput(zmp, obuf, qf, stats);
421 if (dostat & 2)
422 xstatusput(zmp, obuf, qf, stats);
423 if (doign != allignore)
424 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
425 break;
428 hps &= ~HPS_ISENC_1;
429 if ((hps & HPS_IN_FIELD) && blankchar(line[0])) {
430 /* If this line is a continuation (SP / HT) of a previous header
431 * field, determine if the start of the line is a MIME encoded word */
432 if (hps & HPS_ISENC_2) {
433 for (cp = line; blankchar(*cp); ++cp)
435 if (cp > line && linelen - PTR2SIZE(cp - line) > 8 &&
436 cp[0] == '=' && cp[1] == '?')
437 hps |= HPS_ISENC_1;
439 } else {
440 /* Pick up the header field if we have one */
441 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
443 cp2 = cp;
444 while (spacechar(*cp))
445 ++cp;
446 if (cp[0] != ':') {
447 if (lineno != 1)
448 n_err(_("Malformed message: headers and body not separated "
449 "(with empty line)\n"));
450 /* Not a header line, force out status: This happens in uucp style
451 * mail where there are no headers at all */
452 if (level == 0 /*&& lineno == 1*/) {
453 if (dostat & 1)
454 statusput(zmp, obuf, qf, stats);
455 if (dostat & 2)
456 xstatusput(zmp, obuf, qf, stats);
458 if (doign != allignore)
459 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
460 break;
463 /* If it is an ignored field and we care about such things, skip it.
464 * Misuse dostat also for another bit xxx use a bitenum + for more */
465 if (ok_blook(keep_content_length))
466 dostat |= 1 << 2;
467 c = *cp2;
468 *cp2 = 0; /* temporarily null terminate */
469 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
470 (action == SEND_MBOX && !(dostat & (1 << 2)) &&
471 (!asccasecmp(line, "content-length") ||
472 !asccasecmp(line, "lines"))))
473 hps |= HPS_IGNORE;
474 else if (!asccasecmp(line, "status")) {
475 /* If field is "status," go compute and print real Status: field */
476 if (dostat & 1) {
477 statusput(zmp, obuf, qf, stats);
478 dostat &= ~1;
479 hps |= HPS_IGNORE;
481 } else if (!asccasecmp(line, "x-status")) {
482 /* If field is "status," go compute and print real Status: field */
483 if (dostat & 2) {
484 xstatusput(zmp, obuf, qf, stats);
485 dostat &= ~2;
486 hps |= HPS_IGNORE;
488 } else {
489 hps &= ~HPS_IGNORE;
490 /* For colourization we need the complete line, so save it */
491 /* XXX This is all temporary (colour belongs into backend), so
492 * XXX use tmpname as a temporary storage in the meanwhile */
493 #ifdef HAVE_COLOUR
494 if (pstate & PS_COLOUR_ACTIVE)
495 tmpname = savestrbuf(line, PTR2SIZE(cp2 - line));
496 #endif
498 *cp2 = c;
499 dostat &= ~(1 << 2);
500 hps |= HPS_IN_FIELD;
503 /* Determine if the end of the line is a MIME encoded word */
504 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
505 * TODO with header follow lines, and it should be up to the backend
506 * TODO what happens and what not, i.e., it doesn't matter wether it's
507 * TODO a MIME-encoded word or not, as long as a single separating space
508 * TODO remains in between lines (the MIME stuff will correctly remove
509 * TODO whitespace in between multiple adjacent encoded words) */
510 hps &= ~HPS_ISENC_2;
511 if (cnt && (c = getc(ibuf)) != EOF) {
512 if (blankchar(c)) {
513 cp = line + linelen - 1;
514 if (linelen > 0 && *cp == '\n')
515 --cp;
516 while (cp >= line && whitechar(*cp))
517 --cp;
518 if (PTR2SIZE(cp - line > 8) && cp[0] == '=' && cp[-1] == '?')
519 hps |= HPS_ISENC_2;
521 ungetc(c, ibuf);
524 if (!(hps & HPS_IGNORE)) {
525 size_t len = linelen;
526 start = line;
527 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
528 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
529 action == SEND_TOSRCH) {
530 /* Strip blank characters if two MIME-encoded words follow on
531 * continuing lines */
532 if (hps & HPS_ISENC_1)
533 while (len > 0 && blankchar(*start)) {
534 ++start;
535 --len;
537 if (hps & HPS_ISENC_2)
538 if (len > 0 && start[len - 1] == '\n')
539 --len;
540 while (len > 0 && blankchar(start[len - 1]))
541 --len;
543 #ifdef HAVE_COLOUR
545 bool_t colour_stripped = FAL0;
546 if (tmpname != NULL) {
547 n_colour_put(obuf, n_COLOUR_ID_VIEW_HEADER, tmpname);
548 if (len > 0 && start[len - 1] == '\n') {
549 colour_stripped = TRU1;
550 --len;
553 #endif
554 _out(start, len, obuf, convert, action, qf, stats, NULL);
555 #ifdef HAVE_COLOUR
556 if (tmpname != NULL) {
557 n_colour_reset(obuf);
558 if (colour_stripped)
559 putc('\n', obuf);
562 #endif
563 if (ferror(obuf)) {
564 free(line);
565 rv = -1;
566 goto jleave;
570 } /* C99 */
571 quoteflt_flush(qf);
572 free(line);
573 line = NULL;
574 tmpname = NULL;
576 jskip:
577 memset(&mh, 0, sizeof mh);
579 switch (ip->m_mimecontent) {
580 case MIME_822:
581 switch (action) {
582 case SEND_TODISP:
583 case SEND_TODISP_ALL:
584 case SEND_QUOTE:
585 case SEND_QUOTE_ALL:
586 if (ok_blook(rfc822_body_from_)) {
587 if (qf->qf_pfix_len > 0) {
588 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
589 qf->qf_pfix_len, obuf);
590 if (i == qf->qf_pfix_len && stats != NULL)
591 *stats += i;
593 put_from_(obuf, ip->m_multipart, stats);
595 /* FALLTHRU */
596 case SEND_TOSRCH:
597 case SEND_DECRYPT:
598 goto jmulti;
599 case SEND_TOFILE:
600 case SEND_TOPIPE:
601 if (ok_blook(rfc822_body_from_))
602 put_from_(obuf, ip->m_multipart, stats);
603 /* FALLTHRU */
604 case SEND_MBOX:
605 case SEND_RFC822:
606 case SEND_SHOW:
607 break;
609 break;
610 case MIME_TEXT_HTML:
611 case MIME_TEXT:
612 case MIME_TEXT_PLAIN:
613 switch (action) {
614 case SEND_TODISP:
615 case SEND_TODISP_ALL:
616 case SEND_QUOTE:
617 case SEND_QUOTE_ALL:
618 switch (mime_type_handler(&mh, ip, action)) {
619 case MIME_HDL_MSG:
620 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
621 stats, NULL);
622 /* We would print this as plain text, so better force going home */
623 goto jleave;
624 default:
625 break;
627 /* FALLTRHU */
628 default:
629 break;
631 break;
632 case MIME_DISCARD:
633 if (action != SEND_DECRYPT)
634 goto jleave;
635 break;
636 case MIME_PKCS7:
637 if (action != SEND_MBOX && action != SEND_RFC822 &&
638 action != SEND_SHOW && ip->m_multipart != NULL)
639 goto jmulti;
640 /* FALLTHRU */
641 default:
642 switch (action) {
643 case SEND_TODISP:
644 case SEND_TODISP_ALL:
645 case SEND_QUOTE:
646 case SEND_QUOTE_ALL:
647 switch (mime_type_handler(&mh, ip, action)) {
648 case MIME_HDL_MSG:
649 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
650 stats, NULL);
651 /* We would print this as plain text, so better force going home */
652 goto jleave;
653 case MIME_HDL_CMD:
654 /* FIXME WE NEED TO DO THAT IF WE ARE THE ONLY MAIL
655 * FIXME CONTENT !! */
656 case MIME_HDL_TEXT:
657 break;
658 default:
659 case MIME_HDL_NULL:
660 if (level == 0 && cnt) {
661 char const *x = _("[-- Binary content --]\n");
662 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
664 goto jleave;
666 break;
667 case SEND_TOFILE:
668 case SEND_TOPIPE:
669 case SEND_TOSRCH:
670 case SEND_DECRYPT:
671 case SEND_MBOX:
672 case SEND_RFC822:
673 case SEND_SHOW:
674 break;
676 break;
677 case MIME_ALTERNATIVE:
678 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
679 !ok_blook(print_alternatives)) {
680 /* XXX This (a) should not remain (b) should be own fun
681 * TODO (despite the fact that v15 will do this completely differently
682 * TODO by having an action-specific "manager" that will traverse the
683 * TODO parsed MIME tree and decide for each part wether it'll be
684 * TODO displayed or not *before* we walk the tree for doing action */
685 struct mpstack {
686 struct mpstack *outer;
687 struct mimepart *mp;
688 } outermost, * volatile curr, * volatile mpsp;
689 bool_t volatile neednl, hadpart;
690 sighandler_type volatile opsh, oish, ohsh;
692 (curr = &outermost)->outer = NULL;
693 curr->mp = ip;
694 neednl = hadpart = FAL0;
696 __sndalter_sig = 0;
697 opsh = safe_signal(SIGPIPE, &__sndalter_onsig);
698 oish = safe_signal(SIGINT, &__sndalter_onsig);
699 ohsh = safe_signal(SIGHUP, &__sndalter_onsig);
700 if (sigsetjmp(__sndalter_actjmp, 1)) {
701 rv = -1;
702 goto jalter_unroll;
705 for (np = ip->m_multipart;;) {
706 jalter_redo:
707 for (; np != NULL; np = np->m_nextpart) {
708 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
709 if (neednl)
710 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
711 _print_part_info(obuf, np, doign, level, qf, stats);
713 neednl = TRU1;
715 switch (np->m_mimecontent) {
716 case MIME_ALTERNATIVE:
717 case MIME_RELATED:
718 case MIME_MULTI:
719 case MIME_DIGEST:
720 mpsp = salloc(sizeof *mpsp);
721 mpsp->outer = curr;
722 mpsp->mp = np->m_multipart;
723 curr->mp = np;
724 curr = mpsp;
725 np = mpsp->mp;
726 neednl = FAL0;
727 goto jalter_redo;
728 default:
729 if (hadpart)
730 break;
731 switch (mime_type_handler(&mh, np, action)) {
732 default:
733 mh.mh_flags = MIME_HDL_NULL;
734 continue; /* break; break; */
735 case MIME_HDL_PTF:
736 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
737 struct mimepart *x = np;
739 while ((x = x->m_nextpart) != NULL) {
740 struct mime_handler mhx;
742 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
743 mime_type_handler(&mhx, x, action) ==
744 MIME_HDL_TEXT)
745 break;
747 if (x != NULL)
748 continue; /* break; break; */
749 goto jalter_plain;
751 /* FALLTHRU */
752 case MIME_HDL_TEXT:
753 break;
755 /* FALLTHRU */
756 case MIME_TEXT_PLAIN:
757 if (hadpart)
758 break;
759 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
760 struct mimepart *x = np;
762 /* TODO twice TODO, we should dive into /related and
763 * TODO check wether that has rich parts! */
764 while ((x = x->m_nextpart) != NULL) {
765 struct mime_handler mhx;
767 switch (mime_type_handler(&mhx, x, action)) {
768 case MIME_HDL_PTF:
769 break;
770 default:
771 continue;
773 break;
775 if (x != NULL)
776 continue; /* break; break; */
778 jalter_plain:
779 quoteflt_flush(qf);
780 if (action == SEND_QUOTE && hadpart) {
781 struct quoteflt *dummy = quoteflt_dummy();
782 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
783 NULL);
784 quoteflt_flush(dummy);
786 hadpart = TRU1;
787 neednl = FAL0;
788 rv = sendpart(zmp, np, obuf, doign, qf, action, stats,
789 level + 1);
790 quoteflt_reset(qf, origobuf);
792 if (rv < 0)
793 jalter_unroll:
794 curr = &outermost; /* Cause overall loop termination */
795 break;
799 mpsp = curr->outer;
800 if (mpsp == NULL)
801 break;
802 curr = mpsp;
803 np = curr->mp->m_nextpart;
805 safe_signal(SIGHUP, ohsh);
806 safe_signal(SIGINT, oish);
807 safe_signal(SIGPIPE, opsh);
808 if (__sndalter_sig != 0)
809 n_raise(__sndalter_sig);
810 goto jleave;
812 /* FALLTHRU */
813 case MIME_MULTI:
814 case MIME_DIGEST:
815 case MIME_RELATED:
816 switch (action) {
817 case SEND_TODISP:
818 case SEND_TODISP_ALL:
819 case SEND_QUOTE:
820 case SEND_QUOTE_ALL:
821 case SEND_TOFILE:
822 case SEND_TOPIPE:
823 case SEND_TOSRCH:
824 case SEND_DECRYPT:
825 jmulti:
826 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
827 ip->m_multipart != NULL &&
828 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
829 ip->m_multipart->m_nextpart == NULL) {
830 char const *x = _("[Missing multipart boundary - use \"show\" "
831 "to display the raw message]\n");
832 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
835 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
836 bool_t ispipe;
838 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
839 continue;
841 ispipe = FAL0;
842 switch (action) {
843 case SEND_TOFILE:
844 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
845 break;
846 stats = NULL;
847 /* TODO Always open multipart on /dev/null, it's a hack to be
848 * TODO able to dive into that structure, and still better
849 * TODO than asking the user for something stupid.
850 * TODO oh, wait, we did ask for a filename for this MIME mail,
851 * TODO and that outer container is useless anyway ;-P */
852 if (np->m_multipart != NULL) {
853 if ((obuf = Fopen("/dev/null", "w")) == NULL)
854 continue;
855 } else if ((obuf = newfile(np, &ispipe)) == NULL)
856 continue;
857 if (!ispipe)
858 break;
859 if (sigsetjmp(_send_pipejmp, 1)) {
860 rv = -1;
861 goto jpipe_close;
863 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
864 break;
865 case SEND_TODISP:
866 case SEND_TODISP_ALL:
867 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
868 ip->m_mimecontent != MIME_RELATED &&
869 ip->m_mimecontent != MIME_DIGEST &&
870 ip->m_mimecontent != MIME_MULTI)
871 break;
872 _print_part_info(obuf, np, doign, level, qf, stats);
873 break;
874 case SEND_QUOTE:
875 case SEND_QUOTE_ALL:
876 case SEND_MBOX:
877 case SEND_RFC822:
878 case SEND_SHOW:
879 case SEND_TOSRCH:
880 case SEND_DECRYPT:
881 case SEND_TOPIPE:
882 break;
885 quoteflt_flush(qf);
886 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
887 np->m_multipart == NULL && ip->m_parent != NULL) {
888 struct quoteflt *dummy = quoteflt_dummy();
889 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
890 NULL);
891 quoteflt_flush(dummy);
893 if (sendpart(zmp, np, obuf, doign, qf, action, stats, level+1) < 0)
894 rv = -1;
895 quoteflt_reset(qf, origobuf);
897 if (action == SEND_QUOTE) {
898 if (ip->m_mimecontent != MIME_RELATED)
899 break;
901 if (action == SEND_TOFILE && obuf != origobuf) {
902 if (!ispipe)
903 Fclose(obuf);
904 else {
905 jpipe_close:
906 safe_signal(SIGPIPE, SIG_IGN);
907 Pclose(obuf, TRU1);
908 safe_signal(SIGPIPE, oldpipe);
912 goto jleave;
913 case SEND_MBOX:
914 case SEND_RFC822:
915 case SEND_SHOW:
916 break;
918 break;
921 /* Copy out message body */
922 if (doign == allignore && level == 0) /* skip final blank line */
923 --cnt;
924 switch (ip->m_mime_enc) {
925 case MIMEE_BIN:
926 case MIMEE_7B:
927 case MIMEE_8B:
928 convert = CONV_NONE;
929 break;
930 case MIMEE_QP:
931 convert = CONV_FROMQP;
932 break;
933 case MIMEE_B64:
934 switch (ip->m_mimecontent) {
935 case MIME_TEXT:
936 case MIME_TEXT_PLAIN:
937 case MIME_TEXT_HTML:
938 convert = CONV_FROMB64_T;
939 break;
940 default:
941 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
942 case MIME_HDL_TEXT:
943 case MIME_HDL_PTF:
944 convert = CONV_FROMB64_T;
945 break;
946 default:
947 convert = CONV_FROMB64;
948 break;
950 break;
952 break;
953 default:
954 convert = CONV_NONE;
957 if (action == SEND_DECRYPT || action == SEND_MBOX ||
958 action == SEND_RFC822 || action == SEND_SHOW)
959 convert = CONV_NONE;
960 #ifdef HAVE_ICONV
961 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
962 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
963 action == SEND_TOSRCH) &&
964 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
965 ip->m_mimecontent == MIME_TEXT_HTML ||
966 ip->m_mimecontent == MIME_TEXT ||
967 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
968 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
969 char const *tcs = charset_get_lc();
971 if (iconvd != (iconv_t)-1)
972 n_iconv_close(iconvd);
973 /* TODO Since Base64 has an odd 4:3 relation in between input
974 * TODO and output an input line may end with a partial
975 * TODO multibyte character; this is no problem at all unless
976 * TODO we send to the display or whatever, i.e., ensure
977 * TODO makeprint() or something; to avoid this trap, *force*
978 * TODO iconv(), in which case this layer will handle leftovers
979 * TODO correctly */
980 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
981 asccasecmp(charset_get_7bit(), ip->m_charset))) {
982 iconvd = n_iconv_open(tcs, ip->m_charset);
984 * TODO errors should DEFINETELY not be scrolled away!
985 * TODO what about an error buffer (think old shsp(1)),
986 * TODO re-dump errors since last snapshot when the
987 * TODO command loop enters again? i.e., at least print
988 * TODO "There were errors ?" before the next prompt,
989 * TODO so that the user can look at the error buffer?
991 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
992 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
993 /*rv = 1; goto jleave;*/
997 #endif
999 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1000 case MIME_HDL_CMD:
1001 case MIME_HDL_PTF:
1002 tmpname = NULL;
1003 qbuf = obuf;
1005 term_infd = COMMAND_FD_PASS;
1006 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
1007 enum oflags of;
1009 of = OF_RDWR | OF_REGISTER;
1010 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
1011 term_infd = 0;
1012 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1013 of |= OF_UNLINK;
1014 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
1015 of |= OF_REGISTER_UNLINK;
1017 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
1018 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
1019 of)) == NULL)
1020 goto jesend;
1022 if (mh.mh_flags & MIME_HDL_TMPF) {
1023 tmpname = savestr(cp);
1024 Ftmp_free(&cp);
1027 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1028 if (term_infd == 0)
1029 term_infd = fileno(pbuf);
1030 goto jsend;
1034 jpipe_for_real:
1035 pbuf = _pipefile(&mh, ip, UNVOLATILE(&qbuf), tmpname, term_infd);
1036 if (pbuf == NULL) {
1037 jesend:
1038 pbuf = qbuf = NULL;
1039 rv = -1;
1040 goto jend;
1041 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1042 pbuf = qbuf = NULL;
1043 goto jend;
1045 tmpname = NULL;
1046 action = SEND_TOPIPE;
1047 if (pbuf != qbuf) {
1048 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1049 if (sigsetjmp(_send_pipejmp, 1))
1050 goto jend;
1052 break;
1054 default:
1055 mh.mh_flags = MIME_HDL_NULL;
1056 pbuf = qbuf = obuf;
1057 break;
1060 jsend:
1062 bool_t volatile eof;
1063 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1064 ui64_t *save_stats = stats;
1066 if (pbuf != origobuf) {
1067 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1068 stats = NULL;
1070 eof = FAL0;
1071 rest.s = NULL;
1072 rest.l = 0;
1074 if (pbuf == qbuf) {
1075 __sendp_sig = 0;
1076 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1077 if (sigsetjmp(__sendp_actjmp, 1)) {
1078 if (rest.s != NULL)
1079 free(rest.s);
1080 free(line);
1081 #ifdef HAVE_ICONV
1082 if (iconvd != (iconv_t)-1)
1083 n_iconv_close(iconvd);
1084 #endif
1085 safe_signal(SIGPIPE, __sendp_opipe);
1086 n_raise(__sendp_sig);
1090 quoteflt_reset(qf, pbuf);
1091 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1092 joutln:
1093 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1094 ferror(pbuf)) {
1095 rv = -1; /* XXX Should bail away?! */
1096 break;
1099 if (!eof && rest.l != 0) {
1100 linelen = 0;
1101 eof = TRU1;
1102 action |= _TD_EOF;
1103 goto joutln;
1105 quoteflt_flush(qf);
1107 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1108 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1109 fflush(pbuf);
1110 really_rewind(pbuf);
1111 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1112 goto jpipe_for_real;
1115 if (pbuf == qbuf)
1116 safe_signal(SIGPIPE, __sendp_opipe);
1118 if (rest.s != NULL)
1119 free(rest.s);
1121 if (pbuf != origobuf) {
1122 qf->qf_pfix_len = save_qf_pfix_len;
1123 stats = save_stats;
1127 jend:
1128 if (line != NULL)
1129 free(line);
1130 if (pbuf != qbuf) {
1131 safe_signal(SIGPIPE, SIG_IGN);
1132 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1133 safe_signal(SIGPIPE, oldpipe);
1134 if (qbuf != NULL && qbuf != obuf)
1135 pipecpy(qbuf, obuf, origobuf, qf, stats);
1137 #ifdef HAVE_ICONV
1138 if (iconvd != (iconv_t)-1)
1139 n_iconv_close(iconvd);
1140 #endif
1141 jleave:
1142 NYD_LEAVE;
1143 return rv;
1146 static FILE *
1147 newfile(struct mimepart *ip, bool_t *ispipe)
1149 struct str in, out;
1150 char *f;
1151 FILE *fp;
1152 NYD_ENTER;
1154 f = ip->m_filename;
1155 *ispipe = FAL0;
1157 if (f != NULL && f != (char*)-1) {
1158 in.s = f;
1159 in.l = strlen(f);
1160 makeprint(&in, &out);
1161 out.l = delctrl(out.s, out.l);
1162 f = savestrbuf(out.s, out.l);
1163 free(out.s);
1166 if (options & OPT_INTERACTIVE) {
1167 struct str prompt;
1168 char *f2, *f3;
1170 /* TODO Generic function which asks for filename.
1171 * TODO If the current part is the first textpart the target
1172 * TODO is implicit from outer `write' etc! */
1173 /* I18N: Filename input prompt with file type indication */
1174 str_concat_csvl(&prompt, _("Enter filename for part "),
1175 (ip->m_partstring != NULL) ? ip->m_partstring : _("?"),
1176 _(" ("), ip->m_ct_type_plain, _("): "), NULL);
1177 jgetname:
1178 f2 = n_input_cp_addhist(prompt.s, ((f != (char*)-1 && f != NULL)
1179 ? fexpand_nshell_quote(f) : NULL), TRU1);
1180 if(f2 != NULL)
1181 while(spacechar(*f2))
1182 ++f2;
1183 if (f2 == NULL || *f2 == '\0') {
1184 if (options & OPT_D_V)
1185 n_err(_("... skipping this\n"));
1186 fp = NULL;
1187 goto jleave;
1190 if (*f2 == '|')
1191 /* Pipes are expanded by the shell */
1192 f = f2;
1193 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NSHELL)) == NULL)
1194 /* (Error message written by fexpand()) */
1195 goto jgetname;
1196 else
1197 f = f3;
1199 if (f == NULL || f == (char*)-1) {
1200 fp = NULL;
1201 goto jleave;
1204 if (*f == '|') {
1205 char const *cp;
1207 cp = ok_vlook(SHELL);
1208 if (cp == NULL)
1209 cp = XSHELL;
1210 fp = Popen(f + 1, "w", cp, NULL, 1);
1211 if (!(*ispipe = (fp != NULL)))
1212 n_perr(f, 0);
1213 } else {
1214 if ((fp = Fopen(f, "w")) == NULL)
1215 n_err(_("Cannot open \"%s\"\n"), f);
1217 jleave:
1218 NYD_LEAVE;
1219 return fp;
1222 static void
1223 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1224 ui64_t *stats)
1226 char *line = NULL; /* TODO line pool */
1227 size_t linesize = 0, linelen, cnt;
1228 ssize_t all_sz, sz;
1229 NYD_ENTER;
1231 fflush(pipebuf);
1232 rewind(pipebuf);
1233 cnt = fsize(pipebuf);
1234 all_sz = 0;
1236 quoteflt_reset(qf, outbuf);
1237 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1238 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1239 break;
1240 all_sz += sz;
1242 if ((sz = quoteflt_flush(qf)) > 0)
1243 all_sz += sz;
1244 if (line)
1245 free(line);
1247 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1248 *stats += all_sz;
1249 Fclose(pipebuf);
1250 NYD_LEAVE;
1253 static void
1254 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1255 ui64_t *stats)
1257 char statout[3], *cp = statout;
1258 NYD_ENTER;
1260 if (mp->m_flag & MREAD)
1261 *cp++ = 'R';
1262 if (!(mp->m_flag & MNEW))
1263 *cp++ = 'O';
1264 *cp = 0;
1265 if (statout[0]) {
1266 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1267 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1268 if (i > 0 && stats != NULL)
1269 *stats += i;
1271 NYD_LEAVE;
1274 static void
1275 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1276 ui64_t *stats)
1278 char xstatout[4];
1279 char *xp = xstatout;
1280 NYD_ENTER;
1282 if (mp->m_flag & MFLAGGED)
1283 *xp++ = 'F';
1284 if (mp->m_flag & MANSWERED)
1285 *xp++ = 'A';
1286 if (mp->m_flag & MDRAFTED)
1287 *xp++ = 'T';
1288 *xp = 0;
1289 if (xstatout[0]) {
1290 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1291 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1292 if (i > 0 && stats != NULL)
1293 *stats += i;
1295 NYD_LEAVE;
1298 static void
1299 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1301 char const *froma, *date, *nl;
1302 int i;
1303 NYD_ENTER;
1305 if (ip != NULL && ip->m_from != NULL) {
1306 froma = ip->m_from;
1307 date = fakedate(ip->m_time);
1308 nl = "\n";
1309 } else {
1310 froma = myname;
1311 date = time_current.tc_ctime;
1312 nl = "";
1315 n_COLOUR( n_colour_put(fp, n_COLOUR_ID_VIEW_FROM_, NULL); )
1316 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1317 n_COLOUR( n_colour_reset(fp); )
1318 if (i > 0 && stats != NULL)
1319 *stats += i;
1320 NYD_LEAVE;
1323 FL int
1324 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1325 char const *prefix, enum sendaction action, ui64_t *stats)
1327 struct quoteflt qf;
1328 size_t cnt, sz, i;
1329 FILE *ibuf;
1330 enum mime_parse_flags mpf;
1331 struct mimepart *ip;
1332 int rv = -1, c;
1333 NYD_ENTER;
1335 if (mp == dot && action != SEND_TOSRCH)
1336 pstate |= PS_DID_PRINT_DOT;
1337 if (stats != NULL)
1338 *stats = 0;
1339 quoteflt_init(&qf, prefix);
1341 /* First line is the From_ line, so no headers there to worry about */
1342 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1343 goto jleave;
1345 cnt = mp->m_size;
1346 sz = 0;
1348 char const *cpre = "", *csuf = "";
1349 #ifdef HAVE_COLOUR
1350 struct n_colour_pen *cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
1351 struct str const *sp = n_colour_pen_to_str(cpen);
1353 if (sp != NULL) {
1354 cpre = sp->s;
1355 sp = n_colour_reset_to_str();
1356 if (sp != NULL)
1357 csuf = sp->s;
1359 #endif
1360 if (mp->m_flag & MNOFROM) {
1361 if (doign != allignore && doign != fwdignore && action != SEND_RFC822)
1362 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1363 cpre, (int)qf.qf_pfix_len,
1364 (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""), fakefrom(mp),
1365 fakedate(mp->m_time), csuf);
1366 } else if (doign != allignore && doign != fwdignore &&
1367 action != SEND_RFC822) {
1368 if (qf.qf_pfix_len > 0) {
1369 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1370 if (i != qf.qf_pfix_len)
1371 goto jleave;
1372 sz += i;
1374 #ifdef HAVE_COLOUR
1375 if (cpre != NULL) {
1376 fputs(cpre, obuf);
1377 cpre = (char const*)0x1;
1379 #endif
1381 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1382 #ifdef HAVE_COLOUR
1383 if (c == '\n' && csuf != NULL) {
1384 cpre = (char const*)0x1;
1385 fputs(csuf, obuf);
1387 #endif
1388 putc(c, obuf);
1389 ++sz;
1390 --cnt;
1391 if (c == '\n')
1392 break;
1395 #ifdef HAVE_COLOUR
1396 if (csuf != NULL && cpre != (char const*)0x1)
1397 fputs(csuf, obuf);
1398 #endif
1399 } else {
1400 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1401 --cnt;
1402 if (c == '\n')
1403 break;
1407 if (sz > 0 && stats != NULL)
1408 *stats += sz;
1410 mpf = MIME_PARSE_NONE;
1411 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1412 mpf |= MIME_PARSE_DECRYPT | MIME_PARSE_PARTS;
1413 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1414 goto jleave;
1416 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1417 jleave:
1418 quoteflt_destroy(&qf);
1419 NYD_LEAVE;
1420 return rv;
1423 /* s-it-mode */