"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.19/src/mx/cmd-resend.c" (26 Apr 2020, 31320 Bytes) of package /linux/misc/s-nail-14.9.19.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 "cmd-resend.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.18_vs_14.9.19.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ All sorts of `reply', `resend', `forward', and similar user commands.
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2020 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 su_FILE
   37 #define su_FILE cmd_resend
   38 #define mx_SOURCE
   39 
   40 #ifndef mx_HAVE_AMALGAMATION
   41 # include "mx/nail.h"
   42 #endif
   43 
   44 #include <su/cs.h>
   45 #include <su/mem.h>
   46 
   47 #include "mx/attachments.h"
   48 #include "mx/cmd.h"
   49 #include "mx/cmd-charsetalias.h"
   50 #include "mx/cmd-mlist.h"
   51 #include "mx/names.h"
   52 #include "mx/url.h"
   53 #include "mx/tty.h"
   54 
   55 /* TODO fake */
   56 #include "su/code-in.h"
   57 
   58 /* Modify subject we reply to to begin with Re: if it does not already */
   59 static char *a_crese_reedit(char const *subj);
   60 
   61 /* Fetch these headers, as appropriate; *the_rt will be set to Reply-To:
   62  * regardless of whether Reply-To: will be honoured or not */
   63 static struct mx_name *a_crese_reply_to(struct message *mp,
   64       struct mx_name **the_rt);
   65 static struct mx_name *a_crese_mail_followup_to(struct message *mp);
   66 
   67 /* We honoured Reply-To: and/or Mail-Followup-To:, but *recipients-in-cc* is
   68  * set so try to keep "secondary" addressees in Cc:, if possible, */
   69 static void a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
   70       struct mx_name *np);
   71 
   72 /* *reply-to-swap-in* */
   73 static boole a_crese_do_rt_swap_in(struct header *hp, struct mx_name *the_rt);
   74 static void a_crese_rt_swap_in(struct header *hp, struct mx_name *the_rt);
   75 
   76 /* References and charset, as appropriate */
   77 static void a_crese_make_ref_and_cs(struct message *mp, struct header *head);
   78 
   79 /* `reply' and `Lreply' workhorse */
   80 static int a_crese_list_reply(int *msgvec, enum header_flags hf);
   81 
   82 /* Get PTF to implementation of command c (i.e., take care for *flipr*) */
   83 static int (*a_crese_reply_or_Reply(char c))(int *, boole);
   84 
   85 /* Reply to a single message.  Extract each name from the message header and
   86  * send them off to mail1() */
   87 static int a_crese_reply(int *msgvec, boole recipient_record);
   88 
   89 /* Reply to a series of messages by simply mailing to the senders and not
   90  * messing around with the To: and Cc: lists as in normal reply */
   91 static int a_crese_Reply(int *msgvec, boole recipient_record);
   92 
   93 /* Forward a message to a new recipient, in the sense of RFC 2822 */
   94 static int a_crese_fwd(void *vp, boole recipient_record);
   95 
   96 /* Modify the subject we are replying to to begin with Fwd: */
   97 static char *a_crese__fwdedit(char *subj);
   98 
   99 /* Do the real work of resending */
  100 static int a_crese_resend1(void *v, boole add_resent);
  101 
  102 static char *
  103 a_crese_reedit(char const *subj){
  104    char *newsubj;
  105    NYD2_IN;
  106 
  107    newsubj = NULL;
  108 
  109    if(subj != NULL && *subj != '\0'){
  110       struct str in, out;
  111       uz i;
  112       char const *cp;
  113 
  114       in.l = su_cs_len(in.s = n_UNCONST(subj));
  115       mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
  116 
  117       i = su_cs_len(cp = subject_re_trim(out.s)) +1;
  118       /* RFC mandates english "Re: " */
  119       newsubj = n_autorec_alloc(sizeof("Re: ") -1 + i);
  120       su_mem_copy(newsubj, "Re: ", sizeof("Re: ") -1);
  121       su_mem_copy(&newsubj[sizeof("Re: ") -1], cp, i);
  122 
  123       n_free(out.s);
  124    }
  125    NYD2_OU;
  126    return newsubj;
  127 }
  128 
  129 static struct mx_name *
  130 a_crese_reply_to(struct message *mp, struct mx_name **the_rt){
  131    char const *cp;
  132    struct mx_name *rt, *np;
  133    enum gfield gf;
  134    NYD2_IN;
  135 
  136    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  137    rt = NIL;
  138 
  139    if((cp = hfield1("reply-to", mp)) != NIL)
  140       rt = checkaddrs(lextract(cp, GTO | gf), EACM_STRICT, NIL);
  141 
  142    *the_rt = rt;
  143 
  144    if((cp = ok_vlook(reply_to_honour)) != NIL && rt != NIL){
  145       char *lp;
  146       uz l;
  147       char const *tr;
  148 
  149       if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
  150          fprintf(n_stdout, _("Reply-To: header contains:"));
  151          for(np = rt; np != NIL; np = np->n_flink)
  152             fprintf(n_stdout, " %s", np->n_name);
  153          putc('\n', n_stdout);
  154       }
  155 
  156       tr = _("Reply-To %s%s");
  157       l = su_cs_len(tr) + su_cs_len(rt->n_name) + 3 +1;
  158       lp = n_lofi_alloc(l);
  159 
  160       snprintf(lp, l, tr, rt->n_name, (rt->n_flink != NIL ? "..." : su_empty));
  161       if(n_quadify(cp, UZ_MAX, lp, TRU1) <= FAL0)
  162          rt = NIL;
  163 
  164       n_lofi_free(lp);
  165    }else
  166       rt = NIL;
  167 
  168    NYD2_OU;
  169    return rt;
  170 }
  171 
  172 static struct mx_name *
  173 a_crese_mail_followup_to(struct message *mp){
  174    char const *cp, *cp2;
  175    struct mx_name *mft, *np;
  176    enum gfield gf;
  177    NYD2_IN;
  178 
  179    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  180    mft = NULL;
  181 
  182    if((cp = ok_vlook(followup_to_honour)) != NULL &&
  183          (cp2 = hfield1("mail-followup-to", mp)) != NULL &&
  184          (mft = checkaddrs(lextract(cp2, GTO | gf), EACM_STRICT, NULL)
  185             ) != NULL){
  186       char *lp;
  187       uz l;
  188       char const *tr;
  189 
  190       if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
  191          fprintf(n_stdout, _("Mail-Followup-To: header contains:"));
  192          for(np = mft; np != NULL; np = np->n_flink)
  193             fprintf(n_stdout, " %s", np->n_name);
  194          putc('\n', n_stdout);
  195       }
  196 
  197       tr = _("Followup-To %s%s");
  198       l = su_cs_len(tr) + su_cs_len(mft->n_name) + 3 +1;
  199       lp = n_lofi_alloc(l);
  200 
  201       snprintf(lp, l, tr, mft->n_name,
  202          (mft->n_flink != NULL ? "..." : n_empty));
  203       if(n_quadify(cp, UZ_MAX, lp, TRU1) <= FAL0)
  204          mft = NULL;
  205 
  206       n_lofi_free(lp);
  207    }
  208    NYD2_OU;
  209    return mft;
  210 }
  211 
  212 static void
  213 a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
  214       struct mx_name *np){
  215    enum{
  216       a_NONE,
  217       a_ONCE = 1u<<0,
  218       a_LIST_CLASSIFIED = 1u<<1,
  219       a_SEEN_TO = 1u<<2
  220    };
  221 
  222    struct mx_name *np_orig;
  223    u32 f;
  224    NYD2_IN;
  225    UNUSED(mp);
  226 
  227    if(np == hp->h_to)
  228       hp->h_to = NIL;
  229    if(np == hp->h_cc)
  230       hp->h_cc = NIL;
  231 
  232    /* We may find that in the end To: is empty but Cc: is not, in which case we
  233     * upgrade Cc: to To: and jump back and redo the thing slightly different */
  234    f = a_NONE;
  235    np_orig = np;
  236 jredo:
  237    while(np != NIL){
  238       enum gfield gf;
  239       struct mx_name *nnp, **xpp, *xp;
  240 
  241       nnp = np;
  242       np = np->n_flink;
  243 
  244       if(f & a_ONCE){
  245          gf = GTO;
  246          xpp = &hp->h_to;
  247       }else{
  248          gf = GCC;
  249          xpp = &hp->h_cc;
  250       }
  251 
  252       /* Try primary, then secondary */
  253       for(xp = hp->h_mailx_orig_to; xp != NIL; xp = xp->n_flink)
  254          if(!su_cs_cmp_case(xp->n_name, nnp->n_name)){
  255             if(!(f & a_LIST_CLASSIFIED)){
  256                f |= a_SEEN_TO;
  257                goto jclass_ok;
  258             }
  259             goto jlink;
  260          }
  261 
  262       if(f & a_ONCE){
  263          gf = GCC;
  264          xpp = &hp->h_cc;
  265       }
  266 
  267       for(xp = hp->h_mailx_orig_cc; xp != NIL; xp = xp->n_flink)
  268          if(!su_cs_cmp_case(xp->n_name, nnp->n_name))
  269             goto jlink;
  270 
  271       /* If this receiver came in only via R-T: or M-F-T:, place her/him/it in
  272        * To: due to lack of a better place.  But only if To: is not empty after
  273        * all formerly present receivers have been worked, to avoid that yet
  274        * unaddressed receivers propagate to To: whereas formerly addressed ones
  275        * end in Cc: */
  276       if(f & a_LIST_CLASSIFIED){
  277          if(f & a_SEEN_TO){
  278             gf = GCC;
  279             xpp = &hp->h_cc;
  280          }else{
  281             gf = GTO;
  282             xpp = &hp->h_to;
  283          }
  284       }
  285 
  286 jlink:
  287       if(!(f & a_LIST_CLASSIFIED))
  288          continue;
  289 
  290       /* Link it at the end to not loose original sort order */
  291       if((xp = *xpp) != NIL)
  292          while(xp->n_flink != NIL)
  293             xp = xp->n_flink;
  294 
  295       if((nnp->n_blink = xp) != NIL)
  296          xp->n_flink = nnp;
  297       else
  298          *xpp = nnp;
  299       nnp->n_flink = NIL;
  300       nnp->n_type = (nnp->n_type & ~GMASK) | gf;
  301    }
  302 
  303    /* Include formerly unaddressed receivers at the right place */
  304    if(!(f & a_LIST_CLASSIFIED)){
  305 jclass_ok:
  306       f |= a_LIST_CLASSIFIED;
  307       np = np_orig;
  308       goto jredo;
  309    }
  310 
  311    /* If afterwards only Cc: data remains, upgrade all of it to To: */
  312    if(hp->h_to == NIL){
  313       np = hp->h_cc;
  314       hp->h_cc = NIL;
  315       if(!(f & a_ONCE)){
  316          f |= a_ONCE;
  317          hp->h_to = NIL;
  318          goto jredo;
  319       }else
  320          for(hp->h_to = np; np != NIL; np = np->n_flink)
  321             np->n_type = (np->n_type & ~GMASK) | GTO;
  322    }
  323    NYD2_OU;
  324 }
  325 
  326 static boole
  327 a_crese_do_rt_swap_in(struct header *hp, struct mx_name *the_rt){
  328    struct mx_name *np;
  329    char const *rtsi;
  330    boole rv;
  331    NYD2_IN;
  332 
  333    rv = FAL0;
  334 
  335    if(the_rt != NIL && (rtsi = ok_vlook(reply_to_swap_in)) != NIL &&
  336          (np = hp->h_mailx_orig_sender) != NIL){
  337 
  338       rv = TRU1;
  339 
  340       if(*rtsi != '\0'){
  341          char *cp;
  342 
  343          for(cp = savestr(rtsi); (rtsi = su_cs_sep_c(&cp, ',', TRU1)) != NIL;)
  344             if(!su_cs_cmp_case(rtsi, "mlist")){
  345                if(mx_mlist_query(np->n_name, FAL0) == mx_MLIST_OTHER)
  346                   rv = FAL0;
  347             }else
  348                n_err(_("*reply-to-swap-in*: unknown value: %s\n"),
  349                   n_shexp_quote_cp(rtsi, FAL0));
  350       }
  351    }
  352 
  353    NYD2_OU;
  354    return rv;
  355 }
  356 
  357 static void
  358 a_crese_rt_swap_in(struct header *hp, struct mx_name *the_rt){
  359    NYD2_IN;
  360 
  361    if(a_crese_do_rt_swap_in(hp, the_rt)){
  362       boole any;
  363       struct mx_name *np, **xnpp, *xnp;
  364 
  365       np = hp->h_mailx_orig_sender;
  366 
  367       for(xnpp = &hp->h_from, any = FAL0;;){
  368          for(xnp = *xnpp; xnp != NIL; xnp = xnp->n_flink)
  369             if(mx_name_is_same_address(xnp, np)){
  370                xnp->n_fullname = the_rt->n_fullname;
  371                xnp->n_name = the_rt->n_name;
  372                any = TRU1;
  373             }
  374          if(xnpp == &hp->h_from)
  375             xnpp = &hp->h_sender;
  376          else if(xnpp == &hp->h_sender)
  377             xnpp = &hp->h_to;
  378          else if(xnpp == &hp->h_to)
  379             xnpp = &hp->h_cc;
  380          else if(xnpp == &hp->h_cc)
  381             xnpp = &hp->h_bcc;
  382          else if(xnpp == &hp->h_bcc)
  383             xnpp = &hp->h_reply_to;
  384          else if(xnpp == &hp->h_reply_to)
  385             xnpp = &hp->h_mft;
  386          else
  387             break;
  388       }
  389 
  390       if(any){
  391          np = ndup(np, GCC | GSKIN);
  392          hp->h_cc = cat(hp->h_cc, np);
  393       }
  394    }
  395 
  396    NYD2_OU;
  397 }
  398 
  399 static void
  400 a_crese_make_ref_and_cs(struct message *mp, struct header *head) /* TODO ASAP*/
  401 {
  402    char const *ccp;
  403    char *oldref, *oldmsgid, *newref;
  404    uz oldreflen = 0, oldmsgidlen = 0, reflen;
  405    unsigned i;
  406    struct mx_name *n;
  407    NYD2_IN;
  408 
  409    oldref = hfield1("references", mp);
  410    oldmsgid = hfield1("message-id", mp);
  411    if (oldmsgid == NULL || *oldmsgid == '\0') {
  412       head->h_ref = NULL;
  413       goto jleave;
  414    }
  415 
  416    reflen = 1;
  417    if (oldref) {
  418       oldreflen = su_cs_len(oldref);
  419       reflen += oldreflen + 2;
  420    }
  421    if (oldmsgid) {
  422       oldmsgidlen = su_cs_len(oldmsgid);
  423       reflen += oldmsgidlen;
  424    }
  425 
  426    newref = n_alloc(reflen);
  427    if (oldref != NULL) {
  428       su_mem_copy(newref, oldref, oldreflen +1);
  429       if (oldmsgid != NULL) {
  430          newref[oldreflen++] = ',';
  431          newref[oldreflen++] = ' ';
  432          su_mem_copy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
  433       }
  434    } else if (oldmsgid)
  435       su_mem_copy(newref, oldmsgid, oldmsgidlen +1);
  436    n = extract(newref, GREF);
  437    n_free(newref);
  438 
  439    /* Limit number of references TODO better on parser side */
  440    while (n->n_flink != NULL)
  441       n = n->n_flink;
  442    for (i = 1; i <= REFERENCES_MAX; ++i) {
  443       if (n->n_blink != NULL)
  444          n = n->n_blink;
  445       else
  446          break;
  447    }
  448    n->n_blink = NIL;
  449    head->h_ref = n;
  450 
  451    if(ok_blook(reply_in_same_charset) &&
  452          (ccp = hfield1("content-type", mp)) != NIL &&
  453          (ccp = mime_param_get("charset", ccp)) != NIL)
  454       head->h_charset = mx_charsetalias_expand(ccp, FAL0);
  455 
  456 jleave:
  457    NYD2_OU;
  458 }
  459 
  460 static int
  461 a_crese_list_reply(int *msgvec, enum header_flags hf){
  462    struct header head;
  463    struct message *mp;
  464    char const *cp, *cp2;
  465    struct mx_name *rt, *the_rt, *mft, *np;
  466    enum gfield gf;
  467    NYD2_IN;
  468 
  469    n_autorec_relax_create();
  470 
  471    n_pstate_err_no = su_ERR_NONE;
  472 
  473    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  474 
  475 jwork_msg:
  476    mp = &message[*msgvec - 1];
  477    touch(mp);
  478    setdot(mp);
  479 
  480    su_mem_set(&head, 0, sizeof head);
  481    head.h_flags = hf;
  482    head.h_subject = a_crese_reedit(hfield1("subject", mp));
  483    head.h_mailx_command = (hf & HF_LIST_REPLY) ? "Lreply" : "reply";
  484    head.h_mailx_orig_sender = mx_header_sender_of(mp, GIDENT | gf);
  485    head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  486    head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  487    head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  488    head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  489 
  490    /* First of all check for Reply-To: then Mail-Followup-To:, because these,
  491     * if honoured, take precedence over anything else.  We will join the
  492     * resulting list together if so desired.
  493     * So if we shall honour R-T: or M-F-T:, then these are our receivers! */
  494    rt = a_crese_reply_to(mp, &the_rt);
  495    mft = a_crese_mail_followup_to(mp);
  496 
  497    if(rt != NIL || mft != NIL){
  498       np = cat(rt, mft);
  499       if(mft != NIL)
  500          head.h_mft = n_namelist_dup(np, GTO | gf); /* xxx GTO: no "clone"! */
  501 
  502       /* Optionally do not propagate a receiver that originally was in
  503        * secondary Cc: to the primary To: list */
  504       if(ok_blook(recipients_in_cc)){
  505          a_crese_polite_rt_mft_move(mp, &head, np);
  506 
  507          head.h_mailx_raw_cc = n_namelist_dup(head.h_cc, GCC | gf);
  508          head.h_cc = mx_alternates_remove(head.h_cc, FAL0);
  509       }else
  510          head.h_to = np;
  511 
  512       head.h_mailx_raw_to = n_namelist_dup(head.h_to, GTO | gf);
  513       head.h_to = mx_alternates_remove(head.h_to, FAL0);
  514 #ifdef mx_HAVE_DEVEL
  515       for(np = head.h_to; np != NULL; np = np->n_flink)
  516          ASSERT((np->n_type & GMASK) == GTO);
  517       for(np = head.h_cc; np != NULL; np = np->n_flink)
  518          ASSERT((np->n_type & GMASK) == GCC);
  519 #endif
  520       goto jrecipients_done;
  521    }
  522 
  523    /* Otherwise do the normal From: / To: / Cc: dance */
  524 
  525    if(head.h_mailx_orig_sender != NIL)
  526       cp2 = head.h_mailx_orig_sender->n_fullname;
  527    else
  528       cp2 = n_header_senderfield_of(mp);
  529 
  530    /* Cc: */
  531    np = NULL;
  532    if(ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
  533       np = lextract(cp, GCC | gf);
  534    if((cp = hfield1("cc", mp)) != NULL){
  535       struct mx_name *x;
  536 
  537       if((x = lextract(cp, GCC | gf)) != NULL)
  538          np = cat(np, x);
  539    }
  540    if(np != NULL){
  541       head.h_mailx_raw_cc = n_namelist_dup(np, GCC | gf);
  542       head.h_cc = mx_alternates_remove(np, FAL0);
  543    }
  544 
  545    /* To: */
  546    np = NULL;
  547    if(cp2 != NULL)
  548       np = lextract(cp2, GTO | gf);
  549    if(!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL){
  550       struct mx_name *x;
  551 
  552       if((x = lextract(cp, GTO | gf)) != NULL)
  553          np = cat(np, x);
  554    }
  555    /* Delete my name from reply list, and with it, all my alternate names */
  556    if(np != NULL){
  557       head.h_mailx_raw_to = n_namelist_dup(np, GTO | gf);
  558       np = mx_alternates_remove(np, FAL0);
  559       /* The user may have send this to himself, don't ignore that */
  560       if(count(np) == 0){
  561          np = lextract(cp2, GTO | gf);
  562          head.h_mailx_raw_to = n_namelist_dup(np, GTO | gf);
  563       }
  564    }
  565    head.h_to = np;
  566 
  567 jrecipients_done:
  568    a_crese_rt_swap_in(&head, the_rt);
  569 
  570    /* For list replies automatically recognize the list address given in the
  571     * RFC 2369 List-Post: header, so that we will not throw away a possible
  572     * corresponding receiver: temporarily "`mlist' the List-Post: address" */
  573    if(hf & HF_LIST_REPLY){
  574       struct mx_name *lpnp;
  575 
  576       if((lpnp = mx_header_list_post_of(mp)) != NIL){
  577          if(lpnp == R(struct mx_name*,-1)){
  578             /* Default is TRU1 because if there are still other addresses that
  579              * seems to be ok, otherwise we fail anyway */
  580             if(mx_tty_yesorno(_("List-Post: disallows posting; "
  581                   "reply nonetheless"), TRU1))
  582                lpnp = NIL;
  583             else{
  584                n_pstate_err_no = su_ERR_DESTADDRREQ;
  585                msgvec = NIL;
  586                goto jleave;
  587             }
  588          }
  589 
  590          /* A special case has been seen on e.g. ietf-announce@ietf.org:
  591           * these usually post to multiple groups, with ietf-announce@
  592           * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
  593           * -announce@ is only used for announcements, say).
  594           * So our desire is to honour this request and actively overwrite
  595           * List-Post: for our purpose; but only if its a single address.
  596           * However, to avoid ambiguities with users that place themselves in
  597           * Reply-To: and mailing lists which don't overwrite this (or only
  598           * extend this, shall such exist), only do so if reply_to exists of
  599           * a single address which points to the same domain as List-Post: */
  600          if(rt != NIL && rt->n_flink == NIL &&
  601                (lpnp == NIL || mx_name_is_same_domain(lpnp, rt)))
  602             cp = rt->n_name; /* rt is EACM_STRICT tested */
  603          else
  604             cp = (lpnp == NIL) ? NIL : lpnp->n_name;
  605 
  606          /* XXX mx_mlist_query_mp()?? */
  607          if(cp != NIL){
  608             s8 mlt;
  609 
  610             if((mlt = mx_mlist_query(cp, FAL0)) == mx_MLIST_OTHER)
  611                head.h_list_post = cp;
  612          }
  613       }
  614    }
  615 
  616    /* In case of list replies we actively sort out any non-list recipient */
  617    if(hf & HF_LIST_REPLY){
  618       struct mx_name **nhpp, *nhp, *tail;
  619 
  620       cp = head.h_list_post;
  621 
  622       nhp = *(nhpp = &head.h_to);
  623       head.h_to = NULL;
  624 j_lt_redo:
  625       for(tail = NULL; nhp != NULL;){
  626          s8 mlt;
  627 
  628          np = nhp;
  629          nhp = nhp->n_flink;
  630 
  631          /* XXX mx_mlist_query_mp()?? */
  632          if((cp != NIL && !su_cs_cmp_case(cp, np->n_name)) ||
  633                ((mlt = mx_mlist_query(np->n_name, FAL0)) != mx_MLIST_OTHER &&
  634                 mlt != mx_MLIST_POSSIBLY)){
  635             if((np->n_blink = tail) != NIL)
  636                tail->n_flink = np;
  637             else
  638                *nhpp = np;
  639             np->n_flink = NIL;
  640             tail = np;
  641          }
  642       }
  643       if(nhpp == &head.h_to){
  644          nhp = *(nhpp = &head.h_cc);
  645          head.h_cc = NULL;
  646          goto j_lt_redo;
  647       }
  648 
  649       /* For `Lreply' only, fail immediately with DESTADDRREQ if there are no
  650        * receivers at all! */
  651       if(head.h_to == NULL && head.h_cc == NULL){
  652          n_err(_("No recipients specified for `Lreply'\n"));
  653          if(msgvec[1] == 0){
  654             n_pstate_err_no = su_ERR_DESTADDRREQ;
  655             msgvec = NULL;
  656             goto jleave;
  657          }
  658          goto jskip_to_next;
  659       }
  660    }
  661 
  662    /* Move Cc: to To: as appropriate! */
  663    if(head.h_to == NULL && (np = head.h_cc) != NULL){
  664       head.h_cc = NULL;
  665       for(head.h_to = np; np != NULL; np = np->n_flink)
  666          np->n_type = (np->n_type & ~GMASK) | GTO;
  667    }
  668 
  669    a_crese_make_ref_and_cs(mp, &head);
  670 
  671    if(n_mail1((n_MAILSEND_HEADERS_PRINT |
  672             (hf & HF_RECIPIENT_RECORD ? n_MAILSEND_RECORD_RECIPIENT : 0)),
  673          &head, mp, NULL) != OKAY){
  674       msgvec = NIL;
  675       goto jleave;
  676    }
  677 
  678    if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
  679       mp->m_flag |= MANSWER | MANSWERED;
  680 
  681 jskip_to_next:
  682 
  683    if(*++msgvec != 0){
  684       /* TODO message (error) ring.., less sleep */
  685       if(n_psonce & n_PSO_INTERACTIVE){
  686          fprintf(n_stdout,
  687             _("Waiting a second before proceeding to the next message..\n"));
  688          fflush(n_stdout);
  689          n_msleep(1000, FAL0);
  690       }
  691       n_autorec_relax_unroll();
  692       goto jwork_msg;
  693    }
  694 
  695 jleave:
  696    n_autorec_relax_gut();
  697 
  698    NYD2_OU;
  699    return (msgvec == NIL ? n_EXIT_ERR : n_EXIT_OK);
  700 }
  701 
  702 static int
  703 (*a_crese_reply_or_Reply(char c))(int *, boole){
  704    int (*rv)(int*, boole);
  705    NYD2_IN;
  706 
  707    rv = (ok_blook(flipr) ^ (c == 'R')) ? &a_crese_Reply : &a_crese_reply;
  708    NYD2_OU;
  709    return rv;
  710 }
  711 
  712 static int
  713 a_crese_reply(int *msgvec, boole recipient_record){
  714    int rv;
  715    NYD2_IN;
  716 
  717    rv = a_crese_list_reply(msgvec,
  718          (recipient_record ? HF_RECIPIENT_RECORD : HF_NONE));
  719    NYD2_OU;
  720    return rv;
  721 }
  722 
  723 static int
  724 a_crese_Reply(int *msgvec, boole recipient_record){
  725    struct header head;
  726    struct message *mp;
  727    int *ap;
  728    enum gfield gf;
  729    NYD2_IN;
  730 
  731    n_pstate_err_no = su_ERR_NONE;
  732 
  733    su_mem_set(&head, 0, sizeof head);
  734    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  735 
  736    mp = n_msgmark1;
  737    ASSERT(mp != NIL);
  738    head.h_subject = hfield1("subject", mp);
  739    head.h_subject = a_crese_reedit(head.h_subject);
  740    a_crese_make_ref_and_cs(mp, &head);
  741    head.h_mailx_command = "Reply";
  742    head.h_mailx_orig_sender = mx_header_sender_of(mp, GIDENT | gf);
  743    head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  744    head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  745    head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  746    head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  747 
  748    for(ap = msgvec; *ap != 0; ++ap){
  749       struct mx_name *np, *the_rt;
  750 
  751       mp = &message[*ap - 1];
  752       touch(mp);
  753       setdot(mp);
  754 
  755       if((np = a_crese_reply_to(mp, &the_rt)) == NIL)
  756          np = lextract(n_header_senderfield_of(mp), GTO | gf);
  757 
  758       if(a_crese_do_rt_swap_in(&head, the_rt)){
  759          struct mx_name *np_save;
  760 
  761          for(np_save = np; np != NIL; np = np->n_flink)
  762             if(mx_name_is_same_address(np, head.h_mailx_orig_sender)){
  763                np->n_fullname = the_rt->n_fullname;
  764                np->n_name = the_rt->n_name;
  765             }
  766          np = np_save;
  767       }
  768 
  769       head.h_to = cat(head.h_to, np);
  770    }
  771 
  772    mp = n_msgmark1;
  773 
  774    head.h_mailx_raw_to = n_namelist_dup(head.h_to, GTO | gf);
  775    head.h_to = mx_alternates_remove(head.h_to, FAL0);
  776 
  777    if(n_mail1(((recipient_record ? n_MAILSEND_RECORD_RECIPIENT : 0) |
  778             n_MAILSEND_HEADERS_PRINT), &head, mp, NULL) != OKAY){
  779       msgvec = NIL;
  780       goto jleave;
  781    }
  782 
  783    if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
  784       mp->m_flag |= MANSWER | MANSWERED;
  785 
  786 jleave:
  787    NYD2_OU;
  788    return (msgvec == NIL ? n_EXIT_ERR : n_EXIT_OK);
  789 }
  790 
  791 static int
  792 a_crese_fwd(void *vp, boole recipient_record){
  793    struct header head;
  794    struct message *mp;
  795    struct mx_name *recp;
  796    enum gfield gf;
  797    boole forward_as_attachment;
  798    int *msgvec, rv;
  799    struct mx_cmd_arg *cap;
  800    struct mx_cmd_arg_ctx *cacp;
  801    NYD2_IN;
  802 
  803    n_pstate_err_no = su_ERR_NONE;
  804 
  805    cacp = vp;
  806    cap = cacp->cac_arg;
  807    msgvec = cap->ca_arg.ca_msglist;
  808    cap = cap->ca_next;
  809    rv = n_EXIT_ERR;
  810 
  811    if(cap->ca_arg.ca_str.s[0] == '\0'){
  812       if(!(n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) ||
  813             (n_poption & n_PO_D_V)){
  814          n_err(_("No recipient specified.\n"));
  815          mx_cmd_print_synopsis(mx_cmd_firstfit(cacp->cac_desc->cad_name), NIL);
  816       }
  817       su_err_set_no(n_pstate_err_no = su_ERR_DESTADDRREQ);
  818       goto j_leave;
  819    }
  820 
  821    forward_as_attachment = ok_blook(forward_as_attachment);
  822    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  823    recp = lextract(cap->ca_arg.ca_str.s, (GTO | GNOT_A_LIST | gf));
  824 
  825    n_autorec_relax_create();
  826 
  827 jwork_msg:
  828    mp = &message[*msgvec - 1];
  829    touch(mp);
  830    setdot(mp);
  831 
  832    su_mem_set(&head, 0, sizeof head);
  833    head.h_to = ndup(recp, (GTO | gf));
  834    head.h_subject = hfield1("subject", mp);
  835    head.h_subject = a_crese__fwdedit(head.h_subject);
  836    head.h_mailx_command = "forward";
  837    head.h_mailx_raw_to = n_namelist_dup(recp, GTO | gf);
  838    head.h_mailx_orig_sender = mx_header_sender_of(mp, GIDENT | gf);
  839    head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  840    head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  841    head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  842    head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  843 
  844    if(forward_as_attachment){
  845       head.h_attach = n_autorec_calloc(1, sizeof *head.h_attach);
  846       head.h_attach->a_msgno = *msgvec;
  847       head.h_attach->a_content_description =
  848          ok_vlook(content_description_forwarded_message);
  849 
  850       if(head.h_mailx_orig_sender != NIL && ok_blook(forward_add_cc)){
  851          gf = GCC | GSKIN;
  852          if(ok_blook(fullnames))
  853             gf |= GFULL;
  854          head.h_cc = ndup(head.h_mailx_orig_sender, gf);
  855       }
  856    }
  857 
  858    if(n_mail1((n_MAILSEND_IS_FWD |
  859             (recipient_record ? n_MAILSEND_RECORD_RECIPIENT : 0) |
  860             n_MAILSEND_HEADERS_PRINT), &head,
  861          (forward_as_attachment ? NIL : mp), NIL) != OKAY)
  862       goto jleave;
  863 
  864    if(*++msgvec != 0){
  865       /* TODO message (error) ring.., less sleep */
  866       if(n_psonce & n_PSO_INTERACTIVE){
  867          fprintf(n_stdout,
  868             _("Waiting a second before proceeding to the next message..\n"));
  869          fflush(n_stdout);
  870          n_msleep(1000, FAL0);
  871       }
  872       n_autorec_relax_unroll();
  873       goto jwork_msg;
  874    }
  875 
  876    rv = n_EXIT_OK;
  877 jleave:
  878    n_autorec_relax_gut();
  879 j_leave:
  880    NYD2_OU;
  881    return rv;
  882 }
  883 
  884 static char *
  885 a_crese__fwdedit(char *subj){
  886    struct str in, out;
  887    char *newsubj;
  888    NYD2_IN;
  889 
  890    newsubj = NULL;
  891 
  892    if(subj == NULL || *subj == '\0')
  893       goto jleave;
  894 
  895    in.s = subj;
  896    in.l = su_cs_len(subj);
  897    mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
  898 
  899    newsubj = n_autorec_alloc(out.l + 6);
  900    if(!su_cs_cmp_case_n(out.s, "Fwd: ", sizeof("Fwd: ") -1)) /* TODO EXTEND */
  901       su_mem_copy(newsubj, out.s, out.l +1);
  902    else{
  903       su_mem_copy(newsubj, "Fwd: ", 5); /* TODO ..a la subject_re_trim()! */
  904       su_mem_copy(&newsubj[5], out.s, out.l +1);
  905    }
  906 
  907    n_free(out.s);
  908 jleave:
  909    NYD2_OU;
  910    return newsubj;
  911 }
  912 
  913 static int
  914 a_crese_resend1(void *vp, boole add_resent){
  915    struct mx_url url, *urlp = &url;
  916    struct header head;
  917    struct mx_name *myto, *myrawto;
  918    boole mta_isexe;
  919    enum gfield gf;
  920    int *msgvec, rv, *ip;
  921    struct mx_cmd_arg *cap;
  922    struct mx_cmd_arg_ctx *cacp;
  923    NYD2_IN;
  924 
  925    cacp = vp;
  926    cap = cacp->cac_arg;
  927    msgvec = cap->ca_arg.ca_msglist;
  928    cap = cap->ca_next;
  929    rv = 1;
  930    n_pstate_err_no = su_ERR_DESTADDRREQ;
  931 
  932    if(cap->ca_arg.ca_str.s[0] == '\0'){
  933       if(!(n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) || (n_poption & n_PO_D_V))
  934 jedar:
  935          n_err(_("No recipient specified.\n"));
  936       goto jleave;
  937    }
  938 
  939    if(!(mta_isexe = mx_sendout_mta_url(urlp))){
  940       n_pstate_err_no = su_ERR_INVAL;
  941       goto jleave;
  942    }
  943    mta_isexe = (mta_isexe != TRU1);
  944 
  945    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  946 
  947    myrawto = nalloc(cap->ca_arg.ca_str.s, GTO | gf | GNOT_A_LIST | GNULL_OK);
  948    if(myrawto == NIL)
  949       goto jedar;
  950 
  951    su_mem_set(&head, 0, sizeof head);
  952    head.h_to = n_namelist_dup(myrawto, myrawto->n_type);
  953    /* C99 */{
  954       s8 snderr;
  955 
  956       snderr = 0;
  957       myto = n_namelist_vaporise_head(&head, FAL0, !ok_blook(posix),
  958             (EACM_NORMAL | EACM_DOMAINCHECK |
  959                (mta_isexe ? EACM_NONE : EACM_NONAME | EACM_NONAME_OR_FAIL)),
  960             &snderr);
  961 
  962       if(snderr < 0){
  963          n_err(_("Some addressees were classified as \"hard error\"\n"));
  964          n_pstate_err_no = su_ERR_PERM;
  965          goto jleave;
  966       }
  967       if(myto == NIL)
  968          goto jedar;
  969    }
  970 
  971    n_autorec_relax_create();
  972    for(ip = msgvec; *ip != 0; ++ip){
  973       struct message *mp;
  974 
  975       mp = &message[*ip - 1];
  976       touch(mp);
  977       setdot(mp);
  978 
  979       su_mem_set(&head, 0, sizeof head);
  980       head.h_to = myto;
  981       head.h_mailx_command = "resend";
  982       head.h_mailx_raw_to = myrawto;
  983       head.h_mailx_orig_sender = mx_header_sender_of(mp, GIDENT | gf);
  984       head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  985       head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  986       head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  987       head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  988 
  989       if(n_resend_msg(mp, urlp, &head, add_resent) != OKAY){
  990          /* n_autorec_relax_gut(); XXX but is handled automatically? */
  991          goto jleave;
  992       }
  993       n_autorec_relax_unroll();
  994    }
  995    n_autorec_relax_gut();
  996 
  997    n_pstate_err_no = su_ERR_NONE;
  998    rv = 0;
  999 jleave:
 1000    NYD2_OU;
 1001    return rv;
 1002 }
 1003 
 1004 FL int
 1005 c_reply(void *vp){
 1006    int rv;
 1007    NYD_IN;
 1008 
 1009    rv = (*a_crese_reply_or_Reply('r'))(vp, FAL0);
 1010    NYD_OU;
 1011    return rv;
 1012 }
 1013 
 1014 FL int
 1015 c_replyall(void *vp){ /* v15-compat */
 1016    int rv;
 1017    NYD_IN;
 1018 
 1019    rv = a_crese_reply(vp, FAL0);
 1020    NYD_OU;
 1021    return rv;
 1022 }
 1023 
 1024 FL int
 1025 c_replysender(void *vp){ /* v15-compat */
 1026    int rv;
 1027    NYD_IN;
 1028 
 1029    rv = a_crese_Reply(vp, FAL0);
 1030    NYD_OU;
 1031    return rv;
 1032 }
 1033 
 1034 FL int
 1035 c_Reply(void *vp){
 1036    int rv;
 1037    NYD_IN;
 1038 
 1039    rv = (*a_crese_reply_or_Reply('R'))(vp, FAL0);
 1040    NYD_OU;
 1041    return rv;
 1042 }
 1043 
 1044 FL int
 1045 c_Lreply(void *vp){
 1046    int rv;
 1047    NYD_IN;
 1048 
 1049    rv = a_crese_list_reply(vp, HF_LIST_REPLY);
 1050    NYD_OU;
 1051    return rv;
 1052 }
 1053 
 1054 FL int
 1055 c_followup(void *vp){
 1056    int rv;
 1057    NYD_IN;
 1058 
 1059    rv = (*a_crese_reply_or_Reply('r'))(vp, TRU1);
 1060    NYD_OU;
 1061    return rv;
 1062 }
 1063 
 1064 FL int
 1065 c_followupall(void *vp){ /* v15-compat */
 1066    int rv;
 1067    NYD_IN;
 1068 
 1069    rv = a_crese_reply(vp, TRU1);
 1070    NYD_OU;
 1071    return rv;
 1072 }
 1073 
 1074 FL int
 1075 c_followupsender(void *vp){ /* v15-compat */
 1076    int rv;
 1077    NYD_IN;
 1078 
 1079    rv = a_crese_Reply(vp, TRU1);
 1080    NYD_OU;
 1081    return rv;
 1082 }
 1083 
 1084 FL int
 1085 c_Followup(void *vp){
 1086    int rv;
 1087    NYD_IN;
 1088 
 1089    rv = (*a_crese_reply_or_Reply('R'))(vp, TRU1);
 1090    NYD_OU;
 1091    return rv;
 1092 }
 1093 
 1094 FL int
 1095 c_Lfollowup(void *vp){
 1096    int rv;
 1097    NYD_IN;
 1098 
 1099    rv = a_crese_list_reply(vp, HF_LIST_REPLY | HF_RECIPIENT_RECORD);
 1100    NYD_OU;
 1101    return rv;
 1102 }
 1103 
 1104 FL int
 1105 c_forward(void *vp){
 1106    int rv;
 1107    NYD_IN;
 1108 
 1109    rv = a_crese_fwd(vp, FAL0);
 1110    NYD_OU;
 1111    return rv;
 1112 }
 1113 
 1114 FL int
 1115 c_Forward(void *vp){
 1116    int rv;
 1117    NYD_IN;
 1118 
 1119    rv = a_crese_fwd(vp, TRU1);
 1120    NYD_OU;
 1121    return rv;
 1122 }
 1123 
 1124 FL int
 1125 c_resend(void *vp){
 1126    int rv;
 1127    NYD_IN;
 1128 
 1129    rv = a_crese_resend1(vp, TRU1);
 1130    NYD_OU;
 1131    return rv;
 1132 }
 1133 
 1134 FL int
 1135 c_Resend(void *vp){
 1136    int rv;
 1137    NYD_IN;
 1138 
 1139    rv = a_crese_resend1(vp, FAL0);
 1140    NYD_OU;
 1141    return rv;
 1142 }
 1143 
 1144 #include "su/code-ou.h"
 1145 /* s-it-mode */