/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Building, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989, 1990, 1991, 1992  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  $Revision: 2.13 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

/*======================================================================
    reply.c
   
   Code here for forward and reply to mail
   A few support routines as well

  This code will forward and reply to MIME messages. The Pine composer
at this time will only support non-text segments at the end of a
message so, things don't always come out as one would like. If you
always forward a message in MIME format, all will be correct. This
code also has a problem forwarding nested MULTIPART messages. If an
item of TYPEMESSAGE is in between nestings all will be well. This is
due to limitation in the c-client. This code also will result in messages 
that have a text segment as the first part. Lastly, this is all new ground
since I've never seen another implementation of this, so there's probably
lots of things that could be done better that will evolve over time.

  =====*/


#include "headers.h"


#ifdef ANSI
static char   *generate_reply_to(MESSAGECACHE *);
static char   *fetch_format_forward_text(MAILSTREAM *, long, char *,
                                         ENVELOPE *, BODY *);
unsigned char *rfc822_qprint(unsigned char *, unsigned long, unsigned long *);
unsigned char *rfc822_base64(unsigned char *, unsigned long, unsigned long *);
ENVELOPE      *copy_envelope(ENVELOPE *);
BODY          *copy_body(BODY *, BODY *);
int            fetch_contents(MAILSTREAM *, long, BODY *, int, int);
static char   *format_reply_text(ENVELOPE *, char *, char *);
char          *get_body_part_text(MAILSTREAM *, BODY *, long, char *, int *,
                                  char *);
static char   *get_signature();

#else
static char   *generate_reply_to();
static char   *fetch_format_forward_text();
unsigned char *rfc822_qprint();
unsigned char *rfc822_base64();
ENVELOPE      *copy_envelope();
BODY          *copy_body();
int            fetch_contents();
static char   *format_reply_text();
char          *get_body_part_text();
static char   *get_signature();
#endif



/*----------------------------------------------------------------------
        Fill in an outgoing message for reply and pass off to send

    Args: pine_state -- The usual pine structure
          message    -- The MESSAGECACHE of entry to reply to 

  Result: Reply is formated and passed off to composer/mailer

Reply

   - put senders address in To field
   - search to and cc fields to see if we aren't the only recepients
   - if other than us, ask if we should reply to all.
      - if answer yes, fill out the To and Cc fields
   - fill in the subject field
   - fill out the body and the attachments
   - pass off to pine_send()
  ---*/
void
reply(pine_state, message)
     struct pine *pine_state;
     MESSAGECACHE *message;
{
    char       *new_text, *orig_text, *sig;
    ADDRESS    *a, *a_tail;
    ENVELOPE   *env, *outgoing;
    BODY       *body, *orig_body;
    int         text_is_malloced;
    PART       *part;
    long        msgno;
    MAILSTREAM *stream;

    dprint(4, (debugfile,"\n - reply -\n"));

    stream = ps_global->mail_stream;
    msgno  = message->msgno;

    /*---------- Get the envelope of message we're replying to ------*/
    env = message->env;
    if(env == NULL) {
        q_status_message1(1, 2,4,
                          "Error fetching message %s. Can't reply to it.",
			  long2string(msgno));
	return;
    }
    outgoing = mail_newenvelope();

    /*==================== To and Cc fields =====================*/
    /* Always include the originator of the message */
    if(env->reply_to != NULL && env->from != NULL &&
       (strucmp(env->reply_to->mailbox, env->from->mailbox) ||
        strucmp(env->reply_to->host, env->from->host)) &&
        want_to("Use \"Reply to:\" address instead of \"From:\" address", 'y',
                   (char **)NULL, 0) == 'y') {
        outgoing->to = rfc822_cpy_adr(env->reply_to);
    } else {
        outgoing->to = rfc822_cpy_adr(env->from);
    }


    /*------ check to see if we aren't the only recepient -----------*/
    for(a = env->to; a != NULL; a = a->next) {
	if(!address_is_us(a, pine_state))
	  break;
    }
    if(a == NULL)
      for(a = env->cc; a != NULL; a = a->next) {
	  if(!address_is_us(a, pine_state))
	    break;
      }
    /*------ Ask user and possibly include all other recepients ---*/ 
    outgoing->cc = (ADDRESS *)NULL;
    if(a != NULL && want_to("Reply to all recipients", 'n', (char **)NULL, 0)
       =='y'){
	if(env->to != NULL)
          outgoing->cc = rfc822_cpy_adr(env->to);
	if(env->cc != NULL) {
            if(outgoing->cc == NULL) {
                outgoing->cc = rfc822_cpy_adr(env->cc);
            } else {
                for(a = outgoing->cc; a->next != NULL; a = a->next);
                a->next = rfc822_cpy_adr(env->cc);
            }
        }
    }
    for(a = a_tail = outgoing->cc; a != NULL; ){
        if(address_is_us(a, pine_state)){ /*-- eliminate sender from list ---*/
            if(a == outgoing->cc){
                a = outgoing->cc = a->next;
                a_tail->next = NULL;  /* so we only free one address */
                mail_free_address(&a_tail);
                a_tail = a;
            } else {
                a_tail->next = a->next;
                a->next = NULL; /* so we only free one address */
                mail_free_address(&a);
                a = a_tail->next;
            }
        } else {
            a_tail = a;
            a = a->next;
        }
    }
    /*----- eliminate us from to, unless thats all there is ------*/
    if(outgoing->to != NULL && address_is_us(outgoing->to, pine_state)) {
        if(outgoing->cc != NULL) {
            mail_free_address(&(outgoing->to));
            outgoing->to = outgoing->cc;
            outgoing->cc = NULL;
        }
    }


    /*------------ Format the subject line ---------------*/
    if(env->subject == NULL || strlen(env->subject) == 0) {
	/*--- blank : make up our own ---*/        
        outgoing->subject = cpystr("Re: your mail");
    } else {
	/* --- already got "Re:", just pass it along ---*/
	if((isupper(env->subject[0])? tolower(env->subject[0]):env->subject[0])
            == 'r' &&
	   (isupper(env->subject[1])? tolower(env->subject[1]):env->subject[1])
            == 'e' &&
           env->subject[2] == ':'){
	    outgoing->subject = cpystr(env->subject);
	} else {
	    /*--- plan : add and "Re: " ----*/
            outgoing->subject = fs_get(strlen(env->subject) + 6);
	    strcpy(outgoing->subject, "Re: ");
	    strcat(outgoing->subject, env->subject);
	}
    }


    /*==========  Other miscelaneous fields ===================*/   
    outgoing->return_path = (ADDRESS *)NULL;
    outgoing->bcc         = (ADDRESS *)NULL;
    outgoing->sender      = (ADDRESS *)NULL;
    outgoing->return_path = (ADDRESS *)NULL;
    outgoing->in_reply_to = cpystr(generate_reply_to(message));
    outgoing->remail      = NULL;
    outgoing->reply_to    = (ADDRESS *)NULL;

    outgoing->message_id  = cpystr(generate_message_id(pine_state));



   /*==================== Now fix up the message body ====================*/

    sig = get_signature();

    orig_body = message->body;

    /*---- Include the origina text if requested ----*/
    if(want_to("Include original message in Reply",'n',(char **)NULL,0)=='y'){
        if(orig_body == NULL || orig_body->type == TYPETEXT) {

            /*------ Simple text-only message ----*/
            body      = mail_newbody();
            body->type= TYPETEXT;
            orig_text = get_body_part_text(stream, orig_body, msgno, "1",
                                           &text_is_malloced,
                                           ". Text not included");
            body->contents.text = (unsigned char *)format_reply_text(env,
                                                                     orig_text,
                                                                     sig);
        } else if(orig_body->type == TYPEMULTIPART) {

            /*------ Message is Multipart ------*/
            /*-- BUG -- need to do a better job for MULTIPART/alternate --*/
            body = copy_body(NULL, orig_body);

            if(orig_body->contents.part != NULL &&
               orig_body->contents.part->body.type == TYPETEXT) {
                /*---- First part of the message is text -----*/
                orig_text = get_body_part_text(stream,
                                               orig_body, msgno,
                                               "1", &text_is_malloced,
                                               ". Text not included");
                new_text = format_reply_text(env, orig_text, sig);

                body->contents.part->body.contents.text = (unsigned char *)
                  (text_is_malloced ? new_text : cpystr(new_text));

                body->contents.part->body.size.bytes = 
                   strlen((char *)body->contents.part->body.contents.text);
                if(fetch_contents(stream, msgno, body, 0, 2)) 
                  q_status_message(1, 2, 4,
                                  "\007Error including all message parts");

            } else {
                /*--- No text part, create a blank one ---*/
                part                     = mail_newbody_part();
                part->next               = body->contents.part;
                body->contents.part      = part;
                part->body.contents.text = (unsigned char *)
                                            format_reply_text(env,"", sig);
                part->body.size.bytes    = strlen((char *)
                                                 part->body.contents.text);
                if(fetch_contents(stream, msgno, body, 1, 2)) 
                  q_status_message(1, 2, 4,
                                   "\007Error including all message parts");
            }
        } else {
            /*---- Single non-text message of some sort ----*/
            body                     = mail_newbody();
            body->type               = TYPEMULTIPART;
            part                     = mail_newbody_part();
            body->contents.part      = part;
    
            /*--- The first part, a blank text part to be edited ---*/
            part->body.type          = TYPETEXT;
            part->body.contents.text = (unsigned char *)
                                           format_reply_text(env, "", sig);
            part->body.size.bytes    = strlen((char *)
                                              part->body.contents.text);
    
            /*--- The second part, what ever it is ---*/
            part->next               = mail_newbody_part();
            part                     = part->next;
            part->body.id            = cpystr(generate_message_id(ps_global));
            copy_body(&(part->body), orig_body);
            part->body.contents.text = (unsigned char *)cpystr(
                mail_fetchbody(stream, msgno, "1",
                               &part->body.size.bytes));
            if(part->body.contents.text == NULL)
              goto done;
        }

    } else {

        /*--------- No text included --------*/
        body                = mail_newbody();
        body->type          = TYPETEXT;
        body->contents.text = (unsigned char *)cpystr(sig);
    }
    fs_give((void *)&sig);

	    
    pine_send(outgoing,      /* partially formatted outgoing message */
              &body,
              "COMPOSE MESSAGE REPLY",
              NULL, 
              1,
              NULL,
              0);

  done:
    mail_free_envelope(&outgoing);
    mail_free_body(&body);
}



/*----------------------------------------------------------------------
    Format the text for inclusion in a reply

Args:  env       -- Envelope, must not be NULL
       orig_text -- Text to be included with 'prefix' in front, may be NULL
       sig       -- Text of signature, must be a valid string

The signature is included according the variables that control it. The
header of the original message might also be included if the controling
variables indicate so.

The parts in the reply are:
  - signature
  - seperator line
  - date
  - subject 
  - from
  - to
  - cc
  - original text

Returns: malloced formatted string, and never returns NULL
  ---*/

static char *
format_reply_text(env, orig_text, sig)
     ENVELOPE *env;
     char     *orig_text, *sig;
{
    char       *p, *q, *r, *prefix, *new_text;
    char       *subjectbuf, *tobuf, *ccbuf, *onbuf, *datebuf, *frombuf;
    int         line_count, bufsize;
    struct date d;

    /*--- This string is put in front of each line of orig_text ---*/
    prefix = "> ";

    /*====== Format and size up the parts of the reply body ====*/
    /* first count the lines so we know how bigger to make buffer  */
    if(orig_text == NULL)
      orig_text = "";
    line_count = 0;
    for(p = orig_text; *p;) {
        while(*p != '\r' && *p) p++;
        if(*p) p++; /* over the CR */
        if(*p) p++; /* over the NL */
        line_count++;
    }

    parse_date(env->date, &d);
    if(env->from == NULL) {
        sprintf(tmp_20k_buf, "On %s%s%d %s %d, it was written:\n\n",
                d.wkday == -1 ? "" : week_abbrev(d.wkday),
                d.wkday == -1 ? "" : ", ",
                d.day, month_abbrev(d.month), d.year);
    } else if (env->from->personal != NULL) {
        sprintf(tmp_20k_buf, "On %s%s%d %s %d, %s wrote:\n\n",
                d.wkday == -1 ? "" : week_abbrev(d.wkday),
                d.wkday == -1 ? "" : ", ",
                d.day, month_abbrev(d.month), d.year,
                env->from->personal);
    } else {
        sprintf(tmp_20k_buf, "On %s%s%d %s %d %s%s%s wrote:\n\n",
                d.wkday == -1 ? "" : week_abbrev(d.wkday),
                d.wkday == -1 ? "" : ", ",
                d.day, month_abbrev(d.month), d.year,
                env->from->mailbox,
                env->from->host != NULL ? "@" : "", env->from->host);
    }
    onbuf = cpystr(tmp_20k_buf);

    /*---- The Date: field -----*/
    sprintf(tmp_20k_buf, "%sDate: %s\n", prefix, env->date);
    datebuf = cpystr(tmp_20k_buf);

    /*---- The From: field ----*/
    if(env->from == NULL) {
        sprintf(tmp_20k_buf,"%sFrom:     (Sender of message unkown)\n",prefix);
    } else if(env->from->personal != NULL) {
        sprintf(tmp_20k_buf, "%sFrom: %s <%s%s%s>\n",prefix,
                env->from->personal, env->from->mailbox,
                env->from->host != NULL ? "@" : "",
                env->from->host != NULL ? env->from->host : "");
    } else {
        sprintf(tmp_20k_buf, "%sFrom:%s%s%s\n", prefix, env->from->mailbox,
                env->from->host != NULL ? "@" : "",
                env->from->host != NULL ? env->from->host : "");
    }
    frombuf = cpystr(tmp_20k_buf);

    /*-------- The To: field --------*/
    tobuf = env->to == NULL ? NULL :pretty_addr_string("To: ",env->to, prefix);

    /*-------- The Cc: field -----*/
    ccbuf = env->cc == NULL ? NULL : pretty_addr_string("Cc: ",env->cc,prefix);

    /*-------- The subject line ----*/
    sprintf(tmp_20k_buf, "%sSubject: %s\n", prefix,
                             env->subject == NULL ? "" : env->subject);
    subjectbuf = cpystr(tmp_20k_buf);


    /*======= Allocate buffer and piece together ======*/
    bufsize  = strlen(sig) + line_count* strlen(prefix) +
               strlen(orig_text) +
               strlen(onbuf) + strlen(datebuf) + strlen(frombuf) +
               strlen(subjectbuf) +
                 (tobuf == NULL ? 0 : strlen(tobuf)) +
                   (ccbuf == NULL ? 0 : strlen(ccbuf)) + 50;
                              /* 50 for "To:", "Cc:", etc ... */
    dprint(9, (debugfile, "reply text allocated %d\n", bufsize));
    new_text = fs_get(bufsize);

    if(strucmp(ps_global->VAR_OLD_STYLE_REPLY, "yes")) {
        strcpy(new_text, sig);          /*-- signature --*/
        strcat(new_text, "\n\n");
    } else {
        new_text[0] = '\0';
    }

    strcat(new_text, onbuf);        /*-- intro line --*/
    q = new_text + strlen(new_text);

    if(ps_global->header_in_reply) {
        /* This is a bit lazy as we could trim down allocation and
           calculations above for case with out mail header */

        strcpy(q, datebuf);
        q += strlen(q);

        strcpy(q, frombuf);
        q += strlen(q);

        if(tobuf != NULL) {
            strcpy(q, tobuf);
            q += strlen(q);
        }

        if(ccbuf != NULL ) {
            strcpy(q, ccbuf);
            q += strlen(q);
        }

        strcpy(q, subjectbuf);       /*--- subject line ---*/
        q +=strlen(q);

        sprintf(q,"%s\n", prefix);  /*---- The blank line ----*/
        q += strlen(q);
    }

    fs_give((void *)&frombuf);
    fs_give((void *)&datebuf);
    fs_give((void *)&subjectbuf);
    if(tobuf != NULL)
      fs_give((void *)&tobuf);
    if(ccbuf != NULL)
      fs_give((void *)&ccbuf);


    for(p = orig_text; *p != '\0';) {  /*---- The message text ----*/
        for(r = prefix; *r; *q++ = *r++);
        while(*p != '\r' && *p)
          *q++ = *p++;
        if(*p) p++; /* over the CR */
        if(*p) p++; /* over the NL */
        *q++ = '\n';
    }
    *q++ = '\n';
    *q = '\0';

    if(!strucmp(ps_global->VAR_OLD_STYLE_REPLY, "yes"))
      strcat(new_text, sig);          /*-- signature --*/


    dprint(9, (debugfile, "length of reply text %d\n", strlen(new_text)));
    fs_give((void *)&onbuf);

    return(new_text);
}



    
	   
    
/*----------------------------------------------------------------------
       Paritially set up message to forward and pass off to composer/mailer

    Args: pine_state -- The usual pine structure
          message    -- The MESSAGECACHE of entry to reply to 

  Result: outgoing envelope and body created and passed off to composer/mailer

   Create the outgoing envelope for the mail being forwarded, which is 
not much more than filling in the subject, and create the message body
of the outgoing message which requires formatting the header from the
envelope of the original messasge.
  ----------------------------------------------------------------------*/
void
forward(pine_state, message)
     struct pine   *pine_state;
     MESSAGECACHE *message;
{
    ENVELOPE      *outgoing, *env;
    BODY          *body, *orig_body, *text_body, *b;
    unsigned long  text_bytes;
    char           part_string[100], *h_text, *tmp_text;
    int            part_count;
    PART          *part;
    MAILSTREAM    *stream;
    long           msgno;

    dprint(4, (debugfile, "\n - forward -\n"));

    stream                = pine_state->mail_stream;
    msgno                 = message->msgno;
    env                   = message->env;
    outgoing              = mail_newenvelope();
    outgoing->message_id  = cpystr(generate_message_id(pine_state));


    /*==================== Subject ====================*/
    if(env->subject == NULL || strlen(env->subject) == 0) {
	/* --- Blank, make up one ---*/
	outgoing->subject = cpystr("Forwarded mail....");
    } else {
        outgoing->subject = fs_get(strlen(env->subject) + strlen(" (fwd)")+ 1);
	strcpy(outgoing->subject, env->subject);
        removing_trailing_white_space(outgoing->subject);
	/* this next strange compare is to see if the last few chars are
	     already '(fwd)' before we tack another on */
        dprint(9, (debugfile, "checking subject: \"%s\"\n",
                   env->subject + strlen(env->subject) - 5));
	if (strlen(outgoing->subject) < 6 ||
                  (strcmp(outgoing->subject + strlen(outgoing->subject) - 5,
                      "(fwd)") != 0))
	    strcat(outgoing->subject, " (fwd)");
    }


    orig_body = message->body;

    if(ps_global->feature_level == Seasoned &&
       want_to("Forward message as a MIME attachment", 'y', NULL, 0) == 'y') {
        /*----- Forward the message as a MIME type "MESSAGE" ----*/

        body = mail_newbody();
        body->type = TYPEMULTIPART;

        /*---- The TEXT part/body ----*/
        body->contents.part                     = mail_newbody_part();
        body->contents.part->body.type          = TYPETEXT;
        body->contents.part->body.contents.text = (unsigned char *)
                                                              get_signature();

        /*---- The Forwarded Message body ----*/
        body->contents.part->next = mail_newbody_part();
        b = &(body->contents.part->next->body);
        b->type        = TYPEMESSAGE;
        b->id          = cpystr(generate_message_id(ps_global));
        b->description = fs_get(
                 (env->subject != NULL ? strlen(env->subject) : 0) + 30);
        sprintf(b->description, "Forwarded message \'%s\'",
                env->subject == NULL ? "" : env->subject); 
        b->contents.msg.env  = NULL;
        b->contents.msg.body = NULL;
        h_text   = cpystr(mail_fetchheader(stream, msgno));
        tmp_text = mail_fetchtext(stream, msgno);
        if(*tmp_text == '\0')
          goto bomb;
        b->size.bytes = strlen(tmp_text) +strlen(h_text) + 1;
        b->contents.msg.text = fs_get(b->size.bytes + 10);
        strcpy(b->contents.msg.text, h_text);
        strcat(b->contents.msg.text, "\n");
        strcat(b->contents.msg.text, tmp_text);
        fs_give((void **)&h_text);

    } else {
        if(orig_body == NULL || orig_body->type == TYPETEXT) {
            /*---- Message has a single text part -----*/
            body                = mail_newbody();
            body->type          = TYPETEXT;
            body->contents.text = (unsigned char *)
              fetch_format_forward_text(stream, msgno, "1", env, orig_body);
            if(body->contents.text == NULL)
              goto clean; /* error message alread output */
    
            body->size.bytes = strlen((char *)body->contents.text);
    
        } else if(orig_body->type == TYPEMULTIPART) {
            /*---- Message is multipart ----*/
    
            /*--- Copy the body and entire structure  ---*/
            body = copy_body(NULL, orig_body);
    
    
            /*--- The text part of the message ---*/
            if(orig_body->contents.part->body.type == TYPETEXT) {
                /*--- The first part is text ----*/
                text_body                = &body->contents.part->body;
                text_body->contents.text =
                  (unsigned char *)fetch_format_forward_text(
                                            stream, msgno, "1", env, body);
                if(text_body->contents.text == NULL)
                  goto clean; /* error message already output */
                text_body->size.bytes    = (unsigned long)strlen(
                                             (char *)text_body->contents.text);

                if(fetch_contents(stream, msgno, body, 0, 2))
                  goto bomb;
            } else {
                /*--- Create a new blank text part ---*/
                part                     = mail_newbody_part();
                part->next               = body->contents.part;
                body->contents.part      = part;
                part->body.contents.text = (unsigned char *)
                  fetch_format_forward_text(NULL, 0, "", env, NULL);
                part->body.size.bytes    = 0;
                if(fetch_contents(stream, msgno, body, 1, 2))
                  goto bomb;
            }
    
        } else {
            /*---- A single part message, not of type text ----*/
            body                     = mail_newbody();
            body->type               = TYPEMULTIPART;
            part                     = mail_newbody_part();
            body->contents.part      = part;
    
            /*--- The first part, a blank text part to be edited ---*/
            part->body.type          = TYPETEXT;
            part->body.contents.text = (unsigned char *)
                  fetch_format_forward_text(NULL, 0, "", env, NULL);
            part->body.size.bytes    = 0;
    
            /*--- The second part, what ever it is ---*/
            part->next               = mail_newbody_part();
            part                     = part->next;
            part->body.id            = cpystr(generate_message_id(ps_global));
            copy_body(&(part->body), orig_body);
            part->body.contents.text = (unsigned char *)cpystr(
                mail_fetchbody(stream, msgno, "1", &part->body.size.bytes));
            if(part->body.contents.text == NULL)
              goto bomb;
        }
    }


    pine_send(outgoing,      /* partially formatted outgoing message */
              &body,
              "FORWARD MESSAGE",
              NULL, 
              0,
              NULL,
              ps_global->nr_mode);

  clean:
    mail_free_envelope(&outgoing);
    mail_free_body(&body);
    return;

  bomb:
    q_status_message(1, 4, 5,
           "\007Error fetching message contents. Can't forwarded message");
    goto clean;

}


        
/*----------------------------------------------------------------------
    Fetch allocate and format text for forwarding

Args:  stream      -- Mail stream to fetch text for
       message_no  -- Message number of text for foward
       part_number -- Part number of text to forward
       env         -- Envelope of message being forwarded
       body        -- Body structure of message being forwarded

Returns: formated and malloced block of text or NULL if the fetch failed

If the text is richtext, it will be converted to plain text, since there's
no rich text editing capabilities in Pine (yet). The character sets aren't
really handled correctly here. Theoretically editing should not be allowed
if text character set doesn't match what term-character-set is set to.

This function also reads in the signature file if there is one.

Because of the way the MIME decoding functions work, this function will make 
two copies of the text being forwarded if decoding must be done. Eventually
on the fly decoders will have to be created and the memory required here
can be reduced.

The text returned by this function is in standard UNIX new line convention.
  ----*/
static char *
fetch_format_forward_text(stream, message_no,  part_number, env, body)
     MAILSTREAM    *stream;
     long           message_no;
     char          *part_number;
     ENVELOPE      *env;
     BODY          *body;
{
    char         *text, *to, *cc, *orig_text, *sig;
    long          bytes;
    int           is_malloced;
    unsigned long text_size;
    static char *banner ="\n---------- Forwarded message ----------\n";

    /*----- Get the decoded text ------*/
    if(stream != NULL && part_number != NULL){
        orig_text = get_body_part_text(stream, body, message_no, part_number,
                                     &is_malloced, ". Can't forward message");
    } else {
        is_malloced = 0;
        orig_text = "";
    }

    if(orig_text == NULL)
      return(NULL);

    /*----- Format the addresses -------*/
    to = pretty_addr_string("To: ",env->to, "");
    cc = pretty_addr_string("Cc: ",env->cc, "");

    sig = get_signature();

    /*------ Count up bytes to allocate for whole message body -----*/
    bytes = 0;
    bytes += strlen(sig);
    bytes += 10;                              /* misc \n's and ending \0 */
    bytes += strlen(banner);                 
    bytes += (env->date == NULL ? 0 : strlen(env->date) + 10); 
    if(env->from != NULL)                        /* including "Date: \n" */
      bytes += strlen(addr_string(env->from)) + 10; /* include "From: \n" */
    bytes += (env->subject == NULL ? 0 : strlen(env->subject) + 10);
                                                /* include "Subject: \n" */
    bytes += strlen(to);
    bytes += strlen(cc);
    bytes += strlen(orig_text);
    bytes += 10;                              /* misc \n's and ending \0 */

    dprint(9, (debugfile, "Allocating %d bytes for body_text\n", bytes));

    text = fs_get(bytes);


    /*------ Put is all together in the newly allocated text ---- ---*/
    strcpy(text, sig);
    if(strlen(sig) < 4)
      strcat(text, ps_global->nr_mode ? "": "\n\n\n");
    strcat(text, banner);
    if(env->date != NULL) {
        strcat(text, "Date: ");
        strcat(text, env->date);
        strcat(text, "\n");
    }
    if(env->from != NULL) {
        strcat(text, "From: "); 
        strcat(text, addr_string(env->from));
    }
    strcat(text, "\n");
    strcat(text, to);
    strcat(text, cc);
    if(env->subject != NULL) {
        strcat(text, "Subject: ");
        strcat(text, env->subject);
        strcat(text, "\n");
    }
    strcat(text, "\n");
    if(orig_text != NULL)
      strcat_nocr(text, orig_text);

    /*------ Clean up ------*/
    fs_give((void **)&sig);
    fs_give((void **)&to);
    fs_give((void **)&cc);
    if(is_malloced)
      fs_give((void **)&orig_text);

    return(text);
}






/*----------------------------------------------------------------------
     Get a text body part and decoded it. Used for including text when 
   forwarding or replying

Args: stream     -- The mail stream the body part to fetch is on
      message_no -- The message number for the body part
      part_number-- The actual part number in the message
      body       -- The body structure for the message to know encoding
      is_malloced-- Indicates if part returned is a malloced buffer or not
      hmm        -- Add this to any error messages output

The null terminated fetched text is returned. This function may not work
well if the contents aren't text. The text will be decoded from what ever
encoding it was in, and it will be converted from rich text to plain text.
If there is an error fetching or decoding the text a message will be
printed, and NULL returned. If body it NULL, then plain text is assumed
(non-IMAP2bis server).

The text returned here will have new lines terminated by CRLF!
  ----*/
char *
get_body_part_text(stream, body, message_no, part_number, is_malloced, hmm)
     MAILSTREAM *stream;
     long        message_no;
     char       *part_number;
     BODY       *body;
     int        *is_malloced;
     char       *hmm; 
{
    unsigned char *text;
    unsigned long  text_size;

    if(is_malloced != NULL)
      *is_malloced  = 0;

    /*---- Fetch the text body part ---*/
    text = (unsigned char *)mail_fetchbody(stream,  message_no, part_number,
                                           &text_size);
    if(text == NULL)  {
        q_status_message1(1, 2, 4, "Error fetching message text%s", hmm);
        return(NULL);
    }

    if(body == NULL) {
        text[text_size] = '\0';
        return((char *)text);
    }

    /*----- Decode the body part -----*/
    switch(body->encoding) {
      case ENC7BIT:
        break;

      case ENCQUOTEDPRINTABLE:
        text = rfc822_qprint(text, text_size, &text_size); 
        if(is_malloced)
          *is_malloced = 1;
        break;

      case ENCBASE64: /* At the moment the q-p decoder takes \r\n's */
        text = rfc822_base64(text, text_size, &text_size);
        if(is_malloced)
          *is_malloced = 1;
        break;

      default:
        q_status_message1(1, 2, 4,
                     "Error fetching message text: encoding format unknown%s",
                          hmm);
        return(NULL);
        break;
    }
    if(text == NULL) {
        q_status_message2(1, 2, 4,"Error decoding \"%s\" format of text%s",
                         body->encoding == ENCBASE64 ? "base 64" :
                         "quoted-printable", hmm);
        return(NULL);
    }

    text[text_size] = '\0';

    /*--- Scale down to plain text from rich text -----*/
    if(body->subtype != NULL && strucmp(body->subtype,"richtext") == 0)
      rich2plain((char *)text, 75, 1);

    return((char *)text);
}



/*----------------------------------------------------------------------
   Fill in the contents of each body part

Args: stream      -- Stream the message is on
      msgno       -- Message number the body structure is for
      body        -- Body the to fill in
      part_offset -- Part offset between the orignal and the new message
      start_part  -- The part in the new body 'body' to start with

Result: 0 if all went OK, 1 if nothing could be fetched

This function copies the contents from an original message/body to
a new message/body. The the part_offset, is the number of parts 
in the new body that have been added before the original parts from
the original message.

If one or more part (but not all), a status message will be queued.
 ----*/

fetch_contents(stream, msgno, body, part_offset, start_part)
     MAILSTREAM *stream;
     long        msgno;
     BODY       *body;
     int         start_part, part_offset;
{
    char part_string[20], e_mess[200], *e;
    int  part_count, nested_multipart, error_part[50], *ep, got_one;
    PART *part, *ppart;

    nested_multipart = 0;
    ep               = error_part;
    *ep              = 0;

    for(ppart = part = body->contents.part, part_count = 1;
        part != NULL;
        ppart = part, part = part->next, part_count++){

        if(part_count < start_part)
          continue;

        if(part->body.type == TYPEMULTIPART) {
            if(!nested_multipart) {
                q_status_message(1, 2, 4,
                                 "\007Can't include nested multipart sections. They will be omitted");
                nested_multipart = 1;
                
            }
            ppart->next = part->next;
            part = ppart;
            continue;
        }

        if(part->body.id == NULL)
          part->body.id = cpystr(generate_message_id(ps_global));
          
        sprintf(part_string, "%d", part_count - part_offset);
        if(part->body.type == TYPEMESSAGE) {
            part->body.contents.msg.env = NULL;
            part->body.contents.msg.body = NULL;
            if(part->body.subtype != NULL &&
               strucmp(part->body.subtype,"external-body") != 0) {
                part->body.contents.msg.text = 
                        cpystr(mail_fetchbody(stream,
                                              (long)msgno,
                                              part_string,
                                              &part->body.size.bytes));
                if(part->body.contents.msg.text == NULL)
                  *ep++ = part_count;
                else
                  got_one = 1;
            } else {
                got_one = 1;
            }
        } else {
            part->body.contents.text =  (unsigned char *)
                    cpystr(mail_fetchbody(stream,
                                          (long)msgno,
                                          part_string,
                                          &part->body.size.bytes));
            if(part->body.contents.text == NULL) {
                *ep++ = part_count;
            } else {
                got_one = 1;
            }
        }
    }

    if(!got_one)  /* Didn't get any message parts, bomb out of this */
      return(1);

    if(ep != error_part) {
        /* Error fetching one or more parts */
        *ep = 0;
        sprintf(e_mess, "Error fetching message parts");
        e = e_mess + strlen(e_mess);
        for(ep = error_part; *ep != 0; ep++){
            sprintf(e, " %d,", *ep);
            e += strlen(e);
        }
        e--; /* Kill last comma */
        *e = '\0';
        
        q_status_message(1, 3, 5, e_mess);
    }
    return(0);
}



/*----------------------------------------------------------------------
    Copy the body structure

Args: new_body -- Pointer to already allocated body, or NULL, if none
      old_body -- The Body to copy


 This is called recursively traverses the body structure copying all the
elements. The new_body parameter can be NULL in which case a new body is
allocated. Alternatively it can point to an already allocated body
structure. This is used when copying body parts since a PART includes a 
BODY. The contents fields are *not* filled in.
  ----*/

BODY *
copy_body(new_body, old_body)
     BODY *old_body, *new_body;
{
    PART *new_part, *old_part;

    if(old_body == NULL)
      return(NULL);

    if(new_body == NULL)
      new_body = mail_newbody();

    *new_body = *old_body;
    if(old_body->id)
      new_body->id = cpystr(old_body->id);

    if(old_body->description)
      new_body->description = cpystr(old_body->description);

    if(old_body->subtype)
      new_body->subtype = cpystr(old_body->subtype);

    new_body->parameter = copy_parameters(old_body->parameter);

    new_part = NULL;
    if(new_body->type == TYPEMULTIPART) {
        for(old_part = new_body->contents.part; old_part != NULL;
            old_part = old_part->next){
            if(new_part == NULL) {
                new_part = mail_newbody_part();
                new_body->contents.part = new_part;
            } else {
                new_part->next = mail_newbody_part();
                new_part = new_part->next;
            }
            copy_body(&(new_part->body), &(old_part->body));
        }
    } else if(new_body->type == TYPETEXT) {
        new_body->contents.text = NULL;
    }
    return(new_body);
}



/*----------------------------------------------------------------------
    Copy the MIME parameter list
 
 Allocates storage for new part, and returns pointer to new paramter
list. If old_p is NULL, NULL is returned.
 ----*/

PARAMETER *
copy_parameters(old_p)
     PARAMETER *old_p;
{
    PARAMETER *new_p, *p1, *p2;

    if(old_p == NULL)
      return((PARAMETER *)NULL);

    new_p = p2 = NULL;
    for(p1 = old_p; p1 != NULL; p1 = p1->next) {
        if(new_p == NULL) {
            p2 = mail_newbody_parameter();
            new_p = p2;
        } else {
            p2->next = mail_newbody_parameter();
            p2 = p2->next;
        }
        p2->attribute = cpystr(p1->attribute);
        p2->value     = cpystr(p1->value);
    }
    return(new_p);
}
    
    

/*----------------------------------------------------------------------
    Make a complete copy of an envelope and all it's fields

Args:    e -- the envelope to copy

Result:  returns the new envelope, or NULL, if the given envelope was NULL

  ----*/

ENVELOPE *    
copy_envelope(e)
     register ENVELOPE *e;
{
    register ENVELOPE *e2;

    if(e == NULL)
      return(NULL);

    e2              = mail_newenvelope();
    e2->remail      = (e->remail      == NULL ? NULL : cpystr(e->remail));
    e2->return_path = (e->return_path == NULL ? NULL :
                                              rfc822_cpy_adr(e->return_path));
    e2->date        = (e->date        == NULL ? NULL : cpystr(e->date));
    e2->from        = (e->from        == NULL ? NULL :
                                              rfc822_cpy_adr(e->from));
    e2->sender      = (e->sender      == NULL ? NULL :
                                              rfc822_cpy_adr(e->sender));
    e2->reply_to    = (e->reply_to    == NULL ? NULL :
                                              rfc822_cpy_adr(e->reply_to));
    e2->subject     = (e->subject     == NULL ? NULL : cpystr(e->subject));
    e2->to          = (e->to          == NULL ? NULL : rfc822_cpy_adr(e->to));
    e2->cc          = (e->cc          == NULL ? NULL : rfc822_cpy_adr(e->cc));
    e2->bcc         = (e->bcc         == NULL ? NULL : rfc822_cpy_adr(e->bcc));
    e2->in_reply_to = (e->in_reply_to == NULL ? NULL : cpystr(e->in_reply_to));
    e2->message_id  = (e->message_id  == NULL ? NULL : cpystr(e->message_id));
    return(e2);
}






/*----------------------------------------------------------------------
     Generate the "In-reply-to" text from message header

  Args: message -- MESSAGECACHE entry

  Result: returns the string or NULL if there is a problem
 ----*/
static char *
generate_reply_to(message)
     MESSAGECACHE *message;
{
    static char buffer[128];
    ENVELOPE   *env = message->env;
    
    if (message == NULL)		
        return(NULL);
    else {
        sprintf(buffer, "%.100s",  env->message_id);
    }

    return(buffer);
}



/*----------------------------------------------------------------------
        Generate a unique message id string.

   Args: ps -- The usual pine structure

  Result: Unique string is returned

Uniqueness is gaurenteed by using the host name, process id, date to the
second and a single unique character
*----------------------------------------------------------------------*/
char *
generate_message_id(ps)
     struct pine *ps;
{
    static char id[100], a = 'A';
    long        now;
    struct tm  *now_x;
    extern char version_buff[];

    now   = time(0);
    now_x = localtime(&now);

#ifdef	DOS
    sprintf(id,"<PCPine.%.20s.%02d%02d%02d%02d%02d.%c%d@%.50s>",
#else
    sprintf(id,"<Pine.%.20s.%02d%02d%02d%02d%02d.%c%d@%.50s>",
#endif
            version_buff,now_x->tm_year, now_x->tm_mon + 1, now_x->tm_mday,
            now_x->tm_hour, now_x->tm_sec, a, getpid(), ps->hostname);

    if(a == 'Z')
      a = 'a';
    else if(a == 'z')
      a = 'A';
    else    
      a = a++;

    return(id);
}



/*----------------------------------------------------------------------
     Format an address field, wrapping lines nicely at commas

  Args: field_name  -- The name of the field we're formatting ("TO:", Cc:...)
        addr        -- ADDRESS structure to format
        line_prefix -- A prefix string for each line such as "> "

 Result: A formatted, malloced string is returned.

The resuling lines formatted are 80 columns wide.
  ----------------------------------------------------------------------*/
char *
pretty_addr_string(field_name, addr, line_prefix)
     ADDRESS *addr;
     char    *line_prefix, *field_name;
{
    register char *p, *p1, *q;
    int            comma_count, indent, column, i;
    char          *result, *r;

    if(addr == NULL) {
        /* NULL address, return malloced empty string */
        result = fs_get(1);
        *result = '\0';
        return(result);
    }

    ps_global->addr_field_buf[0] = '\0';
    rfc822_write_address(ps_global->addr_field_buf, addr);

    /* Count commas so we know max amount of stuff we'll have to insert.
       Then we know how much to malloc */
    comma_count = 1; /* at least 1 for the field name */
    for(p = ps_global->addr_field_buf; *p; p++) {
        if(*p == ',')
          comma_count++;
    }

    indent = strlen(field_name) + strlen(line_prefix) + 1; /* 1 for \n */
    result = fs_get(strlen(ps_global->addr_field_buf) + comma_count * indent +
                                                          1); /* 1 fo \0 */
    strcpy(result, line_prefix);
    strcat(result, field_name);
    column = indent;
    q = result + indent - 1; /* -1 to offset +1 above */

    /*  Copy addresses into new buffer with careful formatting */
    for(p = ps_global->addr_field_buf;;) {
        for(p1 = p; *p1 && *p1 != ','; p1++); /* check out len of next */
        if(column + (p1 - p) + 1 >= 80 && column != indent) {
            /* time for a new line */
            *q++ = '\n';
            for(r = line_prefix; *r; *q++ = *r++);
            for(i = strlen(field_name); i > 0 ; i--, *q++ = ' ');
            column = indent;
        }
        column += (p1 - p) + 1;
        while(*p && *p != ','){ /* copy the next address in */
            *q++ = *p++;
        }
        if(*p == '\0')
          break;
        *q++ = *p++;
    }
    *q++ = '\n';
    *q   = '\0';

    return(result);
}



/*----------------------------------------------------------------------

  ----*/
static char *
get_signature()
{
    char *sig, sig_path[MAXPATH+1];

    /*----- Get the signature if there is one to get -----*/
    build_path(sig_path, ps_global->home_dir, ps_global->VAR_SIGNATURE_FILE);
    sig = read_file(sig_path);
    if(!can_access(sig_path, 0)) {
        sig = read_file(sig_path);
        if(sig == NULL)
          q_status_message2(1, 2, 4,
                          "\007Error \"%s\" reading signature file \"%s\"",
                          error_description(errno), sig_path);
    } else {
        sig = NULL;
    } 
    if(sig == NULL) {
        sig  = fs_get(sizeof(char));
        *sig = '\0';
    } else {
        char *tmp_sig;
        tmp_sig = sig;
        sig = fs_get(strlen(sig) + 5);
        strcpy(sig, "\n\n");
        strcat(sig, tmp_sig);
        fs_give((void **)&tmp_sig);
    }
    return(sig);
}
