"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/send.c" (25 Mar 2018, 51458 Bytes) of package /linux/misc/s-nail-14.9.10.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.9_vs_14.9.10.

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