"Fossies" - the Fresh Open Source Software Archive

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


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

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