Tweak previous, it added a bad memory access
[s-mailx.git] / send.c
blob9a7982d1d5c27e30b77a7dee2a5b3c866c5baea5
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 struct str const *cpre, *csuf;
92 char const *cp;
93 NYD2_ENTER;
95 cpre = csuf = NULL;
96 #ifdef HAVE_COLOUR
97 if(n_COLOUR_IS_ACTIVE()){
98 struct n_colour_pen *cpen;
100 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_PARTINFO, NULL);
101 if((cpre = n_colour_pen_to_str(cpen)) != NULL)
102 csuf = n_colour_reset_to_str();
104 #endif
106 /* Take care of "99.99", i.e., 5 */
107 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
108 cp = n_qm;
109 if (level || (cp[0] != '1' && cp[1] == '\0') || (cp[0] == '1' && /* TODO */
110 cp[1] == '.' && cp[2] != '1')) /* TODO code should not look like so */
111 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
113 /* Part id, content-type, encoding, charset */
114 if (cpre != NULL)
115 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
116 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
117 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
119 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
120 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
121 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
123 if ((cp = mpp->m_ct_type_usr_ovwr) != NULL)
124 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
125 else
126 cp = mpp->m_ct_type_plain;
127 if ((to.l = strlen(cp)) > 30 && is_asccaseprefix("application/", cp)) {
128 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
129 size_t i = to.l - fl;
130 char *x = salloc(al + i +1);
132 memcpy(x, "appl../", al);
133 memcpy(x + al, cp + fl, i +1);
134 cp = x;
135 to.l = al + i;
137 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
139 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL) {
140 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
141 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
142 cp = "qu.-pr.";
143 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
146 if (mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_charset) != NULL) {
147 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
148 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
151 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
152 if (csuf != NULL)
153 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
154 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
156 /* */
157 if (mpp->m_content_info & CI_MIME_ERRORS) {
158 if (cpre != NULL)
159 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
160 NULL, NULL);
161 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
163 ti.l = strlen(ti.s = n_UNCONST(_("Defective MIME structure")));
164 makeprint(&ti, &to);
165 to.l = delctrl(to.s, to.l);
166 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
167 free(to.s);
169 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
170 if (csuf != NULL)
171 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
172 NULL, NULL);
173 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
176 /* Content-Description */
177 if (n_ignore_is_ign(doitp, "content-description", 19) &&
178 (cp = mpp->m_content_description) != NULL && *cp != '\0') {
179 if (cpre != NULL)
180 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
181 NULL, NULL);
182 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
184 ti.l = strlen(ti.s = n_UNCONST(mpp->m_content_description));
185 mime_fromhdr(&ti, &to, TD_ISPR | TD_ICONV);
186 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
187 free(to.s);
189 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
190 if (csuf != NULL)
191 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
192 NULL, NULL);
193 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
196 /* Filename */
197 if (n_ignore_is_ign(doitp, "content-disposition", 19) &&
198 mpp->m_filename != NULL && *mpp->m_filename != '\0') {
199 if (cpre != NULL)
200 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
201 NULL, NULL);
202 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
204 ti.l = strlen(ti.s = mpp->m_filename);
205 makeprint(&ti, &to);
206 to.l = delctrl(to.s, to.l);
207 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
208 free(to.s);
210 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
211 if (csuf != NULL)
212 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
213 NULL, NULL);
214 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
216 NYD2_LEAVE;
219 static FILE *
220 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
221 char const *tmpname, int term_infd)
223 struct str s;
224 char const *env_addon[8 +8/*v15*/], *cp, *sh;
225 FILE *rbuf;
226 NYD_ENTER;
228 rbuf = *qbuf;
230 if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
231 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
232 NULL) {
233 n_perr(_("tmpfile"), 0);
234 *qbuf = rbuf;
238 if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
239 union {int (*ptf)(void); char const *sh;} u;
241 fflush(*qbuf);
242 if (*qbuf != n_stdout) /* xxx never? v15: it'll be a filter anyway */
243 fflush(n_stdout);
245 u.ptf = mhp->mh_ptf;
246 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
247 goto jerror;
248 goto jleave;
251 /* MAILX_FILENAME */
252 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
253 cp = n_empty;
254 env_addon[0] = str_concat_csvl(&s, n_PIPEENV_FILENAME, "=", cp, NULL)->s;
255 env_addon[1] = str_concat_csvl(&s, "NAIL_FILENAME", "=", cp, NULL)->s;
257 /* MAILX_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
258 * TODO a file wherever he wants! *Do* create a zero-size temporary file
259 * TODO and give *that* path as MAILX_FILENAME_TEMPORARY, clean it up once
260 * TODO the pipe returns? Like this we *can* verify path/name issues! */
261 cp = n_random_create_cp(n_MIN(NAME_MAX / 4, 16));
262 env_addon[2] = str_concat_csvl(&s, n_PIPEENV_FILENAME_GENERATED, "=", cp,
263 NULL)->s;
264 env_addon[3] = str_concat_csvl(&s, "NAIL_FILENAME_GENERATED", "=", cp,
265 NULL)->s;
267 /* MAILX_CONTENT{,_EVIDENCE} */
268 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
269 cp = n_empty;
270 env_addon[4] = str_concat_csvl(&s, n_PIPEENV_CONTENT, "=", cp, NULL)->s;
271 env_addon[5] = str_concat_csvl(&s, "NAIL_CONTENT", "=", cp, NULL)->s;
273 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
274 cp = mpp->m_ct_type_usr_ovwr;
275 env_addon[6] = str_concat_csvl(&s, n_PIPEENV_CONTENT_EVIDENCE, "=", cp,
276 NULL)->s;
277 env_addon[7] = str_concat_csvl(&s, "NAIL_CONTENT_EVIDENCE", "=", cp,
278 NULL)->s;
280 env_addon[8] = NULL;
282 /* MAILX_FILENAME_TEMPORARY? */
283 if (tmpname != NULL) {
284 env_addon[8] = str_concat_csvl(&s,
285 n_PIPEENV_FILENAME_TEMPORARY, "=", tmpname, NULL)->s;
286 env_addon[9] = str_concat_csvl(&s,
287 "NAIL_FILENAME_TEMPORARY", "=", tmpname, NULL)->s;
288 env_addon[10] = NULL;
291 sh = ok_vlook(SHELL);
293 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
294 sigset_t nset;
295 int pid;
297 sigemptyset(&nset);
298 pid = n_child_run(sh, &nset, term_infd, n_CHILD_FD_PASS, "-c",
299 mhp->mh_shell_cmd, NULL, env_addon);
300 rbuf = (pid < 0) ? NULL : (FILE*)-1;
301 } else {
302 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
303 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
304 jerror:
305 if (rbuf == NULL)
306 n_err(_("Cannot run MIME type handler: %s: %s\n"),
307 mhp->mh_msg, n_err_to_doc(n_err_no));
308 else {
309 fflush(*qbuf);
310 if (*qbuf != n_stdout)
311 fflush(n_stdout);
314 jleave:
315 NYD_LEAVE;
316 return rbuf;
319 SINLINE ssize_t
320 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
321 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *outrest,
322 struct str *inrest)
324 ssize_t sz = 0, n;
325 int flags;
326 NYD_ENTER;
328 /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
329 * TODO subclass should detect such From_ cases and either reencode the part
330 * TODO in question, or perform From_ quoting as necessary!?!?!? How?!? */
331 /* C99 */{
332 bool_t from_;
334 if((action == SEND_MBOX || action == SEND_DECRYPT) &&
335 (from_ = is_head(buf, len, TRU1))){
336 if(from_ != TRUM1 || ok_blook(mbox_rfc4155)){
337 putc('>', fp);
338 ++sz;
343 flags = ((int)action & _TD_EOF);
344 action &= ~_TD_EOF;
345 n = mime_write(buf, len, fp,
346 action == SEND_MBOX ? CONV_NONE : convert,
347 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
348 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
349 ? TD_ISPR | TD_ICONV
350 : (action == SEND_TOSRCH || action == SEND_TOPIPE ||
351 action == SEND_TOFILE)
352 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
353 qf, outrest, inrest);
354 if (n < 0)
355 sz = n;
356 else if (n > 0) {
357 sz += n;
358 if (stats != NULL)
359 *stats += sz;
361 NYD_LEAVE;
362 return sz;
365 static void
366 _send_onpipe(int signo)
368 NYD_X; /* Signal handler */
369 n_UNUSED(signo);
370 siglongjmp(_send_pipejmp, 1);
373 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
374 static int __sendp_sig; /* TODO someday.. */
375 static sighandler_type __sendp_opipe;
376 static void
377 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
379 NYD_X; /* Signal handler */
380 __sendp_sig = sig;
381 siglongjmp(__sendp_actjmp, 1);
384 static int
385 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
386 struct n_ignore const *doitp, struct quoteflt *qf,
387 enum sendaction volatile action,
388 char **linedat, size_t *linesize, ui64_t * volatile stats, int level)
390 int volatile rv = 0;
391 struct mime_handler mh;
392 struct str outrest, inrest;
393 char *cp;
394 char const * volatile tmpname = NULL;
395 size_t linelen, cnt;
396 int volatile term_infd;
397 int dostat, c;
398 struct mimepart *volatile np;
399 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
400 * volatile qbuf = obuf, *origobuf = obuf;
401 enum conversion volatile convert;
402 sighandler_type volatile oldpipe = SIG_DFL;
403 NYD_ENTER;
405 n_UNINIT(term_infd, 0);
406 n_UNINIT(cnt, 0);
408 quoteflt_reset(qf, obuf);
410 if (ip->m_mimecontent == MIME_PKCS7) {
411 if (ip->m_multipart &&
412 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
413 goto jheaders_skip;
416 dostat = 0;
417 if (level == 0) {
418 if (doitp != NULL) {
419 if (!n_ignore_is_ign(doitp, "status", 6))
420 dostat |= 1;
421 if (!n_ignore_is_ign(doitp, "x-status", 8))
422 dostat |= 2;
423 } else
424 dostat = 3;
426 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
427 rv = -1;
428 goto jleave;
430 cnt = ip->m_size;
432 if (ip->m_mimecontent == MIME_DISCARD)
433 goto jheaders_skip;
435 if (!(ip->m_flag & MNOFROM))
436 while (cnt && (c = getc(ibuf)) != EOF) {
437 cnt--;
438 if (c == '\n')
439 break;
441 convert = (action == SEND_TODISP || action == SEND_TODISP_ALL ||
442 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
443 action == SEND_TOSRCH)
444 ? CONV_FROMHDR : CONV_NONE;
446 /* Work the headers */
447 /* C99 */{
448 struct n_string hl, *hlp;
449 size_t lineno = 0;
450 bool_t hstop;
452 hlp = n_string_creat_auto(&hl); /* TODO pool [or, v15: filter!] */
453 /* Reserve three lines, still not enough for references and DKIM etc. */
454 hlp = n_string_reserve(hlp, n_MAX(MIME_LINELEN, MIME_LINELEN_RFC2047) * 3);
456 for(hstop = FAL0; !hstop;){
457 size_t lcnt;
459 lcnt = cnt;
460 if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0) == NULL)
461 break;
462 ++lineno;
463 if (linelen == 0 || (cp = *linedat)[0] == '\n')
464 /* If line is blank, we've reached end of headers */
465 break;
466 if(cp[linelen - 1] == '\n')
467 cp[--linelen] = '\0';
469 /* Are we in a header? */
470 if(hlp->s_len > 0){
471 if(!blankchar(*cp)){
472 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
473 cnt = lcnt;
474 goto jhdrput;
476 goto jhdrpush;
477 }else{
478 /* Pick up the header field if we have one */
479 while(*cp != ':' && !spacechar(*cp))
480 ++cp;
481 while(spacechar(*cp))
482 ++cp;
483 if(*cp != ':'){
484 /* That won't work with MIME when saving etc., before v15 */
485 if (lineno != 1)
486 /* XXX This disturbs, and may happen multiple times, and we
487 * XXX cannot heal it for multipart except for display <v15 */
488 n_err(_("Malformed message: headers and body not separated "
489 "(with empty line)\n"));
490 if(level != 0)
491 dostat &= ~(1 | 2);
492 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
493 cnt = lcnt;
494 break;
496 cp = *linedat;
497 jhdrpush:
498 if(convert == CONV_NONE){
499 hlp = n_string_push_buf(hlp, cp, (ui32_t)linelen);
500 hlp = n_string_push_c(hlp, '\n');
501 }else{
502 bool_t lblank, isblank;
504 for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
505 char c8;
507 c8 = *cp;
508 if(!(isblank = blankchar(c8)) || !lblank){
509 if((lblank = isblank))
510 c8 = ' ';
511 hlp = n_string_push_c(hlp, c8);
515 continue;
518 jhdrput:
519 /* If it is an ignored header, skip it */
520 *(cp = memchr(hlp->s_dat, ':', hlp->s_len)) = '\0';
521 /* C99 */{
522 size_t i;
524 i = PTR2SIZE(cp - hlp->s_dat);
525 if((doitp != NULL && n_ignore_is_ign(doitp, hlp->s_dat, i)) ||
526 !asccasecmp(hlp->s_dat, "status") ||
527 !asccasecmp(hlp->s_dat, "x-status") ||
528 (action == SEND_MBOX &&
529 (!asccasecmp(hlp->s_dat, "content-length") ||
530 !asccasecmp(hlp->s_dat, "lines")) &&
531 !ok_blook(keep_content_length)))
532 goto jhdrtrunc;
535 /* Dump it */
536 n_COLOUR(
537 if(n_COLOUR_IS_ACTIVE())
538 n_colour_put(n_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
540 *cp = ':';
541 _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NULL,NULL);
542 n_COLOUR(
543 if(n_COLOUR_IS_ACTIVE())
544 n_colour_reset();
546 if(convert != CONV_NONE)
547 putc('\n', obuf);
549 jhdrtrunc:
550 hlp = n_string_trunc(hlp, 0);
552 hstop = TRU1;
553 if(hlp->s_len > 0)
554 goto jhdrput;
556 /* We've reached end of headers, so eventually force out status: field and
557 * note that we are no longer in header fields */
558 if(dostat & 1)
559 statusput(zmp, obuf, qf, stats);
560 if(dostat & 2)
561 xstatusput(zmp, obuf, qf, stats);
562 if(doitp != n_IGNORE_ALL)
563 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
564 } /* C99 */
566 quoteflt_flush(qf);
568 if(ferror(obuf)){
569 rv = -1;
570 goto jleave;
573 jheaders_skip:
574 memset(&mh, 0, sizeof mh);
576 switch (ip->m_mimecontent) {
577 case MIME_822:
578 switch (action) {
579 case SEND_TODISP:
580 case SEND_TODISP_ALL:
581 case SEND_QUOTE:
582 case SEND_QUOTE_ALL:
583 if (ok_blook(rfc822_body_from_)) {
584 if (qf->qf_pfix_len > 0) {
585 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
586 qf->qf_pfix_len, obuf);
587 if (i == qf->qf_pfix_len && stats != NULL)
588 *stats += i;
590 put_from_(obuf, ip->m_multipart, stats);
592 /* FALLTHRU */
593 case SEND_TOSRCH:
594 case SEND_DECRYPT:
595 goto jmulti;
596 case SEND_TOFILE:
597 case SEND_TOPIPE:
598 if (ok_blook(rfc822_body_from_))
599 put_from_(obuf, ip->m_multipart, stats);
600 /* FALLTHRU */
601 case SEND_MBOX:
602 case SEND_RFC822:
603 case SEND_SHOW:
604 break;
606 break;
607 case MIME_TEXT_HTML:
608 case MIME_TEXT:
609 case MIME_TEXT_PLAIN:
610 switch (action) {
611 case SEND_TODISP:
612 case SEND_TODISP_ALL:
613 case SEND_QUOTE:
614 case SEND_QUOTE_ALL:
615 switch (mime_type_handler(&mh, ip, action)) {
616 case MIME_HDL_MSG:
617 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
618 stats, NULL, NULL);
619 /* We would print this as plain text, so better force going home */
620 goto jleave;
621 default:
622 break;
624 /* FALLTRHU */
625 default:
626 break;
628 break;
629 case MIME_DISCARD:
630 if (action != SEND_DECRYPT)
631 goto jleave;
632 break;
633 case MIME_PKCS7:
634 if (action != SEND_MBOX && action != SEND_RFC822 &&
635 action != SEND_SHOW && ip->m_multipart != NULL)
636 goto jmulti;
637 /* FALLTHRU */
638 default:
639 switch (action) {
640 case SEND_TODISP:
641 case SEND_TODISP_ALL:
642 case SEND_QUOTE:
643 case SEND_QUOTE_ALL:
644 switch (mime_type_handler(&mh, ip, action)) {
645 case MIME_HDL_MSG:
646 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX, qf,
647 stats, NULL, NULL);
648 /* We would print this as plain text, so better force going home */
649 goto jleave;
650 case MIME_HDL_CMD:
651 /* FIXME WE NEED TO DO THAT IF WE ARE THE ONLY MAIL
652 * FIXME CONTENT !! */
653 case MIME_HDL_TEXT:
654 break;
655 default:
656 case MIME_HDL_NULL:
657 if (level == 0 && cnt) {
658 char const *x = _("[-- Binary content --]\n");
659 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
660 NULL,NULL);
662 goto jleave;
664 break;
665 case SEND_TOFILE:
666 case SEND_TOPIPE:
667 case SEND_TOSRCH:
668 case SEND_DECRYPT:
669 case SEND_MBOX:
670 case SEND_RFC822:
671 case SEND_SHOW:
672 break;
674 break;
675 case MIME_ALTERNATIVE:
676 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
677 !ok_blook(print_alternatives)) {
678 /* XXX This (a) should not remain (b) should be own fun
679 * TODO (despite the fact that v15 will do this completely differently
680 * TODO by having an action-specific "manager" that will traverse the
681 * TODO parsed MIME tree and decide for each part whether it'll be
682 * TODO displayed or not *before* we walk the tree for doing action */
683 struct mpstack {
684 struct mpstack *outer;
685 struct mimepart *mp;
686 } outermost, * volatile curr, * volatile mpsp;
687 bool_t volatile neednl, hadpart;
688 struct n_sigman smalter;
690 (curr = &outermost)->outer = NULL;
691 curr->mp = ip;
692 neednl = hadpart = FAL0;
694 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
695 case 0:
696 break;
697 default:
698 rv = -1;
699 goto jalter_leave;
702 for (np = ip->m_multipart;;) {
703 jalter_redo:
704 for (; np != NULL; np = np->m_nextpart) {
705 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
706 if (neednl)
707 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats,
708 NULL, NULL);
709 _print_part_info(obuf, np, doitp, level, qf, stats);
711 neednl = TRU1;
713 switch (np->m_mimecontent) {
714 case MIME_ALTERNATIVE:
715 case MIME_RELATED:
716 case MIME_DIGEST:
717 case MIME_SIGNED:
718 case MIME_ENCRYPTED:
719 case MIME_MULTI:
720 mpsp = salloc(sizeof *mpsp);
721 mpsp->outer = curr;
722 mpsp->mp = np->m_multipart;
723 curr->mp = np;
724 curr = mpsp;
725 np = mpsp->mp;
726 neednl = FAL0;
727 goto jalter_redo;
728 default:
729 if (hadpart)
730 break;
731 switch (mime_type_handler(&mh, np, action)) {
732 default:
733 mh.mh_flags = MIME_HDL_NULL;
734 continue; /* break; break; */
735 case MIME_HDL_PTF:
736 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
737 struct mimepart *x = np;
739 while ((x = x->m_nextpart) != NULL) {
740 struct mime_handler mhx;
742 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
743 mime_type_handler(&mhx, x, action) ==
744 MIME_HDL_TEXT)
745 break;
747 if (x != NULL)
748 continue; /* break; break; */
749 goto jalter_plain;
751 /* FALLTHRU */
752 case MIME_HDL_TEXT:
753 break;
755 /* FALLTHRU */
756 case MIME_TEXT_PLAIN:
757 if (hadpart)
758 break;
759 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
760 struct mimepart *x = np;
762 /* TODO twice TODO, we should dive into /related and
763 * TODO check whether that has rich parts! */
764 while ((x = x->m_nextpart) != NULL) {
765 struct mime_handler mhx;
767 switch (mime_type_handler(&mhx, x, action)) {
768 case MIME_HDL_PTF:
769 break;
770 default:
771 continue;
773 break;
775 if (x != NULL)
776 continue; /* break; break; */
778 jalter_plain:
779 quoteflt_flush(qf);
780 if (action == SEND_QUOTE && hadpart) {
781 struct quoteflt *dummy = quoteflt_dummy();
782 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
783 NULL,NULL);
784 quoteflt_flush(dummy);
786 hadpart = TRU1;
787 neednl = FAL0;
788 rv = sendpart(zmp, np, obuf, doitp, qf, action,
789 linedat, linesize, stats, level + 1);
790 quoteflt_reset(qf, origobuf);
792 if (rv < 0)
793 curr = &outermost; /* Cause overall loop termination */
794 break;
798 mpsp = curr->outer;
799 if (mpsp == NULL)
800 break;
801 curr = mpsp;
802 np = curr->mp->m_nextpart;
804 jalter_leave:
805 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
806 goto jleave;
808 /* FALLTHRU */
809 case MIME_RELATED:
810 case MIME_DIGEST:
811 case MIME_SIGNED:
812 case MIME_ENCRYPTED:
813 case MIME_MULTI:
814 switch (action) {
815 case SEND_TODISP:
816 case SEND_TODISP_ALL:
817 case SEND_QUOTE:
818 case SEND_QUOTE_ALL:
819 case SEND_TOFILE:
820 case SEND_TOPIPE:
821 case SEND_TOSRCH:
822 case SEND_DECRYPT:
823 jmulti:
824 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
825 ip->m_multipart != NULL &&
826 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
827 ip->m_multipart->m_nextpart == NULL) {
828 char const *x = _("[Missing multipart boundary - use show "
829 "to display the raw message]\n");
830 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
831 NULL,NULL);
834 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
835 bool_t volatile ispipe;
837 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
838 continue;
840 ispipe = FAL0;
841 switch (action) {
842 case SEND_TOFILE:
843 if (np->m_partstring &&
844 np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
845 break;
846 stats = NULL;
847 /* TODO Always open multipart on /dev/null, it's a hack to be
848 * TODO able to dive into that structure, and still better
849 * TODO than asking the user for something stupid.
850 * TODO oh, wait, we did ask for a filename for this MIME mail,
851 * TODO and that outer container is useless anyway ;-P */
852 if (np->m_multipart != NULL) {
853 if ((obuf = Fopen("/dev/null", "w")) == NULL)
854 continue;
855 } else if ((obuf = newfile(np, &ispipe)) == NULL)
856 continue;
857 if (!ispipe)
858 break;
859 if (sigsetjmp(_send_pipejmp, 1)) {
860 rv = -1;
861 goto jpipe_close;
863 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
864 break;
865 case SEND_TODISP:
866 case SEND_TODISP_ALL:
867 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
868 ip->m_mimecontent != MIME_RELATED &&
869 ip->m_mimecontent != MIME_DIGEST &&
870 ip->m_mimecontent != MIME_SIGNED &&
871 ip->m_mimecontent != MIME_ENCRYPTED &&
872 ip->m_mimecontent != MIME_MULTI)
873 break;
874 _print_part_info(obuf, np, doitp, level, qf, stats);
875 break;
876 case SEND_QUOTE:
877 case SEND_QUOTE_ALL:
878 case SEND_MBOX:
879 case SEND_RFC822:
880 case SEND_SHOW:
881 case SEND_TOSRCH:
882 case SEND_DECRYPT:
883 case SEND_TOPIPE:
884 break;
887 quoteflt_flush(qf);
888 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
889 np->m_multipart == NULL && ip->m_parent != NULL) {
890 struct quoteflt *dummy = quoteflt_dummy();
891 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
892 NULL,NULL);
893 quoteflt_flush(dummy);
895 if (sendpart(zmp, np, obuf, doitp, qf, action, linedat, linesize,
896 stats, level+1) < 0)
897 rv = -1;
898 quoteflt_reset(qf, origobuf);
900 if (action == SEND_QUOTE) {
901 if (ip->m_mimecontent != MIME_RELATED)
902 break;
904 if (action == SEND_TOFILE && obuf != origobuf) {
905 if (!ispipe)
906 Fclose(obuf);
907 else {
908 jpipe_close:
909 safe_signal(SIGPIPE, SIG_IGN);
910 Pclose(obuf, TRU1);
911 safe_signal(SIGPIPE, oldpipe);
915 goto jleave;
916 case SEND_MBOX:
917 case SEND_RFC822:
918 case SEND_SHOW:
919 break;
921 break;
924 /* Copy out message body */
925 if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
926 --cnt;
927 switch (ip->m_mime_enc) {
928 case MIMEE_BIN:
929 case MIMEE_7B:
930 case MIMEE_8B:
931 convert = CONV_NONE;
932 break;
933 case MIMEE_QP:
934 convert = CONV_FROMQP;
935 break;
936 case MIMEE_B64:
937 switch (ip->m_mimecontent) {
938 case MIME_TEXT:
939 case MIME_TEXT_PLAIN:
940 case MIME_TEXT_HTML:
941 convert = CONV_FROMB64_T;
942 break;
943 default:
944 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
945 case MIME_HDL_TEXT:
946 case MIME_HDL_PTF:
947 convert = CONV_FROMB64_T;
948 break;
949 default:
950 convert = CONV_FROMB64;
951 break;
953 break;
955 break;
956 default:
957 convert = CONV_NONE;
960 /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
961 * TODO cannot mess things up misusing outrest as line buffer */
962 #ifdef HAVE_ICONV
963 if (iconvd != (iconv_t)-1) {
964 n_iconv_close(iconvd);
965 iconvd = (iconv_t)-1;
967 #endif
969 if (action == SEND_DECRYPT || action == SEND_MBOX ||
970 action == SEND_RFC822 || action == SEND_SHOW)
971 convert = CONV_NONE;
972 #ifdef HAVE_ICONV
973 else if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
974 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
975 action == SEND_TOSRCH || action == SEND_TOFILE) &&
976 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
977 ip->m_mimecontent == MIME_TEXT_HTML ||
978 ip->m_mimecontent == MIME_TEXT ||
979 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
980 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
981 char const *tcs;
983 tcs = ok_vlook(ttycharset);
984 if (asccasecmp(tcs, ip->m_charset) &&
985 asccasecmp(ok_vlook(charset_7bit), ip->m_charset)) {
986 iconvd = n_iconv_open(tcs, ip->m_charset);
987 if (iconvd == (iconv_t)-1 && n_err_no == n_ERR_INVAL) {
988 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
989 /*rv = 1; goto jleave;*/
993 #endif
995 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
996 case MIME_HDL_CMD:
997 case MIME_HDL_PTF:
998 tmpname = NULL;
999 qbuf = obuf;
1001 term_infd = n_CHILD_FD_PASS;
1002 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
1003 enum oflags of;
1005 of = OF_RDWR | OF_REGISTER;
1006 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
1007 term_infd = 0;
1008 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1009 of |= OF_UNLINK;
1010 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
1011 of |= OF_REGISTER_UNLINK;
1013 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
1014 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
1015 of)) == NULL)
1016 goto jesend;
1018 if (mh.mh_flags & MIME_HDL_TMPF) {
1019 tmpname = savestr(cp);
1020 Ftmp_free(&cp);
1023 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1024 if (term_infd == 0)
1025 term_infd = fileno(pbuf);
1026 goto jsend;
1030 jpipe_for_real:
1031 pbuf = _pipefile(&mh, ip, n_UNVOLATILE(&qbuf), tmpname, term_infd);
1032 if (pbuf == NULL) {
1033 jesend:
1034 pbuf = qbuf = NULL;
1035 rv = -1;
1036 goto jend;
1037 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1038 pbuf = qbuf = NULL;
1039 goto jend;
1041 tmpname = NULL;
1042 action = SEND_TOPIPE;
1043 if (pbuf != qbuf) {
1044 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1045 if (sigsetjmp(_send_pipejmp, 1))
1046 goto jend;
1048 break;
1050 default:
1051 mh.mh_flags = MIME_HDL_NULL;
1052 pbuf = qbuf = obuf;
1053 break;
1056 jsend:
1058 bool_t volatile eof;
1059 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1060 ui64_t *save_stats = stats;
1062 if (pbuf != origobuf) {
1063 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1064 stats = NULL;
1066 eof = FAL0;
1067 outrest.s = inrest.s = NULL;
1068 outrest.l = inrest.l = 0;
1070 if (pbuf == qbuf) {
1071 __sendp_sig = 0;
1072 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1073 if (sigsetjmp(__sendp_actjmp, 1)) {
1074 if (outrest.s != NULL)
1075 free(outrest.s);
1076 if (inrest.s != NULL)
1077 free(inrest.s);
1078 #ifdef HAVE_ICONV
1079 if (iconvd != (iconv_t)-1)
1080 n_iconv_close(iconvd);
1081 #endif
1082 safe_signal(SIGPIPE, __sendp_opipe);
1083 n_raise(__sendp_sig);
1087 quoteflt_reset(qf, pbuf);
1088 while (!eof && fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0)) {
1089 joutln:
1090 if (_out(*linedat, linelen, pbuf, convert, action, qf, stats, &outrest,
1091 (action & _TD_EOF ? NULL : &inrest)) < 0 || ferror(pbuf)) {
1092 rv = -1; /* XXX Should bail away?! */
1093 break;
1096 if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
1097 linelen = 0;
1098 if(eof || inrest.l == 0)
1099 action |= _TD_EOF;
1100 eof = eof ? TRU1 : TRUM1;
1101 goto joutln;
1103 action &= ~_TD_EOF;
1105 /* TODO HACK: when sending to the display we yet get fooled if a message
1106 * TODO doesn't end in a newline, because of our input/output 1:1.
1107 * TODO This should be handled automatically by a display filter, then */
1108 if(rv >= 0 && !qf->qf_nl_last &&
1109 (action == SEND_TODISP || action == SEND_TODISP_ALL))
1110 rv = quoteflt_push(qf, "\n", 1);
1112 quoteflt_flush(qf);
1114 if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
1115 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1116 fflush(pbuf);
1117 really_rewind(pbuf);
1118 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1119 goto jpipe_for_real;
1122 if (pbuf == qbuf)
1123 safe_signal(SIGPIPE, __sendp_opipe);
1125 if (outrest.s != NULL)
1126 free(outrest.s);
1127 if (inrest.s != NULL)
1128 free(inrest.s);
1130 if (pbuf != origobuf) {
1131 qf->qf_pfix_len = save_qf_pfix_len;
1132 stats = save_stats;
1136 jend:
1137 if (pbuf != qbuf) {
1138 safe_signal(SIGPIPE, SIG_IGN);
1139 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1140 safe_signal(SIGPIPE, oldpipe);
1141 if (rv >= 0 && qbuf != NULL && qbuf != obuf)
1142 pipecpy(qbuf, obuf, origobuf, qf, stats);
1144 #ifdef HAVE_ICONV
1145 if (iconvd != (iconv_t)-1)
1146 n_iconv_close(iconvd);
1147 #endif
1148 jleave:
1149 NYD_LEAVE;
1150 return rv;
1153 static FILE *
1154 newfile(struct mimepart *ip, bool_t volatile *ispipe)
1156 struct str in, out;
1157 char *f;
1158 FILE *fp;
1159 NYD_ENTER;
1161 f = ip->m_filename;
1162 *ispipe = FAL0;
1164 if (f != NULL && f != (char*)-1) {
1165 in.s = f;
1166 in.l = strlen(f);
1167 makeprint(&in, &out);
1168 out.l = delctrl(out.s, out.l);
1169 f = savestrbuf(out.s, out.l);
1170 free(out.s);
1173 /* In interactive mode, let user perform all kind of expansions as desired,
1174 * and offer |SHELL-SPEC pipe targets, too */
1175 if (n_psonce & n_PSO_INTERACTIVE) {
1176 struct str prompt;
1177 struct n_string shou, *shoup;
1178 char *f2, *f3;
1180 shoup = n_string_creat_auto(&shou);
1182 /* TODO Generic function which asks for filename.
1183 * TODO If the current part is the first textpart the target
1184 * TODO is implicit from outer `write' etc! */
1185 /* I18N: Filename input prompt with file type indication */
1186 str_concat_csvl(&prompt, _("Enter filename for part "),
1187 (ip->m_partstring != NULL ? ip->m_partstring : n_qm),
1188 " (", ip->m_ct_type_plain, "): ", NULL);
1189 jgetname:
1190 f2 = n_go_input_cp(n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_HIST_ADD,
1191 prompt.s, ((f != (char*)-1 && f != NULL)
1192 ? n_shexp_quote_cp(f, FAL0) : NULL));
1193 if(f2 != NULL){
1194 in.s = n_UNCONST(f2);
1195 in.l = UIZ_MAX;
1196 if((n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
1197 n_SHEXP_PARSE_TRIMSPACE | n_SHEXP_PARSE_LOG |
1198 n_SHEXP_PARSE_IGNORE_EMPTY), shoup, &in, NULL
1199 ) & (n_SHEXP_STATE_STOP |
1200 n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)
1201 ) != (n_SHEXP_STATE_STOP | n_SHEXP_STATE_OUTPUT))
1202 goto jgetname;
1203 f2 = n_string_cp(shoup);
1205 if (f2 == NULL || *f2 == '\0') {
1206 if (n_poption & n_PO_D_V)
1207 n_err(_("... skipping this\n"));
1208 n_string_gut(shoup);
1209 fp = NULL;
1210 goto jleave;
1213 if (*f2 == '|')
1214 /* Pipes are expanded by the shell */
1215 f = f2;
1216 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
1217 /* (Error message written by fexpand()) */
1218 goto jgetname;
1219 else
1220 f = f3;
1222 n_string_gut(shoup);
1225 if (f == NULL || f == (char*)-1 || *f == '\0')
1226 fp = NULL;
1227 else if (n_psonce & n_PSO_INTERACTIVE) {
1228 if (*f == '|') {
1229 fp = Popen(&f[1], "w", ok_vlook(SHELL), NULL, 1);
1230 if (!(*ispipe = (fp != NULL)))
1231 n_perr(f, 0);
1232 } else if ((fp = Fopen(f, "w")) == NULL)
1233 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
1234 } else {
1235 /* Be very picky in non-interactive mode: actively disallow pipes,
1236 * prevent directory separators, and any filename member that would
1237 * become expanded by the shell if the name would be echo(1)ed */
1238 if(anyof(f, "/" n_SHEXP_MAGIC_PATH_CHARS)){
1239 char c;
1241 for(out.s = salloc((strlen(f) * 3) +1), out.l = 0; (c = *f++) != '\0';)
1242 if(strchr("/" n_SHEXP_MAGIC_PATH_CHARS, c)){
1243 out.s[out.l++] = '%';
1244 n_c_to_hex_base16(&out.s[out.l], c);
1245 out.l += 2;
1246 }else
1247 out.s[out.l++] = c;
1248 out.s[out.l] = '\0';
1249 f = out.s;
1252 /* Avoid overwriting of existing files */
1253 while((fp = Fopen(f, "wx")) == NULL){
1254 int e;
1256 if((e = n_err_no) != n_ERR_EXIST){
1257 n_err(_("Cannot open %s: %s\n"),
1258 n_shexp_quote_cp(f, FAL0), n_err_to_doc(e));
1259 break;
1262 if(ip->m_partstring != NULL)
1263 f = savecatsep(f, '#', ip->m_partstring);
1264 else
1265 f = savecat(f, "#.");
1268 jleave:
1269 NYD_LEAVE;
1270 return fp;
1273 static void
1274 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1275 ui64_t *stats)
1277 char *line = NULL; /* TODO line pool */
1278 size_t linesize = 0, linelen, cnt;
1279 ssize_t all_sz, sz;
1280 NYD_ENTER;
1282 fflush(pipebuf);
1283 rewind(pipebuf);
1284 cnt = (size_t)fsize(pipebuf);
1285 all_sz = 0;
1287 quoteflt_reset(qf, outbuf);
1288 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1289 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1290 break;
1291 all_sz += sz;
1293 if ((sz = quoteflt_flush(qf)) > 0)
1294 all_sz += sz;
1295 if (line)
1296 free(line);
1298 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1299 *stats += all_sz;
1300 Fclose(pipebuf);
1301 NYD_LEAVE;
1304 static void
1305 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1306 ui64_t *stats)
1308 char statout[3], *cp = statout;
1309 NYD_ENTER;
1311 if (mp->m_flag & MREAD)
1312 *cp++ = 'R';
1313 if (!(mp->m_flag & MNEW))
1314 *cp++ = 'O';
1315 *cp = 0;
1316 if (statout[0]) {
1317 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1318 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1319 if (i > 0 && stats != NULL)
1320 *stats += i;
1322 NYD_LEAVE;
1325 static void
1326 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1327 ui64_t *stats)
1329 char xstatout[4];
1330 char *xp = xstatout;
1331 NYD_ENTER;
1333 if (mp->m_flag & MFLAGGED)
1334 *xp++ = 'F';
1335 if (mp->m_flag & MANSWERED)
1336 *xp++ = 'A';
1337 if (mp->m_flag & MDRAFTED)
1338 *xp++ = 'T';
1339 *xp = 0;
1340 if (xstatout[0]) {
1341 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1342 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1343 if (i > 0 && stats != NULL)
1344 *stats += i;
1346 NYD_LEAVE;
1349 static void
1350 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1352 char const *froma, *date, *nl;
1353 int i;
1354 NYD_ENTER;
1356 if (ip != NULL && ip->m_from != NULL) {
1357 froma = ip->m_from;
1358 date = fakedate(ip->m_time);
1359 nl = "\n";
1360 } else {
1361 froma = ok_vlook(LOGNAME);
1362 date = time_current.tc_ctime;
1363 nl = n_empty;
1366 n_COLOUR(
1367 if(n_COLOUR_IS_ACTIVE())
1368 n_colour_put(n_COLOUR_ID_VIEW_FROM_, NULL);
1370 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1371 n_COLOUR(
1372 if(n_COLOUR_IS_ACTIVE())
1373 n_colour_reset();
1375 if (i > 0 && stats != NULL)
1376 *stats += i;
1377 NYD_LEAVE;
1380 FL int
1381 sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp,
1382 char const *prefix, enum sendaction action, ui64_t *stats)
1384 struct n_sigman linedat_protect;
1385 struct quoteflt qf;
1386 FILE *ibuf;
1387 enum mime_parse_flags mpf;
1388 struct mimepart *ip;
1389 size_t linesize, cnt, sz, i;
1390 char *linedat;
1391 int rv, c;
1392 NYD_ENTER;
1394 time_current_update(&time_current, TRU1);
1395 rv = -1;
1396 linedat = NULL;
1397 linesize = 0;
1398 quoteflt_init(&qf, prefix);
1400 n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
1401 case 0:
1402 break;
1403 default:
1404 goto jleave;
1407 if (mp == dot && action != SEND_TOSRCH)
1408 n_pstate |= n_PS_DID_PRINT_DOT;
1409 if (stats != NULL)
1410 *stats = 0;
1412 /* First line is the From_ line, so no headers there to worry about */
1413 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1414 goto jleave;
1416 cnt = mp->m_size;
1417 sz = 0;
1419 bool_t nozap;
1420 char const *cpre = n_empty, *csuf = n_empty;
1422 #ifdef HAVE_COLOUR
1423 if(n_COLOUR_IS_ACTIVE()){
1424 struct n_colour_pen *cpen;
1425 struct str const *sp;
1427 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
1428 if((sp = n_colour_pen_to_str(cpen)) != NULL){
1429 cpre = sp->s;
1430 sp = n_colour_reset_to_str();
1431 if(sp != NULL)
1432 csuf = sp->s;
1435 #endif
1437 nozap = (doitp != n_IGNORE_ALL && doitp != n_IGNORE_FWD &&
1438 action != SEND_RFC822 &&
1439 !n_ignore_is_ign(doitp, "from_", sizeof("from_") -1));
1440 if (mp->m_flag & MNOFROM) {
1441 if (nozap)
1442 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1443 cpre, (int)qf.qf_pfix_len,
1444 (qf.qf_pfix_len != 0 ? qf.qf_pfix : n_empty), fakefrom(mp),
1445 fakedate(mp->m_time), csuf);
1446 } else if (nozap) {
1447 if (qf.qf_pfix_len > 0) {
1448 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1449 if (i != qf.qf_pfix_len)
1450 goto jleave;
1451 sz += i;
1453 #ifdef HAVE_COLOUR
1454 if(*cpre != '\0'){
1455 fputs(cpre, obuf);
1456 cpre = (char const*)0x1;
1458 #endif
1460 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1461 #ifdef HAVE_COLOUR
1462 if(c == '\n' && *csuf != '\0'){
1463 cpre = (char const*)0x1;
1464 fputs(csuf, obuf);
1466 #endif
1467 putc(c, obuf);
1468 ++sz;
1469 --cnt;
1470 if (c == '\n')
1471 break;
1474 #ifdef HAVE_COLOUR
1475 if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
1476 fputs(csuf, obuf);
1477 #endif
1478 } else {
1479 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1480 --cnt;
1481 if (c == '\n')
1482 break;
1486 if (sz > 0 && stats != NULL)
1487 *stats += sz;
1489 mpf = MIME_PARSE_NONE;
1490 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1491 mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT;
1492 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1493 goto jleave;
1495 rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
1496 stats, 0);
1498 n_sigman_cleanup_ping(&linedat_protect);
1499 jleave:
1500 quoteflt_destroy(&qf);
1501 if(linedat != NULL)
1502 free(linedat);
1503 NYD_LEAVE;
1504 n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
1505 return rv;
1508 /* s-it-mode */