#include "args.h"
#include "config.h"

/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#include "mm.h"
#include "cmds.h"
#include "message.h"
#include "parse.h"

#define FORWLINE "                ---------------\n\n"

extern int allow_aborts;
extern int prompt_for_bcc;
extern int prompt_for_cc;
extern int prompt_for_fcc;
extern int prompt_rcpt_always;

static mail_msg outgoing, *msg;		/* outgoing message */

#ifdef undef				/* XXX */
    keylist keylist_copy();
#endif

#define msg msg_	/* avoid warnings about shadowed global */

static char *do_reply_indent ARGS((char *msg));
static char *forward_banner ARGS((int n));
static char *forward_header ARGS((message *m, int n));
static int handle_reply_to ARGS((int key, int *allp, int aborts));
static void do_forward_many ARGS((void));
static void do_forward_one ARGS((int which));
static void do_remail_many ARGS((void));
static void do_remail_one ARGS((int which, addresslist *to));
static void do_reply_one ARGS((int n, int aborts));
static void handle_include ARGS((keyval key, int *inclp));
static void remove_me ARGS((mail_msg *msg));
static void set_forward_subject ARGS((void));

void
#if HAVE_STDC
cmd_send (int n)
#else /* K&R style */
cmd_send (n)
int n;
#endif /* HAVE_STDC */
{
    static fdb txtfdb = { _CMTXT };
    static fdb cfmfdb = { _CMCFM , CM_SDH};
    volatile int ret;				/* return value */
    volatile int aa;
    static addresslist temp= { nil, nil };

    if (mode & MM_SEND) {
	confirm();
	deliver();		/* deliver it now */
	return;
    }

    cm_set_ind(false);			/* no indirections here */

    noise("message to");
    free_addresslist(&temp);
    parse_addresses(&temp);
    free_msg(&outgoing);
    set_send_defaults(&outgoing);
    if (outgoing.to == NULL)
	outgoing.to = new_header(TO, "To", HEAD_KNOWN, &outgoing);
    if (outgoing.to->address == NULL) {
	outgoing.to->address = (addresslist *)malloc(sizeof(addresslist));
	outgoing.to->address->first = nil;
	outgoing.to->address->last = nil;
    }
    send_mode(&outgoing);
    if (temp.first) {
	merge_addresses(outgoing.to->address, &temp);
    }
    else {
	prompt_address(" To: ", outgoing.to->address);
	files_to_fcc(outgoing.to->address, &outgoing);

    }
    if (!temp.first || prompt_rcpt_always) {
	if (temp.first)
	    free_addresslist(&temp);
	if (prompt_for_cc) {
	    if (outgoing.cc->address == NULL) {
		outgoing.cc->address =
		    (addresslist *)malloc(sizeof(addresslist));
		outgoing.cc->address->first = NULL;
		outgoing.cc->address->last = NULL;
	    }
	    prompt_address(" cc: ", outgoing.cc->address);
	    files_to_fcc(outgoing.cc->address, &outgoing);
	}

	if (prompt_for_bcc) {
	    if (outgoing.bcc == NULL) {
		outgoing.bcc = new_header(BCC, "Bcc", HEAD_KNOWN,&outgoing);
		outgoing.bcc->address = NULL;
	    }
	    if (outgoing.bcc->address == NULL) {
		outgoing.bcc->address =
		    (addresslist *)malloc(sizeof(addresslist));
		outgoing.bcc->address->first = NULL;
		outgoing.bcc->address->last = NULL;
	    }
	    prompt_address(" Bcc: ", outgoing.bcc->address);
	    files_to_fcc(outgoing.bcc->address, &outgoing);
	}
	if (prompt_for_fcc) {
	    if (outgoing.bcc == NULL) {
		outgoing.bcc = new_header(BCC, "Fcc", HEAD_KNOWN,&outgoing);
		outgoing.bcc->address = NULL;
	    }
	    prompt_fcc(" Fcc: ", outgoing.fcc->address);
	}
    }
    if (outgoing.to)
	files_to_fcc(outgoing.to->address, &outgoing);
    if (outgoing.cc)
	files_to_fcc(outgoing.cc->address, &outgoing);
    if (outgoing.bcc)
	files_to_fcc(outgoing.bcc->address, &outgoing);
    cm_set_ind(true);
    mode |= MM_SEND;
    aa = allow_aborts;
    cmseteof();
    cmseter();
    prompt(" Subject: ");
    cmsetrp();
    allow_aborts = true;
    parse(fdbchn(&txtfdb, &cfmfdb, nil), &pv, &used);
    if (used == &txtfdb) {
	outgoing.subject = new_header(SUBJECT, "Subject", HEAD_KNOWN,
				      &outgoing);
	outgoing.subject->string =
	    (char *)malloc(strlen(atmbuf)+1);
	strcpy(outgoing.subject->string, atmbuf);
    }
    allow_aborts = aa;
    outgoing.body = NULL;
    ret = getmsg(&outgoing);

    if ((escape_automatic_send && (ret == GET_ESC)) ||
	(control_d_automatic_send && (ret == GET_CTRLD))) { /* send! */
	deliver();
    }
}


void
#if HAVE_STDC
cmd_reply(int n)
#else /* K&R style */
cmd_reply(n)
int n;
#endif /* HAVE_STDC */
{
    if (mode & MM_READ) {		/* in read mode? */
	/* set default based on reply_to mm var */
	do_reply_one(cf->current,allow_aborts);
	mode &= ~MM_REPLY;
    }
    else {
	free_msg(&outgoing);
	if (!check_cf(O_RDONLY))
	    return;
	parse_sequence ("current",NULL,NULL);
	mode |= MM_REPLY;
	copy_sequence (cf->read_sequence, cf->sequence);
	if (sequence_start(cf->read_sequence)) {
	    do_reply_many();
	}
    }
}

void
do_reply_many(VOID)
{
    char buf[100];
    int aa = allow_aborts;
    volatile int doprev = false;

    sprintf(buf," Send reply for message %d to: ", cf->current);
    cmseteof();
    if (cmseter ()) {			/* errors return here */
	if (cmcsb._cmerr == CMxEOF) {
	    return;
	}
	else
	    doprev = true;
    }

    prompt(buf);
    if (doprev) {
	doprev = false;
	prevcmd();
    }

    allow_aborts = true;
    cmsetrp();
    if (!ignore (cf->current))
	do_reply_one (cf->current,aa);
    if (!sequence_next(cf->read_sequence))
	mode &= ~MM_REPLY;
}

/*
 * handle_reply_to
 * take care of parse for reply_to_fdb
 * return FALSE if reply session aborted
 */
static int
#if HAVE_STDC
handle_reply_to (int key, int *allp, int aborts)
#else /* K&R style */
handle_reply_to (key, allp, aborts)
int key, *allp;
int aborts;
#endif /* HAVE_STDC */
{
    switch(key) {
    case CMD_ALL:
	*allp = true;
	break;
    case CMD_SENDER:
	*allp = false;
	break;
    case CMD_QUIT:
	confirm();
	allow_aborts = aborts;
	if (cf->current < sequence_last (cf->read_sequence))
	    if (!yesno (" Continue replying to remaining messages? ", nil))
		mode &= ~MM_REPLY;
	return (FALSE);			/* skip this message */
    }
    return (TRUE);			/* do this message */
}

/*
 * handle_include:
 * handle the include_fdb result
 */
static void
#if HAVE_STDC
handle_include (keyval key, int *inclp)
#else /* K&R style */
handle_include (key, inclp)
keyval key;
int *inclp;
#endif /* HAVE_STDC */
{
    switch(key) {
    case CMD_INCLUDE:
	*inclp = true;
	break;
    case CMD_NOINCLUDE:
	*inclp = false;
	break;
    }
}

/*
 * reply to the current message
 */
static void
#if HAVE_STDC
do_reply_one(int n, int aborts)
#else /* K&R style */
do_reply_one(n, aborts)
int n;
int aborts;
#endif /* HAVE_STDC */
{
    int all, include;

    reply_to_fdb._cmdef = reply_all ? "all" : "sender";
    include_fdb._cmdef = reply_insert ? "including" : "not-including";

    parse(fdbchn (&reply_to_fdb, &include_fdb, NULL), &pv, &used);
    if (used == &reply_to_fdb) {
	if (!handle_reply_to ((int)pv._pvkey, &all, aborts)) /* aborted? */
	    return;
	parse(fdbchn (&include_fdb, NULL), &pv, &used);
	handle_include (pv._pvkey, &include);
    }
    else {				/* used == &include_fdb */
	handle_include (pv._pvkey, &include);
	parse (fdbchn (&reply_to_fdb, NULL), &pv, &used);
	if (!handle_reply_to ((int)pv._pvkey, &all, aborts))
	    return;
    }
    confirm();
    allow_aborts = aborts;
    do_reply(n, all, include);
}


#undef msg

void
#if HAVE_STDC
do_reply(int msgno, int all, int include)
#else /* K&R style */
do_reply(msgno, all, include)
int msgno;
int all;
int include;
#endif /* HAVE_STDC */
{
    int ret;
    int gotfrom = false;
    const char *name;

    msg = parse_msg(&(cf->msgs[msgno]));
    free_msg(&outgoing);
    set_send_defaults(&outgoing);
    if (outgoing.to == NULL) {
	new_header(TO,"To", HEAD_KNOWN, &outgoing);
	outgoing.to->address = NULL;
    }
    if (outgoing.to->address == NULL) {
	outgoing.to->address = (addresslist *) malloc(sizeof(addresslist));
	outgoing.to->address->first = outgoing.to->address->last = NULL;
    }

#ifdef notdef
    if (reply_include_me) {
	if (outgoing.cc == NULL) {
	    new_header(CC,"cc", HEAD_KNOWN, &outgoing);
	    outgoing.cc->address = NULL;
	}
	if (outgoing.cc->address == NULL) {
	    outgoing.cc->address = (addresslist *) malloc(sizeof(addresslist));
	    outgoing.cc->address->first = outgoing.cc->address->last = NULL;
	}
	add_addresslist(outgoing.cc->address, user_name, ADR_ADDRESS);
    }
#endif
    if (!gotfrom && msg->reply_to) {
	name = msg->reply_to->string;
	match_addresses(outgoing.to->address, &name, (int) strlen(name));
	gotfrom = true;

    }
    if (!gotfrom && msg->from) {
	name = msg->from->string;
	match_addresses(outgoing.to->address, &name, (int)strlen(name));
	gotfrom = true;
    }
    if (msg->resent_from) {
	name = msg->resent_from->string;
	if (outgoing.cc == NULL) {
	    new_header(CC,"cc", HEAD_KNOWN, &outgoing);
	    outgoing.cc->address = NULL;
	}
	if (outgoing.cc->address == NULL) {
	    outgoing.cc->address = (addresslist *) malloc(sizeof(addresslist));
	    outgoing.cc->address->first = outgoing.cc->address->last = NULL;
	}
	match_addresses(outgoing.cc->address, &name, (int)strlen(name));
    }
    if (all) {
	if (msg->to || msg->cc) {
	    if (outgoing.cc == NULL) {
		new_header(CC,"cc", HEAD_KNOWN, &outgoing);
		outgoing.cc->address = NULL;
	    }
	    if (outgoing.cc->address == NULL) {
		outgoing.cc->address =
		    (addresslist *) malloc(sizeof(addresslist));
		outgoing.cc->address->first = outgoing.cc->address->last =
		    NULL;
	    }
	    if (msg->to)
		merge_addresses(outgoing.cc->address, msg->to->address);
	    if (msg->cc)
		merge_addresses(outgoing.cc->address, msg->cc->address);
	}
    }

    if (!reply_include_me)
	remove_me (&outgoing);

    if (outgoing.to)
	files_to_fcc(outgoing.to->address, &outgoing);
    if (outgoing.cc)
	files_to_fcc(outgoing.cc->address, &outgoing);
    if (outgoing.bcc)
	files_to_fcc(outgoing.bcc->address, &outgoing);

    if (include) {
	outgoing.body = do_reply_indent(msg->body);
    }
    if (msg->subject) {
	new_header(SUBJECT,"Subject", HEAD_KNOWN, &outgoing);
	if (ustrncmp(msg->subject->string, "Re:", 3) != 0) {
	    outgoing.subject->string = safe_strcpy("Re: ");
	}
	outgoing.subject->string =
	    safe_strcat(outgoing.subject->string, msg->subject->string, false);
    }
    new_header(IN_REPLY_TO, "In-Reply-To", HEAD_KNOWN, &outgoing);
#ifdef undef				/* XXX */
    if (msg->keywords) {
	new_header(KEYWORDS, "Keywords", HEAD_KNOWN, &outgoing);
	outgoing.keywords->keys = keylist_copy (msg->keywords->keys);
    }
#endif /* undef */
#ifdef undef				/* fdc */
    if (msg->date) {
	outgoing.in_reply_to->string = safe_strcpy("Your message of ");
	outgoing.in_reply_to->string =
	    safe_strcat(outgoing.in_reply_to->string,msg->date->string,false);
    }
    else
#endif /* undef */
      if (msg->message_id) {
	outgoing.in_reply_to->string =
	    safe_strcat(outgoing.in_reply_to->string,msg->message_id->string,
			false);
    }
    free_msg(msg);

    if (reply_initial_display) {
	if (cmcsb._cmoj == NULL)
	    display_header (stdout, &outgoing, FALSE, FALSE);
	else
	    display_header (cmcsb._cmoj, &outgoing, FALSE, FALSE);
    }

    send_mode(&outgoing);
    mode |= MM_ANSWER;			/* to set ANSWER when appropriate */
    ret = getmsg(&outgoing);
    if ((escape_automatic_send && (ret == GET_ESC)) ||
	(control_d_automatic_send && (ret == GET_CTRLD))) { /* send! */
	deliver();
    }
}

#define msg msg_	/* avoid warnings about shadowed global */

static char *
#if HAVE_STDC
do_reply_indent (char *msg)
#else /* K&R style */
do_reply_indent (msg)
char *msg;
#endif /* HAVE_STDC */
{
    char *p, *cp;
    int lines,i;

    if (reply_indent == NULL || strlen(reply_indent) == 0) {
	return(safe_strcpy (msg));
    }

    p = msg;
    lines = 0;
    while((p = index(p,'\n'), p))
	lines++, p++;
    p = (char *)malloc(strlen(msg) + lines * strlen(reply_indent) + 2);

    cp = p;
    for(i = 0; i < (int)strlen(reply_indent); i++) /* start off with indent */
        *(cp++) = reply_indent[i];
    for(; *msg != '\0'; cp++, msg++) {
	*cp = *msg;
	if (*msg == '\n' && *(msg+1) != '\0') {
	    for(i = 0; i < (int)strlen(reply_indent); i++)
		*(++cp) = reply_indent[i];
	}
    }
    *cp++ = '\n';
    *cp = '\0';
    return(p);
}

void
#if HAVE_STDC
cmd_forward(int n)
#else /* K&R style */
cmd_forward(n)
int n;
#endif /* HAVE_STDC */
{
    if (mode & MM_SEND) {
	printf ("\
The forward command can not be called while you are currently working on an\n\
outgoing message.\n");
	return;
    }

    if (!check_cf(O_RDONLY))
	return;
    if (mode & MM_READ) {		/* in read mode? */
	/* set default based on reply_to mm var */
	noise("message to");
	do_forward_one(cf->current);
    }
    else {
	parse_sequence ("current",NULL,NULL);
	if (sequence_start(cf->sequence))
	    do_forward_many();
    }
}

#undef msg

static void
#if HAVE_STDC
do_forward_one(int which)
#else /* K&R style */
do_forward_one(which)
int which;
#endif /* HAVE_STDC */
{
    int ret;
    static addresslist temp = { NULL, NULL };

    parse_addresses(&temp);
    free_msg(&outgoing);
    set_send_defaults(&outgoing);
    if (outgoing.to == NULL) {
	new_header(TO, "To", HEAD_KNOWN, &outgoing);
	outgoing.to->address = NULL;
    }
    if (outgoing.to->address == NULL) {
	outgoing.to->address = (addresslist *)malloc(sizeof(addresslist));
	outgoing.to->address->last = outgoing.to->address->first = NULL;
    }
    merge_addresses(outgoing.to->address, &temp);
    if (outgoing.to)
	files_to_fcc(outgoing.to->address, &outgoing);
    if (outgoing.cc)
	files_to_fcc(outgoing.cc->address, &outgoing);
    if (outgoing.bcc)
	files_to_fcc(outgoing.bcc->address, &outgoing);

    if ((msg = parse_msg (&(cf->msgs[which])), msg))
	set_forward_subject ();

    ret = getmsg(&outgoing);
    send_mode(&outgoing);
    if (outgoing.body) {
	if (*outgoing.body) {		/* did user type anything? */
	    if (outgoing.body[strlen(outgoing.body)-1] != '\n')
		outgoing.body = safe_strcat(outgoing.body, "\n", false);
	    outgoing.body = safe_strcat(outgoing.body, FORWLINE, false);
	}
	outgoing.body = safe_strncat(outgoing.body, cf->msgs[which].text,
				     (int)cf->msgs[which].size);
	if ((escape_automatic_send && (ret == GET_ESC)) ||
	    (control_d_automatic_send && (ret == GET_CTRLD))) { /* send! */
	    deliver();
	}
	/* XXX shouldn't set this if sendmail() failed */
	if (!(cf->msgs[which].flags & M_FORWARDED) &&
	    !(cf->flags & MF_RDONLY)) {
	    cf->msgs[which].flags |= (M_FORWARDED|M_MODIFIED);
	    cf->flags |= MF_MODIFIED;	/* we'll have to save that flag */
	}
    }
    else {
	printf("Aborted.\n");
	mode &= ~MM_SEND;
	free_msg(&outgoing);
    }
}

/*
 * set the subject string for a forwarded message
 */

static void
set_forward_subject(VOID)
{
    char *s, *f;
    int fl, sl;

    if (!(msg->from && msg->subject))
	return;

    f = msg->from->string;
    fl = strlen (f);

    s = msg->subject->string;
    sl = strlen (s);

    new_header (SUBJECT, "Subject", HEAD_KNOWN, &outgoing);

    outgoing.subject->string = (char *)malloc (sl + fl + 4 + 4);
    sprintf (outgoing.subject->string, "[%.*s: %.*s]", fl, f, sl, s);
}

static void
do_forward_many(VOID)
{
    char *forwardees = NULL;
    char *head = NULL;
    int ret;

    free_msg(&outgoing);
    set_send_defaults(&outgoing);
    if (outgoing.to == NULL) {
	outgoing.to = new_header(TO, "To", HEAD_KNOWN, &outgoing);
	outgoing.to->address = NULL;
    }
    if (outgoing.to->address == NULL) {
	outgoing.to->address = (addresslist *)malloc(sizeof(addresslist));
	outgoing.to->address->first = NULL;
	outgoing.to->address->last = NULL;
    }
    send_mode(&outgoing);
    prompt_address(" To: ", outgoing.to->address);
    files_to_fcc(outgoing.to->address, &outgoing);

    ret = getmsg(&outgoing);

    if ((msg = parse_msg (&cf->msgs[cf->current]), msg))
	set_forward_subject ();

    /*
     * If there's only one message to forward, we don't include a
     * summary of the forwarded messages.
     */
    if (sequence_first(cf->sequence) == sequence_last(cf->sequence)) {
	message *m = &cf->msgs[cf->current];

	if (*outgoing.body) {
	    if (outgoing.body[strlen(outgoing.body)-1] != '\n')
		outgoing.body = safe_strcat(outgoing.body, "\n", false);
	    outgoing.body = safe_strcat(outgoing.body, FORWLINE, false);
	}
	outgoing.body = safe_strncat(outgoing.body, m->text, (int)m->size);
    }
    else {				/* more than one message */
	int i = 1;
	do {
	    message *m = &cf->msgs[cf->current];
	    head = safe_strcat(head, forward_header (m, i), false);
	    head = safe_strcat(head, "\n", false);
	    if (forwardees && forwardees[strlen(forwardees)-1] !='\n')
		forwardees = safe_strcat(forwardees,"\n", false);
	    forwardees = safe_strcat(forwardees, forward_banner(i), false);
	    forwardees = safe_strncat(forwardees, m->text, (int)m->size);
	    ++i;
	} while (sequence_next (cf->sequence));
	head = safe_strcat(head, forwardees, false);
	if (outgoing.body) {
	    if (outgoing.body[strlen(outgoing.body)-1] != '\n')
		outgoing.body = safe_strcat(outgoing.body, "\n", false);
	    outgoing.body = safe_strcat(outgoing.body, FORWLINE, false);
	}
	outgoing.body = safe_strcat(outgoing.body, head, false);
	free(forwardees);
	free(head);
	if ((escape_automatic_send && (ret == GET_ESC)) ||
	    (control_d_automatic_send && (ret == GET_CTRLD))) { /* send! */
	    deliver ();
	}
    }
}


static char *
#if HAVE_STDC
forward_banner(int n)
#else /* K&R style */
forward_banner(n)
int n;
#endif /* HAVE_STDC */
{
    static char buf[50];
    sprintf(buf,"\nMessage %d -- *********************\n",n);
    return(buf);
}

static char *
#if HAVE_STDC
forward_header(message *m, int n)
           				/* which message */
      					/* position in forwarded message */
#else /* K&R style */
forward_header(m, n)
message *m;				/* which message */
int n;					/* position in forwarded message */
#endif /* HAVE_STDC */
{
    static buffer line;
    char *cp = line;
    char *ht;

    (void) sprintf (cp, "%4d) %s ", n, hdate (m->date));
    cp += strlen (cp);
    ht = htext ("from", m->text);
    sprintf (cp, "%-12.12s ", (ht == NULL) ? "" : ht);
    ht = (char*)safe_free(ht);
    cp += strlen (cp);
    ht = htext ("subject", m->text);
    sprintf (cp, "%-.32s (%ld chars)", (ht == NULL) ? "" : ht,
	     (long)m->size);
    ht = (char*)safe_free(ht);
    cp += strlen (cp);
    return(line);
}

void
#if HAVE_STDC
cmd_remail(int n)
#else /* K&R style */
cmd_remail(n)
int n;
#endif /* HAVE_STDC */
{
    addresslist to;

    if (mode & MM_SEND) {
	printf ("\
The remail command can not be called while you are currently working on an\n\
outgoing message.\n");
	return;
    }
    to.last = to.first = NULL;

    if (mode & MM_READ) {		/* in read mode? */
	/* set default base on reply_to mm var */
	noise("message to");
	parse_addresses(&to);
	while (to.first == NULL)	/* have to have one */
	    prompt_address(" To: ", &to);
	do_remail_one(cf->current, &to);
	free_addresslist(&to);
    }
    else {
	if (!check_cf(O_RDONLY))
	    return;
	parse_sequence ("current",NULL,NULL);
	if (sequence_start(cf->sequence)) {
	    do_remail_many();
	}
    }
}

static void
#if HAVE_STDC
do_remail_one(int which, addresslist *to)
#else /* K&R style */
do_remail_one(which, to)
int which;
addresslist *to;
#endif /* HAVE_STDC */
{
    /* extern void xxmsg(mail_msg *, char *, char); */

    /* not a memory leak unless parse_msg starts to malloc */
    msg = parse_msg(&(cf->msgs[which]));

    /* (void)xxmsg(msg,"DO_REMAIL_ONE",'A'); */

    if (msg->resent_to) {
	free_header(msg, RESENT_TO);
    }
    new_header(RESENT_TO, "Resent-To", HEAD_KNOWN, msg);
    msg->resent_to->address = (addresslist *) malloc(sizeof(addresslist));
    msg->resent_to->address->last = msg->resent_to->address->first =  NULL;
	merge_addresses(msg->resent_to->address, to);
    if (msg->resent_date)
	free_header(msg, RESENT_DATE);
    new_header(RESENT_DATE, "Resent-Date", HEAD_KNOWN, msg);
#ifdef COMMENT
    msg->resent_date->string = safe_strcpy(rfctime(time(0)));
#else
    msg->resent_date->string = safe_strcpy(rfctime((time_t)0));
#endif	/* COMMENT */

    if (msg->resent_from)
	free_header(msg, RESENT_FROM);
    new_header(RESENT_FROM, "Resent-From", HEAD_KNOWN, msg);
					/* check from variable. */
    msg->resent_from->string = safe_strcpy(create_sender());
    if (msg->to)
        files_to_fcc(msg->to->address, msg);

    /* (void)xxmsg(msg,"DO_REMAIL_ONE",'B'); */

    sendmail(msg);
}

static void
do_remail_many(VOID)
{
    addresslist to;

    to.last = to.first = NULL;
    prompt_address(" To: ", &to);
    do {
	if (!ignore(cf->current))
	    do_remail_one(cf->current, &to);
    } while (sequence_next (cf->sequence));
}


void
#if HAVE_STDC
cmd_bug (int n)
#else /* K&R style */
cmd_bug (n)
int n;
#endif /* HAVE_STDC */
{
    static fdb txtfdb = { _CMTXT };
    static fdb cfmfdb = { _CMCFM , CM_SDH};
    const char *name;
    int ret;
    addresslist tmp;
    headers *h;
    int aa;
    volatile int doprev = false;

    cm_set_ind(false);			/* no indirections here */

    tmp.first = tmp.last = NULL;
    free_msg(&outgoing);
    set_send_defaults(&outgoing);
    confirm();
    send_mode(&outgoing);
    cm_set_ind(false);
    name = BUGSTO;
    match_addresses(outgoing.to->address, &name, (int)strlen(name));
    merge_addresses((addresslist *)outgoing.to,(addresslist *)&tmp);
    aa = allow_aborts;
    cmseteof();
    if (cmseter ()) {			/* errors return here */
	if (cmcsb._cmerr == CMxEOF) {
	    return;
	}
	else
	    doprev = true;
    }

    prompt(" Subject: ");
    if (doprev) {
	doprev = false;
	prevcmd();
    }
    cmsetrp();
    allow_aborts = true;
    parse(fdbchn(&txtfdb, &cfmfdb, nil), &pv, &used);
    if (used == &txtfdb) {
	outgoing.subject = new_header(SUBJECT, "Subject", HEAD_KNOWN,
				      &outgoing);
	outgoing.subject->string =
	    (char *)malloc(strlen(atmbuf)+1);
	strcpy(outgoing.subject->string, atmbuf);
    }
    allow_aborts = aa;
    h = new_header(USER_HEADERS, "Bug-Report", HEAD_UNKNOWN, &outgoing);
    h->string = (char *)malloc(strlen("Bug in ")+strlen(mm_version)+
		       strlen(OStype)+3+1);
    sprintf(h->string,"Bug in %s (%s)", mm_version, OStype);

    outgoing.body = NULL;
    ret = getmsg(&outgoing);
    if ((escape_automatic_send && (ret == GET_ESC)) ||
	(control_d_automatic_send && (ret == GET_CTRLD))) { /* send! */
	deliver ();
    }
}

void
#if HAVE_STDC
cmd_insert (int n)
#else /* K&R style */
cmd_insert (n)
int n;
#endif /* HAVE_STDC */
{
    char *fname;
    char *text;

  if (!(mode & MM_SEND)) {
    fprintf (stderr, "\nNot in send mode.  Cannot insert file.\n");
    return;
  }
  noise ("from file");
  fname = parse_input_file (NULL, NULL, false);
  confirm();
  if ((text = read_from_temp(fname)) == NULL) {
    free (fname);
    return;
  }
  free (fname);
  if (outgoing.body == NULL) {
    outgoing.body = (char *) malloc (strlen(text)+1);
    outgoing.body[0] = '\0';
  }
  else
    outgoing.body = (char *) safe_realloc (outgoing.body,
				   (int)strlen(outgoing.body)+strlen(text)+1);
  strcat (outgoing.body, text);
  free (text);
}



mail_msg *
get_outgoing(VOID) {
  return (&outgoing);
}


void
#if HAVE_STDC
set_outgoing (mail_msg *m)
#else /* K&R style */
set_outgoing (m) mail_msg *m;
#endif /* HAVE_STDC */
{
  free_msg (&outgoing);
  bcopy ((char *) m, (char *) &outgoing, sizeof(mail_msg));
}

/*
 * remove self from recipient headers.
 * invoked by "reply" command if reply-include-me is false.
 */

#define msg msg_	/* avoid warnings about shadowed global */

static void
#if HAVE_STDC
remove_me (mail_msg *msg)
#else /* K&R style */
remove_me (msg)
mail_msg *msg;
#endif /* HAVE_STDC */
{
    addr_unit au;
    addresslist al;

    /*
     * construct a trivial addr_unit consisting of our username.
     * we should really use a user-specifiable pattern instead.
     */
    au.type = ADR_ADDRESS;
    au.data = user_name;
    au.next = au.prev = nil;

    /*
     * build a trivial address list
     */
    al.first = al.last = &au;

    /*
     * remove ourself from the To: and cc: headers.
     */
    if (msg->to)
	remove_addr (msg->to->address, &al);
    if (msg->cc)
	remove_addr (msg->cc->address, &al);
}

void
#if HAVE_STDC
cmd_smail (int n)
#else /* K&R style */
cmd_smail (n)
int n;
#endif /* HAVE_STDC */
{
    static addresslist temp= { nil, nil };

    cm_set_ind(false);			/* no indirections here */

    noise("message to");
    free_addresslist(&temp);
    parse_addresses(&temp);
    free_msg(&outgoing);
    set_send_defaults(&outgoing);
    if (outgoing.to == NULL)
	outgoing.to = new_header(TO, "To", HEAD_KNOWN, &outgoing);
    if (outgoing.to->address == NULL) {
	outgoing.to->address = (addresslist *)malloc(sizeof(addresslist));
	outgoing.to->address->first = nil;
	outgoing.to->address->last = nil;
    }
    send_mode(&outgoing);
    if (temp.first) {
	merge_addresses(outgoing.to->address, &temp);
    }
    if (outgoing.to)
	files_to_fcc(outgoing.to->address, &outgoing);
    if (outgoing.cc)
	files_to_fcc(outgoing.cc->address, &outgoing);
    if (outgoing.bcc)
	files_to_fcc(outgoing.bcc->address, &outgoing);
    cm_set_ind(true);
    mode |= MM_SEND;
    outgoing.body = NULL;
    (void)getmsg(&outgoing);
    deliver();
}


