cc-test.sh: ARGH! MBOX content statistics not portable! (Gaetan Bisson)
[s-mailx.git] / send.c
bloba9006f29fc9628c0cc7e4263e3d07669c5a8d816
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 ((cp = mpp->m_external_body_url) != NULL ? cp : n_empty), NULL)->s;
305 /* MAILX_FILENAME_TEMPORARY? */
306 if (tmpname != NULL) {
307 env_addon[i++] = str_concat_csvl(&s,
308 n_PIPEENV_FILENAME_TEMPORARY, "=", tmpname, NULL)->s;
309 env_addon[i++] = str_concat_csvl(&s,
310 "NAIL_FILENAME_TEMPORARY", "=", tmpname, NULL)->s;/* v15 */
313 env_addon[i] = NULL;
314 sh = ok_vlook(SHELL);
316 if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
317 sigset_t nset;
318 int pid;
320 sigemptyset(&nset);
321 pid = n_child_run(sh, &nset, term_infd, n_CHILD_FD_PASS, "-c",
322 mhp->mh_shell_cmd, NULL, env_addon, NULL);
323 rbuf = (pid < 0) ? NULL : (FILE*)-1;
324 } else {
325 rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
326 (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
327 jerror:
328 if (rbuf == NULL)
329 n_err(_("Cannot run MIME type handler: %s: %s\n"),
330 mhp->mh_msg, n_err_to_doc(n_err_no));
331 else {
332 fflush(*qbuf);
333 if (*qbuf != n_stdout)
334 fflush(n_stdout);
337 jleave:
338 NYD_LEAVE;
339 return rbuf;
342 n_INLINE ssize_t
343 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
344 sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *outrest,
345 struct str *inrest)
347 ssize_t sz = 0, n;
348 int flags;
349 NYD_ENTER;
351 /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
352 * TODO subclass should detect such From_ cases and either reencode the part
353 * TODO in question, or perform From_ quoting as necessary!?!?!? How?!? */
354 /* C99 */{
355 bool_t from_;
357 if((action == SEND_MBOX || action == SEND_DECRYPT) &&
358 (from_ = is_head(buf, len, TRU1))){
359 if(from_ != TRUM1 || ok_blook(mbox_rfc4155)){
360 putc('>', fp);
361 ++sz;
366 flags = ((int)action & _TD_EOF);
367 action &= ~_TD_EOF;
368 n = mime_write(buf, len, fp,
369 action == SEND_MBOX ? CONV_NONE : convert,
370 flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
371 action == SEND_TODISP_PARTS ||
372 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
373 ? TD_ISPR | TD_ICONV
374 : (action == SEND_TOSRCH || action == SEND_TOPIPE ||
375 action == SEND_TOFILE)
376 ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
377 qf, outrest, inrest);
378 if (n < 0)
379 sz = n;
380 else if (n > 0) {
381 sz += n;
382 if (stats != NULL)
383 *stats += sz;
385 NYD_LEAVE;
386 return sz;
389 static void
390 _send_onpipe(int signo)
392 NYD_X; /* Signal handler */
393 n_UNUSED(signo);
394 siglongjmp(_send_pipejmp, 1);
397 static sigjmp_buf __sendp_actjmp; /* TODO someday.. */
398 static int __sendp_sig; /* TODO someday.. */
399 static sighandler_type __sendp_opipe;
400 static void
401 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
403 NYD_X; /* Signal handler */
404 __sendp_sig = sig;
405 siglongjmp(__sendp_actjmp, 1);
408 static int
409 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
410 struct n_ignore const *doitp, struct quoteflt *qf,
411 enum sendaction volatile action,
412 char **linedat, size_t *linesize, ui64_t * volatile stats, int level)
414 int volatile rv = 0;
415 struct mime_handler mh;
416 struct str outrest, inrest;
417 char *cp;
418 char const * volatile tmpname = NULL;
419 size_t linelen, cnt;
420 int volatile dostat, term_infd;
421 int c;
422 struct mimepart * volatile np;
423 FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
424 * volatile qbuf = obuf, *origobuf = obuf;
425 enum conversion volatile convert;
426 sighandler_type volatile oldpipe = SIG_DFL;
427 NYD_ENTER;
429 n_UNINIT(term_infd, 0);
430 n_UNINIT(cnt, 0);
432 quoteflt_reset(qf, obuf);
434 #if 0 /* TODO PART_INFO should be displayed here!! search PART_INFO */
435 if(ip->m_mimecontent != MIME_DISCARD && level > 0)
436 _print_part_info(obuf, ip, doitp, level, qf, stats);
437 #endif
439 if (ip->m_mimecontent == MIME_PKCS7) {
440 if (ip->m_multipart &&
441 action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
442 goto jheaders_skip;
445 dostat = 0;
446 if (level == 0 && action != SEND_TODISP_PARTS) {
447 if (doitp != NULL) {
448 if (!n_ignore_is_ign(doitp, "status", 6))
449 dostat |= 1;
450 if (!n_ignore_is_ign(doitp, "x-status", 8))
451 dostat |= 2;
452 } else
453 dostat = 3;
456 if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
457 rv = -1;
458 goto jleave;
461 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
462 action == SEND_TODISP_PARTS ||
463 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
464 action == SEND_TOSRCH)
465 dostat |= 4;
467 cnt = ip->m_size;
469 if (ip->m_mimecontent == MIME_DISCARD)
470 goto jheaders_skip;
472 if (!(ip->m_flag & MNOFROM))
473 while (cnt && (c = getc(ibuf)) != EOF) {
474 cnt--;
475 if (c == '\n')
476 break;
478 convert = (dostat & 4) ? CONV_FROMHDR : CONV_NONE;
480 /* Work the headers */
481 /* C99 */{
482 struct n_string hl, *hlp;
483 size_t lineno = 0;
484 bool_t hstop/*see below, hany*/;
486 hlp = n_string_creat_auto(&hl); /* TODO pool [or, v15: filter!] */
487 /* Reserve three lines, still not enough for references and DKIM etc. */
488 hlp = n_string_reserve(hlp, n_MAX(MIME_LINELEN, MIME_LINELEN_RFC2047) * 3);
490 for(hstop = /*see below hany =*/ FAL0; !hstop;){
491 size_t lcnt;
493 lcnt = cnt;
494 if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0) == NULL)
495 break;
496 ++lineno;
497 if (linelen == 0 || (cp = *linedat)[0] == '\n')
498 /* If line is blank, we've reached end of headers */
499 break;
500 if(cp[linelen - 1] == '\n'){
501 cp[--linelen] = '\0';
502 if(linelen == 0)
503 break;
506 /* Are we in a header? */
507 if(hlp->s_len > 0){
508 if(!blankchar(*cp)){
509 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
510 cnt = lcnt;
511 goto jhdrput;
513 goto jhdrpush;
514 }else{
515 /* Pick up the header field if we have one */
516 while((c = *cp) != ':' && !spacechar(c) && c != '\0')
517 ++cp;
518 for(;;){
519 if(!spacechar(c) || c == '\0')
520 break;
521 c = *++cp;
523 if(c != ':'){
524 /* That won't work with MIME when saving etc., before v15 */
525 if (lineno != 1)
526 /* XXX This disturbs, and may happen multiple times, and we
527 * XXX cannot heal it for multipart except for display <v15 */
528 n_err(_("Malformed message: headers and body not separated "
529 "(with empty line)\n"));
530 if(level != 0)
531 dostat &= ~(1 | 2);
532 fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
533 cnt = lcnt;
534 break;
537 cp = *linedat;
538 jhdrpush:
539 if(!(dostat & 4)){
540 hlp = n_string_push_buf(hlp, cp, (ui32_t)linelen);
541 hlp = n_string_push_c(hlp, '\n');
542 }else{
543 bool_t lblank, isblank;
545 for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
546 char c8;
548 c8 = *cp;
549 if(!(isblank = blankchar(c8)) || !lblank){
550 if((lblank = isblank))
551 c8 = ' ';
552 hlp = n_string_push_c(hlp, c8);
556 continue;
559 jhdrput:
560 /* If it is an ignored header, skip it */
561 *(cp = memchr(hlp->s_dat, ':', hlp->s_len)) = '\0';
562 /* C99 */{
563 size_t i;
565 i = PTR2SIZE(cp - hlp->s_dat);
566 if((doitp != NULL && n_ignore_is_ign(doitp, hlp->s_dat, i)) ||
567 !asccasecmp(hlp->s_dat, "status") ||
568 !asccasecmp(hlp->s_dat, "x-status") ||
569 (action == SEND_MBOX &&
570 (!asccasecmp(hlp->s_dat, "content-length") ||
571 !asccasecmp(hlp->s_dat, "lines")) &&
572 !ok_blook(keep_content_length)))
573 goto jhdrtrunc;
576 /* Dump it */
577 n_COLOUR(
578 if(n_COLOUR_IS_ACTIVE())
579 n_colour_put(n_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
581 *cp = ':';
582 _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NULL,NULL);
583 n_COLOUR(
584 if(n_COLOUR_IS_ACTIVE())
585 n_colour_reset();
587 if(dostat & 4)
588 putc('\n', obuf);
589 /*see below hany = TRU1;*/
591 jhdrtrunc:
592 hlp = n_string_trunc(hlp, 0);
594 hstop = TRU1;
595 if(hlp->s_len > 0)
596 goto jhdrput;
598 /* We've reached end of headers, so eventually force out status: field and
599 * note that we are no longer in header fields */
600 if(dostat & 1){
601 statusput(zmp, obuf, qf, stats);
602 /*see below hany = TRU1;*/
604 if(dostat & 2){
605 xstatusput(zmp, obuf, qf, stats);
606 /*see below hany = TRU1;*/
608 if(/* TODO PART_INFO hany && */ doitp != n_IGNORE_ALL)
609 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
610 } /* C99 */
612 quoteflt_flush(qf);
614 if(ferror(obuf)){
615 rv = -1;
616 goto jleave;
619 jheaders_skip:
620 memset(&mh, 0, sizeof mh);
622 switch (ip->m_mimecontent) {
623 case MIME_822:
624 switch (action) {
625 case SEND_TODISP_PARTS:
626 goto jleave;
627 case SEND_TODISP:
628 case SEND_TODISP_ALL:
629 case SEND_QUOTE:
630 case SEND_QUOTE_ALL:
631 if (ok_blook(rfc822_body_from_)) {
632 if (qf->qf_pfix_len > 0) {
633 size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
634 qf->qf_pfix_len, obuf);
635 if (i == qf->qf_pfix_len && stats != NULL)
636 *stats += i;
638 put_from_(obuf, ip->m_multipart, stats);
640 /* FALLTHRU */
641 case SEND_TOSRCH:
642 case SEND_DECRYPT:
643 goto jmulti;
644 case SEND_TOFILE:
645 case SEND_TOPIPE:
646 put_from_(obuf, ip->m_multipart, stats);
647 /* FALLTHRU */
648 case SEND_MBOX:
649 case SEND_RFC822:
650 case SEND_SHOW:
651 break;
653 break;
654 case MIME_TEXT_HTML:
655 case MIME_TEXT:
656 case MIME_TEXT_PLAIN:
657 switch (action) {
658 case SEND_TODISP:
659 case SEND_TODISP_ALL:
660 case SEND_TODISP_PARTS:
661 case SEND_QUOTE:
662 case SEND_QUOTE_ALL:
663 switch (n_mimetype_handler(&mh, ip, action)) {
664 case MIME_HDL_NULL:
665 if(action != SEND_TODISP_PARTS)
666 break;
667 /* FALLTHRU */
668 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
669 if(mh.mh_msg.l > 0)
670 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
671 qf, stats, NULL, NULL);
672 /* We would print this as plain text, so better force going home */
673 goto jleave;
674 case MIME_HDL_CMD:
675 if(action == SEND_TODISP_PARTS &&
676 (mh.mh_flags & MIME_HDL_COPIOUSOUTPUT))
677 goto jleave;
678 break;
679 case MIME_HDL_TEXT:
680 case MIME_HDL_PTF:
681 if(action == SEND_TODISP_PARTS)
682 goto jleave;
683 break;
684 default:
685 break;
687 /* FALLTRHU */
688 default:
689 break;
691 break;
692 case MIME_DISCARD:
693 if (action != SEND_DECRYPT)
694 goto jleave;
695 break;
696 case MIME_PKCS7:
697 if (action != SEND_MBOX && action != SEND_RFC822 &&
698 action != SEND_SHOW && ip->m_multipart != NULL)
699 goto jmulti;
700 /* FALLTHRU */
701 default:
702 switch (action) {
703 case SEND_TODISP:
704 case SEND_TODISP_ALL:
705 case SEND_TODISP_PARTS:
706 case SEND_QUOTE:
707 case SEND_QUOTE_ALL:
708 switch (n_mimetype_handler(&mh, ip, action)) {
709 default:
710 case MIME_HDL_NULL:
711 if (action != SEND_TODISP && action != SEND_TODISP_ALL &&
712 (level != 0 || cnt))
713 goto jleave;
714 /* FALLTHRU */
715 case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
716 if(mh.mh_msg.l > 0)
717 _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
718 qf, stats, NULL, NULL);
719 /* We would print this as plain text, so better force going home */
720 goto jleave;
721 case MIME_HDL_CMD:
722 if(action == SEND_TODISP_PARTS){
723 if(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)
724 goto jleave;
725 else{
726 _print_part_info(obuf, ip, doitp, level, qf, stats);
727 if(!getapproval(_("Run MIME handler for this part?"), FAL0))
728 goto jleave;
731 break;
732 case MIME_HDL_TEXT:
733 case MIME_HDL_PTF:
734 if(action == SEND_TODISP_PARTS)
735 goto jleave;
736 break;
738 break;
739 case SEND_TOFILE:
740 case SEND_TOPIPE:
741 case SEND_TOSRCH:
742 case SEND_DECRYPT:
743 case SEND_MBOX:
744 case SEND_RFC822:
745 case SEND_SHOW:
746 break;
748 break;
749 case MIME_ALTERNATIVE:
750 if ((action == SEND_TODISP || action == SEND_QUOTE) &&
751 !ok_blook(print_alternatives)) {
752 /* XXX This (a) should not remain (b) should be own fun
753 * TODO (despite the fact that v15 will do this completely differently
754 * TODO by having an action-specific "manager" that will traverse the
755 * TODO parsed MIME tree and decide for each part whether it'll be
756 * TODO displayed or not *before* we walk the tree for doing action */
757 struct mpstack {
758 struct mpstack *outer;
759 struct mimepart *mp;
760 } outermost, * volatile curr, * volatile mpsp;
761 bool_t volatile neednl, hadpart;
762 struct n_sigman smalter;
764 (curr = &outermost)->outer = NULL;
765 curr->mp = ip;
766 neednl = hadpart = FAL0;
768 n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
769 case 0:
770 break;
771 default:
772 rv = -1;
773 goto jalter_leave;
776 for (np = ip->m_multipart;;) {
777 jalter_redo:
778 for (; np != NULL; np = np->m_nextpart) {
779 if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
780 if (neednl)
781 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats,
782 NULL, NULL);
783 _print_part_info(obuf, np, doitp, level, qf, stats);
785 neednl = TRU1;
787 switch (np->m_mimecontent) {
788 case MIME_ALTERNATIVE:
789 case MIME_RELATED:
790 case MIME_DIGEST:
791 case MIME_SIGNED:
792 case MIME_ENCRYPTED:
793 case MIME_MULTI:
794 mpsp = salloc(sizeof *mpsp);
795 mpsp->outer = curr;
796 mpsp->mp = np->m_multipart;
797 curr->mp = np;
798 curr = mpsp;
799 np = mpsp->mp;
800 neednl = FAL0;
801 goto jalter_redo;
802 default:
803 if (hadpart)
804 break;
805 switch (n_mimetype_handler(&mh, np, action)) {
806 default:
807 mh.mh_flags = MIME_HDL_NULL;
808 continue; /* break; break; */
809 case MIME_HDL_CMD:
810 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)){
811 mh.mh_flags = MIME_HDL_NULL;
812 continue; /* break; break; */
814 /* FALLTHRU */
815 case MIME_HDL_PTF:
816 if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
817 struct mimepart *x = np;
819 while ((x = x->m_nextpart) != NULL) {
820 struct mime_handler mhx;
822 if (x->m_mimecontent == MIME_TEXT_PLAIN ||
823 n_mimetype_handler(&mhx, x, action) ==
824 MIME_HDL_TEXT)
825 break;
827 if (x != NULL)
828 continue; /* break; break; */
829 goto jalter_plain;
831 /* FALLTHRU */
832 case MIME_HDL_TEXT:
833 break;
835 /* FALLTHRU */
836 case MIME_TEXT_PLAIN:
837 if (hadpart)
838 break;
839 if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
840 struct mimepart *x = np;
842 /* TODO twice TODO, we should dive into /related and
843 * TODO check whether that has rich parts! */
844 while ((x = x->m_nextpart) != NULL) {
845 struct mime_handler mhx;
847 switch (n_mimetype_handler(&mhx, x, action)) {
848 case MIME_HDL_CMD:
849 if(!(mhx.mh_flags & MIME_HDL_COPIOUSOUTPUT))
850 continue;
851 /* FALLTHRU */
852 case MIME_HDL_PTF:
853 break;
854 default:
855 continue;
857 break;
859 if (x != NULL)
860 continue; /* break; break; */
862 jalter_plain:
863 quoteflt_flush(qf);
864 if (action == SEND_QUOTE && hadpart) {
865 struct quoteflt *dummy = quoteflt_dummy();
866 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
867 NULL,NULL);
868 quoteflt_flush(dummy);
870 hadpart = TRU1;
871 neednl = FAL0;
872 rv = sendpart(zmp, np, obuf, doitp, qf, action,
873 linedat, linesize, stats, level + 1);
874 quoteflt_reset(qf, origobuf);
876 if (rv < 0)
877 curr = &outermost; /* Cause overall loop termination */
878 break;
882 mpsp = curr->outer;
883 if (mpsp == NULL)
884 break;
885 curr = mpsp;
886 np = curr->mp->m_nextpart;
888 jalter_leave:
889 n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
890 goto jleave;
892 /* FALLTHRU */
893 case MIME_RELATED:
894 case MIME_DIGEST:
895 case MIME_SIGNED:
896 case MIME_ENCRYPTED:
897 case MIME_MULTI:
898 switch (action) {
899 case SEND_TODISP:
900 case SEND_TODISP_ALL:
901 case SEND_TODISP_PARTS:
902 case SEND_QUOTE:
903 case SEND_QUOTE_ALL:
904 case SEND_TOFILE:
905 case SEND_TOPIPE:
906 case SEND_TOSRCH:
907 case SEND_DECRYPT:
908 jmulti:
909 if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
910 ip->m_multipart != NULL &&
911 ip->m_multipart->m_mimecontent == MIME_DISCARD &&
912 ip->m_multipart->m_nextpart == NULL) {
913 char const *x = _("[Missing multipart boundary - use `show' "
914 "to display the raw message]\n");
915 _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
916 NULL,NULL);
919 for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
920 bool_t volatile ispipe;
922 if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
923 continue;
925 ispipe = FAL0;
926 switch (action) {
927 case SEND_TOFILE:
928 if (np->m_partstring &&
929 np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
930 break;
931 stats = NULL;
932 /* TODO Always open multipart on /dev/null, it's a hack to be
933 * TODO able to dive into that structure, and still better
934 * TODO than asking the user for something stupid.
935 * TODO oh, wait, we did ask for a filename for this MIME mail,
936 * TODO and that outer container is useless anyway ;-P */
937 if (np->m_multipart != NULL && np->m_mimecontent != MIME_822) {
938 if ((obuf = Fopen(n_path_devnull, "w")) == NULL)
939 continue;
940 } else if ((obuf = newfile(np, &ispipe)) == NULL)
941 continue;
942 if (!ispipe)
943 break;
944 if (sigsetjmp(_send_pipejmp, 1)) {
945 rv = -1;
946 goto jpipe_close;
948 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
949 break;
950 case SEND_TODISP:
951 case SEND_TODISP_ALL:
952 if (ip->m_mimecontent != MIME_ALTERNATIVE &&
953 ip->m_mimecontent != MIME_RELATED &&
954 ip->m_mimecontent != MIME_DIGEST &&
955 ip->m_mimecontent != MIME_SIGNED &&
956 ip->m_mimecontent != MIME_ENCRYPTED &&
957 ip->m_mimecontent != MIME_MULTI)
958 break;
959 _print_part_info(obuf, np, doitp, level, qf, stats);
960 break;
961 case SEND_TODISP_PARTS:
962 case SEND_QUOTE:
963 case SEND_QUOTE_ALL:
964 case SEND_MBOX:
965 case SEND_RFC822:
966 case SEND_SHOW:
967 case SEND_TOSRCH:
968 case SEND_DECRYPT:
969 case SEND_TOPIPE:
970 break;
973 quoteflt_flush(qf);
974 if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
975 np->m_multipart == NULL && ip->m_parent != NULL) {
976 struct quoteflt *dummy = quoteflt_dummy();
977 _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, dummy, stats,
978 NULL,NULL);
979 quoteflt_flush(dummy);
981 if (sendpart(zmp, np, obuf, doitp, qf, action, linedat, linesize,
982 stats, level+1) < 0)
983 rv = -1;
984 quoteflt_reset(qf, origobuf);
986 if (action == SEND_QUOTE) {
987 if (ip->m_mimecontent != MIME_RELATED)
988 break;
990 if (action == SEND_TOFILE && obuf != origobuf) {
991 if (!ispipe)
992 Fclose(obuf);
993 else {
994 jpipe_close:
995 safe_signal(SIGPIPE, SIG_IGN);
996 Pclose(obuf, TRU1);
997 safe_signal(SIGPIPE, oldpipe);
1001 goto jleave;
1002 case SEND_MBOX:
1003 case SEND_RFC822:
1004 case SEND_SHOW:
1005 break;
1007 break;
1010 /* Copy out message body */
1011 if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
1012 --cnt;
1013 switch (ip->m_mime_enc) {
1014 case MIMEE_BIN:
1015 case MIMEE_7B:
1016 case MIMEE_8B:
1017 convert = CONV_NONE;
1018 break;
1019 case MIMEE_QP:
1020 convert = CONV_FROMQP;
1021 break;
1022 case MIMEE_B64:
1023 switch (ip->m_mimecontent) {
1024 case MIME_TEXT:
1025 case MIME_TEXT_PLAIN:
1026 case MIME_TEXT_HTML:
1027 convert = CONV_FROMB64_T;
1028 break;
1029 default:
1030 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1031 case MIME_HDL_TEXT:
1032 case MIME_HDL_PTF:
1033 convert = CONV_FROMB64_T;
1034 break;
1035 default:
1036 convert = CONV_FROMB64;
1037 break;
1039 break;
1041 break;
1042 default:
1043 convert = CONV_NONE;
1046 /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
1047 * TODO cannot mess things up misusing outrest as line buffer */
1048 #ifdef HAVE_ICONV
1049 if (iconvd != (iconv_t)-1) {
1050 n_iconv_close(iconvd);
1051 iconvd = (iconv_t)-1;
1053 #endif
1055 if (action == SEND_DECRYPT || action == SEND_MBOX ||
1056 action == SEND_RFC822 || action == SEND_SHOW)
1057 convert = CONV_NONE;
1058 #ifdef HAVE_ICONV
1059 else if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
1060 action == SEND_TODISP_PARTS ||
1061 action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
1062 action == SEND_TOSRCH || action == SEND_TOFILE) &&
1063 (ip->m_mimecontent == MIME_TEXT_PLAIN ||
1064 ip->m_mimecontent == MIME_TEXT_HTML ||
1065 ip->m_mimecontent == MIME_TEXT ||
1066 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
1067 (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
1068 char const *tcs;
1070 tcs = ok_vlook(ttycharset);
1071 if (asccasecmp(tcs, ip->m_charset) &&
1072 asccasecmp(ok_vlook(charset_7bit), ip->m_charset)) {
1073 iconvd = n_iconv_open(tcs, ip->m_charset);
1074 if (iconvd == (iconv_t)-1 && n_err_no == n_ERR_INVAL) {
1075 n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
1076 /*rv = 1; goto jleave;*/
1080 #endif
1082 switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
1083 case MIME_HDL_CMD:
1084 if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT) &&
1085 action != SEND_TODISP_PARTS)
1086 goto jmhp_default;
1087 /* FALLTHRU */
1088 case MIME_HDL_PTF:
1089 tmpname = NULL;
1090 qbuf = obuf;
1092 term_infd = n_CHILD_FD_PASS;
1093 if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
1094 enum oflags of;
1096 of = OF_RDWR | OF_REGISTER;
1097 if (!(mh.mh_flags & MIME_HDL_TMPF)) {
1098 term_infd = 0;
1099 mh.mh_flags |= MIME_HDL_TMPF_FILL;
1100 of |= OF_UNLINK;
1101 } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
1102 of |= OF_REGISTER_UNLINK;
1104 if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
1105 (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
1106 of)) == NULL)
1107 goto jesend;
1109 if (mh.mh_flags & MIME_HDL_TMPF) {
1110 tmpname = savestr(cp);
1111 Ftmp_free(&cp);
1114 if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
1115 if (term_infd == 0)
1116 term_infd = fileno(pbuf);
1117 goto jsend;
1121 jpipe_for_real:
1122 pbuf = _pipefile(&mh, ip, n_UNVOLATILE(&qbuf), tmpname, term_infd);
1123 if (pbuf == NULL) {
1124 jesend:
1125 pbuf = qbuf = NULL;
1126 rv = -1;
1127 goto jend;
1128 } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
1129 pbuf = qbuf = NULL;
1130 goto jend;
1132 tmpname = NULL;
1133 action = SEND_TOPIPE;
1134 if (pbuf != qbuf) {
1135 oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
1136 if (sigsetjmp(_send_pipejmp, 1))
1137 goto jend;
1139 break;
1141 default:
1142 jmhp_default:
1143 mh.mh_flags = MIME_HDL_NULL;
1144 pbuf = qbuf = obuf;
1145 break;
1148 jsend:
1150 bool_t volatile eof;
1151 ui32_t save_qf_pfix_len = qf->qf_pfix_len;
1152 ui64_t *save_stats = stats;
1154 if (pbuf != origobuf) {
1155 qf->qf_pfix_len = 0; /* XXX legacy (remove filter instead) */
1156 stats = NULL;
1158 eof = FAL0;
1159 outrest.s = inrest.s = NULL;
1160 outrest.l = inrest.l = 0;
1162 if (pbuf == qbuf) {
1163 __sendp_sig = 0;
1164 __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
1165 if (sigsetjmp(__sendp_actjmp, 1)) {
1166 n_pstate &= ~n_PS_BASE64_STRIP_CR;/* (but protected by outer sigman) */
1167 if (outrest.s != NULL)
1168 free(outrest.s);
1169 if (inrest.s != NULL)
1170 free(inrest.s);
1171 #ifdef HAVE_ICONV
1172 if (iconvd != (iconv_t)-1)
1173 n_iconv_close(iconvd);
1174 #endif
1175 safe_signal(SIGPIPE, __sendp_opipe);
1176 n_raise(__sendp_sig);
1180 quoteflt_reset(qf, pbuf);
1181 if((dostat & 4) && pbuf == origobuf) /* TODO */
1182 n_pstate |= n_PS_BASE64_STRIP_CR;
1183 while (!eof && fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0)) {
1184 joutln:
1185 if (_out(*linedat, linelen, pbuf, convert, action, qf, stats, &outrest,
1186 (action & _TD_EOF ? NULL : &inrest)) < 0 || ferror(pbuf)) {
1187 rv = -1; /* XXX Should bail away?! */
1188 break;
1191 if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
1192 linelen = 0;
1193 if(eof || inrest.l == 0)
1194 action |= _TD_EOF;
1195 eof = eof ? TRU1 : TRUM1;
1196 goto joutln;
1198 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1199 action &= ~_TD_EOF;
1201 /* TODO HACK: when sending to the display we yet get fooled if a message
1202 * TODO doesn't end in a newline, because of our input/output 1:1.
1203 * TODO This should be handled automatically by a display filter, then */
1204 if(rv >= 0 && !qf->qf_nl_last &&
1205 (action == SEND_TODISP || action == SEND_TODISP_ALL))
1206 rv = quoteflt_push(qf, "\n", 1);
1208 quoteflt_flush(qf);
1210 if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
1211 mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
1212 fflush(pbuf);
1213 really_rewind(pbuf);
1214 /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
1215 goto jpipe_for_real;
1218 if (pbuf == qbuf)
1219 safe_signal(SIGPIPE, __sendp_opipe);
1221 if (outrest.s != NULL)
1222 free(outrest.s);
1223 if (inrest.s != NULL)
1224 free(inrest.s);
1226 if (pbuf != origobuf) {
1227 qf->qf_pfix_len = save_qf_pfix_len;
1228 stats = save_stats;
1232 jend:
1233 if (pbuf != qbuf) {
1234 safe_signal(SIGPIPE, SIG_IGN);
1235 Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
1236 safe_signal(SIGPIPE, oldpipe);
1237 if (rv >= 0 && qbuf != NULL && qbuf != obuf)
1238 pipecpy(qbuf, obuf, origobuf, qf, stats);
1240 #ifdef HAVE_ICONV
1241 if (iconvd != (iconv_t)-1)
1242 n_iconv_close(iconvd);
1243 #endif
1244 jleave:
1245 NYD_LEAVE;
1246 return rv;
1249 static FILE *
1250 newfile(struct mimepart *ip, bool_t volatile *ispipe)
1252 struct str in, out;
1253 char *f;
1254 FILE *fp;
1255 NYD_ENTER;
1257 f = ip->m_filename;
1258 *ispipe = FAL0;
1260 if (f != NULL && f != (char*)-1) {
1261 in.s = f;
1262 in.l = strlen(f);
1263 makeprint(&in, &out);
1264 out.l = delctrl(out.s, out.l);
1265 f = savestrbuf(out.s, out.l);
1266 free(out.s);
1269 /* In interactive mode, let user perform all kind of expansions as desired,
1270 * and offer |SHELL-SPEC pipe targets, too */
1271 if (n_psonce & n_PSO_INTERACTIVE) {
1272 struct str prompt;
1273 struct n_string shou, *shoup;
1274 char *f2, *f3;
1276 shoup = n_string_creat_auto(&shou);
1278 /* TODO Generic function which asks for filename.
1279 * TODO If the current part is the first textpart the target
1280 * TODO is implicit from outer `write' etc! */
1281 /* I18N: Filename input prompt with file type indication */
1282 str_concat_csvl(&prompt, _("Enter filename for part "),
1283 (ip->m_partstring != NULL ? ip->m_partstring : n_qm),
1284 " (", ip->m_ct_type_plain, "): ", NULL);
1285 jgetname:
1286 f2 = n_go_input_cp(n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_HIST_ADD,
1287 prompt.s, ((f != (char*)-1 && f != NULL)
1288 ? n_shexp_quote_cp(f, FAL0) : NULL));
1289 if(f2 != NULL){
1290 in.s = n_UNCONST(f2);
1291 in.l = UIZ_MAX;
1292 if((n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
1293 n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_TRIM_IFSSPACE |
1294 n_SHEXP_PARSE_LOG | n_SHEXP_PARSE_IGNORE_EMPTY),
1295 shoup, &in, NULL
1296 ) & (n_SHEXP_STATE_STOP |
1297 n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)
1298 ) != (n_SHEXP_STATE_STOP | n_SHEXP_STATE_OUTPUT))
1299 goto jgetname;
1300 f2 = n_string_cp(shoup);
1302 if (f2 == NULL || *f2 == '\0') {
1303 if (n_poption & n_PO_D_V)
1304 n_err(_("... skipping this\n"));
1305 n_string_gut(shoup);
1306 fp = NULL;
1307 goto jleave;
1310 if (*f2 == '|')
1311 /* Pipes are expanded by the shell */
1312 f = f2;
1313 else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
1314 /* (Error message written by fexpand()) */
1315 goto jgetname;
1316 else
1317 f = f3;
1319 n_string_gut(shoup);
1322 if (f == NULL || f == (char*)-1 || *f == '\0')
1323 fp = NULL;
1324 else if (n_psonce & n_PSO_INTERACTIVE) {
1325 if (*f == '|') {
1326 fp = Popen(&f[1], "w", ok_vlook(SHELL), NULL, 1);
1327 if (!(*ispipe = (fp != NULL)))
1328 n_perr(f, 0);
1329 } else if ((fp = Fopen(f, "w")) == NULL)
1330 n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
1331 } else {
1332 /* Be very picky in non-interactive mode: actively disallow pipes,
1333 * prevent directory separators, and any filename member that would
1334 * become expanded by the shell if the name would be echo(1)ed */
1335 if(n_anyof_cp("/" n_SHEXP_MAGIC_PATH_CHARS, f)){
1336 char c;
1338 for(out.s = salloc((strlen(f) * 3) +1), out.l = 0; (c = *f++) != '\0';)
1339 if(strchr("/" n_SHEXP_MAGIC_PATH_CHARS, c)){
1340 out.s[out.l++] = '%';
1341 n_c_to_hex_base16(&out.s[out.l], c);
1342 out.l += 2;
1343 }else
1344 out.s[out.l++] = c;
1345 out.s[out.l] = '\0';
1346 f = out.s;
1349 /* Avoid overwriting of existing files */
1350 while((fp = Fopen(f, "wx")) == NULL){
1351 int e;
1353 if((e = n_err_no) != n_ERR_EXIST){
1354 n_err(_("Cannot open %s: %s\n"),
1355 n_shexp_quote_cp(f, FAL0), n_err_to_doc(e));
1356 break;
1359 if(ip->m_partstring != NULL)
1360 f = savecatsep(f, '#', ip->m_partstring);
1361 else
1362 f = savecat(f, "#.");
1365 jleave:
1366 NYD_LEAVE;
1367 return fp;
1370 static void
1371 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
1372 ui64_t *stats)
1374 char *line = NULL; /* TODO line pool */
1375 size_t linesize = 0, linelen, cnt;
1376 ssize_t all_sz, sz;
1377 NYD_ENTER;
1379 fflush(pipebuf);
1380 rewind(pipebuf);
1381 cnt = (size_t)fsize(pipebuf);
1382 all_sz = 0;
1384 quoteflt_reset(qf, outbuf);
1385 while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
1386 if ((sz = quoteflt_push(qf, line, linelen)) < 0)
1387 break;
1388 all_sz += sz;
1390 if ((sz = quoteflt_flush(qf)) > 0)
1391 all_sz += sz;
1392 if (line)
1393 free(line);
1395 if (all_sz > 0 && outbuf == origobuf && stats != NULL)
1396 *stats += all_sz;
1397 Fclose(pipebuf);
1398 NYD_LEAVE;
1401 static void
1402 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1403 ui64_t *stats)
1405 char statout[3], *cp = statout;
1406 NYD_ENTER;
1408 if (mp->m_flag & MREAD)
1409 *cp++ = 'R';
1410 if (!(mp->m_flag & MNEW))
1411 *cp++ = 'O';
1412 *cp = 0;
1413 if (statout[0]) {
1414 int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
1415 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), statout);
1416 if (i > 0 && stats != NULL)
1417 *stats += i;
1419 NYD_LEAVE;
1422 static void
1423 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
1424 ui64_t *stats)
1426 char xstatout[4];
1427 char *xp = xstatout;
1428 NYD_ENTER;
1430 if (mp->m_flag & MFLAGGED)
1431 *xp++ = 'F';
1432 if (mp->m_flag & MANSWERED)
1433 *xp++ = 'A';
1434 if (mp->m_flag & MDRAFTED)
1435 *xp++ = 'T';
1436 *xp = 0;
1437 if (xstatout[0]) {
1438 int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
1439 (qf->qf_pfix_len > 0 ? qf->qf_pfix : 0), xstatout);
1440 if (i > 0 && stats != NULL)
1441 *stats += i;
1443 NYD_LEAVE;
1446 static void
1447 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
1449 char const *froma, *date, *nl;
1450 int i;
1451 NYD_ENTER;
1453 if (ip != NULL && ip->m_from != NULL) {
1454 froma = ip->m_from;
1455 date = n_time_ctime(ip->m_time, NULL);
1456 nl = "\n";
1457 } else {
1458 froma = ok_vlook(LOGNAME);
1459 date = time_current.tc_ctime;
1460 nl = n_empty;
1463 n_COLOUR(
1464 if(n_COLOUR_IS_ACTIVE())
1465 n_colour_put(n_COLOUR_ID_VIEW_FROM_, NULL);
1467 i = fprintf(fp, "From %s %s%s", froma, date, nl);
1468 n_COLOUR(
1469 if(n_COLOUR_IS_ACTIVE())
1470 n_colour_reset();
1472 if (i > 0 && stats != NULL)
1473 *stats += i;
1474 NYD_LEAVE;
1477 FL int
1478 sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp,
1479 char const *prefix, enum sendaction action, ui64_t *stats)
1481 struct n_sigman linedat_protect;
1482 struct quoteflt qf;
1483 FILE *ibuf;
1484 enum mime_parse_flags mpf;
1485 struct mimepart *ip;
1486 size_t linesize, cnt, sz, i;
1487 char *linedat;
1488 int rv, c;
1489 NYD_ENTER;
1491 time_current_update(&time_current, TRU1);
1492 rv = -1;
1493 linedat = NULL;
1494 linesize = 0;
1495 quoteflt_init(&qf, prefix);
1497 n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
1498 case 0:
1499 break;
1500 default:
1501 goto jleave;
1504 if (mp == dot && action != SEND_TOSRCH)
1505 n_pstate |= n_PS_DID_PRINT_DOT;
1506 if (stats != NULL)
1507 *stats = 0;
1509 /* First line is the From_ line, so no headers there to worry about */
1510 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1511 goto jleave;
1513 cnt = mp->m_size;
1514 sz = 0;
1516 bool_t nozap;
1517 char const *cpre = n_empty, *csuf = n_empty;
1519 #ifdef HAVE_COLOUR
1520 if(n_COLOUR_IS_ACTIVE()){
1521 struct n_colour_pen *cpen;
1522 struct str const *sp;
1524 cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
1525 if((sp = n_colour_pen_to_str(cpen)) != NULL){
1526 cpre = sp->s;
1527 sp = n_colour_reset_to_str();
1528 if(sp != NULL)
1529 csuf = sp->s;
1532 #endif
1534 nozap = (doitp != n_IGNORE_ALL && doitp != n_IGNORE_FWD &&
1535 action != SEND_RFC822 &&
1536 !n_ignore_is_ign(doitp, "from_", sizeof("from_") -1));
1537 if (mp->m_flag & MNOFROM) {
1538 if (nozap)
1539 sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
1540 cpre, (int)qf.qf_pfix_len,
1541 (qf.qf_pfix_len != 0 ? qf.qf_pfix : n_empty), fakefrom(mp),
1542 n_time_ctime(mp->m_time, NULL), csuf);
1543 } else if (nozap) {
1544 if (qf.qf_pfix_len > 0) {
1545 i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
1546 if (i != qf.qf_pfix_len)
1547 goto jleave;
1548 sz += i;
1550 #ifdef HAVE_COLOUR
1551 if(*cpre != '\0'){
1552 fputs(cpre, obuf);
1553 cpre = (char const*)0x1;
1555 #endif
1557 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1558 #ifdef HAVE_COLOUR
1559 if(c == '\n' && *csuf != '\0'){
1560 cpre = (char const*)0x1;
1561 fputs(csuf, obuf);
1563 #endif
1564 putc(c, obuf);
1565 ++sz;
1566 --cnt;
1567 if (c == '\n')
1568 break;
1571 #ifdef HAVE_COLOUR
1572 if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
1573 fputs(csuf, obuf);
1574 #endif
1575 } else {
1576 while (cnt > 0 && (c = getc(ibuf)) != EOF) {
1577 --cnt;
1578 if (c == '\n')
1579 break;
1583 if (sz > 0 && stats != NULL)
1584 *stats += sz;
1586 mpf = MIME_PARSE_NONE;
1587 if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
1588 mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT;
1589 if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
1590 action == SEND_QUOTE || action == SEND_QUOTE_ALL)
1591 mpf |= MIME_PARSE_FOR_USER_CONTEXT;
1592 if ((ip = mime_parse_msg(mp, mpf)) == NULL)
1593 goto jleave;
1595 rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
1596 stats, 0);
1598 n_sigman_cleanup_ping(&linedat_protect);
1599 jleave:
1600 n_pstate &= ~n_PS_BASE64_STRIP_CR;
1601 quoteflt_destroy(&qf);
1602 if(linedat != NULL)
1603 free(linedat);
1604 NYD_LEAVE;
1605 n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
1606 return rv;
1609 /* s-it-mode */