Better hook support: *mailx-{raw,orig}-* a.k.a. Mailx-{Raw,Orig}-From:..
[s-mailx.git] / send.c
blobf519ecc194301b11873b69f90ce4a52d6afd459d
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 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE send
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 static sigjmp_buf _send_pipejmp;
44 /* Going for user display, print Part: info string */
45 static void _print_part_info(FILE *obuf, struct mimepart const *mpp,
46 struct n_ignore const *doitp, int level,
47 struct quoteflt *qf, ui64_t *stats);
49 /* Create a pipe; if mpp is not NULL, place some n_PIPEENV_* environment
50 * variables accordingly */
51 static FILE * _pipefile(struct mime_handler *mhp,
52 struct mimepart const *mpp, FILE **qbuf,
53 char const *tmpname, int term_infd);
55 /* Call mime_write() as approbiate and adjust statistics */
56 SINLINE ssize_t _out(char const *buf, size_t len, FILE *fp,
57 enum conversion convert, enum sendaction action,
58 struct quoteflt *qf, ui64_t *stats, struct str *outrest,
59 struct str *inrest);
61 /* SIGPIPE handler */
62 static void _send_onpipe(int signo);
64 /* Send one part */
65 static int sendpart(struct message *zmp, struct mimepart *ip,
66 FILE *obuf, struct n_ignore const *doitp,
67 struct quoteflt *qf, enum sendaction action,
68 char **linedat, size_t *linesize,
69 ui64_t *stats, int level);
71 /* Get a file for an attachment */
72 static FILE * newfile(struct mimepart *ip, bool_t volatile *ispipe);
74 static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
75 struct quoteflt *qf, ui64_t *stats);
77 /* Output a reasonable looking status field */
78 static void statusput(const struct message *mp, FILE *obuf,
79 struct quoteflt *qf, ui64_t *stats);
80 static void xstatusput(const struct message *mp, FILE *obuf,
81 struct quoteflt *qf, ui64_t *stats);
83 static void put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats);
85 static void
86 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
87 struct n_ignore const *doitp, int level, struct quoteflt *qf, ui64_t *stats)
89 char buf[64];
90 struct str ti, to;
91 bool_t want_ct, needsep;
92 struct str const *cpre, *csuf;
93 char const *cp;
94 NYD2_ENTER;
96 cpre = csuf = NULL;
97 #ifdef HAVE_COLOUR
98 if(n_COLOUR_IS_ACTIVE()){
99 struct n_colour_pen *cpen;
101 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_PARTINFO, NULL);
102 if((cpre = n_colour_pen_to_str(cpen)) != NULL)
103 csuf = n_colour_reset_to_str();
105 #endif
107 /* Take care of "99.99", i.e., 5 */
108 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
109 cp = n_qm;
110 if (level || (cp[0] != '1' && cp[1] == '\0') || (cp[0] == '1' && /* TODO */
111 cp[1] == '.' && cp[2] != '1')) /* TODO code should not look like so */
112 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
114 /* Part id, content-type, encoding, charset */
115 if (cpre != NULL)
116 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
117 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
118 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
120 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
121 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
122 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
124 needsep = FAL0;
126 if((cp = mpp->m_ct_type_usr_ovwr) != NULL){
127 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
128 want_ct = TRU1;
129 }else if((want_ct = n_ignore_is_ign(doitp,
130 "content-type", sizeof("content-type") -1)))
131 cp = mpp->m_ct_type_plain;
132 if (want_ct &&
133 (to.l = strlen(cp)) > 30 && is_asccaseprefix("application/", cp)) {
134 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
135 size_t i = to.l - fl;
136 char *x = salloc(al + i +1);
138 memcpy(x, "appl../", al);
139 memcpy(x + al, cp + fl, i +1);
140 cp = x;
141 to.l = al + i;
143 if(cp != NULL){
144 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
145 needsep = TRU1;
148 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL) {
149 if(needsep)
150 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
151 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
152 cp = "qu.-pr.";
153 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
154 needsep = TRU1;
157 if (want_ct && mpp->m_multipart == NULL/* TODO */ &&
158 (cp = mpp->m_charset) != NULL) {
159 if(needsep)
160 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
161 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
164 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
165 if (csuf != NULL)
166 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
167 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
169 /* */
170 if (mpp->m_content_info & CI_MIME_ERRORS) {
171 if (cpre != NULL)
172 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
173 NULL, NULL);
174 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
176 ti.l = strlen(ti.s = n_UNCONST(_("Defective MIME structure")));
177 makeprint(&ti, &to);
178 to.l = delctrl(to.s, to.l);
179 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
180 free(to.s);
182 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
183 if (csuf != NULL)
184 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
185 NULL, NULL);
186 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
189 /* Content-Description */
190 if (n_ignore_is_ign(doitp, "content-description", 19) &&
191 (cp = mpp->m_content_description) != NULL && *cp != '\0') {
192 if (cpre != NULL)
193 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
194 NULL, NULL);
195 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
197 ti.l = strlen(ti.s = n_UNCONST(mpp->m_content_description));
198 mime_fromhdr(&ti, &to, TD_ISPR | TD_ICONV);
199 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
200 free(to.s);
202 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
203 if (csuf != NULL)
204 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
205 NULL, NULL);
206 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
209 /* Filename */
210 if (n_ignore_is_ign(doitp, "content-disposition", 19) &&
211 mpp->m_filename != NULL && *mpp->m_filename != '\0') {
212 if (cpre != NULL)
213 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
214 NULL, NULL);
215 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
217 ti.l = strlen(ti.s = mpp->m_filename);
218 makeprint(&ti, &to);
219 to.l = delctrl(to.s, to.l);
220 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
221 free(to.s);
223 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
224 if (csuf != NULL)
225 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
226 NULL, NULL);
227 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
229 NYD2_LEAVE;
232 static FILE *
233 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
234 char const *tmpname, int term_infd)
236 struct str s;
237 char const *env_addon[8 +8/*v15*/], *cp, *sh;
238 FILE *rbuf;
239 NYD_ENTER;
241 rbuf = *qbuf;
243 if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
244 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
245 NULL) {
246 n_perr(_("tmpfile"), 0);
247 *qbuf = rbuf;
251 if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
252 union {int (*ptf)(void); char const *sh;} u;
254 fflush(*qbuf);
255 if (*qbuf != n_stdout) /* xxx never? v15: it'll be a filter anyway */
256 fflush(n_stdout);
258 u.ptf = mhp->mh_ptf;
259 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
260 goto jerror;
261 goto jleave;
264 /* MAILX_FILENAME */
265 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
266 cp = n_empty;
267 env_addon[0] = str_concat_csvl(&s, n_PIPEENV_FILENAME, "=", cp, NULL)->s;
268 env_addon[1] = str_concat_csvl(&s, "NAIL_FILENAME", "=", cp, NULL)->s;/* v15 */
270 /* MAILX_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
271 * TODO a file wherever he wants! *Do* create a zero-size temporary file
272 * TODO and give *that* path as MAILX_FILENAME_TEMPORARY, clean it up once
273 * TODO the pipe returns? Like this we *can* verify path/name issues! */
274 cp = n_random_create_cp(n_MIN(NAME_MAX / 4, 16), NULL);
275 env_addon[2] = str_concat_csvl(&s, n_PIPEENV_FILENAME_GENERATED, "=", cp,
276 NULL)->s;
277 env_addon[3] = str_concat_csvl(&s, "NAIL_FILENAME_GENERATED", "=", cp,/* v15 */
278 NULL)->s;
280 /* MAILX_CONTENT{,_EVIDENCE} */
281 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
282 cp = n_empty;
283 env_addon[4] = str_concat_csvl(&s, n_PIPEENV_CONTENT, "=", cp, NULL)->s;
284 env_addon[5] = str_concat_csvl(&s, "NAIL_CONTENT", "=", cp, NULL)->s;/* v15 */
286 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
287 cp = mpp->m_ct_type_usr_ovwr;
288 env_addon[6] = str_concat_csvl(&s, n_PIPEENV_CONTENT_EVIDENCE, "=", cp,
289 NULL)->s;
290 env_addon[7] = str_concat_csvl(&s, "NAIL_CONTENT_EVIDENCE", "=", cp,/* v15 */
291 NULL)->s;
293 env_addon[8] = NULL;
295 /* MAILX_FILENAME_TEMPORARY? */
296 if (tmpname != NULL) {
297 env_addon[8] = str_concat_csvl(&s,
298 n_PIPEENV_FILENAME_TEMPORARY, "=", tmpname, NULL)->s;
299 env_addon[9] = str_concat_csvl(&s,
300 "NAIL_FILENAME_TEMPORARY", "=", tmpname, NULL)->s;/* v15 */
301 env_addon[10] = NULL;
304 sh = ok_vlook(SHELL);
306 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
307 sigset_t nset;
308 int pid;
310 sigemptyset(&nset);
311 pid = n_child_run(sh, &nset, term_infd, n_CHILD_FD_PASS, "-c",
312 mhp->mh_shell_cmd, NULL, env_addon, NULL);
313 rbuf = (pid < 0) ? NULL : (FILE*)-1;
314 } else {
315 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
316 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
317 jerror:
318 if (rbuf == NULL)
319 n_err(_("Cannot run MIME type handler: %s: %s\n"),
320 mhp->mh_msg, n_err_to_doc(n_err_no));
321 else {
322 fflush(*qbuf);
323 if (*qbuf != n_stdout)
324 fflush(n_stdout);
327 jleave:
328 NYD_LEAVE;
329 return rbuf;
332 SINLINE ssize_t
333 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
334 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *outrest,
335 struct str *inrest)
337 ssize_t sz = 0, n;
338 int flags;
339 NYD_ENTER;
341 /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
342 * TODO subclass should detect such From_ cases and either reencode the part
343 * TODO in question, or perform From_ quoting as necessary!?!?!? How?!? */
344 /* C99 */{
345 bool_t from_;
347 if((action == SEND_MBOX || action == SEND_DECRYPT) &&
348 (from_ = is_head(buf, len, TRU1))){
349 if(from_ != TRUM1 || ok_blook(mbox_rfc4155)){
350 putc('>', fp);
351 ++sz;
356 flags = ((int)action & _TD_EOF);
357 action &= ~_TD_EOF;
358 n = mime_write(buf, len, fp,
359 action == SEND_MBOX ? CONV_NONE : convert,
360 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
361 action == SEND_TODISP_PARTS ||
362 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
363 ? TD_ISPR | TD_ICONV
364 : (action == SEND_TOSRCH || action == SEND_TOPIPE ||
365 action == SEND_TOFILE)
366 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
367 qf, outrest, inrest);
368 if (n < 0)
369 sz = n;
370 else if (n > 0) {
371 sz += n;
372 if (stats != NULL)
373 *stats += sz;
375 NYD_LEAVE;
376 return sz;
379 static void
380 _send_onpipe(int signo)
382 NYD_X; /* Signal handler */
383 n_UNUSED(signo);
384 siglongjmp(_send_pipejmp, 1);
387 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
388 static int __sendp_sig; /* TODO someday.. */
389 static sighandler_type __sendp_opipe;
390 static void
391 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
393 NYD_X; /* Signal handler */
394 __sendp_sig = sig;
395 siglongjmp(__sendp_actjmp, 1);
398 static int
399 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
400 struct n_ignore const *doitp, struct quoteflt *qf,
401 enum sendaction volatile action,
402 char **linedat, size_t *linesize, ui64_t * volatile stats, int level)
404 int volatile rv = 0;
405 struct mime_handler mh;
406 struct str outrest, inrest;
407 char *cp;
408 char const * volatile tmpname = NULL;
409 size_t linelen, cnt;
410 int volatile term_infd;
411 int dostat, c;
412 struct mimepart *volatile np;
413 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
414 * volatile qbuf = obuf, *origobuf = obuf;
415 enum conversion volatile convert;
416 sighandler_type volatile oldpipe = SIG_DFL;
417 NYD_ENTER;
419 n_UNINIT(term_infd, 0);
420 n_UNINIT(cnt, 0);
422 quoteflt_reset(qf, obuf);
424 #if 0 /* TODO PART_INFO should be displayed here!! search PART_INFO */
425 if(ip->m_mimecontent != MIME_DISCARD && level > 0)
426 _print_part_info(obuf, ip, doitp, level, qf, stats);
427 #endif
429 if (ip->m_mimecontent == MIME_PKCS7) {
430 if (ip->m_multipart &&
431 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
432 goto jheaders_skip;
435 dostat = 0;
436 if (level == 0 && action != SEND_TODISP_PARTS) {
437 if (doitp != NULL) {
438 if (!n_ignore_is_ign(doitp, "status", 6))
439 dostat |= 1;
440 if (!n_ignore_is_ign(doitp, "x-status", 8))
441 dostat |= 2;
442 } else
443 dostat = 3;
445 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
446 rv = -1;
447 goto jleave;
449 cnt = ip->m_size;
451 if (ip->m_mimecontent == MIME_DISCARD)
452 goto jheaders_skip;
454 if (!(ip->m_flag & MNOFROM))
455 while (cnt && (c = getc(ibuf)) != EOF) {
456 cnt--;
457 if (c == '\n')
458 break;
460 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
461 action == SEND_TODISP_PARTS ||
462 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
463 action == SEND_TOSRCH)
464 ? CONV_FROMHDR : CONV_NONE;
466 /* Work the headers */
467 /* C99 */{
468 struct n_string hl, *hlp;
469 size_t lineno = 0;
470 bool_t hstop, hany;
472 hlp = n_string_creat_auto(&hl); /* TODO pool [or, v15: filter!] */
473 /* Reserve three lines, still not enough for references and DKIM etc. */
474 hlp = n_string_reserve(hlp, n_MAX(MIME_LINELEN, MIME_LINELEN_RFC2047) * 3);
476 for(hstop = hany = FAL0; !hstop;){
477 size_t lcnt;
479 lcnt = cnt;
480 if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0) == NULL)
481 break;
482 ++lineno;
483 if (linelen == 0 || (cp = *linedat)[0] == '\n')
484 /* If line is blank, we've reached end of headers */
485 break;
486 if(cp[linelen - 1] == '\n'){
487 cp[--linelen] = '\0';
488 if(linelen == 0)
489 break;
492 /* Are we in a header? */
493 if(hlp->s_len > 0){
494 if(!blankchar(*cp)){
495 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
496 cnt = lcnt;
497 goto jhdrput;
499 goto jhdrpush;
500 }else{
501 /* Pick up the header field if we have one */
502 while((c = *cp) != ':' && !spacechar(c) && c != '\0')
503 ++cp;
504 for(;;){
505 if(!spacechar(c) || c == '\0')
506 break;
507 c = *++cp;
509 if(c != ':'){
510 /* That won't work with MIME when saving etc., before v15 */
511 if (lineno != 1)
512 /* XXX This disturbs, and may happen multiple times, and we
513 * XXX cannot heal it for multipart except for display <v15 */
514 n_err(_("Malformed message: headers and body not separated "
515 "(with empty line)\n"));
516 if(level != 0)
517 dostat &= ~(1 | 2);
518 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
519 cnt = lcnt;
520 break;
523 cp = *linedat;
524 jhdrpush:
525 if(convert == CONV_NONE){
526 hlp = n_string_push_buf(hlp, cp, (ui32_t)linelen);
527 hlp = n_string_push_c(hlp, '\n');
528 }else{
529 bool_t lblank, isblank;
531 for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
532 char c8;
534 c8 = *cp;
535 if(!(isblank = blankchar(c8)) || !lblank){
536 if((lblank = isblank))
537 c8 = ' ';
538 hlp = n_string_push_c(hlp, c8);
542 continue;
545 jhdrput:
546 /* If it is an ignored header, skip it */
547 *(cp = memchr(hlp->s_dat, ':', hlp->s_len)) = '\0';
548 /* C99 */{
549 size_t i;
551 i = PTR2SIZE(cp - hlp->s_dat);
552 if((doitp != NULL && n_ignore_is_ign(doitp, hlp->s_dat, i)) ||
553 !asccasecmp(hlp->s_dat, "status") ||
554 !asccasecmp(hlp->s_dat, "x-status") ||
555 (action == SEND_MBOX &&
556 (!asccasecmp(hlp->s_dat, "content-length") ||
557 !asccasecmp(hlp->s_dat, "lines")) &&
558 !ok_blook(keep_content_length)))
559 goto jhdrtrunc;
562 /* Dump it */
563 n_COLOUR(
564 if(n_COLOUR_IS_ACTIVE())
565 n_colour_put(n_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
567 *cp = ':';
568 _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NULL,NULL);
569 n_COLOUR(
570 if(n_COLOUR_IS_ACTIVE())
571 n_colour_reset();
573 if(convert != CONV_NONE)
574 putc('\n', obuf);
575 hany = TRU1;
577 jhdrtrunc:
578 hlp = n_string_trunc(hlp, 0);
580 hstop = TRU1;
581 if(hlp->s_len > 0)
582 goto jhdrput;
584 /* We've reached end of headers, so eventually force out status: field and
585 * note that we are no longer in header fields */
586 if(dostat & 1){
587 statusput(zmp, obuf, qf, stats);
588 hany = TRU1;
590 if(dostat & 2){
591 xstatusput(zmp, obuf, qf, stats);
592 hany = TRU1;
594 if(/* TODO PART_INFO hany && */ doitp != n_IGNORE_ALL)
595 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
596 } /* C99 */
598 quoteflt_flush(qf);
600 if(ferror(obuf)){
601 rv = -1;
602 goto jleave;
605 jheaders_skip:
606 memset(&mh, 0, sizeof mh);
608 switch (ip->m_mimecontent) {
609 case MIME_822:
610 switch (action) {
611 case SEND_TODISP_PARTS:
612 goto jleave;
613 case SEND_TODISP:
614 case SEND_TODISP_ALL:
615 case SEND_QUOTE:
616 case SEND_QUOTE_ALL:
617 if (ok_blook(rfc822_body_from_)) {
618 if (qf->qf_pfix_len > 0) {
619 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
620 qf->qf_pfix_len, obuf);
621 if (i == qf->qf_pfix_len && stats != NULL)
622 *stats += i;
624 put_from_(obuf, ip->m_multipart, stats);
626 /* FALLTHRU */
627 case SEND_TOSRCH:
628 case SEND_DECRYPT:
629 goto jmulti;
630 case SEND_TOFILE:
631 case SEND_TOPIPE:
632 if (ok_blook(rfc822_body_from_))
633 put_from_(obuf, ip->m_multipart, stats);
634 /* FALLTHRU */
635 case SEND_MBOX:
636 case SEND_RFC822:
637 case SEND_SHOW:
638 break;
640 break;
641 case MIME_TEXT_HTML:
642 case MIME_TEXT:
643 case MIME_TEXT_PLAIN:
644 switch (action) {
645 case SEND_TODISP:
646 case SEND_TODISP_ALL:
647 case SEND_TODISP_PARTS:
648 case SEND_QUOTE:
649 case SEND_QUOTE_ALL:
650 switch (n_mimetype_handler(&mh, ip, action)) {
651 case MIME_HDL_NULL:
652 if(action != SEND_TODISP_PARTS)
653 break;
654 /* FALLTHRU */
655 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
656 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
657 stats, NULL, NULL);
658 /* We would print this as plain text, so better force going home */
659 goto jleave;
660 case MIME_HDL_CMD:
661 if(action == SEND_TODISP_PARTS &&
662 (mh.mh_flags & MIME_HDL_COPIOUSOUTPUT))
663 goto jleave;
664 break;
665 case MIME_HDL_TEXT:
666 case MIME_HDL_PTF:
667 if(action == SEND_TODISP_PARTS)
668 goto jleave;
669 break;
670 default:
671 break;
673 /* FALLTRHU */
674 default:
675 break;
677 break;
678 case MIME_DISCARD:
679 if (action != SEND_DECRYPT)
680 goto jleave;
681 break;
682 case MIME_PKCS7:
683 if (action != SEND_MBOX && action != SEND_RFC822 &&
684 action != SEND_SHOW && ip->m_multipart != NULL)
685 goto jmulti;
686 /* FALLTHRU */
687 default:
688 switch (action) {
689 case SEND_TODISP:
690 case SEND_TODISP_ALL:
691 case SEND_TODISP_PARTS:
692 case SEND_QUOTE:
693 case SEND_QUOTE_ALL:
694 switch (n_mimetype_handler(&mh, ip, action)) {
695 default:
696 case MIME_HDL_NULL:
697 if (action != SEND_TODISP_ALL && (level != 0 || cnt))
698 goto jleave;
699 /* FALLTHRU */
700 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
701 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
702 stats, NULL, NULL);
703 /* We would print this as plain text, so better force going home */
704 goto jleave;
705 case MIME_HDL_CMD:
706 if(action == SEND_TODISP_PARTS){
707 if(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)
708 goto jleave;
709 else{
710 _print_part_info(obuf, ip, doitp, level, qf, stats);
711 if(!getapproval(_("Run MIME handler for this part?"), FAL0))
712 goto jleave;
715 break;
716 case MIME_HDL_TEXT:
717 case MIME_HDL_PTF:
718 if(action == SEND_TODISP_PARTS)
719 goto jleave;
720 break;
722 break;
723 case SEND_TOFILE:
724 case SEND_TOPIPE:
725 case SEND_TOSRCH:
726 case SEND_DECRYPT:
727 case SEND_MBOX:
728 case SEND_RFC822:
729 case SEND_SHOW:
730 break;
732 break;
733 case MIME_ALTERNATIVE:
734 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
735 !ok_blook(print_alternatives)) {
736 /* XXX This (a) should not remain (b) should be own fun
737 * TODO (despite the fact that v15 will do this completely differently
738 * TODO by having an action-specific "manager" that will traverse the
739 * TODO parsed MIME tree and decide for each part whether it'll be
740 * TODO displayed or not *before* we walk the tree for doing action */
741 struct mpstack {
742 struct mpstack *outer;
743 struct mimepart *mp;
744 } outermost, * volatile curr, * volatile mpsp;
745 bool_t volatile neednl, hadpart;
746 struct n_sigman smalter;
748 (curr = &outermost)->outer = NULL;
749 curr->mp = ip;
750 neednl = hadpart = FAL0;
752 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
753 case 0:
754 break;
755 default:
756 rv = -1;
757 goto jalter_leave;
760 for (np = ip->m_multipart;;) {
761 jalter_redo:
762 for (; np != NULL; np = np->m_nextpart) {
763 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
764 if (neednl)
765 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats,
766 NULL, NULL);
767 _print_part_info(obuf, np, doitp, level, qf, stats);
769 neednl = TRU1;
771 switch (np->m_mimecontent) {
772 case MIME_ALTERNATIVE:
773 case MIME_RELATED:
774 case MIME_DIGEST:
775 case MIME_SIGNED:
776 case MIME_ENCRYPTED:
777 case MIME_MULTI:
778 mpsp = salloc(sizeof *mpsp);
779 mpsp->outer = curr;
780 mpsp->mp = np->m_multipart;
781 curr->mp = np;
782 curr = mpsp;
783 np = mpsp->mp;
784 neednl = FAL0;
785 goto jalter_redo;
786 default:
787 if (hadpart)
788 break;
789 switch (n_mimetype_handler(&mh, np, action)) {
790 default:
791 mh.mh_flags = MIME_HDL_NULL;
792 continue; /* break; break; */
793 case MIME_HDL_PTF:
794 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
795 struct mimepart *x = np;
797 while ((x = x->m_nextpart) != NULL) {
798 struct mime_handler mhx;
800 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
801 n_mimetype_handler(&mhx, x, action) ==
802 MIME_HDL_TEXT)
803 break;
805 if (x != NULL)
806 continue; /* break; break; */
807 goto jalter_plain;
809 /* FALLTHRU */
810 case MIME_HDL_TEXT:
811 break;
813 /* FALLTHRU */
814 case MIME_TEXT_PLAIN:
815 if (hadpart)
816 break;
817 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
818 struct mimepart *x = np;
820 /* TODO twice TODO, we should dive into /related and
821 * TODO check whether that has rich parts! */
822 while ((x = x->m_nextpart) != NULL) {
823 struct mime_handler mhx;
825 switch (n_mimetype_handler(&mhx, x, action)) {
826 case MIME_HDL_PTF:
827 break;
828 default:
829 continue;
831 break;
833 if (x != NULL)
834 continue; /* break; break; */
836 jalter_plain:
837 quoteflt_flush(qf);
838 if (action == SEND_QUOTE && hadpart) {
839 struct quoteflt *dummy = quoteflt_dummy();
840 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
841 NULL,NULL);
842 quoteflt_flush(dummy);
844 hadpart = TRU1;
845 neednl = FAL0;
846 rv = sendpart(zmp, np, obuf, doitp, qf, action,
847 linedat, linesize, stats, level + 1);
848 quoteflt_reset(qf, origobuf);
850 if (rv < 0)
851 curr = &outermost; /* Cause overall loop termination */
852 break;
856 mpsp = curr->outer;
857 if (mpsp == NULL)
858 break;
859 curr = mpsp;
860 np = curr->mp->m_nextpart;
862 jalter_leave:
863 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
864 goto jleave;
866 /* FALLTHRU */
867 case MIME_RELATED:
868 case MIME_DIGEST:
869 case MIME_SIGNED:
870 case MIME_ENCRYPTED:
871 case MIME_MULTI:
872 switch (action) {
873 case SEND_TODISP:
874 case SEND_TODISP_ALL:
875 case SEND_TODISP_PARTS:
876 case SEND_QUOTE:
877 case SEND_QUOTE_ALL:
878 case SEND_TOFILE:
879 case SEND_TOPIPE:
880 case SEND_TOSRCH:
881 case SEND_DECRYPT:
882 jmulti:
883 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
884 ip->m_multipart != NULL &&
885 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
886 ip->m_multipart->m_nextpart == NULL) {
887 char const *x = _("[Missing multipart boundary - use `show' "
888 "to display the raw message]\n");
889 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
890 NULL,NULL);
893 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
894 bool_t volatile ispipe;
896 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
897 continue;
899 ispipe = FAL0;
900 switch (action) {
901 case SEND_TOFILE:
902 if (np->m_partstring &&
903 np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
904 break;
905 stats = NULL;
906 /* TODO Always open multipart on /dev/null, it's a hack to be
907 * TODO able to dive into that structure, and still better
908 * TODO than asking the user for something stupid.
909 * TODO oh, wait, we did ask for a filename for this MIME mail,
910 * TODO and that outer container is useless anyway ;-P */
911 if (np->m_multipart != NULL) {
912 if ((obuf = Fopen(n_path_devnull, "w")) == NULL)
913 continue;
914 } else if ((obuf = newfile(np, &ispipe)) == NULL)
915 continue;
916 if (!ispipe)
917 break;
918 if (sigsetjmp(_send_pipejmp, 1)) {
919 rv = -1;
920 goto jpipe_close;
922 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
923 break;
924 case SEND_TODISP:
925 case SEND_TODISP_ALL:
926 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
927 ip->m_mimecontent != MIME_RELATED &&
928 ip->m_mimecontent != MIME_DIGEST &&
929 ip->m_mimecontent != MIME_SIGNED &&
930 ip->m_mimecontent != MIME_ENCRYPTED &&
931 ip->m_mimecontent != MIME_MULTI)
932 break;
933 _print_part_info(obuf, np, doitp, level, qf, stats);
934 break;
935 case SEND_TODISP_PARTS:
936 case SEND_QUOTE:
937 case SEND_QUOTE_ALL:
938 case SEND_MBOX:
939 case SEND_RFC822:
940 case SEND_SHOW:
941 case SEND_TOSRCH:
942 case SEND_DECRYPT:
943 case SEND_TOPIPE:
944 break;
947 quoteflt_flush(qf);
948 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
949 np->m_multipart == NULL && ip->m_parent != NULL) {
950 struct quoteflt *dummy = quoteflt_dummy();
951 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
952 NULL,NULL);
953 quoteflt_flush(dummy);
955 if (sendpart(zmp, np, obuf, doitp, qf, action, linedat, linesize,
956 stats, level+1) < 0)
957 rv = -1;
958 quoteflt_reset(qf, origobuf);
960 if (action == SEND_QUOTE) {
961 if (ip->m_mimecontent != MIME_RELATED)
962 break;
964 if (action == SEND_TOFILE && obuf != origobuf) {
965 if (!ispipe)
966 Fclose(obuf);
967 else {
968 jpipe_close:
969 safe_signal(SIGPIPE, SIG_IGN);
970 Pclose(obuf, TRU1);
971 safe_signal(SIGPIPE, oldpipe);
975 goto jleave;
976 case SEND_MBOX:
977 case SEND_RFC822:
978 case SEND_SHOW:
979 break;
981 break;
984 /* Copy out message body */
985 if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
986 --cnt;
987 switch (ip->m_mime_enc) {
988 case MIMEE_BIN:
989 case MIMEE_7B:
990 case MIMEE_8B:
991 convert = CONV_NONE;
992 break;
993 case MIMEE_QP:
994 convert = CONV_FROMQP;
995 break;
996 case MIMEE_B64:
997 switch (ip->m_mimecontent) {
998 case MIME_TEXT:
999 case MIME_TEXT_PLAIN:
1000 case MIME_TEXT_HTML:
1001 convert = CONV_FROMB64_T;
1002 break;
1003 default:
1004 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1005 case MIME_HDL_TEXT:
1006 case MIME_HDL_PTF:
1007 convert = CONV_FROMB64_T;
1008 break;
1009 default:
1010 convert = CONV_FROMB64;
1011 break;
1013 break;
1015 break;
1016 default:
1017 convert = CONV_NONE;
1020 /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
1021 * TODO cannot mess things up misusing outrest as line buffer */
1022 #ifdef HAVE_ICONV
1023 if (iconvd != (iconv_t)-1) {
1024 n_iconv_close(iconvd);
1025 iconvd = (iconv_t)-1;
1027 #endif
1029 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1030 action == SEND_RFC822 || action == SEND_SHOW)
1031 convert = CONV_NONE;
1032 #ifdef HAVE_ICONV
1033 else if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
1034 action == SEND_TODISP_PARTS ||
1035 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
1036 action == SEND_TOSRCH || action == SEND_TOFILE) &&
1037 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
1038 ip->m_mimecontent == MIME_TEXT_HTML ||
1039 ip->m_mimecontent == MIME_TEXT ||
1040 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
1041 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
1042 char const *tcs;
1044 tcs = ok_vlook(ttycharset);
1045 if (asccasecmp(tcs, ip->m_charset) &&
1046 asccasecmp(ok_vlook(charset_7bit), ip->m_charset)) {
1047 iconvd = n_iconv_open(tcs, ip->m_charset);
1048 if (iconvd == (iconv_t)-1 && n_err_no == n_ERR_INVAL) {
1049 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
1050 /*rv = 1; goto jleave;*/
1054 #endif
1056 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1057 case MIME_HDL_CMD:
1058 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT) &&
1059 action != SEND_TODISP_PARTS)
1060 goto jmhp_default;
1061 /* FALLTHRU */
1062 case MIME_HDL_PTF:
1063 tmpname = NULL;
1064 qbuf = obuf;
1066 term_infd = n_CHILD_FD_PASS;
1067 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
1068 enum oflags of;
1070 of = OF_RDWR | OF_REGISTER;
1071 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
1072 term_infd = 0;
1073 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1074 of |= OF_UNLINK;
1075 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
1076 of |= OF_REGISTER_UNLINK;
1078 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
1079 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
1080 of)) == NULL)
1081 goto jesend;
1083 if (mh.mh_flags & MIME_HDL_TMPF) {
1084 tmpname = savestr(cp);
1085 Ftmp_free(&cp);
1088 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1089 if (term_infd == 0)
1090 term_infd = fileno(pbuf);
1091 goto jsend;
1095 jpipe_for_real:
1096 pbuf = _pipefile(&mh, ip, n_UNVOLATILE(&qbuf), tmpname, term_infd);
1097 if (pbuf == NULL) {
1098 jesend:
1099 pbuf = qbuf = NULL;
1100 rv = -1;
1101 goto jend;
1102 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1103 pbuf = qbuf = NULL;
1104 goto jend;
1106 tmpname = NULL;
1107 action = SEND_TOPIPE;
1108 if (pbuf != qbuf) {
1109 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1110 if (sigsetjmp(_send_pipejmp, 1))
1111 goto jend;
1113 break;
1115 default:
1116 jmhp_default:
1117 mh.mh_flags = MIME_HDL_NULL;
1118 pbuf = qbuf = obuf;
1119 break;
1122 jsend:
1124 bool_t volatile eof;
1125 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1126 ui64_t *save_stats = stats;
1128 if (pbuf != origobuf) {
1129 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1130 stats = NULL;
1132 eof = FAL0;
1133 outrest.s = inrest.s = NULL;
1134 outrest.l = inrest.l = 0;
1136 if (pbuf == qbuf) {
1137 __sendp_sig = 0;
1138 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1139 if (sigsetjmp(__sendp_actjmp, 1)) {
1140 if (outrest.s != NULL)
1141 free(outrest.s);
1142 if (inrest.s != NULL)
1143 free(inrest.s);
1144 #ifdef HAVE_ICONV
1145 if (iconvd != (iconv_t)-1)
1146 n_iconv_close(iconvd);
1147 #endif
1148 safe_signal(SIGPIPE, __sendp_opipe);
1149 n_raise(__sendp_sig);
1153 quoteflt_reset(qf, pbuf);
1154 while (!eof && fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0)) {
1155 joutln:
1156 if (_out(*linedat, linelen, pbuf, convert, action, qf, stats, &outrest,
1157 (action & _TD_EOF ? NULL : &inrest)) < 0 || ferror(pbuf)) {
1158 rv = -1; /* XXX Should bail away?! */
1159 break;
1162 if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
1163 linelen = 0;
1164 if(eof || inrest.l == 0)
1165 action |= _TD_EOF;
1166 eof = eof ? TRU1 : TRUM1;
1167 goto joutln;
1169 action &= ~_TD_EOF;
1171 /* TODO HACK: when sending to the display we yet get fooled if a message
1172 * TODO doesn't end in a newline, because of our input/output 1:1.
1173 * TODO This should be handled automatically by a display filter, then */
1174 if(rv >= 0 && !qf->qf_nl_last &&
1175 (action == SEND_TODISP || action == SEND_TODISP_ALL))
1176 rv = quoteflt_push(qf, "\n", 1);
1178 quoteflt_flush(qf);
1180 if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
1181 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1182 fflush(pbuf);
1183 really_rewind(pbuf);
1184 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1185 goto jpipe_for_real;
1188 if (pbuf == qbuf)
1189 safe_signal(SIGPIPE, __sendp_opipe);
1191 if (outrest.s != NULL)
1192 free(outrest.s);
1193 if (inrest.s != NULL)
1194 free(inrest.s);
1196 if (pbuf != origobuf) {
1197 qf->qf_pfix_len = save_qf_pfix_len;
1198 stats = save_stats;
1202 jend:
1203 if (pbuf != qbuf) {
1204 safe_signal(SIGPIPE, SIG_IGN);
1205 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1206 safe_signal(SIGPIPE, oldpipe);
1207 if (rv >= 0 && qbuf != NULL && qbuf != obuf)
1208 pipecpy(qbuf, obuf, origobuf, qf, stats);
1210 #ifdef HAVE_ICONV
1211 if (iconvd != (iconv_t)-1)
1212 n_iconv_close(iconvd);
1213 #endif
1214 jleave:
1215 NYD_LEAVE;
1216 return rv;
1219 static FILE *
1220 newfile(struct mimepart *ip, bool_t volatile *ispipe)
1222 struct str in, out;
1223 char *f;
1224 FILE *fp;
1225 NYD_ENTER;
1227 f = ip->m_filename;
1228 *ispipe = FAL0;
1230 if (f != NULL && f != (char*)-1) {
1231 in.s = f;
1232 in.l = strlen(f);
1233 makeprint(&in, &out);
1234 out.l = delctrl(out.s, out.l);
1235 f = savestrbuf(out.s, out.l);
1236 free(out.s);
1239 /* In interactive mode, let user perform all kind of expansions as desired,
1240 * and offer |SHELL-SPEC pipe targets, too */
1241 if (n_psonce & n_PSO_INTERACTIVE) {
1242 struct str prompt;
1243 struct n_string shou, *shoup;
1244 char *f2, *f3;
1246 shoup = n_string_creat_auto(&shou);
1248 /* TODO Generic function which asks for filename.
1249 * TODO If the current part is the first textpart the target
1250 * TODO is implicit from outer `write' etc! */
1251 /* I18N: Filename input prompt with file type indication */
1252 str_concat_csvl(&prompt, _("Enter filename for part "),
1253 (ip->m_partstring != NULL ? ip->m_partstring : n_qm),
1254 " (", ip->m_ct_type_plain, "): ", NULL);
1255 jgetname:
1256 f2 = n_go_input_cp(n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_HIST_ADD,
1257 prompt.s, ((f != (char*)-1 && f != NULL)
1258 ? n_shexp_quote_cp(f, FAL0) : NULL));
1259 if(f2 != NULL){
1260 in.s = n_UNCONST(f2);
1261 in.l = UIZ_MAX;
1262 if((n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
1263 n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_TRIM_IFSSPACE |
1264 n_SHEXP_PARSE_LOG | n_SHEXP_PARSE_IGNORE_EMPTY),
1265 shoup, &in, NULL
1266 ) & (n_SHEXP_STATE_STOP |
1267 n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)
1268 ) != (n_SHEXP_STATE_STOP | n_SHEXP_STATE_OUTPUT))
1269 goto jgetname;
1270 f2 = n_string_cp(shoup);
1272 if (f2 == NULL || *f2 == '\0') {
1273 if (n_poption & n_PO_D_V)
1274 n_err(_("... skipping this\n"));
1275 n_string_gut(shoup);
1276 fp = NULL;
1277 goto jleave;
1280 if (*f2 == '|')
1281 /* Pipes are expanded by the shell */
1282 f = f2;
1283 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
1284 /* (Error message written by fexpand()) */
1285 goto jgetname;
1286 else
1287 f = f3;
1289 n_string_gut(shoup);
1292 if (f == NULL || f == (char*)-1 || *f == '\0')
1293 fp = NULL;
1294 else if (n_psonce & n_PSO_INTERACTIVE) {
1295 if (*f == '|') {
1296 fp = Popen(&f[1], "w", ok_vlook(SHELL), NULL, 1);
1297 if (!(*ispipe = (fp != NULL)))
1298 n_perr(f, 0);
1299 } else if ((fp = Fopen(f, "w")) == NULL)
1300 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
1301 } else {
1302 /* Be very picky in non-interactive mode: actively disallow pipes,
1303 * prevent directory separators, and any filename member that would
1304 * become expanded by the shell if the name would be echo(1)ed */
1305 if(anyof(f, "/" n_SHEXP_MAGIC_PATH_CHARS)){
1306 char c;
1308 for(out.s = salloc((strlen(f) * 3) +1), out.l = 0; (c = *f++) != '\0';)
1309 if(strchr("/" n_SHEXP_MAGIC_PATH_CHARS, c)){
1310 out.s[out.l++] = '%';
1311 n_c_to_hex_base16(&out.s[out.l], c);
1312 out.l += 2;
1313 }else
1314 out.s[out.l++] = c;
1315 out.s[out.l] = '\0';
1316 f = out.s;
1319 /* Avoid overwriting of existing files */
1320 while((fp = Fopen(f, "wx")) == NULL){
1321 int e;
1323 if((e = n_err_no) != n_ERR_EXIST){
1324 n_err(_("Cannot open %s: %s\n"),
1325 n_shexp_quote_cp(f, FAL0), n_err_to_doc(e));
1326 break;
1329 if(ip->m_partstring != NULL)
1330 f = savecatsep(f, '#', ip->m_partstring);
1331 else
1332 f = savecat(f, "#.");
1335 jleave:
1336 NYD_LEAVE;
1337 return fp;
1340 static void
1341 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1342 ui64_t *stats)
1344 char *line = NULL; /* TODO line pool */
1345 size_t linesize = 0, linelen, cnt;
1346 ssize_t all_sz, sz;
1347 NYD_ENTER;
1349 fflush(pipebuf);
1350 rewind(pipebuf);
1351 cnt = (size_t)fsize(pipebuf);
1352 all_sz = 0;
1354 quoteflt_reset(qf, outbuf);
1355 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1356 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1357 break;
1358 all_sz += sz;
1360 if ((sz = quoteflt_flush(qf)) > 0)
1361 all_sz += sz;
1362 if (line)
1363 free(line);
1365 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1366 *stats += all_sz;
1367 Fclose(pipebuf);
1368 NYD_LEAVE;
1371 static void
1372 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1373 ui64_t *stats)
1375 char statout[3], *cp = statout;
1376 NYD_ENTER;
1378 if (mp->m_flag & MREAD)
1379 *cp++ = 'R';
1380 if (!(mp->m_flag & MNEW))
1381 *cp++ = 'O';
1382 *cp = 0;
1383 if (statout[0]) {
1384 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1385 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1386 if (i > 0 && stats != NULL)
1387 *stats += i;
1389 NYD_LEAVE;
1392 static void
1393 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1394 ui64_t *stats)
1396 char xstatout[4];
1397 char *xp = xstatout;
1398 NYD_ENTER;
1400 if (mp->m_flag & MFLAGGED)
1401 *xp++ = 'F';
1402 if (mp->m_flag & MANSWERED)
1403 *xp++ = 'A';
1404 if (mp->m_flag & MDRAFTED)
1405 *xp++ = 'T';
1406 *xp = 0;
1407 if (xstatout[0]) {
1408 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1409 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1410 if (i > 0 && stats != NULL)
1411 *stats += i;
1413 NYD_LEAVE;
1416 static void
1417 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1419 char const *froma, *date, *nl;
1420 int i;
1421 NYD_ENTER;
1423 if (ip != NULL && ip->m_from != NULL) {
1424 froma = ip->m_from;
1425 date = fakedate(ip->m_time);
1426 nl = "\n";
1427 } else {
1428 froma = ok_vlook(LOGNAME);
1429 date = time_current.tc_ctime;
1430 nl = n_empty;
1433 n_COLOUR(
1434 if(n_COLOUR_IS_ACTIVE())
1435 n_colour_put(n_COLOUR_ID_VIEW_FROM_, NULL);
1437 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1438 n_COLOUR(
1439 if(n_COLOUR_IS_ACTIVE())
1440 n_colour_reset();
1442 if (i > 0 && stats != NULL)
1443 *stats += i;
1444 NYD_LEAVE;
1447 FL int
1448 sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp,
1449 char const *prefix, enum sendaction action, ui64_t *stats)
1451 struct n_sigman linedat_protect;
1452 struct quoteflt qf;
1453 FILE *ibuf;
1454 enum mime_parse_flags mpf;
1455 struct mimepart *ip;
1456 size_t linesize, cnt, sz, i;
1457 char *linedat;
1458 int rv, c;
1459 NYD_ENTER;
1461 time_current_update(&time_current, TRU1);
1462 rv = -1;
1463 linedat = NULL;
1464 linesize = 0;
1465 quoteflt_init(&qf, prefix);
1467 n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
1468 case 0:
1469 break;
1470 default:
1471 goto jleave;
1474 if (mp == dot && action != SEND_TOSRCH)
1475 n_pstate |= n_PS_DID_PRINT_DOT;
1476 if (stats != NULL)
1477 *stats = 0;
1479 /* First line is the From_ line, so no headers there to worry about */
1480 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1481 goto jleave;
1483 cnt = mp->m_size;
1484 sz = 0;
1486 bool_t nozap;
1487 char const *cpre = n_empty, *csuf = n_empty;
1489 #ifdef HAVE_COLOUR
1490 if(n_COLOUR_IS_ACTIVE()){
1491 struct n_colour_pen *cpen;
1492 struct str const *sp;
1494 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
1495 if((sp = n_colour_pen_to_str(cpen)) != NULL){
1496 cpre = sp->s;
1497 sp = n_colour_reset_to_str();
1498 if(sp != NULL)
1499 csuf = sp->s;
1502 #endif
1504 nozap = (doitp != n_IGNORE_ALL && doitp != n_IGNORE_FWD &&
1505 action != SEND_RFC822 &&
1506 !n_ignore_is_ign(doitp, "from_", sizeof("from_") -1));
1507 if (mp->m_flag & MNOFROM) {
1508 if (nozap)
1509 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1510 cpre, (int)qf.qf_pfix_len,
1511 (qf.qf_pfix_len != 0 ? qf.qf_pfix : n_empty), fakefrom(mp),
1512 fakedate(mp->m_time), csuf);
1513 } else if (nozap) {
1514 if (qf.qf_pfix_len > 0) {
1515 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1516 if (i != qf.qf_pfix_len)
1517 goto jleave;
1518 sz += i;
1520 #ifdef HAVE_COLOUR
1521 if(*cpre != '\0'){
1522 fputs(cpre, obuf);
1523 cpre = (char const*)0x1;
1525 #endif
1527 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1528 #ifdef HAVE_COLOUR
1529 if(c == '\n' && *csuf != '\0'){
1530 cpre = (char const*)0x1;
1531 fputs(csuf, obuf);
1533 #endif
1534 putc(c, obuf);
1535 ++sz;
1536 --cnt;
1537 if (c == '\n')
1538 break;
1541 #ifdef HAVE_COLOUR
1542 if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
1543 fputs(csuf, obuf);
1544 #endif
1545 } else {
1546 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1547 --cnt;
1548 if (c == '\n')
1549 break;
1553 if (sz > 0 && stats != NULL)
1554 *stats += sz;
1556 mpf = MIME_PARSE_NONE;
1557 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1558 mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT;
1559 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1560 goto jleave;
1562 rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
1563 stats, 0);
1565 n_sigman_cleanup_ping(&linedat_protect);
1566 jleave:
1567 quoteflt_destroy(&qf);
1568 if(linedat != NULL)
1569 free(linedat);
1570 NYD_LEAVE;
1571 n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
1572 return rv;
1575 /* s-it-mode */