1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Message content preparation (sendmp()).
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #ifndef HAVE_AMALGAMATION
43 static sigjmp_buf _send_pipejmp
;
45 /* Going for user display, print Part: info string */
46 static void _print_part_info(FILE *obuf
, struct mimepart
const *mpp
,
47 struct n_ignore
const *doitp
, int level
,
48 struct quoteflt
*qf
, ui64_t
*stats
);
50 /* Create a pipe; if mpp is not NULL, place some n_PIPEENV_* environment
51 * variables accordingly */
52 static FILE * _pipefile(struct mime_handler
*mhp
,
53 struct mimepart
const *mpp
, FILE **qbuf
,
54 char const *tmpname
, int term_infd
);
56 /* Call mime_write() as approbiate and adjust statistics */
57 n_INLINE ssize_t
_out(char const *buf
, size_t len
, FILE *fp
,
58 enum conversion convert
, enum sendaction action
,
59 struct quoteflt
*qf
, ui64_t
*stats
, struct str
*outrest
,
62 /* Simply (!) print out a LF */
63 static bool_t
a_send_out_nl(FILE *fp
, ui64_t
*stats
);
66 static void _send_onpipe(int signo
);
69 static int sendpart(struct message
*zmp
, struct mimepart
*ip
,
70 FILE *obuf
, struct n_ignore
const *doitp
,
71 struct quoteflt
*qf
, enum sendaction action
,
72 char **linedat
, size_t *linesize
,
73 ui64_t
*stats
, int level
);
75 /* Get a file for an attachment */
76 static FILE * newfile(struct mimepart
*ip
, bool_t
volatile *ispipe
);
78 static void pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
,
79 struct quoteflt
*qf
, ui64_t
*stats
);
81 /* Output a reasonable looking status field */
82 static void statusput(const struct message
*mp
, FILE *obuf
,
83 struct quoteflt
*qf
, ui64_t
*stats
);
84 static void xstatusput(const struct message
*mp
, FILE *obuf
,
85 struct quoteflt
*qf
, ui64_t
*stats
);
87 static void put_from_(FILE *fp
, struct mimepart
*ip
, ui64_t
*stats
);
90 _print_part_info(FILE *obuf
, struct mimepart
const *mpp
, /* TODO strtofmt.. */
91 struct n_ignore
const *doitp
, int level
, struct quoteflt
*qf
, ui64_t
*stats
)
95 bool_t want_ct
, needsep
;
96 struct str
const *cpre
, *csuf
;
102 if(n_COLOUR_IS_ACTIVE()){
103 struct n_colour_pen
*cpen
;
105 cpen
= n_colour_pen_create(n_COLOUR_ID_VIEW_PARTINFO
, NULL
);
106 if((cpre
= n_colour_pen_to_str(cpen
)) != NULL
)
107 csuf
= n_colour_reset_to_str();
111 /* Take care of "99.99", i.e., 5 */
112 if ((cp
= mpp
->m_partstring
) == NULL
|| cp
[0] == '\0')
114 if (level
|| (cp
[0] != '1' && cp
[1] == '\0') || (cp
[0] == '1' && /* TODO */
115 cp
[1] == '.' && cp
[2] != '1')) /* TODO code should not look like so */
116 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
118 /* Part id, content-type, encoding, charset */
120 _out(cpre
->s
, cpre
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
121 _out("[-- #", 5, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
122 _out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
124 to
.l
= snprintf(buf
, sizeof buf
, " %" PRIuZ
"/%" PRIuZ
" ",
125 (uiz_t
)mpp
->m_lines
, (uiz_t
)mpp
->m_size
);
126 _out(buf
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
130 if((cp
= mpp
->m_ct_type_usr_ovwr
) != NULL
){
131 _out("+", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
133 }else if((want_ct
= n_ignore_is_ign(doitp
,
134 "content-type", sizeof("content-type") -1)))
135 cp
= mpp
->m_ct_type_plain
;
137 (to
.l
= strlen(cp
)) > 30 && is_asccaseprefix("application/", cp
)) {
138 size_t const al
= sizeof("appl../") -1, fl
= sizeof("application/") -1;
139 size_t i
= to
.l
- fl
;
140 char *x
= n_autorec_alloc(al
+ i
+1);
142 memcpy(x
, "appl../", al
);
143 memcpy(x
+ al
, cp
+ fl
, i
+1);
148 _out(cp
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
152 if(mpp
->m_multipart
== NULL
/* TODO */ && (cp
= mpp
->m_ct_enc
) != NULL
&&
153 (!asccasecmp(cp
, "7bit") ||
154 n_ignore_is_ign(doitp
, "content-transfer-encoding",
155 sizeof("content-transfer-encoding") -1))){
157 _out(", ", 2, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
158 if (to
.l
> 25 && !asccasecmp(cp
, "quoted-printable"))
160 _out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
164 if (want_ct
&& mpp
->m_multipart
== NULL
/* TODO */ &&
165 (cp
= mpp
->m_charset
) != NULL
) {
167 _out(", ", 2, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
168 _out(cp
, strlen(cp
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
172 _out(&" --]"[needsep
], 4 - needsep
,
173 obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
175 _out(csuf
->s
, csuf
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
176 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
179 if (mpp
->m_content_info
& CI_MIME_ERRORS
) {
181 _out(cpre
->s
, cpre
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
183 _out("[-- ", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
185 ti
.l
= strlen(ti
.s
= n_UNCONST(_("Defective MIME structure")));
187 to
.l
= delctrl(to
.s
, to
.l
);
188 _out(to
.s
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
191 _out(" --]", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
193 _out(csuf
->s
, csuf
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
195 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
198 /* Content-Description */
199 if (n_ignore_is_ign(doitp
, "content-description", 19) &&
200 (cp
= mpp
->m_content_description
) != NULL
&& *cp
!= '\0') {
202 _out(cpre
->s
, cpre
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
204 _out("[-- ", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
206 ti
.l
= strlen(ti
.s
= n_UNCONST(mpp
->m_content_description
));
207 mime_fromhdr(&ti
, &to
, TD_ISPR
| TD_ICONV
);
208 _out(to
.s
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
211 _out(" --]", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
213 _out(csuf
->s
, csuf
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
215 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
219 if (n_ignore_is_ign(doitp
, "content-disposition", 19) &&
220 mpp
->m_filename
!= NULL
&& *mpp
->m_filename
!= '\0') {
222 _out(cpre
->s
, cpre
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
224 _out("[-- ", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
226 ti
.l
= strlen(ti
.s
= mpp
->m_filename
);
228 to
.l
= delctrl(to
.s
, to
.l
);
229 _out(to
.s
, to
.l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
232 _out(" --]", 4, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
234 _out(csuf
->s
, csuf
->l
, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
236 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
, NULL
);
242 _pipefile(struct mime_handler
*mhp
, struct mimepart
const *mpp
, FILE **qbuf
,
243 char const *tmpname
, int term_infd
)
246 char const *env_addon
[9 +8/*v15*/], *cp
, *sh
;
253 if (mhp
->mh_flags
& MIME_HDL_ISQUOTE
) {
254 if ((*qbuf
= Ftmp(NULL
, "sendp", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
256 n_perr(_("tmpfile"), 0);
261 if ((mhp
->mh_flags
& MIME_HDL_TYPE_MASK
) == MIME_HDL_PTF
) {
262 union {int (*ptf
)(void); char const *sh
;} u
;
265 if (*qbuf
!= n_stdout
) /* xxx never? v15: it'll be a filter anyway */
269 if((rbuf
= Popen((char*)-1, "W", u
.sh
, NULL
, fileno(*qbuf
))) == NULL
)
277 if (mpp
== NULL
|| (cp
= mpp
->m_filename
) == NULL
)
279 env_addon
[i
++] = str_concat_csvl(&s
, n_PIPEENV_FILENAME
, "=", cp
, NULL
)->s
;
280 env_addon
[i
++] = str_concat_csvl(&s
, "NAIL_FILENAME", "=", cp
, NULL
)->s
;/*v15*/
282 /* MAILX_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
283 * TODO a file wherever he wants! *Do* create a zero-size temporary file
284 * TODO and give *that* path as MAILX_FILENAME_TEMPORARY, clean it up once
285 * TODO the pipe returns? Like this we *can* verify path/name issues! */
286 cp
= n_random_create_cp(n_MIN(NAME_MAX
/ 4, 16), NULL
);
287 env_addon
[i
++] = str_concat_csvl(&s
, n_PIPEENV_FILENAME_GENERATED
, "=", cp
,
289 env_addon
[i
++] = str_concat_csvl(&s
, "NAIL_FILENAME_GENERATED", "=", cp
,/*v15*/
292 /* MAILX_CONTENT{,_EVIDENCE} */
293 if (mpp
== NULL
|| (cp
= mpp
->m_ct_type_plain
) == NULL
)
295 env_addon
[i
++] = str_concat_csvl(&s
, n_PIPEENV_CONTENT
, "=", cp
, NULL
)->s
;
296 env_addon
[i
++] = str_concat_csvl(&s
, "NAIL_CONTENT", "=", cp
, NULL
)->s
;/*v15*/
298 if (mpp
!= NULL
&& mpp
->m_ct_type_usr_ovwr
!= NULL
)
299 cp
= mpp
->m_ct_type_usr_ovwr
;
300 env_addon
[i
++] = str_concat_csvl(&s
, n_PIPEENV_CONTENT_EVIDENCE
, "=", cp
,
302 env_addon
[i
++] = str_concat_csvl(&s
, "NAIL_CONTENT_EVIDENCE", "=", cp
,/* v15 */
305 /* message/external-body, access-type=url */
306 env_addon
[i
++] = str_concat_csvl(&s
, n_PIPEENV_EXTERNAL_BODY_URL
, "=",
307 ((mpp
!= NULL
&& (cp
= mpp
->m_external_body_url
) != NULL
308 ) ? cp
: n_empty
), NULL
)->s
;
310 /* MAILX_FILENAME_TEMPORARY? */
311 if (tmpname
!= NULL
) {
312 env_addon
[i
++] = str_concat_csvl(&s
,
313 n_PIPEENV_FILENAME_TEMPORARY
, "=", tmpname
, NULL
)->s
;
314 env_addon
[i
++] = str_concat_csvl(&s
,
315 "NAIL_FILENAME_TEMPORARY", "=", tmpname
, NULL
)->s
;/* v15 */
318 /* TODO we should include header information, especially From:, so
319 * TODO that same-origin can be tested for e.g. external-body!!! */
322 sh
= ok_vlook(SHELL
);
324 if (mhp
->mh_flags
& MIME_HDL_NEEDSTERM
) {
329 pid
= n_child_run(sh
, &nset
, term_infd
, n_CHILD_FD_PASS
, "-c",
330 mhp
->mh_shell_cmd
, NULL
, env_addon
, NULL
);
331 rbuf
= (pid
< 0) ? NULL
: (FILE*)-1;
333 rbuf
= Popen(mhp
->mh_shell_cmd
, "W", sh
, env_addon
,
334 (mhp
->mh_flags
& MIME_HDL_ASYNC
? -1 : fileno(*qbuf
)));
337 n_err(_("Cannot run MIME type handler: %s: %s\n"),
338 mhp
->mh_msg
, n_err_to_doc(n_err_no
));
341 if (*qbuf
!= n_stdout
)
351 _out(char const *buf
, size_t len
, FILE *fp
, enum conversion convert
, enum
352 sendaction action
, struct quoteflt
*qf
, ui64_t
*stats
, struct str
*outrest
,
359 /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
360 * TODO subclass should detect such From_ cases and either reencode the part
361 * TODO in question, or perform From_ quoting as necessary!?!?!? How?!? */
365 if((action
== SEND_MBOX
|| action
== SEND_DECRYPT
) &&
366 (from_
= is_head(buf
, len
, TRU1
))){
367 if(from_
!= TRUM1
|| ok_blook(mbox_rfc4155
)){
374 flags
= ((int)action
& _TD_EOF
);
376 n
= mime_write(buf
, len
, fp
,
377 action
== SEND_MBOX
? CONV_NONE
: convert
,
378 flags
| ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
379 action
== SEND_TODISP_PARTS
||
380 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
)
382 : (action
== SEND_TOSRCH
|| action
== SEND_TOPIPE
||
383 action
== SEND_TOFILE
)
384 ? TD_ICONV
: (action
== SEND_SHOW
? TD_ISPR
: TD_NONE
)),
385 qf
, outrest
, inrest
);
398 a_send_out_nl(FILE *fp
, ui64_t
*stats
){
403 quoteflt_reset(qf
= quoteflt_dummy(), fp
);
404 rv
= (_out("\n", 1, fp
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
) > 0);
411 _send_onpipe(int signo
)
413 NYD_X
; /* Signal handler */
415 siglongjmp(_send_pipejmp
, 1);
418 static sigjmp_buf __sendp_actjmp
; /* TODO someday.. */
419 static int __sendp_sig
; /* TODO someday.. */
420 static sighandler_type __sendp_opipe
;
422 __sendp_onsig(int sig
) /* TODO someday, we won't need it no more */
424 NYD_X
; /* Signal handler */
426 siglongjmp(__sendp_actjmp
, 1);
430 sendpart(struct message
*zmp
, struct mimepart
*ip
, FILE * volatile obuf
,
431 struct n_ignore
const *doitp
, struct quoteflt
*qf
,
432 enum sendaction
volatile action
,
433 char **linedat
, size_t *linesize
, ui64_t
* volatile stats
, int level
)
436 struct mime_handler mh
;
437 struct str outrest
, inrest
;
439 char const * volatile tmpname
= NULL
;
441 int volatile dostat
, term_infd
;
443 struct mimepart
* volatile np
;
444 FILE * volatile ibuf
= NULL
, * volatile pbuf
= obuf
,
445 * volatile qbuf
= obuf
, *origobuf
= obuf
;
446 enum conversion
volatile convert
;
447 sighandler_type
volatile oldpipe
= SIG_DFL
;
450 n_UNINIT(term_infd
, 0);
453 quoteflt_reset(qf
, obuf
);
455 #if 0 /* TODO PART_INFO should be displayed here!! search PART_INFO */
456 if(ip
->m_mimecontent
!= MIME_DISCARD
&& level
> 0)
457 _print_part_info(obuf
, ip
, doitp
, level
, qf
, stats
);
460 if (ip
->m_mimecontent
== MIME_PKCS7
) {
461 if (ip
->m_multipart
&&
462 action
!= SEND_MBOX
&& action
!= SEND_RFC822
&& action
!= SEND_SHOW
)
467 if (level
== 0 && action
!= SEND_TODISP_PARTS
) {
469 if (!n_ignore_is_ign(doitp
, "status", 6))
471 if (!n_ignore_is_ign(doitp
, "x-status", 8))
477 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
) {
482 if(action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
483 action
== SEND_TODISP_PARTS
||
484 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
485 action
== SEND_TOSRCH
)
490 if (ip
->m_mimecontent
== MIME_DISCARD
)
493 if (!(ip
->m_flag
& MNOFROM
))
494 while (cnt
&& (c
= getc(ibuf
)) != EOF
) {
499 convert
= (dostat
& 4) ? CONV_FROMHDR
: CONV_NONE
;
501 /* Work the headers */
503 struct n_string hl
, *hlp
;
505 bool_t hstop
/*see below, hany*/;
507 hlp
= n_string_creat_auto(&hl
); /* TODO pool [or, v15: filter!] */
508 /* Reserve three lines, still not enough for references and DKIM etc. */
509 hlp
= n_string_reserve(hlp
, n_MAX(MIME_LINELEN
, MIME_LINELEN_RFC2047
) * 3);
511 for(hstop
= /*see below hany =*/ FAL0
; !hstop
;){
515 if(fgetline(linedat
, linesize
, &cnt
, &linelen
, ibuf
, 0) == NULL
)
518 if (linelen
== 0 || (cp
= *linedat
)[0] == '\n')
519 /* If line is blank, we've reached end of headers */
521 if(cp
[linelen
- 1] == '\n'){
522 cp
[--linelen
] = '\0';
527 /* Are we in a header? */
530 fseek(ibuf
, -(off_t
)(lcnt
- cnt
), SEEK_CUR
);
536 /* Pick up the header field if we have one */
537 while((c
= *cp
) != ':' && !spacechar(c
) && c
!= '\0')
540 if(!spacechar(c
) || c
== '\0')
545 /* That won't work with MIME when saving etc., before v15 */
547 /* XXX This disturbs, and may happen multiple times, and we
548 * XXX cannot heal it for multipart except for display <v15 */
549 n_err(_("Malformed message: headers and body not separated "
550 "(with empty line)\n"));
553 fseek(ibuf
, -(off_t
)(lcnt
- cnt
), SEEK_CUR
);
561 hlp
= n_string_push_buf(hlp
, cp
, (ui32_t
)linelen
);
562 hlp
= n_string_push_c(hlp
, '\n');
564 bool_t lblank
, isblank
;
566 for(lblank
= FAL0
, lcnt
= 0; lcnt
< linelen
; ++cp
, ++lcnt
){
570 if(!(isblank
= blankchar(c8
)) || !lblank
){
571 if((lblank
= isblank
))
573 hlp
= n_string_push_c(hlp
, c8
);
581 /* If it is an ignored header, skip it */
582 *(cp
= memchr(hlp
->s_dat
, ':', hlp
->s_len
)) = '\0';
586 i
= PTR2SIZE(cp
- hlp
->s_dat
);
587 if((doitp
!= NULL
&& n_ignore_is_ign(doitp
, hlp
->s_dat
, i
)) ||
588 !asccasecmp(hlp
->s_dat
, "status") ||
589 !asccasecmp(hlp
->s_dat
, "x-status") ||
590 (action
== SEND_MBOX
&&
591 (!asccasecmp(hlp
->s_dat
, "content-length") ||
592 !asccasecmp(hlp
->s_dat
, "lines")) &&
593 !ok_blook(keep_content_length
)))
599 if(n_COLOUR_IS_ACTIVE())
600 n_colour_put(n_COLOUR_ID_VIEW_HEADER
, hlp
->s_dat
);
603 _out(hlp
->s_dat
, hlp
->s_len
, obuf
, convert
, action
, qf
, stats
, NULL
,NULL
);
605 if(n_COLOUR_IS_ACTIVE())
609 _out("\n", sizeof("\n") -1, obuf
, convert
, action
, qf
, stats
,
611 /*see below hany = TRU1;*/
614 hlp
= n_string_trunc(hlp
, 0);
620 /* We've reached end of headers, so eventually force out status: field and
621 * note that we are no longer in header fields */
623 statusput(zmp
, obuf
, qf
, stats
);
624 /*see below hany = TRU1;*/
627 xstatusput(zmp
, obuf
, qf
, stats
);
628 /*see below hany = TRU1;*/
630 if(/* TODO PART_INFO hany && */ doitp
!= n_IGNORE_ALL
)
631 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
, NULL
,NULL
);
642 memset(&mh
, 0, sizeof mh
);
644 switch (ip
->m_mimecontent
) {
647 case SEND_TODISP_PARTS
:
650 case SEND_TODISP_ALL
:
653 if (ok_blook(rfc822_body_from_
)) {
654 if (!qf
->qf_bypass
) {
655 size_t i
= fwrite(qf
->qf_pfix
, sizeof *qf
->qf_pfix
,
656 qf
->qf_pfix_len
, obuf
);
657 if (i
== qf
->qf_pfix_len
&& stats
!= NULL
)
660 put_from_(obuf
, ip
->m_multipart
, stats
);
668 put_from_(obuf
, ip
->m_multipart
, stats
);
678 case MIME_TEXT_PLAIN
:
681 case SEND_TODISP_ALL
:
682 case SEND_TODISP_PARTS
:
685 switch (n_mimetype_handler(&mh
, ip
, action
)) {
687 if(action
!= SEND_TODISP_PARTS
)
690 case MIME_HDL_MSG
:/* TODO these should be part of partinfo! */
692 _out(mh
.mh_msg
.s
, mh
.mh_msg
.l
, obuf
, CONV_NONE
, SEND_MBOX
,
693 qf
, stats
, NULL
, NULL
);
694 /* We would print this as plain text, so better force going home */
697 if(action
== SEND_TODISP_PARTS
&&
698 (mh
.mh_flags
& MIME_HDL_COPIOUSOUTPUT
))
703 if(action
== SEND_TODISP_PARTS
)
715 if (action
!= SEND_DECRYPT
)
719 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&&
720 action
!= SEND_SHOW
&& ip
->m_multipart
!= NULL
)
726 case SEND_TODISP_ALL
:
727 case SEND_TODISP_PARTS
:
730 switch (n_mimetype_handler(&mh
, ip
, action
)) {
733 if (action
!= SEND_TODISP
&& action
!= SEND_TODISP_ALL
&&
737 case MIME_HDL_MSG
:/* TODO these should be part of partinfo! */
739 _out(mh
.mh_msg
.s
, mh
.mh_msg
.l
, obuf
, CONV_NONE
, SEND_MBOX
,
740 qf
, stats
, NULL
, NULL
);
741 /* We would print this as plain text, so better force going home */
744 if(action
== SEND_TODISP_PARTS
){
745 if(mh
.mh_flags
& MIME_HDL_COPIOUSOUTPUT
)
748 _print_part_info(obuf
, ip
, doitp
, level
, qf
, stats
);
749 if(!getapproval(_("Run MIME handler for this part?"), FAL0
))
756 if(action
== SEND_TODISP_PARTS
)
771 case MIME_ALTERNATIVE
:
772 if ((action
== SEND_TODISP
|| action
== SEND_QUOTE
) &&
773 !ok_blook(print_alternatives
)) {
774 /* XXX This (a) should not remain (b) should be own fun
775 * TODO (despite the fact that v15 will do this completely differently
776 * TODO by having an action-specific "manager" that will traverse the
777 * TODO parsed MIME tree and decide for each part whether it'll be
778 * TODO displayed or not *before* we walk the tree for doing action */
780 struct mpstack
*outer
;
782 } outermost
, * volatile curr
, * volatile mpsp
;
783 bool_t
volatile neednl
, hadpart
;
784 struct n_sigman smalter
;
786 (curr
= &outermost
)->outer
= NULL
;
788 neednl
= hadpart
= FAL0
;
790 n_SIGMAN_ENTER_SWITCH(&smalter
, n_SIGMAN_ALL
) {
798 for (np
= ip
->m_multipart
;;) {
800 for (; np
!= NULL
; np
= np
->m_nextpart
) {
801 if (action
!= SEND_QUOTE
&& np
->m_ct_type_plain
!= NULL
) {
803 _out("\n", 1, obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
805 _print_part_info(obuf
, np
, doitp
, level
, qf
, stats
);
809 switch (np
->m_mimecontent
) {
810 case MIME_ALTERNATIVE
:
816 mpsp
= n_autorec_alloc(sizeof *mpsp
);
818 mpsp
->mp
= np
->m_multipart
;
827 switch (n_mimetype_handler(&mh
, np
, action
)) {
829 mh
.mh_flags
= MIME_HDL_NULL
;
830 continue; /* break; break; */
832 if(!(mh
.mh_flags
& MIME_HDL_COPIOUSOUTPUT
)){
833 mh
.mh_flags
= MIME_HDL_NULL
;
834 continue; /* break; break; */
838 if (!ok_blook(mime_alternative_favour_rich
)) {/* TODO */
839 struct mimepart
*x
= np
;
841 while ((x
= x
->m_nextpart
) != NULL
) {
842 struct mime_handler mhx
;
844 if (x
->m_mimecontent
== MIME_TEXT_PLAIN
||
845 n_mimetype_handler(&mhx
, x
, action
) ==
850 continue; /* break; break; */
858 case MIME_TEXT_PLAIN
:
861 if (ok_blook(mime_alternative_favour_rich
)) { /* TODO */
862 struct mimepart
*x
= np
;
864 /* TODO twice TODO, we should dive into /related and
865 * TODO check whether that has rich parts! */
866 while ((x
= x
->m_nextpart
) != NULL
) {
867 struct mime_handler mhx
;
869 switch (n_mimetype_handler(&mhx
, x
, action
)) {
871 if(!(mhx
.mh_flags
& MIME_HDL_COPIOUSOUTPUT
))
882 continue; /* break; break; */
886 if (action
== SEND_QUOTE
&& hadpart
)
887 /* XXX (void)*/a_send_out_nl(obuf
, stats
);
890 rv
= sendpart(zmp
, np
, obuf
, doitp
, qf
, action
,
891 linedat
, linesize
, stats
, level
+ 1);
892 quoteflt_reset(qf
, origobuf
);
895 curr
= &outermost
; /* Cause overall loop termination */
904 np
= curr
->mp
->m_nextpart
;
907 n_sigman_leave(&smalter
, n_SIGMAN_VIPSIGS_NTTYOUT
);
918 case SEND_TODISP_ALL
:
919 case SEND_TODISP_PARTS
:
927 if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
) &&
928 ip
->m_multipart
!= NULL
&&
929 ip
->m_multipart
->m_mimecontent
== MIME_DISCARD
&&
930 ip
->m_multipart
->m_nextpart
== NULL
) {
931 char const *x
= _("[Missing multipart boundary - use `show' "
932 "to display the raw message]\n");
933 _out(x
, strlen(x
), obuf
, CONV_NONE
, SEND_MBOX
, qf
, stats
,
937 for (np
= ip
->m_multipart
; np
!= NULL
; np
= np
->m_nextpart
) {
938 bool_t
volatile ispipe
;
940 if (np
->m_mimecontent
== MIME_DISCARD
&& action
!= SEND_DECRYPT
)
946 if (np
->m_partstring
&&
947 np
->m_partstring
[0] == '1' && np
->m_partstring
[1] == '\0')
950 /* TODO Always open multipart on /dev/null, it's a hack to be
951 * TODO able to dive into that structure, and still better
952 * TODO than asking the user for something stupid.
953 * TODO oh, wait, we did ask for a filename for this MIME mail,
954 * TODO and that outer container is useless anyway ;-P */
955 if (np
->m_multipart
!= NULL
&& np
->m_mimecontent
!= MIME_822
) {
956 if ((obuf
= Fopen(n_path_devnull
, "w")) == NULL
)
958 } else if ((obuf
= newfile(np
, &ispipe
)) == NULL
)
962 if (sigsetjmp(_send_pipejmp
, 1)) {
966 oldpipe
= safe_signal(SIGPIPE
, &_send_onpipe
);
969 case SEND_TODISP_ALL
:
970 if (ip
->m_mimecontent
!= MIME_ALTERNATIVE
&&
971 ip
->m_mimecontent
!= MIME_RELATED
&&
972 ip
->m_mimecontent
!= MIME_DIGEST
&&
973 ip
->m_mimecontent
!= MIME_SIGNED
&&
974 ip
->m_mimecontent
!= MIME_ENCRYPTED
&&
975 ip
->m_mimecontent
!= MIME_MULTI
)
977 _print_part_info(obuf
, np
, doitp
, level
, qf
, stats
);
979 case SEND_TODISP_PARTS
:
992 if ((action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
) &&
993 np
->m_multipart
== NULL
&& ip
->m_parent
!= NULL
)
994 /*XXX (void)*/a_send_out_nl(obuf
, stats
);
995 if (sendpart(zmp
, np
, obuf
, doitp
, qf
, action
, linedat
, linesize
,
998 quoteflt_reset(qf
, origobuf
);
1000 if (action
== SEND_QUOTE
) {
1001 if (ip
->m_mimecontent
!= MIME_RELATED
)
1004 if (action
== SEND_TOFILE
&& obuf
!= origobuf
) {
1009 safe_signal(SIGPIPE
, SIG_IGN
);
1011 safe_signal(SIGPIPE
, oldpipe
);
1024 /* Copy out message body */
1025 if (doitp
== n_IGNORE_ALL
&& level
== 0) /* skip final blank line */
1027 switch (ip
->m_mime_enc
) {
1031 convert
= CONV_NONE
;
1034 convert
= CONV_FROMQP
;
1037 switch (ip
->m_mimecontent
) {
1039 case MIME_TEXT_PLAIN
:
1040 case MIME_TEXT_HTML
:
1041 convert
= CONV_FROMB64_T
;
1044 switch (mh
.mh_flags
& MIME_HDL_TYPE_MASK
) {
1047 convert
= CONV_FROMB64_T
;
1050 convert
= CONV_FROMB64
;
1057 convert
= CONV_NONE
;
1060 /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
1061 * TODO cannot mess things up misusing outrest as line buffer */
1063 if (iconvd
!= (iconv_t
)-1) {
1064 n_iconv_close(iconvd
);
1065 iconvd
= (iconv_t
)-1;
1069 if (action
== SEND_DECRYPT
|| action
== SEND_MBOX
||
1070 action
== SEND_RFC822
|| action
== SEND_SHOW
)
1071 convert
= CONV_NONE
;
1073 else if ((action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
1074 action
== SEND_TODISP_PARTS
||
1075 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
||
1076 action
== SEND_TOSRCH
|| action
== SEND_TOFILE
) &&
1077 (ip
->m_mimecontent
== MIME_TEXT_PLAIN
||
1078 ip
->m_mimecontent
== MIME_TEXT_HTML
||
1079 ip
->m_mimecontent
== MIME_TEXT
||
1080 (mh
.mh_flags
& MIME_HDL_TYPE_MASK
) == MIME_HDL_TEXT
||
1081 (mh
.mh_flags
& MIME_HDL_TYPE_MASK
) == MIME_HDL_PTF
)) {
1084 tcs
= ok_vlook(ttycharset
);
1085 if (asccasecmp(tcs
, ip
->m_charset
) &&
1086 asccasecmp(ok_vlook(charset_7bit
), ip
->m_charset
)) {
1087 iconvd
= n_iconv_open(tcs
, ip
->m_charset
);
1088 if (iconvd
== (iconv_t
)-1 && n_err_no
== n_ERR_INVAL
) {
1089 n_err(_("Cannot convert from %s to %s\n"), ip
->m_charset
, tcs
);
1090 /*rv = 1; goto jleave;*/
1096 switch (mh
.mh_flags
& MIME_HDL_TYPE_MASK
) {
1098 if(!(mh
.mh_flags
& MIME_HDL_COPIOUSOUTPUT
) &&
1099 action
!= SEND_TODISP_PARTS
)
1106 term_infd
= n_CHILD_FD_PASS
;
1107 if (mh
.mh_flags
& (MIME_HDL_TMPF
| MIME_HDL_NEEDSTERM
)) {
1110 of
= OF_RDWR
| OF_REGISTER
;
1111 if (!(mh
.mh_flags
& MIME_HDL_TMPF
)) {
1113 mh
.mh_flags
|= MIME_HDL_TMPF_FILL
;
1115 } else if (mh
.mh_flags
& MIME_HDL_TMPF_UNLINK
)
1116 of
|= OF_REGISTER_UNLINK
;
1118 if ((pbuf
= Ftmp((mh
.mh_flags
& MIME_HDL_TMPF
? &cp
: NULL
),
1119 (mh
.mh_flags
& MIME_HDL_TMPF_FILL
? "mimehdlfill" : "mimehdl"),
1123 if (mh
.mh_flags
& MIME_HDL_TMPF
) {
1124 tmpname
= savestr(cp
);
1128 if (mh
.mh_flags
& MIME_HDL_TMPF_FILL
) {
1130 term_infd
= fileno(pbuf
);
1136 pbuf
= _pipefile(&mh
, ip
, n_UNVOLATILE(&qbuf
), tmpname
, term_infd
);
1142 } else if ((mh
.mh_flags
& MIME_HDL_NEEDSTERM
) && pbuf
== (FILE*)-1) {
1147 action
= SEND_TOPIPE
;
1149 oldpipe
= safe_signal(SIGPIPE
, &_send_onpipe
);
1150 if (sigsetjmp(_send_pipejmp
, 1))
1157 mh
.mh_flags
= MIME_HDL_NULL
;
1164 bool_t
volatile eof
;
1165 bool_t save_qf_bypass
= qf
->qf_bypass
;
1166 ui64_t
*save_stats
= stats
;
1168 if (pbuf
!= origobuf
) {
1169 qf
->qf_bypass
= TRU1
;/* XXX legacy (remove filter instead) */
1173 outrest
.s
= inrest
.s
= NULL
;
1174 outrest
.l
= inrest
.l
= 0;
1178 __sendp_opipe
= safe_signal(SIGPIPE
, &__sendp_onsig
);
1179 if (sigsetjmp(__sendp_actjmp
, 1)) {
1180 n_pstate
&= ~n_PS_BASE64_STRIP_CR
;/* (but protected by outer sigman) */
1181 if (outrest
.s
!= NULL
)
1183 if (inrest
.s
!= NULL
)
1186 if (iconvd
!= (iconv_t
)-1)
1187 n_iconv_close(iconvd
);
1189 safe_signal(SIGPIPE
, __sendp_opipe
);
1190 n_raise(__sendp_sig
);
1194 quoteflt_reset(qf
, pbuf
);
1195 if((dostat
& 4) && pbuf
== origobuf
) /* TODO */
1196 n_pstate
|= n_PS_BASE64_STRIP_CR
;
1197 while (!eof
&& fgetline(linedat
, linesize
, &cnt
, &linelen
, ibuf
, 0)) {
1199 if (_out(*linedat
, linelen
, pbuf
, convert
, action
, qf
, stats
, &outrest
,
1200 (action
& _TD_EOF
? NULL
: &inrest
)) < 0 || ferror(pbuf
)) {
1201 rv
= -1; /* XXX Should bail away?! */
1205 if(eof
<= FAL0
&& rv
>= 0 && (outrest
.l
!= 0 || inrest
.l
!= 0)){
1207 if(eof
|| inrest
.l
== 0)
1209 eof
= eof
? TRU1
: TRUM1
;
1212 n_pstate
&= ~n_PS_BASE64_STRIP_CR
;
1215 /* TODO HACK: when sending to the display we yet get fooled if a message
1216 * TODO doesn't end in a newline, because of our input/output 1:1.
1217 * TODO This should be handled automatically by a display filter, then */
1218 if(rv
>= 0 && !qf
->qf_nl_last
&&
1219 (action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
1220 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
))
1221 rv
= quoteflt_push(qf
, "\n", 1);
1225 if (rv
>= 0 && (mh
.mh_flags
& MIME_HDL_TMPF_FILL
)) {
1226 mh
.mh_flags
&= ~MIME_HDL_TMPF_FILL
;
1228 really_rewind(pbuf
);
1229 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1230 goto jpipe_for_real
;
1234 safe_signal(SIGPIPE
, __sendp_opipe
);
1236 if (outrest
.s
!= NULL
)
1238 if (inrest
.s
!= NULL
)
1241 if (pbuf
!= origobuf
) {
1242 qf
->qf_bypass
= save_qf_bypass
;
1249 safe_signal(SIGPIPE
, SIG_IGN
);
1250 Pclose(pbuf
, !(mh
.mh_flags
& MIME_HDL_ASYNC
));
1251 safe_signal(SIGPIPE
, oldpipe
);
1252 if (rv
>= 0 && qbuf
!= NULL
&& qbuf
!= obuf
)
1253 pipecpy(qbuf
, obuf
, origobuf
, qf
, stats
);
1256 if (iconvd
!= (iconv_t
)-1)
1257 n_iconv_close(iconvd
);
1265 newfile(struct mimepart
*ip
, bool_t
volatile *ispipe
)
1275 if (f
!= NULL
&& f
!= (char*)-1) {
1278 makeprint(&in
, &out
);
1279 out
.l
= delctrl(out
.s
, out
.l
);
1280 f
= savestrbuf(out
.s
, out
.l
);
1284 /* In interactive mode, let user perform all kind of expansions as desired,
1285 * and offer |SHELL-SPEC pipe targets, too */
1286 if (n_psonce
& n_PSO_INTERACTIVE
) {
1288 struct n_string shou
, *shoup
;
1291 shoup
= n_string_creat_auto(&shou
);
1293 /* TODO Generic function which asks for filename.
1294 * TODO If the current part is the first textpart the target
1295 * TODO is implicit from outer `write' etc! */
1296 /* I18N: Filename input prompt with file type indication */
1297 str_concat_csvl(&prompt
, _("Enter filename for part "),
1298 (ip
->m_partstring
!= NULL
? ip
->m_partstring
: n_qm
),
1299 " (", ip
->m_ct_type_plain
, "): ", NULL
);
1301 f2
= n_go_input_cp(n_GO_INPUT_CTX_DEFAULT
| n_GO_INPUT_HIST_ADD
,
1302 prompt
.s
, ((f
!= (char*)-1 && f
!= NULL
)
1303 ? n_shexp_quote_cp(f
, FAL0
) : NULL
));
1305 in
.s
= n_UNCONST(f2
);
1307 if((n_shexp_parse_token((n_SHEXP_PARSE_TRUNC
|
1308 n_SHEXP_PARSE_TRIM_SPACE
| n_SHEXP_PARSE_TRIM_IFSSPACE
|
1309 n_SHEXP_PARSE_LOG
| n_SHEXP_PARSE_IGNORE_EMPTY
),
1311 ) & (n_SHEXP_STATE_STOP
|
1312 n_SHEXP_STATE_OUTPUT
| n_SHEXP_STATE_ERR_MASK
)
1313 ) != (n_SHEXP_STATE_STOP
| n_SHEXP_STATE_OUTPUT
))
1315 f2
= n_string_cp(shoup
);
1317 if (f2
== NULL
|| *f2
== '\0') {
1318 if (n_poption
& n_PO_D_V
)
1319 n_err(_("... skipping this\n"));
1320 n_string_gut(shoup
);
1326 /* Pipes are expanded by the shell */
1328 else if ((f3
= fexpand(f2
, FEXP_LOCAL
| FEXP_NVAR
)) == NULL
)
1329 /* (Error message written by fexpand()) */
1334 n_string_gut(shoup
);
1337 if (f
== NULL
|| f
== (char*)-1 || *f
== '\0')
1339 else if (n_psonce
& n_PSO_INTERACTIVE
) {
1341 fp
= Popen(&f
[1], "w", ok_vlook(SHELL
), NULL
, 1);
1342 if (!(*ispipe
= (fp
!= NULL
)))
1344 } else if ((fp
= Fopen(f
, "w")) == NULL
)
1345 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f
, FAL0
));
1347 /* Be very picky in non-interactive mode: actively disallow pipes,
1348 * prevent directory separators, and any filename member that would
1349 * become expanded by the shell if the name would be echo(1)ed */
1350 if(n_anyof_cp("/" n_SHEXP_MAGIC_PATH_CHARS
, f
)){
1353 for(out
.s
= n_autorec_alloc((strlen(f
) * 3) +1), out
.l
= 0;
1354 (c
= *f
++) != '\0';)
1355 if(strchr("/" n_SHEXP_MAGIC_PATH_CHARS
, c
)){
1356 out
.s
[out
.l
++] = '%';
1357 n_c_to_hex_base16(&out
.s
[out
.l
], c
);
1361 out
.s
[out
.l
] = '\0';
1365 /* Avoid overwriting of existing files */
1366 while((fp
= Fopen(f
, "wx")) == NULL
){
1369 if((e
= n_err_no
) != n_ERR_EXIST
){
1370 n_err(_("Cannot open %s: %s\n"),
1371 n_shexp_quote_cp(f
, FAL0
), n_err_to_doc(e
));
1375 if(ip
->m_partstring
!= NULL
)
1376 f
= savecatsep(f
, '#', ip
->m_partstring
);
1378 f
= savecat(f
, "#.");
1387 pipecpy(FILE *pipebuf
, FILE *outbuf
, FILE *origobuf
, struct quoteflt
*qf
,
1390 char *line
= NULL
; /* TODO line pool */
1391 size_t linesize
= 0, linelen
, cnt
;
1397 cnt
= (size_t)fsize(pipebuf
);
1400 quoteflt_reset(qf
, outbuf
);
1401 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, pipebuf
, 0) != NULL
) {
1402 if ((sz
= quoteflt_push(qf
, line
, linelen
)) < 0)
1406 if ((sz
= quoteflt_flush(qf
)) > 0)
1411 if (all_sz
> 0 && outbuf
== origobuf
&& stats
!= NULL
)
1418 statusput(const struct message
*mp
, FILE *obuf
, struct quoteflt
*qf
,
1421 char statout
[3], *cp
= statout
;
1424 if (mp
->m_flag
& MREAD
)
1426 if (!(mp
->m_flag
& MNEW
))
1430 int i
= fprintf(obuf
, "%.*sStatus: %s\n", (int)qf
->qf_pfix_len
,
1431 (qf
->qf_bypass
? NULL
: qf
->qf_pfix
), statout
);
1432 if (i
> 0 && stats
!= NULL
)
1439 xstatusput(const struct message
*mp
, FILE *obuf
, struct quoteflt
*qf
,
1443 char *xp
= xstatout
;
1446 if (mp
->m_flag
& MFLAGGED
)
1448 if (mp
->m_flag
& MANSWERED
)
1450 if (mp
->m_flag
& MDRAFTED
)
1454 int i
= fprintf(obuf
, "%.*sX-Status: %s\n", (int)qf
->qf_pfix_len
,
1455 (qf
->qf_bypass
? NULL
: qf
->qf_pfix
), xstatout
);
1456 if (i
> 0 && stats
!= NULL
)
1463 put_from_(FILE *fp
, struct mimepart
*ip
, ui64_t
*stats
)
1465 char const *froma
, *date
, *nl
;
1469 if (ip
!= NULL
&& ip
->m_from
!= NULL
) {
1471 date
= n_time_ctime(ip
->m_time
, NULL
);
1474 froma
= ok_vlook(LOGNAME
);
1475 date
= time_current
.tc_ctime
;
1480 if(n_COLOUR_IS_ACTIVE())
1481 n_colour_put(n_COLOUR_ID_VIEW_FROM_
, NULL
);
1483 i
= fprintf(fp
, "From %s %s%s", froma
, date
, nl
);
1485 if(n_COLOUR_IS_ACTIVE())
1488 if (i
> 0 && stats
!= NULL
)
1494 sendmp(struct message
*mp
, FILE *obuf
, struct n_ignore
const *doitp
,
1495 char const *prefix
, enum sendaction action
, ui64_t
*stats
)
1497 struct n_sigman linedat_protect
;
1500 enum mime_parse_flags mpf
;
1501 struct mimepart
*ip
;
1502 size_t linesize
, cnt
, sz
, i
;
1507 time_current_update(&time_current
, TRU1
);
1511 quoteflt_init(&qf
, prefix
, (prefix
== NULL
));
1513 n_SIGMAN_ENTER_SWITCH(&linedat_protect
, n_SIGMAN_ALL
){
1520 if (mp
== dot
&& action
!= SEND_TOSRCH
)
1521 n_pstate
|= n_PS_DID_PRINT_DOT
;
1525 /* First line is the From_ line, so no headers there to worry about */
1526 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
1533 char const *cpre
= n_empty
, *csuf
= n_empty
;
1536 if(n_COLOUR_IS_ACTIVE()){
1537 struct n_colour_pen
*cpen
;
1538 struct str
const *sp
;
1540 cpen
= n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_
,NULL
);
1541 if((sp
= n_colour_pen_to_str(cpen
)) != NULL
){
1543 sp
= n_colour_reset_to_str();
1550 nozap
= (doitp
!= n_IGNORE_ALL
&& doitp
!= n_IGNORE_FWD
&&
1551 action
!= SEND_RFC822
&&
1552 !n_ignore_is_ign(doitp
, "from_", sizeof("from_") -1));
1553 if (mp
->m_flag
& MNOFROM
) {
1555 sz
= fprintf(obuf
, "%s%.*sFrom %s %s%s\n",
1556 cpre
, (int)qf
.qf_pfix_len
,
1557 (qf
.qf_bypass
? n_empty
: qf
.qf_pfix
), fakefrom(mp
),
1558 n_time_ctime(mp
->m_time
, NULL
), csuf
);
1560 if (!qf
.qf_bypass
) {
1561 i
= fwrite(qf
.qf_pfix
, sizeof *qf
.qf_pfix
, qf
.qf_pfix_len
, obuf
);
1562 if (i
!= qf
.qf_pfix_len
)
1569 cpre
= (char const*)0x1;
1573 while (cnt
> 0 && (c
= getc(ibuf
)) != EOF
) {
1575 if(c
== '\n' && *csuf
!= '\0'){
1576 cpre
= (char const*)0x1;
1588 if(*csuf
!= '\0' && cpre
!= (char const*)0x1 && *cpre
!= '\0')
1592 while (cnt
> 0 && (c
= getc(ibuf
)) != EOF
) {
1599 if (sz
> 0 && stats
!= NULL
)
1602 mpf
= MIME_PARSE_NONE
;
1603 if (action
!= SEND_MBOX
&& action
!= SEND_RFC822
&& action
!= SEND_SHOW
)
1604 mpf
|= MIME_PARSE_PARTS
| MIME_PARSE_DECRYPT
;
1605 if(action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
1606 action
== SEND_QUOTE
|| action
== SEND_QUOTE_ALL
)
1607 mpf
|= MIME_PARSE_FOR_USER_CONTEXT
;
1608 if ((ip
= mime_parse_msg(mp
, mpf
)) == NULL
)
1611 rv
= sendpart(mp
, ip
, obuf
, doitp
, &qf
, action
, &linedat
, &linesize
,
1614 n_sigman_cleanup_ping(&linedat_protect
);
1616 n_pstate
&= ~n_PS_BASE64_STRIP_CR
;
1617 quoteflt_destroy(&qf
);
1621 n_sigman_leave(&linedat_protect
, n_SIGMAN_VIPSIGS_NTTYOUT
);