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

            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   *
    ***********************************************************************
 

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


/*======================================================================
       send.c
       Functions for composing and sending mail

 ====*/

#include "headers.h"
#include "../c-client/smtp.h"
#include "../pico/pico.h"


#ifdef ANSI
static void  parse_postponed_header(char *, char *, int, PATMT **);
#ifndef	DOS
static char *call_sendmail(ENVELOPE *, BODY *);
static int   call_system(char *);
#endif	/* DOS */
static int   call_mailer(char *, ENVELOPE *, BODY *);
static char *tidy_smtp_mess(char *);
#ifdef	EDTEST
static int   editor_screen(int, char *, char**, int *, ENVELOPE *, BODY *,
                           char **);
static void   draw_edit_body();
#endif	/* EDTEST */
FileTypes     file_type(void *, long);
unsigned char *rfc822_8bit(unsigned char *, unsigned long, unsigned long *);
unsigned char *rfc822_binary(unsigned char *, unsigned long, unsigned long *);
static void    text_part(char *, BODY *);
char          *mime_stats(BODY *);
void           mime_recur(BODY *);


#else
static void  parse_postponed_header();
#ifndef	DOS
static char *call_sendmail();
static int   call_system();
#endif	/* DOS */
static int   call_mailer();
static char *tidy_smtp_mess();
#ifdef	EDTEST
static int   editor_screen();
static void  draw_edit_body();
#endif	/* EDTEST */
FileTypes     file_type();
unsigned char *rfc822_8bit();
unsigned char *rfc822_binary();
static void    text_part();
char          *mime_stats();
void           mime_recur();
#endif


/*
 * Buffer to hold pointers into pine data that's needed by pico. 
 * Defined here so as not to take up room on the stack.  better malloc'd?
 */
static	PICO	pbuf;


/*----------------------------------------------------------------------
    Compose screen (not forward or reply). Set up envelope, call composer
  
   Args: pine_state -- The usual pine structure
 
  Little front end for the compose screen
  ---*/
void
compose_screen(pine_state)
     struct pine *pine_state;
{
    ps_global = pine_state;
    pine_state->next_screen = pine_state->prev_screen;
    compose_mail(NULL);
}



/*----------------------------------------------------------------------
     Format envelope for outgoing message and call editor

 Args:  given_to -- A address to send mail do (usually from command line 
                       invocation)
 
 If a "To" line is given format that into the envelope and get ready to call
           the composer
 If there's a message postponed, offer to continue it, and set it up

 Otherwise just fill in the outgoing envelope as blank
 ----*/
void 
compose_mail(given_to)
  char *given_to;
{
    ENVELOPE     *outgoing;
    BODY         *body;
    char         *textbuf, *p,*q, *fcc2, *file, *tmp_text,
                 fcc[MAXFOLDER+1], file_path[MAXPATH+1], *sig;
    PATMT        *attachments;

    dprint(1, (debugfile,
                 "\n\n    ---- COMPOSE SCREEN (not in pico yet) ----\n"));


    fcc2 = NULL;
    file_path[0] = '\0';
    attachments = NULL;

    build_path(file_path, ps_global->folders_dir, INTERRUPTED_MAIL);
    if(given_to != NULL || can_access(file_path, ACCESS_EXISTS) != 0 ||
       want_to(
         "Continue interrupted composition (answering \"n\" won't erase it)",
         'y', NULL, 0) == 'n') 
      file_path[0] = '\0';
 
    if(file_path[0] == '\0') {
        build_path(file_path, ps_global->folders_dir, POSTPONED_MAIL);
        if(given_to != NULL || can_access(file_path, ACCESS_EXISTS) != 0 ||
         want_to(
          "Continue postponed composition (answering \"No\" won't erase it)",
          'y', NULL, 0) == 'n') 
           file_path[0] = '\0';
    }


    if(file_path[0] != '\0') {
        /*============== Continue postponed compostion ==============*/

        file = read_file(file_path);
        if(file == NULL) {
            q_status_message2(1, 3,4, 
                "\007Error \"%s\" reading postponed composition in \"%s\"",
                              error_description(errno), file_path);
            dprint(1, (debugfile, "Error %s opening/reading/closing %s\n",
                       error_description(errno), file_path));

            return;
        }

        if(unlink(file_path) < 0){
            q_status_message2(1, 2, 4, 
                "Error \"%s\" unlinking postponed composition in \"%s\"",
                              error_description(errno), file_path);
            dprint(1, (debugfile, "Error %s unlinking %s\n",
                       error_description(errno), file_path));
        }

        /*--------- Parse header for the Fcc: field -----------*/
        parse_postponed_header(file, fcc, sizeof(fcc), &attachments);
        fcc2 = fcc;


        rfc822_parse_msg(&outgoing, &body, file, strlen(file), "", 0,
                          ps_global->maildomain, tmp_20k_buf);

        /*------- Fish out text --------------*/
        /*--- The text, it begins at the first blank line. RFC822
               defines a blank line to contain NO white space --*/
        for(p = file; *p ; p++) {
            if(*p == '\n' && *(p+1) == '\n')
              break;
            if(*p=='\r'  &&  *(p+1)=='\n'  &&  *(p+2)=='\r'  &&  *(p+3)=='\n')
              break;
        }
        if(*p == '\n')
          tmp_text = p+2;
        else if(*p == '\r')
          tmp_text = p+4;
        else
          tmp_text = "";
        body->type          = TYPETEXT;
        body->contents.text = (unsigned char *)cpystr(tmp_text);

        fs_give((void **)&file); 

    } else {
        /*=================  Compose new message ===============*/
        body         = mail_newbody();
        outgoing     = mail_newenvelope();

        if(given_to != NULL) {
            rfc822_parse_adrlist(&(outgoing->to), given_to,
                                               ps_global->maildomain);
        }
        outgoing->message_id = cpystr(generate_message_id(ps_global));

        build_path(file_path, ps_global->home_dir,
                   ps_global->VAR_SIGNATURE_FILE);
        if(!can_access(file_path, 0)) {
            sig = read_file(file_path);
            if(sig == NULL)
              q_status_message2(1, 2, 4,
                              "\007Error \"%s\" reading signature file \"%s\"",
                              error_description(errno), file_path);
        } else {
            sig = NULL;
        } 

        if(sig == NULL) {
            textbuf = fs_get(sizeof(char));
            textbuf[0] = '\0';
        } else {
            textbuf = fs_get(5 + strlen(sig));
            strcpy(textbuf, "\n\n");
            strcat(textbuf, sig);
            fs_give((void **)&sig);
        }
        /* Startout with a message of TYPETEXT, could become MULTIPART */
        body->type          = TYPETEXT;
        body->contents.text = (unsigned char *)textbuf;
    }

    ps_global->prev_screen = compose_screen;

    pine_send(outgoing,&body,"COMPOSE MESSAGE",fcc2,0,(void *)attachments,0);

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



/*----------------------------------------------------------------------
    Parse the postponed message for the Pine specific fields

Args:  text -- Text to parse for fcc and attachments refs
       fcc  -- Pointer to buffer to return fcc in 
       atmt -- Attachments structure to return result in

The current strategy on postponing a message with attachments or one that
is multipart is this:  If the parts are attachments of local files then just 
store a reference to the file in the postponed message file. If the 
parts are not local files and came about by MIME style forwarding, replying
or inclusion of some other way then we would like to store the 
whole part, contents and all. Currently this now done because the code
to parse a whole MIME format message is not written. The code to write
it out does work. Now when a message with attachments is postponed any
MIME style forwarding parts or such will be dropped and only references
to files attached will be included.
 ----*/
static void
parse_postponed_header(text, fcc, fcc_size, atmt)
     char   *text, *fcc;
     PATMT **atmt;
     int     fcc_size;
{
    register char *p, *q, *a;
    PATMT         *pa;
    char           tmp;

    *fcc = '\0';
    for(pa = *atmt; pa != NULL && pa->next != NULL; pa = pa->next);

    /*---- Loop until the end of the header ----*/
    for(p = text; *p && !(*p == '\n' && *(p+1) != '\n'); p++) {
        if(struncmp(p, "Attachment-file:", 16) == 0) {
            p += 16;
            while(*p && *p != '\n' && isspace(*p))
              p++;
            if(*p == '\n')
              continue; /* Blank field, skip this */

            /*---- Got a field, now allocate a structure for it -----*/
            if(pa == NULL) {
                /* empty list */
                *atmt = (PATMT *)fs_get(sizeof(PATMT));
                pa    = *atmt;
            } else {
                pa->next = (PATMT *)fs_get(sizeof(PATMT));
                pa       = pa->next;
            }
            pa->id = NULL;
            pa->description = cpystr("");
            pa->size = cpystr(""); /* BUG these should be NULL, 
                                      but pico can't handle NULL's */
            pa->next = NULL;
            a = p;
            while(*p && *p != '\n' && !isspace(*p))
              p++;
            tmp = *p;
            *p = '\0';
            pa->filename = cpystr(a);
            *p = tmp;
#ifdef X_NEVER
        } else if(struncmp(p, "Attachment-id:", 14) == 0) {
            if(pa != NULL){
                p += 14;
                while(*p && *p != '\n' && isspace(*p))
                  p++;
                if(*p == '\n')
                  continue; /* Blank field, skip this */
                a = p;
                while(*p && *p != '\n' && !isspace(*p))
                  p++;
                tmp = *p;
                *p = '\0';
                pa->id = cpystr(a);
                *p = tmp;
            }
#endif
        } else if(struncmp(p, "Attachment-description:", 23) == 0) {
            if(pa != NULL){
                p += 23;
                while(*p && *p != '\n' && isspace(*p))
                  p++;
                if(*p == '\n')
                  continue; /* Blank field, skip this */
                a = p;
                while(*p && *p != '\n')
                  p++;
                tmp = *p;
                *p = '\0';
                pa->description = cpystr(a);
                *p = tmp;
            }
        } else if(struncmp(p, "Fcc:", 4) == 0) {
            /*-- Skip past white space and field tag --*/
            p += 4; 
            while(*p && *p != '\n' && isspace(*p))
              p++;
            /*-- copy the result in --*/
            for(q=fcc; q < &fcc[fcc_size-1] && *p && *p!='\n' && !isspace(*p);)
              *q++ = *p++;
            *q = '\0';
        }

        /* Skip to end of line, what ever it was */
        while(*p != '\n')
          p++;  
    }
}
        
              
            

            
        
    


/*----------------------------------------------------------------------
     Prepare data structures for pico, call pico, then post message

  Args:  outgoing -- Partially formatted outgoing ENVELOPE
         textbuf  -- Address of text of outgoing message
         editor_title -- Title for anchor line in composer
         fcc      -- The file carbon copy field
         att      -- Possible list of attachment references
                     (declared void * so we don't have to include
                      pico.h everywhere else in Pine)

  Result: message is edited, then postponed, canceled or sent.

  Fields:
     remail                -
     return_path           -
     date                 added here
     from                 added here
     sender                -
     reply_to              -
     subject              passed in, edited and cannonized here
     to                   possibly passed in, edited and cannonized here
     cc                   possibly passed in, edited and cannonized here
     bcc                  edited and cannonized here
     in_reply_to          generated in reply() and passed in
     message_id            -
  
 Storage for these fields comes from anywhere outside. It is remalloced
 here so the composer can realloc them if needed. The copies here are also 
 freed here.


Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
with the first part TYPETEXT! All newlines in the text here also end with
CRLF.
  ----*/

void
pine_send(outgoing, body, editor_title, fcc, are_replying, att, nr_forward)
     ENVELOPE  *outgoing;  /* envelope for outgoing message */
     BODY     **body;   
     char      *editor_title, *fcc;
     void      *att;
     int        nr_forward;
{
    int   editor_result, result;
    char  *p;
    long  l;
    extern char version_buff[];

    dprint(1, (debugfile,"\n === send called === %d\n", nr_forward));

    pbuf.attachments = (PATMT *)att;

    /*----- Fill in a few general parts of the envelope ----*/
    if(outgoing->date == NULL)
      outgoing->date = fs_get(40); /* Date's always 30 or so */
    rfc822_date(outgoing->date);

    if(outgoing->subject == NULL) {
	outgoing->subject    = fs_get(1);
	outgoing->subject[0] = '\0';
    }

    pbuf.fccbuf = cpystr(fcc != NULL ? fcc : ps_global->VAR_DEFAULT_FCC);
    pbuf.fcclen = strlen(pbuf.fccbuf);

    if(outgoing->from == NULL) {
        outgoing->from             = mail_newaddr();
        outgoing->from->personal   = cpystr(ps_global->VAR_PERSONAL_NAME);
        outgoing->from->adl        = NULL;
        outgoing->from->mailbox    = cpystr(ps_global->VAR_USER_ID);
        outgoing->from->host       = cpystr(ps_global->maildomain);
        outgoing->from->error      = NULL;
        outgoing->from->next       = NULL;
    }
    outgoing->return_path      = rfc822_cpy_adr(outgoing->from);
        

    /*----------------------------------------------------------------------
       Loop calling the editor until everything goes well
     ----*/
    do {
        flush_status_messages();

        /*-----------------------------------------------------
          Convert the envelope and body to the string format that
            pico can edit
         */
        outgoing2strings(outgoing, *body, &pbuf.tobuf, &pbuf.ccbuf,
                         &pbuf.bccbuf, &pbuf.messagebuf, &pbuf.attachments);
        pbuf.messbuflen = strlen(pbuf.messagebuf);

	pbuf.pine_anchor = set_titlebar(editor_title, 0, FolderName, 0, 0, 0),
	pbuf.pine_version = version_buff;
	pbuf.tolen = strlen(pbuf.tobuf);
	pbuf.cclen = strlen(pbuf.ccbuf);
	pbuf.bcclen = strlen(pbuf.bccbuf);
	pbuf.subbuf = outgoing->subject;
	pbuf.sublen = strlen(pbuf.subbuf);

	pbuf.raw_io = Raw;
	pbuf.showmsg = display_message;
	pbuf.newmail = new_mail;
	pbuf.addrbook = addr_book;
	pbuf.folders = folders_for_fcc;
	pbuf.buildaddr = build_address;
	pbuf.keybinit = init_keyboard;
	pbuf.helper = helper;
	pbuf.alt_ed = ps_global->VAR_EDITOR;
	pbuf.ins_help = h_composer_ins;
	pbuf.search_help = h_composer_search;
	pbuf.to_help = h_composer_to;
	pbuf.cc_help = h_composer_cc;
	pbuf.bcc_help = h_composer_bcc;
	pbuf.fcc_help = h_composer_fcc;
	pbuf.subject_help = h_composer_subject;
	pbuf.attachment_help = h_composer_attachment;
	pbuf.browse_help = h_composer_browse;
	pbuf.composer_help = h_composer;


      	pbuf.pine_flags  = 0;
	pbuf.pine_flags |= (ps_global->can_suspend) ? P_SUSPEND : 0;
	pbuf.pine_flags |= (ps_global->use_fkeys) ? P_FKEYS : 0;
	pbuf.pine_flags |= ((ps_global->restricted || nr_forward) ? P_SECURE :
                                                                   0);
	pbuf.pine_flags |= (are_replying) ? P_REPLY : 0;
        pbuf.pine_flags |= ps_global->feature_level == Seasoned ? P_ADVANCED : 0;

        dprint(9, (debugfile, "flags: %x\n", pbuf.pine_flags));

        flush_status_messages();
        clear_cursor_pos();

	editor_result = pico(&pbuf);
        /*-- Text returned here has network EOL's, CRLF's that is --*/


        if(outgoing->subject != pbuf.subbuf){ 	/* subject string realloc'd */
	    outgoing->subject = pbuf.subbuf;
	}

        init_signals();        /* Pico has it's own signal stuff */
        mark_status_dirty();
        get_windsize(ps_global->ttyo); /* Might have changed, we won't know */
        clear_cursor_pos();


        /*-------------------------------------------------------------
          Turn strings back into structures
          ----*/
        strings2outgoing(outgoing, body, pbuf.tobuf, pbuf.ccbuf,
                         pbuf.bccbuf, pbuf.messagebuf, pbuf.attachments);

        fs_give((void **)&pbuf.tobuf);
        fs_give((void **)&pbuf.ccbuf);
        fs_give((void **)&pbuf.bccbuf);

        removing_trailing_white_space(pbuf.fccbuf);


        /*----- Message is edited, now decide what to do with it ----*/
        if(editor_result & COMP_CANCEL) {
            /*================ Cancel the message ===================*/
            q_status_message(0, 1, 3, "Message Aborted");
            break;

        } else if(editor_result & COMP_SUSPEND) {
            /*================= Postpone message ====================*/
            PART *p_tmp;
            BODY *b_tmp;

            if((*body)->type != TYPETEXT) {
                /* Leave this question off until we can actually send something
                   with MIME parts that aren't just attachments. Right now the
                   attachments will be postponed by reference */
                if(want_to("Attachments will not be included in postponed message. Postpone anyway",'n', NULL, 0) == 'n')
                  continue;

                /* Hide all but first text part */
                p_tmp = (*body)->contents.part->next;
                (*body)->contents.part->next = NULL;
                b_tmp = &((*body)->contents.part->body);
            } else {
                b_tmp = *body;
            }

            result = append_message2(POSTPONED_MAIL, outgoing, b_tmp, NULL,
                                     NULL,AppendPostpone,0,0,0,0,
	                             pbuf.fccbuf, (void *)pbuf.attachments,
                                     NULL);

            if((*body)->type != TYPETEXT) {
                (*body)->contents.part->next = p_tmp;
            }
            if(result< 0) {
                q_status_message(1, 1, 3, "\007Continuing composition; message not postponed or sent");
                display_message('x');
                sleep(3);
                continue; /* postpone failed, jump back in to composer */
            } else {
                q_status_message(1, 1,3,
                          "Composition postponed. Select Compose to resume.");
                break; /* postpone went OK, get out of here */
            }

        } else if(editor_result & COMP_GOTHUP) {
            /*----- Disconnected, save the interrupted composition ----*/
            result = append_message2(INTERRUPTED_MAIL,outgoing,*body,NULL,NULL,
                                     AppendInterrupted,0,0,0,0,
                                     pbuf.fccbuf, pbuf.attachments, NULL);
            dprint(1, (debugfile, "Save composition on HUP %s\n",
                       result < 0 ? "FAILED" : "succeeded"));
            hup_signal(); /* Do what we normally do on SIGHUP */

        } else {
            /*------ Must be sending mail ! -----*/

            if(check_addresses(outgoing)) {
                /*--- Addresses didn't check out---*/
               display_message('x');
               sleep(3);
               continue;
            }

            if(call_mailer(pbuf.fccbuf, outgoing, *body))
              break;

            /*--- Send failed, loop on back ----*/
        }

    } while(1);

    fs_give((void **)&pbuf.fccbuf);
    display_message('\0');
}



/*----------------------------------------------------------------------
   Check for addresses the user is not permitted to send to, or probably
   doesn't want to send to
   
Returns: 0 if OK, and 1 if the message shouldn't be sent

Alls queues a message indicating what happened
  ---*/

check_addresses(e)
  ENVELOPE *e;
{
    ADDRESS *a;

    /*---- Restricted mode can only send mail back -----*/
    if(ps_global->restricted) {
        if(!address_is_us(e->to, ps_global) ||
          (e->cc != NULL &&
           !address_is_us(e->cc,ps_global)) ||
          (e->bcc != NULL &&
           !address_is_us(e->bcc,ps_global))){
            q_status_message(1, 2, 4,
                             "Restricted demo version of Pine. You may only send mail to yourself");
            return(1);
        }
    }

    /*---- Is he/she trying to send mail to the mailer-daemon ----*/
    for(a = e->to; a != NULL; a = a->next) 
      if(strucmp(a->mailbox, "mailer-daemon") == 0)
        goto really_send;
    for(a = e->cc; a != NULL; a = a->next) 
      if(strucmp(a->mailbox, "mailer-daemon") == 0)
        goto really_send;
    for(a = e->bcc; a != NULL; a = a->next) 
      if(strucmp(a->mailbox, "mailer-daemon") == 0)
          goto really_send;

    return(0); /* All is OK */

  really_send:
    if(want_to("Really send this message to the mailer-daemon", 'n',
               (char **)NULL, 0) == 'n')
      return(1);
    else
      return(0);
}
        


/*----------------------------------------------------------------------
     Call the mailer, SMTP, sendmail or whatever
     
Args: fcc      -- The fcc to write a copy of the message to
      envelope -- The full envelope structure
      body     -- The full body of the message including text
      header   -- a text version of the header LF format
      text     -- Text of the message if text only, LF format

Returns: 0 if failed, 1 if succeeded
----*/      
static int
call_mailer(fcc, envelope, body)
     char     *fcc;
     ENVELOPE *envelope;
     BODY     *body;
{
    SMTPSTREAM  *sstream;
    char        *list[2], error_buf[100], *error_mess,filename[MAXPATH+1];
    long         start_of_append;
    ADDRESS     *a;
    int          addr_error_count;

#define MAX_ADDR_ERROR 2  /* Only display 2 address errors */

    q_status_message(0, 2, 4, "Sending mail.....");
    display_message('x'); 

    /*---- recompute message-id to encode body info stats ----*/
    update_message_id(envelope, mime_stats(body));


    /*----- write message to sent-mail file ----*/
    if(*fcc && append_message2(fcc, envelope, body, ps_global->VAR_USER_ID,
                    &start_of_append, AppendFcc,0,1,0,0,NULL,NULL, NULL) < 0) {
        return(0);
    }

    /*----- dump message off to the mailer ----*/
    if(ps_global->VAR_SMTP_SERVER!=NULL&&strlen(ps_global->VAR_SMTP_SERVER)>0){
       /*---------- SMTP ----------*/
        list[0]    = ps_global->VAR_SMTP_SERVER;
        list[1]    = NULL;
        error_mess = NULL;

        ps_global->noshow_error = 1;
#ifdef DEBUG
        sstream = smtp_open(list, debug);
#else
        sstream = smtp_open(list, 0L);
#endif
        ps_global->noshow_error = 0;
        if(sstream != NULL) {
            dprint(1, (debugfile, "Got SMTP server %s open\n",
                       ps_global->VAR_SMTP_SERVER));
            if(smtp_mail(sstream, "MAIL", envelope, body) != 1) {
                sprintf(error_buf,
                        "\007Mail not sent. Transfer protocol error: %s",
                        sstream->reply == NULL ? "" :
                        sstream->reply);
                dprint(1, (debugfile, error_buf));
                addr_error_count = 0;
                for(a = envelope->to; a != NULL; a = a->next){
                    if(a->error != NULL) {
                        if(addr_error_count++ < MAX_ADDR_ERROR) {
                            if(error_mess != NULL)
                              q_status_message(1, 4, 7, error_mess);
                            sprintf(error_buf, "\007Mail not sent: %s",
                                    tidy_smtp_mess(a->error));
                            error_mess = error_buf;
                        }
                        dprint(1,(debugfile,"Send Error: \"%s\"\n", a->error));
                    }
                }
                for(a = envelope->cc; a != NULL; a = a->next){
                    if(a->error != NULL) {
                        if(addr_error_count++ < MAX_ADDR_ERROR) {
                            if(error_mess != NULL)
                              q_status_message(1, 4, 7, error_mess);
                            sprintf(error_buf, "\007Mail not sent: %s",
                                    tidy_smtp_mess(a->error));
                            error_mess = error_buf;
                        }
                        dprint(1,(debugfile,"Send Error: \"%s\"\n", a->error));
                    }
                }
                for(a = envelope->bcc; a != NULL; a = a->next){
                    if(a->error != NULL) {
                        if(addr_error_count++ < MAX_ADDR_ERROR) {
                            if(error_mess != NULL)
                              q_status_message(1, 4, 7, error_mess);
                            sprintf(error_buf, "\007Mail not sent: %s",
                                    tidy_smtp_mess(a->error));
                            error_mess = error_buf;
                        }
                        dprint(1,(debugfile,"Send Error: \"%s\"\n", a->error));
                    }
                }

                if(error_mess == NULL)
                  error_mess = error_buf;

            } 
            smtp_close(sstream);
        } else  {
            sprintf(error_buf,"\007Error connecting to mail server: %.60s",
                    ps_global->c_client_error);
            dprint(1, (debugfile, error_buf));
            error_mess = error_buf;
        }
    } else {
#ifdef	DOS
        q_status_message(0, 4, 7, "PC's gotta dump this to SMTP server!!");
	return(0);
#else
        /*----- Send mail ------*/
        error_mess = call_sendmail(envelope, body);
#endif
    }

    /*-------- Did message make it ? ----------*/
    if(error_mess != NULL) {
        /*---- Error sending mail -----*/
        q_status_message(1, 4, 7, error_mess);
        /* Back out append */
        strcpy(filename, fcc);
        expand_foldername(filename);
#ifndef	DOS
        truncate(filename, start_of_append);
#endif
        return(0);
    }
    return(1);
}



#ifndef	DOS	
/* ----------------------------------------------------------------------
      Fork off mailer process and pipe the message into it

  Args: envelope -- The envelope for the BCC and debugging
        header   -- The text of the message header
        text     -- The message text
     
   Run the mailer process and pipe the message down to it. Using -t
   option for sendmail so it reads the header and decides what to do.
    Returns string error message or NULL if all is OK
   For now this only drives sendmail.

  ----*/
static char *
call_sendmail(envelope, body)
     ENVELOPE *envelope;
     BODY     *body;
{
    char        mail_cmd[MAXPATH+1], *to;
    static char error_mess[100];
    char       *tmpfile;
    int         rv;

    dprint(1, (debugfile, "=== call_mailer ===\n"));

    tmpfile = temp_file_name("/tmp", "pine-send");

    rv = append_message2(tmpfile, envelope, body, NULL, NULL, AppendSendmail,
                    0, 0, 0, 0, NULL, NULL, NULL);
    if(rv < 0)
      goto io_error;

    sprintf(mail_cmd, "(( %s %s ; rm -rf %s) & )< %s",
            SENDMAIL, SENDMAILFLAGS, tmpfile, tmpfile);

    dprint(6, (debugfile, "Send command \"%s\"\n", mail_cmd));

    (void)call_system(mail_cmd);


    dprint(1, (debugfile, "\n***** MAIL SENT ******\n"));
    to = pretty_addr_string("To: ", envelope->to, "");
    dprint(1, (debugfile, "%s", to));
    dprint(1, (debugfile, "Subject: %s\n", envelope->subject != NULL ?
               envelope->subject : ""));
    dprint(1, (debugfile, "Message ID: %s\n\n", envelope->message_id));
    fs_give((void *)&to);

    return(NULL);

  io_error:
    sprintf(error_mess,"\007Error starting mailer process: %s",
                error_description(errno));
    dprint(1, (debugfile, "Error writing %s: %s\n", tmpfile,
                error_description(errno)));
    return(error_mess);
}



#include <sys/wait.h>

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

  ----*/
static
call_system(command)
     char *command;
{
    SigType (*hsig)(), (*isig)(), (*qsig)();
    int status, pid, r;

    dprint(9, (debugfile, "Command: \"%s\"\n", command));

    if((pid = vfork()) == 0){
        execl("/bin/sh", "sh", "-c", command, 0);
        _exit(-1);
    }
    dprint(9, (debugfile, "PID: %d\n", pid));
    if(pid == -1) {
        q_status_message1(1, 2, 3, "Error creating mailer process: %s\n",
                         error_description(errno));
        return(-1);
    }
    isig = signal(SIGINT, SIG_IGN);
    qsig = signal(SIGQUIT, SIG_IGN);
    hsig = signal(SIGHUP, SIG_IGN);
#ifdef HAVE_WAIT_UNION
    while((r =  wait((union wait *)&status)) && r != -1 && r != pid);
#else
    while((r =  wait(&status)) && r != -1 && r != pid);
#endif
    signal(SIGINT,  isig);
    signal(SIGHUP,  hsig);
    signal(SIGQUIT, qsig);
    dprint(9, (debugfile, "STATUS: %d\n", status));
    return(0);
}
#endif	/* !DOS */


/*----------------------------------------------------------------------
    Remove the leading digits from SMTP error messages
 -----*/
static char *
tidy_smtp_mess(error)
     char *error;
{
    static char mess[200];

    while(isdigit(*error))
      error++;
    while(isspace(*error))
      error++;

    strncpy(mess, error, sizeof(mess) - 1);
    mess[sizeof(mess)] = '\0';
    return(mess);
}

        
    
outgoing2strings(env, bod, to, cc, bcc, text, pico_a)
     ENVELOPE *env;
     BODY     *bod;
     char     **to, **cc, **bcc, **text;
     struct pico_atmt **pico_a;
{
    PART *part;
    PATMT *pa;
    char   size[25], *type;

    if(bod->type == TYPETEXT){
        *text = (char *)bod->contents.text;
    } else if(bod->type == TYPEMULTIPART){
        if(bod->contents.part->body.type != TYPETEXT) {
            panic1("Can't compose message with first type %s",
                   int2string(bod->contents.part->body.type));
        }
        *text = (char *)bod->contents.part->body.contents.text;

        for(pa = *pico_a; pa != NULL; pa = pa->next);
        for(part = bod->contents.part->next; part != NULL; part = part->next) {
            if(pa == NULL) {
                /* empty list */
                *pico_a = (PATMT *)fs_get(sizeof(PATMT));
                pa = *pico_a;
            } else {
                pa->next = (PATMT *)fs_get(sizeof(PATMT));
                pa = pa->next;
            }
            pa->description = part->body.description == NULL ? cpystr("") : 
                                              cpystr(part->body.description);
            
            type         = type_desc(part->body.type,
                                     part->body.subtype,part->body.parameter,0);
            pa->filename = fs_get(strlen(type) + 4);
            sprintf(pa->filename, "[%s]", type);
            pa->flags    = A_FLIT;
            pa->size     = cpystr(byte_string(part->body.size.bytes));
            if(part->body.id == NULL)
              part->body.id = cpystr(generate_message_id(ps_global));
            pa->id       = cpystr(part->body.id);
            pa->next     = NULL;
        }
    } else {
        panic1("Can't compose message of MIME type %s\n",
               int2string(bod->type));
    }
        

    /*------------------------------------------------------------------
       Malloc strings to pass to composer editor because it expects
       such strings so it can realloc them
      -----------------------------------------------------------------*/
    *to  = addr_list_string(env->to);
    *cc  = addr_list_string(env->cc);
    *bcc = addr_list_string(env->bcc);
}



strings2outgoing(env, bod, to, cc, bcc, text, attach)
     ENVELOPE *env;
     BODY    **bod;
     char    *to, *cc, *bcc, *text;
     struct  pico_atmt *attach;
{
    char *maildomain;

    maildomain = ps_global->maildomain;

    mail_free_address(&(env->to));
    env->to = NULL;
    /* rfc822_parse... doesn't like trailing space on it's addresses;
       it gives "junk at end of address" messages. Pico always 
       seems to leave a space so we hack it off here.
     */
    removing_trailing_white_space(to);
    rfc822_parse_adrlist(&(env->to), to, maildomain);

    if(env->to != NULL) {
       dprint(9, (debugfile, "to->personal %x\n", env->to->personal));
       dprint(9, (debugfile, "to->route_list %x\n", env->to->adl));
       dprint(9, (debugfile, "to->mailbox %x\n", env->to->mailbox));
       dprint(9, (debugfile, "to->host %x\n", env->to->host));
       dprint(9, (debugfile, "to->next %x\n", env->to->next));
       dprint(4, (debugfile, "cannonized To \"%s\"\n",
                addr_list_string(env->to)));
    }

    mail_free_address(&(env->cc));
    env->cc = NULL; 
    removing_trailing_white_space(cc);
    rfc822_parse_adrlist(&(env->cc), cc, maildomain);


    mail_free_address(&(env->bcc));
    env->bcc = NULL;
    removing_trailing_white_space(bcc);
    rfc822_parse_adrlist(&(env->bcc), bcc, maildomain);

    create_message_body(bod, text, attach);
    rfc822_encode_body(env, *bod);
}



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

 The head of the body list here is always either TEXT or MULTIPART. It may be
changed from TEXT to MULTIPART if there are attachments to be added
and it is not already multipart. 
  ----*/
create_message_body(b, text, attach)
     BODY            **b;
     char             *text;
     struct pico_atmt *attach;
{
    PART             *p, *p_trail;
    struct pico_atmt *pa;
    BODY             *b1;
    void             *file_contents;
    long              file_len, new_len;
    char              full_filename[MAXPATH + 1];
    int               is_gif, is_tiff, is_ps;
    extern char      *read_binary_file();

    if((*b)->type == TYPETEXT && attach == NULL) {
        text_part(text, *b);
        return;
    }

    if((*b)->type == TYPETEXT) {
        /*-- Current type is text, but there are attachments to add --*/
        /*-- Upgrade to a TYPEMULTIPART --*/
        b1                                  = (BODY *)mail_newbody();
        b1->type                            = TYPEMULTIPART;
        b1->contents.part                   = mail_newbody_part();
        b1->contents.part->body             = **b;
        b1->contents.part->body.subtype     = cpystr((*b)->subtype);
        b1->contents.part->body.id          = cpystr((*b)->id);
        b1->contents.part->body.description = cpystr((*b)->description);
        b1->contents.part->body.parameter   = copy_parameters((*b)->parameter);
        (*b)->contents.text                 = NULL;
        mail_free_body(b);
        *b = b1;
    }

    /*-- Now type must be MULTIPART with first part text --*/
    text_part(text, &((*b)->contents.part->body));

    /*------ Go through the parts list remove those to be deleted -----*/
    for(p = p_trail = (*b)->contents.part->next; p != NULL;) {
        for(pa = attach; pa != NULL; pa = pa->next) {
            if(pa->id == NULL)
              continue;
            if(p->body.id == NULL || strcmp(pa->id, p->body.id) == 0)
              break; /* Found it */
        }
        if(pa == NULL) {
            /* attachment wasn't in the list; zap it */
            if(p == (*b)->contents.part->next) {
                /* Beginning of list */
                (*b)->contents.part->next = p->next;
                p->next = NULL;  /* Don't free the whole chain */
                mail_free_body_part(&p);
                p = p_trail = (*b)->contents.part->next;
            } else {
                p_trail->next = p->next;
                p->next = NULL;  /* Don't free the whole chain */
                mail_free_body_part(&p);
                p = p_trail->next;
            }
        } else {
            p_trail = p;
            p       = p->next;
        }
    }

    /*---------- Now add any new attachments ---------*/
    for(p = (*b)->contents.part ; p->next != NULL; p = p->next);
    for(pa = attach; pa != NULL; pa = pa->next) {
        if(pa->id != NULL)
          continue; /* Has an ID, it's old */
#ifndef	DOS
        strcpy(full_filename, pa->filename);
        if(full_filename[0] == '~') {
            if(fnexpand(full_filename, sizeof(full_filename)) == NULL) {
                char *p = strindex(full_filename, '/');
                if(p != NULL)
                  *p = '\0';
                q_status_message1(1, 1,3,
                    "\007Error expanding file name: \"%s\" unknown user",
    	            full_filename);
                display_message('x');
                continue;
            }
        } else if(full_filename[0] != '/') {
            build_path(full_filename, ps_global->home_dir, pa->filename);
        }
        file_contents = (void *)read_file(full_filename);
#else
        file_contents = read_binary_file(pa->filename);
#endif
        if(file_contents == NULL)  {
            q_status_message2(1, 3, 4,
                              "\007Error \"%s\", couldn't attach file \"%s\"",
                              error_description(errno), pa->filename);
            display_message('x');
            continue;
        }
        file_len = file_size(pa->filename);

        is_gif  = !strncmp(file_contents, "GIF87", 5); /* A hack for fun */
        is_tiff = !strncmp(file_contents, "MM", 2) ||
                  !strncmp(file_contents, "II", 2);
        is_ps   = !strncmp(file_contents, "#!PS", 4);
        
        p->next                      = mail_newbody_part();
        p                            = p->next;
        p->body.id                   = cpystr(generate_message_id(ps_global));
        p->body.type                 = is_gif || is_tiff ? TYPEIMAGE:
                                                           TYPEAPPLICATION;
        p->body.subtype              = cpystr(is_gif ?  "GIF" :
                                              is_tiff ? "X-TIFF":
                                              is_ps   ? "PostScript" :
                                                        "octet-stream");
        p->body.description          = cpystr(pa->description);
        p->body.parameter            = mail_newbody_parameter();
        p->body.parameter->attribute = cpystr("name");
        p->body.parameter->value     = cpystr(last_cmpnt(pa->filename));

        switch(file_type(file_contents, file_len)) {
          case IsText:
            p->body.encoding        = ENC7BIT;
            p->body.contents.text   = (unsigned char *)lf2crlf(file_contents);
            fs_give((void **)&file_contents);
            p->body.size.bytes = strlen((char *)p->body.contents.text);
            break;

          case IsText8:
            p->body.encoding        = ENC8BIT;
            p->body.contents.text   = (unsigned char *)lf2crlf(file_contents);
            fs_give((void **)&file_contents);
            p->body.size.bytes = strlen((char *)p->body.contents.text);
            break;

          case IsBinary:
            p->body.encoding        = ENCBINARY;
            p->body.contents.binary = (void *)file_contents;
            /*--- The base 64 encoding will put CRLF on this part ---*/
            p->body.size.bytes = file_len;
            break;
        }

        p->next = NULL;
        pa->id = cpystr(p->body.id);
    }
}



/*----------------------------------------------------------------------
     Set up encoding and charset parameter for the text part of
  the message
  ----*/
static void
text_part(text, body)
     char *text;
     BODY *body;
{
    FileTypes         text_type;
    PARAMETER        *pm;

    text_type =  file_type(text, strlen(text));

    body->contents.text  = (unsigned char *)text;
    body->encoding       = text_type == IsText ? ENC7BIT : ENC8BIT;
    body->size.bytes     = strlen(text);

    if(body->parameter == NULL) {
        pm = body->parameter = mail_newbody_parameter();
        body->parameter->attribute = cpystr("charset");
    } else {
        for(pm = body->parameter;
            strucmp(pm->attribute, "charset") && pm->next != NULL;
            pm = pm->next);
        if(strucmp(pm->attribute, "charset") != 0) {
            pm->next = mail_newbody_parameter();
            pm = pm->next;
            pm->attribute = cpystr("charset");
        }
    }
    if(struncmp(ps_global->VAR_CHAR_SET, "iso-8859-", 9) == 0 &&
       text_type == IsText)
      pm->value = cpystr("US-ASCII");
    else
      pm->value = cpystr(ps_global->VAR_CHAR_SET == NULL ? "US-ASCII" :
                                                 ps_global->VAR_CHAR_SET);
    return;
}

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

Returns IsText:
  If lines are less than 500 characters and thereis nothing with the 
  8th bit on

Returns IsText8:
  If lines are less than 500 characters and less than 10% of characters
  have 8th bit on

Returns IsBinary
  All other cases;

 ---*/
FileTypes   
file_type(f, len)
     void *f;
     long  len;
{
    long      max_line, eight_bit_chars;
    char     *p, *line_start;
    FileTypes rv;

    max_line        = 0L;
    line_start      = (char *)f;
    eight_bit_chars = 0L;
    
    for(p = (char *)f; p < &(((char *)f)[len]); p++) {
        if(*p == '\n') {
            max_line = max(max_line, p - line_start);
            line_start = p;
        } else if(*p & 0x80){
            eight_bit_chars++;
        }
    }
    if(max_line > 500L)
      rv = IsBinary;  /* Very long lines */
    else if(eight_bit_chars == 0L)
      rv = IsText;    /* short lines, no 8 bit */
    else if((eight_bit_chars * 100L)/len < 10L)
      rv = IsText8;   /* Short lines, < 10% 8 bit chars */
    else
      rv = IsBinary;   /* Short lines, > 10% 8 bit chars */
    
    dprint(4, (debugfile,
               "file_type -- len: %ld max_line: %ld  8 bit chars: %ld  %s\n",
               len, max_line, eight_bit_chars,
               rv==IsText ? "IsText" : rv==IsBinary ? "IsBinary" : "IsText8"));
    return(rv);
}




bezerk_puts(stream_x, string)
     void   *stream_x;
     char   *string;
{
    FILE *stream = (FILE *)stream_x;
    char *p;

    /* Need error checking */
    for(p = string; *p; p++) {
        if(*p == 'F' && strncmp(p, "From ", 5) == 0) {
            fputs(">From ", stream);
            p += 4;
        } else if(*p == '\015' && *(p+1) == '\012') {
            putc('\n', stream);
            p++;
        } else {
            putc(*p, stream);
        }
    }
}



/*----------------------------------------------------------------------
    Output routine for rfc822_output_body

Normally this is used for writing a message down a TCP stream, but
writing a MIME body out is complex business so we use it here.
We must convert the network CRLF to the local new line convention, in this
case on UNIX it's LF.
 ----*/
unix_puts(stream_x, string)
     void   *stream_x;
     char   *string;
{
    FILE *stream = (FILE *)stream_x;
    char *p;

    /* Need error checking */
    for(p = string; *p; p++) {
        if(*p == '\015' && *(p+1) == '\012') {
            putc('\n', stream);
            p++;
        } else {
            putc(*p, stream);
        }
    }
}



/*----------------------------------------------------------------------
    General function to write a message to Berkeley format folder

Args:  folder -- The name of the folder
       env    -- The envelope (header) of the message to save
       bod    -- The message body
       soa    -- File pos of start of the append so it can be undone
       type   -- Type of save operation
       a_flag -- Message status flag, answered
       s_flag -- Message status flag, seen
       f_flag -- Message status flag, flagged
       num    -- Message number for babble message
       a      -- Attachemnts for Postponing
       d      -- Date message was received

Returns: 0 if susccessful, -1 if not

The message can be written in five slightly different formats:

AppendMessage --
  Includes: separator, header, status, body
  gives succes message, confirms if folder dosen't exist
  *** This isn't used for a number of reasons, one being it doesn't carry
       "Recieved ...." lines along

AppendFcc:
  Includes: separator, header, Bcc, status, body
  gives success message, confirms if folder doesn't exist

AppendPostponed:
  Includes: Fcc, Bcc, header, body
  confirms if folder *does* exist

AppendInterrupted:
  Includes: Fcc, Bcc, header, body
  overwrites existing folder without confirmation

AppendSendmail:
  Includes: Bcc, header, body
  overwrites existing folder without confirmation
----*/           
append_message2(folder,env,bod,from,soa,type,a_flag,s_flag,f_flag,num,fcc,aa,d)
     char       *folder, *from, *fcc, *d;
     ENVELOPE   *env;
     BODY       *bod;
     long       *soa, num;
     AppendTypes type;
     int         a_flag, s_flag, f_flag;
     void       *aa;
{
    char   filename[MAXPATH+1], number_string[20], question[100];
    int    appending, rv, save_errno;
    FILE  *folder_stream;
    long   now, start_of_append;
    PATMT *a = (PATMT *)aa;

    strcpy(filename, folder);

    if (! expand_foldername(filename)) {
        /* expand_foldername generates it's own error message */
        dprint(1, (debugfile,
              "Error: Failed on expansion of filename %s (save)\n", filename));
        return(-1);
    }

    if(can_access(filename, ACCESS_EXISTS) != 0) {
        /*---- File doesn't exist ----*/
        if(type == AppendFcc || type == AppendMessage) {
            sprintf(question, "Folder \"%.40s\" doesn't exist. Create it",
                       pretty_fn(folder));
            if(want_to(question, 'y', (char **)NULL, 0) != 'y') {
                if(type == AppendMessage)
                  q_status_message(0, 1, 3, "\007Save message aborted");
                return(-1);
            }
            appending = 0;
        } else {
            appending = 0;
        }
    } else if(file_size(filename) > 0) {
        /*--- Folder exists and has stuff in it ----*/
        if(type == AppendFcc || type == AppendMessage) {
            appending = 1;
        } else if(type == AppendPostpone) {
            if(want_to("One message already postponed. Overwrite it", 'n',
                        (char **)NULL, 0) == 'n')
              return(-1);
            appending = 0;
        } else {
            /* Interrupted message, just overwrite the previously
               interrupted message; they've had a shot at continuing it
                                    OR
               Writing out temp file for sendmail 
             */
            appending = 0;
        }
    }

    folder_stream = fopen(filename, appending ? "a" : "w");

    if(folder_stream == NULL) {
        q_status_message2(1, 2, 4, "\007Error opening folder \"%s\", %s",
                         pretty_fn(filename), error_description(errno));
        return(0);
    }

    start_of_append = ftell(folder_stream);
    if(soa != NULL)
      *soa = start_of_append;

    if(type == AppendMessage || type == AppendFcc) {
        /*--- Output the mail folder message separaptor ----*/
        now = time(0);
        rv = fprintf(folder_stream, "From %s %s%s", from,
                     d != NULL ?  d   : ctime(&now),
                     d != NULL ? "\n": ""); /* ctime includes a \n */
        if (rv == EOF) 
           goto done;
    } else if(type == AppendPostpone || type == AppendInterrupted) {
        while(a != NULL) {
            if(a->flags & A_FLIT) {
                a = a->next;
                continue;
            }
            rv = fprintf(folder_stream, "Attachment-file: %s\n", a->filename);
            if (rv == EOF) 
              goto done;
#ifdef X_NEVER
            rv = fprintf(folder_stream, "Attachment-id: %s\n", a->id);
            if (rv == EOF) 
              goto done;
#endif
            rv = fprintf(folder_stream, "Attachment-description: %s\n",
                         a->description);
            if (rv == EOF) 
              goto done;
            a = a->next;
        }
        if(fcc != NULL) {
            rv = fprintf(folder_stream, "Fcc: %s\n", fcc);
            if (rv == EOF) 
              goto done;
        }
    }
    if(type == AppendPostpone || type==AppendInterrupted ||
       type == AppendSendmail || type == AppendFcc){
        /*--- Output the BCC ---*/
        if(env->bcc != NULL)  {
            rv = fprintf(folder_stream,"Bcc: %s\n",addr_list_string(env->bcc));
            if (rv == EOF) 
              goto done;
        }
    }

    rfc822_encode_body(env, bod);

    /*---- Output the header ----*/
    rfc822_header(tmp_20k_buf, env, bod);
    removing_trailing_white_space(tmp_20k_buf);
    strcat(tmp_20k_buf, "\r\n");
    rv = unix_puts(folder_stream, tmp_20k_buf);
    if(rv == EOF)
      goto done;
    if(type == AppendFcc || type == AppendMessage) {
        /* Messages are never new and deleted flag isn't ever copied */
        rv = fprintf(folder_stream,"Status: O%s\nX-Status: %s%s\n",
                     s_flag ? "R" : " ", 
                     f_flag ? "F" : " ",
                     a_flag ? "A" : " ");
        if(rv == EOF)
          goto done;
    }
    fputs("\n", folder_stream); /* blank line to separate the header */


    /*---- Output the message body ----*/
    rv = rfc822_output_body(bod,
                            type == AppendMessage || type == AppendFcc ?
                              bezerk_puts : unix_puts,
                            folder_stream);

    if (rv == EOF) 
      goto done;

    fputs("\n", folder_stream); /* Bezerk format requires extra line */

    rv = fclose(folder_stream);
    folder_stream == NULL;

  done:
    if(rv == EOF) {
        if(folder_stream != NULL)
          fclose(folder_stream);
        save_errno = errno;
#ifndef	DOS
        truncate(filename, start_of_append);
#endif
        q_status_message3(1, 2, 4, "\007Error \"%s\" writing%s \"%s\"",
                          error_description(save_errno),
                          (type == AppendMessage || type == AppendFcc) ?
                             "folder" : "",
                          pretty_fn(folder));
        dprint(1, (debugfile, "Error writing %s: %s\n",
                   filename, error_description(save_errno)));
        return(-1);
    } else {
        if(type == AppendMessage)
          sprintf(number_string, " %ld", num);
        else
          number_string[0] = '\0';
        if(type == AppendMessage || type == AppendFcc)
          q_status_message4(0, 1,3, "Message%s %s to folder \"%s\"%s",
                            number_string,
                            appending ? "appended" : "saved",
                            pretty_fn(folder),
                            type == AppendMessage &&
                              !ps_global->mail_stream->readonly ?
                              " and marked for deletion" : "");
        return(0);
    }
}



/*----------------------------------------------------------------------
  Insert the addition into the message id before first "@"
  ----*/
update_message_id(e, addition)
     ENVELOPE *e;
     char *addition;
{
    char *p, *q, *r, *new;

    new = fs_get(strlen(e->message_id) + strlen(addition) + 5);
    for(p = new, q = e->message_id; *q && *q != '@'; *p++ = *q++);
    *p++ = '-';
    for(r = addition; *r ; *p++ = *r++);
    for(; *q; *p++ = *q++);
    *p = *q;
    fs_give((void **)&(e->message_id));
    e->message_id = new;
}




static struct mime_count {
    long size;
    int text_parts;
    int image_parts;
    int message_parts;
    int application_parts;
    int audio_parts;
    int  video_parts;
} mc;

char *
mime_stats(body)
     BODY *body;
{
    static char id[10];
    mc. size = 0;
    mc.text_parts = 0;
    mc.image_parts = 0;
    mc.message_parts = 0;
    mc.application_parts = 0;
    mc.audio_parts = 0;
    mc.video_parts = 0;

    mime_recur(body);

    mc.text_parts        = min(8, mc.text_parts );
    mc.image_parts       = min(8, mc.image_parts );
    mc.message_parts     = min(8, mc.message_parts );
    mc.application_parts = min(8, mc.application_parts );
    mc.audio_parts       = min(8, mc.audio_parts );
    mc.video_parts       = min(8, mc.video_parts );


    id[0] = encode_bits(logbase2(mc.size));
    id[1] = encode_bits(mc.text_parts);
    id[2] = encode_bits(mc.message_parts);
    id[3] = encode_bits(mc.application_parts);
    id[4] = encode_bits(mc.video_parts);
    id[5] = encode_bits(mc.audio_parts);
    id[6] = encode_bits(mc.image_parts);
    id[7] = '\0';
    return(id);
}
    


/*----------------------------------------------------------------------
   ----*/
void
mime_recur(body)
     BODY *body;
{
    PART *part;
    switch (body->type) {
      case TYPETEXT:
        mc.text_parts++;
        break;
      case TYPEIMAGE:
        mc.image_parts++;
        break;
      case TYPEMESSAGE:
        mc.message_parts++;
        break;
      case TYPEAUDIO:
        mc.audio_parts++;
        break;
      case TYPEAPPLICATION:
        mc.application_parts++;
        break;
      case TYPEVIDEO:
        mc.video_parts++;
        break;
      case TYPEMULTIPART:
        for(part = body->contents.part; part != NULL; part = part->next) 
          mime_recur(&(part->body));
        break;
    }
    if(body->type != TYPEMULTIPART)
      mc.size += body->size.bytes;
}
        
int        
encode_bits(bits)
     int bits;
{
    if(bits < 10)
      return(bits + '0');
    else if(bits < 36)
      return(bits - 10 + 'a');
    else if (bits < 62)
      return(bits - 36 + 'A');
    else
      return('.');
}

int
logbase2(x)
     long x;
{
    int base2;

    for(base2 = 0; x != 0; base2++)
      x /= 2;
    return(base2);
}



     
