"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/cmd-resend.c" (16 Feb 2018, 26535 Bytes) of package /linux/misc/s-nail-14.9.7.tar.xz:


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

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ 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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    6  */
    7 /*
    8  * Copyright (c) 1980, 1993
    9  *      The Regents of the University of California.  All rights reserved.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. Neither the name of the University nor the names of its contributors
   20  *    may be used to endorse or promote products derived from this software
   21  *    without specific prior written permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33  * SUCH DAMAGE.
   34  */
   35 #undef n_FILE
   36 #define n_FILE cmd_resend
   37 
   38 #ifndef HAVE_AMALGAMATION
   39 # include "nail.h"
   40 #endif
   41 
   42 /* Modify subject we reply to to begin with Re: if it does not already */
   43 static char *a_crese_reedit(char const *subj);
   44 
   45 /* Fetch these headers, as appropriate */
   46 static struct name *a_crese_reply_to(struct message *mp);
   47 static struct name *a_crese_mail_followup_to(struct message *mp);
   48 
   49 /* We honoured Reply-To: and/or Mail-Followup-To:, but *recipients-in-cc* is
   50  * set so try to keep "secondary" addressees in Cc:, if possible, */
   51 static void a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
   52                      struct name *np);
   53 
   54 /* References and charset, as appropriate */
   55 static void a_crese_make_ref_and_cs(struct message *mp, struct header *head);
   56 
   57 /* `reply' and `Lreply' workhorse */
   58 static int a_crese_list_reply(int *msgvec, enum header_flags hf);
   59 
   60 /* Get PTF to implementation of command c (i.e., take care for *flipr*) */
   61 static int (*a_crese_reply_or_Reply(char c))(int *, bool_t);
   62 
   63 /* Reply to a single message.  Extract each name from the message header and
   64  * send them off to mail1() */
   65 static int a_crese_reply(int *msgvec, bool_t recipient_record);
   66 
   67 /* Reply to a series of messages by simply mailing to the senders and not
   68  * messing around with the To: and Cc: lists as in normal reply */
   69 static int a_crese_Reply(int *msgvec, bool_t recipient_record);
   70 
   71 /* Forward a message to a new recipient, in the sense of RFC 2822 */
   72 static int a_crese_fwd(char *str, int recipient_record);
   73 
   74 /* Modify the subject we are replying to to begin with Fwd: */
   75 static char *a_crese__fwdedit(char *subj);
   76 
   77 /* Do the real work of resending */
   78 static int a_crese_resend1(void *v, bool_t add_resent);
   79 
   80 static char *
   81 a_crese_reedit(char const *subj){
   82    char *newsubj;
   83    NYD2_ENTER;
   84 
   85    newsubj = NULL;
   86 
   87    if(subj != NULL && *subj != '\0'){
   88       struct str in, out;
   89       size_t i;
   90       char const *cp;
   91 
   92       in.l = strlen(in.s = n_UNCONST(subj));
   93       mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
   94 
   95       i = strlen(cp = subject_re_trim(out.s)) +1;
   96       /* RFC mandates english "Re: " */
   97       newsubj = n_autorec_alloc(sizeof("Re: ") -1 + i);
   98       memcpy(newsubj, "Re: ", sizeof("Re: ") -1);
   99       memcpy(&newsubj[sizeof("Re: ") -1], cp, i);
  100 
  101       free(out.s);
  102    }
  103    NYD2_LEAVE;
  104    return newsubj;
  105 }
  106 
  107 static struct name *
  108 a_crese_reply_to(struct message *mp){
  109    char const *cp, *cp2;
  110    struct name *rt, *np;
  111    enum gfield gf;
  112    NYD2_ENTER;
  113 
  114    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  115    rt = NULL;
  116 
  117    if((cp = ok_vlook(reply_to_honour)) != NULL &&
  118          (cp2 = hfield1("reply-to", mp)) != NULL &&
  119          (rt = checkaddrs(lextract(cp2, GTO | gf), EACM_STRICT, NULL)) != NULL){
  120       char *sp;
  121       size_t l;
  122       char const *tr;
  123 
  124       if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
  125          fprintf(n_stdout, _("Reply-To: header contains:"));
  126          for(np = rt; np != NULL; np = np->n_flink)
  127             fprintf(n_stdout, " %s", np->n_name);
  128          putc('\n', n_stdout);
  129       }
  130 
  131       tr = _("Reply-To %s%s");
  132       l = strlen(tr) + strlen(rt->n_name) + 3 +1;
  133       sp = n_lofi_alloc(l);
  134 
  135       snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..." : n_empty));
  136       if(quadify(cp, UIZ_MAX, sp, TRU1) <= FAL0)
  137          rt = NULL;
  138 
  139       n_lofi_free(sp);
  140    }
  141    NYD2_LEAVE;
  142    return rt;
  143 }
  144 
  145 static struct name *
  146 a_crese_mail_followup_to(struct message *mp){
  147    char const *cp, *cp2;
  148    struct name *mft, *np;
  149    enum gfield gf;
  150    NYD2_ENTER;
  151 
  152    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  153    mft = NULL;
  154 
  155    if((cp = ok_vlook(followup_to_honour)) != NULL &&
  156          (cp2 = hfield1("mail-followup-to", mp)) != NULL &&
  157          (mft = checkaddrs(lextract(cp2, GTO | gf), EACM_STRICT,NULL)) != NULL){
  158       char *sp;
  159       size_t l;
  160       char const *tr;
  161 
  162       if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
  163          fprintf(n_stdout, _("Mail-Followup-To: header contains:"));
  164          for(np = mft; np != NULL; np = np->n_flink)
  165             fprintf(n_stdout, " %s", np->n_name);
  166          putc('\n', n_stdout);
  167       }
  168 
  169       tr = _("Followup-To %s%s");
  170       l = strlen(tr) + strlen(mft->n_name) + 3 +1;
  171       sp = n_lofi_alloc(l);
  172 
  173       snprintf(sp, l, tr, mft->n_name,
  174          (mft->n_flink != NULL ? "..." : n_empty));
  175       if(quadify(cp, UIZ_MAX, sp, TRU1) <= FAL0)
  176          mft = NULL;
  177 
  178       n_lofi_free(sp);
  179    }
  180    NYD2_LEAVE;
  181    return mft;
  182 }
  183 
  184 static void
  185 a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
  186       struct name *np){
  187    bool_t once;
  188    NYD2_ENTER;
  189    n_UNUSED(mp);
  190 
  191    if(np == hp->h_to)
  192       hp->h_to = NULL;
  193    if(np == hp->h_cc)
  194       hp->h_cc = NULL;
  195 
  196    /* We may find that in the end To: is empty but Cc: is not, in which case we
  197     * upgrade Cc: to To:, and jump back and redo the thing slightly different */
  198    once = FAL0;
  199 jredo:
  200    while(np != NULL){
  201       enum gfield gf;
  202       struct name *nnp, **xpp, *xp;
  203 
  204       nnp = np;
  205       np = np->n_flink;
  206 
  207       if(once){
  208          gf = GTO;
  209          xpp = &hp->h_to;
  210       }else{
  211          gf = GCC;
  212          xpp = &hp->h_cc;
  213       }
  214 
  215       /* Try primary, then secondary */
  216       for(xp = hp->h_mailx_orig_to; xp != NULL; xp = xp->n_flink)
  217          if(!asccasecmp(xp->n_name, nnp->n_name))
  218             goto jlink;
  219 
  220       if(once){
  221          gf = GCC;
  222          xpp = &hp->h_cc;
  223       }
  224 
  225       for(xp = hp->h_mailx_orig_cc; xp != NULL; xp = xp->n_flink)
  226          if(!asccasecmp(xp->n_name, nnp->n_name))
  227             goto jlink;
  228 
  229       /* If this receiver came in only via R-T: or M-F-T:, place her/him/it in
  230        * To: due to lack of a better place */
  231       gf = GTO;
  232       xpp = &hp->h_to;
  233 jlink:
  234       /* Link it at the end to not loose original sort order */
  235       if((xp = *xpp) != NULL)
  236          while(xp->n_flink != NULL)
  237             xp = xp->n_flink;
  238 
  239       if((nnp->n_blink = xp) != NULL)
  240          xp->n_flink = nnp;
  241       else
  242          *xpp = nnp;
  243       nnp->n_flink = NULL;
  244       nnp->n_type = (nnp->n_type & ~GMASK) | gf;
  245    }
  246 
  247    /* If afterwards only Cc: data remains, upgrade all of it to To: */
  248    if(hp->h_to == NULL){
  249       np = hp->h_cc;
  250       hp->h_cc = NULL;
  251       if(!once){
  252          hp->h_to = NULL;
  253          once = TRU1;
  254          goto jredo;
  255       }else
  256          for(hp->h_to = np; np != NULL; np = np->n_flink)
  257             np->n_type = (np->n_type & ~GMASK) | GTO;
  258    }
  259    NYD2_LEAVE;
  260 }
  261 
  262 static void
  263 a_crese_make_ref_and_cs(struct message *mp, struct header *head) /* TODO ASAP */
  264 {
  265    char *oldref, *oldmsgid, *newref, *cp;
  266    size_t oldreflen = 0, oldmsgidlen = 0, reflen;
  267    unsigned i;
  268    struct name *n;
  269    NYD2_ENTER;
  270 
  271    oldref = hfield1("references", mp);
  272    oldmsgid = hfield1("message-id", mp);
  273    if (oldmsgid == NULL || *oldmsgid == '\0') {
  274       head->h_ref = NULL;
  275       goto jleave;
  276    }
  277 
  278    reflen = 1;
  279    if (oldref) {
  280       oldreflen = strlen(oldref);
  281       reflen += oldreflen + 2;
  282    }
  283    if (oldmsgid) {
  284       oldmsgidlen = strlen(oldmsgid);
  285       reflen += oldmsgidlen;
  286    }
  287 
  288    newref = smalloc(reflen);
  289    if (oldref != NULL) {
  290       memcpy(newref, oldref, oldreflen +1);
  291       if (oldmsgid != NULL) {
  292          newref[oldreflen++] = ',';
  293          newref[oldreflen++] = ' ';
  294          memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
  295       }
  296    } else if (oldmsgid)
  297       memcpy(newref, oldmsgid, oldmsgidlen +1);
  298    n = extract(newref, GREF);
  299    free(newref);
  300 
  301    /* Limit number of references TODO better on parser side */
  302    while (n->n_flink != NULL)
  303       n = n->n_flink;
  304    for (i = 1; i <= REFERENCES_MAX; ++i) {
  305       if (n->n_blink != NULL)
  306          n = n->n_blink;
  307       else
  308          break;
  309    }
  310    n->n_blink = NULL;
  311    head->h_ref = n;
  312    if (ok_blook(reply_in_same_charset) &&
  313          (cp = hfield1("content-type", mp)) != NULL){
  314       if((head->h_charset = cp = mime_param_get("charset", cp)) != NULL){
  315          char *cpo, c;
  316 
  317          for(cpo = cp; (c = *cpo) != '\0'; ++cpo)
  318             *cpo = lowerconv(c);
  319 
  320          head->h_charset = n_charsetalias_expand(cp);
  321       }
  322    }
  323 jleave:
  324    NYD2_LEAVE;
  325 }
  326 
  327 static int
  328 a_crese_list_reply(int *msgvec, enum header_flags hf){
  329    struct header head;
  330    struct message *mp;
  331    char const *cp, *cp2;
  332    enum gfield gf;
  333    struct name *rt, *mft, *np;
  334    int *save_msgvec;
  335    NYD2_ENTER;
  336 
  337    n_pstate_err_no = n_ERR_NONE;
  338 
  339    /* TODO Since we may recur and do stuff with message lists we need to save
  340     * TODO away the argument vector as long as that isn't done by machinery */
  341    /* C99 */{
  342       size_t i;
  343       for(i = 0; msgvec[i] != 0; ++i)
  344          ;
  345       ++i;
  346       save_msgvec = n_lofi_alloc(sizeof(*save_msgvec) * i);
  347       while(i-- > 0)
  348          save_msgvec[i] = msgvec[i];
  349       msgvec = save_msgvec;
  350    }
  351 
  352    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  353 
  354 jwork_msg:
  355    n_autorec_relax_create();
  356    mp = &message[*msgvec - 1];
  357    touch(mp);
  358    setdot(mp);
  359 
  360    memset(&head, 0, sizeof head);
  361    head.h_flags = hf;
  362    head.h_subject = a_crese_reedit(hfield1("subject", mp));
  363    head.h_mailx_command = (hf & HF_LIST_REPLY) ? "Lreply" : "reply";
  364    head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  365    head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  366    head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  367    head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  368 
  369    /* First of all check for Reply-To: then Mail-Followup-To:, because these,
  370     * if honoured, take precedence over anything else.  We will join the
  371     * resulting list together if so desired.
  372     * So if we shall honour R-T: or M-F-T:, then these are our receivers! */
  373    rt = a_crese_reply_to(mp);
  374    mft = a_crese_mail_followup_to(mp);
  375 
  376    if(rt != NULL || mft != NULL){
  377       np = cat(rt, mft);
  378       if(mft != NULL)
  379          head.h_mft = namelist_dup(np, GTO | gf); /* xxx GTO: no "clone"! */
  380 
  381       /* Optionally do not propagate a receiver that originally was in
  382        * secondary Cc: to the primary To: list */
  383       if(ok_blook(recipients_in_cc)){
  384          a_crese_polite_rt_mft_move(mp, &head, np);
  385 
  386          head.h_mailx_raw_cc = namelist_dup(head.h_cc, GCC | gf);
  387          head.h_cc = n_alternates_remove(head.h_cc, FAL0);
  388       }else
  389          head.h_to = np;
  390 
  391       head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
  392       head.h_to = n_alternates_remove(head.h_to, FAL0);
  393 #ifdef HAVE_DEVEL
  394       for(np = head.h_to; np != NULL; np = np->n_flink)
  395          assert((np->n_type & GMASK) == GTO);
  396       for(np = head.h_cc; np != NULL; np = np->n_flink)
  397          assert((np->n_type & GMASK) == GCC);
  398 #endif
  399       goto jrecipients_done;
  400    }
  401 
  402    /* Otherwise do the normal From: / To: / Cc: dance */
  403 
  404    if((cp2 = hfield1("from", mp)) == NULL)
  405       cp2 = nameof(mp, 1);
  406 
  407    /* Cc: */
  408    np = NULL;
  409    if(ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
  410       np = lextract(cp, GCC | gf);
  411    if((cp = hfield1("cc", mp)) != NULL){
  412       struct name *x;
  413 
  414       if((x = lextract(cp, GCC | gf)) != NULL)
  415          np = cat(np, x);
  416    }
  417    if(np != NULL){
  418       head.h_mailx_raw_cc = namelist_dup(np, GCC | gf);
  419       head.h_cc = n_alternates_remove(np, FAL0);
  420    }
  421 
  422    /* To: */
  423    np = NULL;
  424    if(cp2 != NULL)
  425       np = lextract(cp2, GTO | gf);
  426    if(!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL){
  427       struct name *x;
  428 
  429       if((x = lextract(cp, GTO | gf)) != NULL)
  430          np = cat(np, x);
  431    }
  432    /* Delete my name from reply list, and with it, all my alternate names */
  433    if(np != NULL){
  434       head.h_mailx_raw_to = namelist_dup(np, GTO | gf);
  435       np = n_alternates_remove(np, FAL0);
  436       /* The user may have send this to himself, don't ignore that */
  437       if(count(np) == 0){
  438          np = lextract(cp2, GTO | gf);
  439          head.h_mailx_raw_to = namelist_dup(np, GTO | gf);
  440       }
  441    }
  442    head.h_to = np;
  443 
  444 jrecipients_done:
  445 
  446    /* For list replies we want to automatically recognize the list address
  447     * given in the List-Post: header, so that we will not throw away a possible
  448     * corresponding receiver: temporarily "`mlist' the List-Post: address" */
  449    if((hf & HF_LIST_REPLY) && (cp = hfield1("list-post", mp)) != NULL){
  450       struct name *x;
  451 
  452       if((x = lextract(cp, GEXTRA | GSKIN)) == NULL || x->n_flink != NULL ||
  453             (cp = url_mailto_to_address(x->n_name)) == NULL ||
  454             /* XXX terribly wasteful to create a new name, and can't we find
  455              * XXX a way to mitigate that?? */
  456             is_addr_invalid(x = nalloc(cp, GEXTRA | GSKIN), EACM_STRICT)){
  457          if(n_poption & n_PO_D_V)
  458             n_err(_("Message contains invalid List-Post: header\n"));
  459       }else{
  460          /* A special case has been seen on e.g. ietf-announce@ietf.org:
  461           * these usually post to multiple groups, with ietf-announce@
  462           * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
  463           * -announce@ is only used for announcements, say).
  464           * So our desire is to honour this request and actively overwrite
  465           * List-Post: for our purpose; but only if its a single address.
  466           * However, to avoid ambiguities with users that place themselve in
  467           * Reply-To: and mailing lists which don't overwrite this (or only
  468           * extend this, shall such exist), only do so if reply_to exists of
  469           * a single address which points to the same domain as List-Post: */
  470          if(rt != NULL && rt->n_flink == NULL &&
  471                name_is_same_domain(x, rt))
  472             cp = rt->n_name; /* rt is EACM_STRICT tested */
  473          else
  474             cp = x->n_name;
  475 
  476          /* XXX is_mlist_mp()?? */
  477          if(is_mlist(cp, FAL0) == MLIST_OTHER)
  478             head.h_list_post = cp;
  479       }
  480    }
  481 
  482    /* In case of list replies we actively sort out any non-list recipient */
  483    if(hf & HF_LIST_REPLY){
  484       struct name **nhpp, *nhp, *tail;
  485 
  486       cp = head.h_list_post;
  487 
  488       nhp = *(nhpp = &head.h_to);
  489       head.h_to = NULL;
  490 j_lt_redo:
  491       for(tail = NULL; nhp != NULL;){
  492          np = nhp;
  493          nhp = nhp->n_flink;
  494 
  495          /* XXX is_mlist_mp()?? */
  496          if((cp != NULL && !asccasecmp(cp, np->n_name)) ||
  497                is_mlist(np->n_name, FAL0) != MLIST_OTHER){
  498             if((np->n_blink = tail) != NULL)
  499                tail->n_flink = np;
  500             else
  501                *nhpp = np;
  502             np->n_flink = NULL;
  503             tail = np;
  504          }
  505       }
  506       if(nhpp == &head.h_to){
  507          nhp = *(nhpp = &head.h_cc);
  508          head.h_cc = NULL;
  509          goto j_lt_redo;
  510       }
  511 
  512       /* For `Lreply' only, fail immediately with DESTADDRREQ if there are no
  513        * receivers at all! */
  514       if(head.h_to == NULL && head.h_cc == NULL){
  515          n_err(_("No recipients specified for `Lreply'\n"));
  516          if(msgvec[1] == 0){
  517             n_pstate_err_no = n_ERR_DESTADDRREQ;
  518             msgvec = NULL;
  519             goto jleave;
  520          }
  521          goto jskip_to_next;
  522       }
  523    }
  524 
  525    /* Move Cc: to To: as appropriate! */
  526    if(head.h_to == NULL && (np = head.h_cc) != NULL){
  527       head.h_cc = NULL;
  528       for(head.h_to = np; np != NULL; np = np->n_flink)
  529          np->n_type = (np->n_type & ~GMASK) | GTO;
  530    }
  531 
  532    a_crese_make_ref_and_cs(mp, &head);
  533 
  534    if(ok_blook(quote_as_attachment)){
  535       head.h_attach = csalloc(1, sizeof *head.h_attach);
  536       head.h_attach->a_msgno = *msgvec;
  537       head.h_attach->a_content_description = _("Original message content");
  538    }
  539 
  540    if(mail1(&head, 1, mp, NULL, !!(hf & HF_RECIPIENT_RECORD), 0) != OKAY){
  541       msgvec = NULL;
  542       goto jleave;
  543    }
  544    if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
  545       mp->m_flag |= MANSWER | MANSWERED;
  546    n_autorec_relax_gut();
  547 
  548 jskip_to_next:
  549    if(*++msgvec != 0){
  550       /* TODO message (error) ring.., less sleep */
  551       if(n_psonce & n_PSO_INTERACTIVE){
  552          fprintf(n_stdout,
  553             _("Waiting a second before proceeding to the next message..\n"));
  554          fflush(n_stdout);
  555          n_msleep(1000, FAL0);
  556       }
  557       goto jwork_msg;
  558    }
  559 
  560 jleave:
  561    n_lofi_free(save_msgvec);
  562    NYD2_LEAVE;
  563    return (msgvec == NULL);
  564 }
  565 
  566 static int
  567 (*a_crese_reply_or_Reply(char c))(int *, bool_t){
  568    int (*rv)(int*, bool_t);
  569    NYD2_ENTER;
  570 
  571    rv = (ok_blook(flipr) ^ (c == 'R')) ? &a_crese_Reply : &a_crese_reply;
  572    NYD2_LEAVE;
  573    return rv;
  574 }
  575 
  576 static int
  577 a_crese_reply(int *msgvec, bool_t recipient_record){
  578    int rv;
  579    NYD2_ENTER;
  580 
  581    rv = a_crese_list_reply(msgvec,
  582          (recipient_record ? HF_RECIPIENT_RECORD : HF_NONE));
  583    NYD2_LEAVE;
  584    return rv;
  585 }
  586 
  587 static int
  588 a_crese_Reply(int *msgvec, bool_t recipient_record){
  589    struct header head;
  590    struct message *mp;
  591    int *ap;
  592    enum gfield gf;
  593    NYD2_ENTER;
  594 
  595    memset(&head, 0, sizeof head);
  596    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  597 
  598    for(ap = msgvec; *ap != 0; ++ap){
  599       struct name *np;
  600 
  601       mp = &message[*ap - 1];
  602       touch(mp);
  603       setdot(mp);
  604 
  605       if((np = a_crese_reply_to(mp)) == NULL){
  606          char *cp;
  607 
  608          if((cp = hfield1("from", mp)) == NULL)
  609             cp = nameof(mp, 2);
  610          np = lextract(cp, GTO | gf);
  611       }
  612       head.h_to = cat(head.h_to, np);
  613    }
  614 
  615    mp = &message[msgvec[0] - 1];
  616    head.h_subject = hfield1("subject", mp);
  617    head.h_subject = a_crese_reedit(head.h_subject);
  618    a_crese_make_ref_and_cs(mp, &head);
  619    head.h_mailx_command = "Reply";
  620    head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  621    head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  622    head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  623    head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  624 
  625    if(ok_blook(recipients_in_cc)){
  626       a_crese_polite_rt_mft_move(mp, &head, head.h_to);
  627 
  628       head.h_mailx_raw_cc = namelist_dup(head.h_cc, GCC | gf);
  629       head.h_cc = n_alternates_remove(head.h_cc, FAL0);
  630    }
  631    head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
  632    head.h_to = n_alternates_remove(head.h_to, FAL0);
  633 
  634    if(ok_blook(quote_as_attachment)){
  635       head.h_attach = csalloc(1, sizeof *head.h_attach);
  636       head.h_attach->a_msgno = *msgvec;
  637       head.h_attach->a_content_description = _("Original message content");
  638    }
  639 
  640    if(mail1(&head, 1, mp, NULL, recipient_record, 0) != OKAY){
  641       msgvec = NULL;
  642       goto jleave;
  643    }
  644 
  645    if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
  646       mp->m_flag |= MANSWER | MANSWERED;
  647 jleave:
  648    NYD2_LEAVE;
  649    return (msgvec == NULL);
  650 }
  651 
  652 static int
  653 a_crese_fwd(char *str, int recipient_record){
  654    struct header head;
  655    struct message *mp;
  656    enum gfield gf;
  657    bool_t f, forward_as_attachment;
  658    char *recipient;
  659    int rv, *msgvec;
  660    NYD2_ENTER;
  661 
  662    rv = 1;
  663 
  664    if((recipient = laststring(str, &f, TRU1)) == NULL){
  665       n_err(_("No recipient specified.\n"));
  666       n_pstate_err_no = n_ERR_DESTADDRREQ;
  667       goto jleave;
  668    }
  669 
  670    forward_as_attachment = ok_blook(forward_as_attachment);
  671    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  672    msgvec = n_autorec_alloc((msgCount + 2) * sizeof *msgvec);
  673 
  674    n_pstate_err_no = n_ERR_NODATA;
  675    if(!f){
  676       *msgvec = first(0, MMNORM);
  677       if(*msgvec != 0)
  678          msgvec[1] = 0;
  679    }else if(getmsglist(str, msgvec, 0) < 0)
  680       goto jleave;
  681 
  682    if(*msgvec == 0){
  683       n_err(_("No applicable messages.\n"));
  684       goto jleave;
  685    }
  686    if(msgvec[1] != 0){
  687       n_err(_("Cannot forward multiple messages at once\n"));
  688       n_pstate_err_no = n_ERR_NOTSUP;
  689       goto jleave;
  690    }
  691 
  692    memset(&head, 0, sizeof head);
  693    head.h_to = lextract(recipient,
  694          (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)));
  695 
  696    mp = &message[*msgvec - 1];
  697    touch(mp);
  698    setdot(mp);
  699    head.h_subject = hfield1("subject", mp);
  700    head.h_subject = a_crese__fwdedit(head.h_subject);
  701    head.h_mailx_command = "forward";
  702    head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
  703    head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  704    head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  705    head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  706    head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  707 
  708    if(forward_as_attachment){
  709       head.h_attach = csalloc(1, sizeof *head.h_attach);
  710       head.h_attach->a_msgno = *msgvec;
  711       head.h_attach->a_content_description = _("Forwarded message");
  712    }
  713 
  714    rv = (mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL,
  715          recipient_record, 1) != OKAY); /* reverse! */
  716 jleave:
  717    NYD2_LEAVE;
  718    return rv;
  719 }
  720 
  721 static char *
  722 a_crese__fwdedit(char *subj){
  723    struct str in, out;
  724    char *newsubj;
  725    NYD2_ENTER;
  726 
  727    newsubj = NULL;
  728 
  729    if(subj == NULL || *subj == '\0')
  730       goto jleave;
  731 
  732    in.s = subj;
  733    in.l = strlen(subj);
  734    mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
  735 
  736    newsubj = n_autorec_alloc(out.l + 6);
  737    if(!ascncasecmp(out.s, "Fwd: ", sizeof("Fwd: ") -1)) /* TODO EXTEND SUPP.. */
  738       memcpy(newsubj, out.s, out.l +1);
  739    else{
  740       memcpy(newsubj, "Fwd: ", 5); /* TODO ..a la subject_re_trim()! */
  741       memcpy(&newsubj[5], out.s, out.l +1);
  742    }
  743 
  744    free(out.s);
  745 jleave:
  746    NYD2_LEAVE;
  747    return newsubj;
  748 }
  749 
  750 static int
  751 a_crese_resend1(void *vp, bool_t add_resent){
  752    struct header head;
  753    struct name *myto, *myrawto;
  754    enum gfield gf;
  755    char *name, *str;
  756    int *ip, *msgvec;
  757    bool_t fail;
  758    NYD2_ENTER;
  759 
  760    fail = TRU1;
  761 
  762    str = vp;
  763    msgvec = n_autorec_alloc((msgCount + 2) * sizeof *msgvec);
  764    name = laststring(str, &fail, TRU1);
  765    if(name == NULL){
  766       n_err(_("No recipient specified.\n"));
  767       n_pstate_err_no = n_ERR_DESTADDRREQ;
  768       goto jleave;
  769    }
  770 
  771    n_pstate_err_no = n_ERR_NODATA;
  772 
  773    if(!fail){
  774       *msgvec = first(0, MMNORM);
  775       if(*msgvec != 0)
  776          msgvec[1] = 0;
  777    }else if(getmsglist(str, msgvec, 0) < 0)
  778       goto jleave;
  779 
  780    if(*msgvec == 0){
  781       n_err(_("No applicable messages.\n"));
  782       goto jleave;
  783    }
  784 
  785    fail = TRU1;
  786    gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
  787 
  788    myrawto = nalloc(name, GTO | gf);
  789    myto = usermap(namelist_dup(myrawto, myrawto->n_type), FAL0);
  790    myto = n_alternates_remove(myto, TRU1);
  791    if(myto == NULL){
  792       n_pstate_err_no = n_ERR_DESTADDRREQ;
  793       goto jleave;
  794    }
  795 
  796    n_autorec_relax_create();
  797    for(ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
  798          ++ip){
  799       struct message *mp;
  800 
  801       mp = &message[*ip - 1];
  802       touch(mp);
  803       setdot(mp);
  804 
  805       memset(&head, 0, sizeof head);
  806       head.h_to = myto;
  807       head.h_mailx_command = "resend";
  808       head.h_mailx_raw_to = myrawto;
  809       head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
  810       head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
  811       head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
  812       head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
  813 
  814       if(resend_msg(mp, &head, add_resent) != OKAY){
  815          /* n_autorec_relax_gut(); XXX but is handled automatically? */
  816          goto jleave;
  817       }
  818       n_autorec_relax_unroll();
  819    }
  820    n_autorec_relax_gut();
  821 
  822    fail = FAL0;
  823    n_pstate_err_no = n_ERR_NONE;
  824 jleave:
  825    NYD2_LEAVE;
  826    return (fail != FAL0);
  827 }
  828 
  829 FL int
  830 c_reply(void *vp){
  831    int rv;
  832    NYD_ENTER;
  833 
  834    rv = (*a_crese_reply_or_Reply('r'))(vp, FAL0);
  835    NYD_LEAVE;
  836    return rv;
  837 }
  838 
  839 FL int
  840 c_replyall(void *vp){
  841    int rv;
  842    NYD_ENTER;
  843 
  844    rv = a_crese_reply(vp, FAL0);
  845    NYD_LEAVE;
  846    return rv;
  847 }
  848 
  849 FL int
  850 c_replysender(void *vp){
  851    int rv;
  852    NYD_ENTER;
  853 
  854    rv = a_crese_Reply(vp, FAL0);
  855    NYD_LEAVE;
  856    return rv;
  857 }
  858 
  859 FL int
  860 c_Reply(void *vp){
  861    int rv;
  862    NYD_ENTER;
  863 
  864    rv = (*a_crese_reply_or_Reply('R'))(vp, FAL0);
  865    NYD_LEAVE;
  866    return rv;
  867 }
  868 
  869 FL int
  870 c_Lreply(void *vp){
  871    int rv;
  872    NYD_ENTER;
  873 
  874    rv = a_crese_list_reply(vp, HF_LIST_REPLY);
  875    NYD_LEAVE;
  876    return rv;
  877 }
  878 
  879 FL int
  880 c_followup(void *vp){
  881    int rv;
  882    NYD_ENTER;
  883 
  884    rv = (*a_crese_reply_or_Reply('r'))(vp, TRU1);
  885    NYD_LEAVE;
  886    return rv;
  887 }
  888 
  889 FL int
  890 c_followupall(void *vp){
  891    int rv;
  892    NYD_ENTER;
  893 
  894    rv = a_crese_reply(vp, TRU1);
  895    NYD_LEAVE;
  896    return rv;
  897 }
  898 
  899 FL int
  900 c_followupsender(void *vp){
  901    int rv;
  902    NYD_ENTER;
  903 
  904    rv = a_crese_Reply(vp, TRU1);
  905    NYD_LEAVE;
  906    return rv;
  907 }
  908 
  909 FL int
  910 c_Followup(void *vp){
  911    int rv;
  912    NYD_ENTER;
  913 
  914    rv = (*a_crese_reply_or_Reply('R'))(vp, TRU1);
  915    NYD_LEAVE;
  916    return rv;
  917 }
  918 
  919 FL int
  920 c_forward(void *vp){
  921    int rv;
  922    NYD_ENTER;
  923 
  924    rv = a_crese_fwd(vp, 0);
  925    NYD_LEAVE;
  926    return rv;
  927 }
  928 
  929 FL int
  930 c_Forward(void *vp){
  931    int rv;
  932    NYD_ENTER;
  933 
  934    rv = a_crese_fwd(vp, 1);
  935    NYD_LEAVE;
  936    return rv;
  937 }
  938 
  939 FL int
  940 c_resend(void *vp){
  941    int rv;
  942    NYD_ENTER;
  943 
  944    rv = a_crese_resend1(vp, TRU1);
  945    NYD_LEAVE;
  946    return rv;
  947 }
  948 
  949 FL int
  950 c_Resend(void *vp){
  951    int rv;
  952    NYD_ENTER;
  953 
  954    rv = a_crese_resend1(vp, FAL0);
  955    NYD_LEAVE;
  956    return rv;
  957 }
  958 
  959 /* s-it-mode */