"Fossies" - the Fresh Open Source Software Archive

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