/* $Id: rules.cpp,v 1.20 2004/01/02 04:05:37 fesnel Exp $ */
/*******************************************************************************
 *   This program is part of the XFMail email client.                          *
 *                                                                             *
 *   Copyright : (C) 1995-1998 Gennady B. Sorokopud (gena@NetVision.net.il)    *
 *               (C) 1995 Ugen. J. S. Antsilevich (ugen@latte.worldbank.org)   *
 *               (C) 1998-2004 by the Archimedes Project                       *
 *                   http://sourceforge.net/projects/archimedes                *
 *                                                                             *
 *             --------------------------------------------                    *
 *                                                                             *
 *   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 2 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 to the Free Software               *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA.  *
 *                                                                             *
 *   Additional Permission granted:                                            *
 *                                                                             *
 *   This program is designed to use the XForms library, so we consider        *
 *   permission to link to that non-GPL-compatible library is implicit.        *
 *   However, in case this is not considered so, we explicitly state:          *
 *                                                                             *
 *   "As a special exception, the Archimedes Project, with the permission      *
 *    of all earlier copyright holders, formally gives permission to link      *
 *    this program with the XForms library, and distribute the resulting       *
 *    executable without the source code for XForms in the source              *
 *    distribution".                                                           *
 *                                                                             *
 ******************************************************************************/

#include <glib.h>

#include <fmail.h>
#include <umail.h>
#include <choose_folder.h>
#include <cfgfile.h>

static int ready = 0;
static FD_Rules_Form *rule_obj;
static int changed, r_modified;

/* Prototypes */
static void add_dead_rule(struct _xf_rule *rule);
static void cleanup_dead_rules(void);
static int compose_rule(int num);
static void display_rule(int num);

/* Rules */
extern vector<struct _xf_rule *> rules;

/* Dead rules */
static vector<struct _xf_rule *> dead_rules;

/* Saved rules -- used when selecting 'Don't Save' */
static vector<struct _xf_rule *> saved_rules;

/* Add a deleted rule candidate onto the dead_rules list. */
static void add_dead_rule(struct _xf_rule *rule)
{
	int i;

	/* Only add the rule if it's not already in the list */
	for (i = 0; i < (int)dead_rules.size(); i++)
		if (dead_rules[i] == rule)
			return;
	dead_rules.push_back(rule);
}

/* Cleanup the dead_rules list. Basically, any rules listed in
 * the dead rules list that are not in the final rules list, we
 * delete.
 */
static void cleanup_dead_rules(void)
{
	vector<struct _xf_rule *>::iterator i = dead_rules.begin();
	int j;

	while(i != dead_rules.end()) {
		for (j = 0; j < (int)rules.size(); j++)
			if (rules[j] == *i)
				break;
		if (j == (int)rules.size()) {
			/* Matching rule not found, so delete it */
#ifdef HAVE_REGCOMP
			regfree(&(*i)->rx);
#endif
			free(*i);
			i = dead_rules.erase(i);
		} else
			i++;
	}

	/* Clear the list for next time */
	dead_rules.clear();
}

void Rule_Name_Call(FL_OBJECT * obj, long param) {
}

void Rule_Resend_To_Call(FL_OBJECT * obj, long param) {
	fl_activate_object(rule_obj->Rule_File);
	fl_deactivate_object(rule_obj->Rule_Vac);
	fl_activate_object(rule_obj->Rule_Vac_Keep);
	fl_set_button(rule_obj->Rule_Vac_Keep, 1);
	fl_deactivate_object(rule_obj->Rule_Fold_Move);
	fl_deactivate_object(rule_obj->Rule_Folder);
	fl_deactivate_object(rule_obj->Rule_Fwd);
	fl_deactivate_object(rule_obj->Rule_Addr);
	fl_activate_object(rule_obj->Rule_Resend);
	fl_activate_object(rule_obj->Rule_Resend_Addr);
	fl_deactivate_object(rule_obj->Rule_Execute);
	fl_deactivate_object(rule_obj->Rule_Exec_File);
	fl_deactivate_object(rule_obj->Rule_EModify);
	fl_set_input(rule_obj->Rule_Execute, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Resend, "");
	r_modified = 1;
}

void Rule_Resend_Addr_Call(FL_OBJECT * obj, long param) {
	struct _mail_addr *addr;
	fl_freeze_form(rule_obj->Rules_Form);
	if(!(addr = addr_book(NULL, NULL, 0))) {
		fl_unfreeze_form(rule_obj->Rules_Form);
		return;
	}

	fl_unfreeze_form(rule_obj->Rules_Form);
	fl_set_input(rule_obj->Rule_Resend, addr->addr);
	r_modified = 1;
}

void Rule_Exec_Call(FL_OBJECT * obj, long param) {
	fl_deactivate_object(rule_obj->Rule_File);
	fl_deactivate_object(rule_obj->Rule_Vac);
	fl_activate_object(rule_obj->Rule_Vac_Keep);
	fl_set_button(rule_obj->Rule_Vac_Keep, 1);
	fl_deactivate_object(rule_obj->Rule_Fold_Move);
	fl_deactivate_object(rule_obj->Rule_Folder);
	fl_deactivate_object(rule_obj->Rule_Fwd);
	fl_deactivate_object(rule_obj->Rule_Addr);
	fl_deactivate_object(rule_obj->Rule_Resend);
	fl_deactivate_object(rule_obj->Rule_Resend_Addr);
	fl_activate_object(rule_obj->Rule_Execute);
	fl_activate_object(rule_obj->Rule_Exec_File);
	fl_activate_object(rule_obj->Rule_EModify);
	fl_set_input(rule_obj->Rule_Execute, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Resend, "");
	r_modified = 1;
}

void Rule_Exec_File_Call(FL_OBJECT * obj, long param) {
	char *fname;

	fl_freeze_form(rule_obj->Rules_Form);
	fl_set_fselector_title("Choose file/script to execute");
	if(!(fname = (char *) fl_show_file_selector("Execute", "", "", ""))) {
		fl_unfreeze_form(rule_obj->Rules_Form);
		return;
	}

	fl_unfreeze_form(rule_obj->Rules_Form);
	fl_set_input(rule_obj->Rule_Execute, fname);
	r_modified = 1;
}

void Rule_New_Call(FL_OBJECT * obj, long param) {
	fl_set_input(rule_obj->Rule_Name, "");
	fl_set_input(rule_obj->Rule_Match, "");
	fl_set_input(rule_obj->Rule_Field, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");

	fl_set_button(rule_obj->Rule_Move_To, 1);
	fl_set_button(rule_obj->Rule_Case, 0);
	fl_set_button(rule_obj->Rule_Vac_Keep, 0);
	fl_set_button(rule_obj->Rule_Save_Addr, 0);
	fl_set_button(rule_obj->Rule_Mark_Read, 0);
	fl_set_button(rule_obj->Rule_Incoming, 1);
	fl_set_button(rule_obj->Rule_Log, 0);

	fl_activate_object(rule_obj->Rule_Fold_Move);
	fl_activate_object(rule_obj->Rule_Folder);
	fl_deactivate_object(rule_obj->Rule_Fwd);
	fl_deactivate_object(rule_obj->Rule_Addr);
	fl_deactivate_object(rule_obj->Rule_Vac);
	fl_deactivate_object(rule_obj->Rule_Vac_Keep);
	fl_deactivate_object(rule_obj->Rule_Resend);
	fl_deactivate_object(rule_obj->Rule_Resend_Addr);
	fl_deactivate_object(rule_obj->Rule_Execute);
	fl_deactivate_object(rule_obj->Rule_Exec_File);
	fl_deactivate_object(rule_obj->Rule_EModify);

	fl_set_focus_object(rule_obj->Rules_Form, rule_obj->Rule_Name);
	changed = 0;
	r_modified = 0;
}

void Rule_Case_Call(FL_OBJECT * obj, long param) {
	r_modified = 1;
}

void Rule_MRead_Call(FL_OBJECT * obj, long param) {
	r_modified = 1;
}

void Rule_Up_Call(FL_OBJECT * obj, long param) {
	struct _xf_rule *tmp_rule;
	int i, r1, r2;
	int k = fl_get_browser(rule_obj->Rule_List);

	if(k < 2)
		return;

	r1 =
	rule_by_name((char *) fl_get_browser_line(rule_obj->Rule_List, k));
	r2 =
	rule_by_name((char *)
				 fl_get_browser_line(rule_obj->Rule_List, --k));

	if((r1 == -1) || (r2 == -1))
		return;

	tmp_rule = rules[r1];
	rules[r1] = rules[r2];
	rules[r2] = tmp_rule;

	fl_freeze_form(rule_obj->Rules_Form);
	fl_clear_browser(rule_obj->Rule_List);
	for(i = 0; i < (int)rules.size(); i++) {
		//if(rules[i]->action == 0)
			//continue;

		fl_add_browser_line(rule_obj->Rule_List, rules[i]->name);
	}

	fl_show_browser_line(rule_obj->Rule_List, k);
	fl_select_browser_line(rule_obj->Rule_List, k);
	fl_unfreeze_form(rule_obj->Rules_Form);
	changed = 1;
	return;
}

void Rule_Down_Call(FL_OBJECT * obj, long param) {
	struct _xf_rule *tmp_rule;
	int i, r1, r2;
	int k = fl_get_browser(rule_obj->Rule_List);

	if(k == fl_get_browser_maxline(rule_obj->Rule_List))
		return;

	r1 =
	rule_by_name((char *) fl_get_browser_line(rule_obj->Rule_List, k));
	r2 =
	rule_by_name((char *)
				 fl_get_browser_line(rule_obj->Rule_List, ++k));

	if((r1 == -1) || (r2 == -1))
		return;

	tmp_rule = rules[r1];
	rules[r1] = rules[r2];
	rules[r2] = tmp_rule;

	fl_freeze_form(rule_obj->Rules_Form);
	fl_clear_browser(rule_obj->Rule_List);
	for(i = 0; i < (int)rules.size(); i++) {
		//if(rules[i]->action == 0)
			//continue;

		fl_add_browser_line(rule_obj->Rule_List, rules[i]->name);
	}

	fl_show_browser_line(rule_obj->Rule_List, k);
	fl_select_browser_line(rule_obj->Rule_List, k);
	fl_unfreeze_form(rule_obj->Rules_Form);
	changed = 1;
	return;
}

void Rule_Common_Field_Call(FL_OBJECT * obj, long param) {
	char fname[16], *p;

	snprintf(fname, sizeof(fname), "%s", fl_get_choice_text(obj));
	if((p = strrchr(fname, '%')) != NULL)
		*p = '\0';

	fl_set_input(rule_obj->Rule_Field, fname);
	r_modified = 1;
}

void Rule_Dumb_Call(FL_OBJECT * obj, long param) {
	r_modified = 1;
}

void Rule_List_Call(FL_OBJECT * obj, long param) {
	if(r_modified
	   && display_msg(MSG_QUEST, "rule was modified", "store changes?"))
		Rule_Modify_Call(NULL, param);

	r_modified = 0;
	display_rule(rule_by_name
				 ((char *) fl_get_browser_line(obj, fl_get_browser(obj))));
}

void Rule_Add_Call(FL_OBJECT * obj, long param) {
	int k, m;
	
	if(rule_by_name((char *) fl_get_input(rule_obj->Rule_Name)) != -1) {
		display_msg(MSG_WARN, "add", "Rule with name %s already exists",
					fl_get_input(rule_obj->Rule_Name));
		return;
	}

	r_modified = 0;
	m = (int)rules.size();
	if(compose_rule(m+1) != -1) {       	
	fl_freeze_form(rule_obj->Rules_Form);
		fl_clear_browser(rule_obj->Rule_List);

		for(k = 0; k < (int)rules.size(); k++)
			fl_add_browser_line(rule_obj->Rule_List, rules[k]->name);
	fl_select_browser_line(rule_obj->Rule_List, k);
	fl_show_browser_line(rule_obj->Rule_List, k);

		fl_unfreeze_form(rule_obj->Rules_Form);
		changed = 1;
	}
	else 
		display_msg(MSG_WARN, "add rule", "Invalid rule");
	
	return;    
}

void Rule_Delete_Call(FL_OBJECT * obj, long param) {
	int i;
	int k = fl_get_browser(rule_obj->Rule_List);

	if(k == 0)
		return;

	i = rule_by_name((char *) fl_get_browser_line(rule_obj->Rule_List, k));

	if(i == -1)
		return;

	r_modified = 0;
	fl_delete_browser_line(rule_obj->Rule_List, k);
	add_dead_rule(rules[i]);
	rules.erase(rules.begin() + i);

	changed = 1;

	/*
	 * Display the next rule in the list, or if the last rule was deleted,
	 * display the previous one.
	 */
	if (k > (int)rules.size() && k != 1)
		k--;

	display_rule(k - 1);
	fl_select_browser_line(rule_obj->Rule_List, k);
}

void Rule_Modify_Call(FL_OBJECT * obj, long param) {
	int i;

	i = rule_by_name((char *) fl_get_input(rule_obj->Rule_Name));

	if(i == -1)
		return;

	if(compose_rule(i) != -1) {
		fl_replace_browser_line(rule_obj->Rule_List, i + 1, rules[i]->name);
		changed = 1;
	} else {
		display_msg(MSG_WARN, "modify", "Invalid rule");
		display_rule(i);
	}

	r_modified = 0;
}

void Rule_Save_Call(FL_OBJECT * obj, long param) {
	Rule_Modify_Call(NULL, param);
	if (save_rules() == 0) {
		changed = 0;
		saved_rules = rules;
	}
}

void Rule_Folder_Call(FL_OBJECT * obj, long param) {
	struct _mail_folder *mf;
	fl_freeze_form(rule_obj->Rules_Form);
	if(!(mf = choose_folder())) {
		fl_unfreeze_form(rule_obj->Rules_Form);
		return;
	}

	fl_unfreeze_form(rule_obj->Rules_Form);
	fl_set_input(rule_obj->Rule_Fold_Move, get_folder_full_name(mf));
	r_modified = 1;
}

void Rule_File_Call(FL_OBJECT * obj, long param) {
	char *fname;

	fl_freeze_form(rule_obj->Rules_Form);
	fl_set_fselector_title("Choose vacation file");
	if(!
	   (fname =
		(char *) fl_show_file_selector("Vacation", "", "",
									   "vacation.txt"))) {
		fl_unfreeze_form(rule_obj->Rules_Form);
		return;
	}

	fl_unfreeze_form(rule_obj->Rules_Form);
	fl_set_input(rule_obj->Rule_Vac, fname);
	r_modified = 1;
}

void Rule_Addr_Call(FL_OBJECT * obj, long param) {
	struct _mail_addr *addr;
	fl_freeze_form(rule_obj->Rules_Form);
	if(!(addr = addr_book(NULL, NULL, 0))) {
		fl_unfreeze_form(rule_obj->Rules_Form);
		return;
	}

	fl_unfreeze_form(rule_obj->Rules_Form);
	fl_set_input(rule_obj->Rule_Fwd, addr->addr);
	r_modified = 1;
}

void Rule_Load_Call(FL_OBJECT * obj, long param) {
	int i;

	if(changed && !readonly)
		if(display_msg(MSG_QUEST, "Do you want to save changes?", ""))
			save_rules();

	/* Clear dead rules */
	cleanup_dead_rules();

	load_rules();
	saved_rules = rules;
	fl_freeze_form(rule_obj->Rules_Form);
	fl_clear_browser(rule_obj->Rule_List);
	for(i = 0; i < (int)rules.size(); i++) {
		//if(rules[i]->action == 0)
			//continue;
		fl_add_browser_line(rule_obj->Rule_List, rules[i]->name);
	}
	fl_unfreeze_form(rule_obj->Rules_Form);

	fl_set_choice(rule_obj->Rule_Common_Field, 1);
	fl_set_button(rule_obj->Rule_Move_To, 1);
	display_rule(0);
	fl_select_browser_line(rule_obj->Rule_List, 1);

	r_modified = 0;
	changed = 0;
}

void Rule_Move_To_Call(FL_OBJECT * obj, long param) {
	fl_activate_object(rule_obj->Rule_Fold_Move);
	fl_activate_object(rule_obj->Rule_Folder);
	fl_deactivate_object(rule_obj->Rule_Fwd);
	fl_deactivate_object(rule_obj->Rule_Addr);
	fl_deactivate_object(rule_obj->Rule_Vac);
	fl_deactivate_object(rule_obj->Rule_Vac_Keep);
	fl_deactivate_object(rule_obj->Rule_File);
	fl_deactivate_object(rule_obj->Rule_Resend);
	fl_deactivate_object(rule_obj->Rule_Resend_Addr);
	fl_deactivate_object(rule_obj->Rule_Execute);
	fl_deactivate_object(rule_obj->Rule_Exec_File);
	fl_deactivate_object(rule_obj->Rule_EModify);
	fl_set_input(rule_obj->Rule_Execute, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Resend, "");
	r_modified = 1;
}

void Rule_Fwd_To_Call(FL_OBJECT * obj, long param) {
	fl_deactivate_object(rule_obj->Rule_Fold_Move);
	fl_deactivate_object(rule_obj->Rule_Folder);
	fl_activate_object(rule_obj->Rule_Fwd);
	fl_activate_object(rule_obj->Rule_Addr);
	fl_activate_object(rule_obj->Rule_Vac_Keep);
	fl_set_button(rule_obj->Rule_Vac_Keep, 1);
	fl_deactivate_object(rule_obj->Rule_Vac);
	fl_deactivate_object(rule_obj->Rule_File);
	fl_deactivate_object(rule_obj->Rule_Resend);
	fl_deactivate_object(rule_obj->Rule_Resend_Addr);
	fl_deactivate_object(rule_obj->Rule_Execute);
	fl_deactivate_object(rule_obj->Rule_Exec_File);
	fl_deactivate_object(rule_obj->Rule_EModify);
	fl_set_input(rule_obj->Rule_Execute, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Resend, "");
	r_modified = 1;
}

void Rule_Discard_Call(FL_OBJECT * obj, long param) {
	fl_deactivate_object(rule_obj->Rule_Fold_Move);
	fl_deactivate_object(rule_obj->Rule_Folder);
	fl_deactivate_object(rule_obj->Rule_Fwd);
	fl_deactivate_object(rule_obj->Rule_Addr);
	fl_deactivate_object(rule_obj->Rule_Vac);
	fl_deactivate_object(rule_obj->Rule_Vac_Keep);
	fl_deactivate_object(rule_obj->Rule_File);
	fl_deactivate_object(rule_obj->Rule_Resend);
	fl_deactivate_object(rule_obj->Rule_Resend_Addr);
	fl_deactivate_object(rule_obj->Rule_Execute);
	fl_deactivate_object(rule_obj->Rule_Exec_File);
	fl_deactivate_object(rule_obj->Rule_EModify);
	fl_set_input(rule_obj->Rule_Execute, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Resend, "");
	r_modified = 1;
}

void Rule_Vacation_Call(FL_OBJECT * obj, long param) {
	fl_activate_object(rule_obj->Rule_File);
	fl_activate_object(rule_obj->Rule_Vac);
	fl_activate_object(rule_obj->Rule_Vac_Keep);
	fl_set_button(rule_obj->Rule_Vac_Keep, 1);
	fl_deactivate_object(rule_obj->Rule_Fold_Move);
	fl_deactivate_object(rule_obj->Rule_Folder);
	fl_deactivate_object(rule_obj->Rule_Fwd);
	fl_deactivate_object(rule_obj->Rule_Addr);
	fl_deactivate_object(rule_obj->Rule_Resend);
	fl_deactivate_object(rule_obj->Rule_Resend_Addr);
	fl_deactivate_object(rule_obj->Rule_Execute);
	fl_deactivate_object(rule_obj->Rule_Exec_File);
	fl_deactivate_object(rule_obj->Rule_EModify);
	fl_set_input(rule_obj->Rule_Execute, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Resend, "");
	r_modified = 1;
}

static int compose_rule(int num) {
	size_t i;
	char *p;
	struct _xf_rule * new_rule;

#ifdef HAVE_REGCOMP
	char errbuf[2048];
	int regerr;
	int regflags;
#endif

	p = (char *) fl_get_input(rule_obj->Rule_Name);
	if(!p || (strlen(p) < 2) || (strlen(p) >= sizeof(new_rule->name))) {
		display_msg(MSG_WARN, "rule", "rule name must have 2-%d characters",
			sizeof(new_rule->name) - 1);
		return -1;
	}

	if(!isalpha(*p)) {
		display_msg(MSG_WARN, "rule", "First character in rule name must be letter");
		return -1;
	}

	for(i = 1; i < strlen(p); i++) {
		if(!isprint(p[i]) || (p[i] == ' ')) {
			display_msg(MSG_WARN, "rule", "invalid character in rule name");
			return -1;
		}
	}

	new_rule = (struct _xf_rule *) malloc(sizeof(struct _xf_rule));
	strcpy(new_rule->name, p);
	/* While new_rule is not really 'dead', we add it to the dead list
	 * in case the user decides NOT to save changes.
	 */
	add_dead_rule(new_rule);

	new_rule->flags = 0;

	snprintf(new_rule->tmatch, sizeof(new_rule->tmatch), "%s",
		fl_get_input(rule_obj->Rule_Match));
	snprintf(new_rule->fmatch, sizeof(new_rule->fmatch), "%s",
		fl_get_input(rule_obj->Rule_Field));

	if(!fl_get_button(rule_obj->Rule_Case))
		new_rule->flags |= CASE_I;

	if(fl_get_button(rule_obj->Rule_Save_Addr))
		new_rule->flags |= R_SAVE_ADDR;

	if(fl_get_button(rule_obj->Rule_Mark_Read))
		new_rule->flags |= R_MARK_READ;

	if(fl_get_button(rule_obj->Rule_Silent))
		new_rule->flags |= R_SILENT;

	if(fl_get_button(rule_obj->Rule_NIncoming))
		new_rule->flags |= R_NINCOMING;
	else if(fl_get_button(rule_obj->Rule_Outgoing))
		new_rule->flags |= R_OUTGOING;

	if(fl_get_button(rule_obj->Rule_Log))
		new_rule->flags |= R_LOG;

	if(fl_get_button(rule_obj->Rule_Move_To)) {
		new_rule->action = R_MOVE;
		snprintf(new_rule->data, sizeof(new_rule->data), "%s",
			fl_get_input(rule_obj->Rule_Fold_Move));
	} else if(fl_get_button(rule_obj->Rule_Discard)) {
		new_rule->action = R_DISCARD;
		strcpy(new_rule->data, TRASH);
	} else if(fl_get_button(rule_obj->Rule_Fwd_To)) {
		new_rule->action = R_FORWARD;
		snprintf(new_rule->data, sizeof(new_rule->data), "%s",
			fl_get_input(rule_obj->Rule_Fwd));
		
	if(fl_get_button(rule_obj->Rule_Vac_Keep))
			new_rule->flags |= R_VAC_KEEP;
		else
			fl_show_alert("WARNING!!! 'Keep messages' is not set",
						  "messages matching this rule will be",
						  "permanently deleted after being forwarded", 1);
	} else if(fl_get_button(rule_obj->Rule_Vacation)) {
		new_rule->action = R_VACATION;
		snprintf(new_rule->data, sizeof(new_rule->data), "%s",
			fl_get_input(rule_obj->Rule_Vac));
		
	if(fl_get_button(rule_obj->Rule_Vac_Keep))
			new_rule->flags |= R_VAC_KEEP;
		else
			fl_show_alert("WARNING!!! 'Keep messages' is not set",
						  "messages matching this rule will be",
						  "permanently deleted after being answered", 1);
	} else if(fl_get_button(rule_obj->Rule_Resend_To)) {
		new_rule->action = R_RESEND;
		snprintf(new_rule->data, sizeof(new_rule->data), "%s",
			fl_get_input(rule_obj->Rule_Resend));

		if(fl_get_button(rule_obj->Rule_Vac_Keep))
			new_rule->flags |= R_VAC_KEEP;
		else
			fl_show_alert("WARNING!!! 'Keep messages' is not set",
						  "messages matching this rule will be",
						  "permanently deleted after being resent", 1);
	} else if(fl_get_button(rule_obj->Rule_Exec)) {
		new_rule->action = R_EXECUTE;
		snprintf(new_rule->data, sizeof(new_rule->data), "%s",
			fl_get_input(rule_obj->Rule_Execute));

		if(fl_get_button(rule_obj->Rule_Vac_Keep))
			new_rule->flags |= R_VAC_KEEP;
		else
			fl_show_alert("WARNING!!! 'Keep messages' is not set",
						  "messages matching this rule will be",
						  "permanently deleted after execution is complete",
						  1);
		if(fl_get_button(rule_obj->Rule_EModify))
			new_rule->flags |= R_EMODIFY;
	} else {
		new_rule->action = R_MOVE;
		strcpy(new_rule->data, INBOX);
	}

#ifdef HAVE_REGCOMP
	if(&new_rule->rx) {
	/* for some UNIX imitations with strange mem management only :-) */
#if defined(__linux__)
	new_rule->rx.buffer=(unsigned char *) NULL;
	new_rule->rx.fastmap=(char *) NULL;
	new_rule->rx.translate=(char *) NULL;
#endif
		regfree(&new_rule->rx);
	}

	regflags = REG_EXTENDED;
	if(new_rule->flags & CASE_I)
		regflags |= REG_ICASE;

	if((regerr = regcomp(&new_rule->rx, fl_get_input(rule_obj->Rule_Match),
		regflags))) {
		regerror(regerr, &new_rule->rx, errbuf, sizeof(errbuf));
		display_msg(MSG_WARN, "Invalid regular expression", "%s", errbuf);
		return -1;
	}
#endif

	if(strchr(new_rule->data, ' ')) {
		display_msg(MSG_WARN, "error in rule", "you can not use spaces in rule definition");
		strcpy(new_rule->data, "##error##");
		return -1;
	}
	if(num > (int)rules.size())
		rules.push_back(new_rule);
	else {
		add_dead_rule(rules[num]);
		rules[num] = new_rule;
	}
	return 0;
}

static void display_rule(int num) {
	
	if (rules.empty())
		return;

	fl_set_input(rule_obj->Rule_Name, rules[num]->name);
	fl_set_input(rule_obj->Rule_Match, rules[num]->tmatch);
	fl_set_input(rule_obj->Rule_Field, rules[num]->fmatch);

	fl_set_input(rule_obj->Rule_Execute, "");
	fl_set_input(rule_obj->Rule_Fold_Move, "");
	fl_set_input(rule_obj->Rule_Fwd, "");
	fl_set_input(rule_obj->Rule_Vac, "");
	fl_set_input(rule_obj->Rule_Resend, "");

	fl_set_button(rule_obj->Rule_Move_To, 0);
	fl_set_button(rule_obj->Rule_Discard, 0);
	fl_set_button(rule_obj->Rule_Fwd_To, 0);
	fl_set_button(rule_obj->Rule_Vacation, 0);
	fl_set_button(rule_obj->Rule_Resend_To, 0);
	fl_set_button(rule_obj->Rule_Exec, 0);

	if(rules[num]->flags & CASE_I)
		fl_set_button(rule_obj->Rule_Case, 0);
	else
		fl_set_button(rule_obj->Rule_Case, 1);

	if(rules[num]->flags & R_SAVE_ADDR)
		fl_set_button(rule_obj->Rule_Save_Addr, 1);
	else
		fl_set_button(rule_obj->Rule_Save_Addr, 0);

	if(rules[num]->flags & R_MARK_READ)
		fl_set_button(rule_obj->Rule_Mark_Read, 1);
	else
		fl_set_button(rule_obj->Rule_Mark_Read, 0);

	if(rules[num]->flags & R_VAC_KEEP)
		fl_set_button(rule_obj->Rule_Vac_Keep, 1);
	else
		fl_set_button(rule_obj->Rule_Vac_Keep, 0);

	if(rules[num]->flags & R_SILENT)
		fl_set_button(rule_obj->Rule_Silent, 1);
	else
		fl_set_button(rule_obj->Rule_Silent, 0);

	if(rules[num]->flags & R_NINCOMING)
		fl_set_button(rule_obj->Rule_NIncoming, 1);
	else if(rules[num]->flags & R_OUTGOING)
		fl_set_button(rule_obj->Rule_Outgoing, 1);
	else
		fl_set_button(rule_obj->Rule_Incoming, 1);

	if(rules[num]->flags & R_LOG)
		fl_set_button(rule_obj->Rule_Log, 1);
	else
		fl_set_button(rule_obj->Rule_Log, 0);

	if(rules[num]->flags & R_EMODIFY)
		fl_set_button(rule_obj->Rule_EModify, 1);
	else
		fl_set_button(rule_obj->Rule_EModify, 0);

	switch(rules[num]->action) {
		case R_MOVE:
			fl_set_button(rule_obj->Rule_Move_To, 1);
			fl_set_input(rule_obj->Rule_Fold_Move, rules[num]->data);

			fl_activate_object(rule_obj->Rule_Fold_Move);
			fl_activate_object(rule_obj->Rule_Folder);
			fl_deactivate_object(rule_obj->Rule_Addr);
			fl_deactivate_object(rule_obj->Rule_Fwd);
			fl_deactivate_object(rule_obj->Rule_Vac);
			fl_deactivate_object(rule_obj->Rule_Vac_Keep);
			fl_deactivate_object(rule_obj->Rule_File);
			fl_deactivate_object(rule_obj->Rule_Resend);
			fl_deactivate_object(rule_obj->Rule_Resend_Addr);
			fl_deactivate_object(rule_obj->Rule_Execute);
			fl_deactivate_object(rule_obj->Rule_Exec_File);
			fl_deactivate_object(rule_obj->Rule_EModify);
			break;

		case R_DISCARD:
			fl_set_button(rule_obj->Rule_Discard, 1);

			fl_deactivate_object(rule_obj->Rule_Fold_Move);
			fl_deactivate_object(rule_obj->Rule_Folder);
			fl_deactivate_object(rule_obj->Rule_Fwd);
			fl_deactivate_object(rule_obj->Rule_Addr);
			fl_deactivate_object(rule_obj->Rule_Vac);
			fl_deactivate_object(rule_obj->Rule_Vac_Keep);
			fl_deactivate_object(rule_obj->Rule_File);
			fl_deactivate_object(rule_obj->Rule_Resend);
			fl_deactivate_object(rule_obj->Rule_Resend_Addr);
			fl_deactivate_object(rule_obj->Rule_Execute);
			fl_deactivate_object(rule_obj->Rule_Exec_File);
			fl_deactivate_object(rule_obj->Rule_EModify);
			break;

		case R_FORWARD:
			fl_set_button(rule_obj->Rule_Fwd_To, 1);
			fl_set_input(rule_obj->Rule_Fwd, rules[num]->data);

			fl_deactivate_object(rule_obj->Rule_Fold_Move);
			fl_deactivate_object(rule_obj->Rule_Folder);
			fl_activate_object(rule_obj->Rule_Fwd);
			fl_activate_object(rule_obj->Rule_Addr);
			fl_activate_object(rule_obj->Rule_Vac_Keep);
			fl_deactivate_object(rule_obj->Rule_Vac);
			fl_deactivate_object(rule_obj->Rule_File);
			fl_deactivate_object(rule_obj->Rule_EModify);
			break;

		case R_VACATION:
			fl_set_button(rule_obj->Rule_Vacation, 1);
			fl_set_input(rule_obj->Rule_Vac, rules[num]->data);

			fl_activate_object(rule_obj->Rule_File);
			fl_activate_object(rule_obj->Rule_Vac);
			fl_activate_object(rule_obj->Rule_Vac_Keep);

			fl_deactivate_object(rule_obj->Rule_Fold_Move);
			fl_deactivate_object(rule_obj->Rule_Folder);
			fl_deactivate_object(rule_obj->Rule_Fwd);
			fl_deactivate_object(rule_obj->Rule_Addr);
			fl_deactivate_object(rule_obj->Rule_Resend);
			fl_deactivate_object(rule_obj->Rule_Resend_Addr);
			fl_deactivate_object(rule_obj->Rule_Execute);
			fl_deactivate_object(rule_obj->Rule_Exec_File);
			fl_deactivate_object(rule_obj->Rule_EModify);
			break;

		case R_RESEND:
			fl_set_button(rule_obj->Rule_Resend_To, 1);
			fl_set_input(rule_obj->Rule_Resend, rules[num]->data);

			fl_deactivate_object(rule_obj->Rule_File);
			fl_deactivate_object(rule_obj->Rule_Vac);
			fl_activate_object(rule_obj->Rule_Vac_Keep);

			fl_deactivate_object(rule_obj->Rule_Fold_Move);
			fl_deactivate_object(rule_obj->Rule_Folder);
			fl_deactivate_object(rule_obj->Rule_Fwd);
			fl_deactivate_object(rule_obj->Rule_Addr);
			fl_activate_object(rule_obj->Rule_Resend);
			fl_activate_object(rule_obj->Rule_Resend_Addr);
			fl_deactivate_object(rule_obj->Rule_Execute);
			fl_deactivate_object(rule_obj->Rule_Exec_File);
			fl_deactivate_object(rule_obj->Rule_EModify);
			break;

		case R_EXECUTE:
			fl_set_button(rule_obj->Rule_Exec, 1);
			fl_set_input(rule_obj->Rule_Execute, rules[num]->data);

			fl_deactivate_object(rule_obj->Rule_File);
			fl_deactivate_object(rule_obj->Rule_Vac);
			fl_activate_object(rule_obj->Rule_Vac_Keep);
			fl_activate_object(rule_obj->Rule_EModify);

			fl_deactivate_object(rule_obj->Rule_Fold_Move);
			fl_deactivate_object(rule_obj->Rule_Folder);
			fl_deactivate_object(rule_obj->Rule_Fwd);
			fl_deactivate_object(rule_obj->Rule_Addr);
			fl_deactivate_object(rule_obj->Rule_Resend);
			fl_deactivate_object(rule_obj->Rule_Resend_Addr);
			fl_activate_object(rule_obj->Rule_Execute);
			fl_activate_object(rule_obj->Rule_Exec_File);
			break;

		default:
			rules[num]->action = R_MOVE;
			fl_set_button(rule_obj->Rule_Move_To, 1);
			fl_set_input(rule_obj->Rule_Fold_Move, rules[num]->data);
			break;
	}

}

void rules_conf() {
	int i;
	
	saved_rules = rules;

	if(ready) {
		XRaiseWindow(fl_display, rule_obj->Rules_Form->window);
		return;
	}

	ready = 1;
	changed = 0;
	r_modified = 0;

	rule_obj = create_form_Rules_Form();

	for(i = 0; i < (int)rules.size(); i++) {
		//if(rules[i]->action == 0)
			//continue;
		fl_add_browser_line(rule_obj->Rule_List, rules[i]->name);
	}

	fl_clear_choice(rule_obj->Rule_Common_Field);

	fl_addto_choice(rule_obj->Rule_Common_Field, "Message");
	fl_addto_choice(rule_obj->Rule_Common_Field, "Header");
	fl_addto_choice(rule_obj->Rule_Common_Field, "Recipients");
	fl_addto_choice(rule_obj->Rule_Common_Field, "Body%l");

	fl_addto_choice(rule_obj->Rule_Common_Field, "Subject");
	fl_addto_choice(rule_obj->Rule_Common_Field, "From");
	fl_addto_choice(rule_obj->Rule_Common_Field, "To");
	fl_addto_choice(rule_obj->Rule_Common_Field, "Cc");
	fl_addto_choice(rule_obj->Rule_Common_Field, "Bcc");
	fl_addto_choice(rule_obj->Rule_Common_Field, "Sender");
	fl_addto_choice(rule_obj->Rule_Common_Field, "Flags");

	fl_set_choice(rule_obj->Rule_Common_Field, 1);
	fl_set_button(rule_obj->Rule_Move_To, 1);
	display_rule(0);
	fl_select_browser_line(rule_obj->Rule_List, 1);

	fl_set_input_return(rule_obj->Rule_Match, FL_RETURN_CHANGED);
	fl_set_input_return(rule_obj->Rule_Field, FL_RETURN_CHANGED);
	fl_set_input_return(rule_obj->Rule_Fold_Move, FL_RETURN_CHANGED);
	fl_set_input_return(rule_obj->Rule_Fwd, FL_RETURN_CHANGED);
	fl_set_input_return(rule_obj->Rule_Vac, FL_RETURN_CHANGED);

	fl_show_form(rule_obj->Rules_Form, FL_PLACE_CENTER, FL_TRANSIENT,
				 "Rules");
	fl_do_only_forms();

	if(changed && !readonly) {
		if(display_msg(MSG_QUEST, "Do you want to save changes?", ""))
			save_rules();
		else
			rules = saved_rules;
		cleanup_dead_rules();
	}
	changed = 0;
	r_modified = 0;

	fl_hide_form(rule_obj->Rules_Form);
	fl_free_form(rule_obj->Rules_Form);
	fl_free(rule_obj);
	rule_obj = NULL;

	saved_rules.clear();

	ready = 0;
}
