static char rcsid[] = "@(#)$Id: savecopy.c,v 1.53 2006/06/11 17:21:53 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.53 $   $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
 *****************************************************************************/

/** Save a copy of the specified message in a folder.

**/

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

DEBUG_VAR(Debug,__FILE__,"mbox");

#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 zero_copy_file(cf)
     struct copy_file *cf;
{
    cf->copy_file = NULL;
    cf->dir       = NULL;
}

void clear_copy_file(cf)
     struct copy_file *cf;
{
    if (cf->copy_file)
	free_string(&(cf->copy_file));
    if (cf->dir)
	free_dir(&(cf->dir));
}

/*
 * save_copy() - Append a copy of the message contained in "filename" to
 * the file specified by "copy_file".  This routine simply gets all of
 * the filenames right, and then invokes "append_copy_to_file()" to do
 * the dirty work.
 */
int save_copy(headers, cf, form, mime_info, conv_file, 
	      page)
     struct mailing_headers * headers; 
     struct copy_file *cf;
     int form;
     mime_send_t *mime_info;
     FILE * conv_file;
     struct menu_context  *page;
{
    int
	is_ordinary_file;
    int flags = 0;
    int is_sent_mail = 0;
    int is_current_folder = 0;
    WRITE_STATE           write_ptr = NULL;
    int ra, rb;
    int reopen_current = 0;
    struct current_storage *storage = NULL;
    struct MailboxView *m;
    int idx;

    if (!cf->copy_file)
	return 0;                /* Selected NONE */

    if (cf->dir)
	flags = give_dir_flags(cf->dir);
    else
	cf->dir = new_browser(selection_folder);

    if (!flags) {
	int s_len=string_len(cf->copy_file);

	if (s_len < 1)
	    return 0;          /* Selected NONE */

	switch (give_unicode_from_string(cf->copy_file,0)) {
        case 0x003D:  /* '='  Handle save copy prompt! */
            if (1 == s_len /* "=" Unconditionally save by name */ 
		|| 
		/* Conditionally save by name name */
		(2 == s_len &&
		 give_unicode_from_string(cf->copy_file,1) 
		 == 0x003F /* '?' */)) {
		if (headers->to.addrs_len > 0) {
		    char buffer[LONG_STRING];
		    struct string *S1 = NULL;
		    int r;

		    /* determine 'to' login */
		    get_return_name(headers->to.addrs[0].addr, 
				    buffer, TRUE,  sizeof buffer);  	

		    if (!buffer[0])
			goto use_sent_mail;
		    
		    S1 = format_string(FRM("=%s"),buffer);
		
		    r = select_dir_item(cf->dir,&S1);
		    free_string(&S1);
		    
		    if (!r)
			goto use_sent_mail;      /* Selection failed! */


		    flags = give_dir_flags(cf->dir);

		    if (2 == s_len &&
			give_unicode_from_string(cf->copy_file,1) 
			== 0x003F /* '?' */ &&
			0 == (BROWSER_EXIST  & flags)) {

			DPRINT(Debug,5,(&Debug, 
					"Conditional save by name: file doesn't exist - using \"<\".\n"));
			goto use_sent_mail2;
		    }


		} else {
		
		    char * sent_val;

		use_sent_mail:
		    
		    sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

		    if (sent_val) {
			lib_error(CATGETS(elm_msg_cat, ElmSet, 
					  ElmCannotDetermineToName,
					  "Cannot determine `to' name to save by! Saving to \"sent\" folder %s instead."),
				  sent_val );

			is_sent_mail++;
		    } else {
			lib_error(CATGETS(elm_msg_cat, ElmSet, 
					  ElmCannotDetermineToName1,
					  "Cannot determine `to' name to save by!"));

			return 0;
		    }
		}
	    }
	}
    }

    if (is_sent_mail) {
	int r;

    use_sent_mail2:
	/* If we come here via goto label, increment is_sent_mail */
	
	if (!is_sent_mail)
	    is_sent_mail++;
	
	
	if (cf->copy_file)
	    free_string(&(cf->copy_file));
	
	cf->copy_file = new_string(system_charset);
	add_ascii_to_string(cf->copy_file,s2us("<"));
	
	r = select_dir_item(cf->dir,&(cf->copy_file));
	    
	if (!r) {
	    char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");
	    
	    if (sent_val) 
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCannotSaveTo,
				  "Cannot save to %s!"), 
			  sent_val);
	    else
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCannotSaveTo2,
				  "sent -folder is not available."));

	    sleep_message();
	    return 0;
	}		
    }

    flags = give_dir_flags(cf->dir);

    if (!flags) {   /* No selection so try it */	
	if (!select_dir_item(cf->dir,&(cf->copy_file))) {
	    
	    char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

	    if (sent_val) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCannotSaveToSavingInstead,
				  "Cannot save to %S! Saving to \"sent\" folder %s instead."), 
			  cf->copy_file,
			  sent_val);

		sleep_message();
		if (is_sent_mail)
		    panic("FILE PANIC",__FILE__,__LINE__,"save copy",
			  "sent mail panic",0);		    
		goto use_sent_mail2;
	    } else {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmCannotSaveTo1,
				  "Cannot save to %S!"), 
			  cf->copy_file);
		sleep_message();
		return 0;
	    }
	}
	flags = give_dir_flags(cf->dir);
    }

    if (cf->copy_file)
	DPRINT(Debug,8,(&Debug, 
			"*** %S have flags:%s%s%s%s%s%s%s%s\n",
			cf->copy_file,
			flags & BROWSER_NODIR    ?   " NODIR":    "",
			flags & BROWSER_NOFOLDER ?   " NOFOLDER": "",
			flags & BROWSER_MARKED   ?   " MARKED":   "",
			
			flags & BROWSER_MAILFILE ?   " MAILFILE": "",
			flags & BROWSER_SELECTED ?   " SELECTED": "",
			flags & BROWSER_EXIST    ?   " EXIST"   : "",
			flags & BROWSER_DIRPREFIX ?   " DIRPREFIX"   : "",
			!flags                   ?   " none"    : ""));
	       
    /*
     *  Allow options
     *  confirm_files, confirm_folders,
     *  confirm_append and confirm_create
     *  to control where the actual copy
     *  should be saved.
     */
    is_ordinary_file = 0 == (flags & BROWSER_MAILFILE);

    /* Replace editing buffer with expanded version ... */
    if (cf->copy_file)
	free_string(&(cf->copy_file));
    cf->copy_file = selection_name_dir(cf->dir);

  
    idx = 0;
    while (NULL != (m = give_next_open_mailbox(&idx,0))) {
	int mbc,i;

	mbc = get_storage_count(m);
	is_current_folder = 0;
	for (i = 0; i < mbc; i++) {
	    storage = get_storage(m,i);
	    
	    if (storage &&
		storage->current_folder)
		is_current_folder = selection_is_folder(cf->dir,
							storage->current_folder);
	    if (is_current_folder)
		break;   /* FOUND */
	}

	if (is_current_folder)
	    break;   /* FOUND */
    }

    if (is_sent_mail) {
	if ((flags & BROWSER_EXIST) == 0) {
	    /* Create it now ... */
            if (!create_selection_dir(cf->dir))
		return 0;
	}	    
    } else if ((flags & BROWSER_EXIST) != 0) {	/* already there!! */
	if (confirm_append || (confirm_files && is_ordinary_file)) {
	    /*
	     *  OK in batch mode it may be impossible
	     *  to ask the user to confirm. So we have
	     *  to use sent_mail anyway.
	     */
	    if (batch_only) 
		goto use_sent_mail2;
	    else {
		char * msg_buffer = NULL;
		char   answer;
		int LINES, COLUMNS;

		menu_get_sizes(page, &LINES, &COLUMNS);   



		if (is_ordinary_file)
		    msg_buffer = elm_message(CATGETS(elm_msg_cat, ElmSet, 
						     ElmConfirmFilesAppend,
						     "Append to an existing file `%S'? (%c/%c) "),
					     cf->copy_file, 
					     *def_ans_yes, *def_ans_no);
		else
		    msg_buffer = elm_message(CATGETS(elm_msg_cat, ElmSet, 
						     ElmConfirmFolderAppend,
						     "Append to mail folder `%S'? (%c/%c) "),
					     cf->copy_file, 
					     *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) {		   
		    char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

		    if (sent_val) {
			PutLineX (LINES-1-2, 0, 
				  CATGETS(elm_msg_cat, 
					  ElmSet, 
					  ElmSavingToInstead,
					  "Alright - saving to `%s' instead"),
				  sent_val);
			sleep_message();
			
			ClearLine (LINES-1-2);
			goto use_sent_mail2;

		    } else {
			PutLineX (LINES-1-2, 0, 
				  CATGETS(elm_msg_cat, 
					  ElmSet, 
					  ElmSaving1,
					  "Alright"));
			sleep_message();
			return 0;
		    }
		    
		}
	    }
	}
    }
    else {
	if (confirm_create || (confirm_folders && !is_ordinary_file)) {
	    /*
	     *  OK in batch mode it may be impossible
	     *  to ask the user to confirm. So we have
	     *  to use sent_mail anyway.
	     */
	    if (batch_only) 
		goto use_sent_mail2;	    
	    else {
		char * msg_buffer = NULL;
		char   answer;
		int LINES, COLUMNS;
		
		menu_get_sizes(page, &LINES, &COLUMNS);   
    
		if (is_ordinary_file)
		    msg_buffer = elm_message(CATGETS(elm_msg_cat, ElmSet, 
						     ElmConfirmFilesCreate,
						     "Create a new file `%S'? (%c/%c) "),
					     cf->copy_file, 
					     *def_ans_yes, *def_ans_no);
		else
		    msg_buffer = elm_message(CATGETS(elm_msg_cat, ElmSet, 
						     ElmConfirmFolderCreate,
						     "Create a new mail folder `%S'? (%c/%c) "),
					     cf->copy_file, 
					     *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) {	       
		    char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

		    if (sent_val) { 
			PutLineX (LINES-1-2, 0, 
				  CATGETS(elm_msg_cat, 
					  ElmSet, 
					  ElmSavingToInstead,
					  "Alright - saving to `%s' instead"),
				  sent_val);
			sleep_message();
			ClearLine (LINES-1-2);
			goto use_sent_mail2;
		    } else {
			PutLineX (LINES-1-2, 0, 
				  CATGETS(elm_msg_cat, 
					  ElmSet, 
					  ElmSaving1,
					  "Alright"));
			sleep_message();
			return 0;
		    }
		}
	    }
	}
	/* Create it now ... */
	if (!create_selection_dir(cf->dir)) {
	    char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

	    if (sent_val) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmCannotSaveToSavingInstead,
				  "Cannot save to %S! Saving to \"sent\" folder %s instead."), 
			  cf->copy_file,
			  sent_val);
		sleep_message();
		goto use_sent_mail2;
	    } else {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmCannotSaveTo1,
				  "Cannot save to %S!"), 
			  cf->copy_file);
		sleep_message();
		return 0;
	    }
	}
    }
    
    if (is_current_folder) {
	int M = get_folder_mode(storage->current_folder);

	DPRINT(Debug,1,(&Debug,  "*** Current folder is save copy folder!\n"));

	if (0 != (M & FOLDER_FILE)) {
	    DPRINT(Debug,3,(&Debug,  "--> Need close current folder...\n"));
	
	    /* current_folder is same than save copy folder, 
	       we need close current_folder so that locking does not
	       cause problems
		   
	       Two problems:
	       - Possible locking on prepare_write_folder()
	         may fail on some situations
		   
	       - Unlocking on end_write_folder() may cause
	         that lock of new_folder is lost!
		   
	    */
		
	    close_folder(storage->current_folder,CLOSE_NORMAL);
	    reopen_current = 1;
	}
    }

    if (!prepare_write_folder(cf->dir,&write_ptr)) {
	char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

	if (!is_sent_mail) {

	    if (sent_val) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmCannotSaveToSavingInstead,
				  "Cannot save to %S! Saving to \"sent\" folder %s instead."), 
			  cf->copy_file,
			  sent_val);
		sleep_message();
		goto use_sent_mail2;
	    } else {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmCannotSaveTo1,
				  "Cannot save to %S!"), 
			  cf->copy_file);
		sleep_message();
		return 0;		
	    }
	}
	if (sent_val)
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCannotSaveTo,
			      "Cannot save to %s!"), 
		      sent_val);
	else
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCannotSaveTo2,
			      "sent -folder is not available."));
	sleep_message();
	return 0;
    }
    
    ra = append_copy_to_file(headers, cf->dir, form,
			     mime_info,conv_file,write_ptr);

    rb = end_write_folder(cf->dir,&write_ptr);
    
    if (reopen_current) {
       	DPRINT(Debug,3,(&Debug,  "--> Reopeing current folder...\n"));

	if (!sessionlock_folder(storage->current_folder,SESSIONLOCK_NORMAL)) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, 
			      ElmFailedReopenFolder,
			      "Failed to reopen %S folder"),
		      storage->current_folder->cur_folder_disp);
	}
    }

    if (!rb || ra < 0) {
	char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

	if (!is_sent_mail) {
	    if (sent_val) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmCannotSaveToSavingInstead,
				  "Cannot save to %S! Saving to \"sent\" folder %s instead."), 
			  cf->copy_file,
			  sent_val);
		sleep_message();
		goto use_sent_mail2;
	    } else {
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmCannotSaveTo1,
				  "Cannot save to %S!"), 
			  cf->copy_file);
		sleep_message();
		return 0;		
	    }
	}

	if (sent_val)
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCannotSaveTo,
			      "Cannot save to %s!"), 
		      sent_val);
	else
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCannotSaveTo2,
			      "sent -folder is not available."));
	    
	return 0;
    }
    return 1;
}

void name_copy_file(cf,aview, page)
     struct copy_file *cf;
     struct AliasView *aview;
     struct menu_context  *page;
{
    /** Prompt user for name of file for saving copy of outbound msg to.
	Called menu_trigger_redraw(page) if we need redraw **/

    if (!cf->dir)
	cf->dir = new_browser(selection_folder);

    gen_browser(page,
		cf->dir, &(cf->copy_file),
		word_save_copy,NULL,
		aview,
		CATGETS(elm_msg_cat, ElmSet, ElmSaveCopyInPrompt,
			"Save copy in (use '?' for help): "));
    
}

int append_copy_to_file(headers, dir,  form, mime_info, conv_file,dest)
     struct mailing_headers * headers; 
     struct folder_browser *dir;
     int form;
     mime_send_t *mime_info;
     FILE * conv_file;
     WRITE_STATE dest; 

{
    int err = 0;
    struct header_rec  X;
    FILE *F = NULL;
    out_state_t fp_copy;    /* We need another out_state_t because
			       dest is not seekable !!! 
			    */
    int env_flag;
    char *     tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");


    char * fname_copy = elm_message(FRM("%selmcopy-%d"),
				    tmp ? tmp : "/tmp/", 
				    getpid ());
    out_state_t buffer;
    charset_t charset_vector[2];

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

    header_zero(&X);
    strfcpy(X.env_from,username,sizeof X.env_from);

    if (headers->env_from) {
	int x;
	CONST char *t = mailer_env_from_value(headers->env_from,&x);
	if (t)
	    strfcpy(X.env_from,t,sizeof X.env_from);
    }

    /* We have perfect out_state_t (dest), but it is not seekable
       so we need build another one .... 

       TODO: Rethink this...
    */

    out_state_clear(&fp_copy,STATE_out_file);

    F = safeopen_rdwr(fname_copy);

    if (!F) {
	DPRINT(Debug,5,(&Debug, 
			"append_copy_to_file: safeopen_rdwr failed!\n"));

	free (fname_copy);
	out_state_destroy(&fp_copy);

	header_clear(&X);
	return -1;
    
    }
    set_out_state_file(F,&fp_copy);
    unlink (fname_copy);

    write_header_info(&fp_copy, headers, (form == YES), TRUE, mime_info);

    X.mime_rec.offset = out_state_ftell(&fp_copy);

    rewind(conv_file);

    /* dump the contents of the message to the end of the copy file */
    if (!copy_message_across(mime_info,&fp_copy, TRUE, conv_file)) {
	DPRINT(Debug,5,(&Debug, 
			"append_copy_to_file: copy_message_across failed!\n"));

	out_state_destroy(&fp_copy);
	fclose(F);
	free (fname_copy);
	header_clear(&X);
	return -1;	
    }
    
    X.content_length = out_state_ftell(&fp_copy) - X.mime_rec.offset;

    out_state_destroy(&fp_copy);
    rewind(F);

    out_state_clear(&buffer,STATE_out_dir);
    buffer.display_charset = charset_vector;
    set_out_state_dir(dir,dest,&buffer);

    if (!write_envelope_start(dir,dest,1,&X,&env_flag)) {
	DPRINT(Debug,5,(&Debug, 
			"append_copy_to_file: write_envelope_start failed!\n"));
	err = -1;
	goto fail;
    }

    if (!copy_message_2(F,&X, "",&buffer,0,env_flag)) {
	DPRINT(Debug,5,(&Debug, 
			"append_copy_to_file: copy_message_2 failed!\n"));
	err = -1;
    }

    if (!write_envelope_end(dir,dest,1,&X)) {
	DPRINT(Debug,5,(&Debug, 
			"append_copy_to_file: write_envelope_end failed!\n"));

	err = -1;
    }

 fail:
    free (fname_copy);
    header_clear(&X);

    out_state_destroy(&buffer);

    if (EOF == fclose(F)) {
	DPRINT(Debug,5,(&Debug, 
			"append_copy_to_file: fclose failed!\n"));
	err = -1;
    }

    return err;
}

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


syntax highlighted by Code2HTML, v. 0.9.1