"Fossies" - the Fresh Open Source Software Archive

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