make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / send.c
blob4e654bcc0adf4f096d7ef474afdec75741e02502
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
7 */
8 /*
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
14 * are met:
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
34 * SUCH DAMAGE.
36 #undef n_FILE
37 #define n_FILE send
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
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,
60 struct str *inrest);
62 /* Simply (!) print out a LF */
63 static bool_t a_send_out_nl(FILE *fp, ui64_t *stats);
65 /* SIGPIPE handler */
66 static void _send_onpipe(int signo);
68 /* Send one part */
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);
89 static void
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)
93 char buf[64];
94 struct str ti, to;
95 bool_t want_ct, needsep;
96 struct str const *cpre, *csuf;
97 char const *cp;
98 NYD2_ENTER;
100 cpre = csuf = NULL;
101 #ifdef HAVE_COLOUR
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();
109 #endif
111 /* Take care of "99.99", i.e., 5 */
112 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
113 cp = n_qm;
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 */
119 if (cpre != NULL)
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);
128 needsep = FAL0;
130 if((cp = mpp->m_ct_type_usr_ovwr) != NULL){
131 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
132 want_ct = TRU1;
133 }else if((want_ct = n_ignore_is_ign(doitp,
134 "content-type", sizeof("content-type") -1)))
135 cp = mpp->m_ct_type_plain;
136 if (want_ct &&
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);
144 cp = x;
145 to.l = al + i;
147 if(cp != NULL){
148 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
149 needsep = TRU1;
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))){
156 if(needsep)
157 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
158 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
159 cp = "qu.-pr.";
160 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
161 needsep = TRU1;
164 if (want_ct && mpp->m_multipart == NULL/* TODO */ &&
165 (cp = mpp->m_charset) != NULL) {
166 if(needsep)
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);
171 needsep = !needsep;
172 _out(&" --]"[needsep], 4 - needsep,
173 obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
174 if (csuf != 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);
178 /* */
179 if (mpp->m_content_info & CI_MIME_ERRORS) {
180 if (cpre != NULL)
181 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
182 NULL, NULL);
183 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
185 ti.l = strlen(ti.s = n_UNCONST(_("Defective MIME structure")));
186 makeprint(&ti, &to);
187 to.l = delctrl(to.s, to.l);
188 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
189 n_free(to.s);
191 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
192 if (csuf != NULL)
193 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
194 NULL, NULL);
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') {
201 if (cpre != NULL)
202 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
203 NULL, NULL);
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);
209 n_free(to.s);
211 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
212 if (csuf != NULL)
213 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
214 NULL, NULL);
215 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
218 /* Filename */
219 if (n_ignore_is_ign(doitp, "content-disposition", 19) &&
220 mpp->m_filename != NULL && *mpp->m_filename != '\0') {
221 if (cpre != NULL)
222 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
223 NULL, NULL);
224 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
226 ti.l = strlen(ti.s = mpp->m_filename);
227 makeprint(&ti, &to);
228 to.l = delctrl(to.s, to.l);
229 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
230 n_free(to.s);
232 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
233 if (csuf != NULL)
234 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
235 NULL, NULL);
236 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
238 NYD2_LEAVE;
241 static FILE *
242 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
243 char const *tmpname, int term_infd)
245 struct str s;
246 char const *env_addon[9 +8/*v15*/], *cp, *sh;
247 size_t i;
248 FILE *rbuf;
249 NYD_ENTER;
251 rbuf = *qbuf;
253 if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
254 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
255 NULL) {
256 n_perr(_("tmpfile"), 0);
257 *qbuf = rbuf;
261 if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
262 union {int (*ptf)(void); char const *sh;} u;
264 fflush(*qbuf);
265 if (*qbuf != n_stdout) /* xxx never? v15: it'll be a filter anyway */
266 fflush(n_stdout);
268 u.ptf = mhp->mh_ptf;
269 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
270 goto jerror;
271 goto jleave;
274 i = 0;
276 /* MAILX_FILENAME */
277 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
278 cp = n_empty;
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,
288 NULL)->s;
289 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME_GENERATED", "=", cp,/*v15*/
290 NULL)->s;
292 /* MAILX_CONTENT{,_EVIDENCE} */
293 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
294 cp = n_empty;
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,
301 NULL)->s;
302 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT_EVIDENCE", "=", cp,/* v15 */
303 NULL)->s;
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!!! */
321 env_addon[i] = NULL;
322 sh = ok_vlook(SHELL);
324 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
325 sigset_t nset;
326 int pid;
328 sigemptyset(&nset);
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;
332 } else {
333 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
334 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
335 jerror:
336 if (rbuf == NULL)
337 n_err(_("Cannot run MIME type handler: %s: %s\n"),
338 mhp->mh_msg, n_err_to_doc(n_err_no));
339 else {
340 fflush(*qbuf);
341 if (*qbuf != n_stdout)
342 fflush(n_stdout);
345 jleave:
346 NYD_LEAVE;
347 return rbuf;
350 n_INLINE ssize_t
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,
353 struct str *inrest)
355 ssize_t sz = 0, n;
356 int flags;
357 NYD_ENTER;
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?!? */
362 /* C99 */{
363 bool_t from_;
365 if((action == SEND_MBOX || action == SEND_DECRYPT) &&
366 (from_ = is_head(buf, len, TRU1))){
367 if(from_ != TRUM1 || ok_blook(mbox_rfc4155)){
368 putc('>', fp);
369 ++sz;
374 flags = ((int)action & _TD_EOF);
375 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)
381 ? TD_ISPR | TD_ICONV
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);
386 if (n < 0)
387 sz = n;
388 else if (n > 0) {
389 sz += n;
390 if (stats != NULL)
391 *stats += sz;
393 NYD_LEAVE;
394 return sz;
397 static bool_t
398 a_send_out_nl(FILE *fp, ui64_t *stats){
399 struct quoteflt *qf;
400 bool_t rv;
401 NYD2_ENTER;
403 quoteflt_reset(qf = quoteflt_dummy(), fp);
404 rv = (_out("\n", 1, fp, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL) > 0);
405 quoteflt_flush(qf);
406 NYD2_LEAVE;
407 return rv;
410 static void
411 _send_onpipe(int signo)
413 NYD_X; /* Signal handler */
414 n_UNUSED(signo);
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;
421 static void
422 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
424 NYD_X; /* Signal handler */
425 __sendp_sig = sig;
426 siglongjmp(__sendp_actjmp, 1);
429 static int
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)
435 int volatile rv = 0;
436 struct mime_handler mh;
437 struct str outrest, inrest;
438 char *cp;
439 char const * volatile tmpname = NULL;
440 size_t linelen, cnt;
441 int volatile dostat, term_infd;
442 int c;
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;
448 NYD_ENTER;
450 n_UNINIT(term_infd, 0);
451 n_UNINIT(cnt, 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);
458 #endif
460 if (ip->m_mimecontent == MIME_PKCS7) {
461 if (ip->m_multipart &&
462 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
463 goto jheaders_skip;
466 dostat = 0;
467 if (level == 0 && action != SEND_TODISP_PARTS) {
468 if (doitp != NULL) {
469 if (!n_ignore_is_ign(doitp, "status", 6))
470 dostat |= 1;
471 if (!n_ignore_is_ign(doitp, "x-status", 8))
472 dostat |= 2;
473 } else
474 dostat = 3;
477 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
478 rv = -1;
479 goto jleave;
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)
486 dostat |= 4;
488 cnt = ip->m_size;
490 if (ip->m_mimecontent == MIME_DISCARD)
491 goto jheaders_skip;
493 if (!(ip->m_flag & MNOFROM))
494 while (cnt && (c = getc(ibuf)) != EOF) {
495 cnt--;
496 if (c == '\n')
497 break;
499 convert = (dostat & 4) ? CONV_FROMHDR : CONV_NONE;
501 /* Work the headers */
502 /* C99 */{
503 struct n_string hl, *hlp;
504 size_t lineno = 0;
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;){
512 size_t lcnt;
514 lcnt = cnt;
515 if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0) == NULL)
516 break;
517 ++lineno;
518 if (linelen == 0 || (cp = *linedat)[0] == '\n')
519 /* If line is blank, we've reached end of headers */
520 break;
521 if(cp[linelen - 1] == '\n'){
522 cp[--linelen] = '\0';
523 if(linelen == 0)
524 break;
527 /* Are we in a header? */
528 if(hlp->s_len > 0){
529 if(!blankchar(*cp)){
530 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
531 cnt = lcnt;
532 goto jhdrput;
534 goto jhdrpush;
535 }else{
536 /* Pick up the header field if we have one */
537 while((c = *cp) != ':' && !spacechar(c) && c != '\0')
538 ++cp;
539 for(;;){
540 if(!spacechar(c) || c == '\0')
541 break;
542 c = *++cp;
544 if(c != ':'){
545 /* That won't work with MIME when saving etc., before v15 */
546 if (lineno != 1)
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"));
551 if(level != 0)
552 dostat &= ~(1 | 2);
553 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
554 cnt = lcnt;
555 break;
558 cp = *linedat;
559 jhdrpush:
560 if(!(dostat & 4)){
561 hlp = n_string_push_buf(hlp, cp, (ui32_t)linelen);
562 hlp = n_string_push_c(hlp, '\n');
563 }else{
564 bool_t lblank, isblank;
566 for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
567 char c8;
569 c8 = *cp;
570 if(!(isblank = blankchar(c8)) || !lblank){
571 if((lblank = isblank))
572 c8 = ' ';
573 hlp = n_string_push_c(hlp, c8);
577 continue;
580 jhdrput:
581 /* If it is an ignored header, skip it */
582 *(cp = memchr(hlp->s_dat, ':', hlp->s_len)) = '\0';
583 /* C99 */{
584 size_t i;
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)))
594 goto jhdrtrunc;
597 /* Dump it */
598 n_COLOUR(
599 if(n_COLOUR_IS_ACTIVE())
600 n_colour_put(n_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
602 *cp = ':';
603 _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NULL,NULL);
604 n_COLOUR(
605 if(n_COLOUR_IS_ACTIVE())
606 n_colour_reset();
608 if(dostat & 4)
609 _out("\n", sizeof("\n") -1, obuf, convert, action, qf, stats,
610 NULL,NULL);
611 /*see below hany = TRU1;*/
613 jhdrtrunc:
614 hlp = n_string_trunc(hlp, 0);
616 hstop = TRU1;
617 if(hlp->s_len > 0)
618 goto jhdrput;
620 /* We've reached end of headers, so eventually force out status: field and
621 * note that we are no longer in header fields */
622 if(dostat & 1){
623 statusput(zmp, obuf, qf, stats);
624 /*see below hany = TRU1;*/
626 if(dostat & 2){
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);
632 } /* C99 */
634 quoteflt_flush(qf);
636 if(ferror(obuf)){
637 rv = -1;
638 goto jleave;
641 jheaders_skip:
642 memset(&mh, 0, sizeof mh);
644 switch (ip->m_mimecontent) {
645 case MIME_822:
646 switch (action) {
647 case SEND_TODISP_PARTS:
648 goto jleave;
649 case SEND_TODISP:
650 case SEND_TODISP_ALL:
651 case SEND_QUOTE:
652 case SEND_QUOTE_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)
658 *stats += i;
660 put_from_(obuf, ip->m_multipart, stats);
662 /* FALLTHRU */
663 case SEND_TOSRCH:
664 case SEND_DECRYPT:
665 goto jmulti;
666 case SEND_TOFILE:
667 case SEND_TOPIPE:
668 put_from_(obuf, ip->m_multipart, stats);
669 /* FALLTHRU */
670 case SEND_MBOX:
671 case SEND_RFC822:
672 case SEND_SHOW:
673 break;
675 break;
676 case MIME_TEXT_HTML:
677 case MIME_TEXT:
678 case MIME_TEXT_PLAIN:
679 switch (action) {
680 case SEND_TODISP:
681 case SEND_TODISP_ALL:
682 case SEND_TODISP_PARTS:
683 case SEND_QUOTE:
684 case SEND_QUOTE_ALL:
685 switch (n_mimetype_handler(&mh, ip, action)) {
686 case MIME_HDL_NULL:
687 if(action != SEND_TODISP_PARTS)
688 break;
689 /* FALLTHRU */
690 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
691 if(mh.mh_msg.l > 0)
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 */
695 goto jleave;
696 case MIME_HDL_CMD:
697 if(action == SEND_TODISP_PARTS &&
698 (mh.mh_flags & MIME_HDL_COPIOUSOUTPUT))
699 goto jleave;
700 break;
701 case MIME_HDL_TEXT:
702 case MIME_HDL_PTF:
703 if(action == SEND_TODISP_PARTS)
704 goto jleave;
705 break;
706 default:
707 break;
709 /* FALLTRHU */
710 default:
711 break;
713 break;
714 case MIME_DISCARD:
715 if (action != SEND_DECRYPT)
716 goto jleave;
717 break;
718 case MIME_PKCS7:
719 if (action != SEND_MBOX && action != SEND_RFC822 &&
720 action != SEND_SHOW && ip->m_multipart != NULL)
721 goto jmulti;
722 /* FALLTHRU */
723 default:
724 switch (action) {
725 case SEND_TODISP:
726 case SEND_TODISP_ALL:
727 case SEND_TODISP_PARTS:
728 case SEND_QUOTE:
729 case SEND_QUOTE_ALL:
730 switch (n_mimetype_handler(&mh, ip, action)) {
731 default:
732 case MIME_HDL_NULL:
733 if (action != SEND_TODISP && action != SEND_TODISP_ALL &&
734 (level != 0 || cnt))
735 goto jleave;
736 /* FALLTHRU */
737 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
738 if(mh.mh_msg.l > 0)
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 */
742 goto jleave;
743 case MIME_HDL_CMD:
744 if(action == SEND_TODISP_PARTS){
745 if(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)
746 goto jleave;
747 else{
748 _print_part_info(obuf, ip, doitp, level, qf, stats);
749 if(!getapproval(_("Run MIME handler for this part?"), FAL0))
750 goto jleave;
753 break;
754 case MIME_HDL_TEXT:
755 case MIME_HDL_PTF:
756 if(action == SEND_TODISP_PARTS)
757 goto jleave;
758 break;
760 break;
761 case SEND_TOFILE:
762 case SEND_TOPIPE:
763 case SEND_TOSRCH:
764 case SEND_DECRYPT:
765 case SEND_MBOX:
766 case SEND_RFC822:
767 case SEND_SHOW:
768 break;
770 break;
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 */
779 struct mpstack {
780 struct mpstack *outer;
781 struct mimepart *mp;
782 } outermost, * volatile curr, * volatile mpsp;
783 bool_t volatile neednl, hadpart;
784 struct n_sigman smalter;
786 (curr = &outermost)->outer = NULL;
787 curr->mp = ip;
788 neednl = hadpart = FAL0;
790 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
791 case 0:
792 break;
793 default:
794 rv = -1;
795 goto jalter_leave;
798 for (np = ip->m_multipart;;) {
799 jalter_redo:
800 for (; np != NULL; np = np->m_nextpart) {
801 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
802 if (neednl)
803 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats,
804 NULL, NULL);
805 _print_part_info(obuf, np, doitp, level, qf, stats);
807 neednl = TRU1;
809 switch (np->m_mimecontent) {
810 case MIME_ALTERNATIVE:
811 case MIME_RELATED:
812 case MIME_DIGEST:
813 case MIME_SIGNED:
814 case MIME_ENCRYPTED:
815 case MIME_MULTI:
816 mpsp = n_autorec_alloc(sizeof *mpsp);
817 mpsp->outer = curr;
818 mpsp->mp = np->m_multipart;
819 curr->mp = np;
820 curr = mpsp;
821 np = mpsp->mp;
822 neednl = FAL0;
823 goto jalter_redo;
824 default:
825 if (hadpart)
826 break;
827 switch (n_mimetype_handler(&mh, np, action)) {
828 default:
829 mh.mh_flags = MIME_HDL_NULL;
830 continue; /* break; break; */
831 case MIME_HDL_CMD:
832 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)){
833 mh.mh_flags = MIME_HDL_NULL;
834 continue; /* break; break; */
836 /* FALLTHRU */
837 case MIME_HDL_PTF:
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) ==
846 MIME_HDL_TEXT)
847 break;
849 if (x != NULL)
850 continue; /* break; break; */
851 goto jalter_plain;
853 /* FALLTHRU */
854 case MIME_HDL_TEXT:
855 break;
857 /* FALLTHRU */
858 case MIME_TEXT_PLAIN:
859 if (hadpart)
860 break;
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)) {
870 case MIME_HDL_CMD:
871 if(!(mhx.mh_flags & MIME_HDL_COPIOUSOUTPUT))
872 continue;
873 /* FALLTHRU */
874 case MIME_HDL_PTF:
875 break;
876 default:
877 continue;
879 break;
881 if (x != NULL)
882 continue; /* break; break; */
884 jalter_plain:
885 quoteflt_flush(qf);
886 if (action == SEND_QUOTE && hadpart)
887 /* XXX (void)*/a_send_out_nl(obuf, stats);
888 hadpart = TRU1;
889 neednl = FAL0;
890 rv = sendpart(zmp, np, obuf, doitp, qf, action,
891 linedat, linesize, stats, level + 1);
892 quoteflt_reset(qf, origobuf);
894 if (rv < 0)
895 curr = &outermost; /* Cause overall loop termination */
896 break;
900 mpsp = curr->outer;
901 if (mpsp == NULL)
902 break;
903 curr = mpsp;
904 np = curr->mp->m_nextpart;
906 jalter_leave:
907 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
908 goto jleave;
910 /* FALLTHRU */
911 case MIME_RELATED:
912 case MIME_DIGEST:
913 case MIME_SIGNED:
914 case MIME_ENCRYPTED:
915 case MIME_MULTI:
916 switch (action) {
917 case SEND_TODISP:
918 case SEND_TODISP_ALL:
919 case SEND_TODISP_PARTS:
920 case SEND_QUOTE:
921 case SEND_QUOTE_ALL:
922 case SEND_TOFILE:
923 case SEND_TOPIPE:
924 case SEND_TOSRCH:
925 case SEND_DECRYPT:
926 jmulti:
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,
934 NULL,NULL);
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)
941 continue;
943 ispipe = FAL0;
944 switch (action) {
945 case SEND_TOFILE:
946 if (np->m_partstring &&
947 np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
948 break;
949 stats = NULL;
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)
957 continue;
958 } else if ((obuf = newfile(np, &ispipe)) == NULL)
959 continue;
960 if (!ispipe)
961 break;
962 if (sigsetjmp(_send_pipejmp, 1)) {
963 rv = -1;
964 goto jpipe_close;
966 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
967 break;
968 case SEND_TODISP:
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)
976 break;
977 _print_part_info(obuf, np, doitp, level, qf, stats);
978 break;
979 case SEND_TODISP_PARTS:
980 case SEND_QUOTE:
981 case SEND_QUOTE_ALL:
982 case SEND_MBOX:
983 case SEND_RFC822:
984 case SEND_SHOW:
985 case SEND_TOSRCH:
986 case SEND_DECRYPT:
987 case SEND_TOPIPE:
988 break;
991 quoteflt_flush(qf);
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,
996 stats, level+1) < 0)
997 rv = -1;
998 quoteflt_reset(qf, origobuf);
1000 if (action == SEND_QUOTE) {
1001 if (ip->m_mimecontent != MIME_RELATED)
1002 break;
1004 if (action == SEND_TOFILE && obuf != origobuf) {
1005 if (!ispipe)
1006 Fclose(obuf);
1007 else {
1008 jpipe_close:
1009 safe_signal(SIGPIPE, SIG_IGN);
1010 Pclose(obuf, TRU1);
1011 safe_signal(SIGPIPE, oldpipe);
1015 goto jleave;
1016 case SEND_MBOX:
1017 case SEND_RFC822:
1018 case SEND_SHOW:
1019 break;
1021 break;
1024 /* Copy out message body */
1025 if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
1026 --cnt;
1027 switch (ip->m_mime_enc) {
1028 case MIMEE_BIN:
1029 case MIMEE_7B:
1030 case MIMEE_8B:
1031 convert = CONV_NONE;
1032 break;
1033 case MIMEE_QP:
1034 convert = CONV_FROMQP;
1035 break;
1036 case MIMEE_B64:
1037 switch (ip->m_mimecontent) {
1038 case MIME_TEXT:
1039 case MIME_TEXT_PLAIN:
1040 case MIME_TEXT_HTML:
1041 convert = CONV_FROMB64_T;
1042 break;
1043 default:
1044 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1045 case MIME_HDL_TEXT:
1046 case MIME_HDL_PTF:
1047 convert = CONV_FROMB64_T;
1048 break;
1049 default:
1050 convert = CONV_FROMB64;
1051 break;
1053 break;
1055 break;
1056 default:
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 */
1062 #ifdef HAVE_ICONV
1063 if (iconvd != (iconv_t)-1) {
1064 n_iconv_close(iconvd);
1065 iconvd = (iconv_t)-1;
1067 #endif
1069 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1070 action == SEND_RFC822 || action == SEND_SHOW)
1071 convert = CONV_NONE;
1072 #ifdef HAVE_ICONV
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)) {
1082 char const *tcs;
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;*/
1094 #endif
1096 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1097 case MIME_HDL_CMD:
1098 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT) &&
1099 action != SEND_TODISP_PARTS)
1100 goto jmhp_default;
1101 /* FALLTHRU */
1102 case MIME_HDL_PTF:
1103 tmpname = NULL;
1104 qbuf = obuf;
1106 term_infd = n_CHILD_FD_PASS;
1107 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
1108 enum oflags of;
1110 of = OF_RDWR | OF_REGISTER;
1111 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
1112 term_infd = 0;
1113 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1114 of |= OF_UNLINK;
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"),
1120 of)) == NULL)
1121 goto jesend;
1123 if (mh.mh_flags & MIME_HDL_TMPF) {
1124 tmpname = savestr(cp);
1125 Ftmp_free(&cp);
1128 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1129 if (term_infd == 0)
1130 term_infd = fileno(pbuf);
1131 goto jsend;
1135 jpipe_for_real:
1136 pbuf = _pipefile(&mh, ip, n_UNVOLATILE(&qbuf), tmpname, term_infd);
1137 if (pbuf == NULL) {
1138 jesend:
1139 pbuf = qbuf = NULL;
1140 rv = -1;
1141 goto jend;
1142 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1143 pbuf = qbuf = NULL;
1144 goto jend;
1146 tmpname = NULL;
1147 action = SEND_TOPIPE;
1148 if (pbuf != qbuf) {
1149 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1150 if (sigsetjmp(_send_pipejmp, 1))
1151 goto jend;
1153 break;
1155 default:
1156 jmhp_default:
1157 mh.mh_flags = MIME_HDL_NULL;
1158 pbuf = qbuf = obuf;
1159 break;
1162 jsend:
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) */
1170 stats = NULL;
1172 eof = FAL0;
1173 outrest.s = inrest.s = NULL;
1174 outrest.l = inrest.l = 0;
1176 if (pbuf == qbuf) {
1177 __sendp_sig = 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)
1182 n_free(outrest.s);
1183 if (inrest.s != NULL)
1184 n_free(inrest.s);
1185 #ifdef HAVE_ICONV
1186 if (iconvd != (iconv_t)-1)
1187 n_iconv_close(iconvd);
1188 #endif
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)) {
1198 joutln:
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?! */
1202 break;
1205 if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
1206 linelen = 0;
1207 if(eof || inrest.l == 0)
1208 action |= _TD_EOF;
1209 eof = eof ? TRU1 : TRUM1;
1210 goto joutln;
1212 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1213 action &= ~_TD_EOF;
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);
1223 quoteflt_flush(qf);
1225 if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
1226 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1227 fflush(pbuf);
1228 really_rewind(pbuf);
1229 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1230 goto jpipe_for_real;
1233 if (pbuf == qbuf)
1234 safe_signal(SIGPIPE, __sendp_opipe);
1236 if (outrest.s != NULL)
1237 n_free(outrest.s);
1238 if (inrest.s != NULL)
1239 n_free(inrest.s);
1241 if (pbuf != origobuf) {
1242 qf->qf_bypass = save_qf_bypass;
1243 stats = save_stats;
1247 jend:
1248 if (pbuf != qbuf) {
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);
1255 #ifdef HAVE_ICONV
1256 if (iconvd != (iconv_t)-1)
1257 n_iconv_close(iconvd);
1258 #endif
1259 jleave:
1260 NYD_LEAVE;
1261 return rv;
1264 static FILE *
1265 newfile(struct mimepart *ip, bool_t volatile *ispipe)
1267 struct str in, out;
1268 char *f;
1269 FILE *fp;
1270 NYD_ENTER;
1272 f = ip->m_filename;
1273 *ispipe = FAL0;
1275 if (f != NULL && f != (char*)-1) {
1276 in.s = f;
1277 in.l = strlen(f);
1278 makeprint(&in, &out);
1279 out.l = delctrl(out.s, out.l);
1280 f = savestrbuf(out.s, out.l);
1281 n_free(out.s);
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) {
1287 struct str prompt;
1288 struct n_string shou, *shoup;
1289 char *f2, *f3;
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);
1300 jgetname:
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));
1304 if(f2 != NULL){
1305 in.s = n_UNCONST(f2);
1306 in.l = UIZ_MAX;
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),
1310 shoup, &in, NULL
1311 ) & (n_SHEXP_STATE_STOP |
1312 n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)
1313 ) != (n_SHEXP_STATE_STOP | n_SHEXP_STATE_OUTPUT))
1314 goto jgetname;
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);
1321 fp = NULL;
1322 goto jleave;
1325 if (*f2 == '|')
1326 /* Pipes are expanded by the shell */
1327 f = f2;
1328 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
1329 /* (Error message written by fexpand()) */
1330 goto jgetname;
1331 else
1332 f = f3;
1334 n_string_gut(shoup);
1337 if (f == NULL || f == (char*)-1 || *f == '\0')
1338 fp = NULL;
1339 else if (n_psonce & n_PSO_INTERACTIVE) {
1340 if (*f == '|') {
1341 fp = Popen(&f[1], "w", ok_vlook(SHELL), NULL, 1);
1342 if (!(*ispipe = (fp != NULL)))
1343 n_perr(f, 0);
1344 } else if ((fp = Fopen(f, "w")) == NULL)
1345 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
1346 } else {
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)){
1351 char c;
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);
1358 out.l += 2;
1359 }else
1360 out.s[out.l++] = c;
1361 out.s[out.l] = '\0';
1362 f = out.s;
1365 /* Avoid overwriting of existing files */
1366 while((fp = Fopen(f, "wx")) == NULL){
1367 int e;
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));
1372 break;
1375 if(ip->m_partstring != NULL)
1376 f = savecatsep(f, '#', ip->m_partstring);
1377 else
1378 f = savecat(f, "#.");
1381 jleave:
1382 NYD_LEAVE;
1383 return fp;
1386 static void
1387 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1388 ui64_t *stats)
1390 char *line = NULL; /* TODO line pool */
1391 size_t linesize = 0, linelen, cnt;
1392 ssize_t all_sz, sz;
1393 NYD_ENTER;
1395 fflush(pipebuf);
1396 rewind(pipebuf);
1397 cnt = (size_t)fsize(pipebuf);
1398 all_sz = 0;
1400 quoteflt_reset(qf, outbuf);
1401 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1402 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1403 break;
1404 all_sz += sz;
1406 if ((sz = quoteflt_flush(qf)) > 0)
1407 all_sz += sz;
1408 if (line)
1409 n_free(line);
1411 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1412 *stats += all_sz;
1413 Fclose(pipebuf);
1414 NYD_LEAVE;
1417 static void
1418 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1419 ui64_t *stats)
1421 char statout[3], *cp = statout;
1422 NYD_ENTER;
1424 if (mp->m_flag & MREAD)
1425 *cp++ = 'R';
1426 if (!(mp->m_flag & MNEW))
1427 *cp++ = 'O';
1428 *cp = 0;
1429 if (statout[0]) {
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)
1433 *stats += i;
1435 NYD_LEAVE;
1438 static void
1439 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1440 ui64_t *stats)
1442 char xstatout[4];
1443 char *xp = xstatout;
1444 NYD_ENTER;
1446 if (mp->m_flag & MFLAGGED)
1447 *xp++ = 'F';
1448 if (mp->m_flag & MANSWERED)
1449 *xp++ = 'A';
1450 if (mp->m_flag & MDRAFTED)
1451 *xp++ = 'T';
1452 *xp = 0;
1453 if (xstatout[0]) {
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)
1457 *stats += i;
1459 NYD_LEAVE;
1462 static void
1463 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1465 char const *froma, *date, *nl;
1466 int i;
1467 NYD_ENTER;
1469 if (ip != NULL && ip->m_from != NULL) {
1470 froma = ip->m_from;
1471 date = n_time_ctime(ip->m_time, NULL);
1472 nl = "\n";
1473 } else {
1474 froma = ok_vlook(LOGNAME);
1475 date = time_current.tc_ctime;
1476 nl = n_empty;
1479 n_COLOUR(
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);
1484 n_COLOUR(
1485 if(n_COLOUR_IS_ACTIVE())
1486 n_colour_reset();
1488 if (i > 0 && stats != NULL)
1489 *stats += i;
1490 NYD_LEAVE;
1493 FL int
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;
1498 struct quoteflt qf;
1499 FILE *ibuf;
1500 enum mime_parse_flags mpf;
1501 struct mimepart *ip;
1502 size_t linesize, cnt, sz, i;
1503 char *linedat;
1504 int rv, c;
1505 NYD_ENTER;
1507 time_current_update(&time_current, TRU1);
1508 rv = -1;
1509 linedat = NULL;
1510 linesize = 0;
1511 quoteflt_init(&qf, prefix, (prefix == NULL));
1513 n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
1514 case 0:
1515 break;
1516 default:
1517 goto jleave;
1520 if (mp == dot && action != SEND_TOSRCH)
1521 n_pstate |= n_PS_DID_PRINT_DOT;
1522 if (stats != NULL)
1523 *stats = 0;
1525 /* First line is the From_ line, so no headers there to worry about */
1526 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1527 goto jleave;
1529 cnt = mp->m_size;
1530 sz = 0;
1532 bool_t nozap;
1533 char const *cpre = n_empty, *csuf = n_empty;
1535 #ifdef HAVE_COLOUR
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){
1542 cpre = sp->s;
1543 sp = n_colour_reset_to_str();
1544 if(sp != NULL)
1545 csuf = sp->s;
1548 #endif
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) {
1554 if (nozap)
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);
1559 } else if (nozap) {
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)
1563 goto jleave;
1564 sz += i;
1566 #ifdef HAVE_COLOUR
1567 if(*cpre != '\0'){
1568 fputs(cpre, obuf);
1569 cpre = (char const*)0x1;
1571 #endif
1573 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1574 #ifdef HAVE_COLOUR
1575 if(c == '\n' && *csuf != '\0'){
1576 cpre = (char const*)0x1;
1577 fputs(csuf, obuf);
1579 #endif
1580 putc(c, obuf);
1581 ++sz;
1582 --cnt;
1583 if (c == '\n')
1584 break;
1587 #ifdef HAVE_COLOUR
1588 if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
1589 fputs(csuf, obuf);
1590 #endif
1591 } else {
1592 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1593 --cnt;
1594 if (c == '\n')
1595 break;
1599 if (sz > 0 && stats != NULL)
1600 *stats += sz;
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)
1609 goto jleave;
1611 rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
1612 stats, 0);
1614 n_sigman_cleanup_ping(&linedat_protect);
1615 jleave:
1616 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1617 quoteflt_destroy(&qf);
1618 if(linedat != NULL)
1619 n_free(linedat);
1620 NYD_LEAVE;
1621 n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
1622 return rv;
1625 /* s-it-mode */