static char rcsid[] = "@(#)$Id: mime.c,v 1.50 2006/04/10 09:03:21 hurtta Exp $";
/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.50 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 ******************************************************************************
 *  The Elm Mail System 
 *
 *			Copyright (c) 1988-1992 USENET Community Trust
 *			Copyright (c) 1986,1987 Dave Taylor
 *****************************************************************************/


#include "def_elm.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"mime");

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif       

static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str) 
     char *str;
{
    return (unsigned char *)str;
}

void clear_mime_send_info(mime_info)
     mime_send_t *mime_info;
{
    mime_info->raw_level        = mailer_7bit;

    mime_info->encoding_top     = ENCODING_7BIT;
    mime_info->TYPE_opts_top    = NULL;
    mime_info->hdr_charset      = text_charset;   

    /* FIXME: is this correct? 
              ... we also should avoid ISO2022 charsets on here
     */


    mime_info->encode_hdr       = 1;

    mime_info->top_parts_count  = 0;
    mime_info->top_parts        = NULL;
    
    /* These fields are not on correct place */
    mime_info->cl_offset = mime_info->cl_start = mime_info->cl_end = 0;
}

static struct mime_send_part * new_part P_((mime_send_t *mime_info));
static struct mime_send_part * new_part(mime_info)
     mime_send_t *mime_info;
{
    int ptr = mime_info->top_parts_count;

    mime_info->top_parts = safe_realloc(mime_info->top_parts,
					(mime_info->top_parts_count +1) *
					sizeof (struct mime_send_part));
    mime_info->top_parts_count++;


    mime_info->top_parts[ptr].encoding_part      = ENCODING_7BIT;
    mime_info->top_parts[ptr].encoding_part_text = NULL;
    mime_info->top_parts[ptr].TYPE               = 0;
    mime_info->top_parts[ptr].TYPE_opts_part     = NULL;
	
    mime_info->top_parts[ptr].disposition        = 0;
    mime_info->top_parts[ptr].DISPOSITION_opts   = NULL;

    mime_info->top_parts[ptr].description        = NULL; 

    mime_info->top_parts[ptr].is_text            = 0;
    mime_info->top_parts[ptr].save_it_on_copy    = 0;
    mime_info->top_parts[ptr].start_loc          = 0;
    mime_info->top_parts[ptr].end_loc            = 0;
    mime_info->top_parts[ptr].result_charset     = NULL;

    DPRINT(Debug,4,
	   (&Debug,
	    "Preparing mail for sending: new_part %d\n",
	    ptr));
    /* Notice that returned pointer is valid only until
     * new_part() is called again
     */
    return &(mime_info->top_parts[ptr]);
}

static void reset_parts P_((mime_send_t *mime_info));
static void reset_parts(mime_info)
     mime_send_t *mime_info;
{
    int i;
    
    for (i = 0; i < mime_info->top_parts_count; i++) {
	mime_info->top_parts[i].encoding_part = 0;
	if (mime_info->top_parts[i].encoding_part_text) {
	    free(mime_info->top_parts[i].encoding_part_text);
	    mime_info->top_parts[i].encoding_part_text = NULL;
	}

	mime_info->top_parts[i].TYPE = NULL;
	if (mime_info->top_parts[i].TYPE_opts_part) 
	    free_mime_param(&(mime_info->top_parts[i].TYPE_opts_part));
	       	
	mime_info->top_parts[i].disposition = 0;
	if (mime_info->top_parts[i].DISPOSITION_opts) 
	    free_mime_param(&(mime_info->top_parts[i].DISPOSITION_opts));	   
	
	if (mime_info->top_parts[i].description) {
	    free_string(&(mime_info->top_parts[i].description));
	    mime_info->top_parts[i].description = NULL;
	}
	    

	mime_info->top_parts[i].is_text         = 0;
	mime_info->top_parts[i].save_it_on_copy = 0;
	mime_info->top_parts[i].result_charset  = NULL;
	mime_info->top_parts[i].start_loc       = 0;
	mime_info->top_parts[i].end_loc         = 0;
    }

    if (mime_info->top_parts) {
	free(mime_info->top_parts);
	mime_info->top_parts = NULL;
    }
    mime_info->top_parts_count = 0;
}

void free_mime_send_info(mime_info)
     mime_send_t *mime_info;
{
    reset_parts(mime_info);

    mime_info->encoding_top     = 0;
    mime_info->mime_boundary[0] = '\0';
    if (mime_info->TYPE_opts_top)
	free_mime_param(&(mime_info->TYPE_opts_top));
    mime_info->hdr_charset      = NULL;
    mime_info->encode_hdr       = 1;
    mime_info->msg_is_multipart = 0;
    mime_info->cl_offset           = 0;
    mime_info->cl_start            = 0;
    mime_info->cl_end              = 0;
}

void add_parameter_1(ptr,name,value,quoted)
     char **ptr; 
     char *name; 
     char *value; 
     int quoted;
{
    char buffer[1024];

    buffer[0] = '\0';
    if (*ptr)
	strfcpy(buffer,*ptr,sizeof buffer);

    add_parameter(buffer,name,value,sizeof buffer, quoted);

    *ptr = strmcpy(*ptr,buffer);
}


static int write_one_part P_((mime_send_t *mime_info,
			      struct mime_send_part * X,
			      out_state_t *mailer,
			      FILE *tmp, 
			      struct menu_context *page));
		       
int write_one_part(mime_info,X,mailer,tmp, page)
     mime_send_t *mime_info;
     struct mime_send_part * X;
     out_state_t *mailer;
     FILE *tmp;
     struct menu_context *page;     
{
    int need_enc, line_len;
    char buffer[SLEN];			/* file reading buffer */
    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);
    int LINES, COLUMNS;

    menu_get_sizes(page,&LINES, &COLUMNS);

    if (!ascii_ptr)
	panic("MIME PANIC",__FILE__,__LINE__,"write_one_part",
	      "US-ASCII not found",0);

    rewind(tmp);

    /* Determine how this message should be MIME encoded:
     * 7BIT, 8BIT, BINARY or QUOTED-PRINTABLE.
     */
    need_enc = needs_encoding (tmp,NULL);
    rewind(tmp);

    if (need_enc & HAVE_BINARY) {
	if (mime_info->raw_level >= mailer_binary)
	    X->encoding_part = ENCODING_BINARY;
	else
	    /* no -BBINARYMIME option */
	    X->encoding_part = ENCODING_QUOTED;
    }
    else if (need_enc & HAVE_8BIT) {
	if (mime_info->raw_level >= mailer_8bit)
	    /* Just send 8BIT anyway */
	    X->encoding_part = ENCODING_8BIT;
	else
	    /* no -B8BITMIME option */
	    X->encoding_part = ENCODING_QUOTED;
    }

    /* Control characters can send with encoding 7BIT
     * HAVE_BINARY takes care really severe stuff */
    if ((need_enc & HAVE_CTRL) &&
	X->encoding_part != ENCODING_QUOTED) {
	if (batch_only) 		
	    X->encoding_part =  ENCODING_QUOTED;
	else { 
	    int ch = prompt_letter(LINES-3,"",
				   *def_ans_yes,
				   PROMPT_yesno|PROMPT_cancel,
				   page,				   
				   CATGETS(elm_msg_cat, ElmSet, 
					   ElmTextHaveControl,
					   "Text part %d have control characters. Encode text with quoted-printable? "),
				   (X - mime_info->top_parts) +1);
	    if (ch == *def_ans_yes) 
		X->encoding_part =  ENCODING_QUOTED;
	    else if (ch != *def_ans_no) {
		return 0;    /* Go again to verify_transmission loop */
	    }
	}
    }

    /* This update may be little late for previous body parts
       but at least headers go correctly also for previous
       body parts ...
    */
    update_encoding(&(mime_info->encoding_top),X->encoding_part);

    if (X->result_charset) {
	int ascii_compat = charset_ok_p(X->result_charset);

	/* Update Charset of part */

	DPRINT(Debug,8,(&Debug, 
			"charset=%s (%p)  8bit=%s  charset ascii compatible=%s\n",
			X->result_charset->MIME_name ? 
			X->result_charset->MIME_name : 
			"???",
			X->result_charset,
			(need_enc & HAVE_8BIT) ? "yes" : "no",
			ascii_compat ? "yes" : "no"));

	/* 1) If charset can be replaced with US-ASCII, do it */
	if (!(need_enc & HAVE_8BIT) &&
	    X->result_charset != ascii_ptr &&
	    ascii_compat) {

	    X->result_charset = ascii_ptr;
	    DPRINT(Debug,8,(&Debug, "Replacing charset with US-ASCII\n"));
	}

	/* 2) If charset claim to be US-ASCII, but there is 8-bit data
           use UNKNOWN-8BIT instead  (should not happen...)
	*/
	else if ((need_enc & HAVE_8BIT) &&
		 X->result_charset->MIME_name &&
		 istrcmp(X->result_charset->MIME_name,"US-ASCII") == 0) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmUsingUNKNOWN8BIT,
			      "Text has 8BIT data and charset=US-ASCII, using charset=UNKNOWN-8BIT instead."));
	    
	    X->result_charset = MIME_name_to_charset("UNKNOWN-8BIT",1);

	    DPRINT(Debug,8,(&Debug, "Replacing charset with UNKNOWN-8BIT\n"));
	}

	/* Add charset to options */
	mime_params_add_compat(&(X->TYPE_opts_part), 
			       "charset", 
			       X->result_charset->MIME_name ? 
			       X->result_charset->MIME_name :
			       "UNKNOWN-8BIT");
    }

    state_putc('\n',mailer);        /* filler */
    X->start_loc = out_state_ftell(mailer);

    while (0 < (line_len = 
		mail_gets(buffer, sizeof buffer -1, tmp))) {
    
	if (mime_info->encoding_top == ENCODING_BINARY) {
	    /* It is better perhaps use canonical eol (CRLF) when mail have
	     * content transfer encoding BINARY somewhere (see notes about 
	     * BINARYMIME)
	     */
	    
	    /* THIS assumes that 
	       dest->EOLN_is_CRLF is set
	    */
	    state_convert_EOLN(buffer,&line_len,sizeof buffer,mailer);
	}
	
	/* Do QUOTED-PRINTABLE conversion if necessary... */
	if (X->encoding_part == ENCODING_QUOTED)
	    line_quoted_printable_encode(buffer,mailer,line_len,TRUE,
					 mime_info);
	else
	    state_put(buffer, line_len, mailer);
    }
	
    X->end_loc = out_state_ftell(mailer);

    /* !! */
    if (ferror(out_state_FILE(mailer))) {
	lib_error(CATGETS(elm_msg_cat, ElmSet, 
			  ElmWriteFailedCopy,
			  "Write failed to temp file in copy"));
	return 0;   /* Just indicate failure */
    }

    DPRINT(Debug,4,
	   (&Debug,
	    "Preparing mail for sending: part %d is %d bytes (%s)\n",
	    X-mime_info->top_parts,
	    X->end_loc-X->start_loc,
	    X->encoding_part == ENCODING_QUOTED ? 
	    "quoted-printable" : "Xbit"));
    
    return 1;
}

static int Include_Part P_((FILE *dest,char *buffer,
			    mime_send_t *mime_info,
			    struct mime_send_part * X,
			    struct mailer_info  *mailer_info,
			    charset_t from_charset));

/* Returns 1 if OK */
int convert_text(source,dest,mime_info,from_charset,to_charset,do_pgp,
		 attachments,mailer_info,page)
     FILE *source;
     FILE *dest; 
     mime_send_t *mime_info;
     charset_t from_charset;
     charset_t to_charset;
     int do_pgp;
     struct Attachments  *attachments;
     struct mailer_info  *mailer_info;
     struct menu_context *page;     
{
    int ok = 1;
    int  line_len;
    char buffer[SLEN];			/* file reading buffer */
    int no_save = 0;
    int keyword_error = 0;

    struct mime_send_part * X = NULL;
   
    char * tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
    if (!tmp)
	tmp = "/tmp/";

    reset_parts(mime_info);
	      
    if (do_pgp) {   
#ifdef USE_PGP
	X = new_part(mime_info);

	X->save_it_on_copy    = 1;
	X->is_text            = 0;
	/* write_one_part will fix encoding */
	X->encoding_part      = ENCODING_7BIT;
	X->encoding_part_text = NULL;

	if (do_pgp & PGP_MESSAGE) {
	    int pe = give_dt_enumerate_as_int(&pgp_encrypt_type);
		    
	    switch (pe) {
	    case 0:
	    default:
		X -> TYPE = give_media_type2(MIME_TYPE_APPLICATION,"pgp",1);
		break;
	    case 1:
		X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
		if (!X -> TYPE)
		    mime_panic(__FILE__,__LINE__,"convert_text",
			       "text/plain is not known");
		break;
	    case 2:
		X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"x-pgp",1);
	    }
	} else {
	    int ps = give_dt_enumerate_as_int(&pgp_sign_type);

	    switch (ps) {
	    case 0:
	    default:
		X -> TYPE = give_media_type2(MIME_TYPE_APPLICATION,"pgp",1);
		break;
	    case 1:
		X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
		if (!X -> TYPE)
		    mime_panic(__FILE__,__LINE__,"convert_text",
			       "text/plain is not known");
		break;
	    case 2:
		X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"x-pgp",1);
	    }
	}

	if (do_pgp & PGP_PUBLIC_KEY)
            mime_params_add_compat(&(X->TYPE_opts_part),"format","keys-only");
	else {
            mime_params_add_compat(&(X->TYPE_opts_part), "format", "text");
            /* This next bit is a non-non-standard, but exmh does this and it
             * can be very useful when parsing the message.
             */
            if (pgp_status & PGP_MESSAGE) {
		mime_params_add_compat(&(X->TYPE_opts_part), "x-action",
				       (do_pgp & PGP_SIGNED_MESSAGE) ? 
				       "encryptsign" : "encrypt");
            } else
		mime_params_add_compat(&(X->TYPE_opts_part), "x-action", "sign");
	}
        
	/* If message is signed, it can not be character set converted! */
	if (get_major_type_code(X -> TYPE)    == MIME_TYPE_TEXT)
	    X -> result_charset = from_charset;
	
	{
	    out_state_t A;

	    out_state_clear(&A,STATE_out_file);
	    set_out_state_file(dest,&A);

	    /* Let state routines be aware of encoding */
	    if (mime_info->encoding_top == ENCODING_BINARY)
		A.EOLN_is_CRLF = 1;
	    
	    /* Whole source -- no splitting ... */
	    if (!write_one_part(mime_info,X,&A,source, page))
		ok = 0;   /* Write failed or user canceled */

	    /* out_state_destroy does not fclose() dest */
	    out_state_destroy(&A);
	}
#else
	panic("PGP PANIC",__FILE__,__LINE__,"convert_text",
	      "do_pgp is set but PGP support not compiled in",0);
#endif
    } else { 
	static int tmpcount = 0;
	FILE * tmpfile      = NULL;
	int crypted         = FALSE;

	if (from_charset != to_charset && 
	    (!(CS_mapping & charset_properties(from_charset)) ||
	     !(CS_mapping & charset_properties(to_charset)))) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantConvertCharset,
			      "Can't convert text from charset %s to charset %s"),
		      from_charset -> MIME_name ? 
		      from_charset -> MIME_name : "<no MIME name>",
		      to_charset -> MIME_name ? 
		      to_charset -> MIME_name : "<no MIME name>");
	    to_charset = from_charset;   /* No conversion */
	    sleep_message();   /* Needed ... */
	}
	
	if (from_charset != to_charset) {
	    DPRINT(Debug,4, 
		   (&Debug, 
		    "Preparing mail for sending: conversion from charset %s to charset %s\n",
		    from_charset -> MIME_name ? 
		    from_charset -> MIME_name : "<no MIME name>",
		    to_charset -> MIME_name ? 
		    to_charset -> MIME_name : "<no MIME name>"));
	}
	
	mime_info->hdr_charset  = to_charset;
	
	while (0 < (line_len = 
		    mail_gets(buffer, sizeof buffer, source))) {
	    int change_part = 0;
	    char *ptr = buffer;
	    int gotten_key   = 0;
	    int incomplete = 0;
	    int was_incomplete;

	    if (buffer[line_len-1] != '\n') {
		DPRINT(Debug,7, 
		       (&Debug, 
			"convert_text: Too long line (read %d bytes)\n",
			line_len));
		incomplete = 1;
	    }
	   
	    if (!tmpfile)
		change_part = 1;

	    if (mime_body_keywords && buffer[0] == '[') {

		if (strncmp(buffer, END_ENCODE, strlen(END_ENCODE)) ==0 &&
		    crypted) {
		    if (tmpfile) {
			fputs(END_ENCODE,tmpfile);
			fputc('\n',tmpfile);
		    }
		    change_part = 1;
		} else if (strncmp(buffer, START_ENCODE, 
				   strlen(START_ENCODE)) == 0 ||
		    strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0 ||
		    strncmp(buffer, DONT_SAVE2, strlen(DONT_SAVE2)) == 0 ||
		    strncmp(buffer, MIME_INCLUDE, strlen(MIME_INCLUDE)) ==0)
		    change_part = 1;
		else if (strncmp(buffer,"[[",2) != 0 &&
			 strncmp(buffer,"[ ",2) != 0) {
		    if (keyword_error++ == 0) {
			if (RawState())
			    lower_prompt(catgets(elm_msg_cat, ElmSet, 
						 ElmBadKeywordHint,
						 "Use [[ to specify line starting with ["));
			lib_error(CATGETS(elm_msg_cat, ElmSet, ElmBadKeyword,
					  "Bad keyword on text: %.50s"),
				  buffer);
		    } else
			lib_transient(CATGETS(elm_msg_cat, ElmSet, 
					      ElmBadKeyword,
					      "Bad keyword on text: %.50s"),
				      buffer);
		    if (!batch_only)
			ok = 0;
		}
	    }

	    /* End previous part */
	    if (change_part) {
		if (tmpfile) {
		    
		    if (ferror(tmpfile)) {
			lib_error(CATGETS(elm_msg_cat, ElmSet, 
					  ElmWriteFailedCopy,
					  "Write failed to temp file in copy"));
			ok = 0;
		    }


		    {
			
			out_state_t A;
			
			out_state_clear(&A,STATE_out_file);
			set_out_state_file(dest,&A);
			
			/* Let state routines be aware of encoding */
			if (mime_info->encoding_top == ENCODING_BINARY)
			    A.EOLN_is_CRLF = 1;

			if (!write_one_part(mime_info,X,&A,tmpfile,page))
			    ok = 0;   /* Write failed or user canceled */

			/* out_state_destroy does not fclose() dest */
			out_state_destroy(&A);
		    }


		    fclose(tmpfile);
		    tmpfile = NULL;
		}
	    }
		
	    if (mime_body_keywords && buffer[0] == '[') {

		if (strncmp(buffer, MIME_INCLUDE, strlen(MIME_INCLUDE)) ==0) {
		    X  = new_part(mime_info);
		    X->save_it_on_copy   =  !no_save;
		    X->is_text           = 0;
		    
		    if (Include_Part(dest,buffer,mime_info,X,mailer_info,
				     from_charset) < 0)
			ok = 0;
		    continue;
		} else if (strncmp(buffer, START_ENCODE, 
				   strlen(START_ENCODE))==0) {
		    if (batch_only) {
			lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoEncryptInBatch,
					  "Sorry. Cannot send encrypted mail in \"batch mode\".\n"));
			ok = 0;
		    } else {
			crypted = TRUE;
			if (! gotten_key) {
			    int zz = getkey(ON);

			    if (zz > 0) 
				gotten_key = 1;
			    else
				ok = 0;
			    if (zz < 0)       /* Ctrl-C */
				break;
			} else 
			    get_key_no_prompt();		/* reinitialize.. */
		    }
		    continue;
		} else if (strncmp(buffer, END_ENCODE, 
				   strlen(END_ENCODE))==0) {
		    crypted = FALSE;
		    continue;
		} else if ((strncmp(buffer, DONT_SAVE, strlen(DONT_SAVE)) == 0) || 
			   (strncmp(buffer, DONT_SAVE2, strlen(DONT_SAVE2))) == 0) {
		    no_save = 1;
		    continue;
		}
	    }

	    /* Start new part */
	    if (change_part) {
		int i;
		for (i = 0; i < 10; i++) {
		    char * filename = elm_message(FRM("%selmsd-%d-%d"),
						  tmp, getpid (),
						  tmpcount++);
		    tmpfile = safeopen_rdwr(filename);
		    if (!tmpfile) {
			int err = errno;
			DPRINT(Debug,10,(&Debug,   
					 "convert_text: safeopen_rdwr: %s: %s (errno %d)\n",
					 filename,
					 error_description(err),err));
		    } else {
			unlink(filename);
			DPRINT(Debug,10,(&Debug,   
					 "convert_text: using temp file (%s)\n",
					 filename));
		    }
		    free(filename);
		    if (tmpfile)
			break;
		}
		if (!tmpfile) {
		    lib_error(CATGETS(elm_msg_cat, ElmSet, 
				      ElmOpenFailedCopy,
				      "Temp file open failed to in copy"));
		    ok = 0;
		    break;   /* Can't continue without target file handle */
		}
		
		X = new_part(mime_info);
		
		X -> save_it_on_copy     =  !no_save;
		X -> is_text             = 0;
		/* write_one_part will fix encoding */
		X -> encoding_part       = ENCODING_7BIT;
		X -> encoding_part_text  = NULL;
		X -> result_charset      = to_charset;		
		if (crypted) {
		    X -> TYPE       = 
			give_media_type2(MIME_TYPE_APPLICATION,"X-ELM-encode",
					 0);
		    if (!X -> TYPE)
			mime_panic(__FILE__,__LINE__,
				   "convert_text",
				   "application/X-ELM-encode is not known");
		    
		    fputs(START_ENCODE,tmpfile);
		    fputc('\n',tmpfile);
		} else {
		    X -> TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
		    if (!X -> TYPE)
			mime_panic(__FILE__,__LINE__,
				   "convert_text",
				   "text/plain is not known");
		}
		/* write_one_part will add charset paramater */
		X -> result_charset      = to_charset; 
	    }
	    
	    /* Double [[ means that user want start line with [ */
	    if (strncmp(buffer,"[[",2) == 0) {
		ptr = buffer+1;
		line_len--;
	    }
	    
	    
	    do {       /* Handle long lines ... */

		struct string * in_str  = new_string(from_charset);
		struct string * out_str = NULL;
		char * result = NULL;
		int resultlen = 0;

		/* Do conversions -- this does not handle binary files,
		 * but they should not occur on here anyway...	     
		 */

		if (add_streambytes_to_string(in_str,line_len,s2us(ptr),
					      NULL) != line_len) {
		    /* FIXME:  check errors */
		}

		out_str = convert_string(to_charset,in_str,0);

		
		bytestream_from_string(out_str,&result,&resultlen);
	
		
		if (crypted) {
		    encode(result);
		    fputs(result,tmpfile);

		} else {

		    /* write_one_part will do \n -> \r\n conversion
		     * for this part if needed
		     */

		    if (resultlen != fwrite(result,1,resultlen,tmpfile)) {
			/* FIXME:  check errors */
		    }
		}
		
		free(result);
		free_string(&out_str);
		free_string(&in_str);

		was_incomplete = incomplete;
		if (incomplete) {
		    incomplete = 0;

		    ptr = buffer;
		    
		    if (0 < (line_len = 
			     mail_gets(buffer, sizeof buffer, source))) {
			
			if (buffer[line_len-1] != '\n') {
			    DPRINT(Debug,15,(&Debug,   
					     "convert_text: Long line continues (read %d bytes)\n",
					     line_len));
			    incomplete = 1;
			} else {
			    DPRINT(Debug,15,(&Debug,   
					     "convert_text: end of long line (read %d bytes)\n",
					     line_len));
                            incomplete = 0;
			}
		    } else {
			DPRINT(Debug,15,(&Debug,   
					 "convert_text: failed read to end of long line\n"));
			break;
		    }
		}
	    } while (was_incomplete);
	}
	if (tmpfile) {
	    if (ferror(tmpfile)) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmWriteFailedCopy,
				  "Write failed to temp file in copy"));
		ok = 0;
	    }


	    {
		out_state_t A;
			
		out_state_clear(&A,STATE_out_file);
		set_out_state_file(dest,&A);
		
		/* Let state routines be aware of encoding */
		if (mime_info->encoding_top == ENCODING_BINARY)
		    A.EOLN_is_CRLF = 1;
		
		if (!write_one_part(mime_info,X,&A,tmpfile,page))
		    ok = 0;   /* Write failed or user canceled */

		/* out_state_destroy does not fclose() dest */
		out_state_destroy(&A);
	    }
	    
	    fclose(tmpfile);
	    tmpfile = NULL;
	}

    }

    if (attachments->attachment_count > 0) {

#ifdef USE_PGP	
	if (do_pgp) {

	    if (do_pgp & PGP_MESSAGE)
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoPgpAttachEncode,
				  "WARNING: I PGP encode only main message"));
	    else
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoPgpAttachSign,
				  "WARNING: I PGP sign only main message"));
	}
#endif

	{
	    int i;
	    out_state_t A;
	    
	    out_state_clear(&A,STATE_out_file);
	    set_out_state_file(dest,&A);
	    
	    /* Let state routines be aware of encoding */
	    if (mime_info->encoding_top == ENCODING_BINARY)
		A.EOLN_is_CRLF = 1;


	    /* now add attachments */
	    for (i = 0; i < attachments->attachment_count; i++) {
		X  = new_part(mime_info);
		X->save_it_on_copy   = !no_save;
		X->is_text           = 0;
		if (!attach_message (& (attachments->attach_files[i]), 
				     &A, mime_info, X))
		    ok = 0;
	    }

	    /* out_state_destroy does not fclose() dest */
	    out_state_destroy(&A);
	}	
    }

    if (keyword_error > 1 && RawState())
	error_sleep(sleepmsg *2);
    
    return ok;
}

int check_for_multipart(filedesc, mime_info, mailer_info, from_charset)
     FILE *filedesc;
     mime_send_t *mime_info;
     struct mailer_info  *mailer_info;
     charset_t from_charset;
{
    char buffer[SLEN];
    int Multi_Part = FALSE;
    
    while (mail_gets(buffer, SLEN, filedesc))
	if (mime_body_keywords && buffer[0] == '[') {
	    if (strncmp(buffer, MIME_INCLUDE, strlen(MIME_INCLUDE)) == 0) {
		Multi_Part = TRUE;
		if (Include_Part(NULL,buffer, mime_info, NULL,
				 mailer_info,
				 from_charset) == -1) {
		    return(-1);
		}
	    }
	}
    rewind(filedesc);
    return(Multi_Part);
}

static int Include_Part(dest, buffer, mime_info, X, mailer_info, from_charset)
     FILE *dest;
     char *buffer;
     mime_send_t *mime_info;
     struct mime_send_part * X;
     struct mailer_info  *mailer_info;
     charset_t from_charset;
{
    int check = (X == NULL);
    char *ptr;
    char *incptr;
    char Include_Filename[SLEN];
    struct string          * orig_filename = NULL;
    struct folder_browser  * expanded_file = new_browser(selection_file);
    char                   * expanded_2    = NULL;
    int                      is_copy = 0;
    int ret = 0;

    char Primary_Type[SLEN];
    char SubType[SLEN];
    media_type_t Type;
    char Params[STRING];
    char Encoding[SLEN];
    int  Enc_Type;
    FILE *incfile;
    int is_text;

    ptr = buffer + strlen(MIME_INCLUDE);
    while ((*ptr != '\0') && (*ptr == ' '))
	ptr++;
    incptr = Include_Filename;
    while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') &&
	   (incptr < Include_Filename + sizeof(Include_Filename) -1))
	*incptr++ = *ptr++;
    *incptr = '\0';

    while ((*ptr != '\0') && (*ptr == ' '))
	ptr++;
    incptr = Primary_Type;
    while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') && (*ptr!='/')
	   && (*ptr != ';') 
	   && (incptr < Primary_Type + sizeof(Primary_Type) -1))
	*incptr++ = *ptr++;
    *incptr = '\0';
    while ((*ptr != '\0') && (*ptr == ' '))
	ptr++;
    incptr = SubType;
    if (*ptr == '/') {
	ptr++;
	while ((*ptr != '\0') && (*ptr == ' '))
	    ptr++;
	while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') && (*ptr!=';')
	       && (incptr < SubType + sizeof(SubType) -1))
	    *incptr++ = *ptr++;
    }
    *incptr = '\0';
    while ((*ptr != '\0') && (*ptr == ' '))
	ptr++;
    incptr = Params;
    while (*ptr == ';') {
	ptr++;
	if (incptr > Params) {
	    *incptr++ = ';';
	} else if (*ptr == ' ')
	    ptr++;

	while ((*ptr != '\0') && (*ptr == ' ')
	       && (incptr < Params + sizeof(Params) -1))
	    *incptr++ = *ptr++;

	while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0') && (*ptr!=';')
	       && (incptr < Params + sizeof(Params) -1))
	    *incptr++ = *ptr++;
	while ((*ptr != '\0') && (*ptr == ' '))
	    ptr++;
    }
    *incptr = '\0';

    while ((*ptr != '\0') && (*ptr == ' '))
	ptr++;
    incptr = Encoding;
    while ((*ptr != ' ') && (*ptr != ']') && (*ptr != '\0')
	   && (incptr < Encoding + sizeof(Encoding) -1))
	*incptr++ = *ptr++;
    *incptr = '\0';

    if (strlen(Include_Filename) == 0) {
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoIncludeFilename,
			  "No Filename given, include line ignored"));
	ret = -1;
	goto check_out;
    }

    orig_filename = new_string2(from_charset,s2us(Include_Filename));

    if (! select_dir_item(expanded_file,&orig_filename)) {
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailedToExpandInclude,
			  "Failed to expand include filename"));
	ret = -1;
	goto check_out;
    }

	if (strlen(Primary_Type) == 0 || strlen(SubType) == 0 ) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoContentTypeGiven,
			      "No Content-type given, include line ignored"));
	    ret = -1;
	    goto check_out;
	}

	Enc_Type = check_encoding(Encoding);

	if (Enc_Type == ENCODING_ILLEGAL) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmEncodingIsIllegal,
			      "Encoding is illegal"));
	    ret = -1;
	    goto check_out;
	}


	Type = give_media_type(Primary_Type,SubType,1);

	/* 1 if is text type (true)
	 * 0 if not text type
	 * -1 if can't be encoded (ie structured) Message/ or Multipart/ 
	 */

	/* 7bit and 8bit are only allowed for line orienteed types */
	if (Enc_Type == ENCODING_NONE || 
	    Enc_Type == ENCODING_7BIT || 
	    Enc_Type == ENCODING_8BIT) {
	    DPRINT(Debug,11,(&Debug,
			     "Include_Part: textual encoding (%s)\n",
			     ENCODING(Enc_Type)));
	    is_text = 1;
	} else
	    is_text = give_text_type_code(Type);




	if (!dir_make_ref(expanded_file,&expanded_2,&is_copy,  is_text)) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmIncludeCannotAccess,
			      "Include File can't be accessed"), 0);
	    ret = -1;
	    goto check_out;
	}


	/* Don't allow 7BIT if 8-bit charcters in any type,    
	 * don't allow 8BIT if 'binary' characters       - K E H */
	if (Enc_Type == ENCODING_7BIT || Enc_Type == ENCODING_NONE
	    || Enc_Type == ENCODING_8BIT) {
	       
	    FILE * fp = fopen (expanded_2, "r");
	    if (fp) {
		int tmp = needs_encoding (fp,NULL);
		if (tmp & HAVE_BINARY) {
		    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmIncludeBINARY,
				      "Include file has BINARY data."));       
		    if (Enc_Type == ENCODING_7BIT || Enc_Type == ENCODING_8BIT) {
		        /* indicate failure */
			ret = -1;
			goto check_out;
		    }
		    Enc_Type = ENCODING_BINARY;
		} else if ((tmp & HAVE_8BIT) && Enc_Type != ENCODING_8BIT) {
		    lib_error (CATGETS(elm_msg_cat, ElmSet,ElmInclude8BIT,
				       "Include file has 8BIT data."));	
		    if (Enc_Type == ENCODING_7BIT) {
			/* indicate failure */
			ret = -1;
			goto check_out;
		    }	      	
		    Enc_Type = ENCODING_8BIT;
		    
		}
		fclose(fp);
	    }
	}

	if (Enc_Type == ENCODING_8BIT) {
	    if (mime_info->raw_level < mailer_8bit) {
		lib_error (CATGETS(elm_msg_cat, ElmSet,ElmDoesnt8BIT,
				   "Mailer (MTA) doesn't support 8BIT encoding."));
		ret = -1;   /* indicate failure */
		goto check_out;		
	    }
	}
	
	if (Enc_Type == ENCODING_BINARY) {
	    if (mime_info->raw_level < mailer_binary) {
		lib_error (CATGETS(elm_msg_cat, ElmSet,ElmDoesntBINARY,
				   "Mailer (MTA) doesn't support BINARY encoding!"));
		ret = -1;
		goto check_out;
	    }
	}

    
	if (is_text < 0 && (Enc_Type == ENCODING_QUOTED ||
			    Enc_Type == ENCODING_BASE64)) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmDontEncoding,
			      "Content-Type don't allow encoding -- ignoring this part."));

	    ret = -1;
	    goto check_out;
	}


	(void) update_encoding(&(mime_info->encoding_top),Enc_Type);

	if (check) {
	check_out:

	    if (orig_filename)
		free_string(&orig_filename);
	    if (expanded_file)
		free_dir(&expanded_file);

	    if (expanded_2) {
		if (is_copy &&
		    0 == unlink(expanded_2)) {
		    DPRINT(Debug,4,(&Debug, 				
				    "Include_Part: '%s' unlinked\n",
				    expanded_2));
		}
		free(expanded_2);

	    }

	    return ret;
	}

      


	incfile = fopen (expanded_2, "r");
	if (incfile) {
	    	DPRINT(Debug,4,(&Debug, 				
				"Include_Part: '%s' C-T=%s/%s Params=%s Enc=%d is_text=%d\n",
				expanded_2,Primary_Type,SubType, Params, Enc_Type,
				is_text));

	    X->encoding_part = Enc_Type;
	    if (Enc_Type >= ENCODING_EXPERIMENTAL) {
		X->encoding_part_text = safe_strdup(Encoding);
	    }
	    X->TYPE = Type;
	    if (Params[0]) {   
		int mp = give_dt_enumerate_as_int(&mime_parameters);
		/* 0 == plain
		   1 == encoded
		   2 == plain-and-encoded
		*/
		
		struct string * XX = new_string2(from_charset,s2us(Params));

		switch(mp) {
		case 0:
		    X->TYPE_opts_part = parse_mime_param_string(XX,0,1);
		    break;
		case 1:
		    X->TYPE_opts_part = parse_mime_param_string(XX,0,0);
		    break;
		default:
		    X->TYPE_opts_part = parse_mime_param_string(XX,
								MAX_COMPAT_LEN,
								0);
		    break;
		}
		free_string(&XX);	    
	    }

	    /* When user uses [include ...] it is better use disposition
	     * 'inline' instead of 'attachment'
	     */
	    { 
		int mp = give_dt_enumerate_as_int(&mime_parameters);
		/* 0 == plain
		   1 == encoded
		   2 == ascii-and-encoded
		*/

		int was_ok = 1;
		char * compat = compat_filename(Include_Filename,&was_ok);
		int   have_encoded = 0;
			       
		if (mp > 0 &&
		    (!was_ok || !can_ascii_string(orig_filename))) {
		    
		    
		    struct string *p1 = pick_name(orig_filename);
		    
		    if (p1) {
			mime_params_add(& (X->DISPOSITION_opts),
					"filename", p1);
			
			free_string(&p1);
			
			have_encoded ++;
		    }
		}
		
		DPRINT(Debug,9,
		       (&Debug,
			"include: mime_parameters=%d have_encoded=%d was_ok=%d\n",
			mp,have_encoded));
			


		if (0 == mp ||
		    2 == mp ||
		    ! have_encoded) {
		    
		    mime_params_add_compat(&(X->DISPOSITION_opts),"filename",
					   compat);      
		    
		}
		free(compat); compat = NULL;
		
	    }
	    X->disposition = DISP_INLINE;

      
	    fputc('\n',dest);        /* filler */
	    X->start_loc = ftell(dest);

	    /* Encoding rules are different for text and non-text. 
	     * For text we must do \n -> \r\n before base64 encoding */
    

	    {
		out_state_t A;
		
		out_state_clear(&A,STATE_out_file);
		set_out_state_file(dest,&A);
		
		/* Let state routines be aware of encoding */
		if (mime_info->encoding_top == ENCODING_BINARY)
		    A.EOLN_is_CRLF = 1;

		/* For ENCODING_EXPERIMENTAL this is already supposed to be 
		   encoded */
		write_encoded(incfile,&A,Enc_Type,is_text,mime_info);
    
		/* out_state_destroy does not fclose() dest */
		out_state_destroy(&A);
	    }

	    X->end_loc = ftell(dest);
	    fclose(incfile);

	    if (ferror(dest)) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmWriteFailedCopy,
				  "Write failed to temp file in copy"));
		/* Just indicate failure */
		ret = -1;
		goto fail_out;
	    }

	    DPRINT(Debug,4,(&Debug,   
			    "Preparing mail for sending: part %d (include) is %d bytes (%s)\n",
			    X-mime_info->top_parts,
			    X->end_loc-X->start_loc,
			    ENCODING(X->encoding_part)));

	} else {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantOpenIncludedFile,
			      "Can't open included File"));
	    ret = -1;
	    goto fail_out;
	}

 fail_out:
	if (orig_filename)
	    free_string(&orig_filename);
	if (expanded_file)
	    free_dir(&expanded_file);

	if (expanded_2) {
	    if (is_copy && 
		0 == unlink(expanded_2)) {
		DPRINT(Debug,4,(&Debug, 				
				"Include_Part: '%s' unlinked\n",
				expanded_2));
	    }
	    free(expanded_2);
	}

	return ret;
}


/* Determine whether or not the data in "fp" needs to be QUOTED-PRINTABLE
    encoded.  Used when sending a message.
       1 == have 8bit data                (HAVE_8BIT)
       4 == have control charcters        (HAVE_CTRL)
       8 == mime requires encoding BINARY (HAVE_BINARY)
*/

int needs_encoding (fp,list)
     FILE *fp;
     struct scan_list *list;
{
    int ch;
    int ret = FALSE;
    int len = 0;
    int printed = 0;

    rewind (fp);

    while ((ch = fgetc (fp)) != EOF) {
	int need_more = 0;
	
	if (list) {
	    need_more = scanlist_need_more(list,ch);
	
	    if (!need_more && !printed) {
		DPRINT(Debug,9,(&Debug,    
				"\nneeds_encoding(): magic scan finished\n"));
		printed++;
	    }
	}

	/* check for end of line */
	if (ch == 13) {   /* CR */
	    ch = fgetc(fp);

	    if (ch != 10) {  /* Not CR LF */
		DPRINT(Debug,9,(&Debug,    
				"\nneeds_encoding(): found CR without LF\n"));
		ret |= HAVE_BINARY;
	    }
	    if (ch == EOF)
		break;

	    if (list) {
		need_more = scanlist_need_more(list,ch);
		
		if (!need_more && !printed) {
		    DPRINT(Debug,9,(&Debug,    
				    "\nneeds_encoding(): magic scan finished\n"));
		    printed++;
		}
	    }
	}
	if (ch == 10) {
	    len = 0;
	    continue; /* skip newlines and tabs */
	}
	len++;
	if (len > 990) {
	    DPRINT(Debug,9,(&Debug,   
			    "\nneeds_encoding(): Line over 990 characters\n"));	
	    ret |= HAVE_BINARY;
	}

	if (ch == 9) /* skip newlines and tabs */
	    continue;
	if (ch < 32 || ch > 126) {
	    DPRINT(Debug,9,(&Debug,    
			    "\nneeds_encoding(): found char decimal=%d\n", ch));
	    if (ch == 0) 
		ret |= HAVE_BINARY;
	    if (ch < 32 || ch == 127)
		ret |= HAVE_CTRL;
	    if (ch > 127)
		ret |= HAVE_8BIT;
	}

	if (ret == (HAVE_8BIT | HAVE_CTRL | HAVE_BINARY) &&
	    !need_more)
	    break;
	
    }
    rewind (fp);

    DPRINT(Debug,4,(&Debug,
		    "needs_encoding()=%d%s%s%s\n",
		    ret,
		    ret & HAVE_8BIT ? " HAVE_8BIT" : "",
		    ret & HAVE_CTRL ? " HAVE_CTRL" : "",
		    ret & HAVE_BINARY ? " HAVE_BINARY" : ""));
		  
    return (ret);
}

char * have_metamail()
{
    char * metamail_value = give_dt_estr_as_str(&metamail_path_e, "metamail");
    char * return_value   = metamail_value;
		         
    if (!metamail_value)
	return NULL;
    
    if (strcmp(metamail_value,"none") == 0 || 
	metamail_value[0] == '\0') {
	return_value = NULL;
    } else if (metamail_value[0] == '/') {
	if (-1 == access(metamail_value,EXECUTE_ACCESS)) {
	    int err = errno;
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantMetamail,
			      "Can't execute metamail: %s.30: %.30s"),
		      metamail_value,
		      error_description(err));
	    DPRINT(Debug,6, (&Debug, 
			     "have_metamail: no access %s: %s\n",
			     metamail_value,
			     error_description(err)));
	    sleep_message();
	    return_value = NULL;
	}
    }
    
    DPRINT(Debug,5,(&Debug,   
		    "have_metamail=%s\n",
		    return_value ? return_value : "<NULL>"));
    return return_value;
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1