"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/sendout.c" (25 Mar 2018, 73139 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 "sendout.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 sending lifecycle, header composing, etc.
    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 sendout
   37 
   38 #ifndef HAVE_AMALGAMATION
   39 # include "nail.h"
   40 #endif
   41 
   42 #undef SEND_LINESIZE
   43 #define SEND_LINESIZE \
   44    ((1024 / B64_ENCODE_INPUT_PER_LINE) * B64_ENCODE_INPUT_PER_LINE)
   45 
   46 enum fmt_flags{
   47    FMT_INC_INVADDR = 1<<0, /* _Do_ include invalid addresses */
   48    FMT_DOMIME = 1<<1,      /* Perform MIME conversion */
   49    FMT_COMMA = GCOMMA,
   50    FMT_FILES = GFILES,
   51    _FMT_GMASK = FMT_COMMA | FMT_FILES
   52 };
   53 n_CTA(!(_FMT_GMASK & (FMT_INC_INVADDR | FMT_DOMIME)),
   54    "Code-required condition not satisfied but actual bit carrier value");
   55 
   56 static char const *__sendout_ident; /* TODO temporary hack; rewrite puthead() */
   57 static char *  _sendout_boundary;
   58 static si8_t   _sendout_error;
   59 
   60 /* *fullnames* appears after command line arguments have been parsed */
   61 static struct name *a_sendout_fullnames_cleanup(struct name *np);
   62 
   63 /* */
   64 static enum okay     _putname(char const *line, enum gfield w,
   65                         enum sendaction action, size_t *gotcha,
   66                         char const *prefix, FILE *fo, struct name **xp,
   67                         enum gfield addflags);
   68 
   69 /* Place Content-Type:, Content-Transfer-Encoding:, Content-Disposition:
   70  * headers, respectively */
   71 static int a_sendout_put_ct(FILE *fo, char const *contenttype,
   72                char const *charset);
   73 n_INLINE int a_sendout_put_cte(FILE *fo, enum conversion conv);
   74 static int a_sendout_put_cd(FILE *fo, char const *cd, char const *filename);
   75 
   76 /* Put all entries of the given header list */
   77 static bool_t        _sendout_header_list(FILE *fo, struct n_header_field *hfp,
   78                         bool_t nodisp);
   79 
   80 /* */
   81 static int a_sendout_body(FILE *fo, FILE *fi, enum conversion convert);
   82 
   83 /* Write an attachment to the file buffer, converting to MIME */
   84 static int a_sendout_attach_file(struct header *hp, struct attachment *ap,
   85             FILE *fo);
   86 static int a_sendout__attach_file(struct header *hp, struct attachment *ap,
   87             FILE *fo);
   88 
   89 /* There are non-local receivers, collect credentials etc. */
   90 static bool_t        _sendbundle_setup_creds(struct sendbundle *sbpm,
   91                         bool_t signing_caps);
   92 
   93 /* Attach a message to the file buffer */
   94 static int a_sendout_attach_msg(struct header *hp, struct attachment *ap,
   95                FILE *fo);
   96 
   97 /* Generate the body of a MIME multipart message */
   98 static int           make_multipart(struct header *hp, int convert, FILE *fi,
   99                         FILE *fo, char const *contenttype, char const *charset);
  100 
  101 /* Prepend a header in front of the collected stuff and return the new file */
  102 static FILE *        infix(struct header *hp, FILE *fi);
  103 
  104 /* Check whether Disposition-Notification-To: is desired */
  105 static bool_t        _check_dispo_notif(struct name *mdn, struct header *hp,
  106                         FILE *fo);
  107 
  108 /* Send mail to a bunch of user names.  The interface is through mail() */
  109 static int           sendmail_internal(void *v, int recipient_record);
  110 
  111 /* Deal with file and pipe addressees */
  112 static struct name *a_sendout_file_a_pipe(struct name *names, FILE *fo,
  113                      bool_t *senderror);
  114 
  115 /* Record outgoing mail if instructed to do so; in *record* unless to is set */
  116 static bool_t        mightrecord(FILE *fp, struct name *to, bool_t resend);
  117 
  118 static bool_t a_sendout__savemail(char const *name, FILE *fp, bool_t resend);
  119 
  120 /*  */
  121 static bool_t        _transfer(struct sendbundle *sbp);
  122 
  123 static bool_t        __mta_start(struct sendbundle *sbp);
  124 static char const ** __mta_prepare_args(struct name *to, struct header *hp);
  125 static void          __mta_debug(struct sendbundle *sbp, char const *mta,
  126                         char const **args);
  127 
  128 /* Create a Message-ID: header field.  Use either host name or from address */
  129 static char const *a_sendout_random_id(struct header *hp, bool_t msgid);
  130 
  131 /* Format the given header line to not exceed 72 characters */
  132 static int           fmt(char const *str, struct name *np, FILE *fo,
  133                         enum fmt_flags ff);
  134 
  135 /* Rewrite a message for resending, adding the Resent-Headers */
  136 static int           infix_resend(FILE *fi, FILE *fo, struct message *mp,
  137                         struct name *to, int add_resent);
  138 
  139 static struct name *
  140 a_sendout_fullnames_cleanup(struct name *np){
  141    struct name *xp;
  142    NYD2_ENTER;
  143 
  144    for(xp = np; xp != NULL; xp = xp->n_flink){
  145       xp->n_type &= ~(GFULL | GFULLEXTRA);
  146       xp->n_fullname = xp->n_name;
  147       xp->n_fullextra = NULL;
  148    }
  149    NYD2_LEAVE;
  150    return np;
  151 }
  152 
  153 static enum okay
  154 _putname(char const *line, enum gfield w, enum sendaction action,
  155    size_t *gotcha, char const *prefix, FILE *fo, struct name **xp,
  156    enum gfield addflags)
  157 {
  158    struct name *np;
  159    enum okay rv = STOP;
  160    NYD_ENTER;
  161 
  162    np = lextract(line, GEXTRA | GFULL | addflags);
  163    if (xp != NULL)
  164       *xp = np;
  165    if (np == NULL)
  166       ;
  167    else if (fmt(prefix, np, fo, ((w & GCOMMA) |
  168          ((action != SEND_TODISP) ? FMT_DOMIME : 0))))
  169       rv = OKAY;
  170    else if (gotcha != NULL)
  171       ++(*gotcha);
  172    NYD_LEAVE;
  173    return rv;
  174 }
  175 
  176 static int
  177 a_sendout_put_ct(FILE *fo, char const *contenttype, char const *charset){
  178    int rv;
  179    NYD2_ENTER;
  180 
  181    if((rv = fprintf(fo, "Content-Type: %s", contenttype)) < 0)
  182       goto jerr;
  183 
  184    if(charset == NULL)
  185       goto jend;
  186 
  187    if(putc(';', fo) == EOF)
  188       goto jerr;
  189    ++rv;
  190 
  191    if(strlen(contenttype) + sizeof("Content-Type: ;")-1 > 50){
  192       if(putc('\n', fo) == EOF)
  193          goto jerr;
  194       ++rv;
  195    }
  196 
  197    /* C99 */{
  198       int i;
  199 
  200       i = fprintf(fo, " charset=%s", charset);
  201       if(i < 0)
  202          goto jerr;
  203       rv += i;
  204    }
  205 
  206 jend:
  207    if(putc('\n', fo) == EOF)
  208       goto jerr;
  209    ++rv;
  210 jleave:
  211    NYD2_LEAVE;
  212    return rv;
  213 jerr:
  214    rv = -1;
  215    goto jleave;
  216 }
  217 
  218 n_INLINE int
  219 a_sendout_put_cte(FILE *fo, enum conversion conv){
  220    int rv;
  221    NYD2_ENTER;
  222 
  223    /* RFC 2045, 6.1.:
  224     *    This is the default value -- that is,
  225     *    "Content-Transfer-Encoding: 7BIT" is assumed if the
  226     *     Content-Transfer-Encoding header field is not present.
  227     */
  228    rv = (conv == CONV_7BIT) ? 0
  229          : fprintf(fo, "Content-Transfer-Encoding: %s\n",
  230             mime_enc_from_conversion(conv));
  231    NYD2_LEAVE;
  232    return rv;
  233 }
  234 
  235 static int
  236 a_sendout_put_cd(FILE *fo, char const *cd, char const *filename){
  237    struct str f;
  238    si8_t mpc;
  239    int rv;
  240    NYD2_ENTER;
  241 
  242    f.s = NULL;
  243 
  244    /* xxx Ugly with the trailing space in case of wrap! */
  245    if((rv = fprintf(fo, "Content-Disposition: %s; ", cd)) < 0)
  246       goto jerr;
  247 
  248    if(!(mpc = mime_param_create(&f, "filename", filename)))
  249       goto jerr;
  250    /* Always fold if result contains newlines */
  251    if(mpc < 0 || f.l + rv > MIME_LINELEN) { /* FIXME MIME_LINELEN_MAX */
  252       if(putc('\n', fo) == EOF || putc(' ', fo) == EOF)
  253          goto jerr;
  254       rv += 2;
  255    }
  256    if(fputs(f.s, fo) == EOF || putc('\n', fo) == EOF)
  257       goto jerr;
  258    rv += (int)++f.l;
  259 
  260 jleave:
  261    NYD2_LEAVE;
  262    return rv;
  263 jerr:
  264    rv = -1;
  265    goto jleave;
  266 
  267 }
  268 
  269 static bool_t
  270 _sendout_header_list(FILE *fo, struct n_header_field *hfp, bool_t nodisp){
  271    bool_t rv;
  272    NYD2_ENTER;
  273 
  274    for(rv = TRU1; hfp != NULL; hfp = hfp->hf_next)
  275       if(fwrite(hfp->hf_dat, sizeof(char), hfp->hf_nl, fo) != hfp->hf_nl ||
  276             putc(':', fo) == EOF || putc(' ', fo) == EOF ||
  277             xmime_write(hfp->hf_dat + hfp->hf_nl +1, hfp->hf_bl, fo,
  278                (!nodisp ? CONV_NONE : CONV_TOHDR),
  279                (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL, NULL) < 0 ||
  280             putc('\n', fo) == EOF){
  281          rv = FAL0;
  282          break;
  283       }
  284    NYD_LEAVE;
  285    return rv;
  286 }
  287 
  288 static int
  289 a_sendout_body(FILE *fo, FILE *fi, enum conversion convert){
  290    struct str outrest, inrest;
  291    char *buf;
  292    size_t sz, bufsize, cnt;
  293    bool_t iseof;
  294    int rv;
  295    NYD2_ENTER;
  296 
  297    rv = n_ERR_INVAL;
  298    iseof = FAL0;
  299    buf = n_alloc(bufsize = SEND_LINESIZE);
  300    outrest.s = inrest.s = NULL;
  301    outrest.l = inrest.l = 0;
  302 
  303    if(convert == CONV_TOQP
  304 #ifdef HAVE_ICONV
  305          || iconvd != (iconv_t)-1
  306 #endif
  307    ){
  308       fflush(fi);
  309       cnt = fsize(fi);
  310    }
  311 
  312    while(!iseof){
  313       if(convert == CONV_TOQP
  314 #ifdef HAVE_ICONV
  315             || iconvd != (iconv_t)-1
  316 #endif
  317       ){
  318          if(fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
  319             break;
  320       }else if((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
  321          break;
  322 joutln:
  323       if(xmime_write(buf, sz, fo, convert, TD_ICONV, &outrest,
  324             (iseof > FAL0 ? NULL : &inrest)) < 0)
  325          goto jleave;
  326    }
  327    if(iseof <= FAL0 && (outrest.l != 0 || inrest.l != 0)){
  328       sz = 0;
  329       iseof = (iseof || inrest.l == 0) ? TRU1 : TRUM1;
  330       goto joutln;
  331    }
  332 
  333    rv = ferror(fi) ? n_ERR_IO : n_ERR_NONE;
  334 jleave:
  335    if(outrest.s != NULL)
  336       n_free(outrest.s);
  337    if(inrest.s != NULL)
  338       n_free(inrest.s);
  339    n_free(buf);
  340 
  341    NYD2_LEAVE;
  342    return rv;
  343 }
  344 
  345 static int
  346 a_sendout_attach_file(struct header *hp, struct attachment *ap, FILE *fo)
  347 {
  348    /* TODO of course, the MIME classification needs to performed once
  349     * TODO only, not for each and every charset anew ... ;-// */
  350    char *charset_iter_orig[2];
  351    long offs;
  352    int err = 0;
  353    NYD_ENTER;
  354 
  355    /* Is this already in target charset?  Simply copy over */
  356    if (ap->a_conv == AC_TMPFILE) {
  357       err = a_sendout__attach_file(hp, ap, fo);
  358       Fclose(ap->a_tmpf);
  359       DBG( ap->a_tmpf = NULL; )
  360       goto jleave;
  361    }
  362 
  363    /* If we don't apply charset conversion at all (fixed input=ouput charset)
  364     * we also simply copy over, since it's the users desire */
  365    if (ap->a_conv == AC_FIX_INCS) {
  366       ap->a_charset = ap->a_input_charset;
  367       err = a_sendout__attach_file(hp, ap, fo);
  368       goto jleave;
  369    } else
  370       assert(ap->a_input_charset != NULL);
  371 
  372    /* Otherwise we need to iterate over all possible output charsets */
  373    if ((offs = ftell(fo)) == -1) {
  374       err = n_ERR_IO;
  375       goto jleave;
  376    }
  377    charset_iter_recurse(charset_iter_orig);
  378    for (charset_iter_reset(NULL);; charset_iter_next()) {
  379       if (!charset_iter_is_valid()) {
  380          err = n_ERR_NOENT;
  381          break;
  382       }
  383       err = a_sendout__attach_file(hp, ap, fo);
  384       if (err == 0 || (err != n_ERR_ILSEQ && err != n_ERR_INVAL))
  385          break;
  386       clearerr(fo);
  387       if (fseek(fo, offs, SEEK_SET) == -1) {
  388          err = n_ERR_IO;
  389          break;
  390       }
  391       if (ap->a_conv != AC_DEFAULT) {
  392          err = n_ERR_ILSEQ;
  393          break;
  394       }
  395       ap->a_charset = NULL;
  396    }
  397    charset_iter_restore(charset_iter_orig);
  398 jleave:
  399    NYD_LEAVE;
  400    return err;
  401 }
  402 
  403 static int
  404 a_sendout__attach_file(struct header *hp, struct attachment *ap, FILE *fo)
  405 {
  406    int err = 0, do_iconv;
  407    FILE *fi;
  408    char const *charset;
  409    enum conversion convert;
  410    NYD_ENTER;
  411 
  412    /* Either charset-converted temporary file, or plain path */
  413    if (ap->a_conv == AC_TMPFILE) {
  414       fi = ap->a_tmpf;
  415       assert(ftell(fi) == 0);
  416    } else if ((fi = Fopen(ap->a_path, "r")) == NULL) {
  417       err = n_err_no;
  418       n_err(_("%s: %s\n"), n_shexp_quote_cp(ap->a_path, FAL0),
  419          n_err_to_doc(err));
  420       goto jleave;
  421    }
  422 
  423    /* MIME part header for attachment */
  424    {  char const *ct, *cp;
  425 
  426       ct = ap->a_content_type;
  427       charset = ap->a_charset;
  428       convert = n_mimetype_classify_file(fi, (char const**)&ct,
  429          &charset, &do_iconv);
  430       if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
  431             ap->a_conv == AC_TMPFILE)
  432          do_iconv = 0;
  433 
  434       if (fprintf(fo, "\n--%s\n", _sendout_boundary) < 0 ||
  435             a_sendout_put_ct(fo, ct, charset) < 0 ||
  436             a_sendout_put_cte(fo, convert) < 0 ||
  437             a_sendout_put_cd(fo, ap->a_content_disposition, ap->a_name) < 0)
  438          goto jerr_header;
  439 
  440       if((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")){
  441          struct name *np;
  442 
  443          /* TODO RFC 2046 specifies that the same Content-ID should be used
  444           * TODO for identical data; this is too hard for use right now,
  445           * TODO because if done right it should be checksum based!?! */
  446          if((np = ap->a_content_id) != NULL)
  447             cp = np->n_name;
  448          else
  449             cp = a_sendout_random_id(hp, FAL0);
  450 
  451          if(cp != NULL && fprintf(fo, "Content-ID: <%s>\n", cp) < 0)
  452             goto jerr_header;
  453       }
  454 
  455       if ((cp = ap->a_content_description) != NULL &&
  456             (fputs("Content-Description: ", fo) == EOF ||
  457              xmime_write(cp, strlen(cp), fo, CONV_TOHDR, (TD_ISPR | TD_ICONV),
  458                NULL, NULL) < 0 || putc('\n', fo) == EOF))
  459          goto jerr_header;
  460 
  461       if (putc('\n', fo) == EOF) {
  462 jerr_header:
  463          err = n_err_no;
  464          goto jerr_fclose;
  465       }
  466    }
  467 
  468 #ifdef HAVE_ICONV
  469    if (iconvd != (iconv_t)-1)
  470       n_iconv_close(iconvd);
  471    if (do_iconv) {
  472       if (asccasecmp(charset, ap->a_input_charset) &&
  473             (iconvd = n_iconv_open(charset, ap->a_input_charset)
  474                ) == (iconv_t)-1 && (err = n_err_no) != 0) {
  475          if (err == n_ERR_INVAL)
  476             n_err(_("Cannot convert from %s to %s\n"), ap->a_input_charset,
  477                charset);
  478          else
  479             n_err(_("iconv_open: %s\n"), n_err_to_doc(err));
  480          goto jerr_fclose;
  481       }
  482    }
  483 #endif
  484 
  485    err = a_sendout_body(fo, fi, convert);
  486 jerr_fclose:
  487    if(ap->a_conv != AC_TMPFILE)
  488       Fclose(fi);
  489 
  490 jleave:
  491    NYD_LEAVE;
  492    return err;
  493 }
  494 
  495 static bool_t
  496 _sendbundle_setup_creds(struct sendbundle *sbp, bool_t signing_caps)
  497 {
  498    bool_t v15, rv = FAL0;
  499    char *shost, *from;
  500 #ifdef HAVE_SMTP
  501    char const *smtp;
  502 #endif
  503    NYD_ENTER;
  504 
  505    v15 = ok_blook(v15_compat);
  506    shost = (v15 ? ok_vlook(smtp_hostname) : NULL);
  507    from = ((signing_caps || !v15 || shost == NULL)
  508          ? skin(myorigin(sbp->sb_hp)) : NULL);
  509 
  510    if (signing_caps) {
  511       if (from == NULL) {
  512 #ifdef HAVE_SSL
  513          n_err(_("No *from* address for signing specified\n"));
  514          goto jleave;
  515 #endif
  516       } else
  517          sbp->sb_signer.l = strlen(sbp->sb_signer.s = from);
  518    }
  519 
  520 #ifndef HAVE_SMTP
  521    rv = TRU1;
  522 #else
  523    if ((smtp = ok_vlook(smtp)) == NULL) { /* TODO v15 url_creat(,ok_vlook(mta)*/
  524       char const *proto;
  525 
  526       /* *smtp* OBSOLETE message in mta_start() */
  527       if((proto = n_servbyname(smtp = ok_vlook(mta), NULL)) == NULL ||
  528             *proto == '\0'){
  529          rv = TRU1;
  530          goto jleave;
  531       }
  532    }
  533 
  534    if (!url_parse(&sbp->sb_url, CPROTO_SMTP, smtp))
  535       goto jleave;
  536 
  537    if (v15) {
  538       if (shost == NULL) {
  539          if (from == NULL)
  540             goto jenofrom;
  541          sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
  542       } else
  543          __sendout_ident = sbp->sb_url.url_u_h.s;
  544       if (!ccred_lookup(&sbp->sb_ccred, &sbp->sb_url))
  545          goto jleave;
  546    } else {
  547       if ((sbp->sb_url.url_flags & n_URL_HAD_USER) ||
  548             sbp->sb_url.url_pass.s != NULL) {
  549          n_err(_("New-style URL used without *v15-compat* being set\n"));
  550          goto jleave;
  551       }
  552       /* TODO part of the entire myorigin() disaster, get rid of this! */
  553       if (from == NULL) {
  554 jenofrom:
  555          n_err(_("Your configuration requires a *from* address, "
  556             "but none was given\n"));
  557          goto jleave;
  558       }
  559       if (!ccred_lookup_old(&sbp->sb_ccred, CPROTO_SMTP, from))
  560          goto jleave;
  561       sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
  562    }
  563 
  564    rv = TRU1;
  565 #endif /* HAVE_SMTP */
  566 #if defined HAVE_SSL || defined HAVE_SMTP
  567 jleave:
  568 #endif
  569    NYD_LEAVE;
  570    return rv;
  571 }
  572 
  573 static int
  574 a_sendout_attach_msg(struct header *hp, struct attachment *ap, FILE *fo)
  575 {
  576    struct message *mp;
  577    char const *ccp;
  578    int rv;
  579    NYD_ENTER;
  580    n_UNUSED(hp);
  581 
  582    fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
  583        "Content-Disposition: inline\n", _sendout_boundary);
  584    if ((ccp = ap->a_content_description) != NULL)
  585       fprintf(fo, "Content-Description: %s\n", ccp);/* TODO MIME! */
  586    putc('\n', fo);
  587 
  588    mp = message + ap->a_msgno - 1;
  589    touch(mp);
  590    rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
  591    NYD_LEAVE;
  592    return rv;
  593 }
  594 
  595 static int
  596 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
  597    char const *contenttype, char const *charset)
  598 {
  599    struct attachment *att;
  600    int rv = -1;
  601    NYD_ENTER;
  602 
  603    fputs("This is a multi-part message in MIME format.\n", fo);
  604    if(fsize(fi) != 0){
  605       char const *cp;
  606 
  607       if(fprintf(fo, "\n--%s\n", _sendout_boundary) < 0 ||
  608             a_sendout_put_ct(fo, contenttype, charset) < 0 ||
  609             a_sendout_put_cte(fo, convert) < 0 ||
  610             fprintf(fo, "Content-Disposition: inline\n") < 0)
  611          goto jleave;
  612       if (((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) &&
  613             (cp = a_sendout_random_id(hp, FAL0)) != NULL &&
  614             fprintf(fo, "Content-ID: <%s>\n", cp) < 0)
  615          goto jleave;
  616       if(putc('\n', fo) == EOF)
  617          goto jleave;
  618 
  619       if(a_sendout_body(fo, fi, convert) != 0)
  620          goto jleave;
  621 
  622       if(ferror(fi))
  623          goto jleave;
  624    }
  625 
  626    for (att = hp->h_attach; att != NULL; att = att->a_flink) {
  627       if (att->a_msgno) {
  628          if (a_sendout_attach_msg(hp, att, fo) != 0)
  629             goto jleave;
  630       } else if (a_sendout_attach_file(hp, att, fo) != 0)
  631          goto jleave;
  632    }
  633 
  634    /* the final boundary with two attached dashes */
  635    fprintf(fo, "\n--%s--\n", _sendout_boundary);
  636    rv = 0;
  637 jleave:
  638    NYD_LEAVE;
  639    return rv;
  640 }
  641 
  642 static FILE *
  643 infix(struct header *hp, FILE *fi) /* TODO check */
  644 {
  645    char *tempMail;
  646    enum conversion convert;
  647    int do_iconv, err;
  648    char const *contenttype, *charset;
  649    FILE *nfo, *nfi;
  650 #ifdef HAVE_ICONV
  651    char const *tcs, *convhdr = NULL;
  652 #endif
  653    NYD_ENTER;
  654 
  655    nfi = NULL;
  656    charset = NULL;
  657    do_iconv = 0;
  658    n_UNINIT(err, 0);
  659 
  660    if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER))
  661          == NULL) {
  662       n_perr(_("infix: temporary mail file"), 0);
  663       goto jleave;
  664    }
  665    if ((nfi = Fopen(tempMail, "r")) == NULL) {
  666       n_perr(tempMail, 0);
  667       Fclose(nfo);
  668    }
  669    Ftmp_release(&tempMail);
  670    if (nfi == NULL)
  671       goto jleave;
  672 
  673    n_pstate &= ~n_PS_HEADER_NEEDED_MIME; /* TODO hack -> be carrier tracked */
  674 
  675    contenttype = "text/plain";
  676    if((n_poption & n_PO_Mm_FLAG) && n_poption_arg_Mm != NULL)
  677       contenttype = n_poption_arg_Mm;
  678    convert = n_mimetype_classify_file(fi, &contenttype, &charset, &do_iconv);
  679 
  680 #ifdef HAVE_ICONV
  681    tcs = ok_vlook(ttycharset);
  682    if ((convhdr = need_hdrconv(hp))) {
  683       if (iconvd != (iconv_t)-1) /* XXX  */
  684          n_iconv_close(iconvd);
  685       if (asccasecmp(convhdr, tcs) != 0 &&
  686             (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
  687             (err = n_err_no) != 0)
  688          goto jiconv_err;
  689    }
  690 #endif
  691    if(puthead(FAL0, hp, nfo,
  692          (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
  693          GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset)){
  694       err = 1;
  695       goto jerr;
  696    }
  697 #ifdef HAVE_ICONV
  698    if (iconvd != (iconv_t)-1)
  699       n_iconv_close(iconvd);
  700 #endif
  701 
  702 #ifdef HAVE_ICONV
  703    if (do_iconv && charset != NULL) { /*TODO charset->n_mimetype_classify_file*/
  704       if (asccasecmp(charset, tcs) != 0 &&
  705             (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
  706             (err = n_err_no) != 0) {
  707 jiconv_err:
  708          if (err == n_ERR_INVAL)
  709             n_err(_("Cannot convert from %s to %s\n"), tcs, charset);
  710          else
  711             n_perr("iconv_open", 0);
  712          goto jerr;
  713       }
  714    }
  715 #endif
  716 
  717    if(hp->h_attach != NULL){
  718       if(make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0){
  719          err = 1;
  720          goto jerr;
  721       }
  722    }else if(a_sendout_body(nfo, fi, convert) != 0){
  723       err = 1;
  724       goto jerr;
  725    }
  726 
  727    fflush(nfo);
  728    if((err = ferror(nfo)))
  729       n_perr(_("infix: temporary mail file I/O"), 0);
  730 jerr:
  731    Fclose(nfo);
  732 
  733    if(!err){
  734       fflush_rewind(nfi);
  735       Fclose(fi);
  736    }else{
  737       Fclose(nfi);
  738       nfi = NULL;
  739    }
  740 jleave:
  741 #ifdef HAVE_ICONV
  742    if(iconvd != (iconv_t)-1)
  743       n_iconv_close(iconvd);
  744 #endif
  745    NYD_LEAVE;
  746    return nfi;
  747 }
  748 
  749 static bool_t
  750 _check_dispo_notif(struct name *mdn, struct header *hp, FILE *fo)
  751 {
  752    char const *from;
  753    bool_t rv = TRU1;
  754    NYD_ENTER;
  755 
  756    /* TODO smtp_disposition_notification (RFC 3798): relation to return-path
  757     * TODO not yet checked */
  758    if (!ok_blook(disposition_notification_send))
  759       goto jleave;
  760 
  761    if (mdn != NULL && mdn != (struct name*)0x1)
  762       from = mdn->n_name;
  763    else if ((from = myorigin(hp)) == NULL) {
  764       if (n_poption & n_PO_D_V)
  765          n_err(_("*disposition-notification-send*: *from* not set\n"));
  766       goto jleave;
  767    }
  768 
  769    if (fmt("Disposition-Notification-To:", nalloc(n_UNCONST(from), 0), fo, 0))
  770       rv = FAL0;
  771 jleave:
  772    NYD_LEAVE;
  773    return rv;
  774 }
  775 
  776 static int
  777 sendmail_internal(void *v, int recipient_record)
  778 {
  779    struct header head;
  780    char *str = v;
  781    int rv;
  782    NYD_ENTER;
  783 
  784    memset(&head, 0, sizeof head);
  785    head.h_mailx_command = "mail";
  786    if((head.h_to = lextract(str, GTO |
  787          (ok_blook(fullnames) ? GFULL | GSKIN : GSKIN))) != NULL)
  788       head.h_mailx_raw_to = namelist_dup(head.h_to, head.h_to->n_type);
  789    rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
  790    NYD_LEAVE;
  791    return (rv != OKAY); /* reverse! */
  792 }
  793 
  794 static struct name *
  795 a_sendout_file_a_pipe(struct name *names, FILE *fo, bool_t *senderror){
  796    ui32_t pipecnt, xcnt, i;
  797    char const *sh;
  798    struct name *np;
  799    FILE *fp, **fppa;
  800    NYD_ENTER;
  801 
  802    fp = NULL;
  803    fppa = NULL;
  804 
  805    /* Look through all recipients and do a quick return if no file or pipe
  806     * addressee is found */
  807    for(pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink){
  808       if(np->n_type & GDEL)
  809          continue;
  810       switch(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE){
  811       case NAME_ADDRSPEC_ISFILE: ++xcnt; break;
  812       case NAME_ADDRSPEC_ISPIPE: ++pipecnt; break;
  813       }
  814    }
  815    if((pipecnt | xcnt) == 0)
  816       goto jleave;
  817 
  818    /* Otherwise create an array of file descriptors for each found pipe
  819     * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
  820     * each pipe subprocess needs its very own file descriptor, and we need
  821     * to deal with that.
  822     * To make our life a bit easier let's just use the auto-reclaimed
  823     * string storage */
  824    if(pipecnt == 0 || (n_poption & n_PO_DEBUG)){
  825       pipecnt = 0;
  826       sh = NULL;
  827    }else{
  828       i = sizeof(FILE*) * pipecnt;
  829       fppa = n_lofi_alloc(i);
  830       memset(fppa, 0, i);
  831       sh = ok_vlook(SHELL);
  832    }
  833 
  834    for(np = names; np != NULL; np = np->n_flink){
  835       if(!(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) || (np->n_type & GDEL))
  836          continue;
  837 
  838       /* In days of old we removed the entry from the the list; now for sake of
  839        * header expansion we leave it in and mark it as deleted */
  840       np->n_type |= GDEL;
  841 
  842       if(n_poption & n_PO_D_VV)
  843          n_err(_(">>> Writing message via %s\n"),
  844             n_shexp_quote_cp(np->n_name, FAL0));
  845       /* We _do_ write to STDOUT, anyway! */
  846       if((n_poption & n_PO_DEBUG) && ((np->n_flags & NAME_ADDRSPEC_ISPIPE) ||
  847             np->n_name[0] != '-' || np->n_name[1] != '\0'))
  848          continue;
  849 
  850       /* See if we have copied the complete message out yet.  If not, do so */
  851       if(fp == NULL){
  852          int c;
  853          char *tempEdit;
  854 
  855          if((fp = Ftmp(&tempEdit, "outof", OF_RDWR | OF_HOLDSIGS | OF_REGISTER)
  856                ) == NULL){
  857             n_perr(_("Creation of temporary image"), 0);
  858             pipecnt = 0;
  859             goto jerror;
  860          }
  861 
  862          for(i = 0; i < pipecnt; ++i)
  863             if((fppa[i] = Fopen(tempEdit, "r")) == NULL){
  864                n_perr(_("Creation of pipe image descriptor"), 0);
  865                break;
  866             }
  867 
  868          Ftmp_release(&tempEdit);
  869          if(i != pipecnt){
  870             pipecnt = i;
  871             goto jerror;
  872          }
  873 
  874          fprintf(fp, "From %s %s", ok_vlook(LOGNAME), time_current.tc_ctime);
  875          c = EOF;
  876          while(i = c, (c = getc(fo)) != EOF)
  877             putc(c, fp);
  878          rewind(fo);
  879          if((int)i != '\n')
  880             putc('\n', fp);
  881          putc('\n', fp);
  882          fflush(fp);
  883          if(ferror(fp)){
  884             n_perr(_("Finalizing write of temporary image"), 0);
  885             goto jerror;
  886          }
  887 
  888          /* From now on use xcnt as a counter for pipecnt */
  889          xcnt = 0;
  890       }
  891 
  892       /* Now either copy "image" to the desired file or give it as the standard
  893        * input to the desired program as appropriate */
  894       if(np->n_flags & NAME_ADDRSPEC_ISPIPE){
  895          int pid;
  896          sigset_t nset;
  897 
  898          sigemptyset(&nset);
  899          sigaddset(&nset, SIGHUP);
  900          sigaddset(&nset, SIGINT);
  901          sigaddset(&nset, SIGQUIT);
  902          pid = n_child_start(sh, &nset, fileno(fppa[xcnt++]), n_CHILD_FD_NULL,
  903                "-c", &np->n_name[1], NULL, NULL);
  904          if(pid < 0){
  905             n_err(_("Piping message to %s failed\n"),
  906                n_shexp_quote_cp(np->n_name, FAL0));
  907             goto jerror;
  908          }
  909          n_child_free(pid);
  910       }else{
  911          int c;
  912          FILE *fout;
  913          char const *fname, *fnameq;
  914 
  915          if((fname = fexpand(np->n_name, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
  916             goto jerror;
  917          fnameq = n_shexp_quote_cp(fname, FAL0);
  918 
  919          if(fname[0] == '-' && fname[1] == '\0')
  920             fout = n_stdout;
  921          else{
  922             int xerr;
  923             enum n_fopen_state fs;
  924 
  925             if((fout = n_fopen_any(fname, "a+", &fs)) == NULL){
  926                xerr = n_err_no;
  927 jefile:
  928                n_err(_("Writing message to %s failed: %s\n"),
  929                   fnameq, n_err_to_doc(xerr));
  930                goto jerror;
  931             }
  932 
  933             if((fs & (n_PROTO_MASK | n_FOPEN_STATE_EXISTS)) ==
  934                   (n_PROTO_FILE | n_FOPEN_STATE_EXISTS)){
  935                n_file_lock(fileno(fout), FLT_WRITE, 0,0, UIZ_MAX);
  936 
  937                if((xerr = n_folder_mbox_prepare_append(fout, NULL)
  938                      ) != n_ERR_NONE)
  939                   goto jefile;
  940             }
  941          }
  942 
  943          rewind(fp);
  944          while((c = getc(fp)) != EOF)
  945             putc(c, fout);
  946          if(ferror(fout)){
  947             n_err(_("Writing message to %s failed: %s\n"),
  948                fnameq, _("write error"));
  949             *senderror = TRU1;
  950          }
  951 
  952          if(fout != n_stdout)
  953             Fclose(fout);
  954       }
  955    }
  956 
  957 jleave:
  958    if(fp != NULL)
  959       Fclose(fp);
  960    if(fppa != NULL){
  961       for(i = 0; i < pipecnt; ++i)
  962          if((fp = fppa[i]) != NULL)
  963             Fclose(fp);
  964       n_lofi_free(fppa);
  965    }
  966    NYD_LEAVE;
  967    return names;
  968 
  969 jerror:
  970    *senderror = TRU1;
  971    while(np != NULL){
  972       if(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
  973          np->n_type |= GDEL;
  974       np = np->n_flink;
  975    }
  976    goto jleave;
  977 }
  978 
  979 static bool_t
  980 mightrecord(FILE *fp, struct name *to, bool_t resend){
  981    char *cp;
  982    char const *ccp;
  983    bool_t rv;
  984    NYD2_ENTER;
  985 
  986    rv = TRU1;
  987 
  988    if(n_poption & n_PO_DEBUG)
  989       ccp = NULL;
  990    else if(to != NULL){
  991       ccp = cp = savestr(skinned_name(to));
  992       while(*cp != '\0' && *cp != '@')
  993          ++cp;
  994       *cp = '\0';
  995    }else
  996       ccp = ok_vlook(record);
  997 
  998    if(ccp != NULL){
  999       if((cp = fexpand(ccp, FEXP_NSHELL)) == NULL)
 1000          goto jbail;
 1001 
 1002       switch(*(ccp = cp)){
 1003       case '.':
 1004          if(cp[1] != '/'){ /* DIRSEP */
 1005       default:
 1006             if(ok_blook(outfolder)){
 1007                struct str s;
 1008                char const *nccp, *folder;
 1009 
 1010                switch(which_protocol(ccp, TRU1, FAL0, &nccp)){
 1011                case PROTO_FILE:
 1012                   ccp = "file://";
 1013                   if(0){
 1014                   /* FALLTHRU */
 1015                case PROTO_MAILDIR:
 1016                      ccp = "maildir://";
 1017                   }
 1018                   folder = n_folder_query();
 1019 #ifdef HAVE_IMAP
 1020                   if(which_protocol(folder, FAL0, FAL0, NULL) == PROTO_IMAP){
 1021                      n_err(_("(*record*): *outfolder* set, *folder* is IMAP "
 1022                         "based: only one protocol per file is possible\n"));
 1023                      goto jbail;
 1024                   }
 1025 #endif
 1026                   ccp = str_concat_csvl(&s, ccp, folder, nccp, NULL)->s;
 1027                   /* FALLTHRU */
 1028                default:
 1029                   break;
 1030                }
 1031             }
 1032          }
 1033          /* FALLTHRU */
 1034       case '/':
 1035          break;
 1036       }
 1037 
 1038       if(!a_sendout__savemail(ccp, fp, resend)){
 1039 jbail:
 1040          n_err(_("Failed to save message in %s - message not sent\n"),
 1041             n_shexp_quote_cp(ccp, FAL0));
 1042          n_exit_status |= n_EXIT_ERR;
 1043          savedeadletter(fp, 1);
 1044          rv = FAL0;
 1045       }
 1046    }
 1047    NYD2_LEAVE;
 1048    return rv;
 1049 }
 1050 
 1051 static bool_t
 1052 a_sendout__savemail(char const *name, FILE *fp, bool_t resend){
 1053    FILE *fo;
 1054    size_t bufsize, buflen, cnt;
 1055    enum n_fopen_state fs;
 1056    bool_t rv, emptyline;
 1057    char *buf;
 1058    NYD_ENTER;
 1059 
 1060    buf = smalloc(bufsize = LINESIZE);
 1061    rv = FAL0;
 1062 
 1063    if((fo = n_fopen_any(name, "a+", &fs)) == NULL){
 1064       n_perr(name, 0);
 1065       goto j_leave;
 1066    }
 1067 
 1068    if((fs & (n_PROTO_MASK | n_FOPEN_STATE_EXISTS)) ==
 1069          (n_PROTO_FILE | n_FOPEN_STATE_EXISTS)){
 1070       int xerr;
 1071 
 1072       /* TODO RETURN check, but be aware of protocols: v15: Mailbox->lock()!
 1073        * TODO BETTER yet: should be returned in lock state already! */
 1074       n_file_lock(fileno(fo), FLT_WRITE, 0,0, UIZ_MAX);
 1075 
 1076       if((xerr = n_folder_mbox_prepare_append(fo, NULL)) != n_ERR_NONE){
 1077          n_perr(name, xerr);
 1078          goto jleave;
 1079       }
 1080    }
 1081 
 1082    fflush_rewind(fp);
 1083    rv = TRU1;
 1084 
 1085    fprintf(fo, "From %s %s", ok_vlook(LOGNAME), time_current.tc_ctime);
 1086    for(emptyline = FAL0, buflen = 0, cnt = fsize(fp);
 1087          fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL;){
 1088       /* Only if we are resending it can happen that we have to quote From_
 1089        * lines here; we don't generate messages which are ambiguous ourselves */
 1090       if(resend){
 1091          if(emptyline && is_head(buf, buflen, FAL0))
 1092             putc('>', fo);
 1093       }DBG(else assert(!is_head(buf, buflen, FAL0)); )
 1094 
 1095       emptyline = (buflen > 0 && *buf == '\n');
 1096       fwrite(buf, sizeof *buf, buflen, fo);
 1097    }
 1098    if(buflen > 0 && buf[buflen - 1] != '\n')
 1099       putc('\n', fo);
 1100    putc('\n', fo);
 1101    fflush(fo);
 1102    if(ferror(fo)){
 1103       n_perr(name, 0);
 1104       rv = FAL0;
 1105    }
 1106 
 1107 jleave:
 1108    really_rewind(fp);
 1109    if(Fclose(fo) != 0)
 1110       rv = FAL0;
 1111 j_leave:
 1112    free(buf);
 1113    NYD_LEAVE;
 1114    return rv;
 1115 }
 1116 
 1117 static bool_t
 1118 _transfer(struct sendbundle *sbp)
 1119 {
 1120    struct name *np;
 1121    ui32_t cnt;
 1122    bool_t rv = TRU1;
 1123    NYD_ENTER;
 1124 
 1125    for (cnt = 0, np = sbp->sb_to; np != NULL;) {
 1126       char const k[] = "smime-encrypt-", *cp;
 1127       size_t nl = strlen(np->n_name);
 1128       char *vs = ac_alloc(sizeof(k)-1 + nl +1);
 1129       memcpy(vs, k, sizeof(k) -1);
 1130       memcpy(vs + sizeof(k) -1, np->n_name, nl +1);
 1131 
 1132       if ((cp = n_var_vlook(vs, FAL0)) != NULL) {
 1133 #ifdef HAVE_SSL
 1134          FILE *ef;
 1135 
 1136          if ((ef = smime_encrypt(sbp->sb_input, cp, np->n_name)) != NULL) {
 1137             FILE *fisave = sbp->sb_input;
 1138             struct name *nsave = sbp->sb_to;
 1139 
 1140             sbp->sb_to = ndup(np, np->n_type & ~(GFULL | GSKIN));
 1141             sbp->sb_input = ef;
 1142             if (!__mta_start(sbp))
 1143                rv = FAL0;
 1144             sbp->sb_to = nsave;
 1145             sbp->sb_input = fisave;
 1146 
 1147             Fclose(ef);
 1148          } else {
 1149 #else
 1150             n_err(_("No SSL support compiled in\n"));
 1151             rv = FAL0;
 1152 #endif
 1153             n_err(_("Message not sent to: %s\n"), np->n_name);
 1154             _sendout_error = TRU1;
 1155 #ifdef HAVE_SSL
 1156          }
 1157 #endif
 1158          rewind(sbp->sb_input);
 1159 
 1160          if (np->n_flink != NULL)
 1161             np->n_flink->n_blink = np->n_blink;
 1162          if (np->n_blink != NULL)
 1163             np->n_blink->n_flink = np->n_flink;
 1164          if (np == sbp->sb_to)
 1165             sbp->sb_to = np->n_flink;
 1166          np = np->n_flink;
 1167       } else {
 1168          ++cnt;
 1169          np = np->n_flink;
 1170       }
 1171       ac_free(vs);
 1172    }
 1173 
 1174    if (cnt > 0 && (ok_blook(smime_force_encryption) || !__mta_start(sbp)))
 1175       rv = FAL0;
 1176    NYD_LEAVE;
 1177    return rv;
 1178 }
 1179 
 1180 static bool_t
 1181 __mta_start(struct sendbundle *sbp)
 1182 {
 1183    pid_t pid;
 1184    sigset_t nset;
 1185    char const **args, *mta;
 1186    bool_t rv;
 1187    NYD_ENTER;
 1188 
 1189    /* Let rv mean "is smtp-based MTA" */
 1190    if((mta = ok_vlook(smtp)) != NULL){
 1191       n_OBSOLETE(_("please don't use *smtp*: assign a smtp:// URL to *mta*!"));
 1192       /* For *smtp* the smtp:// protocol was optional; be simple: don't check
 1193        * that *smtp* is misused with file:// or so */
 1194       if(n_servbyname(mta, NULL) == NULL)
 1195          mta = savecat("smtp://", mta);
 1196       rv = TRU1;
 1197    }else{
 1198       char const *proto;
 1199 
 1200       mta = ok_vlook(mta); /* TODO v15: what solely remains in here */
 1201       if((proto = ok_vlook(sendmail)) != NULL)
 1202          n_OBSOLETE(_("please use *mta* instead of *sendmail*"));
 1203       if(proto != NULL && !strcmp(mta, VAL_MTA))
 1204          mta = proto;
 1205 
 1206       /* TODO for now this is pretty hacky: in v15 we should simply create
 1207        * TODO an URL object; i.e., be able to do so, and it does it right
 1208        * TODO I.e.,: url_creat(&url, ok_vlook(mta)); */
 1209       if((proto = n_servbyname(mta, NULL)) != NULL){
 1210          if(*proto == '\0'){
 1211             mta += sizeof("file://") -1;
 1212             rv = FAL0;
 1213          }else
 1214             rv = TRU1;
 1215       }else
 1216          rv = FAL0;
 1217    }
 1218 
 1219    if(!rv){
 1220       char const *mta_base;
 1221 
 1222       if((mta = fexpand(mta_base = mta, FEXP_LOCAL | FEXP_NOPROTO)) == NULL){
 1223          n_err(_("*mta* variable expansion failure: %s\n"),
 1224             n_shexp_quote_cp(mta_base, FAL0));
 1225          goto jstop;
 1226       }
 1227 
 1228       args = __mta_prepare_args(sbp->sb_to, sbp->sb_hp);
 1229       if (n_poption & n_PO_DEBUG) {
 1230          __mta_debug(sbp, mta, args);
 1231          rv = TRU1;
 1232          goto jleave;
 1233       }
 1234    } else {
 1235       n_UNINIT(args, NULL);
 1236 #ifndef HAVE_SMTP
 1237       n_err(_("No SMTP support compiled in\n"));
 1238       goto jstop;
 1239 #else
 1240       /* C99 */{
 1241          struct name *np;
 1242 
 1243          for(np = sbp->sb_to; np != NULL; np = np->n_flink)
 1244             if(!(np->n_type & GDEL) && (np->n_flags & NAME_ADDRSPEC_ISNAME)){
 1245                n_err(_("SMTP *mta* cannot send to alias name: %s\n"),
 1246                   n_shexp_quote_cp(np->n_name, FAL0));
 1247                rv = FAL0;
 1248             }
 1249          if(!rv)
 1250             goto jstop;
 1251       }
 1252 
 1253       if (n_poption & n_PO_DEBUG) {
 1254          (void)smtp_mta(sbp);
 1255          rv = TRU1;
 1256          goto jleave;
 1257       }
 1258 #endif
 1259    }
 1260 
 1261    /* Fork, set up the temporary mail file as standard input for "mail", and
 1262     * exec with the user list we generated far above */
 1263    if ((pid = n_child_fork()) == -1) {
 1264       n_perr("fork", 0);
 1265 jstop:
 1266       savedeadletter(sbp->sb_input, 0);
 1267       _sendout_error = TRU1;
 1268       goto jleave;
 1269    }
 1270    if (pid == 0) {
 1271       sigemptyset(&nset);
 1272       sigaddset(&nset, SIGHUP);
 1273       sigaddset(&nset, SIGINT);
 1274       sigaddset(&nset, SIGQUIT);
 1275       sigaddset(&nset, SIGTSTP);
 1276       sigaddset(&nset, SIGTTIN);
 1277       sigaddset(&nset, SIGTTOU);
 1278       /* n_stdin = */freopen(n_path_devnull, "r", stdin);
 1279 #ifdef HAVE_SMTP
 1280       if (rv) {
 1281          n_child_prepare(&nset, 0, 1);
 1282          if (smtp_mta(sbp))
 1283             _exit(n_EXIT_OK);
 1284       } else
 1285 #endif
 1286       {
 1287          char const *ecp;
 1288          int e;
 1289 
 1290          n_child_prepare(&nset, fileno(sbp->sb_input), -1);
 1291          execv(mta, n_UNCONST(args));
 1292          e = n_err_no;
 1293          ecp = (e != n_ERR_NOENT) ? n_err_to_doc(e)
 1294                : _("executable not found (adjust *mta* variable)");
 1295          n_err(_("Cannot start %s: %s\n"), n_shexp_quote_cp(mta, FAL0), ecp);
 1296       }
 1297       savedeadletter(sbp->sb_input, 1);
 1298       n_err(_("... message not sent\n"));
 1299       _exit(n_EXIT_ERR);
 1300    }
 1301 
 1302    if ((n_poption & n_PO_D_V) || ok_blook(sendwait)) {
 1303       if (!(rv = n_child_wait(pid, NULL)))
 1304          _sendout_error = TRU1;
 1305    } else {
 1306       n_child_free(pid);
 1307       rv = TRU1;
 1308    }
 1309 jleave:
 1310    NYD_LEAVE;
 1311    return rv;
 1312 }
 1313 
 1314 static char const **
 1315 __mta_prepare_args(struct name *to, struct header *hp)
 1316 {
 1317    size_t vas_cnt, i, j;
 1318    char **vas;
 1319    char const **args, *cp, *cp_v15compat;
 1320    bool_t snda;
 1321    NYD_ENTER;
 1322 
 1323    if((cp_v15compat = ok_vlook(sendmail_arguments)) != NULL)
 1324       n_OBSOLETE(_("please use *mta-arguments*, not *sendmail-arguments*"));
 1325    if((cp = ok_vlook(mta_arguments)) == NULL)
 1326       cp = cp_v15compat;
 1327    if ((cp /* TODO v15: = ok_vlook(mta_arguments)*/) == NULL) {
 1328       vas_cnt = 0;
 1329       vas = NULL;
 1330    } else {
 1331       /* Don't assume anything on the content but do allocate exactly j slots;
 1332        * like this getrawlist will never overflow (and return -1) */
 1333       j = strlen(cp);
 1334       vas = n_lofi_alloc(sizeof(*vas) * j);
 1335       vas_cnt = (size_t)getrawlist(TRU1, vas, j, cp, j);
 1336    }
 1337 
 1338    i = 4 + n_smopts_cnt + vas_cnt + 4 + 1 + count(to) + 1;
 1339    args = salloc(i * sizeof(char*));
 1340 
 1341    if((cp_v15compat = ok_vlook(sendmail_progname)) != NULL)
 1342       n_OBSOLETE(_("please use *mta-argv0*, not *sendmail-progname*"));
 1343    cp = ok_vlook(mta_argv0);
 1344    if(cp_v15compat != NULL && !strcmp(cp, VAL_MTA_ARGV0))
 1345       cp = cp_v15compat;
 1346    args[0] = cp/* TODO v15 only : = ok_vlook(mta_argv0) */;
 1347 
 1348    if ((snda = ok_blook(sendmail_no_default_arguments)))
 1349       n_OBSOLETE(_("please use *mta-no-default-arguments*, "
 1350          "not *sendmail-no-default-arguments*"));
 1351    snda |= ok_blook(mta_no_default_arguments);
 1352    if ((snda /* TODO v15: = ok_blook(mta_no_default_arguments)*/))
 1353       i = 1;
 1354    else {
 1355       args[1] = "-i";
 1356       i = 2;
 1357       if (ok_blook(metoo))
 1358          args[i++] = "-m";
 1359       if (n_poption & n_PO_VERB)
 1360          args[i++] = "-v";
 1361    }
 1362 
 1363    for (j = 0; j < n_smopts_cnt; ++j, ++i)
 1364       args[i] = n_smopts[j];
 1365 
 1366    for (j = 0; j < vas_cnt; ++j, ++i)
 1367       args[i] = vas[j];
 1368 
 1369    /* -r option?  In conjunction with -t we act compatible to postfix(1) and
 1370     * ignore it (it is -f / -F there) if the message specified From:/Sender:.
 1371     * The interdependency with -t has been resolved in puthead() */
 1372    if (!snda && ((n_poption & n_PO_r_FLAG) || ok_blook(r_option_implicit))) {
 1373       struct name const *np;
 1374 
 1375       if (hp != NULL && (np = hp->h_from) != NULL) {
 1376          /* However, what wasn't resolved there was the case that the message
 1377           * specified multiple From: addresses and a Sender: */
 1378          if ((n_psonce & n_PSO_t_FLAG) && hp->h_sender != NULL)
 1379             np = hp->h_sender;
 1380 
 1381          if (np->n_fullextra != NULL) {
 1382             args[i++] = "-F";
 1383             args[i++] = np->n_fullextra;
 1384          }
 1385          cp = np->n_name;
 1386       } else {
 1387          assert(n_poption_arg_r == NULL);
 1388          cp = skin(myorigin(NULL));
 1389       }
 1390 
 1391       if (cp != NULL) {
 1392          args[i++] = "-f";
 1393          args[i++] = cp;
 1394       }
 1395    }
 1396 
 1397    /* Terminate option list to avoid false interpretation of system-wide
 1398     * aliases that start with hyphen */
 1399    if (!snda)
 1400       args[i++] = "--";
 1401 
 1402    /* Receivers follow */
 1403    if(!ok_blook(mta_no_receiver_arguments))
 1404       for (; to != NULL; to = to->n_flink)
 1405          if (!(to->n_type & GDEL))
 1406             args[i++] = to->n_name;
 1407    args[i] = NULL;
 1408 
 1409    if(vas != NULL)
 1410       n_lofi_free(vas);
 1411    NYD_LEAVE;
 1412    return args;
 1413 }
 1414 
 1415 static void
 1416 __mta_debug(struct sendbundle *sbp, char const *mta, char const **args)
 1417 {
 1418    size_t cnt, bufsize, llen;
 1419    char *buf;
 1420    NYD_ENTER;
 1421 
 1422    n_err(_(">>> MTA: %s, arguments:"), n_shexp_quote_cp(mta, FAL0));
 1423    for (; *args != NULL; ++args)
 1424       n_err(" %s", n_shexp_quote_cp(*args, FAL0));
 1425    n_err("\n");
 1426 
 1427    fflush_rewind(sbp->sb_input);
 1428 
 1429    cnt = fsize(sbp->sb_input);
 1430    buf = NULL;
 1431    bufsize = 0;
 1432    while (fgetline(&buf, &bufsize, &cnt, &llen, sbp->sb_input, TRU1) != NULL) {
 1433       buf[--llen] = '\0';
 1434       n_err(">>> %s\n", buf);
 1435    }
 1436    if (buf != NULL)
 1437       free(buf);
 1438    NYD_LEAVE;
 1439 }
 1440 
 1441 static char const *
 1442 a_sendout_random_id(struct header *hp, bool_t msgid)
 1443 {
 1444    static ui32_t reprocnt;
 1445    struct tm *tmp;
 1446    char const *h;
 1447    size_t rl, i;
 1448    char *rv, sep;
 1449    NYD_ENTER;
 1450 
 1451    rv = NULL;
 1452 
 1453    if(msgid && hp != NULL && hp->h_message_id != NULL){
 1454       rv = hp->h_message_id->n_name;
 1455       goto jleave;
 1456    }
 1457 
 1458    if(ok_blook(message_id_disable))
 1459       goto jleave;
 1460 
 1461    sep = '%';
 1462    rl = 5;
 1463    if((h = __sendout_ident) != NULL)
 1464       goto jgen;
 1465    if(ok_vlook(hostname) != NULL){
 1466       h = n_nodename(TRU1);
 1467       sep = '@';
 1468       rl = 8;
 1469       goto jgen;
 1470    }
 1471    if(hp != NULL && (h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
 1472       goto jgen;
 1473    goto jleave;
 1474 
 1475 jgen:
 1476    tmp = &time_current.tc_gm;
 1477    i = sizeof("%04d%02d%02d%02d%02d%02d.%s%c%s") -1 + rl + strlen(h);
 1478    rv = n_autorec_alloc(i +1);
 1479    snprintf(rv, i, "%04d%02d%02d%02d%02d%02d.%s%c%s",
 1480       tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
 1481       tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
 1482       n_random_create_cp(rl, &reprocnt), sep, h);
 1483    rv[i] = '\0'; /* Because we don't test snprintf(3) return */
 1484 jleave:
 1485    NYD_LEAVE;
 1486    return rv;
 1487 }
 1488 
 1489 static int
 1490 fmt(char const *str, struct name *np, FILE *fo, enum fmt_flags ff)
 1491 {
 1492    enum {
 1493       m_INIT   = 1<<0,
 1494       m_COMMA  = 1<<1,
 1495       m_NOPF   = 1<<2,
 1496       m_NONAME = 1<<3,
 1497       m_CSEEN  = 1<<4
 1498    } m = (ff & GCOMMA) ? m_COMMA : 0;
 1499    ssize_t col, len;
 1500    int rv = 1;
 1501    NYD_ENTER;
 1502 
 1503    col = strlen(str);
 1504    if (col) {
 1505       fwrite(str, sizeof *str, col, fo);
 1506 #undef _X
 1507 #define _X(S)  (col == sizeof(S) -1 && !asccasecmp(str, S))
 1508       if (ff & GFILES) {
 1509          ;
 1510       } else if (_X("reply-to:") || _X("mail-followup-to:") ||
 1511             _X("references:") || _X("in-reply-to:") ||
 1512             _X("disposition-notification-to:"))
 1513          m |= m_NOPF | m_NONAME;
 1514       else if (ok_blook(add_file_recipients)) {
 1515          ;
 1516       } else if (_X("to:") || _X("cc:") || _X("bcc:") || _X("resent-to:"))
 1517          m |= m_NOPF;
 1518 #undef _X
 1519    }
 1520 
 1521    for (; np != NULL; np = np->n_flink) {
 1522       if(np->n_type & GDEL)
 1523          continue;
 1524       if(is_addr_invalid(np,
 1525                ((ff & FMT_INC_INVADDR ? 0 : EACM_NOLOG) |
 1526                 (m & m_NONAME ? EACM_NONAME : EACM_NONE))) &&
 1527             !(ff & FMT_INC_INVADDR))
 1528          continue;
 1529 
 1530       /* File and pipe addresses only printed with set *add-file-recipients* */
 1531       if ((m & m_NOPF) && is_fileorpipe_addr(np))
 1532          continue;
 1533 
 1534       if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
 1535          if (putc(',', fo) == EOF)
 1536             goto jleave;
 1537          m |= m_CSEEN;
 1538          ++col;
 1539       }
 1540 
 1541       len = strlen(np->n_fullname);
 1542       if (np->n_type & GREF)
 1543          len += 2;
 1544       ++col; /* The separating space */
 1545       if ((m & m_INIT) && /*col > 1 &&*/
 1546             UICMP(z, col + len, >,
 1547                (np->n_type & GREF ? MIME_LINELEN : 72))) {
 1548          if (fputs("\n ", fo) == EOF)
 1549             goto jleave;
 1550          col = 1;
 1551          m &= ~m_CSEEN;
 1552       } else
 1553          putc(' ', fo);
 1554       m = (m & ~m_CSEEN) | m_INIT;
 1555 
 1556       /* C99 */{
 1557          char *hb;
 1558 
 1559          /* GREF needs to be placed in angle brackets, but which are missing */
 1560          hb = np->n_fullname;
 1561          if(np->n_type & GREF){
 1562             assert(UICMP(z, len, ==, strlen(np->n_fullname) + 2));
 1563             hb = n_lofi_alloc(len +1);
 1564             len -= 2;
 1565             hb[0] = '<';
 1566             hb[len + 1] = '>';
 1567             hb[len + 2] = '\0';
 1568             memcpy(&hb[1], np->n_fullname, len);
 1569             len += 2;
 1570          }
 1571          len = xmime_write(hb, len, fo,
 1572                ((ff & FMT_DOMIME) ? CONV_TOHDR_A : CONV_NONE), TD_ICONV,
 1573                NULL, NULL);
 1574          if(np->n_type & GREF)
 1575             n_lofi_free(hb);
 1576       }
 1577       if (len < 0)
 1578          goto jleave;
 1579       col += len;
 1580    }
 1581 
 1582    if (putc('\n', fo) != EOF)
 1583       rv = 0;
 1584 jleave:
 1585    NYD_LEAVE;
 1586    return rv;
 1587 }
 1588 
 1589 static int
 1590 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
 1591    int add_resent)
 1592 {
 1593    size_t cnt, c, bufsize = 0;
 1594    char *buf = NULL;
 1595    char const *cp;
 1596    struct name *fromfield = NULL, *senderfield = NULL, *mdn;
 1597    int rv = 1;
 1598    NYD_ENTER;
 1599 
 1600    cnt = mp->m_size;
 1601 
 1602    /* Write the Resent-Fields */
 1603    if (add_resent) {
 1604       fputs("Resent-", fo);
 1605       mkdate(fo, "Date");
 1606       if ((cp = myaddrs(NULL)) != NULL) {
 1607          if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
 1608                &fromfield, 0))
 1609             goto jleave;
 1610       }
 1611       /* TODO RFC 5322: Resent-Sender SHOULD NOT be used if it's EQ -From: */
 1612       if ((cp = ok_vlook(sender)) != NULL) {
 1613          if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
 1614                &senderfield, 0))
 1615             goto jleave;
 1616       }
 1617       if (fmt("Resent-To:", to, fo, FMT_COMMA))
 1618          goto jleave;
 1619       if (((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) &&
 1620             (cp = a_sendout_random_id(NULL, TRU1)) != NULL &&
 1621             fprintf(fo, "Resent-Message-ID: <%s>\n", cp) < 0)
 1622          goto jleave;
 1623    }
 1624 
 1625    if ((mdn = n_UNCONST(check_from_and_sender(fromfield, senderfield))) == NULL)
 1626       goto jleave;
 1627    if (!_check_dispo_notif(mdn, NULL, fo))
 1628       goto jleave;
 1629 
 1630    /* Write the original headers */
 1631    while (cnt > 0) {
 1632       if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
 1633          break;
 1634       if (ascncasecmp("status:", buf, 7) &&
 1635             ascncasecmp("disposition-notification-to:", buf, 28) &&
 1636             !is_head(buf, c, FAL0))
 1637          fwrite(buf, sizeof *buf, c, fo);
 1638       if (cnt > 0 && *buf == '\n')
 1639          break;
 1640    }
 1641 
 1642    /* Write the message body */
 1643    while (cnt > 0) {
 1644       if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
 1645          break;
 1646       if (cnt == 0 && *buf == '\n')
 1647          break;
 1648       fwrite(buf, sizeof *buf, c, fo);
 1649    }
 1650    if (buf != NULL)
 1651       free(buf);
 1652    if (ferror(fo)) {
 1653       n_perr(_("infix_resend: temporary mail file"), 0);
 1654       goto jleave;
 1655    }
 1656    rv = 0;
 1657 jleave:
 1658    NYD_LEAVE;
 1659    return rv;
 1660 }
 1661 
 1662 FL int
 1663 mail(struct name *to, struct name *cc, struct name *bcc, char const *subject,
 1664    struct attachment *attach, char const *quotefile, int recipient_record)
 1665 {
 1666    struct header head;
 1667    struct str in, out;
 1668    bool_t fullnames;
 1669    NYD_ENTER;
 1670 
 1671    memset(&head, 0, sizeof head);
 1672 
 1673    /* The given subject may be in RFC1522 format. */
 1674    if (subject != NULL) {
 1675       in.s = n_UNCONST(subject);
 1676       in.l = strlen(subject);
 1677       mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
 1678       head.h_subject = out.s;
 1679    }
 1680 
 1681    fullnames = ok_blook(fullnames);
 1682 
 1683    head.h_mailx_command = "mail";
 1684    if((head.h_to = to) != NULL){
 1685       if(!fullnames)
 1686          head.h_to = to = a_sendout_fullnames_cleanup(to);
 1687       head.h_mailx_raw_to = namelist_dup(to, to->n_type);
 1688    }
 1689    if((head.h_cc = cc) != NULL){
 1690       if(!fullnames)
 1691          head.h_cc = cc = a_sendout_fullnames_cleanup(cc);
 1692       head.h_mailx_raw_cc = namelist_dup(cc, cc->n_type);
 1693    }
 1694    if((head.h_bcc = bcc) != NULL){
 1695       if(!fullnames)
 1696          head.h_bcc = bcc = a_sendout_fullnames_cleanup(bcc);
 1697       head.h_mailx_raw_bcc = namelist_dup(bcc, bcc->n_type);
 1698    }
 1699 
 1700    head.h_attach = attach;
 1701 
 1702    mail1(&head, 0, NULL, quotefile, recipient_record, 0);
 1703 
 1704    if (subject != NULL)
 1705       free(out.s);
 1706    NYD_LEAVE;
 1707    return 0; /* TODO only for main.c, -> n_exit_status, BUT: ARGH! */
 1708 }
 1709 
 1710 FL int
 1711 c_sendmail(void *v)
 1712 {
 1713    int rv;
 1714    NYD_ENTER;
 1715 
 1716    rv = sendmail_internal(v, 0);
 1717    NYD_LEAVE;
 1718    return rv;
 1719 }
 1720 
 1721 FL int
 1722 c_Sendmail(void *v)
 1723 {
 1724    int rv;
 1725    NYD_ENTER;
 1726 
 1727    rv = sendmail_internal(v, 1);
 1728    NYD_LEAVE;
 1729    return rv;
 1730 }
 1731 
 1732 FL enum okay
 1733 mail1(struct header *hp, int printheaders, struct message *quote,
 1734    char const *quotefile, int recipient_record, int doprefix)
 1735 {
 1736    struct n_sigman sm;
 1737    struct sendbundle sb;
 1738    struct name *to;
 1739    bool_t dosign;
 1740    FILE * volatile mtf, *nmtf;
 1741    enum okay volatile rv;
 1742    NYD_ENTER;
 1743 
 1744    _sendout_error = FAL0;
 1745    __sendout_ident = NULL;
 1746    n_pstate_err_no = n_ERR_INVAL;
 1747    rv = STOP;
 1748    mtf = NULL;
 1749 
 1750    n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
 1751    case 0:
 1752       break;
 1753    default:
 1754       goto jleave;
 1755    }
 1756 
 1757    /* Update some globals we likely need first */
 1758    time_current_update(&time_current, TRU1);
 1759 
 1760    /* Collect user's mail from standard input.  Get the result as mtf */
 1761    mtf = collect(hp, printheaders, quote, quotefile, doprefix, &_sendout_error);
 1762    if (mtf == NULL)
 1763       goto jleave;
 1764    /* TODO All custom headers should be joined here at latest
 1765     * TODO In fact that should happen before we enter compose mode, so that the
 1766     * TODO -C headers can be managed (removed etc.) via ~^, too, but the
 1767     * TODO *customhdr* ones are fixated at this very place here, no sooner! */
 1768 
 1769    dosign = TRUM1;
 1770 
 1771    /* */
 1772    if(n_psonce & n_PSO_INTERACTIVE){
 1773       if (ok_blook(asksign))
 1774          dosign = getapproval(_("Sign this message"), TRU1);
 1775    }
 1776 
 1777    if(fsize(mtf) == 0){
 1778       if(n_poption & n_PO_E_FLAG){
 1779          n_pstate_err_no = n_ERR_NONE;
 1780          rv = OKAY;
 1781          goto jleave;
 1782       }
 1783 
 1784       if(hp->h_subject == NULL)
 1785          n_err(_("No message, no subject; hope that's ok\n"));
 1786       else if(ok_blook(bsdcompat) || ok_blook(bsdmsgs))
 1787          n_err(_("Null message body; hope that's ok\n"));
 1788    }
 1789 
 1790    if (dosign == TRUM1)
 1791       dosign = ok_blook(smime_sign); /* TODO USER@HOST <-> *from* +++!!! */
 1792 #ifndef HAVE_SSL
 1793    if (dosign) {
 1794       n_err(_("No SSL support compiled in\n"));
 1795       goto jleave;
 1796    }
 1797 #endif
 1798 
 1799    /* XXX Update time_current again; once collect() offers editing of more
 1800     * XXX headers, including Date:, this must only happen if Date: is the
 1801     * XXX same that it was before collect() (e.g., postponing etc.).
 1802     * XXX But *do* update otherwise because the mail seems to be backdated
 1803     * XXX if the user edited some time, which looks odd and it happened
 1804     * XXX to me that i got mis-dated response mails due to that... */
 1805    time_current_update(&time_current, TRU1);
 1806 
 1807    /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
 1808     * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
 1809     * TODO but: offer a hook that rebuilds/expands/checks/fixates all
 1810     * TODO header fields ONCE, call that ONCE after user editing etc. has
 1811     * TODO completed (one edit cycle) */
 1812 
 1813    /* Take the user names from the combined to and cc lists and do all the
 1814     * alias processing.  The POSIX standard says:
 1815     *   The names shall be substituted when alias is used as a recipient
 1816     *   address specified by the user in an outgoing message (that is,
 1817     *   other recipients addressed indirectly through the reply command
 1818     *   shall not be substituted in this manner).
 1819     * S-nail thus violates POSIX, as has been pointed out correctly by
 1820     * Martin Neitzel, but logic and usability of POSIX standards is not seldom
 1821     * disputable anyway.  Go for user friendliness */
 1822 
 1823    to = namelist_vaporise_head(hp,
 1824          (EACM_NORMAL |
 1825           (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
 1826          TRU1, &_sendout_error);
 1827 
 1828    if(to == NULL){
 1829       n_err(_("No recipients specified\n"));
 1830       n_pstate_err_no = n_ERR_DESTADDRREQ;
 1831       goto jfail_dead;
 1832    }
 1833    if(_sendout_error < 0){
 1834       n_err(_("Some addressees were classified as \"hard error\"\n"));
 1835       n_pstate_err_no = n_ERR_PERM;
 1836       goto jfail_dead;
 1837    }
 1838 
 1839    /* */
 1840    memset(&sb, 0, sizeof sb);
 1841    sb.sb_hp = hp;
 1842    sb.sb_to = to;
 1843    sb.sb_input = mtf;
 1844    if((dosign || count_nonlocal(to) > 0) &&
 1845          !_sendbundle_setup_creds(&sb, (dosign > 0))){
 1846       /* TODO saving $DEAD and recovering etc is not yet well defined */
 1847       n_pstate_err_no = n_ERR_INVAL;
 1848       goto jfail_dead;
 1849    }
 1850 
 1851    /* 'Bit ugly kind of control flow until we find a charset that does it */
 1852    for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
 1853       int err;
 1854 
 1855       if (!charset_iter_is_valid())
 1856          err = n_ERR_NOENT;
 1857       else if ((nmtf = infix(hp, mtf)) != NULL)
 1858          break;
 1859       else if ((err = n_iconv_err_no) == n_ERR_ILSEQ || err == n_ERR_INVAL ||
 1860             err == n_ERR_NOENT) {
 1861          rewind(mtf);
 1862          continue;
 1863       }
 1864 
 1865       n_perr(_("Cannot find a usable character set to encode message"), err);
 1866       n_pstate_err_no = n_ERR_NOTSUP;
 1867       goto jfail_dead;
 1868    }
 1869    mtf = nmtf;
 1870 
 1871    /*  */
 1872 #ifdef HAVE_SSL
 1873    if (dosign) {
 1874       if ((nmtf = smime_sign(mtf, sb.sb_signer.s)) == NULL)
 1875          goto jfail_dead;
 1876       Fclose(mtf);
 1877       mtf = nmtf;
 1878    }
 1879 #endif
 1880 
 1881    /* TODO truly - i still don't get what follows: (1) we deliver file
 1882     * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
 1883     * TODO even if (1) savedeadletter() etc.  To me this doesn't make sense? */
 1884 
 1885    /* C99 */{
 1886       ui32_t cnt;
 1887       bool_t b;
 1888 
 1889       /* Deliver pipe and file addressees */
 1890       b = (ok_blook(record_files) && count(to) > 0);
 1891       to = a_sendout_file_a_pipe(to, mtf, &_sendout_error);
 1892 
 1893       if (_sendout_error)
 1894          savedeadletter(mtf, FAL0);
 1895 
 1896       to = elide(to); /* XXX only to drop GDELs due a_sendout_file_a_pipe()! */
 1897       cnt = count(to);
 1898 
 1899       if ((recipient_record || b || cnt > 0) &&
 1900             !mightrecord(mtf, (recipient_record ? to : NULL), FAL0))
 1901          goto jleave;
 1902       if (cnt > 0) {
 1903          sb.sb_hp = hp;
 1904          sb.sb_to = to;
 1905          sb.sb_input = mtf;
 1906          if (_transfer(&sb))
 1907             rv = OKAY;
 1908       } else if (!_sendout_error)
 1909          rv = OKAY;
 1910    }
 1911 
 1912    n_sigman_cleanup_ping(&sm);
 1913 jleave:
 1914    if(mtf != NULL){
 1915       char const *cp;
 1916 
 1917       Fclose(mtf);
 1918 
 1919       if((cp = ok_vlook(on_compose_cleanup)) != NULL)
 1920          temporary_compose_mode_hook_call(cp, NULL, NULL);
 1921    }
 1922 
 1923    temporary_compose_mode_hook_unroll();
 1924 
 1925    if (_sendout_error)
 1926       n_exit_status |= n_EXIT_SEND_ERROR;
 1927    if(rv == OKAY)
 1928       n_pstate_err_no = n_ERR_NONE;
 1929    NYD_LEAVE;
 1930    n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
 1931    return rv;
 1932 
 1933 jfail_dead:
 1934    _sendout_error = TRU1;
 1935    savedeadletter(mtf, TRU1);
 1936    n_err(_("... message not sent\n"));
 1937    goto jleave;
 1938 }
 1939 
 1940 FL int
 1941 mkdate(FILE *fo, char const *field)
 1942 {
 1943    struct tm tmpgm, *tmptr;
 1944    int tzdiff, tzdiff_hour, tzdiff_min, rv;
 1945    NYD_ENTER;
 1946 
 1947    memcpy(&tmpgm, &time_current.tc_gm, sizeof tmpgm);
 1948    tzdiff = time_current.tc_time - mktime(&tmpgm);
 1949    tzdiff_hour = (int)(tzdiff / 60);
 1950    tzdiff_min = tzdiff_hour % 60;
 1951    tzdiff_hour /= 60;
 1952    tmptr = &time_current.tc_local;
 1953    if (tmptr->tm_isdst > 0)
 1954       ++tzdiff_hour;
 1955    rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
 1956          field,
 1957          n_weekday_names[tmptr->tm_wday],
 1958          tmptr->tm_mday, n_month_names[tmptr->tm_mon],
 1959          tmptr->tm_year + 1900, tmptr->tm_hour,
 1960          tmptr->tm_min, tmptr->tm_sec,
 1961          tzdiff_hour * 100 + tzdiff_min);
 1962    NYD_LEAVE;
 1963    return rv;
 1964 }
 1965 
 1966 FL int
 1967 puthead(bool_t nosend_msg, struct header *hp, FILE *fo, enum gfield w,
 1968    enum sendaction action, enum conversion convert, char const *contenttype,
 1969    char const *charset)
 1970 {
 1971 #define FMT_CC_AND_BCC()   \
 1972 do {\
 1973    if ((w & GCC) && (hp->h_cc != NULL || nosend_msg == TRUM1)) {\
 1974       if (fmt("Cc:", hp->h_cc, fo, ff))\
 1975          goto jleave;\
 1976       ++gotcha;\
 1977    }\
 1978    if ((w & GBCC) && (hp->h_bcc != NULL || nosend_msg == TRUM1)) {\
 1979       if (fmt("Bcc:", hp->h_bcc, fo, ff))\
 1980          goto jleave;\
 1981       ++gotcha;\
 1982    }\
 1983 } while (0)
 1984 
 1985    char const *addr;
 1986    size_t gotcha;
 1987    struct name *np, *fromasender = NULL;
 1988    int stealthmua, rv = 1;
 1989    bool_t nodisp;
 1990    enum fmt_flags ff;
 1991    NYD_ENTER;
 1992 
 1993    if ((addr = ok_vlook(stealthmua)) != NULL)
 1994       stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
 1995    else
 1996       stealthmua = 0;
 1997    gotcha = 0;
 1998    nodisp = (action != SEND_TODISP);
 1999    ff = (w & (GCOMMA | GFILES)) | (nodisp ? FMT_DOMIME : 0);
 2000    if(nosend_msg)
 2001       ff |= FMT_INC_INVADDR;
 2002 
 2003    if (w & GDATE)
 2004       mkdate(fo, "Date"), ++gotcha;
 2005    if (w & GIDENT) {
 2006       if (hp->h_from == NULL || hp->h_sender == NULL)
 2007          setup_from_and_sender(hp);
 2008 
 2009       if (hp->h_from != NULL) {
 2010          if (fmt("From:", hp->h_from, fo, ff))
 2011             goto jleave;
 2012          ++gotcha;
 2013       }
 2014 
 2015       if (hp->h_sender != NULL) {
 2016          if (fmt("Sender:", hp->h_sender, fo, ff))
 2017             goto jleave;
 2018          ++gotcha;
 2019       }
 2020 
 2021       fromasender = n_UNCONST(check_from_and_sender(hp->h_from, hp->h_sender));
 2022       if (fromasender == NULL)
 2023          goto jleave;
 2024       /* Note that fromasender is (NULL,) 0x1 or real sender here */
 2025    }
 2026 
 2027 #if 1
 2028    if ((w & GTO) && (hp->h_to != NULL || nosend_msg == TRUM1)) {
 2029       if (fmt("To:", hp->h_to, fo, ff))
 2030          goto jleave;
 2031       ++gotcha;
 2032    }
 2033 #else
 2034    /* TODO Thought about undisclosed recipients:;, but would be such a fake
 2035     * TODO given that we cannot handle group addresses.  Ridiculous */
 2036    if (w & GTO) {
 2037       struct name *xto;
 2038 
 2039       if ((xto = hp->h_to) != NULL) {
 2040          char const ud[] = "To: Undisclosed recipients:;\n" /* TODO groups */;
 2041 
 2042          if (count_nonlocal(xto) != 0 || ok_blook(add_file_recipients) ||
 2043                (hp->h_cc != NULL && count_nonlocal(hp->h_cc) > 0))
 2044             goto jto_fmt;
 2045          if (fwrite(ud, 1, sizeof(ud) -1, fo) != sizeof(ud) -1)
 2046             goto jleave;
 2047          ++gotcha;
 2048       } else if (nosend_msg == TRUM1) {
 2049 jto_fmt:
 2050          if (fmt("To:", hp->h_to, fo, ff))
 2051             goto jleave;
 2052          ++gotcha;
 2053       }
 2054    }
 2055 #endif
 2056 
 2057    if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
 2058       FMT_CC_AND_BCC();
 2059 
 2060    if ((w & GSUBJECT) && (hp->h_subject != NULL || nosend_msg == TRUM1)) {
 2061       if (fwrite("Subject: ", sizeof(char), 9, fo) != 9)
 2062          goto jleave;
 2063       if (hp->h_subject != NULL) {
 2064          size_t sublen;
 2065          char const *sub;
 2066 
 2067          sublen = strlen(sub = subject_re_trim(hp->h_subject));
 2068 
 2069          /* Trimmed something, (re-)add Re: */
 2070          if (sub != hp->h_subject) {
 2071             if (fwrite("Re: ", 1, 4, fo) != 4) /* RFC mandates eng. "Re: " */
 2072                goto jleave;
 2073             if (sublen > 0 &&
 2074                   xmime_write(sub, sublen, fo,
 2075                      (!nodisp ? CONV_NONE : CONV_TOHDR),
 2076                      (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL, NULL) < 0)
 2077                goto jleave;
 2078          }
 2079          /* This may be, e.g., a Fwd: XXX yes, unfortunately we do like that */
 2080          else if (*sub != '\0') {
 2081             if (xmime_write(sub, sublen, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
 2082                   (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL, NULL) < 0)
 2083                goto jleave;
 2084          }
 2085       }
 2086       ++gotcha;
 2087       putc('\n', fo);
 2088    }
 2089 
 2090    if (ok_blook(bsdcompat) || ok_blook(bsdorder))
 2091       FMT_CC_AND_BCC();
 2092 
 2093    if ((w & GMSGID) && stealthmua <= 0 &&
 2094          (addr = a_sendout_random_id(hp, TRU1)) != NULL) {
 2095       if (fprintf(fo, "Message-ID: <%s>\n", addr) < 0)
 2096          goto jleave;
 2097       ++gotcha;
 2098    }
 2099 
 2100    if(w & GREF){
 2101       struct name *xnp;
 2102 
 2103       if((xnp = np = hp->h_ref) != NULL){
 2104          if(fmt("References:", np, fo, 0))
 2105             goto jleave;
 2106          ++gotcha;
 2107       }
 2108       if((np = hp->h_in_reply_to) != NULL || xnp != NULL){
 2109          if(np == NULL)
 2110             for(; xnp != NULL; xnp = xnp->n_flink)
 2111                np = xnp;
 2112          if(fmt("In-Reply-To:", np, fo, 0))
 2113             goto jleave;
 2114          ++gotcha;
 2115       }
 2116    }
 2117 
 2118    if (w & GIDENT) {
 2119       /* Reply-To:.  Be careful not to destroy a possible user input, duplicate
 2120        * the list first.. TODO it is a terrible codebase.. */
 2121       if((np = hp->h_reply_to) != NULL)
 2122          np = namelist_dup(np, np->n_type);
 2123       else{
 2124          char const *v15compat;
 2125 
 2126          if((v15compat = ok_vlook(replyto)) != NULL)
 2127             n_OBSOLETE(_("please use *reply-to*, not *replyto*"));
 2128          if((addr = ok_vlook(reply_to)) == NULL)
 2129             addr = v15compat;
 2130          np = lextract(addr, GEXTRA |
 2131                (ok_blook(fullnames) ? GFULL | GSKIN : GSKIN));
 2132       }
 2133       if (np != NULL &&
 2134             (np = elide(
 2135                checkaddrs(usermap(np, TRU1), EACM_STRICT | EACM_NOLOG,
 2136                   NULL))) != NULL) {
 2137          if (fmt("Reply-To:", np, fo, ff))
 2138             goto jleave;
 2139          ++gotcha;
 2140       }
 2141    }
 2142 
 2143    if ((w & GIDENT) && !nosend_msg) {
 2144       /* Mail-Followup-To: TODO factor out this huge block of code */
 2145       /* Place ourselfs in there if any non-subscribed list is an addressee */
 2146       if((hp->h_flags & HF_LIST_REPLY) || hp->h_mft != NULL ||
 2147             ok_blook(followup_to)){
 2148          enum{
 2149             _ANYLIST = 1u<<(HF__NEXT_SHIFT + 0),
 2150             _HADMFT = 1u<<(HF__NEXT_SHIFT + 1)
 2151          };
 2152          struct name *mft, **mftp, *x;
 2153          ui32_t f;
 2154 
 2155          f = hp->h_flags | (hp->h_mft != NULL ? _HADMFT : 0);
 2156          mft = NULL;
 2157          mftp = &mft;
 2158 
 2159          /* But for that, we have to remove all incarnations of ourselfs first.
 2160           * TODO It is total crap that we have alternates_remove(), is_myname()
 2161           * TODO or whatever; these work only with variables, not with data
 2162           * TODO that is _currently_ in some header fields!!!  v15.0: complete
 2163           * TODO rewrite, object based, lazy evaluated, on-the-fly marked.
 2164           * TODO then this should be a really cheap thing in here... */
 2165          np = elide(n_alternates_remove(cat(
 2166                namelist_dup(hp->h_to, GEXTRA | GFULL),
 2167                namelist_dup(hp->h_cc, GEXTRA | GFULL)), FAL0));
 2168          addr = hp->h_list_post;
 2169 
 2170          while((x = np) != NULL){
 2171             si8_t ml;
 2172 
 2173             np = np->n_flink;
 2174 
 2175             /* XXX is_mlist_mp()?? */
 2176             if((ml = is_mlist(x->n_name, FAL0)) == MLIST_OTHER &&
 2177                   addr != NULL && !asccasecmp(addr, x->n_name))
 2178                ml = MLIST_KNOWN;
 2179 
 2180             /* Any non-subscribed list?  Add ourselves */
 2181             switch(ml){
 2182             case MLIST_KNOWN:
 2183                f |= HF_MFT_SENDER;
 2184                /* FALLTHRU */
 2185             case MLIST_SUBSCRIBED:
 2186                f |= _ANYLIST;
 2187                goto j_mft_add;
 2188             case MLIST_OTHER:
 2189                if(!(f & HF_LIST_REPLY)){
 2190 j_mft_add:
 2191                   if(!is_addr_invalid(x,
 2192                         EACM_STRICT | EACM_NOLOG | EACM_NONAME)){
 2193                      x->n_blink = *mftp;
 2194                      x->n_flink = NULL;
 2195                      *mftp = x;
 2196                      mftp = &x->n_flink;
 2197                   } /* XXX write some warning?  if verbose?? */
 2198                   continue;
 2199                }
 2200                /* And if this is a reply that honoured a M-F-T: header then
 2201                 * we'll also add all members of the original M-F-T: that are
 2202                 * still addressed by us, regardless of other circumstances */
 2203                else if(f & _HADMFT){
 2204                   struct name *ox;
 2205 
 2206                   for(ox = hp->h_mft; ox != NULL; ox = ox->n_flink)
 2207                      if(!asccasecmp(ox->n_name, x->n_name))
 2208                         goto j_mft_add;
 2209                }
 2210                break;
 2211             }
 2212          }
 2213 
 2214          if((f & (_ANYLIST | _HADMFT)) && mft != NULL){
 2215             if(((f & HF_MFT_SENDER) ||
 2216                      ((f & (_ANYLIST | _HADMFT)) == _HADMFT)) &&
 2217                   (np = fromasender) != NULL && np != (struct name*)0x1)
 2218                *mftp = ndup(np, (np->n_type & ~GMASK) | GEXTRA | GFULL);
 2219 
 2220             if(fmt("Mail-Followup-To:", mft, fo, ff))
 2221                goto jleave;
 2222             ++gotcha;
 2223          }
 2224       }
 2225 
 2226       if (!_check_dispo_notif(fromasender, hp, fo))
 2227          goto jleave;
 2228    }
 2229 
 2230    if ((w & GUA) && stealthmua == 0) {
 2231       if (fprintf(fo, "User-Agent: %s %s\n", n_uagent,
 2232             (n_psonce & n_PSO_REPRODUCIBLE
 2233                ? n_reproducible_name : ok_vlook(version))) < 0)
 2234          goto jleave;
 2235       ++gotcha;
 2236    }
 2237 
 2238    /* Custom headers, as via -C and *customhdr* TODO JOINED AFTER COMPOSE! */
 2239    if(!nosend_msg){
 2240       struct n_header_field *chlp[2], *hfp;
 2241       ui32_t i;
 2242 
 2243       chlp[0] = n_poption_arg_C;
 2244       chlp[1] = n_customhdr_list;
 2245 
 2246       for(i = 0; i < n_NELEM(chlp); ++i)
 2247          if((hfp = chlp[i]) != NULL){
 2248             if(!_sendout_header_list(fo, hfp, nodisp))
 2249                goto jleave;
 2250             ++gotcha;
 2251          }
 2252    }
 2253 
 2254    /* The user may have placed headers when editing */
 2255    if(1){
 2256       struct n_header_field *hfp;
 2257 
 2258       if((hfp = hp->h_user_headers) != NULL){
 2259          if(!_sendout_header_list(fo, hfp, nodisp))
 2260             goto jleave;
 2261          ++gotcha;
 2262       }
 2263    }
 2264 
 2265    /* We don't need MIME unless.. we need MIME?! */
 2266    if ((w & GMIME) && ((n_pstate & n_PS_HEADER_NEEDED_MIME) ||
 2267          hp->h_attach != NULL ||
 2268          ((n_poption & n_PO_Mm_FLAG) && n_poption_arg_Mm != NULL) ||
 2269          convert != CONV_7BIT || !n_iconv_name_is_ascii(charset))) {
 2270       ++gotcha;
 2271       if (fputs("MIME-Version: 1.0\n", fo) == EOF)
 2272          goto jleave;
 2273       if (hp->h_attach != NULL) {
 2274          _sendout_boundary = mime_param_boundary_create();/*TODO carrier*/
 2275          if (fprintf(fo,
 2276                "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
 2277                _sendout_boundary) < 0)
 2278             goto jleave;
 2279       } else {
 2280          if(a_sendout_put_ct(fo, contenttype, charset) < 0 ||
 2281                a_sendout_put_cte(fo, convert) < 0)
 2282             goto jleave;
 2283       }
 2284    }
 2285 
 2286    if (gotcha && (w & GNL))
 2287       if (putc('\n', fo) == EOF)
 2288          goto jleave;
 2289    rv = 0;
 2290 jleave:
 2291    NYD_LEAVE;
 2292    return rv;
 2293 #undef FMT_CC_AND_BCC
 2294 }
 2295 
 2296 FL enum okay
 2297 resend_msg(struct message *mp, struct header *hp, bool_t add_resent)
 2298 {
 2299    struct n_sigman sm;
 2300    struct sendbundle sb;
 2301    FILE * volatile ibuf, *nfo, * volatile nfi;
 2302    char *tempMail;
 2303    struct name *to;
 2304    enum okay volatile rv;
 2305    NYD_ENTER;
 2306 
 2307    _sendout_error = FAL0;
 2308    __sendout_ident = NULL;
 2309    n_pstate_err_no = n_ERR_INVAL;
 2310    rv = STOP;
 2311    to = hp->h_to;
 2312    nfi = ibuf = NULL;
 2313 
 2314    n_SIGMAN_ENTER_SWITCH(&sm, n_SIGMAN_ALL) {
 2315    case 0:
 2316       break;
 2317    default:
 2318       goto jleave;
 2319    }
 2320 
 2321    /* Update some globals we likely need first */
 2322    time_current_update(&time_current, TRU1);
 2323 
 2324    /* If we fail we delay that a bit until we can write $DEAD! */
 2325 
 2326    if((to = checkaddrs(to,
 2327          (EACM_NORMAL |
 2328           (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
 2329          &_sendout_error)) == NULL){
 2330       n_err(_("No recipients specified\n"));
 2331       n_pstate_err_no = n_ERR_DESTADDRREQ;
 2332    }else if(_sendout_error < 0){
 2333       n_err(_("Some addressees were classified as \"hard error\"\n"));
 2334       n_pstate_err_no = n_ERR_PERM;
 2335    }
 2336 
 2337    if((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER)
 2338          ) == NULL) {
 2339       _sendout_error = TRU1;
 2340       n_perr(_("resend_msg: temporary mail file"), 0);
 2341       n_pstate_err_no = n_ERR_IO;
 2342       goto jleave;
 2343    }
 2344    if((nfi = Fopen(tempMail, "r")) == NULL){
 2345       n_perr(tempMail, 0);
 2346       n_pstate_err_no = n_ERR_IO;
 2347    }
 2348    Ftmp_release(&tempMail);
 2349    if(nfi == NULL)
 2350       goto jerr_o;
 2351 
 2352    if((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL){
 2353       n_pstate_err_no = n_ERR_IO;
 2354       goto jerr_io;
 2355    }
 2356 
 2357    /* C99 */{
 2358       char const *cp;
 2359 
 2360       if((cp = ok_vlook(on_resend_enter)) != NULL){
 2361          /*setup_from_and_sender(hp);*/
 2362          temporary_compose_mode_hook_call(cp, &n_temporary_compose_hook_varset,
 2363             hp);
 2364       }
 2365    }
 2366 
 2367    memset(&sb, 0, sizeof sb);
 2368    sb.sb_to = to;
 2369    sb.sb_input = nfi;
 2370    if(!_sendout_error &&
 2371          count_nonlocal(to) > 0 && !_sendbundle_setup_creds(&sb, FAL0)){
 2372       /* ..wait until we can write DEAD */
 2373       n_pstate_err_no = n_ERR_INVAL;
 2374       _sendout_error = -1;
 2375    }
 2376 
 2377    if(infix_resend(ibuf, nfo, mp, to, add_resent) != 0){
 2378 jfail_dead:
 2379       savedeadletter(nfi, TRU1);
 2380       n_err(_("... message not sent\n"));
 2381 jerr_io:
 2382       Fclose(nfi);
 2383       nfi = NULL;
 2384 jerr_o:
 2385       Fclose(nfo);
 2386       _sendout_error = TRU1;
 2387       goto jleave;
 2388    }
 2389 
 2390    if(_sendout_error < 0)
 2391       goto jfail_dead;
 2392 
 2393    Fclose(nfo);
 2394    rewind(nfi);
 2395 
 2396    /* C99 */{
 2397       bool_t b, c;
 2398 
 2399       /* Deliver pipe and file addressees */
 2400       b = (ok_blook(record_files) && count(to) > 0);
 2401       to = a_sendout_file_a_pipe(to, nfi, &_sendout_error);
 2402 
 2403       if(_sendout_error)
 2404          savedeadletter(nfi, FAL0);
 2405 
 2406       to = elide(to); /* XXX only to drop GDELs due a_sendout_file_a_pipe()! */
 2407       c = (count(to) > 0);
 2408 
 2409       if(b || c){
 2410          if(!ok_blook(record_resent) || mightrecord(nfi, NULL, TRU1)){
 2411             sb.sb_to = to;
 2412             /*sb.sb_input = nfi;*/
 2413             if(!c || _transfer(&sb))
 2414                rv = OKAY;
 2415          }
 2416       }else if(!_sendout_error)
 2417          rv = OKAY;
 2418    }
 2419 
 2420    n_sigman_cleanup_ping(&sm);
 2421 jleave:
 2422    if(nfi != NULL){
 2423       char const *cp;
 2424 
 2425       Fclose(nfi);
 2426 
 2427       if(ibuf != NULL){
 2428          if((cp = ok_vlook(on_resend_cleanup)) != NULL)
 2429             temporary_compose_mode_hook_call(cp, NULL, NULL);
 2430 
 2431          temporary_compose_mode_hook_unroll();
 2432       }
 2433    }
 2434 
 2435    if (_sendout_error)
 2436       n_exit_status |= n_EXIT_SEND_ERROR;
 2437    if(rv == OKAY)
 2438       n_pstate_err_no = n_ERR_NONE;
 2439    NYD_LEAVE;
 2440    n_sigman_leave(&sm, n_SIGMAN_VIPSIGS_NTTYOUT);
 2441    return rv;
 2442 }
 2443 
 2444 FL void
 2445 savedeadletter(FILE *fp, bool_t fflush_rewind_first){
 2446    struct n_string line;
 2447    int c;
 2448    enum {a_NONE, a_INIT = 1<<0, a_BODY = 1<<1, a_NL = 1<<2} flags;
 2449    ul_i bytes, lines;
 2450    FILE *dbuf;
 2451    char const *cp, *cpq;
 2452    NYD_ENTER;
 2453 
 2454    if(!ok_blook(save))
 2455       goto jleave;
 2456 
 2457    if(fflush_rewind_first){
 2458       fflush(fp);
 2459       rewind(fp);
 2460    }
 2461    if(fsize(fp) == 0)
 2462       goto jleave;
 2463 
 2464    cp = n_getdeadletter();
 2465    cpq = n_shexp_quote_cp(cp, FAL0);
 2466 
 2467    if(n_poption & n_PO_DEBUG){
 2468       n_err(_(">>> Would (try to) write $DEAD %s\n"), cpq);
 2469       goto jleave;
 2470    }
 2471 
 2472    if((dbuf = Fopen(cp, "w")) == NULL){
 2473       n_perr(_("Cannot save to $DEAD"), 0);
 2474       goto jleave;
 2475    }
 2476    n_file_lock(fileno(dbuf), FLT_WRITE, 0,0, UIZ_MAX); /* XXX Natomic */
 2477 
 2478    fprintf(n_stdout, "%s ", cpq);
 2479    fflush(n_stdout);
 2480 
 2481    /* TODO savedeadletter() non-conforming: should check whether we have any
 2482     * TODO headers, if not we need to place "something", anything will do.
 2483     * TODO MIME is completely missing, we use MBOXO quoting!!  Yuck.
 2484     * TODO I/O error handling missing.  Yuck! */
 2485    n_string_reserve(n_string_creat_auto(&line), 2 * SEND_LINESIZE);
 2486    bytes = (ul_i)fprintf(dbuf, "From %s %s",
 2487          ok_vlook(LOGNAME), time_current.tc_ctime);
 2488    lines = 1;
 2489    for(flags = a_NONE, c = '\0'; c != EOF; bytes += line.s_len, ++lines){
 2490       n_string_trunc(&line, 0);
 2491       while((c = getc(fp)) != EOF && c != '\n')
 2492          n_string_push_c(&line, c);
 2493 
 2494       /* TODO It may be that we have only some plain text.  It may be that we
 2495        * TODO have a complete MIME encoded message.  We don't know, and we
 2496        * TODO have no usable mechanism to dig it!!  We need v15! */
 2497       if(!(flags & a_INIT)){
 2498          size_t i;
 2499 
 2500          /* Throw away leading empty lines! */
 2501          if(line.s_len == 0)
 2502             continue;
 2503          for(i = 0; i < line.s_len; ++i){
 2504             if(fieldnamechar(line.s_dat[i]))
 2505                continue;
 2506             if(line.s_dat[i] == ':'){
 2507                flags |= a_INIT;
 2508                break;
 2509             }else{
 2510                /* We have no headers, this is already a body line! */
 2511                flags |= a_INIT | a_BODY;
 2512                break;
 2513             }
 2514          }
 2515          /* Well, i had to check whether the RFC allows this.  Assume we've
 2516           * passed the headers, too, then! */
 2517          if(i == line.s_len)
 2518             flags |= a_INIT | a_BODY;
 2519       }
 2520       if(flags & a_BODY){
 2521          if(line.s_len >= 5 && !memcmp(line.s_dat, "From ", 5))
 2522             n_string_unshift_c(&line, '>');
 2523       }
 2524       if(line.s_len == 0)
 2525          flags |= a_BODY | a_NL;
 2526       else
 2527          flags &= ~a_NL;
 2528 
 2529       n_string_push_c(&line, '\n');
 2530       fwrite(line.s_dat, sizeof *line.s_dat, line.s_len, dbuf);
 2531    }
 2532    if(!(flags & a_NL)){
 2533       putc('\n', dbuf);
 2534       ++bytes;
 2535       ++lines;
 2536    }
 2537    n_string_gut(&line);
 2538 
 2539    Fclose(dbuf);
 2540    fprintf(n_stdout, "%lu/%lu\n", lines, bytes);
 2541    fflush(n_stdout);
 2542 
 2543    rewind(fp);
 2544 jleave:
 2545    NYD_LEAVE;
 2546 }
 2547 
 2548 #undef SEND_LINESIZE
 2549 
 2550 /* s-it-mode */