"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/send.c" (16 Feb 2018, 51396 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

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