mk-conf.sh: FIX! Restore usability on SunOS / Solaris..
[s-mailx.git] / send.c
blob1da91e7c6b6714db90e77916e7886557df30243b
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Mail to mail folders and displays.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE send
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 enum pipeflags {
43 PIPE_NULL, /* No pipe- mimetype handler */
44 PIPE_COMM, /* Normal command */
45 PIPE_ASYNC, /* Normal command, run asynchronous */
46 PIPE_TEXT, /* @ special command to force treatment as text */
47 PIPE_MSG /* Display message (returned as command string) */
50 static sigjmp_buf _send_pipejmp;
52 /* Going for user display, print Part: info string */
53 static void _print_part_info(FILE *obuf, struct mimepart const *mpp,
54 struct ignoretab *doign, int level,
55 struct quoteflt *qf, ui64_t *stats);
57 /* Query possible pipe command for MIME part */
58 static enum pipeflags _pipecmd(char const **result, struct mimepart const *mpp);
60 /* Create a pipe; if mpp is not NULL, place some NAILENV_* environment
61 * variables accordingly */
62 static FILE * _pipefile(char const *pipecomm, struct mimepart const *mpp,
63 FILE **qbuf, bool_t quote, bool_t async);
65 /* Call mime_write() as approbiate and adjust statistics */
66 SINLINE ssize_t _out(char const *buf, size_t len, FILE *fp,
67 enum conversion convert, enum sendaction action,
68 struct quoteflt *qf, ui64_t *stats, struct str *rest);
70 /* SIGPIPE handler */
71 static void _send_onpipe(int signo);
73 /* Send one part */
74 static int sendpart(struct message *zmp, struct mimepart *ip,
75 FILE *obuf, struct ignoretab *doign,
76 struct quoteflt *qf, enum sendaction action,
77 ui64_t *stats, int level);
79 /* Get a file for an attachment */
80 static FILE * newfile(struct mimepart *ip, int *ispipe);
82 static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
83 struct quoteflt *qf, ui64_t *stats);
85 /* Output a reasonable looking status field */
86 static void statusput(const struct message *mp, FILE *obuf,
87 struct quoteflt *qf, ui64_t *stats);
88 static void xstatusput(const struct message *mp, FILE *obuf,
89 struct quoteflt *qf, ui64_t *stats);
91 static void put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats);
93 static void
94 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
95 struct ignoretab *doign, int level, struct quoteflt *qf, ui64_t *stats)
97 char buf[64];
98 struct str ti = {NULL, 0}, to;
99 struct str const *cpre, *csuf;
100 char const *cp;
101 NYD2_ENTER;
103 #ifdef HAVE_COLOUR
104 cpre = colour_get(COLOURSPEC_PARTINFO);
105 csuf = colour_get(COLOURSPEC_RESET);
106 #else
107 cpre = csuf = NULL;
108 #endif
110 /* Take care of "99.99", i.e., 5 */
111 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
112 cp = "?";
113 if (level || (cp[0] != '1' && cp[1] == '\0'))
114 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
115 if (cpre != NULL)
116 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
117 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
118 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
120 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
121 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
122 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
124 if ((cp = mpp->m_ct_type_usr_ovwr) != NULL)
125 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
126 else
127 cp = mpp->m_ct_type_plain;
128 if ((to.l = strlen(cp)) > 30 && is_asccaseprefix(cp, "application/")) {
129 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
130 size_t i = to.l - fl;
131 char *x = salloc(al + i +1);
133 memcpy(x, "appl../", al);
134 memcpy(x + al, cp + fl, i +1);
135 cp = x;
136 to.l = al + i;
138 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
140 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL) {
141 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
142 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
143 cp = "qu.-pr.";
144 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
147 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_charset) != NULL) {
148 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
149 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
152 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
153 if (csuf != NULL)
154 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
155 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
157 if (is_ign("content-disposition", 19, doign) && mpp->m_filename != NULL &&
158 *mpp->m_filename != '\0') {
159 makeprint(n_str_add_cp(&ti, mpp->m_filename), &to);
160 free(ti.s);
161 to.l = delctrl(to.s, to.l);
163 if (cpre != NULL)
164 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
165 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
166 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
167 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
168 if (csuf != NULL)
169 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
170 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
172 free(to.s);
174 NYD2_LEAVE;
177 static enum pipeflags
178 _pipecmd(char const **result, struct mimepart const *mpp)
180 enum pipeflags rv;
181 char const *cp;
182 NYD2_ENTER;
184 *result = NULL;
186 /* Do we have any handler for this part? */
187 if ((cp = mime_type_mimepart_handler(mpp)) == NULL)
188 rv = PIPE_NULL;
189 else if (cp == MIME_TYPE_HANDLER_TEXT)
190 rv = PIPE_TEXT;
191 else if (
192 #ifdef HAVE_FILTER_HTML_TAGSOUP
193 cp == MIME_TYPE_HANDLER_HTML ||
194 #endif
195 *cp != '@') {
196 *result = cp;
197 rv = PIPE_COMM;
198 } else if (*++cp == '\0') {
199 /* Treat as plain text */
200 rv = PIPE_TEXT;
201 } else if (!(pstate & PS_MSGLIST_DIRECT)) {
202 /* Viewing multiple messages in one go, don't block system */
203 *result = _("[Directly address message only to display this]\n");
204 rv = PIPE_MSG;
205 } else {
206 /* Viewing a single message only */
207 /* TODO send/MIME layer rewrite: when we have a single-pass parser
208 * TODO then the parsing phase and the send phase will be separated;
209 * TODO that allows us to ask a user *before* we start the send, i.e.,
210 * TODO *before* a pager pipe is setup */
211 if (*cp == '&')
212 /* Asynchronous command, normal command line */
213 *result = ++cp, rv = PIPE_ASYNC;
214 else
215 *result = cp, rv = PIPE_COMM;
217 NYD2_LEAVE;
218 return rv;
221 static FILE *
222 _pipefile(char const *pipecomm, struct mimepart const *mpp, FILE **qbuf,
223 bool_t quote, bool_t async)
225 struct str s;
226 char const *env_addon[8], *cp, *sh;
227 FILE *rbuf;
228 NYD_ENTER;
230 rbuf = *qbuf;
232 if (quote) {
233 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER,
234 0600)) == NULL) {
235 n_perr(_("tmpfile"), 0);
236 *qbuf = rbuf;
238 async = FAL0;
241 #ifdef HAVE_FILTER_HTML_TAGSOUP
242 if (pipecomm == MIME_TYPE_HANDLER_HTML) {
243 union {int (*ptf)(void); char const *sh;} u;
244 u.ptf = &htmlflt_process_main;
245 rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf));
246 pipecomm = "Builtin HTML tagsoup filter";
247 goto jafter_tagsoup_hack;
249 #endif
251 /* NAIL_FILENAME */
252 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
253 cp = "";
254 env_addon[0] = str_concat_csvl(&s, NAILENV_FILENAME, "=", cp, NULL)->s;
256 /* NAIL_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
257 * TODO a file wherever he wants! *Do* create a zero-size temporary file
258 * TODO and give *that* path as NAIL_FILENAME_TEMPORARY, clean it up once
259 * TODO the pipe returns? Like this we *can* verify path/name issues! */
260 env_addon[1] = str_concat_csvl(&s, NAILENV_FILENAME_GENERATED, "=",
261 getrandstring(MIN(NAME_MAX / 4, 16)), NULL)->s;
263 /* NAIL_CONTENT{,_EVIDENCE} */
264 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
265 cp = "";
266 env_addon[2] = str_concat_csvl(&s, NAILENV_CONTENT, "=", cp, NULL)->s;
268 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
269 cp = mpp->m_ct_type_usr_ovwr;
270 env_addon[3] = str_concat_csvl(&s, NAILENV_CONTENT_EVIDENCE, "=", cp,
271 NULL)->s;
273 env_addon[4] = str_concat_csvl(&s, NAILENV_TMPDIR, "=", tempdir, NULL)->s;
274 env_addon[5] = str_concat_csvl(&s, "TMPDIR", "=", tempdir, NULL)->s;
276 env_addon[6] = NULL;
278 if ((sh = ok_vlook(SHELL)) == NULL)
279 sh = XSHELL;
281 rbuf = Popen(pipecomm, "W", sh, env_addon, (async ? -1 : fileno(*qbuf)));
282 #ifdef HAVE_FILTER_HTML_TAGSOUP
283 jafter_tagsoup_hack:
284 #endif
285 if (rbuf == NULL)
286 n_err(_("Cannot run MIME type handler \"%s\": %s\n"),
287 pipecomm, strerror(errno));
288 else {
289 fflush(*qbuf);
290 if (*qbuf != stdout)
291 fflush(stdout);
293 NYD_LEAVE;
294 return rbuf;
297 SINLINE ssize_t
298 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
299 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *rest)
301 ssize_t sz = 0, n;
302 int flags;
303 NYD_ENTER;
305 #if 0
306 Well ... it turns out to not work like that since of course a valid
307 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
308 after an empty line has been seen, which cannot be detected that easily
309 right here!
310 ifdef HAVE_DEBUG /* TODO assert legacy */
311 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
312 * TODO other input situations handle RFC 4155 OR, if newly generated,
313 * TODO enforce quoted-printable if there is From_, as "required" by
314 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
315 * TODO if it may happen in this path, we should just treat decryption
316 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
317 if (action == SEND_MBOX || action == SEND_DECRYPT)
318 assert(!is_head(buf, len, TRU1));
319 #else
320 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
321 is_head(buf, len, TRU1)) {
322 putc('>', fp);
323 ++sz;
325 #endif
327 flags = ((int)action & _TD_EOF);
328 action &= ~_TD_EOF;
329 n = mime_write(buf, len, fp,
330 action == SEND_MBOX ? CONV_NONE : convert,
331 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
332 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
333 ? TD_ISPR | TD_ICONV
334 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
335 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
336 qf, rest);
337 if (n < 0)
338 sz = n;
339 else if (n > 0) {
340 sz += n;
341 if (stats != NULL)
342 *stats += sz;
344 NYD_LEAVE;
345 return sz;
348 static void
349 _send_onpipe(int signo)
351 NYD_X; /* Signal handler */
352 UNUSED(signo);
353 siglongjmp(_send_pipejmp, 1);
356 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
357 static int __sendp_sig; /* TODO someday.. */
358 static sighandler_type __sendp_opipe;
359 static void
360 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
362 NYD_X; /* Signal handler */
363 __sendp_sig = sig;
364 siglongjmp(__sendp_actjmp, 1);
367 static sigjmp_buf __sndalter_actjmp; /* TODO someday.. */
368 static int __sndalter_sig; /* TODO someday.. */
369 static void
370 __sndalter_onsig(int sig) /* TODO someday, we won't need it no more */
372 NYD_X; /* Signal handler */
373 __sndalter_sig = sig;
374 siglongjmp(__sndalter_actjmp, 1);
377 static int
378 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
379 struct ignoretab *doign, struct quoteflt *qf,
380 enum sendaction volatile action, ui64_t * volatile stats, int level)
382 int volatile ispipe, rv = 0;
383 struct str rest;
384 char *line = NULL, *cp, *cp2, *start;
385 char const *pipecomm = NULL;
386 size_t linesize = 0, linelen, cnt;
387 int dostat, infld = 0, ignoring = 1, isenc, c;
388 struct mimepart *volatile np;
389 FILE * volatile ibuf = NULL, * volatile pbuf = obuf, * volatile qbuf = obuf,
390 *origobuf = obuf;
391 enum conversion volatile convert;
392 sighandler_type volatile oldpipe = SIG_DFL;
393 long lineno = 0;
394 NYD_ENTER;
396 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
397 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
398 goto jskip;
400 dostat = 0;
401 if (level == 0) {
402 if (doign != NULL) {
403 if (!is_ign("status", 6, doign))
404 dostat |= 1;
405 if (!is_ign("x-status", 8, doign))
406 dostat |= 2;
407 } else
408 dostat = 3;
410 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
411 rv = -1;
412 goto jleave;
414 cnt = ip->m_size;
416 if (ip->m_mimecontent == MIME_DISCARD)
417 goto jskip;
419 if (!(ip->m_flag & MNOFROM))
420 while (cnt && (c = getc(ibuf)) != EOF) {
421 cnt--;
422 if (c == '\n')
423 break;
425 isenc = 0;
426 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
427 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
428 action == SEND_TOSRCH)
429 ? CONV_FROMHDR : CONV_NONE;
431 /* Work the headers */
432 quoteflt_reset(qf, obuf);
433 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
434 ++lineno;
435 if (line[0] == '\n') {
436 /* If line is blank, we've reached end of headers, so force out
437 * status: field and note that we are no longer in header fields */
438 if (dostat & 1)
439 statusput(zmp, obuf, qf, stats);
440 if (dostat & 2)
441 xstatusput(zmp, obuf, qf, stats);
442 if (doign != allignore)
443 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
444 break;
447 isenc &= ~1;
448 if (infld && blankchar(line[0])) {
449 /* If this line is a continuation (SP / HT) of a previous header
450 * field, determine if the start of the line is a MIME encoded word */
451 if (isenc & 2) {
452 for (cp = line; blankchar(*cp); ++cp);
453 if (cp > line && linelen - PTR2SIZE(cp - line) > 8 &&
454 cp[0] == '=' && cp[1] == '?')
455 isenc |= 1;
457 } else {
458 /* Pick up the header field if we have one */
459 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
461 cp2 = cp;
462 while (spacechar(*cp))
463 ++cp;
464 if (cp[0] != ':' && level == 0 && lineno == 1) {
465 /* Not a header line, force out status: This happens in uucp style
466 * mail where there are no headers at all */
467 if (dostat & 1)
468 statusput(zmp, obuf, qf, stats);
469 if (dostat & 2)
470 xstatusput(zmp, obuf, qf, stats);
471 if (doign != allignore)
472 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
473 break;
476 /* If it is an ignored field and we care about such things, skip it.
477 * Misuse dostat also for another bit xxx use a bitenum + for more */
478 if (ok_blook(keep_content_length))
479 dostat |= 1 << 2;
480 c = *cp2;
481 *cp2 = 0; /* temporarily null terminate */
482 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
483 (action == SEND_MBOX && !(dostat & (1 << 2)) &&
484 (!asccasecmp(line, "content-length") ||
485 !asccasecmp(line, "lines"))))
486 ignoring = 1;
487 else if (!asccasecmp(line, "status")) {
488 /* If field is "status," go compute and print real Status: field */
489 if (dostat & 1) {
490 statusput(zmp, obuf, qf, stats);
491 dostat &= ~1;
492 ignoring = 1;
494 } else if (!asccasecmp(line, "x-status")) {
495 /* If field is "status," go compute and print real Status: field */
496 if (dostat & 2) {
497 xstatusput(zmp, obuf, qf, stats);
498 dostat &= ~2;
499 ignoring = 1;
501 } else {
502 ignoring = 0;
503 /* For colourization we need the complete line, so save it */
504 /* XXX This is all temporary (colour belongs into backend), so
505 * XXX use pipecomm as a temporary storage in the meanwhile */
506 #ifdef HAVE_COLOUR
507 if (colour_table != NULL)
508 pipecomm = savestrbuf(line, PTR2SIZE(cp2 - line));
509 #endif
511 *cp2 = c;
512 dostat &= ~(1 << 2);
513 infld = 1;
516 /* Determine if the end of the line is a MIME encoded word */
517 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
518 * TODO with header follow lines, and it should be up to the backend
519 * TODO what happens and what not, i.e., it doesn't matter wether it's
520 * TODO a MIME-encoded word or not, as long as a single separating space
521 * TODO remains in between lines (the MIME stuff will correctly remove
522 * TODO whitespace in between multiple adjacent encoded words) */
523 isenc &= ~2;
524 if (cnt && (c = getc(ibuf)) != EOF) {
525 if (blankchar(c)) {
526 cp = line + linelen - 1;
527 if (linelen > 0 && *cp == '\n')
528 --cp;
529 while (cp >= line && whitechar(*cp))
530 --cp;
531 if (PTR2SIZE(cp - line > 8) && cp[0] == '=' && cp[-1] == '?')
532 isenc |= 2;
534 ungetc(c, ibuf);
537 if (!ignoring) {
538 size_t len = linelen;
539 start = line;
540 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
541 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
542 action == SEND_TOSRCH) {
543 /* Strip blank characters if two MIME-encoded words follow on
544 * continuing lines */
545 if (isenc & 1)
546 while (len > 0 && blankchar(*start)) {
547 ++start;
548 --len;
550 if (isenc & 2)
551 if (len > 0 && start[len - 1] == '\n')
552 --len;
553 while (len > 0 && blankchar(start[len - 1]))
554 --len;
556 #ifdef HAVE_COLOUR
558 bool_t colour_stripped = FAL0;
559 if (pipecomm != NULL) {
560 colour_put_header(obuf, pipecomm);
561 if (len > 0 && start[len - 1] == '\n') {
562 colour_stripped = TRU1;
563 --len;
566 #endif
567 _out(start, len, obuf, convert, action, qf, stats, NULL);
568 #ifdef HAVE_COLOUR
569 if (pipecomm != NULL) {
570 colour_reset(obuf); /* XXX reset after \n!! */
571 if (colour_stripped)
572 putc('\n', obuf);
575 #endif
576 if (ferror(obuf)) {
577 free(line);
578 rv = -1;
579 goto jleave;
583 quoteflt_flush(qf);
584 free(line);
585 line = NULL;
586 pipecomm = NULL;
588 jskip:
589 switch (ip->m_mimecontent) {
590 case MIME_822:
591 switch (action) {
592 case SEND_TODISP:
593 case SEND_TODISP_ALL:
594 case SEND_QUOTE:
595 case SEND_QUOTE_ALL:
596 if (ok_blook(rfc822_body_from_)) {
597 if (qf->qf_pfix_len > 0) {
598 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
599 qf->qf_pfix_len, obuf);
600 if (i == qf->qf_pfix_len && stats != NULL)
601 *stats += i;
603 put_from_(obuf, ip->m_multipart, stats);
605 /* FALLTHRU */
606 case SEND_TOSRCH:
607 case SEND_DECRYPT:
608 goto jmulti;
609 case SEND_TOFILE:
610 case SEND_TOPIPE:
611 if (ok_blook(rfc822_body_from_))
612 put_from_(obuf, ip->m_multipart, stats);
613 /* FALLTHRU */
614 case SEND_MBOX:
615 case SEND_RFC822:
616 case SEND_SHOW:
617 break;
619 break;
620 case MIME_TEXT_HTML:
621 case MIME_TEXT:
622 case MIME_TEXT_PLAIN:
623 switch (action) {
624 case SEND_TODISP:
625 case SEND_TODISP_ALL:
626 case SEND_QUOTE:
627 case SEND_QUOTE_ALL:
628 ispipe = TRU1;
629 switch (_pipecmd(&pipecomm, ip)) {
630 case PIPE_MSG:
631 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
632 stats, NULL);
633 /* We would print this as plain text, so better force going home */
634 goto jleave;
635 case PIPE_TEXT:
636 case PIPE_COMM:
637 case PIPE_NULL:
638 break;
639 case PIPE_ASYNC:
640 ispipe = FAL0;
641 break;
643 /* FALLTRHU */
644 default:
645 break;
647 break;
648 case MIME_DISCARD:
649 if (action != SEND_DECRYPT)
650 goto jleave;
651 break;
652 case MIME_PKCS7:
653 if (action != SEND_MBOX && action != SEND_RFC822 &&
654 action != SEND_SHOW && ip->m_multipart != NULL)
655 goto jmulti;
656 /* FALLTHRU */
657 default:
658 switch (action) {
659 case SEND_TODISP:
660 case SEND_TODISP_ALL:
661 case SEND_QUOTE:
662 case SEND_QUOTE_ALL:
663 ispipe = TRU1;
664 switch (_pipecmd(&pipecomm, ip)) {
665 case PIPE_MSG:
666 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
667 stats, NULL);
668 pipecomm = NULL;
669 break;
670 case PIPE_ASYNC:
671 ispipe = FAL0;
672 /* FALLTHRU */
673 case PIPE_COMM:
674 case PIPE_NULL:
675 break;
676 case PIPE_TEXT:
677 goto jcopyout; /* break; break; */
679 if (pipecomm != NULL)
680 break;
681 if (level == 0 && cnt) {
682 char const *x = _("[Binary content]\n");
683 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
685 goto jleave;
686 case SEND_TOFILE:
687 case SEND_TOPIPE:
688 case SEND_TOSRCH:
689 case SEND_DECRYPT:
690 case SEND_MBOX:
691 case SEND_RFC822:
692 case SEND_SHOW:
693 break;
695 break;
696 case MIME_ALTERNATIVE:
697 case MIME_RELATED:
698 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
699 !ok_blook(print_alternatives)) {
700 /* XXX This (a) should not remain (b) should be own fun */
701 struct mpstack {
702 struct mpstack *outer;
703 struct mimepart *mp;
704 } outermost, * volatile curr = &outermost, * volatile mpsp;
705 sighandler_type volatile opsh, oish, ohsh;
706 size_t volatile partcnt = 0/* silence CC */;
707 bool_t volatile neednl = FAL0;
709 curr->outer = NULL;
710 curr->mp = ip;
712 __sndalter_sig = 0;
713 opsh = safe_signal(SIGPIPE, &__sndalter_onsig);
714 oish = safe_signal(SIGINT, &__sndalter_onsig);
715 ohsh = safe_signal(SIGHUP, &__sndalter_onsig);
716 if (sigsetjmp(__sndalter_actjmp, 1)) {
717 rv = -1;
718 goto jalter_unroll;
721 for (np = ip->m_multipart;;) {
722 partcnt = 0;
723 jalter_redo:
724 for (; np != NULL; np = np->m_nextpart) {
725 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
726 if (neednl)
727 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
728 _print_part_info(obuf, np, doign, level, qf, stats);
730 neednl = TRU1;
732 switch (np->m_mimecontent) {
733 case MIME_ALTERNATIVE:
734 case MIME_RELATED:
735 case MIME_MULTI:
736 case MIME_DIGEST:
737 mpsp = ac_alloc(sizeof *mpsp);
738 mpsp->outer = curr;
739 mpsp->mp = np->m_multipart;
740 curr->mp = np;
741 curr = mpsp;
742 np = mpsp->mp;
743 neednl = FAL0;
744 goto jalter_redo;
745 default:
746 switch (_pipecmd(&pipecomm, np)) {
747 default:
748 continue;
749 case PIPE_TEXT:
750 break;
752 /* FALLTHRU */
753 case MIME_TEXT_PLAIN:
754 ++partcnt;
755 if (action == SEND_QUOTE && partcnt > 1 &&
756 ip->m_mimecontent == MIME_ALTERNATIVE)
757 break;
758 quoteflt_flush(qf);
759 if (action == SEND_QUOTE && partcnt > 1) {
760 struct quoteflt *dummy = quoteflt_dummy();
761 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
762 NULL);
763 quoteflt_flush(dummy);
765 neednl = FAL0;
766 rv = sendpart(zmp, np, obuf, doign, qf, action, stats,
767 level + 1);
768 quoteflt_reset(qf, origobuf);
770 if (rv < 0) {
771 jalter_unroll:
772 for (;; curr = mpsp) {
773 if ((mpsp = curr->outer) == NULL)
774 break;
775 ac_free(curr);
778 break;
782 mpsp = curr->outer;
783 if (mpsp == NULL)
784 break;
785 ac_free(curr);
786 curr = mpsp;
787 np = curr->mp->m_nextpart;
789 safe_signal(SIGHUP, ohsh);
790 safe_signal(SIGINT, oish);
791 safe_signal(SIGPIPE, opsh);
792 if (__sndalter_sig != 0)
793 n_raise(__sndalter_sig);
794 goto jleave;
796 /* FALLTHRU */
797 case MIME_MULTI:
798 case MIME_DIGEST:
799 switch (action) {
800 case SEND_TODISP:
801 case SEND_TODISP_ALL:
802 case SEND_QUOTE:
803 case SEND_QUOTE_ALL:
804 case SEND_TOFILE:
805 case SEND_TOPIPE:
806 case SEND_TOSRCH:
807 case SEND_DECRYPT:
808 jmulti:
809 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
810 ip->m_multipart != NULL &&
811 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
812 ip->m_multipart->m_nextpart == NULL) {
813 char const *x = _("[Missing multipart boundary - use \"show\" "
814 "to display the raw message]\n");
815 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
818 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
819 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
820 continue;
821 ispipe = FAL0;
822 switch (action) {
823 case SEND_TOFILE:
824 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
825 break;
826 stats = NULL;
827 /* TODO Always open multipart on /dev/null, it's a hack to be
828 * TODO able to dive into that structure, and still better
829 * TODO than asking the user for something stupid.
830 * TODO oh, wait, we did ask for a filename for this MIME mail,
831 * TODO and that outer container is useless anyway ;-P */
832 if (np->m_multipart != NULL) {
833 if ((obuf = Fopen("/dev/null", "w")) == NULL)
834 continue;
835 } else if ((obuf = newfile(np, UNVOLATILE(&ispipe))) == NULL)
836 continue;
837 if (!ispipe)
838 break;
839 if (sigsetjmp(_send_pipejmp, 1)) {
840 rv = -1;
841 goto jpipe_close;
843 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
844 break;
845 case SEND_TODISP:
846 case SEND_TODISP_ALL:
847 case SEND_QUOTE_ALL:
848 if (ip->m_mimecontent != MIME_MULTI &&
849 ip->m_mimecontent != MIME_ALTERNATIVE &&
850 ip->m_mimecontent != MIME_RELATED &&
851 ip->m_mimecontent != MIME_DIGEST)
852 break;
853 _print_part_info(obuf, np, doign, level, qf, stats);
854 break;
855 case SEND_MBOX:
856 case SEND_RFC822:
857 case SEND_SHOW:
858 case SEND_TOSRCH:
859 case SEND_QUOTE:
860 case SEND_DECRYPT:
861 case SEND_TOPIPE:
862 break;
865 quoteflt_flush(qf);
866 if (sendpart(zmp, np, obuf, doign, qf, action, stats, level+1) < 0)
867 rv = -1;
868 quoteflt_reset(qf, origobuf);
870 if (action == SEND_QUOTE)
871 break;
872 if (action == SEND_TOFILE && obuf != origobuf) {
873 if (!ispipe)
874 Fclose(obuf);
875 else {
876 jpipe_close:
877 safe_signal(SIGPIPE, SIG_IGN);
878 Pclose(obuf, TRU1);
879 safe_signal(SIGPIPE, oldpipe);
883 goto jleave;
884 case SEND_MBOX:
885 case SEND_RFC822:
886 case SEND_SHOW:
887 break;
891 /* Copy out message body */
892 jcopyout:
893 if (doign == allignore && level == 0) /* skip final blank line */
894 --cnt;
895 switch (ip->m_mime_enc) {
896 case MIMEE_BIN:
897 case MIMEE_7B:
898 case MIMEE_8B:
899 convert = CONV_NONE;
900 break;
901 case MIMEE_QP:
902 convert = CONV_FROMQP;
903 break;
904 case MIMEE_B64:
905 switch (ip->m_mimecontent) {
906 case MIME_TEXT:
907 case MIME_TEXT_PLAIN:
908 case MIME_TEXT_HTML:
909 convert = CONV_FROMB64_T;
910 break;
911 default:
912 convert = CONV_FROMB64;
914 break;
915 default:
916 convert = CONV_NONE;
919 if (action == SEND_DECRYPT || action == SEND_MBOX ||
920 action == SEND_RFC822 || action == SEND_SHOW)
921 convert = CONV_NONE;
922 #ifdef HAVE_ICONV
923 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
924 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
925 action == SEND_TOSRCH) &&
926 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
927 ip->m_mimecontent == MIME_TEXT_HTML ||
928 ip->m_mimecontent == MIME_TEXT)) {
929 char const *tcs = charset_get_lc();
931 if (iconvd != (iconv_t)-1)
932 n_iconv_close(iconvd);
933 /* TODO Since Base64 has an odd 4:3 relation in between input
934 * TODO and output an input line may end with a partial
935 * TODO multibyte character; this is no problem at all unless
936 * TODO we send to the display or whatever, i.e., ensure
937 * TODO makeprint() or something; to avoid this trap, *force*
938 * TODO iconv(), in which case this layer will handle leftovers
939 * TODO correctly */
940 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
941 asccasecmp(charset_get_7bit(), ip->m_charset))) {
942 iconvd = n_iconv_open(tcs, ip->m_charset);
944 * TODO errors should DEFINETELY not be scrolled away!
945 * TODO what about an error buffer (think old shsp(1)),
946 * TODO re-dump errors since last snapshot when the
947 * TODO command loop enters again? i.e., at least print
948 * TODO "There were errors ?" before the next prompt,
949 * TODO so that the user can look at the error buffer?
951 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
952 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
953 /*rv = 1; goto jleave;*/
957 #endif
959 if (pipecomm != NULL && (action == SEND_TODISP ||
960 action == SEND_TODISP_ALL || action == SEND_QUOTE ||
961 action == SEND_QUOTE_ALL)) {
962 qbuf = obuf;
963 pbuf = _pipefile(pipecomm, ip, UNVOLATILE(&qbuf),
964 (action == SEND_QUOTE || action == SEND_QUOTE_ALL), !ispipe);
965 if (pbuf == NULL) {
966 #ifdef HAVE_ICONV
967 if (iconvd != (iconv_t)-1)
968 n_iconv_close(iconvd);
969 #endif
970 rv = -1;
971 goto jleave;
973 action = SEND_TOPIPE;
974 if (pbuf != qbuf) {
975 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
976 if (sigsetjmp(_send_pipejmp, 1))
977 goto jend;
979 } else
980 pbuf = qbuf = obuf;
983 bool_t volatile eof;
984 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
985 ui64_t *save_stats = stats;
987 if (pbuf != origobuf) {
988 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
989 stats = NULL;
991 eof = FAL0;
992 rest.s = NULL;
993 rest.l = 0;
995 if (pbuf == qbuf) {
996 __sendp_sig = 0;
997 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
998 if (sigsetjmp(__sendp_actjmp, 1)) {
999 if (rest.s != NULL)
1000 free(rest.s);
1001 free(line);
1002 #ifdef HAVE_ICONV
1003 if (iconvd != (iconv_t)-1)
1004 n_iconv_close(iconvd);
1005 #endif
1006 safe_signal(SIGPIPE, __sendp_opipe);
1007 n_raise(__sendp_sig);
1011 quoteflt_reset(qf, pbuf);
1012 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1013 joutln:
1014 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1015 ferror(pbuf)) {
1016 rv = -1; /* XXX Should bail away?! */
1017 break;
1020 if (!eof && rest.l != 0) {
1021 linelen = 0;
1022 eof = TRU1;
1023 action |= _TD_EOF;
1024 goto joutln;
1026 if (pbuf == qbuf)
1027 safe_signal(SIGPIPE, __sendp_opipe);
1029 quoteflt_flush(qf);
1030 if (rest.s != NULL)
1031 free(rest.s);
1033 if (pbuf != origobuf) {
1034 qf->qf_pfix_len = save_qf_pfix_len;
1035 stats = save_stats;
1039 jend:
1040 if (line != NULL)
1041 free(line);
1042 if (pbuf != qbuf) {
1043 safe_signal(SIGPIPE, SIG_IGN);
1044 Pclose(pbuf, ispipe);
1045 safe_signal(SIGPIPE, oldpipe);
1046 if (qbuf != NULL && qbuf != obuf)
1047 pipecpy(qbuf, obuf, origobuf, qf, stats);
1049 #ifdef HAVE_ICONV
1050 if (iconvd != (iconv_t)-1)
1051 n_iconv_close(iconvd);
1052 #endif
1053 jleave:
1054 NYD_LEAVE;
1055 return rv;
1058 static FILE *
1059 newfile(struct mimepart *ip, int *ispipe)
1061 struct str in, out;
1062 char *f;
1063 FILE *fp;
1064 NYD_ENTER;
1066 f = ip->m_filename;
1067 *ispipe = 0;
1069 if (f != NULL && f != (char*)-1) {
1070 in.s = f;
1071 in.l = strlen(f);
1072 makeprint(&in, &out);
1073 out.l = delctrl(out.s, out.l);
1074 f = savestrbuf(out.s, out.l);
1075 free(out.s);
1078 if (options & OPT_INTERACTIVE) {
1079 char *f2, *f3;
1080 jgetname:
1081 printf(_("Enter filename for part %s (%s)"),
1082 (ip->m_partstring != NULL) ? ip->m_partstring : "?",
1083 ip->m_ct_type_plain);
1084 f2 = n_input_cp_addhist(": ", ((f != (char*)-1 && f != NULL)
1085 ? fexpand_nshell_quote(f) : NULL), TRU1);
1086 if (f2 == NULL || *f2 == '\0') {
1087 if (options & OPT_D_V)
1088 n_err(_("... skipping this\n"));
1089 fp = NULL;
1090 goto jleave;
1091 } else if (*f2 == '|')
1092 /* Pipes are expanded by the shell */
1093 f = f2;
1094 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NSHELL)) == NULL)
1095 /* (Error message written by fexpand()) */
1096 goto jgetname;
1097 else
1098 f = f3;
1100 if (f == NULL || f == (char*)-1) {
1101 fp = NULL;
1102 goto jleave;
1105 if (*f == '|') {
1106 char const *cp;
1107 cp = ok_vlook(SHELL);
1108 if (cp == NULL)
1109 cp = XSHELL;
1110 fp = Popen(f + 1, "w", cp, NULL, 1);
1111 if (!(*ispipe = (fp != NULL)))
1112 n_perr(f, 0);
1113 } else {
1114 if ((fp = Fopen(f, "w")) == NULL)
1115 n_err(_("Cannot open \"%s\"\n"), f);
1117 jleave:
1118 NYD_LEAVE;
1119 return fp;
1122 static void
1123 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1124 ui64_t *stats)
1126 char *line = NULL; /* TODO line pool */
1127 size_t linesize = 0, linelen, cnt;
1128 ssize_t all_sz, sz;
1129 NYD_ENTER;
1131 fflush(pipebuf);
1132 rewind(pipebuf);
1133 cnt = fsize(pipebuf);
1134 all_sz = 0;
1136 quoteflt_reset(qf, outbuf);
1137 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1138 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1139 break;
1140 all_sz += sz;
1142 if ((sz = quoteflt_flush(qf)) > 0)
1143 all_sz += sz;
1144 if (line)
1145 free(line);
1147 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1148 *stats += all_sz;
1149 Fclose(pipebuf);
1150 NYD_LEAVE;
1153 static void
1154 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1155 ui64_t *stats)
1157 char statout[3], *cp = statout;
1158 NYD_ENTER;
1160 if (mp->m_flag & MREAD)
1161 *cp++ = 'R';
1162 if (!(mp->m_flag & MNEW))
1163 *cp++ = 'O';
1164 *cp = 0;
1165 if (statout[0]) {
1166 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1167 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1168 if (i > 0 && stats != NULL)
1169 *stats += i;
1171 NYD_LEAVE;
1174 static void
1175 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1176 ui64_t *stats)
1178 char xstatout[4];
1179 char *xp = xstatout;
1180 NYD_ENTER;
1182 if (mp->m_flag & MFLAGGED)
1183 *xp++ = 'F';
1184 if (mp->m_flag & MANSWERED)
1185 *xp++ = 'A';
1186 if (mp->m_flag & MDRAFTED)
1187 *xp++ = 'T';
1188 *xp = 0;
1189 if (xstatout[0]) {
1190 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1191 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1192 if (i > 0 && stats != NULL)
1193 *stats += i;
1195 NYD_LEAVE;
1198 static void
1199 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1201 char const *froma, *date, *nl;
1202 int i;
1203 NYD_ENTER;
1205 if (ip != NULL && ip->m_from != NULL) {
1206 froma = ip->m_from;
1207 date = fakedate(ip->m_time);
1208 nl = "\n";
1209 } else {
1210 froma = myname;
1211 date = time_current.tc_ctime;
1212 nl = "";
1215 colour_put(fp, COLOURSPEC_FROM_);
1216 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1217 colour_reset(fp);
1218 if (i > 0 && stats != NULL)
1219 *stats += i;
1220 NYD_LEAVE;
1223 FL int
1224 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1225 char const *prefix, enum sendaction action, ui64_t *stats)
1227 struct quoteflt qf;
1228 size_t cnt, sz, i;
1229 FILE *ibuf;
1230 enum mime_parse_flags mpf;
1231 struct mimepart *ip;
1232 int rv = -1, c;
1233 NYD_ENTER;
1235 if (mp == dot && action != SEND_TOSRCH)
1236 pstate |= PS_DID_PRINT_DOT;
1237 if (stats != NULL)
1238 *stats = 0;
1239 quoteflt_init(&qf, prefix);
1241 /* First line is the From_ line, so no headers there to worry about */
1242 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1243 goto jleave;
1245 cnt = mp->m_size;
1246 sz = 0;
1248 struct str const *cpre, *csuf;
1249 #ifdef HAVE_COLOUR
1250 cpre = colour_get(COLOURSPEC_FROM_);
1251 csuf = colour_get(COLOURSPEC_RESET);
1252 #else
1253 cpre = csuf = NULL;
1254 #endif
1255 if (mp->m_flag & MNOFROM) {
1256 if (doign != allignore && doign != fwdignore && action != SEND_RFC822)
1257 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1258 (cpre != NULL ? cpre->s : ""),
1259 (int)qf.qf_pfix_len, (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""),
1260 fakefrom(mp), fakedate(mp->m_time),
1261 (csuf != NULL ? csuf->s : ""));
1262 } else {
1263 if (doign != allignore && doign != fwdignore && action != SEND_RFC822) {
1264 if (qf.qf_pfix_len > 0) {
1265 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1266 if (i != qf.qf_pfix_len)
1267 goto jleave;
1268 sz += i;
1270 #ifdef HAVE_COLOUR
1271 if (cpre != NULL) {
1272 fputs(cpre->s, obuf);
1273 cpre = (struct str const*)0x1;
1275 #endif
1278 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1279 if (doign != allignore && doign != fwdignore &&
1280 action != SEND_RFC822) {
1281 #ifdef HAVE_COLOUR
1282 if (c == '\n' && csuf != NULL) {
1283 cpre = (struct str const*)0x1;
1284 fputs(csuf->s, obuf);
1286 #endif
1287 putc(c, obuf);
1288 sz++;
1290 --cnt;
1291 if (c == '\n')
1292 break;
1295 #ifdef HAVE_COLOUR
1296 if (csuf != NULL && cpre != (struct str const*)0x1)
1297 fputs(csuf->s, obuf);
1298 #endif
1301 if (sz > 0 && stats != NULL)
1302 *stats += sz;
1304 mpf = MIME_PARSE_NONE;
1305 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1306 mpf |= MIME_PARSE_DECRYPT | MIME_PARSE_PARTS;
1307 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1308 goto jleave;
1310 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1311 jleave:
1312 quoteflt_destroy(&qf);
1313 NYD_LEAVE;
1314 return rv;
1317 /* s-it-mode */