"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/send.c" (8 Aug 2018, 51858 Bytes) of package /linux/misc/s-nail-14.9.11.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "send.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Message content preparation (sendmp()).
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    6  * SPDX-License-Identifier: BSD-3-Clause
    7  */
    8 /*
    9  * Copyright (c) 1980, 1993
   10  *      The Regents of the University of California.  All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 #undef n_FILE
   37 #define n_FILE send
   38 
   39 #ifndef HAVE_AMALGAMATION
   40 # include "nail.h"
   41 #endif
   42 
   43 static sigjmp_buf _send_pipejmp;
   44 
   45 /* Going for user display, print Part: info string */
   46 static void          _print_part_info(FILE *obuf, struct mimepart const *mpp,
   47                         struct n_ignore const *doitp, int level,
   48                         struct quoteflt *qf, ui64_t *stats);
   49 
   50 /* Create a pipe; if mpp is not NULL, place some n_PIPEENV_* environment
   51  * variables accordingly */
   52 static FILE *        _pipefile(struct mime_handler *mhp,
   53                         struct mimepart const *mpp, FILE **qbuf,
   54                         char const *tmpname, int term_infd);
   55 
   56 /* Call mime_write() as approbiate and adjust statistics */
   57 n_INLINE ssize_t      _out(char const *buf, size_t len, FILE *fp,
   58                         enum conversion convert, enum sendaction action,
   59                         struct quoteflt *qf, ui64_t *stats, struct str *outrest,
   60                         struct str *inrest);
   61 
   62 /* Simply (!) print out a LF */
   63 static bool_t a_send_out_nl(FILE *fp, ui64_t *stats);
   64 
   65 /* SIGPIPE handler */
   66 static void          _send_onpipe(int signo);
   67 
   68 /* Send one part */
   69 static int           sendpart(struct message *zmp, struct mimepart *ip,
   70                         FILE *obuf, struct n_ignore const *doitp,
   71                         struct quoteflt *qf, enum sendaction action,
   72                         char **linedat, size_t *linesize,
   73                         ui64_t *stats, int level);
   74 
   75 /* Get a file for an attachment */
   76 static FILE *        newfile(struct mimepart *ip, bool_t volatile *ispipe);
   77 
   78 static void          pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
   79                         struct quoteflt *qf, ui64_t *stats);
   80 
   81 /* Output a reasonable looking status field */
   82 static void          statusput(const struct message *mp, FILE *obuf,
   83                         struct quoteflt *qf, ui64_t *stats);
   84 static void          xstatusput(const struct message *mp, FILE *obuf,
   85                         struct quoteflt *qf, ui64_t *stats);
   86 
   87 static void          put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats);
   88 
   89 static void
   90 _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */
   91    struct n_ignore const *doitp, int level, struct quoteflt *qf, ui64_t *stats)
   92 {
   93    char buf[64];
   94    struct str ti, to;
   95    bool_t want_ct, needsep;
   96    struct str const *cpre, *csuf;
   97    char const *cp;
   98    NYD2_ENTER;
   99 
  100    cpre = csuf = NULL;
  101 #ifdef HAVE_COLOUR
  102    if(n_COLOUR_IS_ACTIVE()){
  103       struct n_colour_pen *cpen;
  104 
  105       cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_PARTINFO, NULL);
  106       if((cpre = n_colour_pen_to_str(cpen)) != NULL)
  107          csuf = n_colour_reset_to_str();
  108    }
  109 #endif
  110 
  111    /* Take care of "99.99", i.e., 5 */
  112    if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0')
  113       cp = n_qm;
  114    if (level || (cp[0] != '1' && cp[1] == '\0') || (cp[0] == '1' && /* TODO */
  115          cp[1] == '.' && cp[2] != '1')) /* TODO code should not look like so */
  116       _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  117 
  118    /* Part id, content-type, encoding, charset */
  119    if (cpre != NULL)
  120       _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  121    _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  122    _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  123 
  124    to.l = snprintf(buf, sizeof buf, " %" PRIuZ "/%" PRIuZ " ",
  125          (uiz_t)mpp->m_lines, (uiz_t)mpp->m_size);
  126    _out(buf, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  127 
  128    needsep = FAL0;
  129 
  130     if((cp = mpp->m_ct_type_usr_ovwr) != NULL){
  131       _out("+", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  132       want_ct = TRU1;
  133    }else if((want_ct = n_ignore_is_ign(doitp,
  134          "content-type", sizeof("content-type") -1)))
  135       cp = mpp->m_ct_type_plain;
  136    if (want_ct &&
  137          (to.l = strlen(cp)) > 30 && is_asccaseprefix("application/", cp)) {
  138       size_t const al = sizeof("appl../") -1, fl = sizeof("application/") -1;
  139       size_t i = to.l - fl;
  140       char *x = n_autorec_alloc(al + i +1);
  141 
  142       memcpy(x, "appl../", al);
  143       memcpy(x + al, cp + fl, i +1);
  144       cp = x;
  145       to.l = al + i;
  146    }
  147    if(cp != NULL){
  148       _out(cp, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  149       needsep = TRU1;
  150    }
  151 
  152    if(mpp->m_multipart == NULL/* TODO */ && (cp = mpp->m_ct_enc) != NULL &&
  153          (!asccasecmp(cp, "7bit") ||
  154           n_ignore_is_ign(doitp, "content-transfer-encoding",
  155             sizeof("content-transfer-encoding") -1))){
  156       if(needsep)
  157          _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  158       if (to.l > 25 && !asccasecmp(cp, "quoted-printable"))
  159          cp = "qu.-pr.";
  160       _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  161       needsep = TRU1;
  162    }
  163 
  164    if (want_ct && mpp->m_multipart == NULL/* TODO */ &&
  165          (cp = mpp->m_charset) != NULL) {
  166       if(needsep)
  167          _out(", ", 2, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  168       _out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  169    }
  170 
  171    needsep = !needsep;
  172    _out(&" --]"[needsep], 4 - needsep,
  173       obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  174    if (csuf != NULL)
  175       _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  176    _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  177 
  178    /* */
  179    if (mpp->m_content_info & CI_MIME_ERRORS) {
  180       if (cpre != NULL)
  181          _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
  182             NULL, NULL);
  183       _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  184 
  185       ti.l = strlen(ti.s = n_UNCONST(_("Defective MIME structure")));
  186       makeprint(&ti, &to);
  187       to.l = delctrl(to.s, to.l);
  188       _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  189       n_free(to.s);
  190 
  191       _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  192       if (csuf != NULL)
  193          _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
  194             NULL, NULL);
  195       _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  196    }
  197 
  198    /* Content-Description */
  199    if (n_ignore_is_ign(doitp, "content-description", 19) &&
  200          (cp = mpp->m_content_description) != NULL && *cp != '\0') {
  201       if (cpre != NULL)
  202          _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
  203             NULL, NULL);
  204       _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  205 
  206       ti.l = strlen(ti.s = n_UNCONST(mpp->m_content_description));
  207       mime_fromhdr(&ti, &to, TD_ISPR | TD_ICONV);
  208       _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  209       n_free(to.s);
  210 
  211       _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  212       if (csuf != NULL)
  213          _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
  214             NULL, NULL);
  215       _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  216    }
  217 
  218    /* Filename */
  219    if (n_ignore_is_ign(doitp, "content-disposition", 19) &&
  220          mpp->m_filename != NULL && *mpp->m_filename != '\0') {
  221       if (cpre != NULL)
  222          _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
  223             NULL, NULL);
  224       _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  225 
  226       ti.l = strlen(ti.s = mpp->m_filename);
  227       makeprint(&ti, &to);
  228       to.l = delctrl(to.s, to.l);
  229       _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  230       n_free(to.s);
  231 
  232       _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  233       if (csuf != NULL)
  234          _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats,
  235             NULL, NULL);
  236       _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL);
  237    }
  238    NYD2_LEAVE;
  239 }
  240 
  241 static FILE *
  242 _pipefile(struct mime_handler *mhp, struct mimepart const *mpp, FILE **qbuf,
  243    char const *tmpname, int term_infd)
  244 {
  245    struct str s;
  246    char const *env_addon[9 +8/*v15*/], *cp, *sh;
  247    size_t i;
  248    FILE *rbuf;
  249    NYD_ENTER;
  250 
  251    rbuf = *qbuf;
  252 
  253    if (mhp->mh_flags & MIME_HDL_ISQUOTE) {
  254       if ((*qbuf = Ftmp(NULL, "sendp", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
  255             NULL) {
  256          n_perr(_("tmpfile"), 0);
  257          *qbuf = rbuf;
  258       }
  259    }
  260 
  261    if ((mhp->mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF) {
  262       union {int (*ptf)(void); char const *sh;} u;
  263 
  264       fflush(*qbuf);
  265       if (*qbuf != n_stdout) /* xxx never?  v15: it'll be a filter anyway */
  266          fflush(n_stdout);
  267 
  268       u.ptf = mhp->mh_ptf;
  269       if((rbuf = Popen((char*)-1, "W", u.sh, NULL, fileno(*qbuf))) == NULL)
  270          goto jerror;
  271       goto jleave;
  272    }
  273 
  274    i = 0;
  275 
  276    /* MAILX_FILENAME */
  277    if (mpp == NULL || (cp = mpp->m_filename) == NULL)
  278       cp = n_empty;
  279    env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME, "=", cp, NULL)->s;
  280 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME", "=", cp, NULL)->s;/*v15*/
  281 
  282    /* MAILX_FILENAME_GENERATED *//* TODO pathconf NAME_MAX; but user can create
  283     * TODO a file wherever he wants!  *Do* create a zero-size temporary file
  284     * TODO and give *that* path as MAILX_FILENAME_TEMPORARY, clean it up once
  285     * TODO the pipe returns?  Like this we *can* verify path/name issues! */
  286    cp = n_random_create_cp(n_MIN(NAME_MAX / 4, 16), NULL);
  287    env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_FILENAME_GENERATED, "=", cp,
  288          NULL)->s;
  289 env_addon[i++] = str_concat_csvl(&s, "NAIL_FILENAME_GENERATED", "=", cp,/*v15*/
  290       NULL)->s;
  291 
  292    /* MAILX_CONTENT{,_EVIDENCE} */
  293    if (mpp == NULL || (cp = mpp->m_ct_type_plain) == NULL)
  294       cp = n_empty;
  295    env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT, "=", cp, NULL)->s;
  296 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT", "=", cp, NULL)->s;/*v15*/
  297 
  298    if (mpp != NULL && mpp->m_ct_type_usr_ovwr != NULL)
  299       cp = mpp->m_ct_type_usr_ovwr;
  300    env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_CONTENT_EVIDENCE, "=", cp,
  301          NULL)->s;
  302 env_addon[i++] = str_concat_csvl(&s, "NAIL_CONTENT_EVIDENCE", "=", cp,/* v15 */
  303       NULL)->s;
  304 
  305    /* message/external-body, access-type=url */
  306    env_addon[i++] = str_concat_csvl(&s, n_PIPEENV_EXTERNAL_BODY_URL, "=",
  307          ((mpp != NULL && (cp = mpp->m_external_body_url) != NULL
  308             ) ? cp : n_empty), NULL)->s;
  309 
  310    /* MAILX_FILENAME_TEMPORARY? */
  311    if (tmpname != NULL) {
  312       env_addon[i++] = str_concat_csvl(&s,
  313             n_PIPEENV_FILENAME_TEMPORARY, "=", tmpname, NULL)->s;
  314 env_addon[i++] = str_concat_csvl(&s,
  315          "NAIL_FILENAME_TEMPORARY", "=", tmpname, NULL)->s;/* v15 */
  316    }
  317 
  318    /* TODO we should include header information, especially From:, so
  319     * TODO that same-origin can be tested for e.g. external-body!!! */
  320 
  321    env_addon[i] = NULL;
  322    sh = ok_vlook(SHELL);
  323 
  324    if (mhp->mh_flags & MIME_HDL_NEEDSTERM) {
  325       sigset_t nset;
  326       int pid;
  327 
  328       sigemptyset(&nset);
  329       pid = n_child_run(sh, &nset, term_infd, n_CHILD_FD_PASS, "-c",
  330             mhp->mh_shell_cmd, NULL, env_addon, NULL);
  331       rbuf = (pid < 0) ? NULL : (FILE*)-1;
  332    } else {
  333       rbuf = Popen(mhp->mh_shell_cmd, "W", sh, env_addon,
  334             (mhp->mh_flags & MIME_HDL_ASYNC ? -1 : fileno(*qbuf)));
  335 jerror:
  336       if (rbuf == NULL)
  337          n_err(_("Cannot run MIME type handler: %s: %s\n"),
  338             mhp->mh_msg, n_err_to_doc(n_err_no));
  339       else {
  340          fflush(*qbuf);
  341          if (*qbuf != n_stdout)
  342             fflush(n_stdout);
  343       }
  344    }
  345 jleave:
  346    NYD_LEAVE;
  347    return rbuf;
  348 }
  349 
  350 n_INLINE ssize_t
  351 _out(char const *buf, size_t len, FILE *fp, enum conversion convert, enum
  352    sendaction action, struct quoteflt *qf, ui64_t *stats, struct str *outrest,
  353    struct str *inrest)
  354 {
  355    ssize_t sz = 0, n;
  356    int flags;
  357    NYD_ENTER;
  358 
  359    /* TODO We should not need is_head() here, i think in v15 the actual Mailbox
  360     * TODO subclass should detect such From_ cases and either reencode the part
  361     * TODO in question, or perform From_ quoting as necessary!?!?!?  How?!? */
  362    /* C99 */{
  363       bool_t from_;
  364 
  365       if((action == SEND_MBOX || action == SEND_DECRYPT) &&
  366             (from_ = is_head(buf, len, TRU1))){
  367          if(from_ != TRUM1 || ok_blook(mbox_rfc4155)){
  368             putc('>', fp);
  369             ++sz;
  370          }
  371       }
  372    }
  373 
  374    flags = ((int)action & _TD_EOF);
  375    action &= ~_TD_EOF;
  376    n = mime_write(buf, len, fp,
  377          action == SEND_MBOX ? CONV_NONE : convert,
  378          flags | ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
  379             action == SEND_TODISP_PARTS ||
  380             action == SEND_QUOTE || action == SEND_QUOTE_ALL)
  381          ?  TD_ISPR | TD_ICONV
  382          : (action == SEND_TOSRCH || action == SEND_TOPIPE ||
  383                action == SEND_TOFILE)
  384             ? TD_ICONV : (action == SEND_SHOW ? TD_ISPR : TD_NONE)),
  385          qf, outrest, inrest);
  386    if (n < 0)
  387       sz = n;
  388    else if (n > 0) {
  389       sz += n;
  390       if (stats != NULL)
  391          *stats += sz;
  392    }
  393    NYD_LEAVE;
  394    return sz;
  395 }
  396 
  397 static bool_t
  398 a_send_out_nl(FILE *fp, ui64_t *stats){
  399    struct quoteflt *qf;
  400    bool_t rv;
  401    NYD2_ENTER;
  402 
  403    quoteflt_reset(qf = quoteflt_dummy(), fp);
  404    rv = (_out("\n", 1, fp, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL) > 0);
  405    quoteflt_flush(qf);
  406    NYD2_LEAVE;
  407    return rv;
  408 }
  409 
  410 static void
  411 _send_onpipe(int signo)
  412 {
  413    NYD_X; /* Signal handler */
  414    n_UNUSED(signo);
  415    siglongjmp(_send_pipejmp, 1);
  416 }
  417 
  418 static sigjmp_buf       __sendp_actjmp; /* TODO someday.. */
  419 static int              __sendp_sig; /* TODO someday.. */
  420 static sighandler_type  __sendp_opipe;
  421 static void
  422 __sendp_onsig(int sig) /* TODO someday, we won't need it no more */
  423 {
  424    NYD_X; /* Signal handler */
  425    __sendp_sig = sig;
  426    siglongjmp(__sendp_actjmp, 1);
  427 }
  428 
  429 static int
  430 sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf,
  431    struct n_ignore const *doitp, struct quoteflt *qf,
  432    enum sendaction volatile action,
  433    char **linedat, size_t *linesize, ui64_t * volatile stats, int level)
  434 {
  435    int volatile rv = 0;
  436    struct mime_handler mh;
  437    struct str outrest, inrest;
  438    char *cp;
  439    char const * volatile tmpname = NULL;
  440    size_t linelen, cnt;
  441    int volatile dostat, term_infd;
  442    int c;
  443    struct mimepart * volatile np;
  444    FILE * volatile ibuf = NULL, * volatile pbuf = obuf,
  445       * volatile qbuf = obuf, *origobuf = obuf;
  446    enum conversion volatile convert;
  447    sighandler_type volatile oldpipe = SIG_DFL;
  448    NYD_ENTER;
  449 
  450    n_UNINIT(term_infd, 0);
  451    n_UNINIT(cnt, 0);
  452 
  453    quoteflt_reset(qf, obuf);
  454 
  455 #if 0 /* TODO PART_INFO should be displayed here!! search PART_INFO */
  456    if(ip->m_mimecontent != MIME_DISCARD && level > 0)
  457       _print_part_info(obuf, ip, doitp, level, qf, stats);
  458 #endif
  459 
  460    if (ip->m_mimecontent == MIME_PKCS7) {
  461       if (ip->m_multipart &&
  462             action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
  463          goto jheaders_skip;
  464    }
  465 
  466    dostat = 0;
  467    if (level == 0 && action != SEND_TODISP_PARTS) {
  468       if (doitp != NULL) {
  469          if (!n_ignore_is_ign(doitp, "status", 6))
  470             dostat |= 1;
  471          if (!n_ignore_is_ign(doitp, "x-status", 8))
  472             dostat |= 2;
  473       } else
  474          dostat = 3;
  475    }
  476 
  477    if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL) {
  478       rv = -1;
  479       goto jleave;
  480    }
  481 
  482    if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
  483          action == SEND_TODISP_PARTS ||
  484          action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
  485          action == SEND_TOSRCH)
  486       dostat |= 4;
  487 
  488    cnt = ip->m_size;
  489 
  490    if (ip->m_mimecontent == MIME_DISCARD)
  491       goto jheaders_skip;
  492 
  493    if (!(ip->m_flag & MNOFROM))
  494       while (cnt && (c = getc(ibuf)) != EOF) {
  495          cnt--;
  496          if (c == '\n')
  497             break;
  498       }
  499    convert = (dostat & 4) ? CONV_FROMHDR : CONV_NONE;
  500 
  501    /* Work the headers */
  502    /* C99 */{
  503    struct n_string hl, *hlp;
  504    size_t lineno = 0;
  505    bool_t hstop/*see below, hany*/;
  506 
  507    hlp = n_string_creat_auto(&hl); /* TODO pool [or, v15: filter!] */
  508    /* Reserve three lines, still not enough for references and DKIM etc. */
  509    hlp = n_string_reserve(hlp, n_MAX(MIME_LINELEN, MIME_LINELEN_RFC2047) * 3);
  510 
  511    for(hstop = /*see below hany =*/ FAL0; !hstop;){
  512       size_t lcnt;
  513 
  514       lcnt = cnt;
  515       if(fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0) == NULL)
  516          break;
  517       ++lineno;
  518       if (linelen == 0 || (cp = *linedat)[0] == '\n')
  519          /* If line is blank, we've reached end of headers */
  520          break;
  521       if(cp[linelen - 1] == '\n'){
  522          cp[--linelen] = '\0';
  523          if(linelen == 0)
  524             break;
  525       }
  526 
  527       /* Are we in a header? */
  528       if(hlp->s_len > 0){
  529          if(!blankchar(*cp)){
  530             fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
  531             cnt = lcnt;
  532             goto jhdrput;
  533          }
  534          goto jhdrpush;
  535       }else{
  536          /* Pick up the header field if we have one */
  537          while((c = *cp) != ':' && !spacechar(c) && c != '\0')
  538             ++cp;
  539          for(;;){
  540             if(!spacechar(c) || c == '\0')
  541                break;
  542             c = *++cp;
  543          }
  544          if(c != ':'){
  545             /* That won't work with MIME when saving etc., before v15 */
  546             if (lineno != 1)
  547                /* XXX This disturbs, and may happen multiple times, and we
  548                 * XXX cannot heal it for multipart except for display <v15 */
  549                n_err(_("Malformed message: headers and body not separated "
  550                   "(with empty line)\n"));
  551             if(level != 0)
  552                dostat &= ~(1 | 2);
  553             fseek(ibuf, -(off_t)(lcnt - cnt), SEEK_CUR);
  554             cnt = lcnt;
  555             break;
  556          }
  557 
  558          cp = *linedat;
  559 jhdrpush:
  560          if(!(dostat & 4)){
  561             hlp = n_string_push_buf(hlp, cp, (ui32_t)linelen);
  562             hlp = n_string_push_c(hlp, '\n');
  563          }else{
  564             bool_t lblank, isblank;
  565 
  566             for(lblank = FAL0, lcnt = 0; lcnt < linelen; ++cp, ++lcnt){
  567                char c8;
  568 
  569                c8 = *cp;
  570                if(!(isblank = blankchar(c8)) || !lblank){
  571                   if((lblank = isblank))
  572                      c8 = ' ';
  573                   hlp = n_string_push_c(hlp, c8);
  574                }
  575             }
  576          }
  577          continue;
  578       }
  579 
  580 jhdrput:
  581       /* If it is an ignored header, skip it */
  582       *(cp = memchr(hlp->s_dat, ':', hlp->s_len)) = '\0';
  583       /* C99 */{
  584          size_t i;
  585 
  586          i = PTR2SIZE(cp - hlp->s_dat);
  587          if((doitp != NULL && n_ignore_is_ign(doitp, hlp->s_dat, i)) ||
  588                !asccasecmp(hlp->s_dat, "status") ||
  589                !asccasecmp(hlp->s_dat, "x-status") ||
  590                (action == SEND_MBOX &&
  591                   (!asccasecmp(hlp->s_dat, "content-length") ||
  592                    !asccasecmp(hlp->s_dat, "lines")) &&
  593                 !ok_blook(keep_content_length)))
  594             goto jhdrtrunc;
  595       }
  596 
  597       /* Dump it */
  598       n_COLOUR(
  599          if(n_COLOUR_IS_ACTIVE())
  600             n_colour_put(n_COLOUR_ID_VIEW_HEADER, hlp->s_dat);
  601       )
  602       *cp = ':';
  603       _out(hlp->s_dat, hlp->s_len, obuf, convert, action, qf, stats, NULL,NULL);
  604       n_COLOUR(
  605          if(n_COLOUR_IS_ACTIVE())
  606             n_colour_reset();
  607       )
  608       if(dostat & 4)
  609          _out("\n", sizeof("\n") -1, obuf, convert, action, qf, stats,
  610             NULL,NULL);
  611       /*see below hany = TRU1;*/
  612 
  613 jhdrtrunc:
  614       hlp = n_string_trunc(hlp, 0);
  615    }
  616    hstop = TRU1;
  617    if(hlp->s_len > 0)
  618       goto jhdrput;
  619 
  620    /* We've reached end of headers, so eventually force out status: field and
  621     * note that we are no longer in header fields */
  622    if(dostat & 1){
  623       statusput(zmp, obuf, qf, stats);
  624       /*see below hany = TRU1;*/
  625    }
  626    if(dostat & 2){
  627       xstatusput(zmp, obuf, qf, stats);
  628       /*see below hany = TRU1;*/
  629    }
  630    if(/* TODO PART_INFO hany && */ doitp != n_IGNORE_ALL)
  631       _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL);
  632    } /* C99 */
  633 
  634    quoteflt_flush(qf);
  635 
  636    if(ferror(obuf)){
  637       rv = -1;
  638       goto jleave;
  639    }
  640 
  641 jheaders_skip:
  642    memset(&mh, 0, sizeof mh);
  643 
  644    switch (ip->m_mimecontent) {
  645    case MIME_822:
  646       switch (action) {
  647       case SEND_TODISP_PARTS:
  648          goto jleave;
  649       case SEND_TODISP:
  650       case SEND_TODISP_ALL:
  651       case SEND_QUOTE:
  652       case SEND_QUOTE_ALL:
  653          if (ok_blook(rfc822_body_from_)) {
  654             if (!qf->qf_bypass) {
  655                size_t i = fwrite(qf->qf_pfix, sizeof *qf->qf_pfix,
  656                      qf->qf_pfix_len, obuf);
  657                if (i == qf->qf_pfix_len && stats != NULL)
  658                   *stats += i;
  659             }
  660             put_from_(obuf, ip->m_multipart, stats);
  661          }
  662          /* FALLTHRU */
  663       case SEND_TOSRCH:
  664       case SEND_DECRYPT:
  665          goto jmulti;
  666       case SEND_TOFILE:
  667       case SEND_TOPIPE:
  668          put_from_(obuf, ip->m_multipart, stats);
  669          /* FALLTHRU */
  670       case SEND_MBOX:
  671       case SEND_RFC822:
  672       case SEND_SHOW:
  673          break;
  674       }
  675       break;
  676    case MIME_TEXT_HTML:
  677    case MIME_TEXT:
  678    case MIME_TEXT_PLAIN:
  679       switch (action) {
  680       case SEND_TODISP:
  681       case SEND_TODISP_ALL:
  682       case SEND_TODISP_PARTS:
  683       case SEND_QUOTE:
  684       case SEND_QUOTE_ALL:
  685          switch (n_mimetype_handler(&mh, ip, action)) {
  686          case MIME_HDL_NULL:
  687             if(action != SEND_TODISP_PARTS)
  688                break;
  689             /* FALLTHRU */
  690          case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
  691             if(mh.mh_msg.l > 0)
  692                _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
  693                   qf, stats, NULL, NULL);
  694             /* We would print this as plain text, so better force going home */
  695             goto jleave;
  696          case MIME_HDL_CMD:
  697             if(action == SEND_TODISP_PARTS &&
  698                   (mh.mh_flags & MIME_HDL_COPIOUSOUTPUT))
  699                goto jleave;
  700             break;
  701          case MIME_HDL_TEXT:
  702          case MIME_HDL_PTF:
  703             if(action == SEND_TODISP_PARTS)
  704                goto jleave;
  705             break;
  706          default:
  707             break;
  708          }
  709          /* FALLTRHU */
  710       default:
  711          break;
  712       }
  713       break;
  714    case MIME_DISCARD:
  715       if (action != SEND_DECRYPT)
  716          goto jleave;
  717       break;
  718    case MIME_PKCS7:
  719       if (action != SEND_MBOX && action != SEND_RFC822 &&
  720             action != SEND_SHOW && ip->m_multipart != NULL)
  721          goto jmulti;
  722       /* FALLTHRU */
  723    default:
  724       switch (action) {
  725       case SEND_TODISP:
  726       case SEND_TODISP_ALL:
  727       case SEND_TODISP_PARTS:
  728       case SEND_QUOTE:
  729       case SEND_QUOTE_ALL:
  730          switch (n_mimetype_handler(&mh, ip, action)) {
  731          default:
  732          case MIME_HDL_NULL:
  733             if (action != SEND_TODISP && action != SEND_TODISP_ALL &&
  734                   (level != 0 || cnt))
  735                goto jleave;
  736             /* FALLTHRU */
  737          case MIME_HDL_MSG:/* TODO these should be part of partinfo! */
  738             if(mh.mh_msg.l > 0)
  739                _out(mh.mh_msg.s, mh.mh_msg.l, obuf, CONV_NONE, SEND_MBOX,
  740                   qf, stats, NULL, NULL);
  741             /* We would print this as plain text, so better force going home */
  742             goto jleave;
  743          case MIME_HDL_CMD:
  744             if(action == SEND_TODISP_PARTS){
  745                if(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)
  746                   goto jleave;
  747                else{
  748                   _print_part_info(obuf, ip, doitp, level, qf, stats);
  749                   if(!getapproval(_("Run MIME handler for this part?"), FAL0))
  750                      goto jleave;
  751                }
  752             }
  753             break;
  754          case MIME_HDL_TEXT:
  755          case MIME_HDL_PTF:
  756             if(action == SEND_TODISP_PARTS)
  757                goto jleave;
  758             break;
  759          }
  760          break;
  761       case SEND_TOFILE:
  762       case SEND_TOPIPE:
  763       case SEND_TOSRCH:
  764       case SEND_DECRYPT:
  765       case SEND_MBOX:
  766       case SEND_RFC822:
  767       case SEND_SHOW:
  768          break;
  769       }
  770       break;
  771    case MIME_ALTERNATIVE:
  772       if ((action == SEND_TODISP || action == SEND_QUOTE) &&
  773             !ok_blook(print_alternatives)) {
  774          /* XXX This (a) should not remain (b) should be own fun
  775           * TODO (despite the fact that v15 will do this completely differently
  776           * TODO by having an action-specific "manager" that will traverse the
  777           * TODO parsed MIME tree and decide for each part whether it'll be
  778           * TODO displayed or not *before* we walk the tree for doing action */
  779          struct mpstack {
  780             struct mpstack *outer;
  781             struct mimepart *mp;
  782          } outermost, * volatile curr, * volatile mpsp;
  783          bool_t volatile neednl, hadpart;
  784          struct n_sigman smalter;
  785 
  786          (curr = &outermost)->outer = NULL;
  787          curr->mp = ip;
  788          neednl = hadpart = FAL0;
  789 
  790          n_SIGMAN_ENTER_SWITCH(&smalter, n_SIGMAN_ALL) {
  791          case 0:
  792             break;
  793          default:
  794             rv = -1;
  795             goto jalter_leave;
  796          }
  797 
  798          for (np = ip->m_multipart;;) {
  799 jalter_redo:
  800             for (; np != NULL; np = np->m_nextpart) {
  801                if (action != SEND_QUOTE && np->m_ct_type_plain != NULL) {
  802                   if (neednl)
  803                      _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats,
  804                         NULL, NULL);
  805                   _print_part_info(obuf, np, doitp, level, qf, stats);
  806                }
  807                neednl = TRU1;
  808 
  809                switch (np->m_mimecontent) {
  810                case MIME_ALTERNATIVE:
  811                case MIME_RELATED:
  812                case MIME_DIGEST:
  813                case MIME_SIGNED:
  814                case MIME_ENCRYPTED:
  815                case MIME_MULTI:
  816                   mpsp = n_autorec_alloc(sizeof *mpsp);
  817                   mpsp->outer = curr;
  818                   mpsp->mp = np->m_multipart;
  819                   curr->mp = np;
  820                   curr = mpsp;
  821                   np = mpsp->mp;
  822                   neednl = FAL0;
  823                   goto jalter_redo;
  824                default:
  825                   if (hadpart)
  826                      break;
  827                   switch (n_mimetype_handler(&mh, np, action)) {
  828                   default:
  829                      mh.mh_flags = MIME_HDL_NULL;
  830                      continue; /* break; break; */
  831                   case MIME_HDL_CMD:
  832                      if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT)){
  833                         mh.mh_flags = MIME_HDL_NULL;
  834                         continue; /* break; break; */
  835                      }
  836                      /* FALLTHRU */
  837                   case MIME_HDL_PTF:
  838                      if (!ok_blook(mime_alternative_favour_rich)) {/* TODO */
  839                         struct mimepart *x = np;
  840 
  841                         while ((x = x->m_nextpart) != NULL) {
  842                            struct mime_handler mhx;
  843 
  844                            if (x->m_mimecontent == MIME_TEXT_PLAIN ||
  845                                  n_mimetype_handler(&mhx, x, action) ==
  846                                     MIME_HDL_TEXT)
  847                               break;
  848                         }
  849                         if (x != NULL)
  850                            continue; /* break; break; */
  851                         goto jalter_plain;
  852                      }
  853                      /* FALLTHRU */
  854                   case MIME_HDL_TEXT:
  855                      break;
  856                   }
  857                   /* FALLTHRU */
  858                case MIME_TEXT_PLAIN:
  859                   if (hadpart)
  860                      break;
  861                   if (ok_blook(mime_alternative_favour_rich)) { /* TODO */
  862                      struct mimepart *x = np;
  863 
  864                      /* TODO twice TODO, we should dive into /related and
  865                       * TODO check whether that has rich parts! */
  866                      while ((x = x->m_nextpart) != NULL) {
  867                         struct mime_handler mhx;
  868 
  869                         switch (n_mimetype_handler(&mhx, x, action)) {
  870                         case MIME_HDL_CMD:
  871                            if(!(mhx.mh_flags & MIME_HDL_COPIOUSOUTPUT))
  872                               continue;
  873                            /* FALLTHRU */
  874                         case MIME_HDL_PTF:
  875                            break;
  876                         default:
  877                            continue;
  878                         }
  879                         break;
  880                      }
  881                      if (x != NULL)
  882                         continue; /* break; break; */
  883                   }
  884 jalter_plain:
  885                   quoteflt_flush(qf);
  886                   if (action == SEND_QUOTE && hadpart)
  887                      /* XXX (void)*/a_send_out_nl(obuf, stats);
  888                   hadpart = TRU1;
  889                   neednl = FAL0;
  890                   rv = sendpart(zmp, np, obuf, doitp, qf, action,
  891                         linedat, linesize, stats, level + 1);
  892                   quoteflt_reset(qf, origobuf);
  893 
  894                   if (rv < 0)
  895                      curr = &outermost; /* Cause overall loop termination */
  896                   break;
  897                }
  898             }
  899 
  900             mpsp = curr->outer;
  901             if (mpsp == NULL)
  902                break;
  903             curr = mpsp;
  904             np = curr->mp->m_nextpart;
  905          }
  906 jalter_leave:
  907          n_sigman_leave(&smalter, n_SIGMAN_VIPSIGS_NTTYOUT);
  908          goto jleave;
  909       }
  910       /* FALLTHRU */
  911    case MIME_RELATED:
  912    case MIME_DIGEST:
  913    case MIME_SIGNED:
  914    case MIME_ENCRYPTED:
  915    case MIME_MULTI:
  916       switch (action) {
  917       case SEND_TODISP:
  918       case SEND_TODISP_ALL:
  919       case SEND_TODISP_PARTS:
  920       case SEND_QUOTE:
  921       case SEND_QUOTE_ALL:
  922       case SEND_TOFILE:
  923       case SEND_TOPIPE:
  924       case SEND_TOSRCH:
  925       case SEND_DECRYPT:
  926 jmulti:
  927          if ((action == SEND_TODISP || action == SEND_TODISP_ALL) &&
  928              ip->m_multipart != NULL &&
  929              ip->m_multipart->m_mimecontent == MIME_DISCARD &&
  930              ip->m_multipart->m_nextpart == NULL) {
  931             char const *x = _("[Missing multipart boundary - use `show' "
  932                   "to display the raw message]\n");
  933             _out(x, strlen(x), obuf, CONV_NONE, SEND_MBOX, qf, stats,
  934                NULL,NULL);
  935          }
  936 
  937          for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) {
  938             bool_t volatile ispipe;
  939 
  940             if (np->m_mimecontent == MIME_DISCARD && action != SEND_DECRYPT)
  941                continue;
  942 
  943             ispipe = FAL0;
  944             switch (action) {
  945             case SEND_TOFILE:
  946                if (np->m_partstring &&
  947                      np->m_partstring[0] == '1' && np->m_partstring[1] == '\0')
  948                   break;
  949                stats = NULL;
  950                /* TODO Always open multipart on /dev/null, it's a hack to be
  951                 * TODO able to dive into that structure, and still better
  952                 * TODO than asking the user for something stupid.
  953                 * TODO oh, wait, we did ask for a filename for this MIME mail,
  954                 * TODO and that outer container is useless anyway ;-P */
  955                if (np->m_multipart != NULL && np->m_mimecontent != MIME_822) {
  956                   if ((obuf = Fopen(n_path_devnull, "w")) == NULL)
  957                      continue;
  958                } else if ((obuf = newfile(np, &ispipe)) == NULL)
  959                   continue;
  960                if (!ispipe)
  961                   break;
  962                if (sigsetjmp(_send_pipejmp, 1)) {
  963                   rv = -1;
  964                   goto jpipe_close;
  965                }
  966                oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
  967                break;
  968             case SEND_TODISP:
  969             case SEND_TODISP_ALL:
  970                if (ip->m_mimecontent != MIME_ALTERNATIVE &&
  971                      ip->m_mimecontent != MIME_RELATED &&
  972                      ip->m_mimecontent != MIME_DIGEST &&
  973                      ip->m_mimecontent != MIME_SIGNED &&
  974                      ip->m_mimecontent != MIME_ENCRYPTED &&
  975                      ip->m_mimecontent != MIME_MULTI)
  976                   break;
  977                _print_part_info(obuf, np, doitp, level, qf, stats);
  978                break;
  979             case SEND_TODISP_PARTS:
  980             case SEND_QUOTE:
  981             case SEND_QUOTE_ALL:
  982             case SEND_MBOX:
  983             case SEND_RFC822:
  984             case SEND_SHOW:
  985             case SEND_TOSRCH:
  986             case SEND_DECRYPT:
  987             case SEND_TOPIPE:
  988                break;
  989             }
  990 
  991             quoteflt_flush(qf);
  992             if ((action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
  993                   np->m_multipart == NULL && ip->m_parent != NULL)
  994                /*XXX (void)*/a_send_out_nl(obuf, stats);
  995             if (sendpart(zmp, np, obuf, doitp, qf, action, linedat, linesize,
  996                   stats, level+1) < 0)
  997                rv = -1;
  998             quoteflt_reset(qf, origobuf);
  999 
 1000             if (action == SEND_QUOTE) {
 1001                if (ip->m_mimecontent != MIME_RELATED)
 1002                   break;
 1003             }
 1004             if (action == SEND_TOFILE && obuf != origobuf) {
 1005                if (!ispipe)
 1006                   Fclose(obuf);
 1007                else {
 1008 jpipe_close:
 1009                   safe_signal(SIGPIPE, SIG_IGN);
 1010                   Pclose(obuf, TRU1);
 1011                   safe_signal(SIGPIPE, oldpipe);
 1012                }
 1013             }
 1014          }
 1015          goto jleave;
 1016       case SEND_MBOX:
 1017       case SEND_RFC822:
 1018       case SEND_SHOW:
 1019          break;
 1020       }
 1021       break;
 1022    }
 1023 
 1024    /* Copy out message body */
 1025    if (doitp == n_IGNORE_ALL && level == 0) /* skip final blank line */
 1026       --cnt;
 1027    switch (ip->m_mime_enc) {
 1028    case MIMEE_BIN:
 1029    case MIMEE_7B:
 1030    case MIMEE_8B:
 1031       convert = CONV_NONE;
 1032       break;
 1033    case MIMEE_QP:
 1034       convert = CONV_FROMQP;
 1035       break;
 1036    case MIMEE_B64:
 1037       switch (ip->m_mimecontent) {
 1038       case MIME_TEXT:
 1039       case MIME_TEXT_PLAIN:
 1040       case MIME_TEXT_HTML:
 1041          convert = CONV_FROMB64_T;
 1042          break;
 1043       default:
 1044          switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
 1045          case MIME_HDL_TEXT:
 1046          case MIME_HDL_PTF:
 1047             convert = CONV_FROMB64_T;
 1048             break;
 1049          default:
 1050             convert = CONV_FROMB64;
 1051             break;
 1052          }
 1053          break;
 1054       }
 1055       break;
 1056    default:
 1057       convert = CONV_NONE;
 1058    }
 1059 
 1060    /* TODO Unless we have filters, ensure iconvd==-1 so that mime.c:fwrite_td()
 1061     * TODO cannot mess things up misusing outrest as line buffer */
 1062 #ifdef HAVE_ICONV
 1063    if (iconvd != (iconv_t)-1) {
 1064       n_iconv_close(iconvd);
 1065       iconvd = (iconv_t)-1;
 1066    }
 1067 #endif
 1068 
 1069    if (action == SEND_DECRYPT || action == SEND_MBOX ||
 1070          action == SEND_RFC822 || action == SEND_SHOW)
 1071       convert = CONV_NONE;
 1072 #ifdef HAVE_ICONV
 1073    else if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
 1074             action == SEND_TODISP_PARTS ||
 1075             action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
 1076             action == SEND_TOSRCH || action == SEND_TOFILE) &&
 1077          (ip->m_mimecontent == MIME_TEXT_PLAIN ||
 1078             ip->m_mimecontent == MIME_TEXT_HTML ||
 1079             ip->m_mimecontent == MIME_TEXT ||
 1080             (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_TEXT ||
 1081             (mh.mh_flags & MIME_HDL_TYPE_MASK) == MIME_HDL_PTF)) {
 1082       char const *tcs;
 1083 
 1084       tcs = ok_vlook(ttycharset);
 1085       if (asccasecmp(tcs, ip->m_charset) &&
 1086             asccasecmp(ok_vlook(charset_7bit), ip->m_charset)) {
 1087          iconvd = n_iconv_open(tcs, ip->m_charset);
 1088          if (iconvd == (iconv_t)-1 && n_err_no == n_ERR_INVAL) {
 1089             n_err(_("Cannot convert from %s to %s\n"), ip->m_charset, tcs);
 1090             /*rv = 1; goto jleave;*/
 1091          }
 1092       }
 1093    }
 1094 #endif
 1095 
 1096    switch (mh.mh_flags & MIME_HDL_TYPE_MASK) {
 1097    case MIME_HDL_CMD:
 1098       if(!(mh.mh_flags & MIME_HDL_COPIOUSOUTPUT) &&
 1099             action != SEND_TODISP_PARTS)
 1100          goto jmhp_default;
 1101       /* FALLTHRU */
 1102    case MIME_HDL_PTF:
 1103       tmpname = NULL;
 1104       qbuf = obuf;
 1105 
 1106       term_infd = n_CHILD_FD_PASS;
 1107       if (mh.mh_flags & (MIME_HDL_TMPF | MIME_HDL_NEEDSTERM)) {
 1108          enum oflags of;
 1109 
 1110          of = OF_RDWR | OF_REGISTER;
 1111          if (!(mh.mh_flags & MIME_HDL_TMPF)) {
 1112             term_infd = 0;
 1113             mh.mh_flags |= MIME_HDL_TMPF_FILL;
 1114             of |= OF_UNLINK;
 1115          } else if (mh.mh_flags & MIME_HDL_TMPF_UNLINK)
 1116             of |= OF_REGISTER_UNLINK;
 1117 
 1118          if ((pbuf = Ftmp((mh.mh_flags & MIME_HDL_TMPF ? &cp : NULL),
 1119                (mh.mh_flags & MIME_HDL_TMPF_FILL ? "mimehdlfill" : "mimehdl"),
 1120                of)) == NULL)
 1121             goto jesend;
 1122 
 1123          if (mh.mh_flags & MIME_HDL_TMPF) {
 1124             tmpname = savestr(cp);
 1125             Ftmp_free(&cp);
 1126          }
 1127 
 1128          if (mh.mh_flags & MIME_HDL_TMPF_FILL) {
 1129             if (term_infd == 0)
 1130                term_infd = fileno(pbuf);
 1131             goto jsend;
 1132          }
 1133       }
 1134 
 1135 jpipe_for_real:
 1136       pbuf = _pipefile(&mh, ip, n_UNVOLATILE(&qbuf), tmpname, term_infd);
 1137       if (pbuf == NULL) {
 1138 jesend:
 1139          pbuf = qbuf = NULL;
 1140          rv = -1;
 1141          goto jend;
 1142       } else if ((mh.mh_flags & MIME_HDL_NEEDSTERM) && pbuf == (FILE*)-1) {
 1143          pbuf = qbuf = NULL;
 1144          goto jend;
 1145       }
 1146       tmpname = NULL;
 1147       action = SEND_TOPIPE;
 1148       if (pbuf != qbuf) {
 1149          oldpipe = safe_signal(SIGPIPE, &_send_onpipe);
 1150          if (sigsetjmp(_send_pipejmp, 1))
 1151             goto jend;
 1152       }
 1153       break;
 1154 
 1155    default:
 1156 jmhp_default:
 1157       mh.mh_flags = MIME_HDL_NULL;
 1158       pbuf = qbuf = obuf;
 1159       break;
 1160    }
 1161 
 1162 jsend:
 1163    {
 1164    bool_t volatile eof;
 1165    bool_t save_qf_bypass = qf->qf_bypass;
 1166    ui64_t *save_stats = stats;
 1167 
 1168    if (pbuf != origobuf) {
 1169       qf->qf_bypass = TRU1;/* XXX legacy (remove filter instead) */
 1170       stats = NULL;
 1171    }
 1172    eof = FAL0;
 1173    outrest.s = inrest.s = NULL;
 1174    outrest.l = inrest.l = 0;
 1175 
 1176    if (pbuf == qbuf) {
 1177       __sendp_sig = 0;
 1178       __sendp_opipe = safe_signal(SIGPIPE, &__sendp_onsig);
 1179       if (sigsetjmp(__sendp_actjmp, 1)) {
 1180          n_pstate &= ~n_PS_BASE64_STRIP_CR;/* (but protected by outer sigman) */
 1181          if (outrest.s != NULL)
 1182             n_free(outrest.s);
 1183          if (inrest.s != NULL)
 1184             n_free(inrest.s);
 1185 #ifdef HAVE_ICONV
 1186          if (iconvd != (iconv_t)-1)
 1187             n_iconv_close(iconvd);
 1188 #endif
 1189          safe_signal(SIGPIPE, __sendp_opipe);
 1190          n_raise(__sendp_sig);
 1191       }
 1192    }
 1193 
 1194    quoteflt_reset(qf, pbuf);
 1195    if((dostat & 4) && pbuf == origobuf) /* TODO */
 1196       n_pstate |= n_PS_BASE64_STRIP_CR;
 1197    while (!eof && fgetline(linedat, linesize, &cnt, &linelen, ibuf, 0)) {
 1198 joutln:
 1199       if (_out(*linedat, linelen, pbuf, convert, action, qf, stats, &outrest,
 1200             (action & _TD_EOF ? NULL : &inrest)) < 0 || ferror(pbuf)) {
 1201          rv = -1; /* XXX Should bail away?! */
 1202          break;
 1203       }
 1204    }
 1205    if(eof <= FAL0 && rv >= 0 && (outrest.l != 0 || inrest.l != 0)){
 1206       linelen = 0;
 1207       if(eof || inrest.l == 0)
 1208          action |= _TD_EOF;
 1209       eof = eof ? TRU1 : TRUM1;
 1210       goto joutln;
 1211    }
 1212    n_pstate &= ~n_PS_BASE64_STRIP_CR;
 1213    action &= ~_TD_EOF;
 1214 
 1215    /* TODO HACK: when sending to the display we yet get fooled if a message
 1216     * TODO doesn't end in a newline, because of our input/output 1:1.
 1217     * TODO This should be handled automatically by a display filter, then */
 1218    if(rv >= 0 && !qf->qf_nl_last &&
 1219          (action == SEND_TODISP || action == SEND_TODISP_ALL ||
 1220           action == SEND_QUOTE || action == SEND_QUOTE_ALL))
 1221       rv = quoteflt_push(qf, "\n", 1);
 1222 
 1223    quoteflt_flush(qf);
 1224 
 1225    if (rv >= 0 && (mh.mh_flags & MIME_HDL_TMPF_FILL)) {
 1226       mh.mh_flags &= ~MIME_HDL_TMPF_FILL;
 1227       fflush(pbuf);
 1228       really_rewind(pbuf);
 1229       /* Don't Fclose() the Ftmp() thing due to OF_REGISTER_UNLINK++ */
 1230       goto jpipe_for_real;
 1231    }
 1232 
 1233    if (pbuf == qbuf)
 1234       safe_signal(SIGPIPE, __sendp_opipe);
 1235 
 1236    if (outrest.s != NULL)
 1237       n_free(outrest.s);
 1238    if (inrest.s != NULL)
 1239       n_free(inrest.s);
 1240 
 1241    if (pbuf != origobuf) {
 1242       qf->qf_bypass = save_qf_bypass;
 1243       stats = save_stats;
 1244    }
 1245    }
 1246 
 1247 jend:
 1248    if (pbuf != qbuf) {
 1249       safe_signal(SIGPIPE, SIG_IGN);
 1250       Pclose(pbuf, !(mh.mh_flags & MIME_HDL_ASYNC));
 1251       safe_signal(SIGPIPE, oldpipe);
 1252       if (rv >= 0 && qbuf != NULL && qbuf != obuf)
 1253          pipecpy(qbuf, obuf, origobuf, qf, stats);
 1254    }
 1255 #ifdef HAVE_ICONV
 1256    if (iconvd != (iconv_t)-1)
 1257       n_iconv_close(iconvd);
 1258 #endif
 1259 jleave:
 1260    NYD_LEAVE;
 1261    return rv;
 1262 }
 1263 
 1264 static FILE *
 1265 newfile(struct mimepart *ip, bool_t volatile *ispipe)
 1266 {
 1267    struct str in, out;
 1268    char *f;
 1269    FILE *fp;
 1270    NYD_ENTER;
 1271 
 1272    f = ip->m_filename;
 1273    *ispipe = FAL0;
 1274 
 1275    if (f != NULL && f != (char*)-1) {
 1276       in.s = f;
 1277       in.l = strlen(f);
 1278       makeprint(&in, &out);
 1279       out.l = delctrl(out.s, out.l);
 1280       f = savestrbuf(out.s, out.l);
 1281       n_free(out.s);
 1282    }
 1283 
 1284    /* In interactive mode, let user perform all kind of expansions as desired,
 1285     * and offer |SHELL-SPEC pipe targets, too */
 1286    if (n_psonce & n_PSO_INTERACTIVE) {
 1287       struct str prompt;
 1288       struct n_string shou, *shoup;
 1289       char *f2, *f3;
 1290 
 1291       shoup = n_string_creat_auto(&shou);
 1292 
 1293       /* TODO Generic function which asks for filename.
 1294        * TODO If the current part is the first textpart the target
 1295        * TODO is implicit from outer `write' etc! */
 1296       /* I18N: Filename input prompt with file type indication */
 1297       str_concat_csvl(&prompt, _("Enter filename for part "),
 1298          (ip->m_partstring != NULL ? ip->m_partstring : n_qm),
 1299          " (", ip->m_ct_type_plain, "): ", NULL);
 1300 jgetname:
 1301       f2 = n_go_input_cp(n_GO_INPUT_CTX_DEFAULT | n_GO_INPUT_HIST_ADD,
 1302             prompt.s, ((f != (char*)-1 && f != NULL)
 1303                ? n_shexp_quote_cp(f, FAL0) : NULL));
 1304       if(f2 != NULL){
 1305          in.s = n_UNCONST(f2);
 1306          in.l = UIZ_MAX;
 1307          if((n_shexp_parse_token((n_SHEXP_PARSE_TRUNC |
 1308                   n_SHEXP_PARSE_TRIM_SPACE | n_SHEXP_PARSE_TRIM_IFSSPACE |
 1309                   n_SHEXP_PARSE_LOG | n_SHEXP_PARSE_IGNORE_EMPTY),
 1310                   shoup, &in, NULL
 1311                ) & (n_SHEXP_STATE_STOP |
 1312                   n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)
 1313                ) != (n_SHEXP_STATE_STOP | n_SHEXP_STATE_OUTPUT))
 1314             goto jgetname;
 1315          f2 = n_string_cp(shoup);
 1316       }
 1317       if (f2 == NULL || *f2 == '\0') {
 1318          if (n_poption & n_PO_D_V)
 1319             n_err(_("... skipping this\n"));
 1320          n_string_gut(shoup);
 1321          fp = NULL;
 1322          goto jleave;
 1323       }
 1324 
 1325       if (*f2 == '|')
 1326          /* Pipes are expanded by the shell */
 1327          f = f2;
 1328       else if ((f3 = fexpand(f2, FEXP_LOCAL | FEXP_NVAR)) == NULL)
 1329          /* (Error message written by fexpand()) */
 1330          goto jgetname;
 1331       else
 1332          f = f3;
 1333 
 1334       n_string_gut(shoup);
 1335    }
 1336 
 1337    if (f == NULL || f == (char*)-1 || *f == '\0')
 1338       fp = NULL;
 1339    else if (n_psonce & n_PSO_INTERACTIVE) {
 1340       if (*f == '|') {
 1341          fp = Popen(&f[1], "w", ok_vlook(SHELL), NULL, 1);
 1342          if (!(*ispipe = (fp != NULL)))
 1343             n_perr(f, 0);
 1344       } else if ((fp = Fopen(f, "w")) == NULL)
 1345          n_err(_("Cannot open %s\n"), n_shexp_quote_cp(f, FAL0));
 1346    } else {
 1347       /* Be very picky in non-interactive mode: actively disallow pipes,
 1348        * prevent directory separators, and any filename member that would
 1349        * become expanded by the shell if the name would be echo(1)ed */
 1350       if(n_anyof_cp("/" n_SHEXP_MAGIC_PATH_CHARS, f)){
 1351          char c;
 1352 
 1353          for(out.s = n_autorec_alloc((strlen(f) * 3) +1), out.l = 0;
 1354                (c = *f++) != '\0';)
 1355             if(strchr("/" n_SHEXP_MAGIC_PATH_CHARS, c)){
 1356                out.s[out.l++] = '%';
 1357                n_c_to_hex_base16(&out.s[out.l], c);
 1358                out.l += 2;
 1359             }else
 1360                out.s[out.l++] = c;
 1361          out.s[out.l] = '\0';
 1362          f = out.s;
 1363       }
 1364 
 1365       /* Avoid overwriting of existing files */
 1366       while((fp = Fopen(f, "wx")) == NULL){
 1367          int e;
 1368 
 1369          if((e = n_err_no) != n_ERR_EXIST){
 1370             n_err(_("Cannot open %s: %s\n"),
 1371                n_shexp_quote_cp(f, FAL0), n_err_to_doc(e));
 1372             break;
 1373          }
 1374 
 1375          if(ip->m_partstring != NULL)
 1376             f = savecatsep(f, '#', ip->m_partstring);
 1377          else
 1378             f = savecat(f, "#.");
 1379       }
 1380    }
 1381 jleave:
 1382    NYD_LEAVE;
 1383    return fp;
 1384 }
 1385 
 1386 static void
 1387 pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf, struct quoteflt *qf,
 1388    ui64_t *stats)
 1389 {
 1390    char *line = NULL; /* TODO line pool */
 1391    size_t linesize = 0, linelen, cnt;
 1392    ssize_t all_sz, sz;
 1393    NYD_ENTER;
 1394 
 1395    fflush(pipebuf);
 1396    rewind(pipebuf);
 1397    cnt = (size_t)fsize(pipebuf);
 1398    all_sz = 0;
 1399 
 1400    quoteflt_reset(qf, outbuf);
 1401    while (fgetline(&line, &linesize, &cnt, &linelen, pipebuf, 0) != NULL) {
 1402       if ((sz = quoteflt_push(qf, line, linelen)) < 0)
 1403          break;
 1404       all_sz += sz;
 1405    }
 1406    if ((sz = quoteflt_flush(qf)) > 0)
 1407       all_sz += sz;
 1408    if (line)
 1409       n_free(line);
 1410 
 1411    if (all_sz > 0 && outbuf == origobuf && stats != NULL)
 1412       *stats += all_sz;
 1413    Fclose(pipebuf);
 1414    NYD_LEAVE;
 1415 }
 1416 
 1417 static void
 1418 statusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
 1419    ui64_t *stats)
 1420 {
 1421    char statout[3], *cp = statout;
 1422    NYD_ENTER;
 1423 
 1424    if (mp->m_flag & MREAD)
 1425       *cp++ = 'R';
 1426    if (!(mp->m_flag & MNEW))
 1427       *cp++ = 'O';
 1428    *cp = 0;
 1429    if (statout[0]) {
 1430       int i = fprintf(obuf, "%.*sStatus: %s\n", (int)qf->qf_pfix_len,
 1431             (qf->qf_bypass ? NULL : qf->qf_pfix), statout);
 1432       if (i > 0 && stats != NULL)
 1433          *stats += i;
 1434    }
 1435    NYD_LEAVE;
 1436 }
 1437 
 1438 static void
 1439 xstatusput(const struct message *mp, FILE *obuf, struct quoteflt *qf,
 1440    ui64_t *stats)
 1441 {
 1442    char xstatout[4];
 1443    char *xp = xstatout;
 1444    NYD_ENTER;
 1445 
 1446    if (mp->m_flag & MFLAGGED)
 1447       *xp++ = 'F';
 1448    if (mp->m_flag & MANSWERED)
 1449       *xp++ = 'A';
 1450    if (mp->m_flag & MDRAFTED)
 1451       *xp++ = 'T';
 1452    *xp = 0;
 1453    if (xstatout[0]) {
 1454       int i = fprintf(obuf, "%.*sX-Status: %s\n", (int)qf->qf_pfix_len,
 1455             (qf->qf_bypass ? NULL : qf->qf_pfix), xstatout);
 1456       if (i > 0 && stats != NULL)
 1457          *stats += i;
 1458    }
 1459    NYD_LEAVE;
 1460 }
 1461 
 1462 static void
 1463 put_from_(FILE *fp, struct mimepart *ip, ui64_t *stats)
 1464 {
 1465    char const *froma, *date, *nl;
 1466    int i;
 1467    NYD_ENTER;
 1468 
 1469    if (ip != NULL && ip->m_from != NULL) {
 1470       froma = ip->m_from;
 1471       date = n_time_ctime(ip->m_time, NULL);
 1472       nl = "\n";
 1473    } else {
 1474       froma = ok_vlook(LOGNAME);
 1475       date = time_current.tc_ctime;
 1476       nl = n_empty;
 1477    }
 1478 
 1479    n_COLOUR(
 1480       if(n_COLOUR_IS_ACTIVE())
 1481          n_colour_put(n_COLOUR_ID_VIEW_FROM_, NULL);
 1482    )
 1483    i = fprintf(fp, "From %s %s%s", froma, date, nl);
 1484    n_COLOUR(
 1485       if(n_COLOUR_IS_ACTIVE())
 1486          n_colour_reset();
 1487    )
 1488    if (i > 0 && stats != NULL)
 1489       *stats += i;
 1490    NYD_LEAVE;
 1491 }
 1492 
 1493 FL int
 1494 sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp,
 1495    char const *prefix, enum sendaction action, ui64_t *stats)
 1496 {
 1497    struct n_sigman linedat_protect;
 1498    struct quoteflt qf;
 1499    FILE *ibuf;
 1500    enum mime_parse_flags mpf;
 1501    struct mimepart *ip;
 1502    size_t linesize, cnt, sz, i;
 1503    char *linedat;
 1504    int rv, c;
 1505    NYD_ENTER;
 1506 
 1507    time_current_update(&time_current, TRU1);
 1508    rv = -1;
 1509    linedat = NULL;
 1510    linesize = 0;
 1511    quoteflt_init(&qf, prefix, (prefix == NULL));
 1512 
 1513    n_SIGMAN_ENTER_SWITCH(&linedat_protect, n_SIGMAN_ALL){
 1514    case 0:
 1515       break;
 1516    default:
 1517       goto jleave;
 1518    }
 1519 
 1520    if (mp == dot && action != SEND_TOSRCH)
 1521       n_pstate |= n_PS_DID_PRINT_DOT;
 1522    if (stats != NULL)
 1523       *stats = 0;
 1524 
 1525    /* First line is the From_ line, so no headers there to worry about */
 1526    if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
 1527       goto jleave;
 1528 
 1529    cnt = mp->m_size;
 1530    sz = 0;
 1531    {
 1532    bool_t nozap;
 1533    char const *cpre = n_empty, *csuf = n_empty;
 1534 
 1535 #ifdef HAVE_COLOUR
 1536    if(n_COLOUR_IS_ACTIVE()){
 1537       struct n_colour_pen *cpen;
 1538       struct str const *sp;
 1539 
 1540       cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_FROM_,NULL);
 1541       if((sp = n_colour_pen_to_str(cpen)) != NULL){
 1542          cpre = sp->s;
 1543          sp = n_colour_reset_to_str();
 1544          if(sp != NULL)
 1545             csuf = sp->s;
 1546       }
 1547    }
 1548 #endif
 1549 
 1550    nozap = (doitp != n_IGNORE_ALL && doitp != n_IGNORE_FWD &&
 1551          action != SEND_RFC822 &&
 1552          !n_ignore_is_ign(doitp, "from_", sizeof("from_") -1));
 1553    if (mp->m_flag & MNOFROM) {
 1554       if (nozap)
 1555          sz = fprintf(obuf, "%s%.*sFrom %s %s%s\n",
 1556                cpre, (int)qf.qf_pfix_len,
 1557                (qf.qf_bypass ? n_empty : qf.qf_pfix), fakefrom(mp),
 1558                n_time_ctime(mp->m_time, NULL), csuf);
 1559    } else if (nozap) {
 1560       if (!qf.qf_bypass) {
 1561          i = fwrite(qf.qf_pfix, sizeof *qf.qf_pfix, qf.qf_pfix_len, obuf);
 1562          if (i != qf.qf_pfix_len)
 1563             goto jleave;
 1564          sz += i;
 1565       }
 1566 #ifdef HAVE_COLOUR
 1567       if(*cpre != '\0'){
 1568          fputs(cpre, obuf);
 1569          cpre = (char const*)0x1;
 1570       }
 1571 #endif
 1572 
 1573       while (cnt > 0 && (c = getc(ibuf)) != EOF) {
 1574 #ifdef HAVE_COLOUR
 1575          if(c == '\n' && *csuf != '\0'){
 1576             cpre = (char const*)0x1;
 1577             fputs(csuf, obuf);
 1578          }
 1579 #endif
 1580          putc(c, obuf);
 1581          ++sz;
 1582          --cnt;
 1583          if (c == '\n')
 1584             break;
 1585       }
 1586 
 1587 #ifdef HAVE_COLOUR
 1588       if(*csuf != '\0' && cpre != (char const*)0x1 && *cpre != '\0')
 1589          fputs(csuf, obuf);
 1590 #endif
 1591    } else {
 1592       while (cnt > 0 && (c = getc(ibuf)) != EOF) {
 1593          --cnt;
 1594          if (c == '\n')
 1595             break;
 1596       }
 1597    }
 1598    }
 1599    if (sz > 0 && stats != NULL)
 1600       *stats += sz;
 1601 
 1602    mpf = MIME_PARSE_NONE;
 1603    if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
 1604       mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT;
 1605    if(action == SEND_TODISP || action == SEND_TODISP_ALL ||
 1606          action == SEND_QUOTE || action == SEND_QUOTE_ALL)
 1607       mpf |= MIME_PARSE_FOR_USER_CONTEXT;
 1608    if ((ip = mime_parse_msg(mp, mpf)) == NULL)
 1609       goto jleave;
 1610 
 1611    rv = sendpart(mp, ip, obuf, doitp, &qf, action, &linedat, &linesize,
 1612          stats, 0);
 1613 
 1614    n_sigman_cleanup_ping(&linedat_protect);
 1615 jleave:
 1616    n_pstate &= ~n_PS_BASE64_STRIP_CR;
 1617    quoteflt_destroy(&qf);
 1618    if(linedat != NULL)
 1619       n_free(linedat);
 1620    NYD_LEAVE;
 1621    n_sigman_leave(&linedat_protect, n_SIGMAN_VIPSIGS_NTTYOUT);
 1622    return rv;
 1623 }
 1624 
 1625 /* s-it-mode */