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>.
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
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
42 #ifndef HAVE_AMALGAMATION
47 PIPE_NULL
, /* No pipe- mimetype handler */
48 PIPE_COMM
, /* Normal command */
49 PIPE_ASYNC
, /* Normal command, run asynchronous */
50 PIPE_TEXT
, /* @ special command to force treatment as text */
51 PIPE_MSG
/* Display message (returned as command string) */
60 static sigjmp_buf _send_pipejmp
;
63 static struct mimepart
*parsemsg(struct message
*mp
, enum parseflags pf
);
64 static enum okay
parsepart(struct message
*zmp
, struct mimepart
*ip
,
65 enum parseflags pf
, int level
);
66 static void parse822(struct message
*zmp
, struct mimepart
*ip
,
67 enum parseflags pf
, int level
);
69 static void parsepkcs7(struct message
*zmp
, struct mimepart
*ip
,
70 enum parseflags pf
, int level
);
72 static void _parsemultipart(struct message
*zmp
,
73 struct mimepart
*ip
, enum parseflags pf
, int level
);
74 static void __newpart(struct mimepart
*ip
, struct mimepart
**np
,
75 off_t offs
, int *part
);
76 static void __endpart(struct mimepart
**np
, off_t xoffs
, long lines
);
78 /* Going for user display, print Part: info string */
79 static void _print_part_info(FILE *obuf
, struct mimepart
const *mpp
,
80 struct ignoretab
*doign
, int level
,
81 struct quoteflt
*qf
, ui64_t
*stats
);
83 /* Query possible pipe command for MIME part */
84 static enum pipeflags
_pipecmd(char const **result
, struct mimepart
const *mpp
);
86 /* Create a pipe; if mpp is not NULL, place some NAILENV_* environment
87 * variables accordingly */
88 static FILE * _pipefile(char const *pipecomm
, struct mimepart
const *mpp
,
89 FILE **qbuf
, bool_t quote
, bool_t async
);
91 /* Call mime_write() as approbiate and adjust statistics */
92 SINLINE ssize_t
_out(char const *buf
, size_t len
, FILE *fp
,
93 enum conversion convert
, enum sendaction action
,
94 struct quoteflt
*qf
, ui64_t
*stats
, struct str
*rest
);
97 static void _send_onpipe(int signo
);
100 static int sendpart(struct message
*zmp
, struct mimepart
*ip
,
101 FILE *obuf
, struct ignoretab
*doign
,
102 struct quoteflt
*qf
, enum sendaction action
,
103 ui64_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
, ui64_t
*stats
);
111 /* Output a reasonable looking status field */
112 static void statusput(const struct message
*mp
, FILE *obuf
,
113 struct quoteflt
*qf
, ui64_t
*stats
);
114 static void xstatusput(const struct message
*mp
, FILE *obuf
,
115 struct quoteflt
*qf
, ui64_t
*stats
);
117 static void put_from_(FILE *fp
, struct mimepart
*ip
, ui64_t
*stats
);
119 static struct mimepart
*
120 parsemsg(struct message
*mp
, enum parseflags pf
)
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
)
141 parsepart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
148 ip
->m_ct_type
= hfield1("content-type", (struct message
*)ip
);
149 if (ip
->m_ct_type
!= NULL
) {
150 ip
->m_ct_type_plain
= cp_b
= savestr(ip
->m_ct_type
);
151 if ((cp
= strchr(cp_b
, ';')) != NULL
)
153 cp
= cp_b
+ strlen(cp_b
);
154 while (cp
> cp_b
&& blankchar(cp
[-1]))
157 } else if (ip
->m_parent
!= NULL
&&
158 ip
->m_parent
->m_mimecontent
== MIME_DIGEST
)
159 ip
->m_ct_type_plain
= "message/rfc822";
161 ip
->m_ct_type_plain
= "text/plain";
162 ip
->m_ct_type_usr_ovwr
= NULL
;
164 if (ip
->m_ct_type
!= NULL
)
165 ip
->m_charset
= mime_param_get("charset", ip
->m_ct_type
);
166 if (ip
->m_charset
== NULL
)
167 ip
->m_charset
= charset_get_7bit();
169 if ((ip
->m_ct_enc
= hfield1("content-transfer-encoding",
170 (struct message
*)ip
)) == NULL
)
171 ip
->m_ct_enc
= mime_enc_from_conversion(CONV_7BIT
);
172 ip
->m_mime_enc
= mime_enc_from_ctehead(ip
->m_ct_enc
);
174 if (((cp
= hfield1("content-disposition", (struct message
*)ip
)) == NULL
||
175 (ip
->m_filename
= mime_param_get("filename", cp
)) == NULL
) &&
176 ip
->m_ct_type
!= NULL
)
177 ip
->m_filename
= mime_param_get("name", ip
->m_ct_type
);
179 ip
->m_mimecontent
= mime_type_mimepart_content(ip
);
181 if (pf
& PARSE_PARTS
) {
182 if (level
> 9999) { /* TODO MAGIC */
183 fprintf(stderr
, _("MIME content too deeply nested\n"));
186 switch (ip
->m_mimecontent
) {
188 if (pf
& PARSE_DECRYPT
) {
190 parsepkcs7(zmp
, ip
, pf
, level
);
193 fprintf(stderr
, _("No SSL support compiled in.\n"));
201 case MIME_ALTERNATIVE
:
202 case MIME_RELATED
: /* TODO /related yet handled like /alternative */
204 _parsemultipart(zmp
, ip
, pf
, level
);
207 parse822(zmp
, ip
, pf
, level
);
218 parse822(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
229 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
234 while (cnt
&& ((c
= getc(ibuf
)) != EOF
)) {
245 np
= csalloc(1, sizeof *np
);
246 np
->m_flag
= MNOFROM
;
247 np
->m_have
= HAVE_HEADER
| HAVE_BODY
;
248 np
->m_block
= mailx_blockof(offs
);
249 np
->m_offset
= mailx_offsetof(offs
);
250 np
->m_size
= np
->m_xsize
= cnt
;
251 np
->m_lines
= np
->m_xlines
= lines
;
252 np
->m_partstring
= ip
->m_partstring
;
254 ip
->m_multipart
= np
;
256 if (ok_blook(rfc822_body_from_
)) {
257 substdate((struct message
*)np
);
258 np
->m_from
= fakefrom((struct message
*)np
);/* TODO strip MNOFROM flag? */
261 parsepart(zmp
, np
, pf
, level
+ 1);
268 parsepkcs7(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
271 struct message m
, *xmp
;
276 memcpy(&m
, ip
, sizeof m
);
277 to
= hfield1("to", zmp
);
278 cc
= hfield1("cc", zmp
);
280 if ((xmp
= smime_decrypt(&m
, to
, cc
, 0)) != NULL
) {
281 np
= csalloc(1, sizeof *np
);
282 np
->m_flag
= xmp
->m_flag
;
283 np
->m_have
= xmp
->m_have
;
284 np
->m_block
= xmp
->m_block
;
285 np
->m_offset
= xmp
->m_offset
;
286 np
->m_size
= xmp
->m_size
;
287 np
->m_xsize
= xmp
->m_xsize
;
288 np
->m_lines
= xmp
->m_lines
;
289 np
->m_xlines
= xmp
->m_xlines
;
290 np
->m_partstring
= ip
->m_partstring
;
292 if (parsepart(zmp
, np
, pf
, level
+ 1) == OKAY
) {
294 ip
->m_multipart
= np
;
302 _parsemultipart(struct message
*zmp
, struct mimepart
*ip
, enum parseflags pf
,
305 /* TODO Instead of the recursive multiple run parse we have today,
306 * TODO the send/MIME layer rewrite must create a "tree" of parts with
307 * TODO a single-pass parse, then address each part directly as
308 * TODO necessary; since boundaries start with -- and the content
309 * TODO rather forms a stack this is pretty cheap indeed! */
310 struct mimepart
*np
= NULL
;
311 char *boundary
, *line
= NULL
;
312 size_t linesize
= 0, linelen
, cnt
, boundlen
;
319 if ((boundary
= mime_param_boundary_get(ip
->m_ct_type
, &linelen
)) == NULL
)
323 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
327 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0))
332 __newpart(ip
, &np
, offs
, NULL
);
333 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0)) {
334 /* XXX linelen includes LF */
335 if (!((lines
> 0 || part
== 0) && linelen
> boundlen
&&
336 !strncmp(line
, boundary
, boundlen
))) {
341 /* Subpart boundary? */
342 if (line
[boundlen
] == '\n') {
345 __endpart(&np
, offs
- boundlen
- 2, lines
);
346 __newpart(ip
, &np
, offs
- boundlen
- 2, NULL
);
348 __endpart(&np
, offs
, 2);
349 __newpart(ip
, &np
, offs
, &part
);
354 /* Final boundary? Be aware of cases where there is no separating
355 * newline in between boundaries, as has been seen in a message with
356 * "Content-Type: multipart/appledouble;" */
357 if (linelen
< boundlen
+ 2)
359 linelen
-= boundlen
+ 2;
360 if (line
[boundlen
] != '-' || line
[boundlen
+ 1] != '-' ||
361 (linelen
> 0 && line
[boundlen
+ 2] != '\n'))
365 __endpart(&np
, offs
- boundlen
- 4, lines
);
366 __newpart(ip
, &np
, offs
- boundlen
- 4, NULL
);
368 __endpart(&np
, offs
+ cnt
, 2);
373 __endpart(&np
, offs
, lines
);
376 for (np
= ip
->m_multipart
; np
!= NULL
; np
= np
->m_nextpart
)
377 if (np
->m_mimecontent
!= MIME_DISCARD
)
378 parsepart(zmp
, np
, pf
, level
+ 1);
385 __newpart(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
, int *part
)
391 *np
= csalloc(1, sizeof **np
);
392 (*np
)->m_flag
= MNOFROM
;
393 (*np
)->m_have
= HAVE_HEADER
| HAVE_BODY
;
394 (*np
)->m_block
= mailx_blockof(offs
);
395 (*np
)->m_offset
= mailx_offsetof(offs
);
399 sz
= (ip
->m_partstring
!= NULL
) ? strlen(ip
->m_partstring
) : 0;
401 (*np
)->m_partstring
= salloc(sz
);
402 if (ip
->m_partstring
)
403 snprintf((*np
)->m_partstring
, sz
, "%s.%u", ip
->m_partstring
, *part
);
405 snprintf((*np
)->m_partstring
, sz
, "%u", *part
);
407 (*np
)->m_mimecontent
= MIME_DISCARD
;
408 (*np
)->m_parent
= ip
;
410 if (ip
->m_multipart
) {
411 for (pp
= ip
->m_multipart
; pp
->m_nextpart
!= NULL
; pp
= pp
->m_nextpart
)
413 pp
->m_nextpart
= *np
;
415 ip
->m_multipart
= *np
;
420 __endpart(struct mimepart
**np
, off_t xoffs
, long lines
)
425 offs
= mailx_positionof((*np
)->m_block
, (*np
)->m_offset
);
426 (*np
)->m_size
= (*np
)->m_xsize
= xoffs
- offs
;
427 (*np
)->m_lines
= (*np
)->m_xlines
= lines
;
433 _print_part_info(FILE *obuf
, struct mimepart
const *mpp
, /* TODO strtofmt.. */
434 struct ignoretab
*doign
, int level
, struct quoteflt
*qf
, ui64_t
*stats
)
437 struct str ti
= {NULL
, 0}, to
;
438 struct str
const *cpre
, *csuf
;
443 cpre
= colour_get(COLOURSPEC_PARTINFO
);
444 csuf
= colour_get(COLOURSPEC_RESET
);
449 /* Take care of "99.99", i.e., 5 */
450 if ((cp
= mpp
->m_partstring
) == NULL
|| cp
[0] == '\0')
452 if (level
|| (cp
[0] != '1' && cp
[1] == '\0'))
453 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
455 _out(cpre
->s
, cpre
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
456 _out("[-- #", 5, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
457 _out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
459 to
.l
= snprintf(buf
, sizeof buf
, " %" PRIuZ
"/%" PRIuZ
" ",
460 (uiz_t
)mpp
->m_lines
, (uiz_t
)mpp
->m_size
);
461 _out(buf
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
463 if ((cp
= mpp
->m_ct_type_usr_ovwr
) != NULL
)
464 _out("+", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
466 cp
= mpp
->m_ct_type_plain
;
467 if ((to
.l
= strlen(cp
)) > 30 && is_asccaseprefix(cp
, "application/")) {
468 size_t const al
= sizeof("appl../") -1, fl
= sizeof("application/") -1;
469 size_t i
= to
.l
- fl
;
470 char *x
= salloc(al
+ i
+1);
472 memcpy(x
, "appl../", al
);
473 memcpy(x
+ al
, cp
+ fl
, i
+1);
477 _out(cp
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
479 if (mpp
->m_multipart
== NULL
/* TODO */ && (cp
= mpp
->m_ct_enc
) != NULL
) {
480 _out(", ", 2, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
481 if (to
.l
> 25 && !asccasecmp(cp
, "quoted-printable"))
483 _out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
486 if (mpp
->m_multipart
== NULL
/* TODO */ && (cp
= mpp
->m_charset
) != NULL
) {
487 _out(", ", 2, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
488 _out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
491 _out(" --]", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
493 _out(csuf
->s
, csuf
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
494 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
496 if (is_ign("content-disposition", 19, doign
) && mpp
->m_filename
!= NULL
&&
497 *mpp
->m_filename
!= '\0') {
498 makeprint(n_str_add_cp(&ti
, mpp
->m_filename
), &to
);
500 to
.l
= delctrl(to
.s
, to
.l
);
503 _out(cpre
->s
, cpre
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
504 _out("[-- ", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
505 _out(to
.s
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
506 _out(" --]", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
508 _out(csuf
->s
, csuf
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
509 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
516 static enum pipeflags
517 _pipecmd(char const **result
, struct mimepart
const *mpp
)
525 /* Do we have any handler for this part? */
526 if ((cp
= mime_type_mimepart_handler(mpp
)) == NULL
)
528 else if (cp
== MIME_TYPE_HANDLER_TEXT
)
531 #ifdef HAVE_FILTER_HTML_TAGSOUP
532 cp
== MIME_TYPE_HANDLER_HTML
||
537 } else if (!(pstate
& PS_MSGLIST_DIRECT
)) {
538 /* Viewing multiple messages in one go, don't block system */
539 *result
= _("[Directly address message only to display this]\n");
542 /* Viewing a single message only */
543 /* TODO send/MIME layer rewrite: when we have a single-pass parser
544 * TODO then the parsing phase and the send phase will be separated;
545 * TODO that allows us to ask a user *before* we start the send, i.e.,
546 * TODO *before* a pager pipe is setup */
548 /* Asynchronous command, normal command line */
549 *result
= ++cp
, rv
= PIPE_ASYNC
;
551 *result
= cp
, rv
= PIPE_COMM
;
558 _pipefile(char const *pipecomm
, struct mimepart
const *mpp
, FILE **qbuf
,
559 bool_t quote
, bool_t async
)
562 char const *env_addon
[8], *cp
, *sh
;
569 if ((*qbuf
= Ftmp(NULL
, "sendp", OF_RDWR
| OF_UNLINK
| OF_REGISTER
,
571 perror(_("tmpfile"));
577 #ifdef HAVE_FILTER_HTML_TAGSOUP
578 if (pipecomm
== MIME_TYPE_HANDLER_HTML
) {
579 union {int (*ptf
)(void); char const *sh
;} u
;
580 u
.ptf
= &htmlflt_process_main
;
581 rbuf
= Popen(MIME_TYPE_HANDLER_HTML
, "W", u
.sh
, NULL
, fileno(*qbuf
));
582 pipecomm
= "Builtin HTML tagsoup filter";
583 goto jafter_tagsoup_hack
;
588 if (mpp
== NULL
|| (cp
= mpp
->m_filename
) == NULL
)
590 env_addon
[0] = str_concat_csvl(&s
, NAILENV_FILENAME
, "=", cp
, NULL
)->s
;
592 /* NAIL_FILENAME_GENERATED */
593 s
.s
= getrandstring(NAME_MAX
);
596 else if (*cp
== '\0') {
597 if ( (((cp
= mpp
->m_ct_type_usr_ovwr
) == NULL
|| *cp
== '\0') &&
598 ((cp
= mpp
->m_ct_type_plain
) == NULL
|| *cp
== '\0')) ||
599 ((sh
= strrchr(cp
, '/')) == NULL
|| *++sh
== '\0'))
604 cp
= savecat(s
.s
, sh
);
607 env_addon
[1] = str_concat_csvl(&s
, NAILENV_FILENAME_GENERATED
, "=", cp
,
610 /* NAIL_CONTENT{,_EVIDENCE} */
611 if (mpp
== NULL
|| (cp
= mpp
->m_ct_type_plain
) == NULL
)
613 env_addon
[2] = str_concat_csvl(&s
, NAILENV_CONTENT
, "=", cp
, NULL
)->s
;
615 if (mpp
!= NULL
&& mpp
->m_ct_type_usr_ovwr
!= NULL
)
616 cp
= mpp
->m_ct_type_usr_ovwr
;
617 env_addon
[3] = str_concat_csvl(&s
, NAILENV_CONTENT_EVIDENCE
, "=", cp
,
620 env_addon
[4] = str_concat_csvl(&s
, NAILENV_TMPDIR
, "=", tempdir
, NULL
)->s
;
621 env_addon
[5] = str_concat_csvl(&s
, "TMPDIR", "=", tempdir
, NULL
)->s
;
625 if ((sh
= ok_vlook(SHELL
)) == NULL
)
628 rbuf
= Popen(pipecomm
, "W", sh
, env_addon
, (async
? -1 : fileno(*qbuf
)));
629 #ifdef HAVE_FILTER_HTML_TAGSOUP
633 fprintf(stderr
, _("Cannot run MIME type handler \"%s\": %s\n"),
634 pipecomm
, strerror(errno
));
645 _out(char const *buf
, size_t len
, FILE *fp
, enum conversion convert
, enum
646 sendaction action
, struct quoteflt
*qf
, ui64_t
*stats
, struct str
*rest
)
653 Well
... it turns out to
not work like that since of course a valid
654 RFC
4155 compliant parser
, like S
-nail
, takes care
for From_ lines only
655 after an empty line has been seen
, which cannot be detected that easily
657 ifdef HAVE_DEBUG
/* TODO assert legacy */
658 /* TODO if at all, this CAN only happen for SEND_DECRYPT, since all
659 * TODO other input situations handle RFC 4155 OR, if newly generated,
660 * TODO enforce quoted-printable if there is From_, as "required" by
661 * TODO RFC 5751. The SEND_DECRYPT case is not yet overhauled;
662 * TODO if it may happen in this path, we should just treat decryption
663 * TODO as we do for the other input paths; i.e., handle it in SSL!! */
664 if (action
== SEND_MBOX
|| action
== SEND_DECRYPT
)
665 assert(!is_head(buf
, len
, TRU1
));
667 if ((/*action == SEND_MBOX ||*/ action
== SEND_DECRYPT
) &&
668 is_head(buf
, len
, TRU1
)) {
674 flags
= ((int)action
& _TD_EOF
);
676 n
= mime_write(buf
, len
, fp
,
677 action
== SEND_MBOX
? CONV_NONE
: convert
,
678 flags
| ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
679 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
)
681 : (action
== SEND_TOSRCH
|| action
== SEND_TOPIPE
)
682 ? TD_ICONV
: (action
== SEND_SHOW
? TD_ISPR
: TD_NONE
)),
696 _send_onpipe(int signo
)
698 NYD_X
; /* Signal handler */
700 siglongjmp(_send_pipejmp
, 1);
703 static sigjmp_buf __sendp_actjmp
; /* TODO someday.. */
704 static int __sendp_sig
; /* TODO someday.. */
705 static sighandler_type __sendp_opipe
;
707 __sendp_onsig(int sig
) /* TODO someday, we won't need it no more */
709 NYD_X
; /* Signal handler */
711 siglongjmp(__sendp_actjmp
, 1);
714 static sigjmp_buf __sndalter_actjmp
; /* TODO someday.. */
715 static int __sndalter_sig
; /* TODO someday.. */
717 __sndalter_onsig(int sig
) /* TODO someday, we won't need it no more */
719 NYD_X
; /* Signal handler */
720 __sndalter_sig
= sig
;
721 siglongjmp(__sndalter_actjmp
, 1);
725 sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE * volatile obuf
,
726 struct ignoretab
*doign
, struct quoteflt
*qf
,
727 enum sendaction
volatile action
, ui64_t
* volatile stats
, int level
)
729 int volatile ispipe
, rv
= 0;
731 char *line
= NULL
, *cp
, *cp2
, *start
;
732 char const *pipecomm
= NULL
;
733 size_t linesize
= 0, linelen
, cnt
;
734 int dostat
, infld
= 0, ignoring
= 1, isenc
, c
;
735 struct mimepart
*volatile np
;
736 FILE * volatile ibuf
= NULL
, * volatile pbuf
= obuf
, * volatile qbuf
= obuf
,
738 enum conversion
volatile convert
;
739 sighandler_type
volatile oldpipe
= SIG_DFL
;
743 if (ip
->m_mimecontent
== MIME_PKCS7
&& ip
->m_multipart
&&
744 action
!= SEND_MBOX
&& action
!= SEND_RFC822
&& action
!= SEND_SHOW
)
750 if (!is_ign("status", 6, doign
))
752 if (!is_ign("x-status", 8, doign
))
757 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
) {
763 if (ip
->m_mimecontent
== MIME_DISCARD
)
766 if (!(ip
->m_flag
& MNOFROM
))
767 while (cnt
&& (c
= getc(ibuf
)) != EOF
) {
773 convert
= (action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
774 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
775 action
== SEND_TOSRCH
)
776 ? CONV_FROMHDR
: CONV_NONE
;
778 /* Work the headers */
779 quoteflt_reset(qf
, obuf
);
780 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0)) {
782 if (line
[0] == '\n') {
783 /* If line is blank, we've reached end of headers, so force out
784 * status: field and note that we are no longer in header fields */
786 statusput(zmp
, obuf
, qf
, stats
);
788 xstatusput(zmp
, obuf
, qf
, stats
);
789 if (doign
!= allignore
)
790 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
795 if (infld
&& blankchar(line
[0])) {
796 /* If this line is a continuation (SP / HT) of a previous header
797 * field, determine if the start of the line is a MIME encoded word */
799 for (cp
= line
; blankchar(*cp
); ++cp
);
800 if (cp
> line
&& linelen
- PTR2SIZE(cp
- line
) > 8 &&
801 cp
[0] == '=' && cp
[1] == '?')
805 /* Pick up the header field if we have one */
806 for (cp
= line
; (c
= *cp
& 0377) && c
!= ':' && !spacechar(c
); ++cp
)
809 while (spacechar(*cp
))
811 if (cp
[0] != ':' && level
== 0 && lineno
== 1) {
812 /* Not a header line, force out status: This happens in uucp style
813 * mail where there are no headers at all */
815 statusput(zmp
, obuf
, qf
, stats
);
817 xstatusput(zmp
, obuf
, qf
, stats
);
818 if (doign
!= allignore
)
819 _out("\n", 1, obuf
, CONV_NONE
,SEND_MBOX
, qf
, stats
, NULL
);
823 /* If it is an ignored field and we care about such things, skip it.
824 * Misuse dostat also for another bit xxx use a bitenum + for more */
825 if (ok_blook(keep_content_length
))
828 *cp2
= 0; /* temporarily null terminate */
829 if ((doign
&& is_ign(line
, PTR2SIZE(cp2
- line
), doign
)) ||
830 (action
== SEND_MBOX
&& !(dostat
& (1 << 2)) &&
831 (!asccasecmp(line
, "content-length") ||
832 !asccasecmp(line
, "lines"))))
834 else if (!asccasecmp(line
, "status")) {
835 /* If field is "status," go compute and print real Status: field */
837 statusput(zmp
, obuf
, qf
, stats
);
841 } else if (!asccasecmp(line
, "x-status")) {
842 /* If field is "status," go compute and print real Status: field */
844 xstatusput(zmp
, obuf
, qf
, stats
);
850 /* For colourization we need the complete line, so save it */
851 /* XXX This is all temporary (colour belongs into backend), so
852 * XXX use pipecomm as a temporary storage in the meanwhile */
854 if (colour_table
!= NULL
)
855 pipecomm
= savestrbuf(line
, PTR2SIZE(cp2
- line
));
863 /* Determine if the end of the line is a MIME encoded word */
864 /* TODO geeeh! all this lengthy stuff that follows is about is dealing
865 * TODO with header follow lines, and it should be up to the backend
866 * TODO what happens and what not, i.e., it doesn't matter wether it's
867 * TODO a MIME-encoded word or not, as long as a single separating space
868 * TODO remains in between lines (the MIME stuff will correctly remove
869 * TODO whitespace in between multiple adjacent encoded words) */
871 if (cnt
&& (c
= getc(ibuf
)) != EOF
) {
873 cp
= line
+ linelen
- 1;
874 if (linelen
> 0 && *cp
== '\n')
876 while (cp
>= line
&& whitechar(*cp
))
878 if (PTR2SIZE(cp
- line
> 8) && cp
[0] == '=' && cp
[-1] == '?')
885 size_t len
= linelen
;
887 if (action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
888 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
889 action
== SEND_TOSRCH
) {
890 /* Strip blank characters if two MIME-encoded words follow on
891 * continuing lines */
893 while (len
> 0 && blankchar(*start
)) {
898 if (len
> 0 && start
[len
- 1] == '\n')
900 while (len
> 0 && blankchar(start
[len
- 1]))
905 bool_t colour_stripped
= FAL0
;
906 if (pipecomm
!= NULL
) {
907 colour_put_header(obuf
, pipecomm
);
908 if (len
> 0 && start
[len
- 1] == '\n') {
909 colour_stripped
= TRU1
;
914 _out(start
, len
, obuf
, convert
, action
, qf
, stats
, NULL
);
916 if (pipecomm
!= NULL
) {
917 colour_reset(obuf
); /* XXX reset after \n!! */
936 switch (ip
->m_mimecontent
) {
940 case SEND_TODISP_ALL
:
943 if (ok_blook(rfc822_body_from_
)) {
944 if (qf
->qf_pfix_len
> 0) {
945 size_t i
= fwrite(qf
->qf_pfix
, sizeof *qf
->qf_pfix
,
946 qf
->qf_pfix_len
, obuf
);
947 if (i
== qf
->qf_pfix_len
&& stats
!= NULL
)
950 put_from_(obuf
, ip
->m_multipart
, stats
);
958 if (ok_blook(rfc822_body_from_
))
959 put_from_(obuf
, ip
->m_multipart
, stats
);
969 case MIME_TEXT_PLAIN
:
972 case SEND_TODISP_ALL
:
976 switch (_pipecmd(&pipecomm
, ip
)) {
978 _out(pipecomm
, strlen(pipecomm
), obuf
, CONV_NONE
, SEND_MBOX
, qf
,
980 /* We would print this as plain text, so better force going home */
996 if (action
!= SEND_DECRYPT
)
1000 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
1001 action
!= SEND_SHOW
&& ip
->m_multipart
!= NULL
)
1007 case SEND_TODISP_ALL
:
1009 case SEND_QUOTE_ALL
:
1011 switch (_pipecmd(&pipecomm
, ip
)) {
1013 _out(pipecomm
, strlen(pipecomm
), obuf
, CONV_NONE
, SEND_MBOX
, qf
,
1024 goto jcopyout
; /* break; break; */
1026 if (pipecomm
!= NULL
)
1028 if (level
== 0 && cnt
) {
1029 char const *x
= _("[Binary content]\n");
1030 _out(x
, strlen(x
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
1043 case MIME_ALTERNATIVE
:
1045 if ((action
== SEND_TODISP
|| action
== SEND_QUOTE
) &&
1046 !ok_blook(print_alternatives
)) {
1047 /* XXX This (a) should not remain (b) should be own fun */
1049 struct mpstack
*outer
;
1050 struct mimepart
*mp
;
1051 } outermost
, * volatile curr
= &outermost
, * volatile mpsp
;
1052 sighandler_type
volatile opsh
, oish
, ohsh
;
1053 size_t volatile partcnt
= 0/* silence CC */;
1054 bool_t
volatile neednl
= FAL0
;
1060 opsh
= safe_signal(SIGPIPE
, &__sndalter_onsig
);
1061 oish
= safe_signal(SIGINT
, &__sndalter_onsig
);
1062 ohsh
= safe_signal(SIGHUP
, &__sndalter_onsig
);
1063 if (sigsetjmp(__sndalter_actjmp
, 1)) {
1068 for (np
= ip
->m_multipart
;;) {
1071 for (; np
!= NULL
; np
= np
->m_nextpart
) {
1072 if (action
!= SEND_QUOTE
&& np
->m_ct_type_plain
!= NULL
) {
1074 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
1075 _print_part_info(obuf
, np
, doign
, level
, qf
, stats
);
1079 switch (np
->m_mimecontent
) {
1080 case MIME_ALTERNATIVE
:
1084 mpsp
= ac_alloc(sizeof *mpsp
);
1086 mpsp
->mp
= np
->m_multipart
;
1093 switch (_pipecmd(&pipecomm
, np
)) {
1100 case MIME_TEXT_PLAIN
:
1102 if (action
== SEND_QUOTE
&& partcnt
> 1 &&
1103 ip
->m_mimecontent
== MIME_ALTERNATIVE
)
1106 if (action
== SEND_QUOTE
&& partcnt
> 1) {
1107 struct quoteflt
*dummy
= quoteflt_dummy();
1108 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, dummy
, stats
,
1110 quoteflt_flush(dummy
);
1113 rv
= sendpart(zmp
, np
, obuf
, doign
, qf
, action
, stats
,
1115 quoteflt_reset(qf
, origobuf
);
1119 for (;; curr
= mpsp
) {
1120 if ((mpsp
= curr
->outer
) == NULL
)
1134 np
= curr
->mp
->m_nextpart
;
1136 safe_signal(SIGHUP
, ohsh
);
1137 safe_signal(SIGINT
, oish
);
1138 safe_signal(SIGPIPE
, opsh
);
1139 if (__sndalter_sig
!= 0)
1140 n_raise(__sndalter_sig
);
1148 case SEND_TODISP_ALL
:
1150 case SEND_QUOTE_ALL
:
1156 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
) &&
1157 ip
->m_multipart
!= NULL
&&
1158 ip
->m_multipart
->m_mimecontent
== MIME_DISCARD
&&
1159 ip
->m_multipart
->m_nextpart
== NULL
) {
1160 char const *x
= _("[Missing multipart boundary - use \"show\" "
1161 "to display the raw message]\n");
1162 _out(x
, strlen(x
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
);
1165 for (np
= ip
->m_multipart
; np
!= NULL
; np
= np
->m_nextpart
) {
1166 if (np
->m_mimecontent
== MIME_DISCARD
&& action
!= SEND_DECRYPT
)
1171 if (np
->m_partstring
&& !strcmp(np
->m_partstring
, "1"))
1174 if ((obuf
= newfile(np
, UNVOLATILE(&ispipe
))) == NULL
)
1178 if (sigsetjmp(_send_pipejmp
, 1)) {
1182 oldpipe
= safe_signal(SIGPIPE
, &_send_onpipe
);
1185 case SEND_TODISP_ALL
:
1186 case SEND_QUOTE_ALL
:
1187 if (ip
->m_mimecontent
!= MIME_MULTI
&&
1188 ip
->m_mimecontent
!= MIME_ALTERNATIVE
&&
1189 ip
->m_mimecontent
!= MIME_RELATED
&&
1190 ip
->m_mimecontent
!= MIME_DIGEST
)
1192 _print_part_info(obuf
, np
, doign
, level
, qf
, stats
);
1205 if (sendpart(zmp
, np
, obuf
, doign
, qf
, action
, stats
, level
+1) < 0)
1207 quoteflt_reset(qf
, origobuf
);
1209 if (action
== SEND_QUOTE
)
1211 if (action
== SEND_TOFILE
&& obuf
!= origobuf
) {
1216 safe_signal(SIGPIPE
, SIG_IGN
);
1218 safe_signal(SIGPIPE
, oldpipe
);
1230 /* Copy out message body */
1232 if (doign
== allignore
&& level
== 0) /* skip final blank line */
1234 switch (ip
->m_mime_enc
) {
1238 convert
= CONV_NONE
;
1241 convert
= CONV_FROMQP
;
1244 switch (ip
->m_mimecontent
) {
1246 case MIME_TEXT_PLAIN
:
1247 case MIME_TEXT_HTML
:
1248 convert
= CONV_FROMB64_T
;
1251 convert
= CONV_FROMB64
;
1255 convert
= CONV_NONE
;
1258 if (action
== SEND_DECRYPT
|| action
== SEND_MBOX
||
1259 action
== SEND_RFC822
|| action
== SEND_SHOW
)
1260 convert
= CONV_NONE
;
1262 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
1263 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
1264 action
== SEND_TOSRCH
) &&
1265 (ip
->m_mimecontent
== MIME_TEXT_PLAIN
||
1266 ip
->m_mimecontent
== MIME_TEXT_HTML
||
1267 ip
->m_mimecontent
== MIME_TEXT
)) {
1268 char const *tcs
= charset_get_lc();
1270 if (iconvd
!= (iconv_t
)-1)
1271 n_iconv_close(iconvd
);
1272 /* TODO Since Base64 has an odd 4:3 relation in between input
1273 * TODO and output an input line may end with a partial
1274 * TODO multibyte character; this is no problem at all unless
1275 * TODO we send to the display or whatever, i.e., ensure
1276 * TODO makeprint() or something; to avoid this trap, *force*
1277 * TODO iconv(), in which case this layer will handle leftovers
1279 if (convert
== CONV_FROMB64_T
|| (asccasecmp(tcs
, ip
->m_charset
) &&
1280 asccasecmp(charset_get_7bit(), ip
->m_charset
))) {
1281 iconvd
= n_iconv_open(tcs
, ip
->m_charset
);
1282 /* XXX Don't bail out if we cannot iconv(3) here;
1283 * XXX alternatively we could avoid trying to open
1284 * XXX if ip->m_charset is "unknown-8bit", which was
1285 * XXX the one that has bitten me?? */
1287 * TODO errors should DEFINETELY not be scrolled away!
1288 * TODO what about an error buffer (think old shsp(1)),
1289 * TODO re-dump errors since last snapshot when the
1290 * TODO command loop enters again? i.e., at least print
1291 * TODO "There were errors ?" before the next prompt,
1292 * TODO so that the user can look at the error buffer?
1294 if (iconvd
== (iconv_t
)-1 && errno
== EINVAL
) {
1295 fprintf(stderr
, _("Cannot convert from %s to %s\n"),
1296 ip
->m_charset
, tcs
);
1297 /*rv = 1; goto jleave;*/
1303 if (pipecomm
!= NULL
&& (action
== SEND_TODISP
||
1304 action
== SEND_TODISP_ALL
|| action
== SEND_QUOTE
||
1305 action
== SEND_QUOTE_ALL
)) {
1307 pbuf
= _pipefile(pipecomm
, ip
, UNVOLATILE(&qbuf
),
1308 (action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
), !ispipe
);
1311 if (iconvd
!= (iconv_t
)-1)
1312 n_iconv_close(iconvd
);
1317 action
= SEND_TOPIPE
;
1319 oldpipe
= safe_signal(SIGPIPE
, &_send_onpipe
);
1320 if (sigsetjmp(_send_pipejmp
, 1))
1327 bool_t
volatile eof
;
1328 ui32_t save_qf_pfix_len
= qf
->qf_pfix_len
;
1329 ui64_t
*save_stats
= stats
;
1331 if (pbuf
!= origobuf
) {
1332 qf
->qf_pfix_len
= 0; /* XXX legacy (remove filter instead) */
1341 __sendp_opipe
= safe_signal(SIGPIPE
, &__sendp_onsig
);
1342 if (sigsetjmp(__sendp_actjmp
, 1)) {
1347 if (iconvd
!= (iconv_t
)-1)
1348 n_iconv_close(iconvd
);
1350 safe_signal(SIGPIPE
, __sendp_opipe
);
1351 n_raise(__sendp_sig
);
1355 quoteflt_reset(qf
, pbuf
);
1356 while (!eof
&& fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0)) {
1358 if (_out(line
, linelen
, pbuf
, convert
, action
, qf
, stats
, &rest
) < 0 ||
1360 rv
= -1; /* XXX Should bail away?! */
1364 if (!eof
&& rest
.l
!= 0) {
1371 safe_signal(SIGPIPE
, __sendp_opipe
);
1377 if (pbuf
!= origobuf
) {
1378 qf
->qf_pfix_len
= save_qf_pfix_len
;
1387 safe_signal(SIGPIPE
, SIG_IGN
);
1388 Pclose(pbuf
, ispipe
);
1389 safe_signal(SIGPIPE
, oldpipe
);
1390 if (qbuf
!= NULL
&& qbuf
!= obuf
)
1391 pipecpy(qbuf
, obuf
, origobuf
, qf
, stats
);
1394 if (iconvd
!= (iconv_t
)-1)
1395 n_iconv_close(iconvd
);
1403 newfile(struct mimepart
*ip
, int *ispipe
)
1413 if (f
!= NULL
&& f
!= (char*)-1) {
1416 makeprint(&in
, &out
);
1417 out
.l
= delctrl(out
.s
, out
.l
);
1418 f
= savestrbuf(out
.s
, out
.l
);
1422 if (options
& OPT_INTERACTIVE
) {
1425 printf(_("Enter filename for part %s (%s)"),
1426 (ip
->m_partstring
!= NULL
) ? ip
->m_partstring
: "?",
1427 ip
->m_ct_type_plain
);
1428 f2
= readstr_input(": ", (f
!= (char*)-1 && f
!= NULL
)
1429 ? fexpand_nshell_quote(f
) : NULL
);
1430 if (f2
== NULL
|| *f2
== '\0') {
1431 fprintf(stderr
, _("... skipping this\n"));
1434 } else if (*f2
== '|')
1435 /* Pipes are expanded by the shell */
1437 else if ((f3
= fexpand(f2
, FEXP_LOCAL
| FEXP_NSHELL
)) == NULL
)
1438 /* (Error message written by fexpand()) */
1443 if (f
== NULL
|| f
== (char*)-1) {
1450 cp
= ok_vlook(SHELL
);
1453 fp
= Popen(f
+ 1, "w", cp
, NULL
, 1);
1454 if (!(*ispipe
= (fp
!= NULL
)))
1457 if ((fp
= Fopen(f
, "w")) == NULL
)
1458 fprintf(stderr
, _("Cannot open \"%s\"\n"), f
);
1466 pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
, struct quoteflt
*qf
,
1469 char *line
= NULL
; /* TODO line pool */
1470 size_t linesize
= 0, linelen
, cnt
;
1476 cnt
= fsize(pipebuf
);
1479 quoteflt_reset(qf
, outbuf
);
1480 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, pipebuf
, 0) != NULL
) {
1481 if ((sz
= quoteflt_push(qf
, line
, linelen
)) < 0)
1485 if ((sz
= quoteflt_flush(qf
)) > 0)
1490 if (all_sz
> 0 && outbuf
== origobuf
&& stats
!= NULL
)
1497 statusput(const struct message
*mp
, FILE *obuf
, struct quoteflt
*qf
,
1500 char statout
[3], *cp
= statout
;
1503 if (mp
->m_flag
& MREAD
)
1505 if (!(mp
->m_flag
& MNEW
))
1509 int i
= fprintf(obuf
, "%.*sStatus: %s\n", (int)qf
->qf_pfix_len
,
1510 (qf
->qf_pfix_len
> 0 ? qf
->qf_pfix
: 0), statout
);
1511 if (i
> 0 && stats
!= NULL
)
1518 xstatusput(const struct message
*mp
, FILE *obuf
, struct quoteflt
*qf
,
1522 char *xp
= xstatout
;
1525 if (mp
->m_flag
& MFLAGGED
)
1527 if (mp
->m_flag
& MANSWERED
)
1529 if (mp
->m_flag
& MDRAFTED
)
1533 int i
= fprintf(obuf
, "%.*sX-Status: %s\n", (int)qf
->qf_pfix_len
,
1534 (qf
->qf_pfix_len
> 0 ? qf
->qf_pfix
: 0), xstatout
);
1535 if (i
> 0 && stats
!= NULL
)
1542 put_from_(FILE *fp
, struct mimepart
*ip
, ui64_t
*stats
)
1544 char const *froma
, *date
, *nl
;
1548 if (ip
!= NULL
&& ip
->m_from
!= NULL
) {
1550 date
= fakedate(ip
->m_time
);
1554 date
= time_current
.tc_ctime
;
1558 colour_put(fp
, COLOURSPEC_FROM_
);
1559 i
= fprintf(fp
, "From %s %s%s", froma
, date
, nl
);
1561 if (i
> 0 && stats
!= NULL
)
1567 sendmp(struct message
*mp
, FILE *obuf
, struct ignoretab
*doign
,
1568 char const *prefix
, enum sendaction action
, ui64_t
*stats
)
1574 struct mimepart
*ip
;
1578 if (mp
== dot
&& action
!= SEND_TOSRCH
)
1579 pstate
|= PS_DID_PRINT_DOT
;
1582 quoteflt_init(&qf
, prefix
);
1584 /* First line is the From_ line, so no headers there to worry about */
1585 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
1591 struct str
const *cpre
, *csuf
;
1593 cpre
= colour_get(COLOURSPEC_FROM_
);
1594 csuf
= colour_get(COLOURSPEC_RESET
);
1598 if (mp
->m_flag
& MNOFROM
) {
1599 if (doign
!= allignore
&& doign
!= fwdignore
&& action
!= SEND_RFC822
)
1600 sz
= fprintf(obuf
, "%s%.*sFrom %s %s%s\n",
1601 (cpre
!= NULL
? cpre
->s
: ""),
1602 (int)qf
.qf_pfix_len
, (qf
.qf_pfix_len
!= 0 ? qf
.qf_pfix
: ""),
1603 fakefrom(mp
), fakedate(mp
->m_time
),
1604 (csuf
!= NULL
? csuf
->s
: ""));
1606 if (doign
!= allignore
&& doign
!= fwdignore
&& action
!= SEND_RFC822
) {
1607 if (qf
.qf_pfix_len
> 0) {
1608 i
= fwrite(qf
.qf_pfix
, sizeof *qf
.qf_pfix
, qf
.qf_pfix_len
, obuf
);
1609 if (i
!= qf
.qf_pfix_len
)
1615 fputs(cpre
->s
, obuf
);
1616 cpre
= (struct str
const*)0x1;
1621 while (cnt
> 0 && (c
= getc(ibuf
)) != EOF
) {
1622 if (doign
!= allignore
&& doign
!= fwdignore
&&
1623 action
!= SEND_RFC822
) {
1625 if (c
== '\n' && csuf
!= NULL
) {
1626 cpre
= (struct str
const*)0x1;
1627 fputs(csuf
->s
, obuf
);
1639 if (csuf
!= NULL
&& cpre
!= (struct str
const*)0x1)
1640 fputs(csuf
->s
, obuf
);
1644 if (sz
> 0 && stats
!= NULL
)
1648 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&& action
!= SEND_SHOW
)
1649 pf
|= PARSE_DECRYPT
| PARSE_PARTS
;
1650 if ((ip
= parsemsg(mp
, pf
)) == NULL
)
1653 rv
= sendpart(mp
, ip
, obuf
, doign
, &qf
, action
, stats
, 0);
1655 quoteflt_destroy(&qf
);