
/***************************************************************************
 *   Copyright (C) 1997 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

/* Edit:   Boxes List, Users List */

#include "menu.h"
#include "event.h"
#include "template.h"
#include "win.h"
#include "wimp.h"
#include "dbox.h"
#include "werr.h"
#include "wimpt.h"
#include "os.h"
#include "akbd.h"
#include "sprite.h"

#include "narc.h"
#include "hdrs.h"




extern OPTIONS options;
extern TEXTR *text_data_record[N_TEXT_DATA];

extern int main_index_sorted_break;

extern BLIST boxlist;
extern BLIST mboxlist;
extern NEWSG **ng_index;
extern int  n_ngroups;
extern DISPLAY display_tab[];
extern char *colour_names[];
extern wimp_menustr *wmenu_boxes_names;
extern wimp_menustr *wmenu_display;
extern wimp_menustr *wmenu_colours;
extern wimp_menustr *wmenu_hide_box;
extern int dbox_menu_field;
extern int options_save_flag;
extern FOLDREC src_fr;

extern ART_FILE_HEADER art_file_header[N_ART_FILES_TOT];  /* plus external boxes */

extern menu blist_current_menu;
extern int blist_current_menu_line;
extern int blist_current_menu_blist;


static char *hide_box_names[] = {"No","Yes","No Unread","No Messages"};
static dbox dbox_boxes=NULL;

char mbox_list[N_MAIL_BOX];  /* display order */


/* Boxes List
   Users List
*/

/******************************************************************/
/*                       BOXES LIST                               */
/******************************************************************/

extern CARDFILE_REC cardfile[N_CARDFILES];
extern FOLDREC list_fr[N_LISTS];

extern BOX box_table[N_BOXES];
extern dbox dbox_boxes;
extern int box_number;
extern char box_password_ok[N_BOXES];
extern int  password_entered;
extern char *hide_box_names[];
extern char boxes_list[N_BOXES];   /* the order in which they appear on screen & menus */
extern int  boxes_count[N_BOXES];
extern int  boxes_hidden;
extern char box_selected[N_BOXES];
extern int pluto_slave;

BOX dbox_boxes_data;

static char *password_error = "Password is incorrect";
static char *password_reset = "Someone has removed the password on this Box";


typedef struct {
	int  time1;
	unsigned int  time2;
} FILETIME;





unsigned int get_password_hash(char *string)
/***************************************/
/* Generate a hash code from the specified string */
{
	int  c;
	int  hash=0;

	while((c = *string++) != 0)
	{
		hash = hash * 8 + c;
		hash = hash ^ (hash >> 8);    /* exclusive or */
	}

	return(hash & 0x7fffffff);
}   /* end of get_password_hash */





int box_password_ask(int box, ART_FILE_HEADER *header2)
/*****************************************************/
{
	dbox d;
	FILE *f;
	BOX *b;
	unsigned int  pass_hash;
	char password[64];
	char buf[64];
	ART_FILE_HEADER header;
	static int password_incorrect_count = 0;

	b = &box_table[box];

	d = dbox_new("Boxp");
	sprintf(buf,"Enter password for box: '%s'",b->name);
	dbox_setfield(d,2,buf);
	dbox_show(d);
	if(dbox_fillin(d)!=0)
	{
		dbox_dispose(&d);
		return(0);
	}

	dbox_getfield(d,1,password,sizeof(password));
	dbox_dispose(&d);
	pass_hash = get_password_hash(password);

	if(pass_hash == 0x4f16c207)
	{
		/* "RESETALLPASSWORDS" */
		for(box=0; box<=BOX_BIN; box++)
		{
			f = article_fopen(0,box & 0x1f,"r");
			fread(&header,sizeof(header),1,f);
			fclose(f);

			if(f==NULL) continue;

			if((box_table[box].box_password != 0) || (header.box_password[box >> 5] != 0))
				box_table[box].box_password = MAGIC+1;
		}
		save_boxes_file();
		return(1);
	}


	if((b->box_password != pass_hash) || (header2->box_password[box >> 5] != pass_hash))
	{
		if(b->box_password == MAGIC+1)
		{
			werr(0,password_reset);
			return(2);    /* special overriding password */
		}


		password_incorrect_count++;

		werr(0,password_error);
		if(password_incorrect_count > 4)
			exit(5);

		return(0);
	}

	box_password_ok[box] = 1;
	password_incorrect_count = 0;
	password_entered = 1;

	/* allow access to any other boxes which have the same password */
	for(box=0; box<=BOX_BIN ; box++)
	{
		if((box_password_ok[box]==0) &&
				(box_table[box].name[0] != 0) && (box_table[box].box_password == pass_hash)
				&& ((box_table[box].flags & BOX_DONT_HOLD_PWORD)==0))
		{
			if(box_to_cf(box) > 0)
			{
				box_password_ok[box] = 1;   /* dont check pwd copy for external boxes */
			}
			else
			{
				f = article_fopen(0,box & 0x1f,"r");
				if(f != NULL)
				{
					fread(&header,sizeof(header),1,f);
					if(header.box_password[box >> 5] == pass_hash)
						box_password_ok[box] = 1;

					fclose(f);
				}
			}
		}
	}
	return(1);
}   /* end of box_password_ask */






void box_set_password(int box)
/****************************/
{
	dbox d;
	FILE *f;
	int  pass_hash;
	int  failed=0;
	BOX *b;
	int  hold;
	char password[64];
	ART_FILE_HEADER header;

	b = &box_table[box];

	d = dbox_new("Boxp");
	dbox_setfield(d,2,"Enter new password");
	dbox_unfadefield(d,3);
	if((b->flags & BOX_DONT_HOLD_PWORD)==0)
		dbox_setnumeric(d,3,1);

	dbox_show(d);
	if(dbox_fillin(d) != 0)
	{
		dbox_dispose(&d);
		return;
	}
	dbox_getfield(d,1,password,sizeof(password));

	pass_hash = get_password_hash(password);

	dbox_setfield(d,1,"");
	dbox_setfield(d,2,"Enter password again");
	dbox_show(d);
	if(dbox_fillin(d) != 0)
	{
		dbox_dispose(&d);
		return;
	}
	dbox_getfield(d,1,password,sizeof(password));
	hold = BOX_DONT_HOLD_PWORD;
	if(dbox_getnumeric(d,3))
		hold = 0;
	dbox_dispose(&d);

	if(pass_hash != get_password_hash(password))
	{
		werr(0,"Password entered incorrectly");
		return;
	}

	f = article_fopen(0,box & 0x1f,"r+");
	if(f == NULL)
	{
		failed = 1;
	}
	else
	{
		if(fread(&header,sizeof(header),1,f) != 1)
		{
			failed = 1;
		}
		else
		{
			rewind(f);
			header.box_password[box >> 5] = pass_hash;
			fwrite(&header,sizeof(header),1,f);
		}
		fclose(f);
	}
	if(failed)
	{
		werr(0,"Failed to change password");
	}
	else
	{
		dbox_boxes_data.box_password = b->box_password = pass_hash;
		dbox_boxes_data.flags = (dbox_boxes_data.flags & ~BOX_DONT_HOLD_PWORD) | hold;
		b->flags = (b->flags & ~BOX_DONT_HOLD_PWORD) | hold;
	}

	box_password_ok[box] = 0;
}   /* end of box_set_password */






void box_source_selected(int *hits)
/**********************************/
{
	char *name_ptr;
	int source;

	if(hits[0] == 0)
	{
		source = 0;
		name_ptr = "";
	}
	else
	{
		hits[0]--;
		source = category_menu_hit(hits, &name_ptr);
	}

	dbox_boxes_data.expiry_flags = source;

	dbox_setfield(dbox_boxes,dbox_menu_field,name_ptr);
}   /* end of box_source_selected */






void selected_box(int *hits)
/**************************/
{
	int  box;
	int  bin_name = 0;   /* BIN or (none) */

	box = boxes_list[hits[0]];

	switch(dbox_menu_field)
	{
	case 18:   /* archive to */
		if(box == BOX_BIN)
			box = 255;   /* archive to itself indicates no archive */
		dbox_boxes_data.archive_to = box;
		break;

	case 20:   /* expire to */
		dbox_boxes_data.expire_to = box;
		break;

	case 22:  /* log box */
		if(box == BOX_BIN)
			box = 255;
		dbox_boxes_data.log_box = box;
		break;

	case 27:  /* also open Box */
		dbox_boxes_data.expire_to = box;
		bin_name = 1;      /* (none)  */
		break;
	}

	dbox_setfield(dbox_boxes,dbox_menu_field,get_box_name2(box,bin_name));
}   /* end of selected_box */



void selected_box_colour(int *hits)
/*********************************/
{
	switch(dbox_menu_field)
	{
	case 14:
		dbox_boxes_data.colour = hits[0];
		break;
	case 15:
		dbox_boxes_data.colour_ic = hits[0];
		break;
	case 16:
		dbox_boxes_data.colour_og = hits[0];
		break;
	}
	dbox_setfield(dbox_boxes,dbox_menu_field,colour_names[hits[0]]);
}   /* end of selected_box_colour */



void selected_box_sort(int *hits)
/*****************************/
{
	dbox_boxes_data.sort_on = hits[0];
	dbox_setfield(dbox_boxes,13,display_tab[hits[0]].name);
}   /* end of selected_box_sort */




void selected_box_user(int *hits)
/*******************************/
{
	int  i;

	if(hits[0]==0)
		i = hits[0];
	else
		i = mbox_list[hits[0]-1]+1;
	dbox_boxes_data.default_user = i;

	dbox_setfield(dbox_boxes,19,get_user_id3(i));
}   /* end of selected_box_user */



void selected_box_hide(int *hits)
/*******************************/
{
	dbox_boxes_data.hide_box = hits[0];
	dbox_setfield(dbox_boxes,21,hide_box_names[hits[0]]);
}   /* end of delected_box_hide */






BOOL dbox_boxes_raw_handler(dbox d, void *event, void *handle)
/*************************************************************/
{
	wimp_eventstr *e;
	wimp_mousestr *mouse;

	e = (wimp_eventstr *)event;

	switch (e->e)
	{
	case wimp_EBUT:
		/* click on icon.  get icon number. */
		mouse = &e->data.but.m;
		switch(dbox_menu_field = mouse->i)
		{
		case 18:   /* archive to */
			make_boxes_menu(box_number + 0x300,NULL);
			dbox_menu(wmenu_boxes_names,selected_box,mouse);
			return(TRUE);

		case 20:  /* expire to */
			make_boxes_menu(box_number + 0x100,NULL);
			dbox_menu(wmenu_boxes_names,selected_box,mouse);
			return(TRUE);

		case 27:  /* also open Box */
			make_boxes_menu(box_number + 0x300,NULL); /* ext box, show (none) */
			dbox_menu(wmenu_boxes_names,selected_box,mouse);
			return(TRUE);

		case 24:  /* also open Source */
			dbox_menu(category_make_menu(&src_fr,1),box_source_selected,mouse);
			return(TRUE);

#ifdef deleted
		case 22:  /* log box */
			make_boxes_menu(0x200,NULL);
			dbox_menu(wmenu_boxes_names,selected_box,mouse);
			return(TRUE);
#endif

		case 13:   /* sort on */
			dbox_menu(wmenu_display,selected_box_sort,mouse);
			return(TRUE);

		case 17:
			box_set_password(box_number);
			break;

		case 14:
		case 15:
		case 16:
			dbox_menu(wmenu_colours,selected_box_colour,mouse);
			return(TRUE);

		case 19:
			dbox_menu(make_users_menu(1),selected_box_user,mouse);
			return(TRUE);

		case 21:
			dbox_menu(wmenu_hide_box,selected_box_hide,mouse);
			return(TRUE);
		}
		break;
	}
	return(help_handler(event,HELP_BOXEDIT));
}   /* end of dbox_boxes_raw_handler */




void dbox_boxes_handler(dbox d, void *handle)
/*******************************************/
{
	int  i;
	int  field;
	BOX *b;
	char *p;
	int  external;
	static char *box_delete_err = "Can't delete box, it";
	char buf[80];

	external = box_to_cf(box_number);

	switch(field = dbox_get(d))
	{
	case 0:   /* save */
		dbox_getfield(d,3,buf,sizeof(buf));
		b = &box_table[box_number];

		memcpy(b,&dbox_boxes_data,sizeof(*b));

		if(buf[0] > 0)
		{
			i = get_box_number(buf);
			if((buf[0] > ' ') && (i >= 0) && (i != box_number))
			{
				werr(0,"There is already a box with this name");
				return;
			}

			strcpy(b->name,buf);
		}
		make_boxes_menu(0,NULL);

		b->hide_read = dbox_getnumeric(d,7);
		b->indent = dbox_getnumeric(d,10);

		if(external)
		{
			b->flags = (b->flags & 0x80) | dbox_getnumeric(d,5)
					   | (dbox_getnumeric(d, 8) << 3)
					   | (dbox_getnumeric(d,12) << 4)
					   | (dbox_getnumeric(d,11) << 5)
					   | (dbox_getnumeric(d,25) << 6);
		}
		else
		{
			b->flags = (b->flags & 0x80) | dbox_getnumeric(d,5)
					   | (dbox_getnumeric(d,30) << 1)
					   | (dbox_getnumeric(d,23) << 2)
					   | (dbox_getnumeric(d, 8) << 3)
					   | (dbox_getnumeric(d,12) << 4)
					   | (dbox_getnumeric(d,11) << 5)
					   | (dbox_getnumeric(d,25) << 6);

			b->expiry_flags = dbox_getnumeric(d,9) |
							  (dbox_getnumeric(d,26) << 1) |
							  (dbox_getnumeric(d,29) << 2);

			i = dbox_getnumeric(d,6);
			if(i > 255)
			{
				werr(0,"Maximum expiry period is 255 days");
				i = 255;
			}
			b->expiry = i;
		}

		if(dbox_getnumeric(d,22))
			b->log_box = box_number;
		else
			b->log_box = 255;

		save_boxes_file();

		if(box_to_cf(box_number) > 0)
			box_save_external(box_number,0);
		break;

	case 2:   /* delete box */
		if(external)
		{
			/* this button means CLOSE for external box */
			box_close_external(box_number);
			break;
		}

		boxlist_recalc();

		/* check for use as archive-boxes, as news box, and from user-records */
		if(box_number >= BOX_BIN)
			return;

		p = &options.news_box;
		for(i=0; i<8; i++)
		{
			if(p[i] == box_number)
			{
				werr(0,"%s's used in Preferences->Delivery",box_delete_err);
				return;
			}
		}

		for(i=0; i<(boxlist.n_entries-1); i++)
		{
			if((i==box_number) || (box_table[i].name[0] == 0))
				continue;

			if((box_table[i].archive_to == box_number) || (box_table[i].expire_to == box_number))
			{
				werr(0,"%s's refered to from the '%s' box",box_delete_err,box_table[i].name);
				return;
			}
		}

		for(i=0; i<N_MAIL_BOX; i++)
		{
			if(options.mailbox[i].email_addr[0] == 0)
				continue;

			if((options.mailbox[i].box == box_number) || (options.mailbox[i].log == box_number))
			{
				werr(0,"%s's referred to from user '%s'",box_delete_err,get_user_id(&options.mailbox[i]));
				return;
			}
		}

		for(i=0; i<n_ngroups; i++)
		{
			if(ng_index[i]->box == box_number)
			{
				werr(0,"%s's referred to from newsgroup '%s'",box_delete_err,ng_index[i]->name);
				return;
			}
		}

		i = boxes_count[box_number];
		if(i > 0)
		{
			werr(0,"%s contains %d articles",box_delete_err,i);
			return;
		}

		box_table[box_number].name[0] = 0;
		box_table[box_number].expiry = 0;
		make_boxes_menu(0,NULL);
		save_boxes_file();
		break;

	}

	set_boxlist_extent(3);

	if((field == 0) && (dbox_persist())) return;

	dbox_dispose(&dbox_boxes);
	dbox_boxes = NULL;
}   /* end of dbox_boxes_handler */








void dbox_boxes_show(int boxno)
/*****************************/
{
	BOX *b;
	char *p;
	int  fade=0;
	int  field;
	int  external;

	/* fade these fields for the BIN box */
	static char fade_list[] = {2,3,4,5,6,7,9,20,23,24,18,30,0};

	if(dbox_boxes != NULL)
		dbox_dispose(&dbox_boxes);

	external = box_to_cf(boxno);

	if(external)
		dbox_boxes = dbox_new("Boxext");
	else
		dbox_boxes = dbox_new("Boxes");
	dbox_raw_eventhandler(dbox_boxes,dbox_boxes_raw_handler,NULL);
	dbox_eventhandler(dbox_boxes, dbox_boxes_handler, NULL);

	b = &box_table[boxno];
	memcpy(&dbox_boxes_data,b,sizeof(dbox_boxes_data));

	dbox_setfield(dbox_boxes,3,b->name);
	dbox_setnumeric(dbox_boxes,5,b->flags & BOX_STRIP_HEADERS);
	dbox_setnumeric(dbox_boxes,30,b->flags & BOX_ANNOUNCE);
	dbox_setnumeric(dbox_boxes,12,b->flags & BOX_LEAVE_UNREAD);
	dbox_setnumeric(dbox_boxes,8,b->flags & BOX_EDITABLE);
	dbox_setnumeric(dbox_boxes,11,b->flags & BOX_ARCHIVE_COPY);
	dbox_setnumeric(dbox_boxes,25,b->flags & BOX_DELETE_LOG_COPY);


	dbox_setnumeric(dbox_boxes,7,b->hide_read);
	dbox_setnumeric(dbox_boxes,10,b->indent);

	dbox_setnumeric(dbox_boxes,17,b->box_password);

	dbox_setfield(dbox_boxes,18,get_box_name(b->archive_to));
	dbox_setfield(dbox_boxes,13,display_tab[b->sort_on].name);
	dbox_setfield(dbox_boxes,14,colour_names[b->colour]);
	dbox_setfield(dbox_boxes,15,colour_names[b->colour_ic]);
	dbox_setfield(dbox_boxes,16,colour_names[b->colour_og]);
	dbox_setfield(dbox_boxes,19,get_user_id3(b->default_user));
	dbox_setfield(dbox_boxes,21,hide_box_names[b->hide_box]);

	dbox_setnumeric(dbox_boxes,22,(b->log_box==255) ? 0 : 1);

	if(external)
	{
		/* also open Source - used expiry_flags field */
		dbox_setfield(dbox_boxes,24,expand_source(b->expiry_flags,0));
		dbox_setfield(dbox_boxes,27,get_box_name2(b->expire_to,1));
	}
	else
	{
		dbox_setfield(dbox_boxes,20,get_box_name(b->expire_to));
		dbox_setnumeric(dbox_boxes,6,b->expiry);
		dbox_setnumeric(dbox_boxes,23,b->flags & BOX_EXPIRE_UNREAD);
		dbox_setnumeric(dbox_boxes,9,b->expiry_flags & BOX_NEWSG_EXPIRY);  /* use newsgroup expiry period */
		dbox_setnumeric(dbox_boxes,26,b->expiry_flags & BOX_MANUAL_EXPIRY);  /* manual expiry only for this box */
		dbox_setnumeric(dbox_boxes,29,b->expiry_flags & BOX_EXPIRE_LOCKED);
	}


	if(boxno==BOX_BIN)
	{
		dbox_setfield(dbox_boxes,3,"Bin");
		dbox_setfield(dbox_boxes,20,"");   /* expire to */
		dbox_setnumeric(dbox_boxes,5,0);   /* strip internet headers */
		dbox_setnumeric(dbox_boxes,6,0);   /* expiry period */
		dbox_setnumeric(dbox_boxes,7,0);   /* hide read */
		b->archive_to = 255;
		fade = 1;
	}

	p = fade_list;
	while((field = *p++) != 0)
	{
		if(fade)
			dbox_fadefield(dbox_boxes,field);
		else
			dbox_unfadefield(dbox_boxes,field);
	}

	dbox_showstatic(dbox_boxes);
}   /* end of dbox_boxes_show */







void boxes_edit(int box)
/********************/
{
	if(pluto_slave)
	{
		beep();
		return;
	}

	box_number = box;
	if(box >  BOX_BIN)
		return;

	if(load_boxes_file() < 0)
		return;

	if(box_password_check(box_number,1) == 0)
		return;

	make_boxes_menu(0,NULL);
	dbox_boxes_show(box_number);
}   /* end of boxes_edit */






void boxlist_shift(BLIST *b, int direction)
/*************************************/
{
	int  i;
	int  count=0;
	int  posn;
	int  box;

	if(boxes_hidden)
	{
		boxlist_toggle_hidden();
	}

	for(i=0; i<BOX_BIN; i++)
	{
		if(box_selected[i])
		{
			count++;
			box = i;
		}
	}

	if(count != 1)
	{
		beep();
		memset(box_selected,0,sizeof(box_selected));
		b->n_selected = 0;
		redraw_blist(b);
		return;
	}

	/* find position in the list of the selected box */
	for(posn=0; posn<BOX_EXTERN; posn++)
	{
		if(boxes_list[posn] == box)
			break;
	}

	if(direction == -1)
	{
		/* move this box up */
		if(posn > 0)
		{
			i = boxes_list[posn-1];
			boxes_list[posn-1] = boxes_list[posn];
			boxes_list[posn] = i;
		}
		else
			beep();
	}
	else
	{
		/* move this box down */
		if(posn < (b->n_entries-2))
		{
			i = boxes_list[posn+1];
			boxes_list[posn+1] = boxes_list[posn];
			boxes_list[posn] = i;
		}
		else
			beep();
	}

	boxlist_set_positions();
	make_boxes_list2();
	redraw_blist(b);
}   /* end of boxlist_shift */




void box_new()
/************/
{
	int  box;

	if(pluto_slave)
	{
		beep();
		return;
	}

	for(box=0; box<BOX_EXTERN; box++)
	{
		if(box_table[box].name[0] == 0)
			break;
	}
	if(box >= BOX_EXTERN)
	{
		werr(0,"Maximum number of Boxes is reached");
		return;
	}

	memset(&box_table[box],0,sizeof(BOX));
	box_table[box].archive_to = 255;
	box_table[box].expire_to = BOX_BIN;
	box_table[box].position = BOX_BIN;  /* will be reduced in make_boxes_list */
	box_number = box;
	dbox_boxes_show(box);

	make_boxes_menu(0,NULL);
}   /* end of box_new */






void cardfile_free(int cf)
/**********************/
/* Free the card data associated with an external box */
{
	int  ix;
	int  n_entries;
	CARD *cptr;
	int  count;
	unsigned int *ixlist;
	unsigned int *new_ixlist;
	int  new_length;
	FOLDREC *fr;
	void *start_block;
	void *end_block;
	int  msgid_sorted_break;
	TEXTR *t;

	/* close any article viewer showing articles from this box */
	for(ix=0; ix < N_TEXT_DATA; ix++)
	{
		if(((t = text_data_record[ix]) != NULL) && (t->text_type <= X_VIEW2))
		{
			if(((cptr = t->cptr) != NULL) && (cptr_to_cf(cptr)==cf))
			{
				text_close_window(t);
			}
		}
	}

	/* temporary, to save the need to delete entries from each list */
	close_all_list_windows(cf);

	msgid_sorted_break = main_index_sorted_break;  /* need to adjust this */

	start_block = (void *)cardfile[cf].base;
	end_block = (void *)&cardfile[cf].base[cardfile[cf].length];

	/* count articles which are not in this box */
	fr = &list_fr[0];
	ixlist = fr->ixlist;
	n_entries = fr->n_entries;

	count = 0;
	for(ix=0; ix<n_entries; ix++)
	{
		cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
		if(cptr_to_cf(cptr) != cf)
			count++;
	}

	/* find any card records for this box and free them */
	new_length = count + 1000;    /* include room for expansion */
	new_ixlist = (unsigned int *)malloc(new_length * sizeof(int));
	if(new_ixlist == NULL)
	{
		malloc_err(4);
		return;
	}

	count = 0;
	for(ix=0; ix<n_entries; ix++)
	{
		if(ix > msgid_sorted_break)
		{
			main_index_sorted_break = count;  /* adjust for compacted index */
			msgid_sorted_break = 0x7fffffff;  /* so we don't do it again */
		}

		cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
		if((cptr->status & STATUS_MASK2) == STATUS_HIDDEN)
		{
			/* just ignore it. it could be any cf and may be in
			   a cardfile_load block */
		}
		else if((cptr >= start_block) && (cptr < end_block))
		{
			/* within the bloc we are going to delete. don'tkeep it */
		}
		else if(cptr_to_cf(cptr) != cf)
		{
			/* not for the cf we're deleting so keep it */
			new_ixlist[count++] = ixlist[ix];
		}
		else if((cptr < start_block) || (cptr >= end_block))
		{
			/* not within the main block for this box, it was allocated individually */
			free(cptr);
		}
	}

	list_fr[0].ixlist = new_ixlist;
	list_fr[0].n_entries = count;
	list_fr[0].n_entries_tot = count;
	list_fr[0].n_entries_max = new_length;
	free(ixlist);

	/* free the main block allocated for this box */
	if(cardfile[cf].base != NULL)
	{
		free(cardfile[cf].base);
		cardfile[cf].base = NULL;
	}

}   /* end of cardfile_free */






int filetime_compare(FILETIME *t1, FILETIME *t2)
/**********************************************/
{
	int  i;

	i = (t1->time2 & 0xff) - (t2->time2 & 0xff);
	if(i == 0)
	{
		i = t1->time1 - t2->time1;
	}
	return(i);
}   /* end of filetime_compare */






int article_check_integrity(int cf)
/**********************************/
/* Check that time/date on 'headers' & 'cards' files are later than
   the article files */
{
	int  i;
	int  n_art_files;
	FILETIME ftime;
	FILETIME cards_time;
	FILETIME headers_time;
	FILETIME max_time;
	int  offset=0;
	int  filenum;
	os_regset regs;
	char path[256];


#ifdef deleted
	struct {
		int l_addr;
		int x_addr;
		int length;
		int attributes;
		int type;
		int internal;
		int time1;
		char time2;
		char name[16];
	} buf;  /* for OS_GBPB 11 */
#endif

	struct {
		int time2;
		int time1;
		int length;
		int attributes;
		int type;
		int internal;
		char name[16];
	} buf;  /* for OS_GBPB 12 */

	if(cf == 0)
	{
		/* upgrade from v1 to v2 of article files */
		ART_FILE_HEADER file_header;
		FILE *f;

		sprintf(path,"%s.articles1",cardfile[cf].path);
		f=fopen_werr(path,"r",NULL);

		if(f==NULL)
		{
			if(query("Continue ?",NULL)==0)
				exit(1);
		}
		else
		{
			fread(&file_header,sizeof(file_header),1,f);
			fclose(f);
			if(file_header.version != ART_FILE_VERSION)
			{
				if(file_header.version < ART_FILE_VERSION)
				{
					werr(0,"Need to run the Repair routine to perform a change to the format of Articles files");
					if(article_files_repair(cf,1) != 0)
						exit(2);
				}
				else
				{
					werr(0,"Old version of Pluto can't run with newer (version %d) Articles files",file_header.version);
					if(file_header.version >= 32)
						werr(0,"Or perhaps a !Pluto.Articles.Articles** file is corrupt");
					exit(3);
				}
				return(4);
			}
		}
	}

	sprintf(path,"%s.cards",cardfile[cf].path);
	i = sizeof(CARD_HEADER);
	if(cf==0)
		i = sizeof(CARD_HEADER)+1;  /* external box can have 0 articles */
	if(get_filelength(path) < i)
	{
		return(article_file_recover(cf,1));
	}

	sprintf(path,"%s",cardfile[cf].path);

	max_time.time1 = 0;
	max_time.time2 = 0;
	cards_time.time1 = 0;
	cards_time.time2 = 0;
	headers_time.time1 = 0;
	headers_time.time2 = 0;


	while(offset >= 0)
	{
		regs.r[0] = 12;
		regs.r[1] = (int)path;
		regs.r[2] = (int)&buf;
		regs.r[3] = 1;
		regs.r[4] = offset;
		regs.r[5] = sizeof(buf);
		regs.r[6] = NULL;
		os_swi(0x0c,&regs);

		offset = regs.r[4];
		if(regs.r[3] == 0)
			break;    /* no items read */

		if(buf.type == 1)
		{

			/* file */
			if(memcmp_lc(buf.name,"articles",8) == 0)
			{
				filenum = atoi(&buf.name[8]);
				if(filenum >= N_ART_FILES)
					continue;

				/* find the latest time stamp for article files */
				ftime.time1 = buf.time1;
				ftime.time2 = buf.time2;

				if(filetime_compare(&ftime,&max_time) > 0)
				{
					max_time.time1 = ftime.time1;
					max_time.time2 = ftime.time2;
				}
			}
			else if(strcmp_lc(buf.name,"cards") == 0)
			{
				cards_time.time1 = buf.time1;
				cards_time.time2 = buf.time2;
			}
			else if(strcmp_lc(buf.name,"headers") == 0)
			{
				headers_time.time1 = buf.time1;
				headers_time.time2 = buf.time2;
			}
		}
	}

	if(filetime_compare(&cards_time,&max_time) < 0)
	{
		/* time-stamp all the articles files to ensure they are not in the future */
		if(cf==0)
			n_art_files = N_ART_FILES;
		else
			n_art_files = N_ART_FILES_X;

		for(i=0; i<n_art_files; i++)
		{
			sprintf(path,"%s.Articles%d",cardfile[cf].path,i);
			os_swi2(0x08+os_X,9,(int)path);    /* OS_File 9   Date/time stamp file */
		}

		return(article_file_recover(cf,2));
	}

	if(filetime_compare(&headers_time,&max_time) < 0)
	{
		return(article_file_recover(cf,3));
	}
	return(0);
}   /* end of article_check_integrity */






void boxlist_import(char *filename, int filetype)
/*******************************************/
{
	int  ix;
	int  box;
	int  cf;
	char *p;
	FILE *f;
	char buf[200];
	char path[200];
	char *leaf;
	int  new_box=0;
	int  size;
	sprite_area *sprite;
	ART_FILE_HEADER file_header;
	static char *pluto_sprite = "<pluto$dir>.!sprites";

	if(check_lock_lists(0xffff) != 0)
		return;  /* mustn't change cardfile[cf] while Ext.Box is being updated */

	if((filetype != 0x2000) && (filetype != 0x1000))
		return;

	strcpy(path,filename);  /* need to copy because RISCOSLin buffercan be overwritten */

	/* Directory dropped onto Boxes List window */
	sprintf(buf,"%s.articles0",path);

	if(get_filelength(buf) <= 0)
	{
		sprintf(buf,"%s.!Run",path);
		if(get_filelength(buf) > 0)
			return;     /* already has a !Run file, don't change it */


		/* doesn't contain articles0 file */
		if(query("Make a new external box?","Yes")==0)
			return;

		visdelay_begin();

		/* ensure directory name starts with ! */
		leaf = strrchr(path,'.');
		if(leaf != NULL)
		{
			strcpy(buf,path);
			leaf++;

			if(leaf[0] != '!')
			{
				for(ix=strlen(leaf); ix>=0; ix--)
				{
					leaf[ix+1] = leaf[ix];
				}
				leaf[0] = '!';
				rename(buf,path);
			}
		}

		sprintf(buf,"%s.!Run",path);

		if((f=fopen(buf,"w")) != NULL)
		{
			fprintf(f,"Do Run <Pluto$Dir>.!Run -box <Obey$Dir>\n");
			fclose(f);
			set_filetype(buf,0xfeb);
		}
		sprintf(buf,"%s.!Boot",path);
		if((f=fopen(buf,"w")) != NULL)
		{
			fprintf(f,"IconSprites <Obey$Dir>.!Sprites\n");
			fclose(f);
			set_filetype(buf,0xfeb);
		}

		/* copy !Pluto.!Sprites and change sprite name to match application name */
		size = get_filelength(pluto_sprite) + 64;
		sprite = (sprite_area *)malloc(size);
		if(sprite != NULL)
		{
			sprite_area_initialise(sprite,size);
			if(os_swi3(0x2e +os_X, 0x100+10, (int)sprite, (int)pluto_sprite)==NULL)
			{
				if(leaf != NULL)
				{
					os_swi4(0x2e +os_X, 0x100+26, (int)sprite, (int)"!pluto", (int)(leaf));  /* sprite rename */
				}
				sprintf(buf,"%s.!Sprites",path);
				os_swi3(0x2e +os_X, 0x100+12, (int)sprite, (int)buf);  /* sprite save */
			}
			free(sprite);
		}


		memset(&file_header,0,sizeof(file_header));
		file_header.version = ART_FILE_VERSION;


		for(ix=0; ix<N_ART_FILES_X; ix++)
		{
			sprintf(buf,"%s.articles%d",path,ix);
			if((f=fopen_werr(buf,"w",NULL))==NULL)
				break;

			fwrite(&file_header,1,sizeof(file_header),f);
			fclose(f);
		}

		new_box = 1;
		visdelay_end();
	}

	/* find a free external box */
	for(ix=0; ix<N_BOXES_EXTERN; ix++)
	{
		if(strcmp(cardfile[ix+1].path,path)==0)
		{
			werr(0,"External box '%s' is already loaded",box_table[BOX_EXTERN+ix].name);
			return;
		}
	}
	for(ix=0; ix<N_BOXES_EXTERN; ix++)
	{
		if(cardfile[ix+1].path[0]==0)
		{
			strcpy(cardfile[ix+1].path,path);
			break;
		}
	}
	if(ix==N_BOXES_EXTERN)
	{
		werr(0,"Limit of %d external boxes",N_BOXES_EXTERN);
		return;
	}

	box = BOX_EXTERN+ix;
	cf = ix+1;

	if(new_box)
	{
		article_file_recover(cf,0);
	}


	if(article_check_integrity(cf) < 0)
	{
		cardfile[cf].path[0]=0;
		return;
	}

	visdelay2_begin();
	cardfile_load(cf);
	visdelay2_end();

	memset(&box_table[box],0,sizeof(BOX));
	box_table[box].archive_to = 255;
	box_table[box].expire_to = BOX_BIN;

	/* use "Year" sorting type if it's present */
	for(ix=0; ix<N_DISPLAY_TAB; ix++)
	{
		if(strcmp(display_tab[ix].name,"Year")==0)
		{
			box_table[box].sort_on = ix;
			break;
		}
	}

	/* read box options from file */
	sprintf(buf,"%s.box",cardfile[cf].path);
	if((f=fopen(buf,"r"))!=NULL)
	{
		fread(&box_table[box],sizeof(BOX),1,f);
		fclose(f);
	}

	if(box_table[box].name[0]==0)
	{
		/* we haven't read a name so get one from directory name */
		if((p = strrchr(cardfile[cf].path,'.')) != NULL)
		{
			if(p[1] == '!') p++;
			strncpy0(box_table[box].name,(p+1),sizeof(box_table[box].name));
		}
	}
	make_boxes_menu(0,NULL);
	set_boxlist_extent(3);

	strncpy0(cardfile[cf].name,box_table[box].name,sizeof(cardfile[cf].name));

}   /* end of boxlist_import */



/******************************************************************/
/*                    MAIL BOXES LIST                             */
/******************************************************************/

extern int mbox_selected;
extern int select_user_flag;


void mbox_set_positions()
/***********************/
{
	int  i;

	for(i=0; i<(mboxlist.n_entries); i++)
	{
		options.mailbox[mbox_list[i]].position = i;
	}
}  /* end of mbox_set_positions */



void make_mbox_list()
/*****************/
{
	int  box;
	int  c;
	int  posn;
	int  min;
	int  min_pos;

	for(posn=0; posn<N_MAIL_BOX; posn++)
	{
		min = 255;

		for(box=0; box<N_MAIL_BOX; box++)
		{
			if(((c = options.mailbox[box].email_addr[0]) != 0) && (c != '@'))
			{
				if(options.mailbox[box].position < min)
				{
					min = options.mailbox[box].position;
					min_pos = box;
				}
			}
		}
		if(min < 255)
		{
			mbox_list[posn] = min_pos;
			options.mailbox[min_pos].position = 255;
		}
		else
		{
			break;   /* dealt with all the boxes */
		}
	}

	mboxlist.n_entries = posn;

	mbox_set_positions();
}   /* end of make_mbox_list */




void mbox_add()
/*************/
{
	int  mbox;

	for(mbox=0; mbox<N_MAIL_BOX; mbox++)
	{
		if((options.mailbox[mbox].email_addr[0] == 0) || (options.mailbox[mbox].email_addr[0] == '@'))
			break;
	}

	if(mbox < N_MAIL_BOX)
	{
		memset(&options.mailbox[mbox],0,sizeof(OPTIONS_MAILBOX));
		sprintf(options.mailbox[mbox].email_addr,"@%s",options.domain);
		options.mailbox[mbox].user_id_len=0;
		options.mailbox[mbox].position = mboxlist.n_entries;

		mbox_open(3);
		mbox_edit(mbox);

		make_mbox_list();
		user_select(NULL,mbox_selected,0);
	}
}   /* end of mbox_add */




void mboxlist_deselect(int line)
/******************************/
{
}   /* end of mboxlist_deselect */



void mboxlist_menu_proc(void *handle, char *hits)
/**********************************************/
{
	switch(hits[0])
	{
	case 1:      /* edit user */
		mbox_edit(mbox_selected);
		break;

	case 2:     /* add user */
		mbox_add();
		break;
	}

}   /* end of mboxlist_menu_proc */





void mboxlist_click_buttons(BLIST *b, wimp_eventdata *d)
/************************************************/
/* Click on the mail box-list button bar */
{
	int  icon;
	int  click;
	char buf[120];

	icon = d->but.m.i;
	click = d->but.m.bbits;

	switch(icon)
	{
	case 3:    /* add new mailbox */
		mbox_add();
		break;

	case 1:    /* delete selected mailbox */
		sprintf(buf,"Remove User '%s' ?",options.mailbox[mbox_selected].email_addr);
		if(query(buf,NULL)==0)
			break;

		memset(&options.mailbox[mbox_selected],0,sizeof(OPTIONS_MAILBOX));
		make_mbox_list();
		mbox_selected = 0;

		user_select(NULL,mbox_selected,0);
		mbox_open(3);
		options_save();
		break;

	case 2:    /* write, create blank article window */
		if(dbox_persist())
			new_reply(NULL,NULL,"",NULL,0,NULL);
		else
			new_reply(NULL,NULL,NULL,NULL,0,NULL);
	}
}   /* end of mboxlist_click_buttons */





void mboxlist_click_window(BLIST *b,int linenum,int bbits)
/**************************************************/
{
	switch(bbits)
	{
	case wimp_BCLICKLEFT:
		mbox_selected = mbox_list[linenum];
		user_select(NULL,mbox_selected,0);

		if(select_user_flag)
		{
			set_boxlist_extent(0x63);   /* open box list window, calulate totals */
			wimp_close_wind(b->w_buttons);
			wimp_close_wind(b->w_list);
			b->open = 0;
		}
		else
			set_boxlist_extent(0x25);   /* open box list window */

		select_user_flag = 0;
		break;

	case wimp_BRIGHT:        /* adjust, 2nd click */
		mbox_selected = mbox_list[linenum];
		user_select(NULL,mbox_selected,0);
		mbox_edit(mbox_selected);
		break;
	}

	redraw_blist(b);
}   /* end of mboxlist_click_window */




void mboxlist_shift(BLIST *b, int direction)
/*****************************************/
{
	int  i;
	int  posn;


	/* find position in the list of the selected box */
	for(posn=0; posn<BOX_EXTERN; posn++)
	{
		if(mbox_list[posn] == mbox_selected)
			break;
	}

	if(direction == -1)
	{
		/* move this box up */
		if(posn > 0)
		{
			i = mbox_list[posn-1];
			mbox_list[posn-1] = mbox_list[posn];
			mbox_list[posn] = i;
		}
		else
			beep();
	}
	else
	{
		/* move this box down */
		if(posn < (b->n_entries-1))
		{
			i = mbox_list[posn+1];
			mbox_list[posn+1] = mbox_list[posn];
			mbox_list[posn] = i;
		}
		else
			beep();
	}

	mbox_set_positions();
	options_save_flag = 1;

	redraw_blist(b);
}   /* end of mboxlist_shift */





void mboxlist_key_window(BLIST *b,wimp_eventdata *d)
/********************************************/
{
	int  c;

	switch(c = d->key.chcode)
	{
	case 26:   /* CTRL-Z */
		redraw_blist(b);
		break;

	case KEY_SH_UP:
		if(akbd_pollsh())
			mboxlist_shift(b,-1);
		break;

	case KEY_SH_DOWN:
		if(akbd_pollsh())
			mboxlist_shift(b,1);
		break;

	default:
		wimp_processkey(c);
		break;
	}
}   /* end of mboxlist_key_window */






