cc-test.sh: do a simple \`Resend' test
[s-mailx.git] / send.c
blobbaaf51e6ae4084a7d817990d4cc3dc5aa049af14
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 #undef _X
261 #define _X MIN(NAME_MAX / 4, 16)
262 s.s = getrandstring(_X);
263 if (mpp == NULL)
264 cp = s.s;
265 else if (*cp == '\0') {
266 size_t i;
268 if ( (((cp = mpp->m_ct_type_usr_ovwr) == NULL || *cp == '\0') &&
269 ((cp = mpp->m_ct_type_plain) == NULL || *cp == '\0')) ||
270 ((sh = strrchr(cp, '/')) == NULL || *++sh == '\0') ||
271 (i = strlen(sh)) > _X)
272 cp = s.s;
273 else {
274 LCTA(_X >= 13);
276 s.s[0] = '-';
277 cp = s.s = savecat(sh, s.s);
278 s.s[i + _X - 4] = '.';
281 #undef _X
282 env_addon[1] = str_concat_csvl(&s, NAILENV_FILENAME_GENERATED, "=", cp,
283 NULL)->s;
285 /* NAIL_CONTENT{,_EVIDENCE} */
286 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
287 cp = "";
288 env_addon[2] = str_concat_csvl(&s, NAILENV_CONTENT, "=", cp, NULL)->s;
290 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
291 cp = mpp->m_ct_type_usr_ovwr;
292 env_addon[3] = str_concat_csvl(&s, NAILENV_CONTENT_EVIDENCE, "=", cp,
293 NULL)->s;
295 env_addon[4] = str_concat_csvl(&s, NAILENV_TMPDIR, "=", tempdir, NULL)->s;
296 env_addon[5] = str_concat_csvl(&s, "TMPDIR", "=", tempdir, NULL)->s;
298 env_addon[6] = NULL;
300 if ((sh = ok_vlook(SHELL)) == NULL)
301 sh = XSHELL;
303 rbuf = Popen(pipecomm, "W", sh, env_addon, (async ? -1 : fileno(*qbuf)));
304 #ifdef HAVE_FILTER_HTML_TAGSOUP
305 jafter_tagsoup_hack:
306 #endif
307 if (rbuf == NULL)
308 n_err(_("Cannot run MIME type handler \"%s\": %s\n"),
309 pipecomm, strerror(errno));
310 else {
311 fflush(*qbuf);
312 if (*qbuf != stdout)
313 fflush(stdout);
315 NYD_LEAVE;
316 return rbuf;
319 SINLINE ssize_t
320 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
321 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *rest)
323 ssize_t sz = 0, n;
324 int flags;
325 NYD_ENTER;
327 #if 0
328 Well ... it turns out to not work like that since of course a valid
329 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
330 after an empty line has been seen, which cannot be detected that easily
331 right here!
332 ifdef HAVE_DEBUG /* TODO assert legacy */
333 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
334 * TODO other input situations handle RFC 4155 OR, if newly generated,
335 * TODO enforce quoted-printable if there is From_, as "required" by
336 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
337 * TODO if it may happen in this path, we should just treat decryption
338 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
339 if (action == SEND_MBOX || action == SEND_DECRYPT)
340 assert(!is_head(buf, len, TRU1));
341 #else
342 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
343 is_head(buf, len, TRU1)) {
344 putc('>', fp);
345 ++sz;
347 #endif
349 flags = ((int)action & _TD_EOF);
350 action &= ~_TD_EOF;
351 n = mime_write(buf, len, fp,
352 action == SEND_MBOX ? CONV_NONE : convert,
353 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
354 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
355 ? TD_ISPR | TD_ICONV
356 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
357 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
358 qf, rest);
359 if (n < 0)
360 sz = n;
361 else if (n > 0) {
362 sz += n;
363 if (stats != NULL)
364 *stats += sz;
366 NYD_LEAVE;
367 return sz;
370 static void
371 _send_onpipe(int signo)
373 NYD_X; /* Signal handler */
374 UNUSED(signo);
375 siglongjmp(_send_pipejmp, 1);
378 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
379 static int __sendp_sig; /* TODO someday.. */
380 static sighandler_type __sendp_opipe;
381 static void
382 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
384 NYD_X; /* Signal handler */
385 __sendp_sig = sig;
386 siglongjmp(__sendp_actjmp, 1);
389 static sigjmp_buf __sndalter_actjmp; /* TODO someday.. */
390 static int __sndalter_sig; /* TODO someday.. */
391 static void
392 __sndalter_onsig(int sig) /* TODO someday, we won't need it no more */
394 NYD_X; /* Signal handler */
395 __sndalter_sig = sig;
396 siglongjmp(__sndalter_actjmp, 1);
399 static int
400 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
401 struct ignoretab *doign, struct quoteflt *qf,
402 enum sendaction volatile action, ui64_t * volatile stats, int level)
404 int volatile ispipe, rv = 0;
405 struct str rest;
406 char *line = NULL, *cp, *cp2, *start;
407 char const *pipecomm = NULL;
408 size_t linesize = 0, linelen, cnt;
409 int dostat, infld = 0, ignoring = 1, isenc, c;
410 struct mimepart *volatile np;
411 FILE * volatile ibuf = NULL, * volatile pbuf = obuf, * volatile qbuf = obuf,
412 *origobuf = obuf;
413 enum conversion volatile convert;
414 sighandler_type volatile oldpipe = SIG_DFL;
415 long lineno = 0;
416 NYD_ENTER;
418 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
419 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
420 goto jskip;
422 dostat = 0;
423 if (level == 0) {
424 if (doign != NULL) {
425 if (!is_ign("status", 6, doign))
426 dostat |= 1;
427 if (!is_ign("x-status", 8, doign))
428 dostat |= 2;
429 } else
430 dostat = 3;
432 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
433 rv = -1;
434 goto jleave;
436 cnt = ip->m_size;
438 if (ip->m_mimecontent == MIME_DISCARD)
439 goto jskip;
441 if (!(ip->m_flag & MNOFROM))
442 while (cnt && (c = getc(ibuf)) != EOF) {
443 cnt--;
444 if (c == '\n')
445 break;
447 isenc = 0;
448 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
449 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
450 action == SEND_TOSRCH)
451 ? CONV_FROMHDR : CONV_NONE;
453 /* Work the headers */
454 quoteflt_reset(qf, obuf);
455 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
456 ++lineno;
457 if (line[0] == '\n') {
458 /* If line is blank, we've reached end of headers, so force out
459 * status: field and note that we are no longer in header fields */
460 if (dostat & 1)
461 statusput(zmp, obuf, qf, stats);
462 if (dostat & 2)
463 xstatusput(zmp, obuf, qf, stats);
464 if (doign != allignore)
465 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
466 break;
469 isenc &= ~1;
470 if (infld && blankchar(line[0])) {
471 /* If this line is a continuation (SP / HT) of a previous header
472 * field, determine if the start of the line is a MIME encoded word */
473 if (isenc & 2) {
474 for (cp = line; blankchar(*cp); ++cp);
475 if (cp > line && linelen - PTR2SIZE(cp - line) > 8 &&
476 cp[0] == '=' && cp[1] == '?')
477 isenc |= 1;
479 } else {
480 /* Pick up the header field if we have one */
481 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
483 cp2 = cp;
484 while (spacechar(*cp))
485 ++cp;
486 if (cp[0] != ':' && level == 0 && lineno == 1) {
487 /* Not a header line, force out status: This happens in uucp style
488 * mail where there are no headers at all */
489 if (dostat & 1)
490 statusput(zmp, obuf, qf, stats);
491 if (dostat & 2)
492 xstatusput(zmp, obuf, qf, stats);
493 if (doign != allignore)
494 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
495 break;
498 /* If it is an ignored field and we care about such things, skip it.
499 * Misuse dostat also for another bit xxx use a bitenum + for more */
500 if (ok_blook(keep_content_length))
501 dostat |= 1 << 2;
502 c = *cp2;
503 *cp2 = 0; /* temporarily null terminate */
504 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
505 (action == SEND_MBOX && !(dostat & (1 << 2)) &&
506 (!asccasecmp(line, "content-length") ||
507 !asccasecmp(line, "lines"))))
508 ignoring = 1;
509 else if (!asccasecmp(line, "status")) {
510 /* If field is "status," go compute and print real Status: field */
511 if (dostat & 1) {
512 statusput(zmp, obuf, qf, stats);
513 dostat &= ~1;
514 ignoring = 1;
516 } else if (!asccasecmp(line, "x-status")) {
517 /* If field is "status," go compute and print real Status: field */
518 if (dostat & 2) {
519 xstatusput(zmp, obuf, qf, stats);
520 dostat &= ~2;
521 ignoring = 1;
523 } else {
524 ignoring = 0;
525 /* For colourization we need the complete line, so save it */
526 /* XXX This is all temporary (colour belongs into backend), so
527 * XXX use pipecomm as a temporary storage in the meanwhile */
528 #ifdef HAVE_COLOUR
529 if (colour_table != NULL)
530 pipecomm = savestrbuf(line, PTR2SIZE(cp2 - line));
531 #endif
533 *cp2 = c;
534 dostat &= ~(1 << 2);
535 infld = 1;
538 /* Determine if the end of the line is a MIME encoded word */
539 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
540 * TODO with header follow lines, and it should be up to the backend
541 * TODO what happens and what not, i.e., it doesn't matter wether it's
542 * TODO a MIME-encoded word or not, as long as a single separating space
543 * TODO remains in between lines (the MIME stuff will correctly remove
544 * TODO whitespace in between multiple adjacent encoded words) */
545 isenc &= ~2;
546 if (cnt && (c = getc(ibuf)) != EOF) {
547 if (blankchar(c)) {
548 cp = line + linelen - 1;
549 if (linelen > 0 && *cp == '\n')
550 --cp;
551 while (cp >= line && whitechar(*cp))
552 --cp;
553 if (PTR2SIZE(cp - line > 8) && cp[0] == '=' && cp[-1] == '?')
554 isenc |= 2;
556 ungetc(c, ibuf);
559 if (!ignoring) {
560 size_t len = linelen;
561 start = line;
562 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
563 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
564 action == SEND_TOSRCH) {
565 /* Strip blank characters if two MIME-encoded words follow on
566 * continuing lines */
567 if (isenc & 1)
568 while (len > 0 && blankchar(*start)) {
569 ++start;
570 --len;
572 if (isenc & 2)
573 if (len > 0 && start[len - 1] == '\n')
574 --len;
575 while (len > 0 && blankchar(start[len - 1]))
576 --len;
578 #ifdef HAVE_COLOUR
580 bool_t colour_stripped = FAL0;
581 if (pipecomm != NULL) {
582 colour_put_header(obuf, pipecomm);
583 if (len > 0 && start[len - 1] == '\n') {
584 colour_stripped = TRU1;
585 --len;
588 #endif
589 _out(start, len, obuf, convert, action, qf, stats, NULL);
590 #ifdef HAVE_COLOUR
591 if (pipecomm != NULL) {
592 colour_reset(obuf); /* XXX reset after \n!! */
593 if (colour_stripped)
594 putc('\n', obuf);
597 #endif
598 if (ferror(obuf)) {
599 free(line);
600 rv = -1;
601 goto jleave;
605 quoteflt_flush(qf);
606 free(line);
607 line = NULL;
608 pipecomm = NULL;
610 jskip:
611 switch (ip->m_mimecontent) {
612 case MIME_822:
613 switch (action) {
614 case SEND_TODISP:
615 case SEND_TODISP_ALL:
616 case SEND_QUOTE:
617 case SEND_QUOTE_ALL:
618 if (ok_blook(rfc822_body_from_)) {
619 if (qf->qf_pfix_len > 0) {
620 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
621 qf->qf_pfix_len, obuf);
622 if (i == qf->qf_pfix_len && stats != NULL)
623 *stats += i;
625 put_from_(obuf, ip->m_multipart, stats);
627 /* FALLTHRU */
628 case SEND_TOSRCH:
629 case SEND_DECRYPT:
630 goto jmulti;
631 case SEND_TOFILE:
632 case SEND_TOPIPE:
633 if (ok_blook(rfc822_body_from_))
634 put_from_(obuf, ip->m_multipart, stats);
635 /* FALLTHRU */
636 case SEND_MBOX:
637 case SEND_RFC822:
638 case SEND_SHOW:
639 break;
641 break;
642 case MIME_TEXT_HTML:
643 case MIME_TEXT:
644 case MIME_TEXT_PLAIN:
645 switch (action) {
646 case SEND_TODISP:
647 case SEND_TODISP_ALL:
648 case SEND_QUOTE:
649 case SEND_QUOTE_ALL:
650 ispipe = TRU1;
651 switch (_pipecmd(&pipecomm, ip)) {
652 case PIPE_MSG:
653 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
654 stats, NULL);
655 /* We would print this as plain text, so better force going home */
656 goto jleave;
657 case PIPE_TEXT:
658 case PIPE_COMM:
659 case PIPE_NULL:
660 break;
661 case PIPE_ASYNC:
662 ispipe = FAL0;
663 break;
665 /* FALLTRHU */
666 default:
667 break;
669 break;
670 case MIME_DISCARD:
671 if (action != SEND_DECRYPT)
672 goto jleave;
673 break;
674 case MIME_PKCS7:
675 if (action != SEND_MBOX && action != SEND_RFC822 &&
676 action != SEND_SHOW && ip->m_multipart != NULL)
677 goto jmulti;
678 /* FALLTHRU */
679 default:
680 switch (action) {
681 case SEND_TODISP:
682 case SEND_TODISP_ALL:
683 case SEND_QUOTE:
684 case SEND_QUOTE_ALL:
685 ispipe = TRU1;
686 switch (_pipecmd(&pipecomm, ip)) {
687 case PIPE_MSG:
688 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
689 stats, NULL);
690 pipecomm = NULL;
691 break;
692 case PIPE_ASYNC:
693 ispipe = FAL0;
694 /* FALLTHRU */
695 case PIPE_COMM:
696 case PIPE_NULL:
697 break;
698 case PIPE_TEXT:
699 goto jcopyout; /* break; break; */
701 if (pipecomm != NULL)
702 break;
703 if (level == 0 && cnt) {
704 char const *x = _("[Binary content]\n");
705 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
707 goto jleave;
708 case SEND_TOFILE:
709 case SEND_TOPIPE:
710 case SEND_TOSRCH:
711 case SEND_DECRYPT:
712 case SEND_MBOX:
713 case SEND_RFC822:
714 case SEND_SHOW:
715 break;
717 break;
718 case MIME_ALTERNATIVE:
719 case MIME_RELATED:
720 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
721 !ok_blook(print_alternatives)) {
722 /* XXX This (a) should not remain (b) should be own fun */
723 struct mpstack {
724 struct mpstack *outer;
725 struct mimepart *mp;
726 } outermost, * volatile curr = &outermost, * volatile mpsp;
727 sighandler_type volatile opsh, oish, ohsh;
728 size_t volatile partcnt = 0/* silence CC */;
729 bool_t volatile neednl = FAL0;
731 curr->outer = NULL;
732 curr->mp = ip;
734 __sndalter_sig = 0;
735 opsh = safe_signal(SIGPIPE, &__sndalter_onsig);
736 oish = safe_signal(SIGINT, &__sndalter_onsig);
737 ohsh = safe_signal(SIGHUP, &__sndalter_onsig);
738 if (sigsetjmp(__sndalter_actjmp, 1)) {
739 rv = -1;
740 goto jalter_unroll;
743 for (np = ip->m_multipart;;) {
744 partcnt = 0;
745 jalter_redo:
746 for (; np != NULL; np = np->m_nextpart) {
747 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
748 if (neednl)
749 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
750 _print_part_info(obuf, np, doign, level, qf, stats);
752 neednl = TRU1;
754 switch (np->m_mimecontent) {
755 case MIME_ALTERNATIVE:
756 case MIME_RELATED:
757 case MIME_MULTI:
758 case MIME_DIGEST:
759 mpsp = ac_alloc(sizeof *mpsp);
760 mpsp->outer = curr;
761 mpsp->mp = np->m_multipart;
762 curr->mp = np;
763 curr = mpsp;
764 np = mpsp->mp;
765 neednl = FAL0;
766 goto jalter_redo;
767 default:
768 switch (_pipecmd(&pipecomm, np)) {
769 default:
770 continue;
771 case PIPE_TEXT:
772 break;
774 /* FALLTHRU */
775 case MIME_TEXT_PLAIN:
776 ++partcnt;
777 if (action == SEND_QUOTE && partcnt > 1 &&
778 ip->m_mimecontent == MIME_ALTERNATIVE)
779 break;
780 quoteflt_flush(qf);
781 if (action == SEND_QUOTE && partcnt > 1) {
782 struct quoteflt *dummy = quoteflt_dummy();
783 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
784 NULL);
785 quoteflt_flush(dummy);
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 for (;; curr = mpsp) {
795 if ((mpsp = curr->outer) == NULL)
796 break;
797 ac_free(curr);
800 break;
804 mpsp = curr->outer;
805 if (mpsp == NULL)
806 break;
807 ac_free(curr);
808 curr = mpsp;
809 np = curr->mp->m_nextpart;
811 safe_signal(SIGHUP, ohsh);
812 safe_signal(SIGINT, oish);
813 safe_signal(SIGPIPE, opsh);
814 if (__sndalter_sig != 0)
815 n_raise(__sndalter_sig);
816 goto jleave;
818 /* FALLTHRU */
819 case MIME_MULTI:
820 case MIME_DIGEST:
821 switch (action) {
822 case SEND_TODISP:
823 case SEND_TODISP_ALL:
824 case SEND_QUOTE:
825 case SEND_QUOTE_ALL:
826 case SEND_TOFILE:
827 case SEND_TOPIPE:
828 case SEND_TOSRCH:
829 case SEND_DECRYPT:
830 jmulti:
831 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
832 ip->m_multipart != NULL &&
833 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
834 ip->m_multipart->m_nextpart == NULL) {
835 char const *x = _("[Missing multipart boundary - use \"show\" "
836 "to display the raw message]\n");
837 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
840 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
841 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
842 continue;
843 ispipe = FAL0;
844 switch (action) {
845 case SEND_TOFILE:
846 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
847 break;
848 stats = NULL;
849 if ((obuf = newfile(np, UNVOLATILE(&ispipe))) == NULL)
850 continue;
851 if (!ispipe)
852 break;
853 if (sigsetjmp(_send_pipejmp, 1)) {
854 rv = -1;
855 goto jpipe_close;
857 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
858 break;
859 case SEND_TODISP:
860 case SEND_TODISP_ALL:
861 case SEND_QUOTE_ALL:
862 if (ip->m_mimecontent != MIME_MULTI &&
863 ip->m_mimecontent != MIME_ALTERNATIVE &&
864 ip->m_mimecontent != MIME_RELATED &&
865 ip->m_mimecontent != MIME_DIGEST)
866 break;
867 _print_part_info(obuf, np, doign, level, qf, stats);
868 break;
869 case SEND_MBOX:
870 case SEND_RFC822:
871 case SEND_SHOW:
872 case SEND_TOSRCH:
873 case SEND_QUOTE:
874 case SEND_DECRYPT:
875 case SEND_TOPIPE:
876 break;
879 quoteflt_flush(qf);
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 break;
886 if (action == SEND_TOFILE && obuf != origobuf) {
887 if (!ispipe)
888 Fclose(obuf);
889 else {
890 jpipe_close:
891 safe_signal(SIGPIPE, SIG_IGN);
892 Pclose(obuf, TRU1);
893 safe_signal(SIGPIPE, oldpipe);
897 goto jleave;
898 case SEND_MBOX:
899 case SEND_RFC822:
900 case SEND_SHOW:
901 break;
905 /* Copy out message body */
906 jcopyout:
907 if (doign == allignore && level == 0) /* skip final blank line */
908 --cnt;
909 switch (ip->m_mime_enc) {
910 case MIMEE_BIN:
911 case MIMEE_7B:
912 case MIMEE_8B:
913 convert = CONV_NONE;
914 break;
915 case MIMEE_QP:
916 convert = CONV_FROMQP;
917 break;
918 case MIMEE_B64:
919 switch (ip->m_mimecontent) {
920 case MIME_TEXT:
921 case MIME_TEXT_PLAIN:
922 case MIME_TEXT_HTML:
923 convert = CONV_FROMB64_T;
924 break;
925 default:
926 convert = CONV_FROMB64;
928 break;
929 default:
930 convert = CONV_NONE;
933 if (action == SEND_DECRYPT || action == SEND_MBOX ||
934 action == SEND_RFC822 || action == SEND_SHOW)
935 convert = CONV_NONE;
936 #ifdef HAVE_ICONV
937 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
938 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
939 action == SEND_TOSRCH) &&
940 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
941 ip->m_mimecontent == MIME_TEXT_HTML ||
942 ip->m_mimecontent == MIME_TEXT)) {
943 char const *tcs = charset_get_lc();
945 if (iconvd != (iconv_t)-1)
946 n_iconv_close(iconvd);
947 /* TODO Since Base64 has an odd 4:3 relation in between input
948 * TODO and output an input line may end with a partial
949 * TODO multibyte character; this is no problem at all unless
950 * TODO we send to the display or whatever, i.e., ensure
951 * TODO makeprint() or something; to avoid this trap, *force*
952 * TODO iconv(), in which case this layer will handle leftovers
953 * TODO correctly */
954 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
955 asccasecmp(charset_get_7bit(), ip->m_charset))) {
956 iconvd = n_iconv_open(tcs, ip->m_charset);
958 * TODO errors should DEFINETELY not be scrolled away!
959 * TODO what about an error buffer (think old shsp(1)),
960 * TODO re-dump errors since last snapshot when the
961 * TODO command loop enters again? i.e., at least print
962 * TODO "There were errors ?" before the next prompt,
963 * TODO so that the user can look at the error buffer?
965 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
966 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
967 /*rv = 1; goto jleave;*/
971 #endif
973 if (pipecomm != NULL && (action == SEND_TODISP ||
974 action == SEND_TODISP_ALL || action == SEND_QUOTE ||
975 action == SEND_QUOTE_ALL)) {
976 qbuf = obuf;
977 pbuf = _pipefile(pipecomm, ip, UNVOLATILE(&qbuf),
978 (action == SEND_QUOTE || action == SEND_QUOTE_ALL), !ispipe);
979 if (pbuf == NULL) {
980 #ifdef HAVE_ICONV
981 if (iconvd != (iconv_t)-1)
982 n_iconv_close(iconvd);
983 #endif
984 rv = -1;
985 goto jleave;
987 action = SEND_TOPIPE;
988 if (pbuf != qbuf) {
989 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
990 if (sigsetjmp(_send_pipejmp, 1))
991 goto jend;
993 } else
994 pbuf = qbuf = obuf;
997 bool_t volatile eof;
998 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
999 ui64_t *save_stats = stats;
1001 if (pbuf != origobuf) {
1002 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1003 stats = NULL;
1005 eof = FAL0;
1006 rest.s = NULL;
1007 rest.l = 0;
1009 if (pbuf == qbuf) {
1010 __sendp_sig = 0;
1011 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1012 if (sigsetjmp(__sendp_actjmp, 1)) {
1013 if (rest.s != NULL)
1014 free(rest.s);
1015 free(line);
1016 #ifdef HAVE_ICONV
1017 if (iconvd != (iconv_t)-1)
1018 n_iconv_close(iconvd);
1019 #endif
1020 safe_signal(SIGPIPE, __sendp_opipe);
1021 n_raise(__sendp_sig);
1025 quoteflt_reset(qf, pbuf);
1026 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1027 joutln:
1028 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1029 ferror(pbuf)) {
1030 rv = -1; /* XXX Should bail away?! */
1031 break;
1034 if (!eof && rest.l != 0) {
1035 linelen = 0;
1036 eof = TRU1;
1037 action |= _TD_EOF;
1038 goto joutln;
1040 if (pbuf == qbuf)
1041 safe_signal(SIGPIPE, __sendp_opipe);
1043 quoteflt_flush(qf);
1044 if (rest.s != NULL)
1045 free(rest.s);
1047 if (pbuf != origobuf) {
1048 qf->qf_pfix_len = save_qf_pfix_len;
1049 stats = save_stats;
1053 jend:
1054 if (line != NULL)
1055 free(line);
1056 if (pbuf != qbuf) {
1057 safe_signal(SIGPIPE, SIG_IGN);
1058 Pclose(pbuf, ispipe);
1059 safe_signal(SIGPIPE, oldpipe);
1060 if (qbuf != NULL && qbuf != obuf)
1061 pipecpy(qbuf, obuf, origobuf, qf, stats);
1063 #ifdef HAVE_ICONV
1064 if (iconvd != (iconv_t)-1)
1065 n_iconv_close(iconvd);
1066 #endif
1067 jleave:
1068 NYD_LEAVE;
1069 return rv;
1072 static FILE *
1073 newfile(struct mimepart *ip, int *ispipe)
1075 struct str in, out;
1076 char *f;
1077 FILE *fp;
1078 NYD_ENTER;
1080 f = ip->m_filename;
1081 *ispipe = 0;
1083 if (f != NULL && f != (char*)-1) {
1084 in.s = f;
1085 in.l = strlen(f);
1086 makeprint(&in, &out);
1087 out.l = delctrl(out.s, out.l);
1088 f = savestrbuf(out.s, out.l);
1089 free(out.s);
1092 if (options & OPT_INTERACTIVE) {
1093 char *f2, *f3;
1094 jgetname:
1095 printf(_("Enter filename for part %s (%s)"),
1096 (ip->m_partstring != NULL) ? ip->m_partstring : "?",
1097 ip->m_ct_type_plain);
1098 f2 = readstr_input(": ", (f != (char*)-1 && f != NULL)
1099 ? fexpand_nshell_quote(f) : NULL);
1100 if (f2 == NULL || *f2 == '\0') {
1101 n_err(_("... skipping this\n"));
1102 fp = NULL;
1103 goto jleave;
1104 } else if (*f2 == '|')
1105 /* Pipes are expanded by the shell */
1106 f = f2;
1107 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NSHELL)) == NULL)
1108 /* (Error message written by fexpand()) */
1109 goto jgetname;
1110 else
1111 f = f3;
1113 if (f == NULL || f == (char*)-1) {
1114 fp = NULL;
1115 goto jleave;
1118 if (*f == '|') {
1119 char const *cp;
1120 cp = ok_vlook(SHELL);
1121 if (cp == NULL)
1122 cp = XSHELL;
1123 fp = Popen(f + 1, "w", cp, NULL, 1);
1124 if (!(*ispipe = (fp != NULL)))
1125 n_perr(f, 0);
1126 } else {
1127 if ((fp = Fopen(f, "w")) == NULL)
1128 n_err(_("Cannot open \"%s\"\n"), f);
1130 jleave:
1131 NYD_LEAVE;
1132 return fp;
1135 static void
1136 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1137 ui64_t *stats)
1139 char *line = NULL; /* TODO line pool */
1140 size_t linesize = 0, linelen, cnt;
1141 ssize_t all_sz, sz;
1142 NYD_ENTER;
1144 fflush(pipebuf);
1145 rewind(pipebuf);
1146 cnt = fsize(pipebuf);
1147 all_sz = 0;
1149 quoteflt_reset(qf, outbuf);
1150 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1151 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1152 break;
1153 all_sz += sz;
1155 if ((sz = quoteflt_flush(qf)) > 0)
1156 all_sz += sz;
1157 if (line)
1158 free(line);
1160 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1161 *stats += all_sz;
1162 Fclose(pipebuf);
1163 NYD_LEAVE;
1166 static void
1167 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1168 ui64_t *stats)
1170 char statout[3], *cp = statout;
1171 NYD_ENTER;
1173 if (mp->m_flag & MREAD)
1174 *cp++ = 'R';
1175 if (!(mp->m_flag & MNEW))
1176 *cp++ = 'O';
1177 *cp = 0;
1178 if (statout[0]) {
1179 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1180 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1181 if (i > 0 && stats != NULL)
1182 *stats += i;
1184 NYD_LEAVE;
1187 static void
1188 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1189 ui64_t *stats)
1191 char xstatout[4];
1192 char *xp = xstatout;
1193 NYD_ENTER;
1195 if (mp->m_flag & MFLAGGED)
1196 *xp++ = 'F';
1197 if (mp->m_flag & MANSWERED)
1198 *xp++ = 'A';
1199 if (mp->m_flag & MDRAFTED)
1200 *xp++ = 'T';
1201 *xp = 0;
1202 if (xstatout[0]) {
1203 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1204 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1205 if (i > 0 && stats != NULL)
1206 *stats += i;
1208 NYD_LEAVE;
1211 static void
1212 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1214 char const *froma, *date, *nl;
1215 int i;
1216 NYD_ENTER;
1218 if (ip != NULL && ip->m_from != NULL) {
1219 froma = ip->m_from;
1220 date = fakedate(ip->m_time);
1221 nl = "\n";
1222 } else {
1223 froma = myname;
1224 date = time_current.tc_ctime;
1225 nl = "";
1228 colour_put(fp, COLOURSPEC_FROM_);
1229 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1230 colour_reset(fp);
1231 if (i > 0 && stats != NULL)
1232 *stats += i;
1233 NYD_LEAVE;
1236 FL int
1237 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1238 char const *prefix, enum sendaction action, ui64_t *stats)
1240 struct quoteflt qf;
1241 size_t cnt, sz, i;
1242 FILE *ibuf;
1243 enum mime_parse_flags mpf;
1244 struct mimepart *ip;
1245 int rv = -1, c;
1246 NYD_ENTER;
1248 if (mp == dot && action != SEND_TOSRCH)
1249 pstate |= PS_DID_PRINT_DOT;
1250 if (stats != NULL)
1251 *stats = 0;
1252 quoteflt_init(&qf, prefix);
1254 /* First line is the From_ line, so no headers there to worry about */
1255 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1256 goto jleave;
1258 cnt = mp->m_size;
1259 sz = 0;
1261 struct str const *cpre, *csuf;
1262 #ifdef HAVE_COLOUR
1263 cpre = colour_get(COLOURSPEC_FROM_);
1264 csuf = colour_get(COLOURSPEC_RESET);
1265 #else
1266 cpre = csuf = NULL;
1267 #endif
1268 if (mp->m_flag & MNOFROM) {
1269 if (doign != allignore && doign != fwdignore && action != SEND_RFC822)
1270 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1271 (cpre != NULL ? cpre->s : ""),
1272 (int)qf.qf_pfix_len, (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""),
1273 fakefrom(mp), fakedate(mp->m_time),
1274 (csuf != NULL ? csuf->s : ""));
1275 } else {
1276 if (doign != allignore && doign != fwdignore && action != SEND_RFC822) {
1277 if (qf.qf_pfix_len > 0) {
1278 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1279 if (i != qf.qf_pfix_len)
1280 goto jleave;
1281 sz += i;
1283 #ifdef HAVE_COLOUR
1284 if (cpre != NULL) {
1285 fputs(cpre->s, obuf);
1286 cpre = (struct str const*)0x1;
1288 #endif
1291 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1292 if (doign != allignore && doign != fwdignore &&
1293 action != SEND_RFC822) {
1294 #ifdef HAVE_COLOUR
1295 if (c == '\n' && csuf != NULL) {
1296 cpre = (struct str const*)0x1;
1297 fputs(csuf->s, obuf);
1299 #endif
1300 putc(c, obuf);
1301 sz++;
1303 --cnt;
1304 if (c == '\n')
1305 break;
1308 #ifdef HAVE_COLOUR
1309 if (csuf != NULL && cpre != (struct str const*)0x1)
1310 fputs(csuf->s, obuf);
1311 #endif
1314 if (sz > 0 && stats != NULL)
1315 *stats += sz;
1317 mpf = MIME_PARSE_NONE;
1318 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1319 mpf |= MIME_PARSE_DECRYPT | MIME_PARSE_PARTS;
1320 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1321 goto jleave;
1323 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1324 jleave:
1325 quoteflt_destroy(&qf);
1326 NYD_LEAVE;
1327 return rv;
1330 /* s-it-mode */