mk-conf.sh: work with old shells (mdocmx.sh does it right, pfff!)
[s-mailx.git] / send.c
blob82a7e4b4a1f6313ba6475bb190a4231d5f68b2d8
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Message content preparation (sendmp()).
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 volatile *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 sh = ok_vlook(SHELL);
241 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
242 sigset_t nset;
243 int pid;
245 sigemptyset(&nset);
246 pid = run_command(sh, NULL, term_infd, COMMAND_FD_PASS, "-c",
247 mhp->mh_shell_cmd, NULL, env_addon);
248 rbuf = (pid < 0) ? NULL : (FILE*)-1;
249 } else {
250 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
251 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
252 jerror:
253 if (rbuf == NULL)
254 n_err(_("Cannot run MIME type handler: %s: %s\n"),
255 mhp->mh_msg, strerror(errno));
256 else {
257 fflush(*qbuf);
258 if (*qbuf != stdout)
259 fflush(stdout);
262 jleave:
263 NYD_LEAVE;
264 return rbuf;
267 SINLINE ssize_t
268 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
269 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *rest)
271 ssize_t sz = 0, n;
272 int flags;
273 NYD_ENTER;
275 #if 0
276 Well ... it turns out to not work like that since of course a valid
277 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
278 after an empty line has been seen, which cannot be detected that easily
279 right here!
280 ifdef HAVE_DEBUG /* TODO assert legacy */
281 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
282 * TODO other input situations handle RFC 4155 OR, if newly generated,
283 * TODO enforce quoted-printable if there is From_, as "required" by
284 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
285 * TODO if it may happen in this path, we should just treat decryption
286 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
287 if (action == SEND_MBOX || action == SEND_DECRYPT)
288 assert(!is_head(buf, len, TRU1));
289 #else
290 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
291 is_head(buf, len, TRU1)) {
292 putc('>', fp);
293 ++sz;
295 #endif
297 flags = ((int)action & _TD_EOF);
298 action &= ~_TD_EOF;
299 n = mime_write(buf, len, fp,
300 action == SEND_MBOX ? CONV_NONE : convert,
301 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
302 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
303 ? TD_ISPR | TD_ICONV
304 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
305 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
306 qf, rest);
307 if (n < 0)
308 sz = n;
309 else if (n > 0) {
310 sz += n;
311 if (stats != NULL)
312 *stats += sz;
314 NYD_LEAVE;
315 return sz;
318 static void
319 _send_onpipe(int signo)
321 NYD_X; /* Signal handler */
322 UNUSED(signo);
323 siglongjmp(_send_pipejmp, 1);
326 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
327 static int __sendp_sig; /* TODO someday.. */
328 static sighandler_type __sendp_opipe;
329 static void
330 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
332 NYD_X; /* Signal handler */
333 __sendp_sig = sig;
334 siglongjmp(__sendp_actjmp, 1);
337 static int
338 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
339 struct ignoretab *doign, struct quoteflt *qf,
340 enum sendaction volatile action, ui64_t * volatile stats, int level)
342 int volatile rv = 0;
343 struct mime_handler mh;
344 struct str rest;
345 char *line = NULL, *cp, *cp2, *start;
346 char const * volatile tmpname = NULL;
347 size_t linesize = 0, linelen, cnt;
348 int volatile term_infd;
349 int dostat, c;
350 struct mimepart *volatile np;
351 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
352 * volatile qbuf = obuf, *origobuf = obuf;
353 enum conversion volatile convert;
354 sighandler_type volatile oldpipe = SIG_DFL;
355 NYD_ENTER;
357 UNINIT(term_infd, 0);
358 UNINIT(cnt, 0);
360 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
361 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
362 goto jskip;
364 dostat = 0;
365 if (level == 0) {
366 if (doign != NULL) {
367 if (!is_ign("status", 6, doign))
368 dostat |= 1;
369 if (!is_ign("x-status", 8, doign))
370 dostat |= 2;
371 } else
372 dostat = 3;
374 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
375 rv = -1;
376 goto jleave;
378 cnt = ip->m_size;
380 if (ip->m_mimecontent == MIME_DISCARD)
381 goto jskip;
383 if (!(ip->m_flag & MNOFROM))
384 while (cnt && (c = getc(ibuf)) != EOF) {
385 cnt--;
386 if (c == '\n')
387 break;
389 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
390 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
391 action == SEND_TOSRCH)
392 ? CONV_FROMHDR : CONV_NONE;
394 /* Work the headers */
395 quoteflt_reset(qf, obuf);
396 /* C99 */{
397 enum {
398 HPS_NONE = 0,
399 HPS_IN_FIELD = 1<<0,
400 HPS_IGNORE = 1<<1,
401 HPS_ISENC_1 = 1<<2,
402 HPS_ISENC_2 = 1<<3
403 } hps = HPS_NONE;
404 size_t lineno = 0;
406 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
407 ++lineno;
408 if (line[0] == '\n') {
409 /* If line is blank, we've reached end of headers, so force out
410 * status: field and note that we are no longer in header fields */
411 if (dostat & 1)
412 statusput(zmp, obuf, qf, stats);
413 if (dostat & 2)
414 xstatusput(zmp, obuf, qf, stats);
415 if (doign != allignore)
416 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
417 break;
420 hps &= ~HPS_ISENC_1;
421 if ((hps & HPS_IN_FIELD) && blankchar(line[0])) {
422 /* If this line is a continuation (SP / HT) of a previous header
423 * field, determine if the start of the line is a MIME encoded word */
424 if (hps & HPS_ISENC_2) {
425 for (cp = line; blankchar(*cp); ++cp)
427 if (cp > line && linelen - PTR2SIZE(cp - line) > 8 &&
428 cp[0] == '=' && cp[1] == '?')
429 hps |= HPS_ISENC_1;
431 } else {
432 /* Pick up the header field if we have one */
433 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
435 cp2 = cp;
436 while (spacechar(*cp))
437 ++cp;
438 if (cp[0] != ':') {
439 if (lineno != 1)
440 n_err(_("Malformed message: headers and body not separated "
441 "(with empty line)\n"));
442 /* Not a header line, force out status: This happens in uucp style
443 * mail where there are no headers at all */
444 if (level == 0 /*&& lineno == 1*/) {
445 if (dostat & 1)
446 statusput(zmp, obuf, qf, stats);
447 if (dostat & 2)
448 xstatusput(zmp, obuf, qf, stats);
450 if (doign != allignore)
451 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
452 break;
455 /* If it is an ignored field and we care about such things, skip it.
456 * Misuse dostat also for another bit xxx use a bitenum + for more */
457 if (ok_blook(keep_content_length))
458 dostat |= 1 << 2;
459 c = *cp2;
460 *cp2 = 0; /* temporarily null terminate */
461 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
462 (action == SEND_MBOX && !(dostat & (1 << 2)) &&
463 (!asccasecmp(line, "content-length") ||
464 !asccasecmp(line, "lines"))))
465 hps |= HPS_IGNORE;
466 else if (!asccasecmp(line, "status")) {
467 /* If field is "status," go compute and print real Status: field */
468 if (dostat & 1) {
469 statusput(zmp, obuf, qf, stats);
470 dostat &= ~1;
471 hps |= HPS_IGNORE;
473 } else if (!asccasecmp(line, "x-status")) {
474 /* If field is "status," go compute and print real Status: field */
475 if (dostat & 2) {
476 xstatusput(zmp, obuf, qf, stats);
477 dostat &= ~2;
478 hps |= HPS_IGNORE;
480 } else {
481 hps &= ~HPS_IGNORE;
482 /* For colourization we need the complete line, so save it */
483 /* XXX This is all temporary (colour belongs into backend), so
484 * XXX use tmpname as a temporary storage in the meanwhile */
485 #ifdef HAVE_COLOUR
486 if (pstate & PS_COLOUR_ACTIVE)
487 tmpname = savestrbuf(line, PTR2SIZE(cp2 - line));
488 #endif
490 *cp2 = c;
491 dostat &= ~(1 << 2);
492 hps |= HPS_IN_FIELD;
495 /* Determine if the end of the line is a MIME encoded word */
496 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
497 * TODO with header follow lines, and it should be up to the backend
498 * TODO what happens and what not, i.e., it doesn't matter whether it's
499 * TODO a MIME-encoded word or not, as long as a single separating space
500 * TODO remains in between lines (the MIME stuff will correctly remove
501 * TODO whitespace in between multiple adjacent encoded words) */
502 hps &= ~HPS_ISENC_2;
503 if (cnt && (c = getc(ibuf)) != EOF) {
504 if (blankchar(c)) {
505 cp = line + linelen - 1;
506 if (linelen > 0 && *cp == '\n')
507 --cp;
508 while (cp >= line && whitechar(*cp))
509 --cp;
510 if (PTR2SIZE(cp - line > 8) && cp[0] == '=' && cp[-1] == '?')
511 hps |= HPS_ISENC_2;
513 ungetc(c, ibuf);
516 if (!(hps & HPS_IGNORE)) {
517 size_t len = linelen;
518 start = line;
519 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
520 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
521 action == SEND_TOSRCH) {
522 /* Strip blank characters if two MIME-encoded words follow on
523 * continuing lines */
524 if (hps & HPS_ISENC_1)
525 while (len > 0 && blankchar(*start)) {
526 ++start;
527 --len;
529 if (hps & HPS_ISENC_2)
530 if (len > 0 && start[len - 1] == '\n')
531 --len;
532 while (len > 0 && blankchar(start[len - 1]))
533 --len;
535 #ifdef HAVE_COLOUR
537 bool_t colour_stripped = FAL0;
538 if (tmpname != NULL) {
539 n_colour_put(obuf, n_COLOUR_ID_VIEW_HEADER, tmpname);
540 if (len > 0 && start[len - 1] == '\n') {
541 colour_stripped = TRU1;
542 --len;
545 #endif
546 _out(start, len, obuf, convert, action, qf, stats, NULL);
547 #ifdef HAVE_COLOUR
548 if (tmpname != NULL) {
549 n_colour_reset(obuf);
550 if (colour_stripped)
551 putc('\n', obuf);
554 #endif
555 if (ferror(obuf)) {
556 free(line);
557 rv = -1;
558 goto jleave;
562 } /* C99 */
563 quoteflt_flush(qf);
564 free(line);
565 line = NULL;
566 tmpname = NULL;
568 jskip:
569 memset(&mh, 0, sizeof mh);
571 switch (ip->m_mimecontent) {
572 case MIME_822:
573 switch (action) {
574 case SEND_TODISP:
575 case SEND_TODISP_ALL:
576 case SEND_QUOTE:
577 case SEND_QUOTE_ALL:
578 if (ok_blook(rfc822_body_from_)) {
579 if (qf->qf_pfix_len > 0) {
580 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
581 qf->qf_pfix_len, obuf);
582 if (i == qf->qf_pfix_len && stats != NULL)
583 *stats += i;
585 put_from_(obuf, ip->m_multipart, stats);
587 /* FALLTHRU */
588 case SEND_TOSRCH:
589 case SEND_DECRYPT:
590 goto jmulti;
591 case SEND_TOFILE:
592 case SEND_TOPIPE:
593 if (ok_blook(rfc822_body_from_))
594 put_from_(obuf, ip->m_multipart, stats);
595 /* FALLTHRU */
596 case SEND_MBOX:
597 case SEND_RFC822:
598 case SEND_SHOW:
599 break;
601 break;
602 case MIME_TEXT_HTML:
603 case MIME_TEXT:
604 case MIME_TEXT_PLAIN:
605 switch (action) {
606 case SEND_TODISP:
607 case SEND_TODISP_ALL:
608 case SEND_QUOTE:
609 case SEND_QUOTE_ALL:
610 switch (mime_type_handler(&mh, ip, action)) {
611 case MIME_HDL_MSG:
612 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
613 stats, NULL);
614 /* We would print this as plain text, so better force going home */
615 goto jleave;
616 default:
617 break;
619 /* FALLTRHU */
620 default:
621 break;
623 break;
624 case MIME_DISCARD:
625 if (action != SEND_DECRYPT)
626 goto jleave;
627 break;
628 case MIME_PKCS7:
629 if (action != SEND_MBOX && action != SEND_RFC822 &&
630 action != SEND_SHOW && ip->m_multipart != NULL)
631 goto jmulti;
632 /* FALLTHRU */
633 default:
634 switch (action) {
635 case SEND_TODISP:
636 case SEND_TODISP_ALL:
637 case SEND_QUOTE:
638 case SEND_QUOTE_ALL:
639 switch (mime_type_handler(&mh, ip, action)) {
640 case MIME_HDL_MSG:
641 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
642 stats, NULL);
643 /* We would print this as plain text, so better force going home */
644 goto jleave;
645 case MIME_HDL_CMD:
646 /* FIXME WE NEED TO DO THAT IF WE ARE THE ONLY MAIL
647 * FIXME CONTENT !! */
648 case MIME_HDL_TEXT:
649 break;
650 default:
651 case MIME_HDL_NULL:
652 if (level == 0 && cnt) {
653 char const *x = _("[-- Binary content --]\n");
654 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
656 goto jleave;
658 break;
659 case SEND_TOFILE:
660 case SEND_TOPIPE:
661 case SEND_TOSRCH:
662 case SEND_DECRYPT:
663 case SEND_MBOX:
664 case SEND_RFC822:
665 case SEND_SHOW:
666 break;
668 break;
669 case MIME_ALTERNATIVE:
670 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
671 !ok_blook(print_alternatives)) {
672 /* XXX This (a) should not remain (b) should be own fun
673 * TODO (despite the fact that v15 will do this completely differently
674 * TODO by having an action-specific "manager" that will traverse the
675 * TODO parsed MIME tree and decide for each part whether it'll be
676 * TODO displayed or not *before* we walk the tree for doing action */
677 struct mpstack {
678 struct mpstack *outer;
679 struct mimepart *mp;
680 } outermost, * volatile curr, * volatile mpsp;
681 bool_t volatile neednl, hadpart;
682 struct n_sigman smalter;
684 (curr = &outermost)->outer = NULL;
685 curr->mp = ip;
686 neednl = hadpart = FAL0;
688 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
689 case 0:
690 break;
691 default:
692 rv = -1;
693 goto jalter_leave;
696 for (np = ip->m_multipart;;) {
697 jalter_redo:
698 for (; np != NULL; np = np->m_nextpart) {
699 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
700 if (neednl)
701 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
702 _print_part_info(obuf, np, doign, level, qf, stats);
704 neednl = TRU1;
706 switch (np->m_mimecontent) {
707 case MIME_ALTERNATIVE:
708 case MIME_RELATED:
709 case MIME_MULTI:
710 case MIME_DIGEST:
711 mpsp = salloc(sizeof *mpsp);
712 mpsp->outer = curr;
713 mpsp->mp = np->m_multipart;
714 curr->mp = np;
715 curr = mpsp;
716 np = mpsp->mp;
717 neednl = FAL0;
718 goto jalter_redo;
719 default:
720 if (hadpart)
721 break;
722 switch (mime_type_handler(&mh, np, action)) {
723 default:
724 mh.mh_flags = MIME_HDL_NULL;
725 continue; /* break; break; */
726 case MIME_HDL_PTF:
727 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
728 struct mimepart *x = np;
730 while ((x = x->m_nextpart) != NULL) {
731 struct mime_handler mhx;
733 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
734 mime_type_handler(&mhx, x, action) ==
735 MIME_HDL_TEXT)
736 break;
738 if (x != NULL)
739 continue; /* break; break; */
740 goto jalter_plain;
742 /* FALLTHRU */
743 case MIME_HDL_TEXT:
744 break;
746 /* FALLTHRU */
747 case MIME_TEXT_PLAIN:
748 if (hadpart)
749 break;
750 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
751 struct mimepart *x = np;
753 /* TODO twice TODO, we should dive into /related and
754 * TODO check whether that has rich parts! */
755 while ((x = x->m_nextpart) != NULL) {
756 struct mime_handler mhx;
758 switch (mime_type_handler(&mhx, x, action)) {
759 case MIME_HDL_PTF:
760 break;
761 default:
762 continue;
764 break;
766 if (x != NULL)
767 continue; /* break; break; */
769 jalter_plain:
770 quoteflt_flush(qf);
771 if (action == SEND_QUOTE && hadpart) {
772 struct quoteflt *dummy = quoteflt_dummy();
773 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
774 NULL);
775 quoteflt_flush(dummy);
777 hadpart = TRU1;
778 neednl = FAL0;
779 rv = sendpart(zmp, np, obuf, doign, qf, action, stats,
780 level + 1);
781 quoteflt_reset(qf, origobuf);
783 if (rv < 0)
784 curr = &outermost; /* Cause overall loop termination */
785 break;
789 mpsp = curr->outer;
790 if (mpsp == NULL)
791 break;
792 curr = mpsp;
793 np = curr->mp->m_nextpart;
795 jalter_leave:
796 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
797 goto jleave;
799 /* FALLTHRU */
800 case MIME_MULTI:
801 case MIME_DIGEST:
802 case MIME_RELATED:
803 switch (action) {
804 case SEND_TODISP:
805 case SEND_TODISP_ALL:
806 case SEND_QUOTE:
807 case SEND_QUOTE_ALL:
808 case SEND_TOFILE:
809 case SEND_TOPIPE:
810 case SEND_TOSRCH:
811 case SEND_DECRYPT:
812 jmulti:
813 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
814 ip->m_multipart != NULL &&
815 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
816 ip->m_multipart->m_nextpart == NULL) {
817 char const *x = _("[Missing multipart boundary - use show "
818 "to display the raw message]\n");
819 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
822 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
823 bool_t volatile ispipe;
825 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
826 continue;
828 ispipe = FAL0;
829 switch (action) {
830 case SEND_TOFILE:
831 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
832 break;
833 stats = NULL;
834 /* TODO Always open multipart on /dev/null, it's a hack to be
835 * TODO able to dive into that structure, and still better
836 * TODO than asking the user for something stupid.
837 * TODO oh, wait, we did ask for a filename for this MIME mail,
838 * TODO and that outer container is useless anyway ;-P */
839 if (np->m_multipart != NULL) {
840 if ((obuf = Fopen("/dev/null", "w")) == NULL)
841 continue;
842 } else if ((obuf = newfile(np, &ispipe)) == NULL)
843 continue;
844 if (!ispipe)
845 break;
846 if (sigsetjmp(_send_pipejmp, 1)) {
847 rv = -1;
848 goto jpipe_close;
850 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
851 break;
852 case SEND_TODISP:
853 case SEND_TODISP_ALL:
854 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
855 ip->m_mimecontent != MIME_RELATED &&
856 ip->m_mimecontent != MIME_DIGEST &&
857 ip->m_mimecontent != MIME_MULTI)
858 break;
859 _print_part_info(obuf, np, doign, level, qf, stats);
860 break;
861 case SEND_QUOTE:
862 case SEND_QUOTE_ALL:
863 case SEND_MBOX:
864 case SEND_RFC822:
865 case SEND_SHOW:
866 case SEND_TOSRCH:
867 case SEND_DECRYPT:
868 case SEND_TOPIPE:
869 break;
872 quoteflt_flush(qf);
873 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
874 np->m_multipart == NULL && ip->m_parent != NULL) {
875 struct quoteflt *dummy = quoteflt_dummy();
876 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
877 NULL);
878 quoteflt_flush(dummy);
880 if (sendpart(zmp, np, obuf, doign, qf, action, stats, level+1) < 0)
881 rv = -1;
882 quoteflt_reset(qf, origobuf);
884 if (action == SEND_QUOTE) {
885 if (ip->m_mimecontent != MIME_RELATED)
886 break;
888 if (action == SEND_TOFILE && obuf != origobuf) {
889 if (!ispipe)
890 Fclose(obuf);
891 else {
892 jpipe_close:
893 safe_signal(SIGPIPE, SIG_IGN);
894 Pclose(obuf, TRU1);
895 safe_signal(SIGPIPE, oldpipe);
899 goto jleave;
900 case SEND_MBOX:
901 case SEND_RFC822:
902 case SEND_SHOW:
903 break;
905 break;
908 /* Copy out message body */
909 if (doign == allignore && level == 0) /* skip final blank line */
910 --cnt;
911 switch (ip->m_mime_enc) {
912 case MIMEE_BIN:
913 case MIMEE_7B:
914 case MIMEE_8B:
915 convert = CONV_NONE;
916 break;
917 case MIMEE_QP:
918 convert = CONV_FROMQP;
919 break;
920 case MIMEE_B64:
921 switch (ip->m_mimecontent) {
922 case MIME_TEXT:
923 case MIME_TEXT_PLAIN:
924 case MIME_TEXT_HTML:
925 convert = CONV_FROMB64_T;
926 break;
927 default:
928 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
929 case MIME_HDL_TEXT:
930 case MIME_HDL_PTF:
931 convert = CONV_FROMB64_T;
932 break;
933 default:
934 convert = CONV_FROMB64;
935 break;
937 break;
939 break;
940 default:
941 convert = CONV_NONE;
944 if (action == SEND_DECRYPT || action == SEND_MBOX ||
945 action == SEND_RFC822 || action == SEND_SHOW)
946 convert = CONV_NONE;
947 #ifdef HAVE_ICONV
948 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
949 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
950 action == SEND_TOSRCH) &&
951 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
952 ip->m_mimecontent == MIME_TEXT_HTML ||
953 ip->m_mimecontent == MIME_TEXT ||
954 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
955 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
956 char const *tcs = charset_get_lc();
958 if (iconvd != (iconv_t)-1)
959 n_iconv_close(iconvd);
960 /* TODO Since Base64 has an odd 4:3 relation in between input
961 * TODO and output an input line may end with a partial
962 * TODO multibyte character; this is no problem at all unless
963 * TODO we send to the display or whatever, i.e., ensure
964 * TODO makeprint() or something; to avoid this trap, *force*
965 * TODO iconv(), in which case this layer will handle leftovers
966 * TODO correctly */
967 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
968 asccasecmp(charset_get_7bit(), ip->m_charset))) {
969 iconvd = n_iconv_open(tcs, ip->m_charset);
971 * TODO errors should DEFINETELY not be scrolled away!
972 * TODO what about an error buffer (think old shsp(1)),
973 * TODO re-dump errors since last snapshot when the
974 * TODO command loop enters again? i.e., at least print
975 * TODO "There were errors ?" before the next prompt,
976 * TODO so that the user can look at the error buffer?
978 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
979 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
980 /*rv = 1; goto jleave;*/
984 #endif
986 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
987 case MIME_HDL_CMD:
988 case MIME_HDL_PTF:
989 tmpname = NULL;
990 qbuf = obuf;
992 term_infd = COMMAND_FD_PASS;
993 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
994 enum oflags of;
996 of = OF_RDWR | OF_REGISTER;
997 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
998 term_infd = 0;
999 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1000 of |= OF_UNLINK;
1001 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
1002 of |= OF_REGISTER_UNLINK;
1004 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
1005 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
1006 of)) == NULL)
1007 goto jesend;
1009 if (mh.mh_flags & MIME_HDL_TMPF) {
1010 tmpname = savestr(cp);
1011 Ftmp_free(&cp);
1014 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1015 if (term_infd == 0)
1016 term_infd = fileno(pbuf);
1017 goto jsend;
1021 jpipe_for_real:
1022 pbuf = _pipefile(&mh, ip, UNVOLATILE(&qbuf), tmpname, term_infd);
1023 if (pbuf == NULL) {
1024 jesend:
1025 pbuf = qbuf = NULL;
1026 rv = -1;
1027 goto jend;
1028 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1029 pbuf = qbuf = NULL;
1030 goto jend;
1032 tmpname = NULL;
1033 action = SEND_TOPIPE;
1034 if (pbuf != qbuf) {
1035 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1036 if (sigsetjmp(_send_pipejmp, 1))
1037 goto jend;
1039 break;
1041 default:
1042 mh.mh_flags = MIME_HDL_NULL;
1043 pbuf = qbuf = obuf;
1044 break;
1047 jsend:
1049 bool_t volatile eof;
1050 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1051 ui64_t *save_stats = stats;
1053 if (pbuf != origobuf) {
1054 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1055 stats = NULL;
1057 eof = FAL0;
1058 rest.s = NULL;
1059 rest.l = 0;
1061 if (pbuf == qbuf) {
1062 __sendp_sig = 0;
1063 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1064 if (sigsetjmp(__sendp_actjmp, 1)) {
1065 if (rest.s != NULL)
1066 free(rest.s);
1067 free(line);
1068 #ifdef HAVE_ICONV
1069 if (iconvd != (iconv_t)-1)
1070 n_iconv_close(iconvd);
1071 #endif
1072 safe_signal(SIGPIPE, __sendp_opipe);
1073 n_raise(__sendp_sig);
1077 quoteflt_reset(qf, pbuf);
1078 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1079 joutln:
1080 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1081 ferror(pbuf)) {
1082 rv = -1; /* XXX Should bail away?! */
1083 break;
1086 if (!eof && rv >= 0 && rest.l != 0) {
1087 linelen = 0;
1088 eof = TRU1;
1089 action |= _TD_EOF;
1090 goto joutln;
1093 /* TODO HACK: when sending to the display we yet get fooled if a message
1094 * TODO doesn't end in a newline, because of our input/output 1:1.
1095 * TODO This should be handled automatically by a display filter, then */
1096 if(rv >= 0 && !qf->qf_nl_last &&
1097 (action == SEND_TODISP || action == SEND_TODISP_ALL))
1098 rv = quoteflt_push(qf, "\n", 1);
1100 quoteflt_flush(qf);
1102 if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
1103 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1104 fflush(pbuf);
1105 really_rewind(pbuf);
1106 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1107 goto jpipe_for_real;
1110 if (pbuf == qbuf)
1111 safe_signal(SIGPIPE, __sendp_opipe);
1113 if (rest.s != NULL)
1114 free(rest.s);
1116 if (pbuf != origobuf) {
1117 qf->qf_pfix_len = save_qf_pfix_len;
1118 stats = save_stats;
1122 jend:
1123 if (line != NULL)
1124 free(line);
1125 if (pbuf != qbuf) {
1126 safe_signal(SIGPIPE, SIG_IGN);
1127 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1128 safe_signal(SIGPIPE, oldpipe);
1129 if (rv >= 0 && qbuf != NULL && qbuf != obuf)
1130 pipecpy(qbuf, obuf, origobuf, qf, stats);
1132 #ifdef HAVE_ICONV
1133 if (iconvd != (iconv_t)-1)
1134 n_iconv_close(iconvd);
1135 #endif
1136 jleave:
1137 NYD_LEAVE;
1138 return rv;
1141 static FILE *
1142 newfile(struct mimepart *ip, bool_t volatile *ispipe)
1144 struct str in, out;
1145 char *f;
1146 FILE *fp;
1147 NYD_ENTER;
1149 f = ip->m_filename;
1150 *ispipe = FAL0;
1152 if (f != NULL && f != (char*)-1) {
1153 in.s = f;
1154 in.l = strlen(f);
1155 makeprint(&in, &out);
1156 out.l = delctrl(out.s, out.l);
1157 f = savestrbuf(out.s, out.l);
1158 free(out.s);
1161 if (options & OPT_INTERACTIVE) {
1162 struct str prompt;
1163 struct n_string shou, *shoup;
1164 char *f2, *f3;
1166 shoup = n_string_creat_auto(&shou);
1168 /* TODO Generic function which asks for filename.
1169 * TODO If the current part is the first textpart the target
1170 * TODO is implicit from outer `write' etc! */
1171 /* I18N: Filename input prompt with file type indication */
1172 str_concat_csvl(&prompt, _("Enter filename for part "),
1173 (ip->m_partstring != NULL) ? ip->m_partstring : _("?"),
1174 _(" ("), ip->m_ct_type_plain, _("): "), NULL);
1175 jgetname:
1176 f2 = n_lex_input_cp((n_LEXINPUT_CTX_BASE | n_LEXINPUT_HIST_ADD),
1177 prompt.s, ((f != (char*)-1 && f != NULL)
1178 ? n_shell_quote_cp(f, FAL0) : NULL));
1179 if(f2 != NULL){
1180 in.s = UNCONST(f2);
1181 in.l = UIZ_MAX;
1182 if((n_shell_parse_token(shoup, &in, n_SHEXP_PARSE_TRUNC |
1183 n_SHEXP_PARSE_TRIMSPACE | n_SHEXP_PARSE_LOG |
1184 n_SHEXP_PARSE_IGNORE_EMPTY) &
1185 (n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)) !=
1186 n_SHEXP_STATE_OUTPUT)
1187 goto jgetname;
1188 if(in.l != 0)
1189 goto jgetname;
1190 f2 = n_string_cp(shoup);
1192 if (f2 == NULL || *f2 == '\0') {
1193 if (options & OPT_D_V)
1194 n_err(_("... skipping this\n"));
1195 n_string_gut(shoup);
1196 fp = NULL;
1197 goto jleave;
1200 if (*f2 == '|')
1201 /* Pipes are expanded by the shell */
1202 f = f2;
1203 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
1204 /* (Error message written by fexpand()) */
1205 goto jgetname;
1206 else
1207 f = f3;
1209 n_string_gut(shoup);
1211 if (f == NULL || f == (char*)-1) {
1212 fp = NULL;
1213 goto jleave;
1216 if (*f == '|') {
1217 fp = Popen(f + 1, "w", ok_vlook(SHELL), NULL, 1);
1218 if (!(*ispipe = (fp != NULL)))
1219 n_perr(f, 0);
1220 } else {
1221 if ((fp = Fopen(f, "w")) == NULL)
1222 n_err(_("Cannot open %s\n"), n_shell_quote_cp(f, FAL0));
1224 jleave:
1225 NYD_LEAVE;
1226 return fp;
1229 static void
1230 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1231 ui64_t *stats)
1233 char *line = NULL; /* TODO line pool */
1234 size_t linesize = 0, linelen, cnt;
1235 ssize_t all_sz, sz;
1236 NYD_ENTER;
1238 fflush(pipebuf);
1239 rewind(pipebuf);
1240 cnt = (size_t)fsize(pipebuf);
1241 all_sz = 0;
1243 quoteflt_reset(qf, outbuf);
1244 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1245 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1246 break;
1247 all_sz += sz;
1249 if ((sz = quoteflt_flush(qf)) > 0)
1250 all_sz += sz;
1251 if (line)
1252 free(line);
1254 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1255 *stats += all_sz;
1256 Fclose(pipebuf);
1257 NYD_LEAVE;
1260 static void
1261 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1262 ui64_t *stats)
1264 char statout[3], *cp = statout;
1265 NYD_ENTER;
1267 if (mp->m_flag & MREAD)
1268 *cp++ = 'R';
1269 if (!(mp->m_flag & MNEW))
1270 *cp++ = 'O';
1271 *cp = 0;
1272 if (statout[0]) {
1273 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1274 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1275 if (i > 0 && stats != NULL)
1276 *stats += i;
1278 NYD_LEAVE;
1281 static void
1282 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1283 ui64_t *stats)
1285 char xstatout[4];
1286 char *xp = xstatout;
1287 NYD_ENTER;
1289 if (mp->m_flag & MFLAGGED)
1290 *xp++ = 'F';
1291 if (mp->m_flag & MANSWERED)
1292 *xp++ = 'A';
1293 if (mp->m_flag & MDRAFTED)
1294 *xp++ = 'T';
1295 *xp = 0;
1296 if (xstatout[0]) {
1297 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1298 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1299 if (i > 0 && stats != NULL)
1300 *stats += i;
1302 NYD_LEAVE;
1305 static void
1306 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1308 char const *froma, *date, *nl;
1309 int i;
1310 NYD_ENTER;
1312 if (ip != NULL && ip->m_from != NULL) {
1313 froma = ip->m_from;
1314 date = fakedate(ip->m_time);
1315 nl = "\n";
1316 } else {
1317 froma = myname;
1318 date = time_current.tc_ctime;
1319 nl = "";
1322 n_COLOUR( n_colour_put(fp, n_COLOUR_ID_VIEW_FROM_, NULL); )
1323 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1324 n_COLOUR( n_colour_reset(fp); )
1325 if (i > 0 && stats != NULL)
1326 *stats += i;
1327 NYD_LEAVE;
1330 FL int
1331 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1332 char const *prefix, enum sendaction action, ui64_t *stats)
1334 struct quoteflt qf;
1335 size_t cnt, sz, i;
1336 FILE *ibuf;
1337 enum mime_parse_flags mpf;
1338 struct mimepart *ip;
1339 int rv = -1, c;
1340 NYD_ENTER;
1342 if (mp == dot && action != SEND_TOSRCH)
1343 pstate |= PS_DID_PRINT_DOT;
1344 if (stats != NULL)
1345 *stats = 0;
1346 quoteflt_init(&qf, prefix);
1348 /* First line is the From_ line, so no headers there to worry about */
1349 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1350 goto jleave;
1352 cnt = mp->m_size;
1353 sz = 0;
1355 bool_t nozap;
1356 char const *cpre = "", *csuf = "";
1357 #ifdef HAVE_COLOUR
1358 struct n_colour_pen *cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
1359 struct str const *sp = n_colour_pen_to_str(cpen);
1361 if (sp != NULL) {
1362 cpre = sp->s;
1363 sp = n_colour_reset_to_str();
1364 if (sp != NULL)
1365 csuf = sp->s;
1367 #endif
1369 nozap = (doign != allignore && doign != fwdignore && action != SEND_RFC822 &&
1370 !is_ign("from_", sizeof("from_") -1, doign));
1371 if (mp->m_flag & MNOFROM) {
1372 if (nozap)
1373 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1374 cpre, (int)qf.qf_pfix_len,
1375 (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""), fakefrom(mp),
1376 fakedate(mp->m_time), csuf);
1377 } else if (nozap) {
1378 if (qf.qf_pfix_len > 0) {
1379 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1380 if (i != qf.qf_pfix_len)
1381 goto jleave;
1382 sz += i;
1384 #ifdef HAVE_COLOUR
1385 if (cpre != NULL) {
1386 fputs(cpre, obuf);
1387 cpre = (char const*)0x1;
1389 #endif
1391 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1392 #ifdef HAVE_COLOUR
1393 if (c == '\n' && csuf != NULL) {
1394 cpre = (char const*)0x1;
1395 fputs(csuf, obuf);
1397 #endif
1398 putc(c, obuf);
1399 ++sz;
1400 --cnt;
1401 if (c == '\n')
1402 break;
1405 #ifdef HAVE_COLOUR
1406 if (csuf != NULL && cpre != (char const*)0x1)
1407 fputs(csuf, obuf);
1408 #endif
1409 } else {
1410 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1411 --cnt;
1412 if (c == '\n')
1413 break;
1417 if (sz > 0 && stats != NULL)
1418 *stats += sz;
1420 mpf = MIME_PARSE_NONE;
1421 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1422 mpf |= MIME_PARSE_DECRYPT | MIME_PARSE_PARTS;
1423 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1424 goto jleave;
1426 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1427 jleave:
1428 quoteflt_destroy(&qf);
1429 NYD_LEAVE;
1430 return rv;
1433 /* s-it-mode */