static char rcsid[] = "@(#)$Id: attach_menu.c,v 1.104.2.2 2007/11/03 13:22:53 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.104.2.2 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 *
 *  Initially written by: Michael Elkins <elkins@aero.org>, 1995
 *****************************************************************************/

#include "def_elm.h"
#include "s_elm.h"
#include "s_me.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;
}

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

static FILE *  get_attachment P_((FILE *F, mime_t *att));
static FILE *  get_attachment(F,att)
     FILE *F;
     mime_t *att;
{
    FILE * result = NULL;
    
    if (att->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"get_attachment",
		   "Bad magic number");
    
    if (att->pathname0) {

	if (can_open(att->pathname0,"r") != 0) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorReadableUser,
			      "%S isn't readable by user!"), 
		      att->dispname);
	    sleep_message();
	    return NULL;
	}
    
	result = fopen(att->pathname0,"r");
	if (!result) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantOpenAttach,
			      "Can't open attachment: %S"),
		      att->dispname);
	    sleep_message();
	}
    } else {
	result = F;
	if (-1 == fseek(F,att->offset,SEEK_SET)) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailedSeekAttach,
			      "Failed to seek beginning of attachment!"));
	    sleep_message();
	}
    }
    return result;
}

static void close_attachment P_((FILE *F, FILE *tmpfd));
static void close_attachment(F, tmpfd)
     FILE *F;
     FILE *tmpfd;
{
    if (!tmpfd)
	return;
    if (F != tmpfd)
	fclose(tmpfd);
}

static FILE *  get_decoded_attachment P_((FILE *F, mime_t *att, 
					  int *isdecoded));
static FILE *  get_decoded_attachment(F,att, isdecoded)
     FILE *F;
     mime_t *att;
     int *isdecoded;   /* return 1 is result is contetn-transfer-encdoing 
			  decoded */
{
    if (att->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"get_attachment",
		   "Bad magic number");
    
    if (att->pathname0) {
	FILE *result = NULL;

	if (can_open(att->pathname0,"r") != 0) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorReadableUser,
			      "%S isn't readable by user!"), 
		      att->dispname);
	    sleep_message();
	    return NULL;
	}
    
	result = fopen(att->pathname0,"r");
	if (!result) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantOpenAttach,
			      "Can't open attachment: %S"),
		      att->dispname);
	    sleep_message();
	}

	*isdecoded = 1;
	return result;

    } else {

	in_state_t state_in;
	out_state_t state_out;

	char *tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
	char *fname;
	FILE * tmpfp = NULL;
	charset_t vector[2];


	in_state_clear(&state_in, STATE_in_file);
	out_state_clear(&state_out, STATE_out_file);

	if (-1 == fseek(F,att->offset,SEEK_SET)) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailedSeekAttach,
			      "Failed to seek beginning of attachment!"));
	    return NULL;
	}

	if (!tmp)
	    return NULL;
	
	fname = elm_message(FRM("%selmdecode.%d"), 
			    tmp, getpid ());
	if (NULL == (tmpfp = safeopen_rdwr(fname))) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeFailedCreate,
			      "Failed to create file for decoding."));
	    return NULL;
	}
	
	unlink(fname);

	set_out_state_file(tmpfp,&state_out);
	state_out.displaying      = FALSE;   /* No messages */
	state_out.prefix          = NULL;

	vector[0] = RAW_BUFFER;    /* We want just raw bytes */
	vector[1] = NULL;

	state_out.display_charset = vector;
	set_in_state_file(F,&state_in);

	if (run_cte_decoder(att,&state_in,&state_out)) {
	    *isdecoded = 1;
	} else {

	    int r = state_copy_range(&state_in, &state_out, att->length);

	    *isdecoded = 0;

	    if (-1 == r)
		lib_error(CATGETS(elm_msg_cat, MeSet, 
				  MeDecodeUnexpectedEOF,
				  "Unexpected EOF when copying encoded mime part."));
	    if (0 == r)
		lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeFailedCopy1,
				  "Failed to copy encoded mime part."));

	}

	if (EOF == fflush(tmpfp)) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeErrorFlush,
			      "Error when flushing temporary file."));
	}
	

	rewind(tmpfp); /* Rewind it for reading */

	out_state_destroy(&state_out);
	in_state_destroy(&state_in);

	return tmpfp;
    }
}


static void attachment_copy P_((mime_t *att, FILE *tmpfd, FILE *outfd, 
				charset_t defcharset));
static void attachment_copy(att, tmpfd, outfd, defcharset)
     mime_t *att;
     FILE *tmpfd, *outfd;
     charset_t defcharset;
{

    if (att->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"attachment_copy",
		   "Bad magic number");
    
    if (att->pathname0) {  /* Already decoded */
	char buf[VERY_LONG_STRING];
	int len;
	while (0 < (len = fread(buf,1,sizeof(buf),tmpfd))) {
	    if (fwrite(buf,1,len,outfd) != len) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmWriteErrorAttach,
				  "Write error when copying attachment!"));
		sleep_message();
		break;
	    }
	}
	if (ferror(tmpfd)) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorReadingS,
			      "Error reading from %S"),
		      att->dispname);
	    sleep_message();
	}
    } else { /* Needs decode */
	in_state_t state_in;
	out_state_t state_out;
	charset_t vector[2];
	
	int disp = att -> disposition;
	
	in_state_clear(&state_in, STATE_in_file);
	out_state_clear(&state_out, STATE_out_file);
	
	set_out_state_file(outfd,&state_out);
	state_out.displaying      = FALSE;
	
	vector[0] = display_charset;
	vector[1] = NULL;
	state_out.display_charset = vector;
	set_in_state_file(tmpfd,&state_in);
	
	att -> disposition = DISP_INLINE;  /* Show it ! */
	mime_decode(att,&state_in, &state_out,
		    defcharset, NULL, mime_signature_mismatch);
	att -> disposition = disp;
	
	
	in_state_destroy(&state_in);
	out_state_destroy(&state_out);
    }
}

static void attach_print P_((FILE *F, mime_t *att, charset_t defcharset,
			     struct menu_context  *page));
static void attach_print (F,att, defcharset,page)
     FILE *F;
     mime_t *att;
     charset_t defcharset;
     struct menu_context  *page;
{
    char tempfile[STRING];
    char buf1[VERY_LONG_STRING];
    char *tmp;
    char * metamail_val;
    char * printout_val;

    if (att->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"attach_print",
		   "Bad magic number");

    if (!(printout_val=have_printout()))
	return;

    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
    if (!tmp)
	return;

    elm_sfprintf(tempfile,sizeof tempfile,
		 FRM("%selm.%d"), tmp, getpid());

    if (get_major_type_code(att->TYPE) == MIME_TYPE_TEXT && 
	istrcmp(get_subtype_name(att->TYPE),"plain") == 0) {
	FILE *f_out, *f_in;
	int ret;
	
	DPRINT(Debug,3,
	       (&Debug,"attach_print: printing directly: %s\n",
		printout_val));
	
	if (!(f_out = safeopen(tempfile))) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorCreatTempfile,
			      "Error creating tempfile %s"),
		      tempfile);
	    sleep_message();
	    return;
	}

	if (!(f_in = get_attachment(F,att))) {
	    fclose(f_out);
	    unlink(tempfile);
	    return;
	}
	attachment_copy(att,f_in,f_out, defcharset);
	
	(void) elm_chown (tempfile, userid, groupid);
	
	fclose(f_out);
	elm_sfprintf(buf1, sizeof buf1,
		     FRM(printout_val), tempfile);
	ret = system_call(buf1,0, NULL);
	if (ret == 0)
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmJobSpooled,
			      "Print job spooled."));
	else
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorPrinting,
			      "Error while printing!"));
	unlink(tempfile);
	close_attachment(F,f_in);

    } else if (NULL != (metamail_val=have_metamail())) {

	DPRINT(Debug,3,
	       (&Debug,"attach_print: printing via metamail: %s\n",
		metamail_val));

	if (att->pathname0)
	    elm_sfprintf(buf1,sizeof buf1,
			 FRM("%s -m Elm -h -b -c %s/%s %s"),
			 metamail_val,
			 get_major_type_name(att->TYPE), 
			 get_subtype_name(att->TYPE), 
			 att->pathname0);
	else {
	    char copy_buf[VERY_LONG_STRING];
	    FILE *fpout;
	    out_state_t state_out;

	    fpout = safeopen(tempfile);

	    if (!fpout) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorCreatTempfile,
				  "Error creating tempfile %s"),
			  tempfile);
		sleep_message();
		return;
	    }
	    (void) elm_chown (tempfile, userid, groupid);

	    out_state_clear(&state_out, STATE_out_file);
	    set_out_state_file(fpout,&state_out);

	    state_out.EOLN_is_CRLF   = 0;  
	    /* !! Metamail do not cope with CRLF */

	    /* the headers plus the message to a temp file */
	    fseek(F,att->begin_offset,SEEK_SET);

	    while (ftell(F) < att->offset + att->length) {
		int len = mail_gets(copy_buf,sizeof copy_buf,F);
		if (len <= 0) 
		    break; /* Error ? */
		
		/* Take care of CRLF => LF conversion (or
		   LF -> CRLF conversion)
		   -- metamail do not cope with CRLF
		*/
		state_convert_EOLN(copy_buf,&len,sizeof copy_buf,
				   &state_out);
		
		state_put(copy_buf,len,&state_out);
	    }
	    out_state_destroy(&state_out);
	    fclose(fpout);


	    /* Option -z causes that metamail unlinks tempfile */
	    elm_sfprintf(buf1,sizeof buf1,
			 FRM("%s -m Elm -h -z %s"),
			 metamail_val, tempfile);
	}

	Raw(OFF);

	system_call(buf1,SY_ENV_METAMAIL, NULL);

	PressAnyKeyToContinue();

	Raw(ON);
	menu_trigger_redraw(page);

    } else
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNotKnowPrint,
			  "Don't know how to print this type of data!"));
}

struct string * pick_name(str)
     const struct string *str;
{
    int L = string_len(str);
    struct string *ret = NULL;

    int i;
    int pos = 0;

    for (i = 0; i < L; i++) {
	uint16 code = give_unicode_from_string(str,i);

	if (0x002F /* / */ == code ||
	    code < 0x0020          ||
	    0x005C /* \ */ == code)
	    pos = i+1;
    }


    ret = clip_from_string(str,&pos,L);

    return ret;
}

static CONST char * pick_name_ascii P_((const char *str));
static CONST char * pick_name_ascii(str) 
     const char *str;
{
    const char *ret = str;
    const char *p;
    
    for (p = str; *p; p++) {
	if ('/'  == *p ||
	    '\\' == *p ||
	    isascii(*p) && iscntrl(*p))
	    ret = p+1;
    }

    return ret;
}

static char * give_extension P_((struct string * testname));
static char * give_extension(str) 
     struct string * str;
{
    int L = string_len(str);
    int i;
    int pos = -1;

    for (i = 0; i < L; i++) {
	uint16 code = give_unicode_from_string(str,i);

	if (0x002F /* / */ == code ||
	    code < 0x0020          ||
	    0x005C /* \ */ == code)
	    pos = -1;

	if (0x002E /* . */ == code)
	    pos = i+1;
    }

    if (-1 == pos)
	return 0;

    /* FIXME:   -- this may produce unexpected results if 
                   extension is non-ascii 

		   but that should not cause problems,
		   because that is just matched agaist 
		   mime.types
    */
    return us2s(streamclip_from_string(str,&pos,L,NULL,NULL));
}

/* 
 * We need check type= parameters from default elm.mimetypes 
 */

static int filter_scanlist_OK P_((const mime_t * att, 
				  const struct mime_types_item *type_item));
static int filter_scanlist_OK(att,type_item)
     CONST mime_t * att;
     CONST struct mime_types_item *type_item;
{
    charset_t cs = NULL;
    int r = 1;

    CONST char * params =  mime_type_to_params(type_item,&cs);
    char * temp,* opt;
    char * walk = NULL;
	
    
    if (!params)
	return 1;

    if (!att->TYPE_opts) {
	DPRINT(Debug,9,(&Debug,
			"filter_scanlist_OK: no parms -- paramlist %s\n",
			params));
	
	return 0;
    }


    temp = safe_strdup(params);

    for (opt = mime_parse_content_opts(temp, &walk); 
	 opt; 
	 opt = mime_parse_content_opts(NULL, &walk)) {

	char * q = strchr(opt,'=');
	char *val;
	CONST char *val1;

	if (!q) {

	    DPRINT(Debug,9,(&Debug,
			     "filter_scanlist_OK: bad param '%s' -- bad paramlist %s\n",
			     opt,params));

	    r = 0;
	    goto fail;
	}
	*q++ = '\0';

	if (NULL != strchr(opt,'*')) {

	    DPRINT(Debug,9,(&Debug,
			    "filter_scanlist_OK: unsupported param '%s' -- bad paramlist %s\n",
			     opt,params));
	    
	    r = 0;
	    goto fail;

	}


	val1 = get_mime_param_ascii(att->TYPE_opts,opt);

	if (!val1) {
	    DPRINT(Debug,9,(&Debug,
			    "filter_scanlist_OK: param '%s' not found -- paramlist %s\n",
			     opt,params));
	    
	    r = 0;
	    goto fail;

	}

	val = dequote_opt(q,strlen(q));

	if (0 != strcmp(val,val1)) {
	    DPRINT(Debug,9,(&Debug,
			    "filter_scanlist_OK: param '%s' -- values not match %s <> %s -- paramlist %s\n",
			    opt,val,val1,
			    params));
	    
	    r = 0;
	}

	free(val);

    }

    if (r) {
	DPRINT(Debug,9,(&Debug,
			"filter_scanlist_OK: paramlist OK: %s\n",
			params));
    }

 fail:
    free(temp);
    
    return r;
}
static void attach_save P_((FILE *F, mime_t *a, struct AliasView *aview,
			    struct menu_context  *page, 
			    struct screen_parts *LOC));
static void attach_save (F,a,aview, page, LOC)
     FILE *F;
     mime_t *a;
     struct AliasView *aview;
     struct menu_context  *page;
     struct screen_parts  *LOC;
{
    char buf[VERY_LONG_STRING];
    int bytes=0, err, is_text;
    in_state_t state_in;
    FILE *f_in = NULL;
    int code;
    int br_flags = 0;
    
    struct string         * savefile = NULL;
    struct string         * msgname  = NULL;
    struct string         * default_extension = NULL;
    struct folder_browser * br       = new_browser(selection_file);

    WRITE_STATE handle =  NULL;
    int use_attachment_dir   = 0;
    int was_decoded = 0;

    int LINES, COLUMNS;
    
    char * attachment_dir_val = 
	give_dt_estr_as_str(&attachment_dir_e,
			    "attachment-dir");


    menu_get_sizes(page,&LINES,&COLUMNS);


    in_state_clear(&state_in, STATE_in_file);

    if (attachment_dir_val &&
	0 == access(attachment_dir_val,ACCESS_EXISTS)) {
	DPRINT(Debug,15,(&Debug,
			 "attach_save: attachment-dir=%s set and exists\n",
			 attachment_dir_val));
	use_attachment_dir++;
    }
    
    if (a->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"attach_save",
		   "Bad magic number");
    
    if (get_major_type_code(a->TYPE) == MIME_TYPE_APPLICATION &&
	istrcmp (get_subtype_name(a->TYPE), "octet-stream") == 0) {

	int mp = give_dt_enumerate_as_int(&mime_parameters);
	CONST struct string *pv;
	CONST char * pva;
	struct string * testname = NULL;

	/* 0 == plain
	   1 == encoded
	   2 == plain-and-encoded
	*/
		    	


	/* See if there is a name=<...> field for the default filename */
	

	if ( mp &&
	     (pv = get_mime_param(a->TYPE_opts,"name"))) {

	    struct string * p = pick_name(pv);

	    
	    msgname = dup_string(p);
	    
	    if (use_attachment_dir) {
		testname = format_string(FRM("{doc}/%S"),p);
		
	    } else {
		testname = 	format_string(FRM("./%S"),p);
		
	    }

	    free_string(&p);
	    
	} else if ((pva = get_mime_param_compat(a->TYPE_opts,"name"))) {

	    CONST char * p = pick_name_ascii(pva);

	    msgname = format_string(FRM("%s"),p);

	    if (use_attachment_dir) {
		testname = format_string(FRM("{doc}/%s"),p);
		
	    } else {
		testname = format_string(FRM("./%s"),p);
		
	    }
	}

	if (testname) {
	    int ok = select_dir_item(br,&testname);
	    
	    if (ok) {
		int flags = give_dir_flags(br);
		
		if (0 == (flags & BROWSER_EXIST))    {
		    if (savefile)
			free_string(&savefile);
		    savefile = dup_string(testname);
		}
	    }
	    
	    free_string(&testname);
	}

    }
    

    /* See if there is a filename=<...> field for the default filename */


    {

	int mp = give_dt_enumerate_as_int(&mime_parameters);
	CONST struct string *pv;
	CONST char * pva;
	struct string * testname = NULL;

	if ( mp &&
	     (pv = get_mime_param(a->DISPOSITION_opts,"filename"))) {
	
	    struct string * p = pick_name(pv);
	
	    if (p) {

		if (msgname)
		    free_string(&msgname);
		msgname = dup_string(p);

		if (use_attachment_dir) {
		    testname = 	format_string(FRM("{doc}/%S"),p);
		    
		} else {
		    testname = 	format_string(FRM("./%S"),p);
		    
		}
	    
		free_string(&p);
	    }
	    
	} else if ( (pva = get_mime_param_compat(a->DISPOSITION_opts,
						 "filename"))) {

	    CONST char * p = pick_name_ascii(pva);

	    if (p) {

		if (msgname)
		    free_string(&msgname);
		msgname = format_string(FRM("%s"),p);
	       
		if (use_attachment_dir) {
		    testname = 	format_string(FRM("{doc}/%s"),p);
		    
		} else {
		    testname = 	format_string(FRM("./%s"),p);
		    
		}		
	    }
	}
	
	if (testname) {
	    int ok = select_dir_item(br,&testname);
	    
	    if (ok) {
		int flags = give_dir_flags(br);
		
		if (0 == (flags & BROWSER_EXIST))    {
		    if (savefile)
			free_string(&savefile);
		    savefile = dup_string(testname);
		}
	    }

	    free_string(&testname);
	}

    }
        
    if (!savefile && use_attachment_dir)
	savefile = format_string(FRM("{doc}/"));


    if (!(f_in = get_decoded_attachment(F,a, &was_decoded)))  {
	goto clean;
    }

    if (!was_decoded) {

	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmUnsupportedEncoding,
			  "Unsupported encoding! Decode manually!"));

	if (attachment_fix_extension)
	    default_extension = format_string(FRM("encoded"));

    } else if (attachment_fix_extension || check_type_pattern) {
	struct scan_list *scanlist = NULL;
	struct scan_list *Y;
	int ch;
	struct mime_types_item *found;
	char * ext = NULL;

	if (user_mimetypes_map)
	    scanlist = get_scan_list_by_type(user_mimetypes_map,a->TYPE);
	if (system_mimetypes_map) {
	    struct scan_list * X = get_scan_list_by_type(system_mimetypes_map,
							 a->TYPE);
	    
	    if (X) {
		if (scanlist) {
		    append_scanlist(scanlist,X);
		    free_scan_list(&X);
		} else
		    scanlist = X;
	    }
	}
	
	Y = get_scan_list_by_type(builtin_mimetypes_map,a->TYPE);
	
	if (Y) {
	    if (scanlist) {
		append_scanlist(scanlist,Y);
		free_scan_list(&Y);
	    } else
		scanlist = Y;
	}
	
	if (!scanlist) {
	    DPRINT(Debug,7,(&Debug, 
			    "attach_save --- no scanlist\n"));
	    goto no_scan;
	}

	filter_scanlist(scanlist,a,filter_scanlist_OK);

	if (! have_scanlist(scanlist)) {
	    DPRINT(Debug,7,(&Debug, 
			    "attach_save --- empty scanlist\n"));
	    
	    goto no_scan1;
	}

	while (EOF != (ch = getc(f_in))) {
	    int need_more = 0;
	    
	    need_more = scanlist_need_more(scanlist,ch);
	    
	    if (!need_more) {
		DPRINT(Debug,9,(&Debug,    
				"attach_save: magic scan finished\n"));
		break;
	    }
	}

	if (msgname) {
	    DPRINT(Debug,9,(&Debug,    
 			    "attach_save: filename %S\n",
 			    msgname));
	    ext = give_extension(msgname);
	}
	
	if ( ext) {
 	    DPRINT(Debug,9,(&Debug, 
 			    "attach_save: extension %s\n",ext));
	    found = loc_mime_type_from_scan_extension(scanlist,ext);
	} else {
 	    DPRINT(Debug,9,(&Debug, 
			    "attach_save: No extension\n"));
	    found = loc_mime_type_from_scan(scanlist);
	}

	if (found && attachment_fix_extension) {
	    CONST char * ext1 = mime_type_to_extension(found);

	    if (ext1) {
 		DPRINT(Debug,9,(&Debug, 
 				"attach_save: Fixed extension: %s\n",
 				ext1));

		default_extension = format_string(FRM("%s"),
						  ext1);
	    }
	}

	if (!found) {
	    if (get_major_type_code(a->TYPE) == MIME_TYPE_APPLICATION &&
		0 == istrcmp(get_subtype_name(a->TYPE),"octet-stream")) {
		
		DPRINT(Debug,5,(&Debug, 
				"attach-save  -- APPLICATION/octet-stream is unkonw/anonymous type\n"));
		

	    } else if (msgname)
		lib_error(CATGETS(elm_msg_cat, MeSet, MeMiscMatchMessage1,
				  "Signature of %S do not match to type %s/%s"),
			  msgname,
			  get_major_type_name(a->TYPE), 
			  get_subtype_name(a->TYPE));
	    else
		lib_error(CATGETS(elm_msg_cat, MeSet, MeMiscMatchMessage2,
				  "Signature of this attachment do not match to type %s/%s"),
			  get_major_type_name(a->TYPE), 
			  get_subtype_name(a->TYPE));
		
	}

    no_scan1:
	free_scan_list(&scanlist);


    no_scan:
	rewind(f_in);

	if (ext)
	    free(ext);
    }


    if (msgname)
	free_string(&msgname);


    code = file_browser(page,br,&savefile,word_save,
			aview,default_extension,
			CATGETS(elm_msg_cat, MeSet, MeToFile,
				"To file: "));

    if (!code) {
	ClearLine(LINES-3);
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmMailNotSaved,
			  "Mail not saved."));
	
	goto clean;
    }

    if (savefile)
	free_string(&savefile);
    if (default_extension)
	free_string(&default_extension);

    savefile = selection_name_dir(br);

    br_flags = give_dir_flags(br);

    DPRINT(Debug,4, (&Debug, 
		     "*** %S have flags:%s%s%s%s%s%s%s%s\n",
		     savefile,
		     br_flags & BROWSER_NODIR    ?   " NODIR":    "",
		     br_flags & BROWSER_NOFOLDER ?   " NOFOLDER": "",
		     br_flags & BROWSER_MARKED   ?   " MARKED":   "",
		     
		     br_flags & BROWSER_MAILFILE ?   " MAILFILE": "",
		     br_flags & BROWSER_SELECTED ?   " SELECTED": "",
		     br_flags & BROWSER_EXIST    ?   " EXIST"   : "",
		     br_flags & BROWSER_DIRPREFIX ?   " DIRPREFIX"   : "",
		     !br_flags                   ?   " none"    : ""));
	   
	   if ((br_flags & BROWSER_EXIST) == 0) {
	/* Create it now ... */
	if (!create_selection_dir(br)) {
	    ClearLine(LINES-3);
	    goto clean;
	}
    } else {
	/* Confirm overwrite */

	char * msg_buffer = NULL;
	char   answer;

	msg_buffer = elm_message(CATGETS(elm_msg_cat, ElmSet, 
					 ElmConfirmFileOverwrite,
					 "Overwrite existing file `%S'? (%c/%c) "),
				 savefile, 
				 *def_ans_yes, *def_ans_no);

	answer = want_to(msg_buffer, *def_ans_no, LINES-3, 1,
			 page);
	free(msg_buffer);

	if (answer != *def_ans_yes) {		   
	    ClearLine(LINES-3);
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmMailNotSaved,
			      "Mail not saved."));
	    goto clean;
	}
    }

    ClearLine(LINES-3);
  

    if (!prepare_write_folder(br,&handle))
	goto clean;
    else {
	out_state_t state_out;
	charset_t charset_vector[2];
	
	out_state_clear(&state_out, STATE_out_dir);

	/* Needed by STATE_out_dir */
	charset_vector[0] = RAW_BUFFER;
	charset_vector[1] = NULL;

	set_in_state_file(f_in,&state_in);

	state_out.display_charset = charset_vector;
	set_out_state_dir(br,handle, &state_out);    
	state_out.prefix = NULL;
	state_out.displaying = 0;
	    
	if (!state_copy(&state_in,&state_out)) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeFailedCopy,
			      "Failed to copy file when decoding."));
	}

	out_state_destroy(&state_out);
    }
    err = !end_write_folder(br,&handle);
    
    if (err)
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorSaving,
			  "Error saving file!"));
    else
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmMailSaved,
			  "Mail saved."));
    
 clean:
    in_state_destroy(&state_in);
        
    if (f_in)
	close_attachment(F,f_in);

    if (savefile)
	free_string(&savefile);
    free_dir(&br);

    
    menu_trigger_redraw(LOC->prompt_page);
    menu_trigger_redraw(LOC->menu_page);
		    
    return;
}

static int attach_info P_((mime_t *ptr,struct mailer_info  *mailer_info, 
			   int new));
static void attach_edit P_((mime_t *ptr, struct mailer_info  *mailer_info));

static void attach_edit (ptr,mailer_info)
     mime_t *ptr;
     struct mailer_info  *mailer_info;
{
    int savetime;
    struct stat sb;
    char buf[STRING];
    char *editor_val;
    
    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"attach_edit",
		   "Bad magic number");
    
    if (!ptr->pathname0)
	return;
    
    if (-1 == stat (ptr->pathname0, &sb)) {
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantStat,
			  "Can't stat file!"));
	sleep_message();
	return;
    }
    
    editor_val = give_dt_estr_as_str(&editor_e,"editor");


    if (!editor_val ||
	strlen(ptr->pathname0) + strlen(editor_val) > STRING-5)
	return;
    savetime = sb.st_mtime;

    Raw(OFF);
    elm_sfprintf (buf, sizeof buf,
		  FRM("%s %s"), editor_val, ptr->pathname0);
    system_call (buf, 0, NULL);
    Raw(ON);

    if (stat (ptr->pathname0, &sb) == -1 || sb.st_mtime != savetime)
	(void) attach_info (ptr,mailer_info,0);  /* update the information on this attachment */
    return;
}

static void attach_viewer P_((FILE *F,mime_t *a, 
			      struct header_rec *hdr,
			      struct menu_context *page));

static void attach_viewer (F,a, hdr, page)
     FILE *F;
     mime_t *a;
     struct header_rec *hdr;
     struct menu_context *page;
{
    char buf1[LONG_STRING];

    struct header_rec tmp;
    FILE *tmpfp = NULL;
    struct stat sb;

    int is_need_meta = 0;
    
    /* The next line is required so that mime_t_clear() doesn't call
     * mime_destroy() or free() on bad pointers
     */
    header_zero(&tmp);
    mime_t_clear (&tmp.mime_rec);
    
    if (a->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"attach_viewer",
		   "Bad magic number");
    
    /* So that metapager() doesn't try to do special handling */
    tmp.status = MIME_MESSAGE;  /* Now I test again MIME_MESSAGE
				 * - K E H   <hurtta@dionysos.FMI.FI>
				 */

    tmp.header_charset = display_charset;
    if (hdr)
	tmp.override_charset = hdr->override_charset;

#ifdef USE_PGP
    tmp.pgp = 0;
#endif

    if (a->pathname0) {
	menu_ClearScreen(page); /* Extra clear for mime_parse_routine ... */
	
	if (can_open(a->pathname0,"r") != 0) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorReadableUser,
			      "%S isn't readable by user!"), 
		      a->dispname);
	    sleep_message();
	    return;
	}

	tmpfp = fopen (a->pathname0, "r");
	if (! tmpfp) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCouldOpenReading,
			      "Could not open file for reading!"));
	    sleep_message();
	    return;
	}
	
	tmp.mime_rec.TYPE = a->TYPE;
	if (a->TYPE_opts) {
	    tmp.mime_rec.TYPE_opts = copy_mime_param(a->TYPE_opts);
	}

	tmp.mime_rec.mime_flags = a->mime_flags;  
	/* NOTPLAIN_need_metamail           0x01
	   NOTPLAIN_need_mailcap            0x02
	   NOTPLAIN_canuse_mailcap          0x04
	*/
	
	tmp.mime_rec.disposition = DISP_INLINE;  /* Show it ! */
	tmp.offset = tmp.mime_rec.offset = 0;
	tmp.mime_rec.encoding = ENCODING_7BIT;
	stat(a->pathname0, &sb);
	tmp.content_length = tmp.mime_rec.length = sb.st_size;
	mime_parse_routine(NULL,&tmp, tmpfp);
	
    } else {
	
	/* Make copy of mime structure: */
	mime_t_copy(&(tmp.mime_rec),a);
	tmp.mime_rec.disposition = DISP_INLINE;  /* Show it ! */
	
	tmp.offset = tmp.mime_rec.offset = a -> offset;
	tmp.content_length = tmp.mime_rec.length = a->length;
#ifdef USE_PGP
	if (hdr)
	    tmp.pgp = hdr->pgp;
#endif
  }

    /* This value is used for selecting external pager versus internal
     * pager.
     */
    if (tmp.lines < 1)
	tmp.lines = tmp.content_length / 60; 

    /* there is nothing to display! */
    if (tmp.mime_rec.length <= 0)
	goto fail;

    DPRINT(Debug,9,	   
	   (&Debug,"attach_viewer: Looking %p=%s/%s ...\n",
	    tmp.mime_rec.TYPE,
	    get_major_type_name(tmp.mime_rec.TYPE), 
	    get_subtype_name(tmp.mime_rec.TYPE)));


    /* mime_t_copy() is called copy_parser_data() so 
       mime_parser_parse() need not to be called
       ... however classification is not copied!!
    */
    mime_classify_media(&(tmp.mime_rec),&tmp);

    is_need_meta = need_meta(&tmp,0,0);
    if (is_need_meta < 0) {
	goto fail;
    }

	   
    if (!is_need_meta) {
	struct pager_page *PP =  init_pager_page(NULL);

	if (tmpfp)
	    metapager (tmpfp, &tmp, FALSE, 0,0, PP);
	else if (F)
	    metapager (F, &tmp, FALSE, 0,0, PP);

	exit_pager_page(&PP,page);
    } else {
	struct menu_context *cpage;

	char * metamail_val = 
	    give_dt_estr_as_str(&metamail_path_e, "metamail");

	if (!metamail_val)
	    goto fail;

	/* otherwise call metamail */

	if (!a->pathname0) {
	    char tmpfile[STRING];
	    char copy_buf[VERY_LONG_STRING];
	    FILE *out;
	    
	    out_state_t state_out;
	    char *tmp;
	    
	    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
	    if (!tmp)
		goto fail;

	    elm_sfprintf (tmpfile, sizeof tmpfile,
			  FRM("%selm.%d"), tmp, getpid());
	    
	    out = safeopen(tmpfile);
	    
	    if (out == NULL) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorCreatTempfile,
				  "Error creating tempfile %s"),   
			  tmpfile);
		sleep_message();
		goto fail;
	    }

	    (void) elm_chown (tmpfile, userid, groupid);

	    out_state_clear(&state_out, STATE_out_file);
	    set_out_state_file(out,&state_out);

	    state_out.EOLN_is_CRLF   = 0;  
	    /* !! Metamail do not cope with CRLF */

	    /* copy the headers plus the message to a temp file */
	    fseek(F,a->begin_offset,SEEK_SET);
		
	    while (ftell(F) < a->offset + a->length) {
		int len = mail_gets(copy_buf,sizeof copy_buf,F);
		if (len <= 0) 
		    break; /* Error ? */
		    
		/* Take care of CRLF => LF conversion (or
		   LF -> CRLF conversion)
		   -- metamail do not cope with CRLF
		*/
		state_convert_EOLN(copy_buf,&len,sizeof copy_buf,
				   &state_out);
		
		state_put(copy_buf,len,&state_out);
	    }
	    out_state_destroy(&state_out);
	    fclose(out);

	    /* Option -z causes that metamail unlinks tempfile */
	    elm_sfprintf (buf1, sizeof buf1,
			  FRM("%s -m Elm -p -z %s"), metamail_val,tmpfile);

	} else
	    /* This is the case when we are viewing attachments for an outgoing
	     * message.
	     */

	    elm_sfprintf(buf1,sizeof buf1,
			 FRM("%s -m Elm -p -b -c %s/%s %s"), 
			 metamail_val,
			 get_major_type_name(a->TYPE), 
			 get_subtype_name(a->TYPE), 
			 a->pathname0);
    

	/* Option -z causes that metamail deletes input file */
	
	cpage = Raw (OFF);
	menu_ClearScreen(cpage);

	printf ("Executing: %s\n", buf1);
	system_call (buf1, SY_ENAB_SIGINT|SY_ENV_METAMAIL, NULL);

	PressAnyKeyToContinue();
	Raw (ON);
    }
    
 fail:
    mime_t_clear (&(tmp.mime_rec));
    
    if (tmpfp) {
	fclose (tmpfp);
    }
    
    return;
}

/* Check initial attachments */
int Check_attachments() 
{ 
    if (attach_files.attachment_count > 0) {
	int i;
	
	/* this is not necessary correct ... */
	struct mailer_info  *mailer_info = get_mailer_info();
   
	if (!mailer_info) {
	    /* mailer not available */

	    sleep_message();
	    return 0;
	}
	    
	for (i = 0; i < attach_files.attachment_count; i++) {
	    int need_enc;
	    int is_text;

	    if (!attach_files.attach_files[i].dispname)
		attach_files.attach_files[i].dispname = 
		    new_string2(system_charset,
				s2us(attach_files.attach_files[i].pathname0));

	    need_enc = attach_info (&(attach_files.attach_files[i]),
				    mailer_info,1);
	    if (need_enc < 0) {
		return 0;
	    }

	    /* 7bit and 8bit are only allowed for line orienteed types */
	    /* But do not check attach_files.attach_files[i].encoding
	            agaist ENCODING_NONE, ENCODING_7BIT, ENCODING_8BIT
		    when sending 
	    */
	    is_text = give_text_type_code(attach_files.attach_files[i].
					  TYPE);
	    
	    if (is_text < 0 && (attach_files.attach_files[i].encoding == 
				ENCODING_QUOTED || 
				attach_files.attach_files[i].encoding == 
				ENCODING_BASE64)) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmStrucredNoEncoding,
				  "%S: Structured types don't allow encoding of data."),
			  attach_files.attach_files[i].dispname);
		sleep_message();
	    
		return 0;
	    }
	}

	free_mailer_info(&mailer_info);
    }


    return 1;
}

/* Make initial attachment */
int Attach_it(pathname) 
     char *pathname;
{
    mime_t tmp;
    
    if (access(pathname,READ_ACCESS) < 0) {
	int err = errno;
	lib_error(FRM("%.45s: %.33s"),
		  pathname,error_description(err));
	return 0;
    }
    
    mime_t_zero(&tmp);
    
    tmp.pathname0 = safe_strdup(pathname);
    tmp.dispname  = NULL;  /* Filled later */

    /* Default disposition for attachments: attachment */
    tmp.disposition = DISP_ATTACH;

    add_Attachments(&attach_files,&tmp);

    return 1;
}

static void forgot_previous P_((mime_t *att));
static void forgot_previous(att)
     mime_t *att;
{
    if (att->pathname0) {		    
	if (att->unlink) {
	    if (unlink(att->pathname0) == 0) {
		DPRINT(Debug,5, (&Debug, 
				 "attach_modify: Unlinking cache %s\n",
				 att->pathname0));
	    }
	}
	
	free (att->pathname0);
	att->pathname0 = NULL;
    }

    if (att->dispname)
	free_string(& (att->dispname));
}


static int attach_modify P_((mime_t *att, int new,
			     struct mailer_info  *mailer_info,
			     struct AliasView *aview));
static int attach_modify (att, new, mailer_info,aview)
     mime_t *att;
     int new;
     struct mailer_info  *mailer_info;
     struct AliasView *aview;
{
    int ret_value = FALSE;


    int update = TRUE, prompt = TRUE, need_enc = 0;
    int is_text = -1, ch = '\0';
    struct folder_browser * br = new_browser(selection_file);
    struct menu_context  *page = new_menu_context();

    if (att->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"attach_modify",
		   "Bad magic number");

    DPRINT(Debug,6, (&Debug, "attach_modify: att=%p, new=%d\n",att,new));

    
    if (new) {
	/* Default disposition for attachments: attachment */
	att->disposition = DISP_ATTACH;
	
	/* force a prompt for a filename */
	prompt = FALSE;
	ch = 'f';
	att->unlink = 0;
    } else {
	need_enc = attach_info (att, mailer_info,0);
	if (need_enc < 0) {
	    prompt = FALSE;
	    ch = 'f';
	}
    }

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


    /* 7bit and 8bit are only allowed for line orienteed types */
    /* But do not check attach_files.attach_files[i].encoding
       agaist ENCODING_NONE, ENCODING_7BIT, ENCODING_8BIT
       when sending 
    */
    is_text = give_text_type_code(att->TYPE);

    for (;;) {
	int LINES, COLUMNS;

    resize_mark:
	menu_get_sizes(page,&LINES,&COLUMNS);

	if (menu_resized(page)) {
	    
	    menu_get_sizes(page,&LINES,&COLUMNS);
	    update++;

	} else if (menu_need_redraw(page))
	    update++;

	if (update) {
	    struct string * Title = 
		format_string(CATGETS(elm_msg_cat, MeSet, MeAttachTitle,
				      "Attachment Configuration"));      
	    int add = 0;
	    int Width = COLUMNS - 16;
	    if (Width < 40)
		Width = 40;
	    
	    menu_ClearScreen(page);

	    print_center(1,Title);
	    free_string(&Title);
	    
	    if (att->unlink == 2) 
		PutLineX(3, 0, 
			 CATGETS(elm_msg_cat, MeSet, MeAttachFilenameS,
				 "Filename                  : %.*S"),  
			 Width,att->dispname);
	    else  if (att->dispname)
		PutLineX(3, 0, 
			 CATGETS(elm_msg_cat, MeSet, MeAttachFilename3,
				 "F)ilename                 : %.*S"),  
			 Width,att->dispname);
	    else
		PutLineX(3, 0, 
			 CATGETS(elm_msg_cat, MeSet, MeAttachFilename0,
				 "F)ilename                 : <none>"));


	    if (!att->description)
		att->description = new_string(display_charset);
	    PutLineX(4, 0, 
		     CATGETS(elm_msg_cat, MeSet, MeAttachDescription,
			         "D)escription              : %.*S"),  
		     Width, att->description);

	    PutLineX(5, 0, 
		     CATGETS(elm_msg_cat, MeSet, MeAttachContentType,
			     "Content-T)ype             : %.15s/%.30s%s"), 
		     get_major_type_name(att->TYPE), 
		     get_subtype_name(att->TYPE),
		     att->TYPE_opts ? ";" : "");

	    if (att->TYPE_opts) {
		struct string * X = show_mime_params(att->TYPE_opts);
		
		if (X) {
		    PutLineX(6+add, 28, FRM("%S"),X);
		    add++;

		    free_string(&X);
		}
	    }

	    PutLineX(6+add, 0, 
		     CATGETS(elm_msg_cat, MeSet, MeAttachContentTE,
			         "Content-transfer-E)ncoding: %s"), 
		     ENCODING(att->encoding));
     
	    PutLineX(7+add, 0, 
		     CATGETS(elm_msg_cat, MeSet, MeAttachContentTE,
			     "Content-disP)osition      : %.15s%s"), 
		     DISPOSITION(att->disposition),
		     att->DISPOSITION_opts ? ";" : "");

	    if (att->DISPOSITION_opts) {
		struct string * X = show_mime_params(att->DISPOSITION_opts);

		if (X) {
		    PutLineX(8+add, 28, FRM("%S"), X);
		    add++;

		    free_string(&X);
		}
	    }
	    
	    if (is_text < 0)
		PutLineX(9+add, 0, 
			 CATGETS(elm_msg_cat, MeSet, MeAttachContentCRLF1,
				 "CRLF-conversions          : structured (direct content-encoding not allowed)"));
	    else if (is_text)
		PutLineX(9+add, 0, 
			 CATGETS(elm_msg_cat, MeSet, MeAttachContentCRLF2,
			         "CRLF-conversions          : Text (line orienteed, convert LF <-> CRLF)"));
	    else 
		PutLineX(9+add, 0, 
			 CATGETS(elm_msg_cat, MeSet, MeAttachContentCRLF3,
			         "CRLF-conversions          : Binary (no conversions)"));

	    update = FALSE;
	    show_last_error();
	}
	
	if (prompt) {
	    menu_PutLineX (page,
			   LINES-3, 0, 
			   CATGETS(elm_msg_cat, MeSet, MeAttachEnter1,
				   "Enter letter or RETURN to quit: "));
	    menu_CleartoEOS(page);
	    ch = menu_ReadCh(page,REDRAW_MARK|READCH_resize|READCH_sig_char);
	    clear_error();
	}
	
	if (ch == '\n' || ch == 'q' || ch == 'Q' || ch == 'x' || ch == 'X') {
	    ret_value = TRUE;
	    goto cleanup;
	} else if (ch == RESIZE_MARK) {
	    DPRINT(Debug,4,(&Debug,
			    "   ... resizing\n"));
	    goto resize_mark;
	} else if (ch == ctrl('L') || ch == REDRAW_MARK)
	    update = TRUE;
	else if (ch == TERMCH_interrupt_char) 
	    goto cleanup;
  	else if (ch == 'f' || ch == 'F') {

	    struct string * tmpname = NULL;
	    int old_is_text = is_text;
	    int loop_count = 0;
	    int use_attachment_dir = 0;

	    char * attachment_dir_val = 
		give_dt_estr_as_str(&attachment_dir_e,
				    "attachment-dir");

	    int code;
	    
	    if (att->unlink == 2)  {
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmCantChangeFilename,
				  "You can't change filename!"));
		continue;
	    }
	    
	    if (attachment_dir_val &&
		0 == access(attachment_dir_val,ACCESS_EXISTS)) {
		DPRINT(Debug,15,(&Debug,
				 "attach_modify: attachment-dir=%s set and exists\n",
				 attachment_dir_val));
		use_attachment_dir++;
	    }

	    if (!att->dispname) {
		DPRINT(Debug,15,(&Debug,
				 "attach_modify: No dispname\n"));

	    } else
		tmpname = dup_string(att->dispname);
	    
	    if (!tmpname && use_attachment_dir)
		tmpname = format_string(FRM("{doc}/"));

	    prompt = TRUE;

	    code = file_browser(page,br,&tmpname,word_read,
				aview,
				NULL,
				CATGETS(elm_msg_cat, MeSet, 
					MeAttachEnterFilename,
					"Filename: "));

	    if (!code) {
		if (!tmpname || string_len(tmpname) == 0) {
		    if (tmpname)
			free_string(&tmpname);


		    DPRINT(Debug,5, (&Debug, 
				     "attach_modify: filename not given"));
		    if (att->pathname0) {
			DPRINT(Debug,5, (&Debug, " pathname0=%s",
					 att->pathname0));
		    }
		    if (att->dispname) {
			DPRINT(Debug,5, (&Debug, " dispname=%S",
					 att->dispname));
		    }
		    DPRINT(Debug,5, (&Debug, "\n"));

		    if (new) {
			ret_value = FALSE;
			goto cleanup;
		    } else
			continue;
		}

	    attach_failure:

		if (tmpname)
		    free_string(&tmpname);		

		update = TRUE;
		
		if (att->pathname0) {		    
		    if (att->unlink) {
			if (unlink(att->pathname0) == 0) {
			    DPRINT(Debug,5,
				   (&Debug,
				    "attach_modify: Unlinking cache %s\n",
				    att->pathname0));
			}
		    }

		    free (att->pathname0);
		    att->pathname0 = NULL;
		}
		if (new) {
		    ret_value = FALSE;
		    goto cleanup;
		} else
		    continue;
	    }

	    update = TRUE;

	    do {
		int br_flags = 0;
		int iscopy;

		old_is_text = is_text;

		if (tmpname)
		    free_string(&tmpname);

		tmpname = selection_name_dir(br);

		br_flags = give_dir_flags(br);

		DPRINT(Debug,4, (&Debug, 
				 "*** %S have flags:%s%s%s%s%s%s%s%s\n",
				 tmpname,
				 br_flags & BROWSER_NODIR    ?   " NODIR":    "",
				 br_flags & BROWSER_NOFOLDER ?   " NOFOLDER": "",
				 br_flags & BROWSER_MARKED   ?   " MARKED":   "",
				 
				 br_flags & BROWSER_MAILFILE ?   " MAILFILE": "",
				 br_flags & BROWSER_SELECTED ?   " SELECTED": "",
				 br_flags & BROWSER_EXIST    ?   " EXIST"   : "",
				 br_flags & BROWSER_DIRPREFIX ?   " DIRPREFIX"   : "",
				 !br_flags                   ?   " none"    : ""));
			   
		if ((br_flags & BROWSER_EXIST) == 0) {
		    lib_error(CATGETS(elm_msg_cat, MeSet, MeAttachNotExist,
				      "File %S not exist"),
			      tmpname);		
		    goto attach_failure;
		}
	    
		/* Now forgot previous file .... 
		   forgot_previous: clears  att->dispname 
                                       and  att->pathname0
		*/

		forgot_previous(att);
		
		att->dispname = tmpname;
		tmpname = NULL;

		iscopy = att->unlink;
		if (!dir_make_ref(br, & (att->pathname0), & iscopy,
				  is_text)) {
		    att->unlink = iscopy;
		    goto attach_failure;
		}
		att->unlink = iscopy;

		/* Set some information about this attachment */
		need_enc = attach_info (att, mailer_info,1);
		if (need_enc < 0)
		    continue;
		
		/* 1 if is text type (true)
		 * 0 if not text type
		 * -1 if can't be encoded (ie structured) Message/ or Multpart/
		 */
		
		/* 7bit and 8bit are only allowed for line orienteed types */
		/* But do not check attach_files.attach_files[i].encoding
		   agaist ENCODING_NONE, ENCODING_7BIT, ENCODING_8BIT
		   when sending 
		*/
		is_text = give_text_type_code(att->TYPE);

		    
		if (old_is_text != is_text) {
		    DPRINT(Debug,1,
			   (&Debug,
			    "attach_modify: OPPS! CLRF encoding changed...\n"));
		}
	    } while (old_is_text != is_text && loop_count++ < 5);

	    
	    if (is_text > 0 && (need_enc & HAVE_BINARY)) {
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmWarningBINARY,
				  "Warning: BINARY data? Check Content-Type!"));
	    }

	    if (is_text < 0 && (att->encoding == ENCODING_QUOTED || 
				att->encoding == ENCODING_BASE64)) {
		/* Reconsider encoding ... */
		ch = 'e';
		prompt = FALSE;
		update = TRUE;
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmStructuredNoencoding,
				  "%S: Structured types don't allow encoding of data."),
			  att->dispname);
		sleep_message();
		
		break;
	    } 
	    
	    /* now let the user do what they want */
	    if (new)
		prompt = TRUE;
	    
	}
	else if (ch == 'd' || ch == 'D') {
	    int code = optionally_enter2(page,
					 &(att->description),
					 LINES-3, 0, 
					 OE_APPEND_CURRENT|OE_REDRAW_MARK|
					 OE_SIG_CHAR /* Ctrl-C */,
					 CATGETS(elm_msg_cat, MeSet, 
						 MeAttachEnterDescription,
						 "Description: "));

	    prompt = TRUE;
	    
	    if (REDRAW_MARK == code) {
		update = TRUE;
		prompt = FALSE;
		continue;
	    }
	    if (code != 0)
		continue;
	    
	    update = TRUE;
	}
	else if (ch == 't' || ch == 'T') {
	    int old_is_text = is_text;
	    int loop_count = 0;
	    int code;
	    struct string *buf1 = NULL;
	    char **X;

	    prompt = TRUE;

	    if (att->TYPE_opts) {
		struct string *buf2 = NULL;

		buf2 =  show_mime_params(att->TYPE_opts);
		
		if (buf2) {
		    buf1 = format_string(FRM("%.15s/%.30s; %S"), 
					 get_major_type_name(att->TYPE), 
					 get_subtype_name(att->TYPE),
					 buf2);
		    
		    free_string(&buf2);
		} else
		    buf1 = format_string(FRM("%.15s/%.30s"), 
					 get_major_type_name(att->TYPE), 
					 get_subtype_name(att->TYPE));
	    } else
		buf1 = format_string(FRM("%.15s/%.30s"), 
				     get_major_type_name(att->TYPE), 
				     get_subtype_name(att->TYPE));
		

	    code = optionally_enter2(page,&buf1,
				     LINES-3, 0, 
				     OE_APPEND_CURRENT|
				     OE_REDRAW_MARK|OE_SIG_CHAR /* Ctrl-C */, 
				     CATGETS(elm_msg_cat, MeSet, 
					     MeAttachEnterContentType,
					     "Content-Type: "));

	    if (REDRAW_MARK == code) {
		update = TRUE;
		prompt = FALSE;

		if (buf1)
		    free_string(&buf1);

		continue;
	    }

	    if (0 != code) {
		if (buf1)
		    free_string(&buf1);
		continue;
	    }


	    {
		int mp = give_dt_enumerate_as_int(&mime_parameters);
		/* 0 == plain
		   1 == encoded
		   2 == ascii-and-encoded
		*/
		
		switch (mp) {
		case 0:
		    X = split_mime_params(& (att->TYPE_opts),buf1,0,1);
		    break;
		case 1:
		    X = split_mime_params(& (att->TYPE_opts),buf1,0,0);
		    break;
		default:
		    X = split_mime_params(& (att->TYPE_opts),buf1,
					  MAX_COMPAT_LEN,0);
		    break;
		}
	    }

	    free_string(&buf1);
	    if (X) {
		media_type_t T = NULL;

		if (X[0] &&
		    X[1] && 0 == strcmp(X[1],"/") &&
		    X[2])
		    T =  give_media_type(X[0],X[2],1);

		if (T)
		    att->TYPE = T;

		free_rfc822tokenized(X);
	    }


	    if (get_major_type_code(att->TYPE) == MIME_TYPE_TEXT && 
		(need_enc & HAVE_8BIT) &&
		!(get_mime_param_compat(att->TYPE_opts,"charset")) &&
		display_charset->MIME_name)
		mime_params_add_compat(& (att->TYPE_opts),
				       "charset", display_charset->MIME_name);

	    do {
		int iscopy;
		old_is_text = is_text;

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

		/* 7bit and 8bit are only allowed for line orienteed types */
		/* But do not check attach_files.attach_files[i].encoding
		   agaist ENCODING_NONE, ENCODING_7BIT, ENCODING_8BIT
		   when sending 
		*/
		is_text = give_text_type_code(att->TYPE);

		if (is_text == 0 &&
		    (att->encoding == ENCODING_7BIT ||
		     att->encoding == ENCODING_8BIT))
		    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmWarningEncoding,
				      "%s is allowed only for line orienteed types -- check encoding"),
			  ENCODING(att->encoding));
		
		/* We reload file if it is user suplied */
		if (old_is_text != is_text && att->dispname) {

		    struct string *XX = att->dispname;

		    /* forgot_previous()  frees att->dispname */
		    att->dispname = NULL;

		    DPRINT(Debug,1,
			   (&Debug,
			    "attach_modify: OPPS! CLRF encoding changed...\n"));		    
		    forgot_previous(att);
		    
		    if (!select_dir_item(br,&XX)) {
			ch = 'f';
			prompt = FALSE;

			free_string(&XX);
			goto main_loop;
		    }
		    
		    iscopy = att->unlink;
		    if (!dir_make_ref(br, & (att->pathname0), & iscopy,
				      is_text)) {
			ch = 'f';
			prompt = FALSE;
			att->unlink = iscopy;

			free_string(&XX);
			goto main_loop;
		    }
		    att->dispname = XX;
		    att->unlink   = iscopy;

		    /* Set some information about this attachment */
		    need_enc = attach_info (att, mailer_info,0);
		    if (need_enc < 0) {
			ch = 'f';
			prompt = FALSE;
			goto main_loop;
		    }
		}
	    } while (old_is_text != is_text && loop_count++ < 5 &&
		     att->dispname);


	    update = TRUE;
	    if (is_text < 0 && (att->encoding == ENCODING_QUOTED || 
				att->encoding == ENCODING_BASE64)) {
		/* Reconsider encoding ... */
		ch = 'e';
		prompt = FALSE;
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmStructuredNoencoding,
				  "%S: Structured types don't allow encoding of data."),
			  att->dispname);
		sleep_message();
	    } 
	}
	else if (ch == 'p' || ch == 'P') {
	    int code;

	    struct string *buf1 = NULL;
	    char **X;
	    
	    prompt = TRUE;

	    if (att->DISPOSITION_opts) {
	       
		struct string *buf2 = NULL;

		buf2 =  show_mime_params(att->DISPOSITION_opts);
		
		if (buf2) {
		    buf1 = format_string(FRM("%.30s; %S"), 
					 DISPOSITION(att->disposition),
					 buf2);
		    
		    free_string(&buf2);
		} else
		    buf1 = format_string(FRM("%.30s"), 
					 DISPOSITION(att->disposition));
	    } else
		buf1 = format_string(FRM("%.30s"), 
				     DISPOSITION(att->disposition));
	    

	    code = optionally_enter2(page,&buf1,
				     LINES-3, 0, 
				     OE_APPEND_CURRENT|
				     OE_REDRAW_MARK|OE_SIG_CHAR /* Ctrl-C */, 
				     CATGETS(elm_msg_cat, MeSet, 
					     MeAttachEnterContentDisp,
					     "Content-Disposition: "));

	    if (REDRAW_MARK == code) {
		update = TRUE;
		prompt = FALSE;

		if (buf1)
		    free_string(&buf1);

		continue;
	    }

	    if (0 != code) {
		if (buf1)
		    free_string(&buf1);
		continue;
	    }

	    {
		int mp = give_dt_enumerate_as_int(&mime_parameters);
		/* 0 == plain
		   1 == encoded
		   2 == ascii-and-encoded
		*/

		switch (mp) {
		case 0:
		    X = split_mime_params(& (att->DISPOSITION_opts),buf1, 0,1);
		    break;
		case 1:
		    X = split_mime_params(& (att->DISPOSITION_opts),buf1, 0,0);
		    break;
		default:
		    X = split_mime_params(& (att->DISPOSITION_opts),buf1, 
					  MAX_COMPAT_LEN,0);
		}
	    }
	    
	    free_string(&buf1);

	    if (X) {
		media_type_t T = NULL;

		if (X[0]) {
		    if (istrcmp (X[0], "inline") != 0)
			att->disposition = DISP_ATTACH;
		    else
			att->disposition = DISP_INLINE;
		}

		free_rfc822tokenized(X);
	    }

	    update = TRUE;
	}
	else if (ch == 'e' || ch == 'E') {
	    int oldenc = att->encoding;
	    prompt = TRUE;
	    PutLineX (LINES-3, 0, 
		      CATGETS(elm_msg_cat, MeSet, MeAttachEnterContentTE,
			      "Content-Transfer-Encoding: "));

	    /* !!! */
	    Centerline (LINES-2, 
			"<SPACE> for next value, <RETURN> to accept.",
			page);
	    for (;;) {

		DPRINT(Debug,9, (&Debug, 
				 "... changing content-transfer-encoding, need_enc=%d, enc=%s\n",
				 need_enc,ENCODING(att->encoding)));

		menu_MoveCursor (page,
				 LINES-3, 27);
		menu_CleartoEOLN(page);

#define NEXT_ENCODING  { \
			   att->encoding++; \
			   if (att->encoding > 5) att->encoding = 1; \
			   continue; \
		       }

		if (!mailer_info ||
		    !query_mailer_info(mailer_info,MI_HAVE_8BITMIME)) {

		    int an = give_dt_enumerate_as_int(&allow_no_encoding);

		    DPRINT(Debug,8,(&Debug,
				    "mailer does not support 8BITMIME\n"));
		    if (an < 1) {
			if (att->encoding == ENCODING_8BIT) {  
			    /* Mailer won't support ! */
			    /* TRY next encosing instead */
			    NEXT_ENCODING;
			}
		    }
		}
	
		if (!mailer_info ||
		    !query_mailer_info(mailer_info,MI_HAVE_BINARYMIME)) {

		    int an = give_dt_enumerate_as_int(&allow_no_encoding);

		    DPRINT(Debug,8,(&Debug,
				    "mailer does not support BINARYMIME\n"));

		    if (an < 2) {
			if (att->encoding == ENCODING_BINARY) {  
			    /* Mailer won't support ! */
			    /* TRY next encoding instead */
			    NEXT_ENCODING;
			}
		    }
		}

		/* Don't allow 7bit if the file contains 8bit chars... 
		 * 7bit encoding is allowed if file includes control 
		 * characters 
		 */
		if (att->encoding == ENCODING_7BIT && (need_enc & HAVE_8BIT)) {
		    NEXT_ENCODING;
		}
		/* Don't allow 7bit or 8bit if the file required binary
		 * encoding according of Mime Standard.
		 */
		if ((att->encoding == ENCODING_7BIT || 
		     att->encoding == ENCODING_8BIT)
		    && (need_enc & HAVE_BINARY)) {
		    NEXT_ENCODING;
		}
		
		/* Don't allow encoding for Multipart/ and Message/ 
		 * See Mime Standard. Be carefull that don't create
		 * infinitive loop! */

		if (is_text < 0) {
		    static int again = 0;    /* Prevent looping */
		    if (att->encoding == ENCODING_QUOTED || 
			att->encoding == ENCODING_BASE64) {
			if (again == att->encoding) {
			    lib_error(CATGETS(elm_msg_cat, ElmSet,
					      ElmStructuredLeaf,
					      "Structured types must be encoded in leaf type!"));
			    sleep_message();
			    
			    /* prompt for new content-type */
			    prompt = FALSE;
			    ch = 't';
			    break;
			} else {
			    if (!again)
				again = att->encoding;
			    NEXT_ENCODING;
			}
		    } else
			again = 0;
		}

		menu_Write_to_screen (page,
				      FRM("%s"),ENCODING(att->encoding));
		ch = menu_ReadCh(page,REDRAW_MARK|READCH_sig_char);
		if (ch == '\n')
		    break;
		else if (ch == TERMCH_interrupt_char) {
		    att->encoding = oldenc;
		    update = TRUE;
		    
		    break;
		} else if (ch == ' ') {
		    NEXT_ENCODING;
		}
		else if (ch == REDRAW_MARK) {
		    update = TRUE;
		    prompt = FALSE;
		    ch =  'e';
		    
		    break;
		}
	    }

#undef NEXT_ENCODING

	    ClearLine (LINES-2);
	    /* 1 if is text type (true)
	     * 0 if not text type
	     * -1 if can't be encoded (ie structured) Message/ or Multpart/
	     */

	    is_text = give_text_type_code(att->TYPE);

	    if (is_text == 0 &&
		(att->encoding == ENCODING_7BIT ||
		 att->encoding == ENCODING_8BIT))
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmWarningEncoding,
				  "%s is allowed only for line orienteed types -- check encoding"),
			  ENCODING(att->encoding));
		
	    update = TRUE;
	}
	else
	    lib_error(CATGETS(elm_msg_cat, ElmSet,
			      ElmUnknownCommand2,			
			      "Unknown command."));
    }
    ret_value = FALSE;

 cleanup:
    if (br)
	free_dir(&br);

    erase_menu_context(&page);

    DPRINT(Debug,5,
	   (&Debug,"attach_modify=%d\n",ret_value));
    return ret_value;
}


char * compat_filename(orig,ok)
     char * orig; 
     int *ok;
{
    char * buf = safe_malloc(MAX_COMPAT_LEN+2);
    char *x;
    char *p;

    *ok = 1;
    for (p = buf, x=orig; *x; p++, x++) {

	*p = *x;

	if (p-buf > MAX_COMPAT_LEN) {
	    *ok = 0;
	    break;
	}

	if (!isascii(*p) || iscntrl(*p)) {
	    *ok = 0;
	    *p = '_';
	}
    }
    *p = '\0';


    return buf;
}

static void mime_add_name P_((mime_t *ptr));
static void mime_add_name(ptr)
     mime_t *ptr;
{
    char * compat = NULL;
    int was_ok = 1;
    char * p;

    int mp = give_dt_enumerate_as_int(&mime_parameters);
    /* 0 == plain
       1 == encoded
       2 == ascii-and-encoded
    */

    int have_encoded = 0;

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"mime_add_name",
		   "Bad magic number");

    
    DPRINT(Debug,3, (&Debug, "mime_add_name: pathname=%s\n",
		     ptr->pathname0));

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"mime_guess_content_type",
		   "Bad magic number");
    
    /* Set the "filename" field for content-disposition */
    p = strrchr (ptr->pathname0, '/');
    if (p)
	p++;
    else
	p = ptr->pathname0;

    compat = compat_filename(p,&was_ok);


    /* Assurance */
    if (ptr->DISPOSITION_opts)
	free_mime_param (& (ptr->DISPOSITION_opts));

    if (mp > 0 &&
	(!was_ok || !can_ascii_string(ptr->dispname))) {
	struct string *p1 = pick_name(ptr->dispname);
	
	if (p1) {
	    mime_params_add(& (ptr->DISPOSITION_opts),"filename", p1);
	    
	    free_string(&p1);

	    have_encoded ++;
			
	}
    }

    if (0 == mp ||
	2 == mp ||
	! have_encoded) {

	mime_params_add_compat(& (ptr->DISPOSITION_opts),
			       "filename", compat);
	free(compat); compat = NULL;
    }



}

static void set_mime_type P_((mime_t *ptr,struct mime_types_item *XX));
static void set_mime_type(ptr,XX)
     mime_t *ptr;
     struct mime_types_item *XX;
{
    charset_t  CS = NULL;
    
    CONST char * params =  mime_type_to_params(XX,&CS);
    
    if (ptr->TYPE_opts) {
	free_mime_param(&(ptr->TYPE_opts));
    }
    
    ptr->TYPE      = mime_type_to_media_type(XX);
    if (params)
	ptr->TYPE_opts = parse_mime_param("Content-Type",
					  params, CS,
					  NULL);
}


static int mime_guess_content_type1 P_((mime_t *ptr, struct scan_list *list));
static int mime_guess_content_type1 (ptr, list)
     mime_t *ptr;
     struct scan_list *list;
{
    struct mime_types_item *XX = loc_mime_type_from_scan(list);

    if (XX) {
	set_mime_type(ptr,XX);

	DPRINT(Debug,3,
	       (&Debug,
		"mime_guess_content_type1: scanned as \"%s/%s\"\n", 
		get_major_type_name(ptr->TYPE), 
		get_subtype_name(ptr->TYPE)));			    
	
	return 1;
    }

    return 0;
}


static void mime_guess_content_type P_((mime_t *ptr, char *ext));
static void mime_guess_content_type (ptr, ext)
     mime_t *ptr;
     char *ext;
{
    /* This routine tries to guess the content-type of an attachment by looking
     * at the suffix of ptr->pathname.  It first looks to see if it can find
     * an entry in ~/.elm/mime.types, then in "system_mime_types", and failing
     * that, to a small list of builtin definitions.
     */
    int i, found = FALSE;
    
    for (i = 0; i < 3; i++) {
	
	struct mime_types_item  * mime_types_list = NULL;
	struct mime_types_item * XX;

	DPRINT(Debug,3,
	       (&Debug, 
		"mime_guess_content_type: searching \"%s\", i=%d\n",
		ext,i));
	if (i == 0) {
	    
	    /* First try the user's mime.types file */
	    
	    mime_types_list = user_mimetypes_map;

	    if (!mime_types_list)
		continue;
	}
	else if (i == 1) {
	    
	    /* If it wasn't there, try the system file... */
      
	    mime_types_list = system_mimetypes_map;
	    if (!mime_types_list)
		continue;
	}
	else {
	    /* Couldn't find user or system mime.types file,
	     * use these defaults...
	     */

	    mime_types_list = builtin_mimetypes_map;
	}
	
	XX = loc_mime_type(mime_types_list,ext);
	
	if (XX) {
	    set_mime_type(ptr,XX);
	    
	    if (i < 2) {		
		DPRINT(Debug,3,
		       (&Debug,
			"mime_guess_content_type: user defined \"%s\" as \"%s/%s\"\n", 
			ext, get_major_type_name(ptr->TYPE), 
			get_subtype_name(ptr->TYPE)));			    
	    }  else {
		DPRINT(Debug,3,
		       (&Debug, 
			"mime_guess_content_type: built-in default \"%s\" as \"%s/%s\"\n", 
			ext, get_major_type_name(ptr->TYPE), 
			get_subtype_name(ptr->TYPE)));
	    }
	    break;
	}
    } 
}

static int attach_info (ptr,mailer_info,new)
     mime_t *ptr;
     struct mailer_info  *mailer_info;
     int new;
{
    struct stat sb;
    FILE *fp;
    int need_enc;
    char *ext = NULL;

    struct scan_list * scanlist = NULL;

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"attach_info",
		   "Bad magic number");

    if (stat (ptr->pathname0, &sb) == -1) {
	if (errno == ENOENT)
	    lib_error(CATGETS(elm_msg_cat, ElmSet,
			      ElmFileNotExistS,			
			      "That file %S does not exist!"),
		      ptr->dispname);
	else
	    lib_error(CATGETS(elm_msg_cat, ElmSet,
			      ElmCantStatFileS,			
			      "Could not stat file %S!"),
		      ptr->dispname);
	sleep_message();
	return (-1);
    }

    ptr->length = sb.st_size;
    
    if (can_open(ptr->pathname0,"r") != 0) {
	lib_error(CATGETS(elm_msg_cat, ElmSet,
			  ElmNotReadableByUserS,			
			  "%S isn't readable by user!"), 
		  ptr->dispname);
	sleep_message();
	return (-1);
    }
    
    /* Figure out what the default encoding is... */
    
    lib_transient(CATGETS(elm_msg_cat, ElmSet,
			  ElmCheckingEncodingS,			
			  "Checking %S..."), 
		  ptr->dispname);
    fp = fopen (ptr->pathname0, "r");
    if (!fp) {
	lib_error(CATGETS(elm_msg_cat, ElmSet,
			  ElmCantOpenFileS,			
			  "Can't open %S!"), 
		  ptr->dispname);
	sleep_message();
	return -1;
    }
    
    if (new)
	mime_add_name(ptr);

    ext = strrchr (ptr->pathname0, '.');
    if (ext)
	ext++;

    if (new) {
	struct scan_list * Y;

	if (user_mimetypes_map)
	    scanlist = get_scan_list(user_mimetypes_map,ext);
	if (system_mimetypes_map) {
	    struct scan_list * X = get_scan_list(system_mimetypes_map,ext);
	    
	    if (X) {
		if (scanlist) {
		    append_scanlist(scanlist,X);
		    free_scan_list(&X);
		} else
		    scanlist = X;
	    }
	}

    
	Y = get_scan_list(builtin_mimetypes_map,ext);

	if (Y) {
	    if (scanlist) {
		append_scanlist(scanlist,Y);
		free_scan_list(&Y);
	    } else
		scanlist = Y;
	}
    }

    need_enc = needs_encoding (fp,scanlist);

    
    if (need_enc & HAVE_CTRL) {
	ptr->encoding = (need_enc & HAVE_BINARY) 
	    ? ENCODING_BASE64 : ENCODING_QUOTED;
	
	if (new && (need_enc & HAVE_BINARY)) {
	    /* Set default to be application/octet-stream instead of 
	       text/plain 
	    */
	    ptr->TYPE = give_media_type2(MIME_TYPE_APPLICATION, 
					 "octet-stream", 1);
	}
    } else if (need_enc & HAVE_BINARY) { 
	/* HAVE_BINARY, but not HAVE_CTRL so that have long lines! */

	if(mailer_info &&
	   query_mailer_info(mailer_info,MI_HAVE_BINARYMIME)) {
	    DPRINT(Debug,8,(&Debug,"mailer supports BINARYMIME\n"));
	    ptr->encoding = ENCODING_BINARY;
	} else
	    ptr->encoding = ENCODING_QUOTED;
    }
    else if (need_enc & HAVE_8BIT) {
	if(mailer_info &&
	   query_mailer_info(mailer_info,MI_HAVE_8BITMIME)) {
	    DPRINT(Debug,8,(&Debug,"mailer supports 8BITMIME\n"));
	    ptr->encoding = ENCODING_8BIT;
	}
	else
	    ptr->encoding = ENCODING_QUOTED;	
    }
    fclose (fp);
    clear_error();  /* Remove reading ... -message */

    if (new) {
	
	int found = 0;

	/* Try to guess the content type of the data by the 
	   scanned context (and filename extension) */
		
	if (scanlist)
	    found = mime_guess_content_type1 (ptr,scanlist);

	/* Try to guess the content type of the data by the 
	   filename extension */
	if (!found && ext) 
	    mime_guess_content_type (ptr,ext);
        
	if (get_major_type_code(ptr->TYPE) == MIME_TYPE_TEXT && 
	    (need_enc & HAVE_8BIT)) {
	    CONST char *cs;

	    cs = get_mime_param_compat(ptr->TYPE_opts,"charset");
	    if (!cs &&
		display_charset && display_charset->MIME_name) 
		mime_params_add_compat(& (ptr->TYPE_opts),
				       "charset", display_charset->MIME_name);
	}
    }


    if (scanlist)
	free_scan_list(&scanlist);

    DPRINT(Debug,3, (&Debug, 
		     "attach_info: need_enc=%d, encoding=%d, pathname0=%s, type=%s/%s\n",
		     need_enc,ptr->encoding,ptr->pathname0,
		     get_major_type_name(ptr->TYPE), 
		     get_subtype_name(ptr->TYPE)));
    return (need_enc);
}


struct attach_structure {
    int attachments_index;   /* -1 if not on attachments list */
    mime_t     * current_node;
    struct attach_structure   *subnodes;
    int                        subnodes_len;
    int                        display_index;
    int                        display_depth;
};


static void attach_header P_((struct attach_structure *S, int is_cur, 
			      int use_desc, int offset,
			      struct menu_context  *page));
static void attach_header (S, is_cur, use_desc, offset, page)
     struct attach_structure *S;
     int is_cur, offset, use_desc;
     struct menu_context  *page;
{
    mime_t * mt = S -> current_node;
    int row,col;
    int i;

    /* Displays a header for a menu for the specified attachment. */
    char *Encoding = "???";
    int x = S->display_depth;

    int Width;
    int LINES, COLUMNS;

    menu_get_sizes(page, &LINES, &COLUMNS);   
	      
    Width = COLUMNS;     

    
    Encoding = ENCODING(mt->encoding);

    if (is_cur && has_highlighting && ! arrow_cursor) 
	menu_StartXX(page,pg_INVERSE);
    
    if (is_cur && arrow_cursor) {	
	menu_PutLine0 (page,offset, 0, "->");
    } else
	menu_PutLine0 (page,offset, 0, "  ");
    menu_CleartoEOLN(page);

    if (x < 0)
	goto out;

    menu_Write_to_screen(page,FRM("%2d"),S->display_index);
		    
    /* display the header value */
    menu_GetXYLocation(page,&row, &col);

    if (row != offset)
	goto out;

    menu_Writechar(page,' ');                           
    col++;

    for (i = 0; i < x && i < 5; i++) {         
	if (i < x-1) {
	    menu_Writechar(page,' ');                           
	    menu_Writechar(page,' ');                           	   
	    col += 2;
	} else {
	    menu_Writechar(page,'|');                           
	    if (0 == S->subnodes_len &&
		S->current_node->parser_data &&
		mime_parser_subparts(S->current_node->parser_data) > 0)
		menu_Writechar(page,'*');                           	   	    
	    else
		menu_Writechar(page,'-');                           	   	    
	    col += 2;
	}
    }
        
    x = Width - 46 - col;
    if (x < 1)
	goto out;

    if (use_desc && mt->description) 
	menu_Write_to_screen(page,
			     FRM("%-*.*S (%d) "),
			     x,x,
			     mt->description,
			     mt->length);    
    else if (mt->dispname)
	menu_Write_to_screen(page,
			     FRM("%-*.*S (%d) "),
			     x,x,		      
			     mt->dispname,
			     mt->length);
    else
	menu_Write_to_screen(page,
			     FRM("%-*.*s (%d) "),
			     x,x,		      
			     "",
			     mt->length);

    menu_GetXYLocation(page,&row, &col);
    if (row != offset)
	goto out;

    if (mt->length < 10) {
	menu_Writechar(page,' ');
	col++;
    }
    if (mt->length < 100) {
     	menu_Writechar(page,' ');
	col++;  
    }
    if (mt->length < 1000) {
	menu_Writechar(page,' ');
	col++;
    }
    
    x = Width -col;    
    if (x > 45)
	x = 45;
    x /= 3;
    if (x < 1)
	goto out;

    menu_Write_to_screen(page,
			 FRM("%.*s/%.*s"), 
			 x,
			 get_major_type_name(mt->TYPE), 
			 2*x,
			 get_subtype_name(mt->TYPE));

    menu_GetXYLocation(page,
		       &row, &col);
    if (row != offset)
	goto out;

    if (col + strlen(Encoding) +3 >= Width) {
	if (Width-col-7 < 1)
	    goto out;
	menu_PutLineX(page,
		      row, col+1, FRM("%.*s..."),
		      Width-col-7,Encoding);
    } else {
	x = Width - strlen(Encoding) -3;
	while (col < x) {
	    menu_Writechar(page,' ');
	    col++;
	}

	menu_PutLine0(page,
		      row, x, Encoding);
    }

    out:
    if (is_cur && has_highlighting && ! arrow_cursor) 
	menu_EndXX(page,pg_INVERSE);
}

static void write_num P_((int num, int is_cur, int offset,
			  struct menu_context  *page));
static void write_num(num, is_cur, offset,page)
     int num, is_cur,offset;
     struct menu_context  *page;
{
    char buf[10];
    
    elm_sfprintf (buf,sizeof buf,
		  FRM("%4d"),num);
    
    if (is_cur) {
	buf[0] = '-';
	buf[1] = '>';
    }
    menu_PutLine0 (page,offset, 0, buf);
}

static void free_structure P_((struct attach_structure   **structure,
			       int                        *len));
static void free_structure(structure,len)
     struct attach_structure **structure;
     int                      *len;
{
    int i;

    if (*structure) {
	for (i = 0; i < *len; i++) {
	    free_structure(& ((*structure)[i].subnodes),
			   & ((*structure)[i].subnodes_len));
	}

	free(*structure);
	*structure = NULL;
    }
    *len = 0;
}

static void feed_attachments P_((struct Attachments *A,
				 struct attach_structure   **structure,
				 int                        *len));
static void feed_attachments(A,structure,len)
     struct Attachments *A;
     struct attach_structure **structure;
     int                      *len;
{
    int L = A->attachment_count;
    int i;

    if (*structure)
	free_structure(structure,len);

    if (0 == L) {
	*len = 0;
	return;
    }
    *structure = safe_malloc(L * sizeof ((*structure)[0]));
    *len       = L;
    
    /* bzero is defined on hdrs/defs.h */
    bzero((void *)*structure, L * sizeof ((*structure)[0]));

    for (i = 0; i < L; i++) {
	(*structure)[i].attachments_index = i;
	(*structure)[i].current_node = & (A->attach_files[i]);
	(*structure)[i].subnodes     = NULL;
	(*structure)[i].subnodes_len = 0;
	(*structure)[i].display_index  = -1;
	(*structure)[i].display_depth  = -1;
    }       
}				 

static void expand_node P_((mime_t *T,
			    struct attach_structure   **structure,
			    int                        *len,
			    charset_t                 defcharset,
			    FILE *F,
			    struct header_errors **header_error));
static void expand_node(T,structure,len,defcharset,F,header_error)
     mime_t *T;
     struct attach_structure **structure;
     int                      *len;
     charset_t                 defcharset;
     FILE *F;
     struct header_errors **header_error;
{
    int L,i;

    if (*structure)
	free_structure(structure,len);

    if (! T->parser_data) {
	if (!F)
	    return;
	if (!mime_parser_parse(T,defcharset,F,header_error))
	    return;
    }
	
    if (! T->parser_data) {
	mime_panic(__FILE__,__LINE__,"expand_node",
		   "parser_data == NULL");
    }

    L = mime_parser_subparts(T->parser_data);

    if (0 == L) {
	*len = 0;
	return;
    }
    *structure = safe_malloc(L * sizeof ((*structure)[0]));
    *len       = L;
    
    /* bzero is defined on hdrs/defs.h */
    bzero((void *)*structure, L * sizeof ((*structure)[0]));

    for (i = 0; i < L; i++) {
	(*structure)[i].attachments_index = -1;
	(*structure)[i].current_node = mime_parser_index(T->parser_data,i);
	(*structure)[i].subnodes     = NULL;
	(*structure)[i].subnodes_len = 0;
	(*structure)[i].display_index  = -1;
	(*structure)[i].display_depth  = -1;
    }       
}

struct display_vector {
    struct attach_structure  **vector;
    int                      len;
};


static void count_len P_((struct attach_structure   *structure,
			  int                        len,
			  int                        *index,
			  int                        depth,
			  struct display_vector      * dv));
static void count_len(structure,len,index,depth,dv)
     struct attach_structure *structure;
     int                      len;
     int                     *index;
     int                      depth;
     struct display_vector   *dv;
{
    int i;

    for (i = 0; i < len; i++) {
	structure[i].display_index = (*index)++;
	structure[i].display_depth = depth;

	dv->vector = safe_realloc(dv->vector,
				  (*index) * sizeof (dv->vector[0]));

	if (dv->len < (*index)) {
	    while (dv->len < (*index)) {
		dv->vector[dv->len] = NULL;
		dv->len++;
	    }
				   
	} else
	    dv->len = (*index);

	dv->vector[structure[i].display_index] = & structure[i];

	count_len(structure[i].subnodes,
		  structure[i].subnodes_len,
		  index,depth+1,dv);		  
    }
}

static void reset_display_vector P_((struct display_vector      * dv));
static void reset_display_vector(dv)
     struct display_vector      * dv;
{
    if (dv->vector)
	free(dv->vector);
    dv->vector = NULL;
    dv->len     = 0;
}


struct menu_anon_param {
    struct attach_structure   *top_structure;
    int                        top_len;
    struct display_vector      DV;
};



enum { attach_mp_rdonly,  
       attach_mp_mime_structure,
       attach_mp_LENGTH,
       attach_mp_param,
       attach_mp_COUNT };


#if ANSI_C
#define S_(x) static x;
#else
#define S_(x)
#endif


S_(subpage_simple_redraw sb_update_attach_menu)
static int sb_update_attach_menu(ptr,list)
     struct menu_context  *ptr;
     struct menu_param *list;
{
    int mime   = mp_lookup_integer(list,attach_mp_mime_structure);
    int rdonly = mp_lookup_integer(list,attach_mp_rdonly);

    menu_ClearScreen(ptr);

    if (!mime)
	menu_print_format_center(ptr,0,
				 CATGETS(elm_msg_cat, MeSet, MeAttachMenuLine1a,
					 "To view attachment, press <return>. j = move down, k = move up")); 
    else
	menu_print_format_center(ptr,0,
				 CATGETS(elm_msg_cat, MeSet, MeAttachMenuLine1,
					 "To view mime part, press <return>. j = move down, k = move up")); 
    if (rdonly) 
	menu_print_format_center(ptr,1,
				 CATGETS(elm_msg_cat, MeSet, MeAttachMenuLowRd,
					 "p)rint, s)ave, v)iew subparts, q)uit"));
    else
	menu_print_format_center(ptr,1,
				 CATGETS(elm_msg_cat, MeSet, MeAttachMenuLow,
					 "a)dd, e)dit, d)elete, m)odify, p)rint, s)ave, v)iew subparts, q)uit"));

    return 1;   /* menu completed */
}

S_(subpage_simple_redraw sb_update_attach_title)
static int sb_update_attach_title(ptr,list)
     struct menu_context  *ptr;
     struct menu_param *list;
{
    int mime   = mp_lookup_integer(list,attach_mp_mime_structure);
    int LENGTH = mp_lookup_integer(list,attach_mp_LENGTH);

    menu_ClearScreen(ptr);
	        
    if (!mime)
	menu_print_format_center(ptr,1,
				 CATGETS(elm_msg_cat, ElmSet, ElmAttachMenu,
					 "Attachment Menu (%d attachments)"), 
				 LENGTH);
    else
	menu_print_format_center(ptr,1,
				 CATGETS(elm_msg_cat, ElmSet, ElmAttachMenu2,
					 "MIME structure Menu (%d visible parts)"), 
				 LENGTH);
    return 1;   /* title completed */
}


S_(header_line_redraw hdr_attach_line_redraw)
static void hdr_attach_line_redraw P_((struct menu_context  *ptr,
				       struct menu_param *list,	    
				       int line_number,
				       int index,
				       int is_current));
static void hdr_attach_line_redraw(ptr,list,line_number,index,is_current)
     struct menu_context  *ptr;
     struct menu_param *list;
     int line_number;
     int index;
     int is_current;
{
    struct menu_anon_param *Ap = mp_lookup_anon(list,attach_mp_param);

    if (0 <= index && index < Ap->DV.len)
	attach_header(Ap->DV.vector[index],is_current,FALSE,line_number,ptr);
    else {
	menu_ClearLine(ptr,line_number);
    }       
}

S_(header_line_redraw hdr_attach_current_redraw)
static void hdr_attach_current_redraw P_((struct menu_context  *ptr,
				       struct menu_param *list,	    
				       int line_number,
				       int index,
				       int is_current));
static void hdr_attach_current_redraw(ptr,list,line_number,index,is_current)
     struct menu_context  *ptr;
     struct menu_param *list;
     int line_number;
     int index;
     int is_current;
{
    struct menu_anon_param *Ap = mp_lookup_anon(list,attach_mp_param);

    if (0 <= index && index < Ap->DV.len) {
	if (arrow_cursor)
	    write_num(index,is_current,line_number,ptr);
	else
	    attach_header(Ap->DV.vector[index],is_current,FALSE,line_number,
			  ptr);
    } else {
	menu_ClearLine(ptr,line_number);
    }       
}

S_(header_line_redraw hdr_attach_status_redraw)
static void hdr_attach_status_redraw P_((struct menu_context  *ptr,
				       struct menu_param *list,	    
				       int line_number,
				       int index,
				       int is_current));
static void hdr_attach_status_redraw(ptr,list,line_number,index,is_current)
     struct menu_context  *ptr;
     struct menu_param *list;
     int line_number;
     int index;
     int is_current;
{
    /* FIXME ? ? */
    hdr_attach_current_redraw(ptr,list,line_number,index,is_current);
}




/* FIXME:  this should be more close to hadling of main screen ... */

static void set_attach_screen P_((struct menu_context  *page, 
				  struct screen_parts *LOC,
				  struct menu_param  *LIST));
static void set_attach_screen(page,LOC, LIST)
     struct menu_context  *page;
     struct screen_parts *LOC;
     struct menu_param  *LIST;
{    
    int   LINES, COLUMNS;	

    menu_get_sizes(page,&LINES, &COLUMNS);

    /* Title part */

    if (! LOC->title_page)
	LOC->title_page = new_menu_subpage(page,0,3,sb_update_attach_title,
					   LIST);
    else
	menu_subpage_relocate(LOC->title_page,page,0,3);

    /* Attachments part */

    if (! LOC->header_page)
	LOC->header_page = new_menu_header(page,3,LINES-10,
					   hdr_attach_line_redraw,
					   hdr_attach_current_redraw,
					   null_header_param_changed,
					   hdr_attach_status_redraw,
					   LIST);
    else
	menu_header_relocate(LOC->header_page,page,3,LINES-10);

    /* Menu part */

    if (LOC->menu_page)
	menu_subpage_relocate(LOC->menu_page,page,LINES-7,3);
    else 
	LOC->menu_page = new_menu_subpage(page,LINES-7,3,
					  sb_update_attach_menu,LIST);
  
    /* Prompt part */

    if (LOC->prompt_page)
	menu_subpage_relocate(LOC->prompt_page,page,LINES-4,4);
    else 
	LOC->prompt_page = new_menu_subpage(page,LINES-4,4,
					    subpage_simple_noredraw,LIST);

}

static void check_attach_screen P_((struct screen_parts *LOC,
				    struct menu_param *list));
static void check_attach_screen(LOC,list)
     struct screen_parts *LOC;
     struct menu_param *list;
{
    /* Title area */

    if (menu_resized(LOC->title_page)) {
	DPRINT(Debug,1, (&Debug, "title page resized\n"));

    }
    if (menu_need_redraw(LOC->title_page)) {
	DPRINT(Debug,1, (&Debug, "title page redraw???\n"));
	sb_update_attach_title(LOC->title_page,list);
    }

    /* Menu area */

    if (menu_resized(LOC->menu_page)) {
	DPRINT(Debug,1, (&Debug, "menu page resized\n"));
	
    }
    if (menu_need_redraw(LOC->menu_page)) {
	DPRINT(Debug,1, (&Debug, "menu page redraw\n"));
	sb_update_attach_menu(LOC->menu_page,list);
    }

    /* Prompt area */
    if (menu_resized(LOC->prompt_page)) {
	DPRINT(Debug,1, (&Debug, "prompt page resized\n"));
    }
    if (menu_need_redraw(LOC->prompt_page)) {
	DPRINT(Debug,7, (&Debug, "prompt page redraw\n"));
	menu_ClearScreen(LOC->prompt_page);

	show_last_error();	/* for those operations that have to
				 * clear the footer except for a message.
				 */
    }

    /* Attachments part */

    if (menu_resized(LOC->header_page)) {
	DPRINT(Debug,1, (&Debug, "attachment page resized\n"));

	menu_trigger_redraw(LOC->header_page);
    }

    if (menu_need_redraw(LOC->header_page)) {

	DPRINT(Debug,1, (&Debug, "attachment page redraw\n"));

	menu_ClearScreen(LOC->header_page);

    }
}


static void first_item P_((void));
static void first_item()
{
    lib_error(CATGETS(elm_msg_cat, ElmSet,
		      ElmFirstAttachment,			
		      "You are on the first attachment!"));
}

static void last_item P_((void));
static void last_item()
{
    lib_error(CATGETS(elm_msg_cat, ElmSet,
		      ElmLastAttachment,			
		      "You are on the last attachment!"));
}


static struct move_messages M = {
    first_item,
    last_item
};


void attach_menu (F,T,A,defcharset, mailer_info,hdr, aview,header_error)
     FILE *F;
     mime_t *T;
     struct Attachments *A;		      
     charset_t defcharset;
     struct mailer_info  *mailer_info;
     struct header_rec *hdr;
     struct AliasView *aview;
     struct header_errors **header_error;
{    
    /* A generic attachment menu.  "rdonly" controls whether or not the list
     * of attachments "mt" may be edited.
     */
    
    struct menu_anon_param   Ap = { NULL, 0, 
				    { NULL, 0 }
    };
    int ch;
    int update = TRUE;

    int LENGTH = 0;
    struct menu_context  *page =  new_menu_context();
    int error_count = 0;

    struct screen_parts  LOC  = { NULL, NULL, NULL, NULL };
    struct menu_param  PARAM[attach_mp_COUNT+1] = { 
	{ mp_integer, 0 },
	{ mp_integer, 0 },
	{ mp_integer, 0 },
	{ mp_anon_param,0 },
	{ mp_END,0 }
    };
    
    Ap.top_structure = NULL;
    Ap.top_len       = 0;
    Ap.DV.vector     = NULL;
    Ap.DV.len        = 0;

    mp_list_set_integer(PARAM,attach_mp_rdonly,T != NULL);
    
    if (A)
	feed_attachments(A,&Ap.top_structure,&Ap.top_len);
    else if (T) {
	Ap.top_structure = safe_malloc(sizeof (Ap.top_structure[0]));

	/* bzero is defined on hdrs/defs.h */
	bzero((void *)Ap.top_structure, sizeof (Ap.top_structure[0]));
	Ap.top_len = 1;
	Ap.top_structure[0].attachments_index = -1;
	Ap.top_structure[0].current_node       = T;
	Ap.top_structure[0].subnodes           = NULL;
	Ap.top_structure[0].subnodes_len       = 0;
	Ap.top_structure[0].display_index      = 0;
	Ap.top_structure[0].display_depth      = 0;

	expand_node(Ap.top_structure[0].current_node,
		    &Ap.top_structure[0].subnodes,
		    &Ap.top_structure[0].subnodes_len,
		    defcharset,F,
		    header_error);

	mp_list_set_integer(PARAM,attach_mp_mime_structure,1);
    }

    count_len(Ap.top_structure,Ap.top_len,&LENGTH,0,&Ap.DV);

    mp_list_set_integer(PARAM,attach_mp_LENGTH,LENGTH);
    mp_list_set_anon(PARAM,attach_mp_param,&Ap);

    set_attach_screen(page,&LOC,PARAM);


#if 0
    if (!mailer_info && !rdonly)
	mime_panic(__FILE__,__LINE__,
		   "attach_menu",
		   "No mailer information available");
#endif
    
    for (;;) {

	if (header_error) {
	    int ec = 0;
	    int cancel_it = 0;

	    if (*header_error)
		ec = get_header_errors_count(*header_error);

	    if (ec > error_count) {
		/* Print new errors .. if arriced during handling
		   if mime structure
		*/
		print_errors_att(*header_error,error_count,&cancel_it);

		if (cancel_it)
		    goto OUT;

		error_count = ec;
		update = TRUE;
	    }


	}

	
	if (menu_resized(page)) {
	    set_attach_screen(page,&LOC,PARAM);
	    
	    update = TRUE;
	} 

	if (update || menu_need_redraw(page)) {
	    menu_ClearScreen(page);

	    /* Call refresh routines of children */
	    menu_redraw_children(page);
	    	    
	    update = FALSE;
	    show_last_error(); 
	} 

	check_attach_screen(&LOC,PARAM);

	{
	    int lin,col;
	    
	    menu_ClearLine(LOC.prompt_page,0);

	    menu_PutLineX (LOC.prompt_page,0, 0, 
			   CATGETS(elm_msg_cat, MeSet, MeAttachMenuPrompt,
				   "Attachments: "));
	    menu_GetXYLocation(LOC.prompt_page,&lin,&col);
	    
	    menu_CleartoEOS(LOC.prompt_page);   
	    
	    show_last_error();
	    menu_MoveCursor(LOC.prompt_page,lin,col);
	    
	    ch = menu_ReadCh(LOC.prompt_page, 
			     REDRAW_MARK|READCH_CURSOR|READCH_resize|
			     READCH_sig_char);

	    menu_CleartoEOS(LOC.prompt_page);
	    set_error("");	/* clear error buffer */
	}

	/* Translate keys not handled on do_movement()
	   to equivalent keys 
	*/
	switch (ch) {
	case LEFT_MARK:  ch = PAGEUP_MARK;   break;
	case RIGHT_MARK: ch = PAGEDOWN_MARK; break;
	case 'n':
	case ctrl('N'):  ch = DOWN_MARK;     break;
	case ctrl('K'):  ch = UP_MARK;       break;

	}
	

	ch = do_movement(LOC.header_page,ch,LENGTH,&M);


	switch (ch) {
	case RESIZE_MARK:
	    DPRINT(Debug,4, (&Debug, " ... resizing\n"));
	    continue;

	case 's': {
	    int cur = menu_header_get(LOC.header_page,header_current);

	    menu_Write_to_screen(LOC.prompt_page,
				 CATGETS(elm_msg_cat, MeSet,
					 MeSaveToFile,
					 "Save to File"));
	    FlushBuffer();

	    if (cur < LENGTH &&
		0 <= cur && cur < Ap.DV.len && Ap.DV.vector[cur]) {
		attach_save (F,Ap.DV.vector[cur]->current_node, 
			     aview, page, &LOC);
	    } else
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmNoAttachments,			
				  "There are no attachments!"));
	}
	    break;
	case ' ':
	case '\n': {
	    int cur = menu_header_get(LOC.header_page,header_current);

	    if (cur < LENGTH &&
		0 <= cur && cur < Ap.DV.len && Ap.DV.vector[cur]) {
		attach_viewer (F,Ap.DV.vector[cur]->current_node,hdr, page);
		update = TRUE;
	    }
	    else
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmNoAttachments,			
				  "There are no attachments!"));
	}
	    break;
	case 'p': {
	    int cur = menu_header_get(LOC.header_page,header_current);
	    
	    menu_Write_to_screen(LOC.prompt_page,
				 CATGETS(elm_msg_cat, MeSet,
					 MePrintAttach,
					 "Print attachment"));
	    FlushBuffer();

	    if (cur < LENGTH &&
		0 <= cur && cur < Ap.DV.len && Ap.DV.vector[cur]) {
		attach_print(F,Ap.DV.vector[cur]->current_node, 
			     defcharset,page);
	    }
	    else
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmNoAttachments,			
				  "There are no attachments!"));
	}
	    break;

	case 'v': { /* Expand current node */

	    int cur = menu_header_get(LOC.header_page,header_current);
	    
	    menu_Write_to_screen(LOC.prompt_page,
				 CATGETS(elm_msg_cat, MeSet,
					 MeViewSubparts,
					 "View MIME subparts"));
	    FlushBuffer();

	    if (cur < LENGTH &&
		0 <= cur && cur < Ap.DV.len && Ap.DV.vector[cur]) {

		expand_node(Ap.DV.vector[cur]->current_node,
			    & (Ap.DV.vector[cur]->subnodes),
			    & (Ap.DV.vector[cur]->subnodes_len),
			    defcharset,
			    F,
			    header_error);

		LENGTH = 0;
		reset_display_vector(&Ap.DV);
		count_len(Ap.top_structure,Ap.top_len,&LENGTH,0,&Ap.DV);
		mp_list_set_integer(PARAM,attach_mp_LENGTH,LENGTH);
		
		menu_trigger_redraw(LOC.title_page);
		menu_trigger_redraw(LOC.header_page);
	    } else
		lib_error(CATGETS(elm_msg_cat, ElmSet,
				  ElmNoAttachments,			
				  "There are no attachments!"));
	    
	}
	    break;

	case 'e':
	    if (A) {
		int cur = menu_header_get(LOC.header_page,header_current);

		if (cur < LENGTH &&
		    0 <= cur && cur < Ap.DV.len && Ap.DV.vector[cur]) {
		    attach_edit (Ap.DV.vector[cur]->current_node,
				 mailer_info);
		    update = TRUE;
		} else
		    lib_error(CATGETS(elm_msg_cat, ElmSet,
				      ElmNoAttachments,			
				      "There are no attachments!"));
	    }
	    break;
	case 'd':
	    if (A) {
		int cur = menu_header_get(LOC.header_page,header_current);

		if (cur < 0 || cur >= LENGTH || cur >= Ap.DV.len ) {
		    lib_error(CATGETS(elm_msg_cat, ElmSet,
				      ElmNoAttachments,			
				      "There are no attachments!"));
		    break;
		}

		if (question_me) {
		    for(;;) {
			/* FIXME */
			menu_PutLine0(LOC.prompt_page,
				      0, 0, "Are you sure? (y/n): y");
			menu_MoveCursor(LOC.prompt_page,
					0, 21);
			ch = menu_ReadCh(LOC.prompt_page, 0);
			if (ch == 'y' || ch == '\n' || ch == 'n')
			    break;
		    }
		    menu_ClearLine(LOC.prompt_page,0);
		    if (ch == 'n')
			break;
		}

	    delete_it: 
	        cur = menu_header_get(LOC.header_page,header_current);

		if (cur < LENGTH &&
		    0 <= cur && cur < Ap.DV.len && Ap.DV.vector[cur] &&
		    Ap.DV.vector[cur] && A &&
		    Ap.DV.vector[cur]->attachments_index >= 0 &&
		    Ap.DV.vector[cur]->attachments_index < 
		    A->attachment_count) {

		    int i = Ap.DV.vector[cur]->attachments_index;

		    mime_t_clear(& (A->attach_files[i]) );
		    for (; i < A->attachment_count-1; i++)
			A->attach_files[i] = A->attach_files[i+1];

		    mime_t_zero ( & (A->attach_files[i]) );
		    A->attachment_count--;
		    
		    free_structure(&Ap.top_structure,&Ap.top_len);
		    feed_attachments(A,&Ap.top_structure,&Ap.top_len);
		    
		    LENGTH = 0;
		    reset_display_vector(&Ap.DV);
		    count_len(Ap.top_structure,Ap.top_len,&LENGTH,0,&Ap.DV);
		    mp_list_set_integer(PARAM,attach_mp_LENGTH,LENGTH);
		    
		    menu_trigger_redraw(LOC.title_page);
		    menu_trigger_redraw(LOC.header_page);
		}	    
	    }
	    break;

	case 'a':

	    if (A) {
		mime_t attach;
		mime_t_zero(&attach);
		
		if (attach_modify (&attach, TRUE, mailer_info, aview)) {
		    add_Attachments(A,&attach);
		    free_structure(&Ap.top_structure,&Ap.top_len);
		    feed_attachments(A,&Ap.top_structure,&Ap.top_len);

		    LENGTH = 0;
		    reset_display_vector(&Ap.DV);
		    count_len(Ap.top_structure,Ap.top_len,&LENGTH,0,&Ap.DV);

		    mp_list_set_integer(PARAM,attach_mp_LENGTH,LENGTH);

		   
		    menu_header_change(LOC.header_page, header_current,
				       Ap.top_len-1);
		    
		    
		    menu_trigger_redraw(LOC.title_page);
		    menu_trigger_redraw(LOC.header_page);
		    
		} else {
		    mime_t_clear(& attach );
		}		
	    }
	    break;

	case 'm':
	    if (A) {
		int cur = menu_header_get(LOC.header_page,header_current);
		
		if (cur < LENGTH &&
		    0 <= cur && cur < Ap.DV.len && Ap.DV.vector[cur]) {
		    attach_modify (Ap.DV.vector[cur]->current_node, FALSE, 
				   mailer_info,
				   aview);
		    /* If there is not pathname it is otherwise assumed to be 
		     * part from mailfile...!
		     */
		    if (Ap.DV.vector[cur]->current_node->pathname0 == NULL)
			goto delete_it;
		    update = TRUE;
		} else
		    lib_error(CATGETS(elm_msg_cat, ElmSet,
				      ElmNoAttachments,			
				      "There are no attachments!"));
	    }
	    
	    break;
	case 'i':
	case 'q':
	case 'x':
	case TERMCH_interrupt_char:
	    goto OUT;

	case ctrl('F'):
	    forget_passphrase();
	    break;
	case ctrl('L'):
	case REDRAW_MARK:
	    update = TRUE;
	    break;
#ifdef ALLOW_SUBSHELL
	case '!':
	    
	    subshell(NULL,page,LOC.prompt_page);

	    break;
#endif
	case 0:
	    break;

	default:
	    lib_error(CATGETS(elm_msg_cat, ElmSet,
			      ElmUnknownCommand3,		       
			      "Unknown command: %c"), 
		      ch);
	}
    }

 OUT:
    reset_display_vector(&Ap.DV);

    if (Ap.top_structure)
	free_structure(&Ap.top_structure,&Ap.top_len);

    free_mailbox_screen(&LOC);

    erase_menu_context(&page);

    return;
}

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


syntax highlighted by Code2HTML, v. 0.9.1