Review: quit.c
[s-mailx.git] / send.c
blobbd8f19fa49c87486c8f32aa6859e066af73fc899
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 - 2014 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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 enum pipeflags {
45 PIPE_NULL, /* No pipe- mimetype handler */
46 PIPE_COMM, /* Normal command */
47 PIPE_ASYNC, /* Normal command, run asynchronous */
48 PIPE_TEXT, /* @ special command to force treatment as text */
49 PIPE_MSG /* Display message (returned as command string) */
52 enum parseflags {
53 PARSE_DEFAULT = 0,
54 PARSE_DECRYPT = 01,
55 PARSE_PARTS = 02
58 static sigjmp_buf _send_pipejmp;
60 /* */
61 static struct mimepart *parsemsg(struct message *mp, enum parseflags pf);
62 static enum okay parsepart(struct message *zmp, struct mimepart *ip,
63 enum parseflags pf, int level);
64 static void parse822(struct message *zmp, struct mimepart *ip,
65 enum parseflags pf, int level);
66 #ifdef HAVE_SSL
67 static void parsepkcs7(struct message *zmp, struct mimepart *ip,
68 enum parseflags pf, int level);
69 #endif
70 static void _parsemultipart(struct message *zmp,
71 struct mimepart *ip, enum parseflags pf, int level);
72 static void __newpart(struct mimepart *ip, struct mimepart **np,
73 off_t offs, int *part);
74 static void __endpart(struct mimepart **np, off_t xoffs, long lines);
76 /* Going for user display, print Part: info string */
77 static void _print_part_info(struct str *out, struct mimepart *mip,
78 struct ignoretab *doign, int level);
80 /* Query possible pipe command for MIME type */
81 static enum pipeflags _pipecmd(char **result, char const *content_type);
83 /* Create a pipe */
84 static FILE * _pipefile(char const *pipecomm, FILE **qbuf, bool_t quote,
85 bool_t async);
87 /* Adjust output statistics */
88 SINLINE void _addstats(off_t *stats, off_t lines, off_t bytes);
90 /* Call mime_write() as approbiate and adjust statistics */
91 SINLINE ssize_t _out(char const *buf, size_t len, FILE *fp,
92 enum conversion convert, enum sendaction action,
93 struct quoteflt *qf, off_t *stats,
94 struct str *rest);
96 /* SIGPIPE handler */
97 static void _send_onpipe(int signo);
99 /* Send one part */
100 static int sendpart(struct message *zmp, struct mimepart *ip,
101 FILE *obuf, struct ignoretab *doign,
102 struct quoteflt *qf, enum sendaction action,
103 off_t *stats, int level);
105 /* Get a file for an attachment */
106 static FILE * newfile(struct mimepart *ip, int *ispipe);
108 static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
109 struct quoteflt *qf, off_t *stats);
111 /* Output a reasonable looking status field */
112 static void statusput(const struct message *mp, FILE *obuf,
113 struct quoteflt *qf, off_t *stats);
114 static void xstatusput(const struct message *mp, FILE *obuf,
115 struct quoteflt *qf, off_t *stats);
117 static void put_from_(FILE *fp, struct mimepart *ip, off_t *stats);
119 static struct mimepart *
120 parsemsg(struct message *mp, enum parseflags pf)
122 struct mimepart *ip;
123 NYD_ENTER;
125 ip = csalloc(1, sizeof *ip);
126 ip->m_flag = mp->m_flag;
127 ip->m_have = mp->m_have;
128 ip->m_block = mp->m_block;
129 ip->m_offset = mp->m_offset;
130 ip->m_size = mp->m_size;
131 ip->m_xsize = mp->m_xsize;
132 ip->m_lines = mp->m_lines;
133 ip->m_xlines = mp->m_lines;
134 if (parsepart(mp, ip, pf, 0) != OKAY)
135 ip = NULL;
136 NYD_LEAVE;
137 return ip;
140 static enum okay
141 parsepart(struct message *zmp, struct mimepart *ip, enum parseflags pf,
142 int level)
144 char *cp;
145 enum okay rv = STOP;
146 NYD_ENTER;
148 ip->m_ct_type = hfield1("content-type", (struct message*)ip);
149 if (ip->m_ct_type != NULL) {
150 ip->m_ct_type_plain = savestr(ip->m_ct_type);
151 if ((cp = strchr(ip->m_ct_type_plain, ';')) != NULL)
152 *cp = '\0';
153 } else if (ip->m_parent && ip->m_parent->m_mimecontent == MIME_DIGEST)
154 ip->m_ct_type_plain = UNCONST("message/rfc822");
155 else
156 ip->m_ct_type_plain = UNCONST("text/plain");
158 if (ip->m_ct_type)
159 ip->m_charset = mime_getparam("charset", ip->m_ct_type);
160 if (ip->m_charset == NULL)
161 ip->m_charset = charset_get_7bit();
163 ip->m_ct_transfer_enc = hfield1("content-transfer-encoding",
164 (struct message *)ip);
165 ip->m_mimeenc = ip->m_ct_transfer_enc ?
166 mime_getenc(ip->m_ct_transfer_enc) : MIME_7B;
168 if (((cp = hfield1("content-disposition", (struct message*)ip)) == 0 ||
169 (ip->m_filename = mime_getparam("filename", cp)) == 0) &&
170 ip->m_ct_type != NULL)
171 ip->m_filename = mime_getparam("name", ip->m_ct_type);
173 ip->m_mimecontent = mime_classify_content_of_part(ip);
175 if (pf & PARSE_PARTS) {
176 if (level > 9999) { /* TODO MAGIC */
177 fprintf(stderr, tr(36, "MIME content too deeply nested\n"));
178 goto jleave;
180 switch (ip->m_mimecontent) {
181 case MIME_PKCS7:
182 if (pf & PARSE_DECRYPT) {
183 #ifdef HAVE_SSL
184 parsepkcs7(zmp, ip, pf, level);
185 break;
186 #else
187 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
188 goto jleave;
189 #endif
191 /* FALLTHRU */
192 default:
193 break;
194 case MIME_MULTI:
195 case MIME_ALTERNATIVE:
196 case MIME_DIGEST:
197 _parsemultipart(zmp, ip, pf, level);
198 break;
199 case MIME_822:
200 parse822(zmp, ip, pf, level);
201 break;
204 rv = OKAY;
205 jleave:
206 NYD_LEAVE;
207 return rv;
210 static void
211 parse822(struct message *zmp, struct mimepart *ip, enum parseflags pf,
212 int level)
214 int c, lastc = '\n';
215 size_t cnt;
216 FILE *ibuf;
217 off_t offs;
218 struct mimepart *np;
219 long lines;
220 NYD_ENTER;
222 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
223 goto jleave;
225 cnt = ip->m_size;
226 lines = ip->m_lines;
227 while (cnt && ((c = getc(ibuf)) != EOF)) {
228 --cnt;
229 if (c == '\n') {
230 --lines;
231 if (lastc == '\n')
232 break;
234 lastc = c;
236 offs = ftell(ibuf);
238 np = csalloc(1, sizeof *np);
239 np->m_flag = MNOFROM;
240 np->m_have = HAVE_HEADER | HAVE_BODY;
241 np->m_block = mailx_blockof(offs);
242 np->m_offset = mailx_offsetof(offs);
243 np->m_size = np->m_xsize = cnt;
244 np->m_lines = np->m_xlines = lines;
245 np->m_partstring = ip->m_partstring;
246 np->m_parent = ip;
247 ip->m_multipart = np;
249 if (ok_blook(rfc822_body_from_)) {
250 substdate((struct message *)np);
251 np->m_from = fakefrom((struct message*)np);/* TODO strip MNOFROM flag? */
254 parsepart(zmp, np, pf, level+1);
255 jleave:
256 NYD_LEAVE;
259 #ifdef HAVE_SSL
260 static void
261 parsepkcs7(struct message *zmp, struct mimepart *ip, enum parseflags pf,
262 int level)
264 struct message m, *xmp;
265 struct mimepart *np;
266 char *to, *cc;
267 NYD_ENTER;
269 memcpy(&m, ip, sizeof m);
270 to = hfield1("to", zmp);
271 cc = hfield1("cc", zmp);
273 if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) {
274 np = csalloc(1, sizeof *np);
275 np->m_flag = xmp->m_flag;
276 np->m_have = xmp->m_have;
277 np->m_block = xmp->m_block;
278 np->m_offset = xmp->m_offset;
279 np->m_size = xmp->m_size;
280 np->m_xsize = xmp->m_xsize;
281 np->m_lines = xmp->m_lines;
282 np->m_xlines = xmp->m_xlines;
283 np->m_partstring = ip->m_partstring;
285 if (parsepart(zmp, np, pf, level+1) == OKAY) {
286 np->m_parent = ip;
287 ip->m_multipart = np;
290 NYD_LEAVE;
292 #endif
294 static void
295 _parsemultipart(struct message *zmp, struct mimepart *ip, enum parseflags pf,
296 int level)
298 /* TODO Instead of the recursive multiple run parse we have today,
299 * TODO the send/MIME layer rewrite must create a "tree" of parts with
300 * TODO a single-pass parse, then address each part directly as
301 * TODO necessary; since boundaries start with -- and the content
302 * TODO rather forms a stack this is pretty cheap indeed! */
303 struct mimepart *np = NULL;
304 char *boundary, *line = NULL;
305 size_t linesize = 0, linelen, cnt, boundlen;
306 FILE *ibuf;
307 off_t offs;
308 int part = 0;
309 long lines = 0;
310 NYD_ENTER;
312 if ((boundary = mime_get_boundary(ip->m_ct_type, &linelen)) == NULL)
313 goto jleave;
315 boundlen = linelen;
316 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
317 goto jleave;
319 cnt = ip->m_size;
320 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0))
321 if (line[0] == '\n')
322 break;
323 offs = ftell(ibuf);
325 __newpart(ip, &np, offs, NULL);
326 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
327 /* XXX linelen includes LF */
328 if (!((lines > 0 || part == 0) && linelen > boundlen &&
329 !strncmp(line, boundary, boundlen))) {
330 ++lines;
331 continue;
334 /* Subpart boundary? */
335 if (line[boundlen] == '\n') {
336 offs = ftell(ibuf);
337 if (part > 0) {
338 __endpart(&np, offs - boundlen - 2, lines);
339 __newpart(ip, &np, offs - boundlen - 2, NULL);
341 __endpart(&np, offs, 2);
342 __newpart(ip, &np, offs, &part);
343 lines = 0;
344 continue;
347 /* Final boundary? Be aware of cases where there is no separating
348 * newline in between boundaries, as has been seen in a message with
349 * "Content-Type: multipart/appledouble;" */
350 if (linelen < boundlen + 2)
351 continue;
352 linelen -= boundlen + 2;
353 if (line[boundlen] != '-' || line[boundlen + 1] != '-' ||
354 (linelen > 0 && line[boundlen + 2] != '\n'))
355 continue;
356 offs = ftell(ibuf);
357 if (part != 0) {
358 __endpart(&np, offs - boundlen - 4, lines);
359 __newpart(ip, &np, offs - boundlen - 4, NULL);
361 __endpart(&np, offs + cnt, 2);
362 break;
364 if (np) {
365 offs = ftell(ibuf);
366 __endpart(&np, offs, lines);
369 for (np = ip->m_multipart; np; np = np->m_nextpart)
370 if (np->m_mimecontent != MIME_DISCARD)
371 parsepart(zmp, np, pf, level + 1);
372 free(line);
373 jleave:
374 NYD_LEAVE;
377 static void
378 __newpart(struct mimepart *ip, struct mimepart **np, off_t offs, int *part)
380 struct mimepart *pp;
381 size_t sz;
382 NYD_ENTER;
384 *np = csalloc(1, sizeof **np);
385 (*np)->m_flag = MNOFROM;
386 (*np)->m_have = HAVE_HEADER | HAVE_BODY;
387 (*np)->m_block = mailx_blockof(offs);
388 (*np)->m_offset = mailx_offsetof(offs);
390 if (part) {
391 ++(*part);
392 sz = (ip->m_partstring != NULL) ? strlen(ip->m_partstring) : 0;
393 sz += 20;
394 (*np)->m_partstring = salloc(sz);
395 if (ip->m_partstring)
396 snprintf((*np)->m_partstring, sz, "%s.%u", ip->m_partstring, *part);
397 else
398 snprintf((*np)->m_partstring, sz, "%u", *part);
399 } else
400 (*np)->m_mimecontent = MIME_DISCARD;
401 (*np)->m_parent = ip;
403 if (ip->m_multipart) {
404 for (pp = ip->m_multipart; pp->m_nextpart != NULL; pp = pp->m_nextpart)
406 pp->m_nextpart = *np;
407 } else
408 ip->m_multipart = *np;
409 NYD_LEAVE;
412 static void
413 __endpart(struct mimepart **np, off_t xoffs, long lines)
415 off_t offs;
416 NYD_ENTER;
418 offs = mailx_positionof((*np)->m_block, (*np)->m_offset);
419 (*np)->m_size = (*np)->m_xsize = xoffs - offs;
420 (*np)->m_lines = (*np)->m_xlines = lines;
421 *np = NULL;
422 NYD_LEAVE;
425 static void
426 _print_part_info(struct str *out, struct mimepart *mip,
427 struct ignoretab *doign, int level)
429 struct str ct = {NULL, 0}, cd = {NULL, 0};
430 char const *ps;
431 struct str const *cpre, *csuf;
432 NYD_ENTER;
434 /* Max. 24 */
435 if (is_ign("content-type", 12, doign)) {
436 out->s = mip->m_ct_type_plain;
437 out->l = strlen(out->s);
438 ct.s = ac_alloc(out->l + 2 +1);
439 ct.s[0] = ',';
440 ct.s[1] = ' ';
441 ct.l = 2;
442 if (is_prefix("application/", out->s)) {
443 memcpy(ct.s + 2, "appl../", 7);
444 ct.l += 7;
445 out->l -= 12;
446 out->s += 12;
447 out->l = MIN(out->l, 17);
448 } else
449 out->l = MIN(out->l, 24);
450 memcpy(ct.s + ct.l, out->s, out->l);
451 ct.l += out->l;
452 ct.s[ct.l] = '\0';
455 /* Max. 27 */
456 if (is_ign("content-disposition", 19, doign) &&
457 mip->m_filename != NULL) {
458 struct str ti, to;
460 ti.l = strlen(ti.s = mip->m_filename);
461 mime_fromhdr(&ti, &to, TD_ISPR | TD_ICONV | TD_DELCTRL);
462 to.l = MIN(to.l, 25); /* FIXME MIME: filename may be multibyte enc!! */
463 cd.s = ac_alloc(to.l + 2 +1); /* FIXME ..visual length would be better */
464 cd.s[0] = ',';
465 cd.s[1] = ' ';
466 memcpy(cd.s + 2, to.s, to.l);
467 to.l += 2;
468 cd.s[to.l] = '\0';
469 free(to.s);
472 /* Take care of "99.99", i.e., 5 */
473 if ((ps = mip->m_partstring) == NULL || ps[0] == '\0')
474 ps = "?";
476 #ifdef HAVE_COLOUR
477 cpre = colour_get(COLOURSPEC_PARTINFO);
478 csuf = colour_get(COLOURSPEC_RESET);
479 #else
480 cpre = csuf = NULL;
481 #endif
483 /* Assume maximum possible sizes for 64 bit integers here to avoid any
484 * buffer overflows just in case we have a bug somewhere and / or the
485 * snprintf() is our internal version that doesn't really provide hard
486 * buffer cuts */
487 #define __msg "%s%s[-- #%s : %lu/%lu%s%s --]%s\n"
488 out->l = sizeof(__msg) +
489 #ifdef HAVE_COLOUR
490 (cpre != NULL ? cpre->l + csuf->l : 0) +
491 #endif
492 strlen(ps) + 2*21 + ct.l + cd.l + 1;
493 out->s = salloc(out->l);
494 out->l = snprintf(out->s, out->l, __msg,
495 (level || (ps[0] != '1' && ps[1] == '\0') ? "\n" : ""),
496 (cpre != NULL ? cpre->s : ""),
497 ps, (ul_it)mip->m_lines, (ul_it)mip->m_size,
498 (ct.s != NULL ? ct.s : ""),
499 (cd.s != NULL ? cd.s : ""),
500 (csuf != NULL ? csuf->s : ""));
501 out->s[out->l] = '\0';
502 #undef __msg
504 if (cd.s != NULL)
505 ac_free(cd.s);
506 if (ct.s != NULL)
507 ac_free(ct.s);
508 NYD_LEAVE;
511 static enum pipeflags
512 _pipecmd(char **result, char const *content_type)
514 enum pipeflags ret;
515 char *s, *cp;
516 char const *cq;
517 NYD_ENTER;
519 ret = PIPE_NULL;
520 *result = NULL;
521 if (content_type == NULL)
522 goto jleave;
524 /* First check wether there is a special pipe-MIMETYPE handler */
525 s = ac_alloc(strlen(content_type) + 6);
526 memcpy(s, "pipe-", 5);
527 cp = &s[5];
528 cq = content_type;
530 *cp++ = lowerconv(*cq);
531 while (*cq++ != '\0');
532 cp = vok_vlook(s);
533 ac_free(s);
535 if (cp == NULL)
536 goto jleave;
538 /* User specified a command, inspect for special cases */
539 if (cp[0] != '@') {
540 /* Normal command line */
541 ret = PIPE_COMM;
542 *result = cp;
543 } else if (*++cp == '\0') {
544 /* Treat as plain text */
545 ret = PIPE_TEXT;
546 } else if (!msglist_is_single) {
547 /* Viewing multiple messages in one go, don't block system */
548 ret = PIPE_MSG;
549 *result = UNCONST(tr(86,
550 "[Directly address message only to display this]\n"));
551 } else {
552 /* Viewing a single message only */
553 #if 0 /* TODO send/MIME layer rewrite: when we have a single-pass parser
554 * TODO then the parsing phase and the send phase will be separated;
555 * TODO that allows us to ask a user *before* we start the send, i.e.,
556 * TODO *before* a pager pipe is setup (which is the problem with
557 * TODO the '#if 0' code here) */
558 size_t l = strlen(content_type);
559 char const *x = tr(999, "Should i display a part `%s' (y/n)? ");
560 s = ac_alloc(l += strlen(x) + 1);
561 snprintf(s, l - 1, x, content_type);
562 l = yorn(s);
563 puts(""); /* .. we've hijacked a pipe 8-] ... */
564 ac_free(s);
565 if (!l) {
566 x = tr(210, "[User skipped diplay]\n");
567 ret = PIPE_MSG;
568 *result = UNCONST(x);
569 } else
570 #endif
571 if (cp[0] == '&')
572 /* Asynchronous command, normal command line */
573 ret = PIPE_ASYNC, *result = ++cp;
574 else
575 ret = PIPE_COMM, *result = cp;
577 jleave:
578 NYD_LEAVE;
579 return ret;
582 static FILE *
583 _pipefile(char const *pipecomm, FILE **qbuf, bool_t quote, bool_t async)
585 char const *sh;
586 FILE *rbuf;
587 NYD_ENTER;
589 rbuf = *qbuf;
591 if (quote) {
592 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER,
593 0600)) == NULL) {
594 perror(tr(173, "tmpfile"));
595 *qbuf = rbuf;
597 async = FAL0;
600 if ((sh = ok_vlook(SHELL)) == NULL)
601 sh = XSHELL;
602 if ((rbuf = Popen(pipecomm, "W", sh, async ? -1 : fileno(*qbuf))) == NULL)
603 perror(pipecomm);
604 else {
605 fflush(*qbuf);
606 if (*qbuf != stdout)
607 fflush(stdout);
609 NYD_LEAVE;
610 return rbuf;
613 SINLINE void
614 _addstats(off_t *stats, off_t lines, off_t bytes)
616 NYD_ENTER;
617 if (stats != NULL) {
618 if (stats[0] >= 0)
619 stats[0] += lines;
620 stats[1] += bytes;
622 NYD_LEAVE;
625 SINLINE ssize_t
626 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
627 sendaction action, struct quoteflt *qf, off_t *stats, struct str *rest)
629 ssize_t sz = 0, n;
630 int flags;
631 char const *cp;
632 NYD_ENTER;
634 #if 0
635 Well ... it turns out to not work like that since of course a valid
636 RFC 4155 compliant parser, like S-nail, takes care for From_ lines only
637 after an empty line has been seen, which cannot be detected that easily
638 right here!
639 ifdef HAVE_DEBUG /* TODO assert legacy */
640 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
641 * TODO other input situations handle RFC 4155 OR, if newly generated,
642 * TODO enforce quoted-printable if there is From_, as "required" by
643 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
644 * TODO if it may happen in this path, we should just treat decryption
645 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
646 if (action == SEND_MBOX || action == SEND_DECRYPT)
647 assert(!is_head(buf, len));
648 #else
649 if ((/*action == SEND_MBOX ||*/ action == SEND_DECRYPT) &&
650 is_head(buf, len)) {
651 putc('>', fp);
652 ++sz;
654 #endif
656 flags = ((int)action & _TD_EOF);
657 action &= ~_TD_EOF;
658 n = mime_write(buf, len, fp,
659 action == SEND_MBOX ? CONV_NONE : convert,
660 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
661 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
662 ? TD_ISPR | TD_ICONV
663 : (action == SEND_TOSRCH || action == SEND_TOPIPE)
664 ? TD_ICONV : (action == SEND_TOFLTR)
665 ? TD_DELCTRL : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
666 qf, rest);
667 if (n < 0)
668 sz = n;
669 else if (n > 0) {
670 sz = (ssize_t)((size_t)sz + n);
671 n = 0;
672 if (stats != NULL && stats[0] != -1)
673 for (cp = buf; cp < &buf[sz]; ++cp)
674 if (*cp == '\n')
675 ++n;
676 _addstats(stats, n, sz);
678 NYD_LEAVE;
679 return sz;
682 static void
683 _send_onpipe(int signo)
685 NYD_X; /* Signal handler */
686 UNUSED(signo);
687 siglongjmp(_send_pipejmp, 1);
690 static int
691 sendpart(struct message *zmp, struct mimepart *ip, FILE *obuf,
692 struct ignoretab *doign, struct quoteflt *qf,
693 enum sendaction volatile action, off_t *volatile stats, int level)
695 int volatile ispipe, rv = 0;
696 struct str rest;
697 char *line = NULL, *cp, *cp2, *start, *pipecomm = NULL;
698 size_t linesize = 0, linelen, cnt;
699 int dostat, infld = 0, ignoring = 1, isenc, c;
700 struct mimepart *volatile np;
701 FILE *volatile ibuf = NULL, *volatile pbuf = obuf,
702 *volatile qbuf = obuf, *origobuf = obuf;
703 enum conversion volatile convert;
704 sighandler_type volatile oldpipe = SIG_DFL;
705 long lineno = 0;
706 NYD_ENTER;
708 if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
709 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
710 goto jskip;
712 dostat = 0;
713 if (level == 0) {
714 if (doign != NULL) {
715 if (!is_ign("status", 6, doign))
716 dostat |= 1;
717 if (!is_ign("x-status", 8, doign))
718 dostat |= 2;
719 } else
720 dostat = 3;
722 if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL) {
723 rv = -1;
724 goto jleave;
726 cnt = ip->m_size;
728 if (ip->m_mimecontent == MIME_DISCARD)
729 goto jskip;
731 if ((ip->m_flag & MNOFROM) == 0)
732 while (cnt && (c = getc(ibuf)) != EOF) {
733 cnt--;
734 if (c == '\n')
735 break;
737 isenc = 0;
738 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
739 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
740 action == SEND_TOSRCH || action == SEND_TOFLTR)
741 ? CONV_FROMHDR : CONV_NONE;
743 /* Work the headers */
744 quoteflt_reset(qf, obuf);
745 while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
746 ++lineno;
747 if (line[0] == '\n') {
748 /* If line is blank, we've reached end of headers, so force out
749 * status: field and note that we are no longer in header fields */
750 if (dostat & 1)
751 statusput(zmp, obuf, qf, stats);
752 if (dostat & 2)
753 xstatusput(zmp, obuf, qf, stats);
754 if (doign != allignore)
755 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
756 break;
759 isenc &= ~1;
760 if (infld && blankchar(line[0])) {
761 /* If this line is a continuation (SP / HT) of a previous header
762 * field, determine if the start of the line is a MIME encoded word */
763 if (isenc & 2) {
764 for (cp = line; blankchar(*cp); cp++);
765 if (cp > line && linelen - (cp - line) > 8 && cp[0] == '=' &&
766 cp[1] == '?')
767 isenc |= 1;
769 } else {
770 /* Pick up the header field if we have one */
771 for (cp = line; (c = *cp & 0377) && c != ':' && !spacechar(c); ++cp)
773 cp2 = cp;
774 while (spacechar(*cp))
775 ++cp;
776 if (cp[0] != ':' && level == 0 && lineno == 1) {
777 /* Not a header line, force out status: This happens in uucp style
778 * mail where there are no headers at all */
779 if (dostat & 1)
780 statusput(zmp, obuf, qf, stats);
781 if (dostat & 2)
782 xstatusput(zmp, obuf, qf, stats);
783 if (doign != allignore)
784 _out("\n", 1, obuf, CONV_NONE,SEND_MBOX, qf, stats, NULL);
785 break;
788 /* If it is an ignored field and we care about such things, skip it */
789 c = *cp2;
790 *cp2 = 0; /* temporarily null terminate */
791 if ((doign && is_ign(line, PTR2SIZE(cp2 - line), doign)) ||
792 (action == SEND_MBOX && !ok_blook(keep_content_length) &&
793 (!asccasecmp(line, "content-length") ||
794 !asccasecmp(line, "lines"))))
795 ignoring = 1;
796 else if (!asccasecmp(line, "status")) {
797 /* If field is "status," go compute and print real Status: field */
798 if (dostat & 1) {
799 statusput(zmp, obuf, qf, stats);
800 dostat &= ~1;
801 ignoring = 1;
803 } else if (!asccasecmp(line, "x-status")) {
804 /* If field is "status," go compute and print real Status: field */
805 if (dostat & 2) {
806 xstatusput(zmp, obuf, qf, stats);
807 dostat &= ~2;
808 ignoring = 1;
810 } else {
811 ignoring = 0;
812 #ifdef HAVE_COLOUR
813 pipecomm = savestrbuf(line, PTR2SIZE(cp2 - line));
814 #endif
816 *cp2 = c;
817 infld = 1;
820 /* Determine if the end of the line is a MIME encoded word */
821 isenc &= ~2;
822 if (cnt && (c = getc(ibuf)) != EOF) {
823 if (blankchar(c)) {
824 if (linelen > 0 && line[linelen - 1] == '\n')
825 cp = &line[linelen - 2];
826 else
827 cp = &line[linelen - 1];
828 while (cp >= line && whitechar(*cp))
829 ++cp;
830 if (cp - line > 8 && cp[0] == '=' && cp[-1] == '?')
831 isenc |= 2;
833 ungetc(c, ibuf);
836 if (!ignoring) {
837 size_t len = linelen;
838 start = line;
839 if (action == SEND_TODISP || action == SEND_TODISP_ALL ||
840 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
841 action == SEND_TOSRCH || action == SEND_TOFLTR) {
842 /* Strip blank characters if two MIME-encoded words follow on
843 * continuing lines */
844 if (isenc & 1)
845 while (len > 0 && blankchar(*start)) {
846 ++start;
847 --len;
849 if (isenc & 2)
850 if (len > 0 && start[len - 1] == '\n')
851 --len;
852 while (len > 0 && blankchar(start[len - 1]))
853 --len;
855 #ifdef HAVE_COLOUR
857 bool_t colour_stripped = FAL0;
858 if (pipecomm != NULL) {
859 colour_put_header(obuf, pipecomm);
860 if (len > 0 && start[len - 1] == '\n') {
861 colour_stripped = TRU1;
862 --len;
865 #endif
866 _out(start, len, obuf, convert, action, qf, stats, NULL);
867 #ifdef HAVE_COLOUR
868 if (pipecomm != NULL) {
869 colour_reset(obuf); /* XXX reset after \n!! */
870 if (colour_stripped)
871 fputc('\n', obuf);
874 #endif
875 if (ferror(obuf)) {
876 free(line);
877 rv = -1;
878 goto jleave;
882 quoteflt_flush(qf);
883 free(line);
884 line = NULL;
886 jskip:
887 switch (ip->m_mimecontent) {
888 case MIME_822:
889 switch (action) {
890 case SEND_TOFLTR:
891 putc('\0', obuf);
892 /* FALLTHRU */
893 case SEND_TODISP:
894 case SEND_TODISP_ALL:
895 case SEND_QUOTE:
896 case SEND_QUOTE_ALL:
897 if (ok_blook(rfc822_body_from_)) {
898 if (qf->qf_pfix_len > 0) {
899 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
900 qf->qf_pfix_len, obuf);
901 if (i == qf->qf_pfix_len)
902 _addstats(stats, 0, i);
904 put_from_(obuf, ip->m_multipart, stats);
906 /* FALLTHRU */
907 case SEND_TOSRCH:
908 case SEND_DECRYPT:
909 goto jmulti;
910 case SEND_TOFILE:
911 case SEND_TOPIPE:
912 if (ok_blook(rfc822_body_from_))
913 put_from_(obuf, ip->m_multipart, stats);
914 /* FALLTHRU */
915 case SEND_MBOX:
916 case SEND_RFC822:
917 case SEND_SHOW:
918 break;
920 break;
921 case MIME_TEXT_HTML:
922 if (action == SEND_TOFLTR)
923 putc('\b', obuf);
924 /* FALLTHRU */
925 case MIME_TEXT:
926 case MIME_TEXT_PLAIN:
927 switch (action) {
928 case SEND_TODISP:
929 case SEND_TODISP_ALL:
930 case SEND_QUOTE:
931 case SEND_QUOTE_ALL:
932 ispipe = TRU1;
933 switch (_pipecmd(&pipecomm, ip->m_ct_type_plain)) {
934 case PIPE_MSG:
935 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
936 stats, NULL);
937 pipecomm = NULL;
938 /* FALLTRHU */
939 case PIPE_TEXT:
940 case PIPE_COMM:
941 case PIPE_ASYNC:
942 case PIPE_NULL:
943 break;
945 /* FALLTRHU */
946 default:
947 break;
949 break;
950 case MIME_DISCARD:
951 if (action != SEND_DECRYPT)
952 goto jleave;
953 break;
954 case MIME_PKCS7:
955 if (action != SEND_MBOX && action != SEND_RFC822 &&
956 action != SEND_SHOW && ip->m_multipart)
957 goto jmulti;
958 /* FALLTHRU */
959 default:
960 switch (action) {
961 case SEND_TODISP:
962 case SEND_TODISP_ALL:
963 case SEND_QUOTE:
964 case SEND_QUOTE_ALL:
965 ispipe = TRU1;
966 switch (_pipecmd(&pipecomm, ip->m_ct_type_plain)) {
967 case PIPE_MSG:
968 _out(pipecomm, strlen(pipecomm), obuf, CONV_NONE, SEND_MBOX, qf,
969 stats, NULL);
970 pipecomm = NULL;
971 break;
972 case PIPE_ASYNC:
973 ispipe = FAL0;
974 /* FALLTHRU */
975 case PIPE_COMM:
976 case PIPE_NULL:
977 break;
978 case PIPE_TEXT:
979 goto jcopyout; /* break; break; */
981 if (pipecomm != NULL)
982 break;
983 if (level == 0 && cnt) {
984 char const *x = tr(210, "[Binary content]\n");
985 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
987 /* FALLTHRU */
988 case SEND_TOFLTR:
989 goto jleave;
990 case SEND_TOFILE:
991 case SEND_TOPIPE:
992 case SEND_TOSRCH:
993 case SEND_DECRYPT:
994 case SEND_MBOX:
995 case SEND_RFC822:
996 case SEND_SHOW:
997 break;
999 break;
1000 case MIME_ALTERNATIVE:
1001 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
1002 !ok_blook(print_alternatives)) {
1003 bool_t doact = FAL0;
1005 for (np = ip->m_multipart; np; np = np->m_nextpart)
1006 if (np->m_mimecontent == MIME_TEXT_PLAIN)
1007 doact = TRU1;
1008 if (doact) {
1009 for (np = ip->m_multipart; np; np = np->m_nextpart) {
1010 if (np->m_ct_type_plain != NULL && action != SEND_QUOTE) {
1011 _print_part_info(&rest, np, doign, level);
1012 _out(rest.s, rest.l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
1013 NULL);
1015 if (doact && np->m_mimecontent == MIME_TEXT_PLAIN) {
1016 doact = FAL0;
1017 rv = sendpart(zmp, np, obuf, doign, qf, action, stats,
1018 level + 1);
1019 quoteflt_reset(qf, origobuf);
1020 if (rv < 0)
1021 break;
1024 goto jleave;
1027 /* FALLTHRU */
1028 case MIME_MULTI:
1029 case MIME_DIGEST:
1030 switch (action) {
1031 case SEND_TODISP:
1032 case SEND_TODISP_ALL:
1033 case SEND_QUOTE:
1034 case SEND_QUOTE_ALL:
1035 case SEND_TOFILE:
1036 case SEND_TOPIPE:
1037 case SEND_TOSRCH:
1038 case SEND_TOFLTR:
1039 case SEND_DECRYPT:
1040 jmulti:
1041 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
1042 ip->m_multipart != NULL &&
1043 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
1044 ip->m_multipart->m_nextpart == NULL) {
1045 char const *x = tr(85,
1046 "[Missing multipart boundary - use \"show\" to display "
1047 "the raw message]\n");
1048 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL);
1051 for (np = ip->m_multipart; np; np = np->m_nextpart) {
1052 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
1053 continue;
1054 ispipe = FAL0;
1055 switch (action) {
1056 case SEND_TOFILE:
1057 if (np->m_partstring && !strcmp(np->m_partstring, "1"))
1058 break;
1059 stats = NULL;
1060 if ((obuf = newfile(np, UNVOLATILE(&ispipe))) == NULL)
1061 continue;
1062 if (!ispipe)
1063 break;
1064 if (sigsetjmp(_send_pipejmp, 1)) {
1065 rv = -1;
1066 goto jpipe_close;
1068 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1069 break;
1070 case SEND_TODISP:
1071 case SEND_TODISP_ALL:
1072 case SEND_QUOTE_ALL:
1073 if (ip->m_mimecontent != MIME_MULTI &&
1074 ip->m_mimecontent != MIME_ALTERNATIVE &&
1075 ip->m_mimecontent != MIME_DIGEST)
1076 break;
1077 _print_part_info(&rest, np, doign, level);
1078 _out(rest.s, rest.l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
1079 NULL);
1080 break;
1081 case SEND_TOFLTR:
1082 putc('\0', obuf);
1083 /* FALLTHRU */
1084 case SEND_MBOX:
1085 case SEND_RFC822:
1086 case SEND_SHOW:
1087 case SEND_TOSRCH:
1088 case SEND_QUOTE:
1089 case SEND_DECRYPT:
1090 case SEND_TOPIPE:
1091 break;
1094 quoteflt_flush(qf);
1095 if (sendpart(zmp, np, obuf, doign, qf, action, stats, level+1) < 0)
1096 rv = -1;
1097 quoteflt_reset(qf, origobuf);
1098 if (action == SEND_QUOTE)
1099 break;
1100 if (action == SEND_TOFILE && obuf != origobuf) {
1101 if (!ispipe)
1102 Fclose(obuf);
1103 else {
1104 jpipe_close:
1105 safe_signal(SIGPIPE, SIG_IGN);
1106 Pclose(obuf, TRU1);
1107 safe_signal(SIGPIPE, oldpipe);
1111 goto jleave;
1112 case SEND_MBOX:
1113 case SEND_RFC822:
1114 case SEND_SHOW:
1115 break;
1119 /* Copy out message body */
1120 jcopyout:
1121 if (doign == allignore && level == 0) /* skip final blank line */
1122 --cnt;
1123 switch (ip->m_mimeenc) {
1124 case MIME_BIN:
1125 if (stats)
1126 stats[0] = -1;
1127 /* FALLTHRU */
1128 case MIME_7B:
1129 case MIME_8B:
1130 convert = CONV_NONE;
1131 break;
1132 case MIME_QP:
1133 convert = CONV_FROMQP;
1134 break;
1135 case MIME_B64:
1136 switch (ip->m_mimecontent) {
1137 case MIME_TEXT:
1138 case MIME_TEXT_PLAIN:
1139 case MIME_TEXT_HTML:
1140 convert = CONV_FROMB64_T;
1141 break;
1142 default:
1143 convert = CONV_FROMB64;
1145 break;
1146 default:
1147 convert = CONV_NONE;
1150 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1151 action == SEND_RFC822 || action == SEND_SHOW)
1152 convert = CONV_NONE;
1153 #ifdef HAVE_ICONV
1154 if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
1155 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
1156 action == SEND_TOSRCH) &&
1157 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
1158 ip->m_mimecontent == MIME_TEXT_HTML ||
1159 ip->m_mimecontent == MIME_TEXT)) {
1160 char const *tcs = charset_get_lc();
1162 if (iconvd != (iconv_t)-1)
1163 n_iconv_close(iconvd);
1164 /* TODO Since Base64 has an odd 4:3 relation in between input
1165 * TODO and output an input line may end with a partial
1166 * TODO multibyte character; this is no problem at all unless
1167 * TODO we send to the display or whatever, i.e., ensure
1168 * TODO makeprint() or something; to avoid this trap, *force*
1169 * TODO iconv(), in which case this layer will handle leftovers
1170 * TODO correctly */
1171 if (convert == CONV_FROMB64_T || (asccasecmp(tcs, ip->m_charset) &&
1172 asccasecmp(charset_get_7bit(), ip->m_charset))) {
1173 iconvd = n_iconv_open(tcs, ip->m_charset);
1174 /* XXX Don't bail out if we cannot iconv(3) here;
1175 * XXX alternatively we could avoid trying to open
1176 * XXX if ip->m_charset is "unknown-8bit", which was
1177 * XXX the one that has bitten me?? */
1179 * TODO errors should DEFINETELY not be scrolled away!
1180 * TODO what about an error buffer (think old shsp(1)),
1181 * TODO re-dump errors since last snapshot when the
1182 * TODO command loop enters again? i.e., at least print
1183 * TODO "There were errors ?" before the next prompt,
1184 * TODO so that the user can look at the error buffer?
1186 if (iconvd == (iconv_t)-1 && errno == EINVAL) {
1187 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
1188 ip->m_charset, tcs);
1189 /*rv = 1; goto jleave*/
1193 #endif
1195 if (pipecomm != NULL && (action == SEND_TODISP ||
1196 action == SEND_TODISP_ALL || action == SEND_QUOTE ||
1197 action == SEND_QUOTE_ALL)) {
1198 qbuf = obuf;
1199 pbuf = _pipefile(pipecomm, UNVOLATILE(&qbuf),
1200 (action == SEND_QUOTE || action == SEND_QUOTE_ALL), !ispipe);
1201 action = SEND_TOPIPE;
1202 if (pbuf != qbuf) {
1203 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1204 if (sigsetjmp(_send_pipejmp, 1))
1205 goto jend;
1207 } else
1208 pbuf = qbuf = obuf;
1211 bool_t eof;
1212 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1213 off_t *save_stats = stats;
1215 if (pbuf != origobuf) {
1216 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1217 stats = NULL;
1219 eof = FAL0;
1220 rest.s = NULL;
1221 rest.l = 0;
1223 quoteflt_reset(qf, pbuf);
1224 while (!eof && fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
1225 joutln:
1226 if (_out(line, linelen, pbuf, convert, action, qf, stats, &rest) < 0 ||
1227 ferror(pbuf)) {
1228 rv = -1; /* XXX Should bail away?! */
1229 break;
1232 if (!eof && rest.l != 0) {
1233 linelen = 0;
1234 eof = TRU1;
1235 action |= _TD_EOF;
1236 goto joutln;
1238 quoteflt_flush(qf);
1239 if (rest.s != NULL)
1240 free(rest.s);
1242 if (pbuf != origobuf) {
1243 qf->qf_pfix_len = save_qf_pfix_len;
1244 stats = save_stats;
1248 jend:
1249 free(line);
1250 if (pbuf != qbuf) {
1251 safe_signal(SIGPIPE, SIG_IGN);
1252 Pclose(pbuf, ispipe);
1253 safe_signal(SIGPIPE, oldpipe);
1254 if (qbuf != obuf)
1255 pipecpy(qbuf, obuf, origobuf, qf, stats);
1257 #ifdef HAVE_ICONV
1258 if (iconvd != (iconv_t)-1)
1259 n_iconv_close(iconvd);
1260 #endif
1261 jleave:
1262 NYD_LEAVE;
1263 return rv;
1266 static FILE *
1267 newfile(struct mimepart *ip, int *ispipe)
1269 struct str in, out;
1270 char *f;
1271 FILE *fp;
1272 NYD_ENTER;
1274 f = ip->m_filename;
1275 *ispipe = 0;
1277 if (f != NULL && f != (char*)-1) {
1278 in.s = f;
1279 in.l = strlen(f);
1280 mime_fromhdr(&in, &out, TD_ISPR);
1281 memcpy(f, out.s, out.l);
1282 *(f + out.l) = '\0';
1283 free(out.s);
1286 if (options & OPT_INTERACTIVE) {
1287 char *f2, *f3;
1288 jgetname:
1289 printf(tr(278, "Enter filename for part %s (%s)"),
1290 ip->m_partstring ? ip->m_partstring : "?",
1291 ip->m_ct_type_plain);
1292 f2 = readstr_input(": ", (f != (char*)-1) ? f : NULL);
1293 if (f2 == NULL || *f2 == '\0') {
1294 fprintf(stderr, tr(279, "... skipping this\n"));
1295 fp = NULL;
1296 goto jleave;
1297 } else if (*f2 == '|')
1298 /* Pipes are expanded by the shell */
1299 f = f2;
1300 else if ((f3 = file_expand(f2)) == NULL)
1301 /* (Error message written by file_expand()) */
1302 goto jgetname;
1303 else
1304 f = f3;
1306 if (f == NULL || f == (char *)-1) {
1307 fp = NULL;
1308 goto jleave;
1311 if (*f == '|') {
1312 char const *cp;
1313 cp = ok_vlook(SHELL);
1314 if (cp == NULL)
1315 cp = XSHELL;
1316 fp = Popen(f + 1, "w", cp, 1);
1317 if (!(*ispipe = (fp != NULL)))
1318 perror(f);
1319 } else {
1320 if ((fp = Fopen(f, "w")) == NULL)
1321 fprintf(stderr, tr(176, "Cannot open %s\n"), f);
1323 jleave:
1324 NYD_LEAVE;
1325 return fp;
1328 static void
1329 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1330 off_t *stats)
1332 char *line = NULL;
1333 size_t linesize = 0, linelen, cnt;
1334 ssize_t all_sz, sz;
1335 NYD_ENTER;
1337 fflush(pipebuf);
1338 rewind(pipebuf);
1339 cnt = fsize(pipebuf);
1340 all_sz = 0;
1342 quoteflt_reset(qf, outbuf);
1343 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1344 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1345 break;
1346 all_sz += sz;
1348 if ((sz = quoteflt_flush(qf)) > 0)
1349 all_sz += sz;
1350 if (line)
1351 free(line);
1353 if (all_sz > 0 && outbuf == origobuf)
1354 _addstats(stats, 1, all_sz);
1355 fclose(pipebuf);
1356 NYD_LEAVE;
1359 static void
1360 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1361 off_t *stats)
1363 char statout[3], *cp = statout;
1364 NYD_ENTER;
1366 if (mp->m_flag & MREAD)
1367 *cp++ = 'R';
1368 if ((mp->m_flag & MNEW) == 0)
1369 *cp++ = 'O';
1370 *cp = 0;
1371 if (statout[0]) {
1372 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1373 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1374 if (i > 0)
1375 _addstats(stats, 1, i);
1377 NYD_LEAVE;
1380 static void
1381 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1382 off_t *stats)
1384 char xstatout[4];
1385 char *xp = xstatout;
1386 NYD_ENTER;
1388 if (mp->m_flag & MFLAGGED)
1389 *xp++ = 'F';
1390 if (mp->m_flag & MANSWERED)
1391 *xp++ = 'A';
1392 if (mp->m_flag & MDRAFTED)
1393 *xp++ = 'T';
1394 *xp = 0;
1395 if (xstatout[0]) {
1396 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1397 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1398 if (i > 0)
1399 _addstats(stats, 1, i);
1401 NYD_LEAVE;
1404 static void
1405 put_from_(FILE *fp, struct mimepart *ip, off_t *stats)
1407 char const *froma, *date, *nl;
1408 int i;
1409 NYD_ENTER;
1411 if (ip && ip->m_from) {
1412 froma = ip->m_from;
1413 date = fakedate(ip->m_time);
1414 nl = "\n";
1415 } else {
1416 froma = myname;
1417 date = time_current.tc_ctime;
1418 nl = "";
1421 colour_put(fp, COLOURSPEC_FROM_);
1422 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1423 colour_reset(fp);
1424 if (i > 0)
1425 _addstats(stats, (*nl != '\0'), i);
1426 NYD_LEAVE;
1429 FL int
1430 sendmp(struct message *mp, FILE *obuf, struct ignoretab *doign,
1431 char const *prefix, enum sendaction action, off_t *stats)
1433 struct quoteflt qf;
1434 size_t cnt, sz, i;
1435 FILE *ibuf;
1436 enum parseflags pf;
1437 struct mimepart *ip;
1438 int rv = -1, c;
1439 NYD_ENTER;
1441 if (mp == dot && action != SEND_TOSRCH && action != SEND_TOFLTR)
1442 did_print_dot = 1;
1443 if (stats)
1444 stats[0] = stats[1] = 0;
1445 quoteflt_init(&qf, prefix);
1447 /* First line is the From_ line, so no headers there to worry about */
1448 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1449 goto jleave;
1451 cnt = mp->m_size;
1452 sz = 0;
1454 struct str const *cpre, *csuf;
1455 #ifdef HAVE_COLOUR
1456 cpre = colour_get(COLOURSPEC_FROM_);
1457 csuf = colour_get(COLOURSPEC_RESET);
1458 #else
1459 cpre = csuf = NULL;
1460 #endif
1461 if (mp->m_flag & MNOFROM) {
1462 if (doign != allignore && doign != fwdignore && action != SEND_RFC822)
1463 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1464 (cpre != NULL ? cpre->s : ""),
1465 (int)qf.qf_pfix_len, (qf.qf_pfix_len != 0 ? qf.qf_pfix : ""),
1466 fakefrom(mp), fakedate(mp->m_time),
1467 (csuf != NULL ? csuf->s : ""));
1468 } else {
1469 if (doign != allignore && doign != fwdignore && action != SEND_RFC822) {
1470 if (qf.qf_pfix_len > 0) {
1471 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1472 if (i != qf.qf_pfix_len)
1473 goto jleave;
1474 sz += i;
1476 #ifdef HAVE_COLOUR
1477 if (cpre != NULL) {
1478 fputs(cpre->s, obuf);
1479 cpre = (struct str const*)0x1;
1481 #endif
1484 while (cnt && (c = getc(ibuf)) != EOF) {
1485 if (doign != allignore && doign != fwdignore &&
1486 action != SEND_RFC822) {
1487 #ifdef HAVE_COLOUR
1488 if (c == '\n' && csuf != NULL) {
1489 cpre = (struct str const*)0x1;
1490 fputs(csuf->s, obuf);
1492 #endif
1493 putc(c, obuf);
1494 sz++;
1496 --cnt;
1497 if (c == '\n')
1498 break;
1501 #ifdef HAVE_COLOUR
1502 if (csuf != NULL && cpre != (struct str const*)0x1)
1503 fputs(csuf->s, obuf);
1504 #endif
1507 if (sz)
1508 _addstats(stats, 1, sz);
1510 pf = 0;
1511 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1512 pf |= PARSE_DECRYPT | PARSE_PARTS;
1513 if ((ip = parsemsg(mp, pf)) == NULL)
1514 goto jleave;
1516 rv = sendpart(mp, ip, obuf, doign, &qf, action, stats, 0);
1517 jleave:
1518 quoteflt_destroy(&qf);
1519 NYD_LEAVE;
1520 return rv;
1523 /* vim:set fenc=utf-8:s-it-mode */