cc-test.sh: move MTA creation to a function
[s-mailx.git] / send.c
blob1ca7a3998f7c651bf7753edd6b067669cffa628c
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 /* Simply (!) print out a LF */
62 static bool_t a_send_out_nl(FILE *fp, ui64_t *stats);
64 /* SIGPIPE handler */
65 static void _send_onpipe(int signo);
67 /* Send one part */
68 static int sendpart(struct message *zmp, struct mimepart *ip,
69 FILE *obuf, struct n_ignore const *doitp,
70 struct quoteflt *qf, enum sendaction action,
71 char **linedat, size_t *linesize,
72 ui64_t *stats, int level);
74 /* Get a file for an attachment */
75 static FILE * newfile(struct mimepart *ip, bool_t volatile *ispipe);
77 static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
78 struct quoteflt *qf, ui64_t *stats);
80 /* Output a reasonable looking status field */
81 static void statusput(const struct message *mp, FILE *obuf,
82 struct quoteflt *qf, ui64_t *stats);
83 static void xstatusput(const struct message *mp, FILE *obuf,
84 struct quoteflt *qf, ui64_t *stats);
86 static void put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats);
88 static void
89 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
90 struct n_ignore const *doitp, int level, struct quoteflt *qf, ui64_t *stats)
92 char buf[64];
93 struct str ti, to;
94 bool_t want_ct, needsep;
95 struct str const *cpre, *csuf;
96 char const *cp;
97 NYD2_ENTER;
99 cpre = csuf = NULL;
100 #ifdef HAVE_COLOUR
101 if(n_COLOUR_IS_ACTIVE()){
102 struct n_colour_pen *cpen;
104 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_PARTINFO, NULL);
105 if((cpre = n_colour_pen_to_str(cpen)) != NULL)
106 csuf = n_colour_reset_to_str();
108 #endif
110 /* Take care of "99.99", i.e., 5 */
111 if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
112 cp = n_qm;
113 if (level || (cp[0] != '1' && cp[1] == '\0') || (cp[0] == '1' && /* TODO */
114 cp[1] == '.' && cp[2] != '1')) /* TODO code should not look like so */
115 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
117 /* Part id, content-type, encoding, charset */
118 if (cpre != NULL)
119 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
120 _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
121 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
123 to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
124 (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
125 _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
127 needsep = FAL0;
129 if((cp = mpp->m_ct_type_usr_ovwr) != NULL){
130 _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
131 want_ct = TRU1;
132 }else if((want_ct = n_ignore_is_ign(doitp,
133 "content-type", sizeof("content-type") -1)))
134 cp = mpp->m_ct_type_plain;
135 if (want_ct &&
136 (to.l = strlen(cp)) > 30 && is_asccaseprefix("application/", cp)) {
137 size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
138 size_t i = to.l - fl;
139 char *x = salloc(al + i +1);
141 memcpy(x, "appl../", al);
142 memcpy(x + al, cp + fl, i +1);
143 cp = x;
144 to.l = al + i;
146 if(cp != NULL){
147 _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
148 needsep = TRU1;
151 if(mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL &&
152 (!asccasecmp(cp, "7bit") ||
153 n_ignore_is_ign(doitp, "content-transfer-encoding",
154 sizeof("content-transfer-encoding") -1))){
155 if(needsep)
156 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
157 if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
158 cp = "qu.-pr.";
159 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
160 needsep = TRU1;
163 if (want_ct && mpp->m_multipart == NULL/* TODO */ &&
164 (cp = mpp->m_charset) != NULL) {
165 if(needsep)
166 _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
167 _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
170 needsep = !needsep;
171 _out(&" --]"[needsep], 4 - needsep,
172 obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
173 if (csuf != NULL)
174 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
175 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
177 /* */
178 if (mpp->m_content_info & CI_MIME_ERRORS) {
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(_("Defective MIME structure")));
185 makeprint(&ti, &to);
186 to.l = delctrl(to.s, to.l);
187 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
188 free(to.s);
190 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
191 if (csuf != NULL)
192 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
193 NULL, NULL);
194 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
197 /* Content-Description */
198 if (n_ignore_is_ign(doitp, "content-description", 19) &&
199 (cp = mpp->m_content_description) != NULL && *cp != '\0') {
200 if (cpre != NULL)
201 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
202 NULL, NULL);
203 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
205 ti.l = strlen(ti.s = n_UNCONST(mpp->m_content_description));
206 mime_fromhdr(&ti, &to, TD_ISPR | TD_ICONV);
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);
217 /* Filename */
218 if (n_ignore_is_ign(doitp, "content-disposition", 19) &&
219 mpp->m_filename != NULL && *mpp->m_filename != '\0') {
220 if (cpre != NULL)
221 _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
222 NULL, NULL);
223 _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
225 ti.l = strlen(ti.s = mpp->m_filename);
226 makeprint(&ti, &to);
227 to.l = delctrl(to.s, to.l);
228 _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
229 free(to.s);
231 _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
232 if (csuf != NULL)
233 _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
234 NULL, NULL);
235 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
237 NYD2_LEAVE;
240 static FILE *
241 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
242 char const *tmpname, int term_infd)
244 struct str s;
245 char const *env_addon[9 +8/*v15*/], *cp, *sh;
246 size_t i;
247 FILE *rbuf;
248 NYD_ENTER;
250 rbuf = *qbuf;
252 if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
253 if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
254 NULL) {
255 n_perr(_("tmpfile"), 0);
256 *qbuf = rbuf;
260 if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
261 union {int (*ptf)(void); char const *sh;} u;
263 fflush(*qbuf);
264 if (*qbuf != n_stdout) /* xxx never? v15: it'll be a filter anyway */
265 fflush(n_stdout);
267 u.ptf = mhp->mh_ptf;
268 if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
269 goto jerror;
270 goto jleave;
273 i = 0;
275 /* MAILX_FILENAME */
276 if (mpp == NULL || (cp = mpp->m_filename) == NULL)
277 cp = n_empty;
278 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME, "=", cp, NULL)->s;
279 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME", "=", cp, NULL)->s;/*v15*/
281 /* MAILX_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
282 * TODO a file wherever he wants! *Do* create a zero-size temporary file
283 * TODO and give *that* path as MAILX_FILENAME_TEMPORARY, clean it up once
284 * TODO the pipe returns? Like this we *can* verify path/name issues! */
285 cp = n_random_create_cp(n_MIN(NAME_MAX / 4, 16), NULL);
286 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME_GENERATED, "=", cp,
287 NULL)->s;
288 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME_GENERATED", "=", cp,/*v15*/
289 NULL)->s;
291 /* MAILX_CONTENT{,_EVIDENCE} */
292 if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
293 cp = n_empty;
294 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT, "=", cp, NULL)->s;
295 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT", "=", cp, NULL)->s;/*v15*/
297 if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
298 cp = mpp->m_ct_type_usr_ovwr;
299 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT_EVIDENCE, "=", cp,
300 NULL)->s;
301 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT_EVIDENCE", "=", cp,/* v15 */
302 NULL)->s;
304 /* message/external-body, access-type=url */
305 env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_EXTERNAL_BODY_URL, "=",
306 ((mpp != NULL && (cp = mpp->m_external_body_url) != NULL
307 ) ? cp : n_empty), NULL)->s;
309 /* MAILX_FILENAME_TEMPORARY? */
310 if (tmpname != NULL) {
311 env_addon[i++] = str_concat_csvl(&s,
312 n_PIPEENV_FILENAME_TEMPORARY, "=", tmpname, NULL)->s;
313 env_addon[i++] = str_concat_csvl(&s,
314 "NAIL_FILENAME_TEMPORARY", "=", tmpname, NULL)->s;/* v15 */
317 /* TODO we should include header information, especially From:, so
318 * TODO that same-origin can be tested for e.g. external-body!!! */
320 env_addon[i] = NULL;
321 sh = ok_vlook(SHELL);
323 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
324 sigset_t nset;
325 int pid;
327 sigemptyset(&nset);
328 pid = n_child_run(sh, &nset, term_infd, n_CHILD_FD_PASS, "-c",
329 mhp->mh_shell_cmd, NULL, env_addon, NULL);
330 rbuf = (pid < 0) ? NULL : (FILE*)-1;
331 } else {
332 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
333 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
334 jerror:
335 if (rbuf == NULL)
336 n_err(_("Cannot run MIME type handler: %s: %s\n"),
337 mhp->mh_msg, n_err_to_doc(n_err_no));
338 else {
339 fflush(*qbuf);
340 if (*qbuf != n_stdout)
341 fflush(n_stdout);
344 jleave:
345 NYD_LEAVE;
346 return rbuf;
349 n_INLINE ssize_t
350 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
351 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *outrest,
352 struct str *inrest)
354 ssize_t sz = 0, n;
355 int flags;
356 NYD_ENTER;
358 /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
359 * TODO subclass should detect such From_ cases and either reencode the part
360 * TODO in question, or perform From_ quoting as necessary!?!?!? How?!? */
361 /* C99 */{
362 bool_t from_;
364 if((action == SEND_MBOX || action == SEND_DECRYPT) &&
365 (from_ = is_head(buf, len, TRU1))){
366 if(from_ != TRUM1 || ok_blook(mbox_rfc4155)){
367 putc('>', fp);
368 ++sz;
373 flags = ((int)action & _TD_EOF);
374 action &= ~_TD_EOF;
375 n = mime_write(buf, len, fp,
376 action == SEND_MBOX ? CONV_NONE : convert,
377 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
378 action == SEND_TODISP_PARTS ||
379 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
380 ? TD_ISPR | TD_ICONV
381 : (action == SEND_TOSRCH || action == SEND_TOPIPE ||
382 action == SEND_TOFILE)
383 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
384 qf, outrest, inrest);
385 if (n < 0)
386 sz = n;
387 else if (n > 0) {
388 sz += n;
389 if (stats != NULL)
390 *stats += sz;
392 NYD_LEAVE;
393 return sz;
396 static bool_t
397 a_send_out_nl(FILE *fp, ui64_t *stats){
398 struct quoteflt *qf;
399 bool_t rv;
400 NYD2_ENTER;
402 quoteflt_reset(qf = quoteflt_dummy(), fp);
403 rv = (_out("\n", 1, fp, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL) > 0);
404 quoteflt_flush(qf);
405 NYD2_LEAVE;
406 return rv;
409 static void
410 _send_onpipe(int signo)
412 NYD_X; /* Signal handler */
413 n_UNUSED(signo);
414 siglongjmp(_send_pipejmp, 1);
417 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
418 static int __sendp_sig; /* TODO someday.. */
419 static sighandler_type __sendp_opipe;
420 static void
421 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
423 NYD_X; /* Signal handler */
424 __sendp_sig = sig;
425 siglongjmp(__sendp_actjmp, 1);
428 static int
429 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
430 struct n_ignore const *doitp, struct quoteflt *qf,
431 enum sendaction volatile action,
432 char **linedat, size_t *linesize, ui64_t * volatile stats, int level)
434 int volatile rv = 0;
435 struct mime_handler mh;
436 struct str outrest, inrest;
437 char *cp;
438 char const * volatile tmpname = NULL;
439 size_t linelen, cnt;
440 int volatile dostat, term_infd;
441 int c;
442 struct mimepart * volatile np;
443 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
444 * volatile qbuf = obuf, *origobuf = obuf;
445 enum conversion volatile convert;
446 sighandler_type volatile oldpipe = SIG_DFL;
447 NYD_ENTER;
449 n_UNINIT(term_infd, 0);
450 n_UNINIT(cnt, 0);
452 quoteflt_reset(qf, obuf);
454 #if 0 /* TODO PART_INFO should be displayed here!! search PART_INFO */
455 if(ip->m_mimecontent != MIME_DISCARD && level > 0)
456 _print_part_info(obuf, ip, doitp, level, qf, stats);
457 #endif
459 if (ip->m_mimecontent == MIME_PKCS7) {
460 if (ip->m_multipart &&
461 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
462 goto jheaders_skip;
465 dostat = 0;
466 if (level == 0 && action != SEND_TODISP_PARTS) {
467 if (doitp != NULL) {
468 if (!n_ignore_is_ign(doitp, "status", 6))
469 dostat |= 1;
470 if (!n_ignore_is_ign(doitp, "x-status", 8))
471 dostat |= 2;
472 } else
473 dostat = 3;
476 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
477 rv = -1;
478 goto jleave;
481 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
482 action == SEND_TODISP_PARTS ||
483 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
484 action == SEND_TOSRCH)
485 dostat |= 4;
487 cnt = ip->m_size;
489 if (ip->m_mimecontent == MIME_DISCARD)
490 goto jheaders_skip;
492 if (!(ip->m_flag & MNOFROM))
493 while (cnt && (c = getc(ibuf)) != EOF) {
494 cnt--;
495 if (c == '\n')
496 break;
498 convert = (dostat & 4) ? CONV_FROMHDR : CONV_NONE;
500 /* Work the headers */
501 /* C99 */{
502 struct n_string hl, *hlp;
503 size_t lineno = 0;
504 bool_t hstop/*see below, hany*/;
506 hlp = n_string_creat_auto(&hl); /* TODO pool [or, v15: filter!] */
507 /* Reserve three lines, still not enough for references and DKIM etc. */
508 hlp = n_string_reserve(hlp, n_MAX(MIME_LINELEN, MIME_LINELEN_RFC2047) * 3);
510 for(hstop = /*see below hany =*/ FAL0; !hstop;){
511 size_t lcnt;
513 lcnt = cnt;
514 if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0) == NULL)
515 break;
516 ++lineno;
517 if (linelen == 0 || (cp = *linedat)[0] == '\n')
518 /* If line is blank, we've reached end of headers */
519 break;
520 if(cp[linelen - 1] == '\n'){
521 cp[--linelen] = '\0';
522 if(linelen == 0)
523 break;
526 /* Are we in a header? */
527 if(hlp->s_len > 0){
528 if(!blankchar(*cp)){
529 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
530 cnt = lcnt;
531 goto jhdrput;
533 goto jhdrpush;
534 }else{
535 /* Pick up the header field if we have one */
536 while((c = *cp) != ':' && !spacechar(c) && c != '\0')
537 ++cp;
538 for(;;){
539 if(!spacechar(c) || c == '\0')
540 break;
541 c = *++cp;
543 if(c != ':'){
544 /* That won't work with MIME when saving etc., before v15 */
545 if (lineno != 1)
546 /* XXX This disturbs, and may happen multiple times, and we
547 * XXX cannot heal it for multipart except for display <v15 */
548 n_err(_("Malformed message: headers and body not separated "
549 "(with empty line)\n"));
550 if(level != 0)
551 dostat &= ~(1 | 2);
552 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
553 cnt = lcnt;
554 break;
557 cp = *linedat;
558 jhdrpush:
559 if(!(dostat & 4)){
560 hlp = n_string_push_buf(hlp, cp, (ui32_t)linelen);
561 hlp = n_string_push_c(hlp, '\n');
562 }else{
563 bool_t lblank, isblank;
565 for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
566 char c8;
568 c8 = *cp;
569 if(!(isblank = blankchar(c8)) || !lblank){
570 if((lblank = isblank))
571 c8 = ' ';
572 hlp = n_string_push_c(hlp, c8);
576 continue;
579 jhdrput:
580 /* If it is an ignored header, skip it */
581 *(cp = memchr(hlp->s_dat, ':', hlp->s_len)) = '\0';
582 /* C99 */{
583 size_t i;
585 i = PTR2SIZE(cp - hlp->s_dat);
586 if((doitp != NULL && n_ignore_is_ign(doitp, hlp->s_dat, i)) ||
587 !asccasecmp(hlp->s_dat, "status") ||
588 !asccasecmp(hlp->s_dat, "x-status") ||
589 (action == SEND_MBOX &&
590 (!asccasecmp(hlp->s_dat, "content-length") ||
591 !asccasecmp(hlp->s_dat, "lines")) &&
592 !ok_blook(keep_content_length)))
593 goto jhdrtrunc;
596 /* Dump it */
597 n_COLOUR(
598 if(n_COLOUR_IS_ACTIVE())
599 n_colour_put(n_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
601 *cp = ':';
602 _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NULL,NULL);
603 n_COLOUR(
604 if(n_COLOUR_IS_ACTIVE())
605 n_colour_reset();
607 if(dostat & 4)
608 _out("\n", sizeof("\n") -1, obuf, convert, action, qf, stats,
609 NULL,NULL);
610 /*see below hany = TRU1;*/
612 jhdrtrunc:
613 hlp = n_string_trunc(hlp, 0);
615 hstop = TRU1;
616 if(hlp->s_len > 0)
617 goto jhdrput;
619 /* We've reached end of headers, so eventually force out status: field and
620 * note that we are no longer in header fields */
621 if(dostat & 1){
622 statusput(zmp, obuf, qf, stats);
623 /*see below hany = TRU1;*/
625 if(dostat & 2){
626 xstatusput(zmp, obuf, qf, stats);
627 /*see below hany = TRU1;*/
629 if(/* TODO PART_INFO hany && */ doitp != n_IGNORE_ALL)
630 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
631 } /* C99 */
633 quoteflt_flush(qf);
635 if(ferror(obuf)){
636 rv = -1;
637 goto jleave;
640 jheaders_skip:
641 memset(&mh, 0, sizeof mh);
643 switch (ip->m_mimecontent) {
644 case MIME_822:
645 switch (action) {
646 case SEND_TODISP_PARTS:
647 goto jleave;
648 case SEND_TODISP:
649 case SEND_TODISP_ALL:
650 case SEND_QUOTE:
651 case SEND_QUOTE_ALL:
652 if (ok_blook(rfc822_body_from_)) {
653 if (qf->qf_pfix_len > 0) {
654 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
655 qf->qf_pfix_len, obuf);
656 if (i == qf->qf_pfix_len && stats != NULL)
657 *stats += i;
659 put_from_(obuf, ip->m_multipart, stats);
661 /* FALLTHRU */
662 case SEND_TOSRCH:
663 case SEND_DECRYPT:
664 goto jmulti;
665 case SEND_TOFILE:
666 case SEND_TOPIPE:
667 put_from_(obuf, ip->m_multipart, stats);
668 /* FALLTHRU */
669 case SEND_MBOX:
670 case SEND_RFC822:
671 case SEND_SHOW:
672 break;
674 break;
675 case MIME_TEXT_HTML:
676 case MIME_TEXT:
677 case MIME_TEXT_PLAIN:
678 switch (action) {
679 case SEND_TODISP:
680 case SEND_TODISP_ALL:
681 case SEND_TODISP_PARTS:
682 case SEND_QUOTE:
683 case SEND_QUOTE_ALL:
684 switch (n_mimetype_handler(&mh, ip, action)) {
685 case MIME_HDL_NULL:
686 if(action != SEND_TODISP_PARTS)
687 break;
688 /* FALLTHRU */
689 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
690 if(mh.mh_msg.l > 0)
691 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
692 qf, stats, NULL, NULL);
693 /* We would print this as plain text, so better force going home */
694 goto jleave;
695 case MIME_HDL_CMD:
696 if(action == SEND_TODISP_PARTS &&
697 (mh.mh_flags & MIME_HDL_COPIOUSOUTPUT))
698 goto jleave;
699 break;
700 case MIME_HDL_TEXT:
701 case MIME_HDL_PTF:
702 if(action == SEND_TODISP_PARTS)
703 goto jleave;
704 break;
705 default:
706 break;
708 /* FALLTRHU */
709 default:
710 break;
712 break;
713 case MIME_DISCARD:
714 if (action != SEND_DECRYPT)
715 goto jleave;
716 break;
717 case MIME_PKCS7:
718 if (action != SEND_MBOX && action != SEND_RFC822 &&
719 action != SEND_SHOW && ip->m_multipart != NULL)
720 goto jmulti;
721 /* FALLTHRU */
722 default:
723 switch (action) {
724 case SEND_TODISP:
725 case SEND_TODISP_ALL:
726 case SEND_TODISP_PARTS:
727 case SEND_QUOTE:
728 case SEND_QUOTE_ALL:
729 switch (n_mimetype_handler(&mh, ip, action)) {
730 default:
731 case MIME_HDL_NULL:
732 if (action != SEND_TODISP && action != SEND_TODISP_ALL &&
733 (level != 0 || cnt))
734 goto jleave;
735 /* FALLTHRU */
736 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
737 if(mh.mh_msg.l > 0)
738 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
739 qf, stats, NULL, NULL);
740 /* We would print this as plain text, so better force going home */
741 goto jleave;
742 case MIME_HDL_CMD:
743 if(action == SEND_TODISP_PARTS){
744 if(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)
745 goto jleave;
746 else{
747 _print_part_info(obuf, ip, doitp, level, qf, stats);
748 if(!getapproval(_("Run MIME handler for this part?"), FAL0))
749 goto jleave;
752 break;
753 case MIME_HDL_TEXT:
754 case MIME_HDL_PTF:
755 if(action == SEND_TODISP_PARTS)
756 goto jleave;
757 break;
759 break;
760 case SEND_TOFILE:
761 case SEND_TOPIPE:
762 case SEND_TOSRCH:
763 case SEND_DECRYPT:
764 case SEND_MBOX:
765 case SEND_RFC822:
766 case SEND_SHOW:
767 break;
769 break;
770 case MIME_ALTERNATIVE:
771 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
772 !ok_blook(print_alternatives)) {
773 /* XXX This (a) should not remain (b) should be own fun
774 * TODO (despite the fact that v15 will do this completely differently
775 * TODO by having an action-specific "manager" that will traverse the
776 * TODO parsed MIME tree and decide for each part whether it'll be
777 * TODO displayed or not *before* we walk the tree for doing action */
778 struct mpstack {
779 struct mpstack *outer;
780 struct mimepart *mp;
781 } outermost, * volatile curr, * volatile mpsp;
782 bool_t volatile neednl, hadpart;
783 struct n_sigman smalter;
785 (curr = &outermost)->outer = NULL;
786 curr->mp = ip;
787 neednl = hadpart = FAL0;
789 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
790 case 0:
791 break;
792 default:
793 rv = -1;
794 goto jalter_leave;
797 for (np = ip->m_multipart;;) {
798 jalter_redo:
799 for (; np != NULL; np = np->m_nextpart) {
800 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
801 if (neednl)
802 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats,
803 NULL, NULL);
804 _print_part_info(obuf, np, doitp, level, qf, stats);
806 neednl = TRU1;
808 switch (np->m_mimecontent) {
809 case MIME_ALTERNATIVE:
810 case MIME_RELATED:
811 case MIME_DIGEST:
812 case MIME_SIGNED:
813 case MIME_ENCRYPTED:
814 case MIME_MULTI:
815 mpsp = salloc(sizeof *mpsp);
816 mpsp->outer = curr;
817 mpsp->mp = np->m_multipart;
818 curr->mp = np;
819 curr = mpsp;
820 np = mpsp->mp;
821 neednl = FAL0;
822 goto jalter_redo;
823 default:
824 if (hadpart)
825 break;
826 switch (n_mimetype_handler(&mh, np, action)) {
827 default:
828 mh.mh_flags = MIME_HDL_NULL;
829 continue; /* break; break; */
830 case MIME_HDL_CMD:
831 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)){
832 mh.mh_flags = MIME_HDL_NULL;
833 continue; /* break; break; */
835 /* FALLTHRU */
836 case MIME_HDL_PTF:
837 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
838 struct mimepart *x = np;
840 while ((x = x->m_nextpart) != NULL) {
841 struct mime_handler mhx;
843 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
844 n_mimetype_handler(&mhx, x, action) ==
845 MIME_HDL_TEXT)
846 break;
848 if (x != NULL)
849 continue; /* break; break; */
850 goto jalter_plain;
852 /* FALLTHRU */
853 case MIME_HDL_TEXT:
854 break;
856 /* FALLTHRU */
857 case MIME_TEXT_PLAIN:
858 if (hadpart)
859 break;
860 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
861 struct mimepart *x = np;
863 /* TODO twice TODO, we should dive into /related and
864 * TODO check whether that has rich parts! */
865 while ((x = x->m_nextpart) != NULL) {
866 struct mime_handler mhx;
868 switch (n_mimetype_handler(&mhx, x, action)) {
869 case MIME_HDL_CMD:
870 if(!(mhx.mh_flags & MIME_HDL_COPIOUSOUTPUT))
871 continue;
872 /* FALLTHRU */
873 case MIME_HDL_PTF:
874 break;
875 default:
876 continue;
878 break;
880 if (x != NULL)
881 continue; /* break; break; */
883 jalter_plain:
884 quoteflt_flush(qf);
885 if (action == SEND_QUOTE && hadpart)
886 /* XXX (void)*/a_send_out_nl(obuf, stats);
887 hadpart = TRU1;
888 neednl = FAL0;
889 rv = sendpart(zmp, np, obuf, doitp, qf, action,
890 linedat, linesize, stats, level + 1);
891 quoteflt_reset(qf, origobuf);
893 if (rv < 0)
894 curr = &outermost; /* Cause overall loop termination */
895 break;
899 mpsp = curr->outer;
900 if (mpsp == NULL)
901 break;
902 curr = mpsp;
903 np = curr->mp->m_nextpart;
905 jalter_leave:
906 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
907 goto jleave;
909 /* FALLTHRU */
910 case MIME_RELATED:
911 case MIME_DIGEST:
912 case MIME_SIGNED:
913 case MIME_ENCRYPTED:
914 case MIME_MULTI:
915 switch (action) {
916 case SEND_TODISP:
917 case SEND_TODISP_ALL:
918 case SEND_TODISP_PARTS:
919 case SEND_QUOTE:
920 case SEND_QUOTE_ALL:
921 case SEND_TOFILE:
922 case SEND_TOPIPE:
923 case SEND_TOSRCH:
924 case SEND_DECRYPT:
925 jmulti:
926 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
927 ip->m_multipart != NULL &&
928 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
929 ip->m_multipart->m_nextpart == NULL) {
930 char const *x = _("[Missing multipart boundary - use `show' "
931 "to display the raw message]\n");
932 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
933 NULL,NULL);
936 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
937 bool_t volatile ispipe;
939 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
940 continue;
942 ispipe = FAL0;
943 switch (action) {
944 case SEND_TOFILE:
945 if (np->m_partstring &&
946 np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
947 break;
948 stats = NULL;
949 /* TODO Always open multipart on /dev/null, it's a hack to be
950 * TODO able to dive into that structure, and still better
951 * TODO than asking the user for something stupid.
952 * TODO oh, wait, we did ask for a filename for this MIME mail,
953 * TODO and that outer container is useless anyway ;-P */
954 if (np->m_multipart != NULL && np->m_mimecontent != MIME_822) {
955 if ((obuf = Fopen(n_path_devnull, "w")) == NULL)
956 continue;
957 } else if ((obuf = newfile(np, &ispipe)) == NULL)
958 continue;
959 if (!ispipe)
960 break;
961 if (sigsetjmp(_send_pipejmp, 1)) {
962 rv = -1;
963 goto jpipe_close;
965 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
966 break;
967 case SEND_TODISP:
968 case SEND_TODISP_ALL:
969 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
970 ip->m_mimecontent != MIME_RELATED &&
971 ip->m_mimecontent != MIME_DIGEST &&
972 ip->m_mimecontent != MIME_SIGNED &&
973 ip->m_mimecontent != MIME_ENCRYPTED &&
974 ip->m_mimecontent != MIME_MULTI)
975 break;
976 _print_part_info(obuf, np, doitp, level, qf, stats);
977 break;
978 case SEND_TODISP_PARTS:
979 case SEND_QUOTE:
980 case SEND_QUOTE_ALL:
981 case SEND_MBOX:
982 case SEND_RFC822:
983 case SEND_SHOW:
984 case SEND_TOSRCH:
985 case SEND_DECRYPT:
986 case SEND_TOPIPE:
987 break;
990 quoteflt_flush(qf);
991 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
992 np->m_multipart == NULL && ip->m_parent != NULL)
993 /*XXX (void)*/a_send_out_nl(obuf, stats);
994 if (sendpart(zmp, np, obuf, doitp, qf, action, linedat, linesize,
995 stats, level+1) < 0)
996 rv = -1;
997 quoteflt_reset(qf, origobuf);
999 if (action == SEND_QUOTE) {
1000 if (ip->m_mimecontent != MIME_RELATED)
1001 break;
1003 if (action == SEND_TOFILE && obuf != origobuf) {
1004 if (!ispipe)
1005 Fclose(obuf);
1006 else {
1007 jpipe_close:
1008 safe_signal(SIGPIPE, SIG_IGN);
1009 Pclose(obuf, TRU1);
1010 safe_signal(SIGPIPE, oldpipe);
1014 goto jleave;
1015 case SEND_MBOX:
1016 case SEND_RFC822:
1017 case SEND_SHOW:
1018 break;
1020 break;
1023 /* Copy out message body */
1024 if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
1025 --cnt;
1026 switch (ip->m_mime_enc) {
1027 case MIMEE_BIN:
1028 case MIMEE_7B:
1029 case MIMEE_8B:
1030 convert = CONV_NONE;
1031 break;
1032 case MIMEE_QP:
1033 convert = CONV_FROMQP;
1034 break;
1035 case MIMEE_B64:
1036 switch (ip->m_mimecontent) {
1037 case MIME_TEXT:
1038 case MIME_TEXT_PLAIN:
1039 case MIME_TEXT_HTML:
1040 convert = CONV_FROMB64_T;
1041 break;
1042 default:
1043 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1044 case MIME_HDL_TEXT:
1045 case MIME_HDL_PTF:
1046 convert = CONV_FROMB64_T;
1047 break;
1048 default:
1049 convert = CONV_FROMB64;
1050 break;
1052 break;
1054 break;
1055 default:
1056 convert = CONV_NONE;
1059 /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
1060 * TODO cannot mess things up misusing outrest as line buffer */
1061 #ifdef HAVE_ICONV
1062 if (iconvd != (iconv_t)-1) {
1063 n_iconv_close(iconvd);
1064 iconvd = (iconv_t)-1;
1066 #endif
1068 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1069 action == SEND_RFC822 || action == SEND_SHOW)
1070 convert = CONV_NONE;
1071 #ifdef HAVE_ICONV
1072 else if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
1073 action == SEND_TODISP_PARTS ||
1074 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
1075 action == SEND_TOSRCH || action == SEND_TOFILE) &&
1076 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
1077 ip->m_mimecontent == MIME_TEXT_HTML ||
1078 ip->m_mimecontent == MIME_TEXT ||
1079 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
1080 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
1081 char const *tcs;
1083 tcs = ok_vlook(ttycharset);
1084 if (asccasecmp(tcs, ip->m_charset) &&
1085 asccasecmp(ok_vlook(charset_7bit), ip->m_charset)) {
1086 iconvd = n_iconv_open(tcs, ip->m_charset);
1087 if (iconvd == (iconv_t)-1 && n_err_no == n_ERR_INVAL) {
1088 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
1089 /*rv = 1; goto jleave;*/
1093 #endif
1095 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1096 case MIME_HDL_CMD:
1097 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT) &&
1098 action != SEND_TODISP_PARTS)
1099 goto jmhp_default;
1100 /* FALLTHRU */
1101 case MIME_HDL_PTF:
1102 tmpname = NULL;
1103 qbuf = obuf;
1105 term_infd = n_CHILD_FD_PASS;
1106 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
1107 enum oflags of;
1109 of = OF_RDWR | OF_REGISTER;
1110 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
1111 term_infd = 0;
1112 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1113 of |= OF_UNLINK;
1114 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
1115 of |= OF_REGISTER_UNLINK;
1117 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
1118 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
1119 of)) == NULL)
1120 goto jesend;
1122 if (mh.mh_flags & MIME_HDL_TMPF) {
1123 tmpname = savestr(cp);
1124 Ftmp_free(&cp);
1127 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1128 if (term_infd == 0)
1129 term_infd = fileno(pbuf);
1130 goto jsend;
1134 jpipe_for_real:
1135 pbuf = _pipefile(&mh, ip, n_UNVOLATILE(&qbuf), tmpname, term_infd);
1136 if (pbuf == NULL) {
1137 jesend:
1138 pbuf = qbuf = NULL;
1139 rv = -1;
1140 goto jend;
1141 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1142 pbuf = qbuf = NULL;
1143 goto jend;
1145 tmpname = NULL;
1146 action = SEND_TOPIPE;
1147 if (pbuf != qbuf) {
1148 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1149 if (sigsetjmp(_send_pipejmp, 1))
1150 goto jend;
1152 break;
1154 default:
1155 jmhp_default:
1156 mh.mh_flags = MIME_HDL_NULL;
1157 pbuf = qbuf = obuf;
1158 break;
1161 jsend:
1163 bool_t volatile eof;
1164 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1165 ui64_t *save_stats = stats;
1167 if (pbuf != origobuf) {
1168 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1169 stats = NULL;
1171 eof = FAL0;
1172 outrest.s = inrest.s = NULL;
1173 outrest.l = inrest.l = 0;
1175 if (pbuf == qbuf) {
1176 __sendp_sig = 0;
1177 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1178 if (sigsetjmp(__sendp_actjmp, 1)) {
1179 n_pstate &= ~n_PS_BASE64_STRIP_CR;/* (but protected by outer sigman) */
1180 if (outrest.s != NULL)
1181 free(outrest.s);
1182 if (inrest.s != NULL)
1183 free(inrest.s);
1184 #ifdef HAVE_ICONV
1185 if (iconvd != (iconv_t)-1)
1186 n_iconv_close(iconvd);
1187 #endif
1188 safe_signal(SIGPIPE, __sendp_opipe);
1189 n_raise(__sendp_sig);
1193 quoteflt_reset(qf, pbuf);
1194 if((dostat & 4) && pbuf == origobuf) /* TODO */
1195 n_pstate |= n_PS_BASE64_STRIP_CR;
1196 while (!eof && fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0)) {
1197 joutln:
1198 if (_out(*linedat, linelen, pbuf, convert, action, qf, stats, &outrest,
1199 (action & _TD_EOF ? NULL : &inrest)) < 0 || ferror(pbuf)) {
1200 rv = -1; /* XXX Should bail away?! */
1201 break;
1204 if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
1205 linelen = 0;
1206 if(eof || inrest.l == 0)
1207 action |= _TD_EOF;
1208 eof = eof ? TRU1 : TRUM1;
1209 goto joutln;
1211 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1212 action &= ~_TD_EOF;
1214 /* TODO HACK: when sending to the display we yet get fooled if a message
1215 * TODO doesn't end in a newline, because of our input/output 1:1.
1216 * TODO This should be handled automatically by a display filter, then */
1217 if(rv >= 0 && !qf->qf_nl_last &&
1218 (action == SEND_TODISP || action == SEND_TODISP_ALL))
1219 rv = quoteflt_push(qf, "\n", 1);
1221 quoteflt_flush(qf);
1223 if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
1224 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1225 fflush(pbuf);
1226 really_rewind(pbuf);
1227 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1228 goto jpipe_for_real;
1231 if (pbuf == qbuf)
1232 safe_signal(SIGPIPE, __sendp_opipe);
1234 if (outrest.s != NULL)
1235 free(outrest.s);
1236 if (inrest.s != NULL)
1237 free(inrest.s);
1239 if (pbuf != origobuf) {
1240 qf->qf_pfix_len = save_qf_pfix_len;
1241 stats = save_stats;
1245 jend:
1246 if (pbuf != qbuf) {
1247 safe_signal(SIGPIPE, SIG_IGN);
1248 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1249 safe_signal(SIGPIPE, oldpipe);
1250 if (rv >= 0 && qbuf != NULL && qbuf != obuf)
1251 pipecpy(qbuf, obuf, origobuf, qf, stats);
1253 #ifdef HAVE_ICONV
1254 if (iconvd != (iconv_t)-1)
1255 n_iconv_close(iconvd);
1256 #endif
1257 jleave:
1258 NYD_LEAVE;
1259 return rv;
1262 static FILE *
1263 newfile(struct mimepart *ip, bool_t volatile *ispipe)
1265 struct str in, out;
1266 char *f;
1267 FILE *fp;
1268 NYD_ENTER;
1270 f = ip->m_filename;
1271 *ispipe = FAL0;
1273 if (f != NULL && f != (char*)-1) {
1274 in.s = f;
1275 in.l = strlen(f);
1276 makeprint(&in, &out);
1277 out.l = delctrl(out.s, out.l);
1278 f = savestrbuf(out.s, out.l);
1279 free(out.s);
1282 /* In interactive mode, let user perform all kind of expansions as desired,
1283 * and offer |SHELL-SPEC pipe targets, too */
1284 if (n_psonce & n_PSO_INTERACTIVE) {
1285 struct str prompt;
1286 struct n_string shou, *shoup;
1287 char *f2, *f3;
1289 shoup = n_string_creat_auto(&shou);
1291 /* TODO Generic function which asks for filename.
1292 * TODO If the current part is the first textpart the target
1293 * TODO is implicit from outer `write' etc! */
1294 /* I18N: Filename input prompt with file type indication */
1295 str_concat_csvl(&prompt, _("Enter filename for part "),
1296 (ip->m_partstring != NULL ? ip->m_partstring : n_qm),
1297 " (", ip->m_ct_type_plain, "): ", NULL);
1298 jgetname:
1299 f2 = n_go_input_cp(n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_HIST_ADD,
1300 prompt.s, ((f != (char*)-1 && f != NULL)
1301 ? n_shexp_quote_cp(f, FAL0) : NULL));
1302 if(f2 != NULL){
1303 in.s = n_UNCONST(f2);
1304 in.l = UIZ_MAX;
1305 if((n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
1306 n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_TRIM_IFSSPACE |
1307 n_SHEXP_PARSE_LOG | n_SHEXP_PARSE_IGNORE_EMPTY),
1308 shoup, &in, NULL
1309 ) & (n_SHEXP_STATE_STOP |
1310 n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)
1311 ) != (n_SHEXP_STATE_STOP | n_SHEXP_STATE_OUTPUT))
1312 goto jgetname;
1313 f2 = n_string_cp(shoup);
1315 if (f2 == NULL || *f2 == '\0') {
1316 if (n_poption & n_PO_D_V)
1317 n_err(_("... skipping this\n"));
1318 n_string_gut(shoup);
1319 fp = NULL;
1320 goto jleave;
1323 if (*f2 == '|')
1324 /* Pipes are expanded by the shell */
1325 f = f2;
1326 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
1327 /* (Error message written by fexpand()) */
1328 goto jgetname;
1329 else
1330 f = f3;
1332 n_string_gut(shoup);
1335 if (f == NULL || f == (char*)-1 || *f == '\0')
1336 fp = NULL;
1337 else if (n_psonce & n_PSO_INTERACTIVE) {
1338 if (*f == '|') {
1339 fp = Popen(&f[1], "w", ok_vlook(SHELL), NULL, 1);
1340 if (!(*ispipe = (fp != NULL)))
1341 n_perr(f, 0);
1342 } else if ((fp = Fopen(f, "w")) == NULL)
1343 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
1344 } else {
1345 /* Be very picky in non-interactive mode: actively disallow pipes,
1346 * prevent directory separators, and any filename member that would
1347 * become expanded by the shell if the name would be echo(1)ed */
1348 if(n_anyof_cp("/" n_SHEXP_MAGIC_PATH_CHARS, f)){
1349 char c;
1351 for(out.s = salloc((strlen(f) * 3) +1), out.l = 0; (c = *f++) != '\0';)
1352 if(strchr("/" n_SHEXP_MAGIC_PATH_CHARS, c)){
1353 out.s[out.l++] = '%';
1354 n_c_to_hex_base16(&out.s[out.l], c);
1355 out.l += 2;
1356 }else
1357 out.s[out.l++] = c;
1358 out.s[out.l] = '\0';
1359 f = out.s;
1362 /* Avoid overwriting of existing files */
1363 while((fp = Fopen(f, "wx")) == NULL){
1364 int e;
1366 if((e = n_err_no) != n_ERR_EXIST){
1367 n_err(_("Cannot open %s: %s\n"),
1368 n_shexp_quote_cp(f, FAL0), n_err_to_doc(e));
1369 break;
1372 if(ip->m_partstring != NULL)
1373 f = savecatsep(f, '#', ip->m_partstring);
1374 else
1375 f = savecat(f, "#.");
1378 jleave:
1379 NYD_LEAVE;
1380 return fp;
1383 static void
1384 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1385 ui64_t *stats)
1387 char *line = NULL; /* TODO line pool */
1388 size_t linesize = 0, linelen, cnt;
1389 ssize_t all_sz, sz;
1390 NYD_ENTER;
1392 fflush(pipebuf);
1393 rewind(pipebuf);
1394 cnt = (size_t)fsize(pipebuf);
1395 all_sz = 0;
1397 quoteflt_reset(qf, outbuf);
1398 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1399 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1400 break;
1401 all_sz += sz;
1403 if ((sz = quoteflt_flush(qf)) > 0)
1404 all_sz += sz;
1405 if (line)
1406 free(line);
1408 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1409 *stats += all_sz;
1410 Fclose(pipebuf);
1411 NYD_LEAVE;
1414 static void
1415 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1416 ui64_t *stats)
1418 char statout[3], *cp = statout;
1419 NYD_ENTER;
1421 if (mp->m_flag & MREAD)
1422 *cp++ = 'R';
1423 if (!(mp->m_flag & MNEW))
1424 *cp++ = 'O';
1425 *cp = 0;
1426 if (statout[0]) {
1427 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1428 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1429 if (i > 0 && stats != NULL)
1430 *stats += i;
1432 NYD_LEAVE;
1435 static void
1436 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1437 ui64_t *stats)
1439 char xstatout[4];
1440 char *xp = xstatout;
1441 NYD_ENTER;
1443 if (mp->m_flag & MFLAGGED)
1444 *xp++ = 'F';
1445 if (mp->m_flag & MANSWERED)
1446 *xp++ = 'A';
1447 if (mp->m_flag & MDRAFTED)
1448 *xp++ = 'T';
1449 *xp = 0;
1450 if (xstatout[0]) {
1451 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1452 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1453 if (i > 0 && stats != NULL)
1454 *stats += i;
1456 NYD_LEAVE;
1459 static void
1460 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1462 char const *froma, *date, *nl;
1463 int i;
1464 NYD_ENTER;
1466 if (ip != NULL && ip->m_from != NULL) {
1467 froma = ip->m_from;
1468 date = n_time_ctime(ip->m_time, NULL);
1469 nl = "\n";
1470 } else {
1471 froma = ok_vlook(LOGNAME);
1472 date = time_current.tc_ctime;
1473 nl = n_empty;
1476 n_COLOUR(
1477 if(n_COLOUR_IS_ACTIVE())
1478 n_colour_put(n_COLOUR_ID_VIEW_FROM_, NULL);
1480 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1481 n_COLOUR(
1482 if(n_COLOUR_IS_ACTIVE())
1483 n_colour_reset();
1485 if (i > 0 && stats != NULL)
1486 *stats += i;
1487 NYD_LEAVE;
1490 FL int
1491 sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp,
1492 char const *prefix, enum sendaction action, ui64_t *stats)
1494 struct n_sigman linedat_protect;
1495 struct quoteflt qf;
1496 FILE *ibuf;
1497 enum mime_parse_flags mpf;
1498 struct mimepart *ip;
1499 size_t linesize, cnt, sz, i;
1500 char *linedat;
1501 int rv, c;
1502 NYD_ENTER;
1504 time_current_update(&time_current, TRU1);
1505 rv = -1;
1506 linedat = NULL;
1507 linesize = 0;
1508 quoteflt_init(&qf, prefix);
1510 n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
1511 case 0:
1512 break;
1513 default:
1514 goto jleave;
1517 if (mp == dot && action != SEND_TOSRCH)
1518 n_pstate |= n_PS_DID_PRINT_DOT;
1519 if (stats != NULL)
1520 *stats = 0;
1522 /* First line is the From_ line, so no headers there to worry about */
1523 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1524 goto jleave;
1526 cnt = mp->m_size;
1527 sz = 0;
1529 bool_t nozap;
1530 char const *cpre = n_empty, *csuf = n_empty;
1532 #ifdef HAVE_COLOUR
1533 if(n_COLOUR_IS_ACTIVE()){
1534 struct n_colour_pen *cpen;
1535 struct str const *sp;
1537 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
1538 if((sp = n_colour_pen_to_str(cpen)) != NULL){
1539 cpre = sp->s;
1540 sp = n_colour_reset_to_str();
1541 if(sp != NULL)
1542 csuf = sp->s;
1545 #endif
1547 nozap = (doitp != n_IGNORE_ALL && doitp != n_IGNORE_FWD &&
1548 action != SEND_RFC822 &&
1549 !n_ignore_is_ign(doitp, "from_", sizeof("from_") -1));
1550 if (mp->m_flag & MNOFROM) {
1551 if (nozap)
1552 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1553 cpre, (int)qf.qf_pfix_len,
1554 (qf.qf_pfix_len != 0 ? qf.qf_pfix : n_empty), fakefrom(mp),
1555 n_time_ctime(mp->m_time, NULL), csuf);
1556 } else if (nozap) {
1557 if (qf.qf_pfix_len > 0) {
1558 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1559 if (i != qf.qf_pfix_len)
1560 goto jleave;
1561 sz += i;
1563 #ifdef HAVE_COLOUR
1564 if(*cpre != '\0'){
1565 fputs(cpre, obuf);
1566 cpre = (char const*)0x1;
1568 #endif
1570 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1571 #ifdef HAVE_COLOUR
1572 if(c == '\n' && *csuf != '\0'){
1573 cpre = (char const*)0x1;
1574 fputs(csuf, obuf);
1576 #endif
1577 putc(c, obuf);
1578 ++sz;
1579 --cnt;
1580 if (c == '\n')
1581 break;
1584 #ifdef HAVE_COLOUR
1585 if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
1586 fputs(csuf, obuf);
1587 #endif
1588 } else {
1589 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1590 --cnt;
1591 if (c == '\n')
1592 break;
1596 if (sz > 0 && stats != NULL)
1597 *stats += sz;
1599 mpf = MIME_PARSE_NONE;
1600 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1601 mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT;
1602 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
1603 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
1604 mpf |= MIME_PARSE_FOR_USER_CONTEXT;
1605 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1606 goto jleave;
1608 rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
1609 stats, 0);
1611 n_sigman_cleanup_ping(&linedat_protect);
1612 jleave:
1613 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1614 quoteflt_destroy(&qf);
1615 if(linedat != NULL)
1616 free(linedat);
1617 NYD_LEAVE;
1618 n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
1619 return rv;
1622 /* s-it-mode */