Fix [1785be65] from 2017-03-xx
[s-mailx.git] / send.c
blobb6a3d7f4e1d31561607ec265e72ce478a3a42887
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 */
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 n_INLINE 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 (!asccasecmp(cp, "7bit") ||
150 n_ignore_is_ign(doitp, "content-transfer-encoding",
151 sizeof("content-transfer-encoding") -1))){
152 if(needsep)
153 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
154 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
155 cp = "qu.-pr.";
156 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
157 needsep = TRU1;
160 if (want_ct && mpp->m_multipart == NULL/* TODO */ &&
161 (cp = mpp->m_charset) != NULL) {
162 if(needsep)
163 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
164 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
167 needsep = !needsep;
168 _out(&" --]"[needsep], 4 - needsep,
169 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, NULL,NULL);
172 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
174 /* */
175 if (mpp->m_content_info & CI_MIME_ERRORS) {
176 if (cpre != NULL)
177 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
178 NULL, NULL);
179 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
181 ti.l = strlen(ti.s = n_UNCONST(_("Defective MIME structure")));
182 makeprint(&ti, &to);
183 to.l = delctrl(to.s, to.l);
184 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
185 free(to.s);
187 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
188 if (csuf != NULL)
189 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
190 NULL, NULL);
191 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
194 /* Content-Description */
195 if (n_ignore_is_ign(doitp, "content-description", 19) &&
196 (cp = mpp->m_content_description) != NULL && *cp != '\0') {
197 if (cpre != NULL)
198 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
199 NULL, NULL);
200 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
202 ti.l = strlen(ti.s = n_UNCONST(mpp->m_content_description));
203 mime_fromhdr(&ti, &to, TD_ISPR | TD_ICONV);
204 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
205 free(to.s);
207 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
208 if (csuf != NULL)
209 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
210 NULL, NULL);
211 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
214 /* Filename */
215 if (n_ignore_is_ign(doitp, "content-disposition", 19) &&
216 mpp->m_filename != NULL && *mpp->m_filename != '\0') {
217 if (cpre != NULL)
218 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
219 NULL, NULL);
220 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
222 ti.l = strlen(ti.s = mpp->m_filename);
223 makeprint(&ti, &to);
224 to.l = delctrl(to.s, to.l);
225 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
226 free(to.s);
228 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
229 if (csuf != NULL)
230 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
231 NULL, NULL);
232 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
234 NYD2_LEAVE;
237 static FILE *
238 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
239 char const *tmpname, int term_infd)
241 struct str s;
242 char const *env_addon[9 +8/*v15*/], *cp, *sh;
243 size_t i;
244 FILE *rbuf;
245 NYD_ENTER;
247 rbuf = *qbuf;
249 if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
250 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
251 NULL) {
252 n_perr(_("tmpfile"), 0);
253 *qbuf = rbuf;
257 if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
258 union {int (*ptf)(void); char const *sh;} u;
260 fflush(*qbuf);
261 if (*qbuf != n_stdout) /* xxx never? v15: it'll be a filter anyway */
262 fflush(n_stdout);
264 u.ptf = mhp->mh_ptf;
265 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
266 goto jerror;
267 goto jleave;
270 i = 0;
272 /* MAILX_FILENAME */
273 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
274 cp = n_empty;
275 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME, "=", cp, NULL)->s;
276 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME", "=", cp, NULL)->s;/*v15*/
278 /* MAILX_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
279 * TODO a file wherever he wants! *Do* create a zero-size temporary file
280 * TODO and give *that* path as MAILX_FILENAME_TEMPORARY, clean it up once
281 * TODO the pipe returns? Like this we *can* verify path/name issues! */
282 cp = n_random_create_cp(n_MIN(NAME_MAX / 4, 16), NULL);
283 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME_GENERATED, "=", cp,
284 NULL)->s;
285 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME_GENERATED", "=", cp,/*v15*/
286 NULL)->s;
288 /* MAILX_CONTENT{,_EVIDENCE} */
289 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
290 cp = n_empty;
291 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT, "=", cp, NULL)->s;
292 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT", "=", cp, NULL)->s;/*v15*/
294 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
295 cp = mpp->m_ct_type_usr_ovwr;
296 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT_EVIDENCE, "=", cp,
297 NULL)->s;
298 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT_EVIDENCE", "=", cp,/* v15 */
299 NULL)->s;
301 /* message/external-body, access-type=url */
302 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_EXTERNAL_BODY_URL, "=",
303 ((mpp != NULL && (cp = mpp->m_external_body_url) != NULL
304 ) ? cp : n_empty), NULL)->s;
306 /* MAILX_FILENAME_TEMPORARY? */
307 if (tmpname != NULL) {
308 env_addon[i++] = str_concat_csvl(&s,
309 n_PIPEENV_FILENAME_TEMPORARY, "=", tmpname, NULL)->s;
310 env_addon[i++] = str_concat_csvl(&s,
311 "NAIL_FILENAME_TEMPORARY", "=", tmpname, NULL)->s;/* v15 */
314 /* TODO we should include header information, especially From:, so
315 * TODO that same-origin can be tested for e.g. external-body!!! */
317 env_addon[i] = NULL;
318 sh = ok_vlook(SHELL);
320 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
321 sigset_t nset;
322 int pid;
324 sigemptyset(&nset);
325 pid = n_child_run(sh, &nset, term_infd, n_CHILD_FD_PASS, "-c",
326 mhp->mh_shell_cmd, NULL, env_addon, NULL);
327 rbuf = (pid < 0) ? NULL : (FILE*)-1;
328 } else {
329 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
330 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
331 jerror:
332 if (rbuf == NULL)
333 n_err(_("Cannot run MIME type handler: %s: %s\n"),
334 mhp->mh_msg, n_err_to_doc(n_err_no));
335 else {
336 fflush(*qbuf);
337 if (*qbuf != n_stdout)
338 fflush(n_stdout);
341 jleave:
342 NYD_LEAVE;
343 return rbuf;
346 n_INLINE ssize_t
347 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
348 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *outrest,
349 struct str *inrest)
351 ssize_t sz = 0, n;
352 int flags;
353 NYD_ENTER;
355 /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
356 * TODO subclass should detect such From_ cases and either reencode the part
357 * TODO in question, or perform From_ quoting as necessary!?!?!? How?!? */
358 /* C99 */{
359 bool_t from_;
361 if((action == SEND_MBOX || action == SEND_DECRYPT) &&
362 (from_ = is_head(buf, len, TRU1))){
363 if(from_ != TRUM1 || ok_blook(mbox_rfc4155)){
364 putc('>', fp);
365 ++sz;
370 flags = ((int)action & _TD_EOF);
371 action &= ~_TD_EOF;
372 n = mime_write(buf, len, fp,
373 action == SEND_MBOX ? CONV_NONE : convert,
374 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
375 action == SEND_TODISP_PARTS ||
376 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
377 ? TD_ISPR | TD_ICONV
378 : (action == SEND_TOSRCH || action == SEND_TOPIPE ||
379 action == SEND_TOFILE)
380 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
381 qf, outrest, inrest);
382 if (n < 0)
383 sz = n;
384 else if (n > 0) {
385 sz += n;
386 if (stats != NULL)
387 *stats += sz;
389 NYD_LEAVE;
390 return sz;
393 static void
394 _send_onpipe(int signo)
396 NYD_X; /* Signal handler */
397 n_UNUSED(signo);
398 siglongjmp(_send_pipejmp, 1);
401 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
402 static int __sendp_sig; /* TODO someday.. */
403 static sighandler_type __sendp_opipe;
404 static void
405 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
407 NYD_X; /* Signal handler */
408 __sendp_sig = sig;
409 siglongjmp(__sendp_actjmp, 1);
412 static int
413 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
414 struct n_ignore const *doitp, struct quoteflt *qf,
415 enum sendaction volatile action,
416 char **linedat, size_t *linesize, ui64_t * volatile stats, int level)
418 int volatile rv = 0;
419 struct mime_handler mh;
420 struct str outrest, inrest;
421 char *cp;
422 char const * volatile tmpname = NULL;
423 size_t linelen, cnt;
424 int volatile dostat, term_infd;
425 int c;
426 struct mimepart * volatile np;
427 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
428 * volatile qbuf = obuf, *origobuf = obuf;
429 enum conversion volatile convert;
430 sighandler_type volatile oldpipe = SIG_DFL;
431 NYD_ENTER;
433 n_UNINIT(term_infd, 0);
434 n_UNINIT(cnt, 0);
436 quoteflt_reset(qf, obuf);
438 #if 0 /* TODO PART_INFO should be displayed here!! search PART_INFO */
439 if(ip->m_mimecontent != MIME_DISCARD && level > 0)
440 _print_part_info(obuf, ip, doitp, level, qf, stats);
441 #endif
443 if (ip->m_mimecontent == MIME_PKCS7) {
444 if (ip->m_multipart &&
445 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
446 goto jheaders_skip;
449 dostat = 0;
450 if (level == 0 && action != SEND_TODISP_PARTS) {
451 if (doitp != NULL) {
452 if (!n_ignore_is_ign(doitp, "status", 6))
453 dostat |= 1;
454 if (!n_ignore_is_ign(doitp, "x-status", 8))
455 dostat |= 2;
456 } else
457 dostat = 3;
460 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
461 rv = -1;
462 goto jleave;
465 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
466 action == SEND_TODISP_PARTS ||
467 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
468 action == SEND_TOSRCH)
469 dostat |= 4;
471 cnt = ip->m_size;
473 if (ip->m_mimecontent == MIME_DISCARD)
474 goto jheaders_skip;
476 if (!(ip->m_flag & MNOFROM))
477 while (cnt && (c = getc(ibuf)) != EOF) {
478 cnt--;
479 if (c == '\n')
480 break;
482 convert = (dostat & 4) ? CONV_FROMHDR : CONV_NONE;
484 /* Work the headers */
485 /* C99 */{
486 struct n_string hl, *hlp;
487 size_t lineno = 0;
488 bool_t hstop/*see below, hany*/;
490 hlp = n_string_creat_auto(&hl); /* TODO pool [or, v15: filter!] */
491 /* Reserve three lines, still not enough for references and DKIM etc. */
492 hlp = n_string_reserve(hlp, n_MAX(MIME_LINELEN, MIME_LINELEN_RFC2047) * 3);
494 for(hstop = /*see below hany =*/ FAL0; !hstop;){
495 size_t lcnt;
497 lcnt = cnt;
498 if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0) == NULL)
499 break;
500 ++lineno;
501 if (linelen == 0 || (cp = *linedat)[0] == '\n')
502 /* If line is blank, we've reached end of headers */
503 break;
504 if(cp[linelen - 1] == '\n'){
505 cp[--linelen] = '\0';
506 if(linelen == 0)
507 break;
510 /* Are we in a header? */
511 if(hlp->s_len > 0){
512 if(!blankchar(*cp)){
513 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
514 cnt = lcnt;
515 goto jhdrput;
517 goto jhdrpush;
518 }else{
519 /* Pick up the header field if we have one */
520 while((c = *cp) != ':' && !spacechar(c) && c != '\0')
521 ++cp;
522 for(;;){
523 if(!spacechar(c) || c == '\0')
524 break;
525 c = *++cp;
527 if(c != ':'){
528 /* That won't work with MIME when saving etc., before v15 */
529 if (lineno != 1)
530 /* XXX This disturbs, and may happen multiple times, and we
531 * XXX cannot heal it for multipart except for display <v15 */
532 n_err(_("Malformed message: headers and body not separated "
533 "(with empty line)\n"));
534 if(level != 0)
535 dostat &= ~(1 | 2);
536 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
537 cnt = lcnt;
538 break;
541 cp = *linedat;
542 jhdrpush:
543 if(!(dostat & 4)){
544 hlp = n_string_push_buf(hlp, cp, (ui32_t)linelen);
545 hlp = n_string_push_c(hlp, '\n');
546 }else{
547 bool_t lblank, isblank;
549 for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
550 char c8;
552 c8 = *cp;
553 if(!(isblank = blankchar(c8)) || !lblank){
554 if((lblank = isblank))
555 c8 = ' ';
556 hlp = n_string_push_c(hlp, c8);
560 continue;
563 jhdrput:
564 /* If it is an ignored header, skip it */
565 *(cp = memchr(hlp->s_dat, ':', hlp->s_len)) = '\0';
566 /* C99 */{
567 size_t i;
569 i = PTR2SIZE(cp - hlp->s_dat);
570 if((doitp != NULL && n_ignore_is_ign(doitp, hlp->s_dat, i)) ||
571 !asccasecmp(hlp->s_dat, "status") ||
572 !asccasecmp(hlp->s_dat, "x-status") ||
573 (action == SEND_MBOX &&
574 (!asccasecmp(hlp->s_dat, "content-length") ||
575 !asccasecmp(hlp->s_dat, "lines")) &&
576 !ok_blook(keep_content_length)))
577 goto jhdrtrunc;
580 /* Dump it */
581 n_COLOUR(
582 if(n_COLOUR_IS_ACTIVE())
583 n_colour_put(n_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
585 *cp = ':';
586 _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NULL,NULL);
587 n_COLOUR(
588 if(n_COLOUR_IS_ACTIVE())
589 n_colour_reset();
591 if(dostat & 4)
592 _out("\n", sizeof("\n") -1, obuf, convert, action, qf, stats,
593 NULL,NULL);
594 /*see below hany = TRU1;*/
596 jhdrtrunc:
597 hlp = n_string_trunc(hlp, 0);
599 hstop = TRU1;
600 if(hlp->s_len > 0)
601 goto jhdrput;
603 /* We've reached end of headers, so eventually force out status: field and
604 * note that we are no longer in header fields */
605 if(dostat & 1){
606 statusput(zmp, obuf, qf, stats);
607 /*see below hany = TRU1;*/
609 if(dostat & 2){
610 xstatusput(zmp, obuf, qf, stats);
611 /*see below hany = TRU1;*/
613 if(/* TODO PART_INFO hany && */ doitp != n_IGNORE_ALL)
614 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
615 } /* C99 */
617 quoteflt_flush(qf);
619 if(ferror(obuf)){
620 rv = -1;
621 goto jleave;
624 jheaders_skip:
625 memset(&mh, 0, sizeof mh);
627 switch (ip->m_mimecontent) {
628 case MIME_822:
629 switch (action) {
630 case SEND_TODISP_PARTS:
631 goto jleave;
632 case SEND_TODISP:
633 case SEND_TODISP_ALL:
634 case SEND_QUOTE:
635 case SEND_QUOTE_ALL:
636 if (ok_blook(rfc822_body_from_)) {
637 if (qf->qf_pfix_len > 0) {
638 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
639 qf->qf_pfix_len, obuf);
640 if (i == qf->qf_pfix_len && stats != NULL)
641 *stats += i;
643 put_from_(obuf, ip->m_multipart, stats);
645 /* FALLTHRU */
646 case SEND_TOSRCH:
647 case SEND_DECRYPT:
648 goto jmulti;
649 case SEND_TOFILE:
650 case SEND_TOPIPE:
651 put_from_(obuf, ip->m_multipart, stats);
652 /* FALLTHRU */
653 case SEND_MBOX:
654 case SEND_RFC822:
655 case SEND_SHOW:
656 break;
658 break;
659 case MIME_TEXT_HTML:
660 case MIME_TEXT:
661 case MIME_TEXT_PLAIN:
662 switch (action) {
663 case SEND_TODISP:
664 case SEND_TODISP_ALL:
665 case SEND_TODISP_PARTS:
666 case SEND_QUOTE:
667 case SEND_QUOTE_ALL:
668 switch (n_mimetype_handler(&mh, ip, action)) {
669 case MIME_HDL_NULL:
670 if(action != SEND_TODISP_PARTS)
671 break;
672 /* FALLTHRU */
673 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
674 if(mh.mh_msg.l > 0)
675 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
676 qf, stats, NULL, NULL);
677 /* We would print this as plain text, so better force going home */
678 goto jleave;
679 case MIME_HDL_CMD:
680 if(action == SEND_TODISP_PARTS &&
681 (mh.mh_flags & MIME_HDL_COPIOUSOUTPUT))
682 goto jleave;
683 break;
684 case MIME_HDL_TEXT:
685 case MIME_HDL_PTF:
686 if(action == SEND_TODISP_PARTS)
687 goto jleave;
688 break;
689 default:
690 break;
692 /* FALLTRHU */
693 default:
694 break;
696 break;
697 case MIME_DISCARD:
698 if (action != SEND_DECRYPT)
699 goto jleave;
700 break;
701 case MIME_PKCS7:
702 if (action != SEND_MBOX && action != SEND_RFC822 &&
703 action != SEND_SHOW && ip->m_multipart != NULL)
704 goto jmulti;
705 /* FALLTHRU */
706 default:
707 switch (action) {
708 case SEND_TODISP:
709 case SEND_TODISP_ALL:
710 case SEND_TODISP_PARTS:
711 case SEND_QUOTE:
712 case SEND_QUOTE_ALL:
713 switch (n_mimetype_handler(&mh, ip, action)) {
714 default:
715 case MIME_HDL_NULL:
716 if (action != SEND_TODISP && action != SEND_TODISP_ALL &&
717 (level != 0 || cnt))
718 goto jleave;
719 /* FALLTHRU */
720 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
721 if(mh.mh_msg.l > 0)
722 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
723 qf, stats, NULL, NULL);
724 /* We would print this as plain text, so better force going home */
725 goto jleave;
726 case MIME_HDL_CMD:
727 if(action == SEND_TODISP_PARTS){
728 if(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)
729 goto jleave;
730 else{
731 _print_part_info(obuf, ip, doitp, level, qf, stats);
732 if(!getapproval(_("Run MIME handler for this part?"), FAL0))
733 goto jleave;
736 break;
737 case MIME_HDL_TEXT:
738 case MIME_HDL_PTF:
739 if(action == SEND_TODISP_PARTS)
740 goto jleave;
741 break;
743 break;
744 case SEND_TOFILE:
745 case SEND_TOPIPE:
746 case SEND_TOSRCH:
747 case SEND_DECRYPT:
748 case SEND_MBOX:
749 case SEND_RFC822:
750 case SEND_SHOW:
751 break;
753 break;
754 case MIME_ALTERNATIVE:
755 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
756 !ok_blook(print_alternatives)) {
757 /* XXX This (a) should not remain (b) should be own fun
758 * TODO (despite the fact that v15 will do this completely differently
759 * TODO by having an action-specific "manager" that will traverse the
760 * TODO parsed MIME tree and decide for each part whether it'll be
761 * TODO displayed or not *before* we walk the tree for doing action */
762 struct mpstack {
763 struct mpstack *outer;
764 struct mimepart *mp;
765 } outermost, * volatile curr, * volatile mpsp;
766 bool_t volatile neednl, hadpart;
767 struct n_sigman smalter;
769 (curr = &outermost)->outer = NULL;
770 curr->mp = ip;
771 neednl = hadpart = FAL0;
773 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
774 case 0:
775 break;
776 default:
777 rv = -1;
778 goto jalter_leave;
781 for (np = ip->m_multipart;;) {
782 jalter_redo:
783 for (; np != NULL; np = np->m_nextpart) {
784 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
785 if (neednl)
786 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats,
787 NULL, NULL);
788 _print_part_info(obuf, np, doitp, level, qf, stats);
790 neednl = TRU1;
792 switch (np->m_mimecontent) {
793 case MIME_ALTERNATIVE:
794 case MIME_RELATED:
795 case MIME_DIGEST:
796 case MIME_SIGNED:
797 case MIME_ENCRYPTED:
798 case MIME_MULTI:
799 mpsp = salloc(sizeof *mpsp);
800 mpsp->outer = curr;
801 mpsp->mp = np->m_multipart;
802 curr->mp = np;
803 curr = mpsp;
804 np = mpsp->mp;
805 neednl = FAL0;
806 goto jalter_redo;
807 default:
808 if (hadpart)
809 break;
810 switch (n_mimetype_handler(&mh, np, action)) {
811 default:
812 mh.mh_flags = MIME_HDL_NULL;
813 continue; /* break; break; */
814 case MIME_HDL_CMD:
815 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)){
816 mh.mh_flags = MIME_HDL_NULL;
817 continue; /* break; break; */
819 /* FALLTHRU */
820 case MIME_HDL_PTF:
821 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
822 struct mimepart *x = np;
824 while ((x = x->m_nextpart) != NULL) {
825 struct mime_handler mhx;
827 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
828 n_mimetype_handler(&mhx, x, action) ==
829 MIME_HDL_TEXT)
830 break;
832 if (x != NULL)
833 continue; /* break; break; */
834 goto jalter_plain;
836 /* FALLTHRU */
837 case MIME_HDL_TEXT:
838 break;
840 /* FALLTHRU */
841 case MIME_TEXT_PLAIN:
842 if (hadpart)
843 break;
844 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
845 struct mimepart *x = np;
847 /* TODO twice TODO, we should dive into /related and
848 * TODO check whether that has rich parts! */
849 while ((x = x->m_nextpart) != NULL) {
850 struct mime_handler mhx;
852 switch (n_mimetype_handler(&mhx, x, action)) {
853 case MIME_HDL_CMD:
854 if(!(mhx.mh_flags & MIME_HDL_COPIOUSOUTPUT))
855 continue;
856 /* FALLTHRU */
857 case MIME_HDL_PTF:
858 break;
859 default:
860 continue;
862 break;
864 if (x != NULL)
865 continue; /* break; break; */
867 jalter_plain:
868 quoteflt_flush(qf);
869 if (action == SEND_QUOTE && hadpart) {
870 struct quoteflt *dummy = quoteflt_dummy();
871 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
872 NULL,NULL);
873 quoteflt_flush(dummy);
875 hadpart = TRU1;
876 neednl = FAL0;
877 rv = sendpart(zmp, np, obuf, doitp, qf, action,
878 linedat, linesize, stats, level + 1);
879 quoteflt_reset(qf, origobuf);
881 if (rv < 0)
882 curr = &outermost; /* Cause overall loop termination */
883 break;
887 mpsp = curr->outer;
888 if (mpsp == NULL)
889 break;
890 curr = mpsp;
891 np = curr->mp->m_nextpart;
893 jalter_leave:
894 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
895 goto jleave;
897 /* FALLTHRU */
898 case MIME_RELATED:
899 case MIME_DIGEST:
900 case MIME_SIGNED:
901 case MIME_ENCRYPTED:
902 case MIME_MULTI:
903 switch (action) {
904 case SEND_TODISP:
905 case SEND_TODISP_ALL:
906 case SEND_TODISP_PARTS:
907 case SEND_QUOTE:
908 case SEND_QUOTE_ALL:
909 case SEND_TOFILE:
910 case SEND_TOPIPE:
911 case SEND_TOSRCH:
912 case SEND_DECRYPT:
913 jmulti:
914 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
915 ip->m_multipart != NULL &&
916 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
917 ip->m_multipart->m_nextpart == NULL) {
918 char const *x = _("[Missing multipart boundary - use `show' "
919 "to display the raw message]\n");
920 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
921 NULL,NULL);
924 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
925 bool_t volatile ispipe;
927 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
928 continue;
930 ispipe = FAL0;
931 switch (action) {
932 case SEND_TOFILE:
933 if (np->m_partstring &&
934 np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
935 break;
936 stats = NULL;
937 /* TODO Always open multipart on /dev/null, it's a hack to be
938 * TODO able to dive into that structure, and still better
939 * TODO than asking the user for something stupid.
940 * TODO oh, wait, we did ask for a filename for this MIME mail,
941 * TODO and that outer container is useless anyway ;-P */
942 if (np->m_multipart != NULL && np->m_mimecontent != MIME_822) {
943 if ((obuf = Fopen(n_path_devnull, "w")) == NULL)
944 continue;
945 } else if ((obuf = newfile(np, &ispipe)) == NULL)
946 continue;
947 if (!ispipe)
948 break;
949 if (sigsetjmp(_send_pipejmp, 1)) {
950 rv = -1;
951 goto jpipe_close;
953 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
954 break;
955 case SEND_TODISP:
956 case SEND_TODISP_ALL:
957 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
958 ip->m_mimecontent != MIME_RELATED &&
959 ip->m_mimecontent != MIME_DIGEST &&
960 ip->m_mimecontent != MIME_SIGNED &&
961 ip->m_mimecontent != MIME_ENCRYPTED &&
962 ip->m_mimecontent != MIME_MULTI)
963 break;
964 _print_part_info(obuf, np, doitp, level, qf, stats);
965 break;
966 case SEND_TODISP_PARTS:
967 case SEND_QUOTE:
968 case SEND_QUOTE_ALL:
969 case SEND_MBOX:
970 case SEND_RFC822:
971 case SEND_SHOW:
972 case SEND_TOSRCH:
973 case SEND_DECRYPT:
974 case SEND_TOPIPE:
975 break;
978 quoteflt_flush(qf);
979 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
980 np->m_multipart == NULL && ip->m_parent != NULL) {
981 struct quoteflt *dummy = quoteflt_dummy();
982 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
983 NULL,NULL);
984 quoteflt_flush(dummy);
986 if (sendpart(zmp, np, obuf, doitp, qf, action, linedat, linesize,
987 stats, level+1) < 0)
988 rv = -1;
989 quoteflt_reset(qf, origobuf);
991 if (action == SEND_QUOTE) {
992 if (ip->m_mimecontent != MIME_RELATED)
993 break;
995 if (action == SEND_TOFILE && obuf != origobuf) {
996 if (!ispipe)
997 Fclose(obuf);
998 else {
999 jpipe_close:
1000 safe_signal(SIGPIPE, SIG_IGN);
1001 Pclose(obuf, TRU1);
1002 safe_signal(SIGPIPE, oldpipe);
1006 goto jleave;
1007 case SEND_MBOX:
1008 case SEND_RFC822:
1009 case SEND_SHOW:
1010 break;
1012 break;
1015 /* Copy out message body */
1016 if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
1017 --cnt;
1018 switch (ip->m_mime_enc) {
1019 case MIMEE_BIN:
1020 case MIMEE_7B:
1021 case MIMEE_8B:
1022 convert = CONV_NONE;
1023 break;
1024 case MIMEE_QP:
1025 convert = CONV_FROMQP;
1026 break;
1027 case MIMEE_B64:
1028 switch (ip->m_mimecontent) {
1029 case MIME_TEXT:
1030 case MIME_TEXT_PLAIN:
1031 case MIME_TEXT_HTML:
1032 convert = CONV_FROMB64_T;
1033 break;
1034 default:
1035 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1036 case MIME_HDL_TEXT:
1037 case MIME_HDL_PTF:
1038 convert = CONV_FROMB64_T;
1039 break;
1040 default:
1041 convert = CONV_FROMB64;
1042 break;
1044 break;
1046 break;
1047 default:
1048 convert = CONV_NONE;
1051 /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
1052 * TODO cannot mess things up misusing outrest as line buffer */
1053 #ifdef HAVE_ICONV
1054 if (iconvd != (iconv_t)-1) {
1055 n_iconv_close(iconvd);
1056 iconvd = (iconv_t)-1;
1058 #endif
1060 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1061 action == SEND_RFC822 || action == SEND_SHOW)
1062 convert = CONV_NONE;
1063 #ifdef HAVE_ICONV
1064 else if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
1065 action == SEND_TODISP_PARTS ||
1066 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
1067 action == SEND_TOSRCH || action == SEND_TOFILE) &&
1068 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
1069 ip->m_mimecontent == MIME_TEXT_HTML ||
1070 ip->m_mimecontent == MIME_TEXT ||
1071 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
1072 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
1073 char const *tcs;
1075 tcs = ok_vlook(ttycharset);
1076 if (asccasecmp(tcs, ip->m_charset) &&
1077 asccasecmp(ok_vlook(charset_7bit), ip->m_charset)) {
1078 iconvd = n_iconv_open(tcs, ip->m_charset);
1079 if (iconvd == (iconv_t)-1 && n_err_no == n_ERR_INVAL) {
1080 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
1081 /*rv = 1; goto jleave;*/
1085 #endif
1087 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1088 case MIME_HDL_CMD:
1089 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT) &&
1090 action != SEND_TODISP_PARTS)
1091 goto jmhp_default;
1092 /* FALLTHRU */
1093 case MIME_HDL_PTF:
1094 tmpname = NULL;
1095 qbuf = obuf;
1097 term_infd = n_CHILD_FD_PASS;
1098 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
1099 enum oflags of;
1101 of = OF_RDWR | OF_REGISTER;
1102 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
1103 term_infd = 0;
1104 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1105 of |= OF_UNLINK;
1106 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
1107 of |= OF_REGISTER_UNLINK;
1109 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
1110 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
1111 of)) == NULL)
1112 goto jesend;
1114 if (mh.mh_flags & MIME_HDL_TMPF) {
1115 tmpname = savestr(cp);
1116 Ftmp_free(&cp);
1119 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1120 if (term_infd == 0)
1121 term_infd = fileno(pbuf);
1122 goto jsend;
1126 jpipe_for_real:
1127 pbuf = _pipefile(&mh, ip, n_UNVOLATILE(&qbuf), tmpname, term_infd);
1128 if (pbuf == NULL) {
1129 jesend:
1130 pbuf = qbuf = NULL;
1131 rv = -1;
1132 goto jend;
1133 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1134 pbuf = qbuf = NULL;
1135 goto jend;
1137 tmpname = NULL;
1138 action = SEND_TOPIPE;
1139 if (pbuf != qbuf) {
1140 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1141 if (sigsetjmp(_send_pipejmp, 1))
1142 goto jend;
1144 break;
1146 default:
1147 jmhp_default:
1148 mh.mh_flags = MIME_HDL_NULL;
1149 pbuf = qbuf = obuf;
1150 break;
1153 jsend:
1155 bool_t volatile eof;
1156 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1157 ui64_t *save_stats = stats;
1159 if (pbuf != origobuf) {
1160 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1161 stats = NULL;
1163 eof = FAL0;
1164 outrest.s = inrest.s = NULL;
1165 outrest.l = inrest.l = 0;
1167 if (pbuf == qbuf) {
1168 __sendp_sig = 0;
1169 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1170 if (sigsetjmp(__sendp_actjmp, 1)) {
1171 n_pstate &= ~n_PS_BASE64_STRIP_CR;/* (but protected by outer sigman) */
1172 if (outrest.s != NULL)
1173 free(outrest.s);
1174 if (inrest.s != NULL)
1175 free(inrest.s);
1176 #ifdef HAVE_ICONV
1177 if (iconvd != (iconv_t)-1)
1178 n_iconv_close(iconvd);
1179 #endif
1180 safe_signal(SIGPIPE, __sendp_opipe);
1181 n_raise(__sendp_sig);
1185 quoteflt_reset(qf, pbuf);
1186 if((dostat & 4) && pbuf == origobuf) /* TODO */
1187 n_pstate |= n_PS_BASE64_STRIP_CR;
1188 while (!eof && fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0)) {
1189 joutln:
1190 if (_out(*linedat, linelen, pbuf, convert, action, qf, stats, &outrest,
1191 (action & _TD_EOF ? NULL : &inrest)) < 0 || ferror(pbuf)) {
1192 rv = -1; /* XXX Should bail away?! */
1193 break;
1196 if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
1197 linelen = 0;
1198 if(eof || inrest.l == 0)
1199 action |= _TD_EOF;
1200 eof = eof ? TRU1 : TRUM1;
1201 goto joutln;
1203 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1204 action &= ~_TD_EOF;
1206 /* TODO HACK: when sending to the display we yet get fooled if a message
1207 * TODO doesn't end in a newline, because of our input/output 1:1.
1208 * TODO This should be handled automatically by a display filter, then */
1209 if(rv >= 0 && !qf->qf_nl_last &&
1210 (action == SEND_TODISP || action == SEND_TODISP_ALL))
1211 rv = quoteflt_push(qf, "\n", 1);
1213 quoteflt_flush(qf);
1215 if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
1216 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1217 fflush(pbuf);
1218 really_rewind(pbuf);
1219 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1220 goto jpipe_for_real;
1223 if (pbuf == qbuf)
1224 safe_signal(SIGPIPE, __sendp_opipe);
1226 if (outrest.s != NULL)
1227 free(outrest.s);
1228 if (inrest.s != NULL)
1229 free(inrest.s);
1231 if (pbuf != origobuf) {
1232 qf->qf_pfix_len = save_qf_pfix_len;
1233 stats = save_stats;
1237 jend:
1238 if (pbuf != qbuf) {
1239 safe_signal(SIGPIPE, SIG_IGN);
1240 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1241 safe_signal(SIGPIPE, oldpipe);
1242 if (rv >= 0 && qbuf != NULL && qbuf != obuf)
1243 pipecpy(qbuf, obuf, origobuf, qf, stats);
1245 #ifdef HAVE_ICONV
1246 if (iconvd != (iconv_t)-1)
1247 n_iconv_close(iconvd);
1248 #endif
1249 jleave:
1250 NYD_LEAVE;
1251 return rv;
1254 static FILE *
1255 newfile(struct mimepart *ip, bool_t volatile *ispipe)
1257 struct str in, out;
1258 char *f;
1259 FILE *fp;
1260 NYD_ENTER;
1262 f = ip->m_filename;
1263 *ispipe = FAL0;
1265 if (f != NULL && f != (char*)-1) {
1266 in.s = f;
1267 in.l = strlen(f);
1268 makeprint(&in, &out);
1269 out.l = delctrl(out.s, out.l);
1270 f = savestrbuf(out.s, out.l);
1271 free(out.s);
1274 /* In interactive mode, let user perform all kind of expansions as desired,
1275 * and offer |SHELL-SPEC pipe targets, too */
1276 if (n_psonce & n_PSO_INTERACTIVE) {
1277 struct str prompt;
1278 struct n_string shou, *shoup;
1279 char *f2, *f3;
1281 shoup = n_string_creat_auto(&shou);
1283 /* TODO Generic function which asks for filename.
1284 * TODO If the current part is the first textpart the target
1285 * TODO is implicit from outer `write' etc! */
1286 /* I18N: Filename input prompt with file type indication */
1287 str_concat_csvl(&prompt, _("Enter filename for part "),
1288 (ip->m_partstring != NULL ? ip->m_partstring : n_qm),
1289 " (", ip->m_ct_type_plain, "): ", NULL);
1290 jgetname:
1291 f2 = n_go_input_cp(n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_HIST_ADD,
1292 prompt.s, ((f != (char*)-1 && f != NULL)
1293 ? n_shexp_quote_cp(f, FAL0) : NULL));
1294 if(f2 != NULL){
1295 in.s = n_UNCONST(f2);
1296 in.l = UIZ_MAX;
1297 if((n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
1298 n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_TRIM_IFSSPACE |
1299 n_SHEXP_PARSE_LOG | n_SHEXP_PARSE_IGNORE_EMPTY),
1300 shoup, &in, NULL
1301 ) & (n_SHEXP_STATE_STOP |
1302 n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)
1303 ) != (n_SHEXP_STATE_STOP | n_SHEXP_STATE_OUTPUT))
1304 goto jgetname;
1305 f2 = n_string_cp(shoup);
1307 if (f2 == NULL || *f2 == '\0') {
1308 if (n_poption & n_PO_D_V)
1309 n_err(_("... skipping this\n"));
1310 n_string_gut(shoup);
1311 fp = NULL;
1312 goto jleave;
1315 if (*f2 == '|')
1316 /* Pipes are expanded by the shell */
1317 f = f2;
1318 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
1319 /* (Error message written by fexpand()) */
1320 goto jgetname;
1321 else
1322 f = f3;
1324 n_string_gut(shoup);
1327 if (f == NULL || f == (char*)-1 || *f == '\0')
1328 fp = NULL;
1329 else if (n_psonce & n_PSO_INTERACTIVE) {
1330 if (*f == '|') {
1331 fp = Popen(&f[1], "w", ok_vlook(SHELL), NULL, 1);
1332 if (!(*ispipe = (fp != NULL)))
1333 n_perr(f, 0);
1334 } else if ((fp = Fopen(f, "w")) == NULL)
1335 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
1336 } else {
1337 /* Be very picky in non-interactive mode: actively disallow pipes,
1338 * prevent directory separators, and any filename member that would
1339 * become expanded by the shell if the name would be echo(1)ed */
1340 if(n_anyof_cp("/" n_SHEXP_MAGIC_PATH_CHARS, f)){
1341 char c;
1343 for(out.s = salloc((strlen(f) * 3) +1), out.l = 0; (c = *f++) != '\0';)
1344 if(strchr("/" n_SHEXP_MAGIC_PATH_CHARS, c)){
1345 out.s[out.l++] = '%';
1346 n_c_to_hex_base16(&out.s[out.l], c);
1347 out.l += 2;
1348 }else
1349 out.s[out.l++] = c;
1350 out.s[out.l] = '\0';
1351 f = out.s;
1354 /* Avoid overwriting of existing files */
1355 while((fp = Fopen(f, "wx")) == NULL){
1356 int e;
1358 if((e = n_err_no) != n_ERR_EXIST){
1359 n_err(_("Cannot open %s: %s\n"),
1360 n_shexp_quote_cp(f, FAL0), n_err_to_doc(e));
1361 break;
1364 if(ip->m_partstring != NULL)
1365 f = savecatsep(f, '#', ip->m_partstring);
1366 else
1367 f = savecat(f, "#.");
1370 jleave:
1371 NYD_LEAVE;
1372 return fp;
1375 static void
1376 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1377 ui64_t *stats)
1379 char *line = NULL; /* TODO line pool */
1380 size_t linesize = 0, linelen, cnt;
1381 ssize_t all_sz, sz;
1382 NYD_ENTER;
1384 fflush(pipebuf);
1385 rewind(pipebuf);
1386 cnt = (size_t)fsize(pipebuf);
1387 all_sz = 0;
1389 quoteflt_reset(qf, outbuf);
1390 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1391 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1392 break;
1393 all_sz += sz;
1395 if ((sz = quoteflt_flush(qf)) > 0)
1396 all_sz += sz;
1397 if (line)
1398 free(line);
1400 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1401 *stats += all_sz;
1402 Fclose(pipebuf);
1403 NYD_LEAVE;
1406 static void
1407 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1408 ui64_t *stats)
1410 char statout[3], *cp = statout;
1411 NYD_ENTER;
1413 if (mp->m_flag & MREAD)
1414 *cp++ = 'R';
1415 if (!(mp->m_flag & MNEW))
1416 *cp++ = 'O';
1417 *cp = 0;
1418 if (statout[0]) {
1419 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1420 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1421 if (i > 0 && stats != NULL)
1422 *stats += i;
1424 NYD_LEAVE;
1427 static void
1428 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1429 ui64_t *stats)
1431 char xstatout[4];
1432 char *xp = xstatout;
1433 NYD_ENTER;
1435 if (mp->m_flag & MFLAGGED)
1436 *xp++ = 'F';
1437 if (mp->m_flag & MANSWERED)
1438 *xp++ = 'A';
1439 if (mp->m_flag & MDRAFTED)
1440 *xp++ = 'T';
1441 *xp = 0;
1442 if (xstatout[0]) {
1443 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1444 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1445 if (i > 0 && stats != NULL)
1446 *stats += i;
1448 NYD_LEAVE;
1451 static void
1452 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1454 char const *froma, *date, *nl;
1455 int i;
1456 NYD_ENTER;
1458 if (ip != NULL && ip->m_from != NULL) {
1459 froma = ip->m_from;
1460 date = n_time_ctime(ip->m_time, NULL);
1461 nl = "\n";
1462 } else {
1463 froma = ok_vlook(LOGNAME);
1464 date = time_current.tc_ctime;
1465 nl = n_empty;
1468 n_COLOUR(
1469 if(n_COLOUR_IS_ACTIVE())
1470 n_colour_put(n_COLOUR_ID_VIEW_FROM_, NULL);
1472 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1473 n_COLOUR(
1474 if(n_COLOUR_IS_ACTIVE())
1475 n_colour_reset();
1477 if (i > 0 && stats != NULL)
1478 *stats += i;
1479 NYD_LEAVE;
1482 FL int
1483 sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp,
1484 char const *prefix, enum sendaction action, ui64_t *stats)
1486 struct n_sigman linedat_protect;
1487 struct quoteflt qf;
1488 FILE *ibuf;
1489 enum mime_parse_flags mpf;
1490 struct mimepart *ip;
1491 size_t linesize, cnt, sz, i;
1492 char *linedat;
1493 int rv, c;
1494 NYD_ENTER;
1496 time_current_update(&time_current, TRU1);
1497 rv = -1;
1498 linedat = NULL;
1499 linesize = 0;
1500 quoteflt_init(&qf, prefix);
1502 n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
1503 case 0:
1504 break;
1505 default:
1506 goto jleave;
1509 if (mp == dot && action != SEND_TOSRCH)
1510 n_pstate |= n_PS_DID_PRINT_DOT;
1511 if (stats != NULL)
1512 *stats = 0;
1514 /* First line is the From_ line, so no headers there to worry about */
1515 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1516 goto jleave;
1518 cnt = mp->m_size;
1519 sz = 0;
1521 bool_t nozap;
1522 char const *cpre = n_empty, *csuf = n_empty;
1524 #ifdef HAVE_COLOUR
1525 if(n_COLOUR_IS_ACTIVE()){
1526 struct n_colour_pen *cpen;
1527 struct str const *sp;
1529 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
1530 if((sp = n_colour_pen_to_str(cpen)) != NULL){
1531 cpre = sp->s;
1532 sp = n_colour_reset_to_str();
1533 if(sp != NULL)
1534 csuf = sp->s;
1537 #endif
1539 nozap = (doitp != n_IGNORE_ALL && doitp != n_IGNORE_FWD &&
1540 action != SEND_RFC822 &&
1541 !n_ignore_is_ign(doitp, "from_", sizeof("from_") -1));
1542 if (mp->m_flag & MNOFROM) {
1543 if (nozap)
1544 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1545 cpre, (int)qf.qf_pfix_len,
1546 (qf.qf_pfix_len != 0 ? qf.qf_pfix : n_empty), fakefrom(mp),
1547 n_time_ctime(mp->m_time, NULL), csuf);
1548 } else if (nozap) {
1549 if (qf.qf_pfix_len > 0) {
1550 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1551 if (i != qf.qf_pfix_len)
1552 goto jleave;
1553 sz += i;
1555 #ifdef HAVE_COLOUR
1556 if(*cpre != '\0'){
1557 fputs(cpre, obuf);
1558 cpre = (char const*)0x1;
1560 #endif
1562 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1563 #ifdef HAVE_COLOUR
1564 if(c == '\n' && *csuf != '\0'){
1565 cpre = (char const*)0x1;
1566 fputs(csuf, obuf);
1568 #endif
1569 putc(c, obuf);
1570 ++sz;
1571 --cnt;
1572 if (c == '\n')
1573 break;
1576 #ifdef HAVE_COLOUR
1577 if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
1578 fputs(csuf, obuf);
1579 #endif
1580 } else {
1581 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1582 --cnt;
1583 if (c == '\n')
1584 break;
1588 if (sz > 0 && stats != NULL)
1589 *stats += sz;
1591 mpf = MIME_PARSE_NONE;
1592 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1593 mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT;
1594 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
1595 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
1596 mpf |= MIME_PARSE_FOR_USER_CONTEXT;
1597 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1598 goto jleave;
1600 rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
1601 stats, 0);
1603 n_sigman_cleanup_ping(&linedat_protect);
1604 jleave:
1605 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1606 quoteflt_destroy(&qf);
1607 if(linedat != NULL)
1608 free(linedat);
1609 NYD_LEAVE;
1610 n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
1611 return rv;
1614 /* s-it-mode */