1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2012 the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24 
25 #include "defs.h"
26 
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 
36 #include "gtkcmoptionmenu.h"
37 #include "main.h"
38 #include "prefs_gtk.h"
39 #include "prefs_filtering_action.h"
40 #include "prefs_common.h"
41 #include "mainwindow.h"
42 #include "foldersel.h"
43 #include "manage_window.h"
44 #include "inc.h"
45 #include "matcher.h"
46 #include "utils.h"
47 #include "gtkutils.h"
48 #include "alertpanel.h"
49 #include "folder.h"
50 #include "description_window.h"
51 #include "addr_compl.h"
52 #include "tags.h"
53 #include "matcher_parser.h"
54 #include "colorlabel.h"
55 #include "combobox.h"
56 #include "account.h"
57 #ifndef USE_ALT_ADDRBOOK
58 	#include "addressbook.h"
59 #endif
60 
61 enum {
62 	PFA_ACTION,
63 	PFA_VALID_ACTION,
64 	N_PFA_COLUMNS
65 };
66 
67 
68 static void prefs_filtering_action_create(void);
69 static void prefs_filtering_action_delete_cb(void);
70 static void prefs_filtering_action_substitute_cb(void);
71 static void prefs_filtering_action_register_cb(void);
72 static void prefs_filtering_action_reset_dialog(void);
73 static gboolean prefs_filtering_action_key_pressed(GtkWidget *widget,
74     GdkEventKey *event, gpointer data);
75 static void prefs_filtering_action_cancel(void);
76 static void prefs_filtering_action_ok(void);
77 static gint prefs_filtering_action_deleted(GtkWidget *widget,
78     GdkEventAny *event, gpointer data);
79 static void prefs_filtering_action_type_selection_changed(GtkWidget *widget,
80     gpointer user_data);
81 static void prefs_filtering_action_select_dest(void);
82 #ifndef USE_ALT_ADDRBOOK
83 static void prefs_filtering_action_select_addressbook(void);
84 #endif
85 static void prefs_filtering_action_up(void);
86 static void prefs_filtering_action_down(void);
87 static void prefs_filtering_action_set_dialog(GSList *action_list);
88 static GSList *prefs_filtering_action_get_list(void);
89 
90 static GtkListStore* prefs_filtering_action_create_data_store	(void);
91 static void prefs_filtering_action_list_view_insert_action	(GtkWidget   *list_view,
92 								 GtkTreeIter *row,
93 								 const gchar *action,
94 								 gboolean     is_valid);
95 static GtkWidget *prefs_filtering_action_list_view_create	(void);
96 static void prefs_filtering_action_create_list_view_columns	(GtkTreeView *list_view);
97 static gboolean prefs_filtering_actions_selected		(GtkTreeSelection *selector,
98 								 GtkTreeModel *model,
99 								 GtkTreePath *path,
100 								 gboolean currently_selected,
101 								 gpointer data);
102 
103 static void prefs_filtering_action_exec_info			(GtkWidget *widget,
104 								 GtkWidget *parent);
105 
106 /*!
107  *\brief	UI data for matcher dialog
108  */
109 static struct FilteringAction_ {
110 	GtkWidget *window;
111 
112 	GtkWidget *ok_btn;
113 
114 	GtkWidget *action_list_view;
115 	GtkWidget *label1;
116 	GtkWidget *label2;
117 	GtkWidget *label3;
118 	GtkWidget *action_combo;
119 	GtkWidget *account_combo;
120 	GtkWidget *dest_entry;
121 	GtkWidget *dest_btn;
122 	GtkWidget *exec_btn;
123 	GtkWidget *color_optmenu;
124 	GtkWidget *header_combo;
125 	GtkWidget *header_entry;
126 #ifndef USE_ALT_ADDRBOOK
127 	GtkWidget *addressbook_btn;
128 #endif
129 	GtkWidget *score_entry;
130 	GtkWidget *tags_combo;
131 
132 	gint current_action;
133 } filtering_action;
134 
135 
136 typedef enum Action_ {
137 	ACTION_MOVE,
138 	ACTION_COPY,
139 	ACTION_DELETE,
140 	ACTION_MARK,
141 	ACTION_UNMARK,
142 	ACTION_LOCK,
143 	ACTION_UNLOCK,
144 	ACTION_MARK_AS_READ,
145 	ACTION_MARK_AS_UNREAD,
146 	ACTION_MARK_AS_SPAM,
147 	ACTION_MARK_AS_HAM,
148 	ACTION_FORWARD,
149 	ACTION_FORWARD_AS_ATTACHMENT,
150 	ACTION_REDIRECT,
151 	ACTION_EXECUTE,
152 	ACTION_COLOR,
153 	ACTION_CHANGE_SCORE,
154 	ACTION_SET_SCORE,
155 	ACTION_SET_TAG,
156 	ACTION_UNSET_TAG,
157 	ACTION_CLEAR_TAGS,
158 	ACTION_HIDE,
159 	ACTION_IGNORE,
160 	ACTION_WATCH,
161 	ACTION_ADD_TO_ADDRESSBOOK,
162 	ACTION_STOP,
163 	/* add other action constants */
164 } Action;
165 
166 static struct {
167 	gchar *menu;
168 	gchar *text;
169 	Action action;
170 } action_menu [] = {
171 	{ NULL, 		N_("Move"),			ACTION_MOVE },
172 	{ NULL, 		N_("Copy"),			ACTION_COPY },
173 	{ NULL, 		N_("Delete"),			ACTION_DELETE },
174 	{ NULL, 		N_("Hide"),		        ACTION_HIDE },
175 	{ N_("Message flags"), 	N_("Mark"),			ACTION_MARK },
176 	{ N_("Message flags"), 	N_("Unmark"),			ACTION_UNMARK },
177 	{ N_("Message flags"), 	N_("Lock"),			ACTION_LOCK },
178 	{ N_("Message flags"), 	N_("Unlock"),			ACTION_UNLOCK },
179 	{ N_("Message flags"), 	N_("Mark as read"),		ACTION_MARK_AS_READ },
180 	{ N_("Message flags"), 	N_("Mark as unread"),		ACTION_MARK_AS_UNREAD },
181 	{ N_("Message flags"), 	N_("Mark as spam"),		ACTION_MARK_AS_SPAM },
182 	{ N_("Message flags"), 	N_("Mark as ham"),		ACTION_MARK_AS_HAM },
183 	{ NULL, 		N_("Execute"),			ACTION_EXECUTE },
184 	{ NULL, 		N_("Color label"),		ACTION_COLOR },
185 	{ N_("Resend"), 	N_("Forward"),			ACTION_FORWARD },
186 	{ N_("Resend"), 	N_("Forward as attachment"),	ACTION_FORWARD_AS_ATTACHMENT },
187 	{ N_("Resend"), 	N_("Redirect"),			ACTION_REDIRECT },
188 	{ N_("Score"), 		N_("Change score"),		ACTION_CHANGE_SCORE },
189 	{ N_("Score"), 		N_("Set score"),		ACTION_SET_SCORE },
190 	{ N_("Tags"), 		N_("Apply tag"),		ACTION_SET_TAG },
191 	{ N_("Tags"), 		N_("Unset tag"),		ACTION_UNSET_TAG },
192 	{ N_("Tags"), 		N_("Clear tags"),		ACTION_CLEAR_TAGS },
193 	{ N_("Threads"), 	N_("Ignore thread"),	        ACTION_IGNORE },
194 	{ N_("Threads"), 	N_("Watch thread"),	        ACTION_WATCH },
195 	{ NULL, 		N_("Add to address book"),	ACTION_ADD_TO_ADDRESSBOOK },
196 	{ NULL, 		N_("Stop filter"),		ACTION_STOP },
197 	{ NULL,			NULL,				0},
198 };
199 
200 enum {
201 	ACTION_COMBO_TEXT,
202 	ACTION_COMBO_DATA,
203 	ACTION_COMBO_SENS,
204 	N_ACTION_COMBO
205 };
206 
207 /*!
208  *\brief	Hooks
209  */
210 static PrefsFilteringActionSignal *filtering_action_callback;
211 
prefs_filtering_action_create_model(void)212 static GtkTreeModel *prefs_filtering_action_create_model(void)
213 {
214 	GtkTreeIter iter, iter2;
215 	GtkTreeStore *store;
216 	gchar *curr_menu = NULL;
217 	gint i;
218 
219 	store = gtk_tree_store_new(N_ACTION_COMBO, G_TYPE_STRING, G_TYPE_INT,
220 				   G_TYPE_BOOLEAN);
221 
222 	for (i = 0; action_menu[i].menu || action_menu[i].text; i++)
223 	{
224 		if (action_menu[i].menu) {
225 			if (!curr_menu || strcmp(action_menu[i].menu, curr_menu)) {
226 				gtk_tree_store_append(store, &iter, NULL);
227 				gtk_tree_store_set(store, &iter,
228 						   ACTION_COMBO_TEXT,
229 						   gettext(action_menu[i].menu),
230 						   ACTION_COMBO_SENS, TRUE,
231 						   -1);
232 				curr_menu = action_menu[i].menu;
233 			}
234 
235 			gtk_tree_store_append(store, &iter2, &iter);
236 			gtk_tree_store_set(store, &iter2,
237 					   ACTION_COMBO_TEXT, gettext(action_menu[i].text),
238 					   ACTION_COMBO_DATA, action_menu[i].action,
239 					   ACTION_COMBO_SENS, TRUE,
240 					   -1);
241 		} else {
242 			curr_menu = NULL;
243 
244 			gtk_tree_store_append(store, &iter, NULL);
245 			gtk_tree_store_set(store, &iter,
246 					   ACTION_COMBO_TEXT, gettext(action_menu[i].text),
247 					   ACTION_COMBO_DATA, action_menu[i].action,
248 					   ACTION_COMBO_SENS, TRUE,
249 					   -1);
250 		}
251 	}
252 
253 	return GTK_TREE_MODEL(store);
254 }
255 
cell_is_sensitive(GtkCellLayout * cell_layout,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer data)256 static void cell_is_sensitive(GtkCellLayout   *cell_layout,
257 			      GtkCellRenderer *cell,
258 			      GtkTreeModel    *tree_model,
259 			      GtkTreeIter     *iter,
260 			      gpointer         data)
261 {
262 	if(gtk_tree_model_iter_has_child (tree_model, iter)) {
263 		g_object_set (cell, "sensitive", FALSE, NULL);
264 	}
265 }
266 
267 /*!
268  *\brief	Opens the filtering action dialog with a list of actions
269  *
270  *\param	matchers List of conditions
271  *\param	cb Callback
272  *
273  */
prefs_filtering_action_open(GSList * action_list,PrefsFilteringActionSignal * cb)274 void prefs_filtering_action_open(GSList *action_list,
275     PrefsFilteringActionSignal *cb)
276 {
277 	inc_lock();
278 
279 	if (!filtering_action.window) {
280 		prefs_filtering_action_create();
281 	} else {
282 		/* update color label menu */
283 		gtk_cmoption_menu_set_menu(GTK_CMOPTION_MENU(filtering_action.color_optmenu),
284 				colorlabel_create_color_menu());
285 	}
286 
287 	manage_window_set_transient(GTK_WINDOW(filtering_action.window));
288 	gtk_widget_grab_focus(filtering_action.ok_btn);
289 
290 	filtering_action_callback = cb;
291 
292 	prefs_filtering_action_set_dialog(action_list);
293 
294 	gtk_widget_show(filtering_action.window);
295 	gtk_window_set_modal(GTK_WINDOW(filtering_action.window), TRUE);
296 }
297 
298 /*!
299  *\brief	Save Gtk object size to prefs dataset
300  */
prefs_filtering_action_size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation)301 static void prefs_filtering_action_size_allocate_cb(GtkWidget *widget,
302 					 GtkAllocation *allocation)
303 {
304 	cm_return_if_fail(allocation != NULL);
305 
306 	prefs_common.filteringactionwin_width = allocation->width;
307 	prefs_common.filteringactionwin_height = allocation->height;
308 }
309 
310 #define LABELS_WIDTH		80
311 #define SECOND_ROW_WIDTH	250
312 
prefs_filtering_action_check_widget_width(GtkWidget * widget)313 static void prefs_filtering_action_check_widget_width(GtkWidget *widget)
314 {
315 	GtkRequisition req;
316 
317 	gtk_widget_size_request(widget, &req);
318 	if(req.width > SECOND_ROW_WIDTH)
319 		gtk_widget_set_size_request(widget, SECOND_ROW_WIDTH, -1);
320 }
321 
322 /*!
323  *\brief	Create the matcher dialog
324  */
prefs_filtering_action_create(void)325 static void prefs_filtering_action_create(void)
326 {
327 	GtkWidget *window;
328 	GtkWidget *vbox;
329 	GtkWidget *ok_btn;
330 	GtkWidget *cancel_btn;
331 	GtkWidget *confirm_area;
332 
333 	GtkWidget *vbox1;
334 	GtkWidget *frame;
335 	GtkWidget *table;
336 	GtkWidget *hbox1;
337 
338 	GtkWidget *label1;
339 	GtkWidget *label2;
340 	GtkWidget *label3;
341 	GtkWidget *action_combo;
342 	GtkWidget *account_combo;
343 	GtkWidget *header_combo;
344 	GtkWidget *header_entry;
345 #ifndef USE_ALT_ADDRBOOK
346 	GtkWidget *addressbook_btn;
347 #endif
348 	GtkWidget *dest_entry;
349 	GtkWidget *dest_btn;
350 	GtkWidget *score_entry;
351 	GtkWidget *tags_combo;
352 
353 	GtkWidget *reg_hbox;
354 	GtkWidget *btn_hbox;
355 	GtkWidget *arrow;
356 	GtkWidget *reg_btn;
357 	GtkWidget *subst_btn;
358 	GtkWidget *del_btn;
359 
360 	GtkWidget *action_hbox;
361 	GtkWidget *action_scrolledwin;
362 	GtkWidget *action_list_view;
363 
364 	GtkWidget *btn_vbox;
365 	GtkWidget *up_btn;
366 	GtkWidget *down_btn;
367 
368 	GtkWidget *exec_btn;
369 
370 	GtkWidget *color_optmenu;
371 
372 	static GdkGeometry geometry;
373 
374         GList * accounts;
375 	GSList *tmp, *tags;
376 
377 	GtkSizeGroup *size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
378 	GtkSizeGroup *size_action = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
379 	GtkTreeModel *model;
380 	GtkCellRenderer *renderer;
381 
382 	debug_print("Creating matcher configuration window...\n");
383 
384 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_filtering_action");
385 	gtk_container_set_border_width(GTK_CONTAINER(window), VBOX_BORDER);
386 	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
387 	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
388 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
389 
390 	vbox = gtk_vbox_new(FALSE, 6);
391 	gtk_container_add(GTK_CONTAINER(window), vbox);
392 
393 	gtkut_stock_button_set_create(&confirm_area,
394 				      &cancel_btn, GTK_STOCK_CANCEL,
395 				      &ok_btn, GTK_STOCK_OK,
396 				      NULL, NULL);
397 	gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
398 	gtk_widget_grab_default(ok_btn);
399 
400 	gtk_window_set_title(GTK_WINDOW(window),
401 			     _("Action configuration"));
402 	g_signal_connect(G_OBJECT(window), "delete_event",
403 			 G_CALLBACK(prefs_filtering_action_deleted), NULL);
404 	g_signal_connect(G_OBJECT(window), "size_allocate",
405 			 G_CALLBACK(prefs_filtering_action_size_allocate_cb), NULL);
406 	g_signal_connect(G_OBJECT(window), "key_press_event",
407 			 G_CALLBACK(prefs_filtering_action_key_pressed), NULL);
408 	MANAGE_WINDOW_SIGNALS_CONNECT(window);
409 	g_signal_connect(G_OBJECT(ok_btn), "clicked",
410 			 G_CALLBACK(prefs_filtering_action_ok), NULL);
411 	g_signal_connect(G_OBJECT(cancel_btn), "clicked",
412 			 G_CALLBACK(prefs_filtering_action_cancel), NULL);
413 
414 	vbox1 = gtk_vbox_new(FALSE, VSPACING);
415 	gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
416 	gtk_container_set_border_width(GTK_CONTAINER (vbox1), 2);
417 
418 	frame = gtk_frame_new(_("Rule"));
419 	gtk_frame_set_label_align(GTK_FRAME(frame), 0.01, 0.5);
420 	gtk_box_pack_start (GTK_BOX (vbox1), frame, FALSE, FALSE, 0);
421 	gtk_widget_set_size_request(frame, -1, 110);
422 
423 	table = gtk_table_new(3, 3, FALSE);
424 	gtk_container_set_border_width(GTK_CONTAINER(table), 2);
425 	gtk_table_set_row_spacings (GTK_TABLE (table), VSPACING_NARROW_2);
426 	gtk_table_set_col_spacings (GTK_TABLE (table), HSPACING_NARROW);
427         gtk_container_add(GTK_CONTAINER(frame), table);
428 
429         /* first row labels */
430 
431 	label1 = gtk_label_new (_("Action"));
432 	gtk_widget_set_size_request(label1, LABELS_WIDTH, -1);
433 	gtk_size_group_add_widget(size_group, label1);
434 	gtk_misc_set_alignment (GTK_MISC (label1), 1, 0.5);
435 	gtk_table_attach(GTK_TABLE(table), label1, 0, 1, 0, 1,
436 			GTK_FILL, GTK_SHRINK, 0, 0);
437 
438 	label2 = gtk_label_new ("");
439 	gtk_size_group_add_widget(size_group, label2);
440 	gtk_misc_set_alignment (GTK_MISC (label2), 1, 0.5);
441 	gtk_table_attach(GTK_TABLE(table), label2, 0, 1, 1, 2,
442 			GTK_FILL, GTK_SHRINK, 0, 0);
443 
444 	label3 = gtk_label_new ("");
445 	gtk_size_group_add_widget(size_group, label3);
446 	gtk_misc_set_alignment (GTK_MISC (label3), 1, 0.5);
447 	gtk_table_attach(GTK_TABLE(table), label3, 0, 1, 2, 3,
448 			GTK_FILL, GTK_SHRINK, 0, 0);
449 
450 	/* action combo */
451 
452 	hbox1 = gtk_hbox_new(FALSE, 0);
453 	gtk_table_attach(GTK_TABLE(table), hbox1, 1, 2, 0, 1,
454 			GTK_FILL, GTK_SHRINK, 0, 0);
455 
456 	model = prefs_filtering_action_create_model();
457 	action_combo = gtk_combo_box_new_with_model(model);
458 	gtk_size_group_add_widget(size_action, action_combo);
459 
460 	renderer = gtk_cell_renderer_text_new();
461 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(action_combo), renderer, TRUE);
462         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(action_combo), renderer,
463 				       "text", ACTION_COMBO_TEXT,
464 				       "sensitive", ACTION_COMBO_SENS,
465 				       NULL);
466 	gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(action_combo), renderer,
467 					   cell_is_sensitive, NULL, NULL);
468 
469 	combobox_select_by_data(GTK_COMBO_BOX(action_combo), ACTION_MOVE);
470 	g_signal_connect(G_OBJECT(action_combo), "changed",
471 			 G_CALLBACK(prefs_filtering_action_type_selection_changed),
472 			 NULL);
473 	gtk_box_pack_start(GTK_BOX(hbox1), action_combo, FALSE, FALSE, 0);
474 	gtk_box_pack_start(GTK_BOX(hbox1), gtk_label_new(""), TRUE, TRUE, 0);
475 
476 	/* accounts */
477 
478 	hbox1 = gtk_hbox_new (FALSE, 0);
479 	gtk_table_attach(GTK_TABLE(table), hbox1, 1, 2, 1, 2,
480 			 GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
481 
482 	account_combo = gtk_combo_box_text_new ();
483 	gtk_size_group_add_widget(size_action, account_combo);
484 
485 	for (accounts = account_get_list() ; accounts != NULL;
486 	     accounts = accounts->next) {
487 		PrefsAccount *ac = (PrefsAccount *)accounts->data;
488 		gchar *name = g_strdup(ac->account_name);
489 		gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(account_combo), (gpointer) name);
490 		g_free(name);
491 	}
492 
493 	prefs_filtering_action_check_widget_width(account_combo);
494 	gtk_combo_box_set_active(GTK_COMBO_BOX(account_combo), 0);
495 	gtk_box_pack_start (GTK_BOX (hbox1), account_combo,
496 			    FALSE, FALSE, 0);
497 
498 	/* header */
499 
500 	header_combo = combobox_text_new(TRUE, "From", "To", "Cc", "Reply-To", "Sender", NULL);
501 	gtk_size_group_add_widget(size_action, header_combo);
502 	gtk_box_pack_start (GTK_BOX (hbox1), header_combo,
503 			    FALSE, FALSE, 0);
504 	header_entry = gtk_bin_get_child(GTK_BIN((header_combo)));
505 
506 	/* destination */
507 
508 	hbox1 = gtk_hbox_new (FALSE, 0);
509 	gtk_table_attach(GTK_TABLE(table), hbox1, 1, 2, 2, 3,
510 			GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
511 
512 	dest_entry = gtk_entry_new ();
513 	gtk_box_pack_start (GTK_BOX (hbox1), dest_entry, TRUE, TRUE, 0);
514 
515 	score_entry = gtk_spin_button_new_with_range(-1000, 1000, 1);
516 	gtk_box_pack_start(GTK_BOX(hbox1), score_entry, FALSE, FALSE, 0);
517 
518 	color_optmenu = gtk_cmoption_menu_new();
519 	gtk_size_group_add_widget(size_action, color_optmenu);
520 	gtk_cmoption_menu_set_menu(GTK_CMOPTION_MENU(color_optmenu),
521 				 colorlabel_create_color_menu());
522 	prefs_filtering_action_check_widget_width(color_optmenu);
523 	gtk_box_pack_start(GTK_BOX(hbox1), color_optmenu, FALSE, FALSE, 0);
524 
525 	tags_combo = gtk_combo_box_text_new ();
526 	gtk_size_group_add_widget(size_action, tags_combo);
527 
528 	for (tmp = tags = tags_get_list() ; tmp != NULL;
529 	     tmp = tmp->next) {
530 		gchar *name = g_strdup(tags_get_tag(GPOINTER_TO_INT(tmp->data)));
531 
532 		gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(tags_combo), (gpointer) name);
533 		g_free(name);
534 	}
535 	g_slist_free(tags);
536 
537 	prefs_filtering_action_check_widget_width(tags_combo);
538 	gtk_combo_box_set_active(GTK_COMBO_BOX(tags_combo), 0);
539 	gtk_box_pack_start (GTK_BOX (hbox1), tags_combo,
540 			    FALSE, FALSE, 0);
541 
542 	hbox1 = gtk_hbox_new (FALSE, 0);
543 	gtk_table_attach(GTK_TABLE(table), hbox1, 2, 3, 2, 3,
544 			GTK_FILL, GTK_SHRINK, 0, 0);
545 
546 	dest_btn = gtk_button_new_with_label (_("Select..."));
547 	gtk_box_pack_start (GTK_BOX (hbox1), dest_btn, FALSE, FALSE, 0);
548 	g_signal_connect (G_OBJECT (dest_btn), "clicked",
549 			  G_CALLBACK(prefs_filtering_action_select_dest),
550 			  NULL);
551 
552 #ifndef USE_ALT_ADDRBOOK
553 	addressbook_btn = gtk_button_new_with_label (_("Select..."));
554 	gtk_box_pack_start (GTK_BOX (hbox1), addressbook_btn, FALSE, FALSE, 0);
555 	g_signal_connect (G_OBJECT (addressbook_btn), "clicked",
556 			  G_CALLBACK(prefs_filtering_action_select_addressbook),
557 			  NULL);
558 #endif
559 
560 	exec_btn = gtk_button_new_from_stock(GTK_STOCK_INFO);
561 	gtk_box_pack_start (GTK_BOX (hbox1), exec_btn, FALSE, FALSE, 0);
562 	g_signal_connect (G_OBJECT (exec_btn), "clicked",
563 			  G_CALLBACK(prefs_filtering_action_exec_info),
564 			  window);
565 
566 	/* register / substitute / delete */
567 
568 	reg_hbox = gtk_hbox_new(FALSE, 4);
569 	gtk_box_pack_start(GTK_BOX(vbox1), reg_hbox, FALSE, FALSE, 0);
570 
571 	arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
572 	gtk_box_pack_start(GTK_BOX(reg_hbox), arrow, FALSE, FALSE, 0);
573 	gtk_widget_set_size_request(arrow, -1, 16);
574 
575 	btn_hbox = gtk_hbox_new(TRUE, 4);
576 	gtk_box_pack_start(GTK_BOX(reg_hbox), btn_hbox, FALSE, FALSE, 0);
577 
578 	reg_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
579 	gtk_box_pack_start(GTK_BOX(btn_hbox), reg_btn, FALSE, TRUE, 0);
580 	g_signal_connect(G_OBJECT(reg_btn), "clicked",
581 			 G_CALLBACK(prefs_filtering_action_register_cb), NULL);
582 
583 	subst_btn = gtkut_get_replace_btn(_("_Replace"));
584 	gtk_box_pack_start(GTK_BOX(btn_hbox), subst_btn, FALSE, TRUE, 0);
585 	g_signal_connect(G_OBJECT(subst_btn), "clicked",
586 			 G_CALLBACK(prefs_filtering_action_substitute_cb),
587 			 NULL);
588 
589 	del_btn = gtk_button_new_with_mnemonic (_("D_elete"));
590 	gtk_button_set_image(GTK_BUTTON(del_btn),
591 			gtk_image_new_from_stock(GTK_STOCK_REMOVE,GTK_ICON_SIZE_BUTTON));
592 	gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0);
593 	g_signal_connect(G_OBJECT(del_btn), "clicked",
594 			 G_CALLBACK(prefs_filtering_action_delete_cb), NULL);
595 
596 	action_hbox = gtk_hbox_new(FALSE, 8);
597 	gtk_box_pack_start(GTK_BOX(vbox1), action_hbox, TRUE, TRUE, 0);
598 
599 	action_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
600 	gtk_widget_set_size_request(action_scrolledwin, -1, 150);
601 	gtk_box_pack_start(GTK_BOX(action_hbox), action_scrolledwin,
602 			   TRUE, TRUE, 0);
603 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(action_scrolledwin),
604 					    GTK_SHADOW_ETCHED_IN);
605 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(action_scrolledwin),
606 				       GTK_POLICY_AUTOMATIC,
607 				       GTK_POLICY_AUTOMATIC);
608 
609 	action_list_view = prefs_filtering_action_list_view_create();
610 	gtk_container_add(GTK_CONTAINER(action_scrolledwin), action_list_view);
611 
612 	btn_vbox = gtk_vbox_new(FALSE, 8);
613 	gtk_box_pack_start(GTK_BOX(action_hbox), btn_vbox, FALSE, FALSE, 0);
614 
615 	up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
616 	gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0);
617 	g_signal_connect(G_OBJECT(up_btn), "clicked",
618 			 G_CALLBACK(prefs_filtering_action_up), NULL);
619 
620 	down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
621 	gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0);
622 	g_signal_connect(G_OBJECT(down_btn), "clicked",
623 			 G_CALLBACK(prefs_filtering_action_down), NULL);
624 
625 	if (!geometry.min_height) {
626 		geometry.min_width = 490;
627 		geometry.min_height = 348;
628 	}
629 
630 	gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
631 				      GDK_HINT_MIN_SIZE);
632 	gtk_widget_set_size_request(window, prefs_common.filteringactionwin_width,
633 				    prefs_common.filteringactionwin_height);
634 
635 	g_object_unref(G_OBJECT(size_group));
636 	g_object_unref(G_OBJECT(size_action));
637 
638 	gtk_widget_show_all(window);
639 
640 	filtering_action.window    = window;
641 	filtering_action.label1 = label1;
642 	filtering_action.label2 = label2;
643 	filtering_action.label3 = label3;
644 	filtering_action.action_combo = action_combo;
645 	filtering_action.account_combo = account_combo;
646 	filtering_action.tags_combo = tags_combo;
647 	filtering_action.dest_entry = dest_entry;
648 	filtering_action.dest_btn = dest_btn;
649 	filtering_action.exec_btn = exec_btn;
650 	filtering_action.color_optmenu = color_optmenu;
651 	filtering_action.score_entry = score_entry;
652 	filtering_action.header_combo = header_combo;
653 	filtering_action.header_entry = header_entry;
654 #ifndef USE_ALT_ADDRBOOK
655 	filtering_action.addressbook_btn = addressbook_btn;
656 #endif
657 	filtering_action.ok_btn = ok_btn;
658 	filtering_action.action_list_view = action_list_view;
659 
660 	prefs_filtering_action_type_selection_changed(NULL, NULL);
661 }
662 
663 /*!
664  *\brief	Set the contents of a row
665  *
666  *\param	row Index of row to set
667  *\param	prop Condition to set
668  *
669  */
prefs_filtering_action_list_view_set_row(GtkTreeIter * row,FilteringAction * action)670 static void prefs_filtering_action_list_view_set_row(GtkTreeIter *row,
671 						     FilteringAction *action)
672 {
673         gchar *buf;
674 
675 	if (row == NULL && action == NULL) {
676 		prefs_filtering_action_list_view_insert_action
677 			(filtering_action.action_list_view,
678 			 NULL, _("(New)"), FALSE);
679 		return;
680 	}
681 
682         buf = filteringaction_to_string(action);
683 
684 	prefs_filtering_action_list_view_insert_action
685 			(filtering_action.action_list_view,
686 			 row, buf, TRUE);
687 	g_free(buf);
688 }
689 
690 /*!
691  *\brief	Initializes dialog with a set of conditions
692  *
693  *\param	matchers List of conditions
694  */
prefs_filtering_action_set_dialog(GSList * action_list)695 static void prefs_filtering_action_set_dialog(GSList *action_list)
696 {
697 	GSList *cur;
698 
699 	gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model
700 			(GTK_TREE_VIEW(filtering_action.action_list_view))));
701 
702 	prefs_filtering_action_list_view_set_row(NULL, NULL);
703 	if (action_list != NULL) {
704 		for (cur = action_list; cur != NULL;
705 		     cur = g_slist_next(cur)) {
706 			FilteringAction *action;
707 			action = (FilteringAction *) cur->data;
708 			prefs_filtering_action_list_view_set_row(NULL, action);
709 		}
710 	}
711 
712         prefs_filtering_action_reset_dialog();
713 
714         combobox_set_sensitive(GTK_COMBO_BOX(filtering_action.action_combo), 9,
715         		(tags_get_size() > 0) ? TRUE : FALSE);
716 }
717 
718 /*!
719  *\brief	Converts current actions in list box in
720  *		an action list used by the filtering system.
721  *
722  *\return	GSList * List of actions.
723  */
prefs_filtering_action_get_list(void)724 static GSList *prefs_filtering_action_get_list(void)
725 {
726 	gchar *action_str;
727 	gboolean is_valid;
728 	gint row = 1;
729 	GSList *action_list;
730 	GtkTreeView *list_view = GTK_TREE_VIEW(filtering_action.action_list_view);
731 	GtkTreeModel *model = gtk_tree_view_get_model(list_view);
732 	GtkTreeIter iter;
733 
734 	action_list = NULL;
735 
736 	while (gtk_tree_model_iter_nth_child(model, &iter, NULL, row)) {
737 
738 		gtk_tree_model_get(model, &iter,
739 			           PFA_ACTION, &action_str,
740 				   PFA_VALID_ACTION, &is_valid,
741 				   -1);
742 
743 		if (is_valid) {
744                         GSList * tmp_action_list;
745 			tmp_action_list = matcher_parser_get_action_list(action_str);
746 
747 			if (tmp_action_list == NULL) {
748 				g_free(action_str);
749 				break;
750 			}
751 
752 			action_list = g_slist_concat(action_list,
753                             tmp_action_list);
754 		}
755 
756 		g_free(action_str);
757 		action_str = NULL;
758 		row ++;
759 
760 	}
761 
762 	return action_list;
763 }
764 
765 /*!
766  *\brief	Returns account ID from the given list index
767  *
768  *\return	gint account ID
769  */
get_account_id_from_list_id(gint list_id)770 static gint get_account_id_from_list_id(gint list_id)
771 {
772 	GList * accounts;
773 
774 	for (accounts = account_get_list() ; accounts != NULL;
775 	     accounts = accounts->next) {
776 		PrefsAccount *ac = (PrefsAccount *)accounts->data;
777 
778 		if (list_id == 0)
779 			return ac->account_id;
780 		list_id--;
781 	}
782 	return 0;
783 }
784 
785 /*!
786  *\brief	Returns list index from the given account ID
787  *
788  *\return	gint list index
789  */
get_list_id_from_account_id(gint account_id)790 static gint get_list_id_from_account_id(gint account_id)
791 {
792 	GList * accounts;
793 	gint list_id = 0;
794 
795 	for (accounts = account_get_list() ; accounts != NULL;
796 	     accounts = accounts->next) {
797 		PrefsAccount *ac = (PrefsAccount *)accounts->data;
798 
799 		if (account_id == ac->account_id)
800 			return list_id;
801 		list_id++;
802 	}
803 	return 0;
804 }
805 
806 
807 /*!
808  *\brief	Returns parser action ID from internal action ID
809  *
810  *\return	gint parser action ID
811  */
prefs_filtering_action_get_matching_from_action(Action action_id)812 static gint prefs_filtering_action_get_matching_from_action(Action action_id)
813 {
814 	switch (action_id) {
815 	case ACTION_MOVE:
816 		return MATCHACTION_MOVE;
817 	case ACTION_SET_TAG:
818 		return MATCHACTION_SET_TAG;
819 	case ACTION_UNSET_TAG:
820 		return MATCHACTION_UNSET_TAG;
821 	case ACTION_CLEAR_TAGS:
822 		return MATCHACTION_CLEAR_TAGS;
823 	case ACTION_COPY:
824 		return MATCHACTION_COPY;
825 	case ACTION_DELETE:
826 		return MATCHACTION_DELETE;
827 	case ACTION_MARK:
828 		return MATCHACTION_MARK;
829 	case ACTION_UNMARK:
830 		return MATCHACTION_UNMARK;
831 	case ACTION_LOCK:
832 		return MATCHACTION_LOCK;
833 	case ACTION_UNLOCK:
834 		return MATCHACTION_UNLOCK;
835 	case ACTION_MARK_AS_READ:
836 		return MATCHACTION_MARK_AS_READ;
837 	case ACTION_MARK_AS_UNREAD:
838 		return MATCHACTION_MARK_AS_UNREAD;
839 	case ACTION_MARK_AS_SPAM:
840 		return MATCHACTION_MARK_AS_SPAM;
841 	case ACTION_MARK_AS_HAM:
842 		return MATCHACTION_MARK_AS_HAM;
843 	case ACTION_FORWARD:
844 		return MATCHACTION_FORWARD;
845 	case ACTION_FORWARD_AS_ATTACHMENT:
846 		return MATCHACTION_FORWARD_AS_ATTACHMENT;
847 	case ACTION_REDIRECT:
848 		return MATCHACTION_REDIRECT;
849 	case ACTION_EXECUTE:
850 		return MATCHACTION_EXECUTE;
851 	case ACTION_COLOR:
852 		return MATCHACTION_COLOR;
853 	case ACTION_HIDE:
854 		return MATCHACTION_HIDE;
855 	case ACTION_IGNORE:
856 		return MATCHACTION_IGNORE;
857 	case ACTION_WATCH:
858 		return MATCHACTION_WATCH;
859 	case ACTION_STOP:
860 		return MATCHACTION_STOP;
861 	case ACTION_CHANGE_SCORE:
862 		return MATCHACTION_CHANGE_SCORE;
863 	case ACTION_SET_SCORE:
864 		return MATCHACTION_SET_SCORE;
865 	case ACTION_ADD_TO_ADDRESSBOOK:
866 		return MATCHACTION_ADD_TO_ADDRESSBOOK;
867 	default:
868 		return -1;
869 	}
870 }
871 
872 /*!
873  *\brief	Returns action from the content of the dialog
874  *
875  *\param	alert specifies whether alert dialog boxes should be shown
876  *                or not.
877  *
878  *\return	FilteringAction * action entered in the dialog box.
879  */
prefs_filtering_action_dialog_to_action(gboolean alert)880 static FilteringAction * prefs_filtering_action_dialog_to_action(gboolean alert)
881 {
882 	Action action_id;
883 	gint action_type;
884 	gint list_id;
885 	gint account_id;
886 	gchar * destination = NULL;
887 	gint labelcolor = 0;
888         FilteringAction * action;
889         gint score;
890 	gchar * header = NULL;
891 
892 	action_id = combobox_get_active_data(GTK_COMBO_BOX(filtering_action.action_combo));
893 	action_type = prefs_filtering_action_get_matching_from_action(action_id);
894 	list_id = gtk_combo_box_get_active(GTK_COMBO_BOX(filtering_action.account_combo));
895 	account_id = get_account_id_from_list_id(list_id);
896         score = 0;
897         destination = NULL;
898 
899 	switch (action_id) {
900 	case ACTION_MOVE:
901 	case ACTION_COPY:
902 	case ACTION_EXECUTE:
903 		destination = gtk_editable_get_chars(GTK_EDITABLE(filtering_action.dest_entry), 0, -1);
904 		if (*destination == '\0') {
905 			if (alert)
906                                 alertpanel_error(action_id == ACTION_EXECUTE
907 						 ? _("Command-line not set")
908 						 : _("Destination is not set."));
909 			g_free(destination);
910 			return NULL;
911 		}
912 		break;
913 	case ACTION_FORWARD:
914 	case ACTION_FORWARD_AS_ATTACHMENT:
915 	case ACTION_REDIRECT:
916 		destination = gtk_editable_get_chars(GTK_EDITABLE(filtering_action.dest_entry), 0, -1);
917 		if (*destination == '\0') {
918 			if (alert)
919                                 alertpanel_error(_("Recipient is not set."));
920 			g_free(destination);
921 			return NULL;
922 		}
923 		break;
924 	case ACTION_COLOR:
925 		labelcolor = colorlabel_get_color_menu_active_item(
926 			gtk_cmoption_menu_get_menu(GTK_CMOPTION_MENU(filtering_action.color_optmenu)));
927 		destination = NULL;
928 		break;
929         case ACTION_CHANGE_SCORE:
930         case ACTION_SET_SCORE:
931         	score = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(
932 						filtering_action.score_entry));
933 		if (!score && action_id == ACTION_CHANGE_SCORE) {
934 			if (alert)
935                                 alertpanel_error(_("Score is not set"));
936 			return NULL;
937 		}
938                 break;
939 	case ACTION_ADD_TO_ADDRESSBOOK:
940 		header = gtk_editable_get_chars(GTK_EDITABLE(filtering_action.header_entry), 0, -1);
941 		if (*header == '\0') {
942 			if (alert)
943                                 alertpanel_error(_("Header is not set."));
944 			g_free(header);
945 			return NULL;
946 		}
947 		destination = gtk_editable_get_chars(GTK_EDITABLE(filtering_action.dest_entry), 0, -1);
948 		if (*destination == '\0') {
949 			if (alert)
950                                 alertpanel_error(_("Target addressbook/folder is not set."));
951 			g_free(destination);
952 			return NULL;
953 		}
954 		break;
955 	case ACTION_SET_TAG:
956 	case ACTION_UNSET_TAG:
957 		destination = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(filtering_action.tags_combo));
958 		if (!destination || *destination == '\0') {
959 			if (alert)
960                                 alertpanel_error(_("Tag name is empty."));
961 			g_free(destination);
962 			return NULL;
963 		}
964 		break;
965 	case ACTION_STOP:
966 	case ACTION_HIDE:
967 	case ACTION_IGNORE:
968 	case ACTION_WATCH:
969         case ACTION_DELETE:
970         case ACTION_MARK:
971         case ACTION_UNMARK:
972         case ACTION_LOCK:
973         case ACTION_UNLOCK:
974         case ACTION_MARK_AS_READ:
975         case ACTION_MARK_AS_UNREAD:
976         case ACTION_MARK_AS_SPAM:
977         case ACTION_MARK_AS_HAM:
978         case ACTION_CLEAR_TAGS:
979 	default:
980 		break;
981 	}
982 	action = filteringaction_new(action_type, account_id,
983             destination, labelcolor, score, header);
984 
985 	g_free(destination);
986 	return action;
987 }
988 
989 /*!
990  *\brief	Signal handler for register button
991  */
prefs_filtering_action_register_cb(void)992 static void prefs_filtering_action_register_cb(void)
993 {
994 	FilteringAction *action;
995 
996 	action = prefs_filtering_action_dialog_to_action(TRUE);
997 	if (action == NULL)
998 		return;
999 
1000 	prefs_filtering_action_list_view_set_row(NULL, action);
1001 
1002 	filteringaction_free(action);
1003 	/* GTK 1 NOTE:
1004 	 * (presumably gtk_list_select_item(), called by
1005 	 * prefs_filtering_action_reset_dialog() activates
1006 	 * what seems to be a bug. this causes any other
1007 	 * list items to be unselectable)
1008 	 * prefs_filtering_action_reset_dialog(); */
1009 	gtk_combo_box_set_active(GTK_COMBO_BOX(filtering_action.account_combo), 0);
1010 	gtk_combo_box_set_active(GTK_COMBO_BOX(filtering_action.tags_combo), 0);
1011 	gtk_entry_set_text(GTK_ENTRY(filtering_action.dest_entry), "");
1012 }
1013 
1014 /*!
1015  *\brief	Signal handler for substitute button
1016  */
prefs_filtering_action_substitute_cb(void)1017 static void prefs_filtering_action_substitute_cb(void)
1018 {
1019 	GtkTreeView *list_view = GTK_TREE_VIEW
1020 			(filtering_action.action_list_view);
1021 	GtkTreeSelection *selection = gtk_tree_view_get_selection(list_view);
1022 	GtkTreeModel *model;
1023 	gboolean is_valid;
1024 	GtkTreeIter row;
1025 	FilteringAction *action;
1026 
1027 	if (!gtk_tree_selection_get_selected(selection, &model, &row))
1028 		return;
1029 
1030 	gtk_tree_model_get(model, &row, PFA_VALID_ACTION, &is_valid, -1);
1031 	if (!is_valid)
1032 		return;
1033 
1034 	action = prefs_filtering_action_dialog_to_action(TRUE);
1035 	if (action == NULL)
1036 		return;
1037 
1038 	prefs_filtering_action_list_view_set_row(&row, action);
1039 
1040 	filteringaction_free(action);
1041 
1042 	prefs_filtering_action_reset_dialog();
1043 }
1044 
1045 /*!
1046  *\brief	Signal handler for delete button
1047  */
prefs_filtering_action_delete_cb(void)1048 static void prefs_filtering_action_delete_cb(void)
1049 {
1050 	GtkTreeView *list_view = GTK_TREE_VIEW
1051 			(filtering_action.action_list_view);
1052 	GtkTreeSelection *selection = gtk_tree_view_get_selection(list_view);
1053 	GtkTreeModel *model;
1054 	gboolean is_valid;
1055 	GtkTreeIter row;
1056 
1057 	if (!gtk_tree_selection_get_selected(selection, &model, &row))
1058 		return;
1059 
1060 	gtk_tree_model_get(model, &row, PFA_VALID_ACTION, &is_valid, -1);
1061 	if (!is_valid)
1062 		return;
1063 
1064 	gtk_list_store_remove(GTK_LIST_STORE(model), &row);
1065 
1066 	prefs_filtering_action_reset_dialog();
1067 }
1068 
1069 /*!
1070  *\brief	Signal handler for 'move up' button
1071  */
prefs_filtering_action_up(void)1072 static void prefs_filtering_action_up(void)
1073 {
1074 	GtkTreePath *prev, *sel, *try;
1075 	GtkTreeIter isel;
1076 	GtkListStore *store = NULL;
1077 	GtkTreeModel *model = NULL;
1078 	GtkTreeIter iprev;
1079 
1080 	if (!gtk_tree_selection_get_selected
1081 		(gtk_tree_view_get_selection
1082 			(GTK_TREE_VIEW(filtering_action.action_list_view)),
1083 		 &model,
1084 		 &isel))
1085 		return;
1086 	store = (GtkListStore *)model;
1087 	sel = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &isel);
1088 	if (!sel)
1089 		return;
1090 
1091 	/* no move if we're at row 0 or 1, looks phony, but other
1092 	 * solutions are more convoluted... */
1093 	try = gtk_tree_path_copy(sel);
1094 	if (!gtk_tree_path_prev(try) || !gtk_tree_path_prev(try)) {
1095 		gtk_tree_path_free(try);
1096 		gtk_tree_path_free(sel);
1097 		return;
1098 	}
1099 	gtk_tree_path_free(try);
1100 
1101 	prev = gtk_tree_path_copy(sel);
1102 	if (gtk_tree_path_prev(prev)) {
1103 		gtk_tree_model_get_iter(GTK_TREE_MODEL(store),
1104 					&iprev, prev);
1105 		gtk_list_store_swap(store, &iprev, &isel);
1106 		/* XXX: GTK2 select row?? */
1107 	}
1108 
1109 	gtk_tree_path_free(sel);
1110 	gtk_tree_path_free(prev);
1111 }
1112 
1113 /*!
1114  *\brief	Signal handler for 'move down' button
1115  */
prefs_filtering_action_down(void)1116 static void prefs_filtering_action_down(void)
1117 {
1118 	GtkListStore *store = NULL;
1119 	GtkTreeModel *model = NULL;
1120 	GtkTreeIter next, sel;
1121 	GtkTreePath *try;
1122 
1123 	if (!gtk_tree_selection_get_selected
1124 		(gtk_tree_view_get_selection
1125 			(GTK_TREE_VIEW(filtering_action.action_list_view)),
1126 		 &model,
1127 		 &sel))
1128 		return;
1129 	store = (GtkListStore *)model;
1130 	try = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &sel);
1131 	if (!try)
1132 		return;
1133 
1134 	/* move when not at row 0 ... */
1135 	if (gtk_tree_path_prev(try)) {
1136 		next = sel;
1137 		if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &next))
1138 			gtk_list_store_swap(store, &next, &sel);
1139 	}
1140 
1141 	gtk_tree_path_free(try);
1142 }
1143 
1144 /*!
1145  *\brief	Handle key press
1146  *
1147  *\param	widget Widget receiving key press
1148  *\param	event Key event
1149  *\param	data User data
1150  */
prefs_filtering_action_key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)1151 static gboolean prefs_filtering_action_key_pressed(GtkWidget *widget,
1152     GdkEventKey *event, gpointer data)
1153 {
1154 	if (event && event->keyval == GDK_KEY_Escape) {
1155 		prefs_filtering_action_cancel();
1156 		return TRUE;
1157 	}
1158 	return FALSE;
1159 }
1160 
1161 /*!
1162  *\brief	Cancel matcher dialog
1163  */
prefs_filtering_action_cancel(void)1164 static void prefs_filtering_action_cancel(void)
1165 {
1166 	gtk_widget_destroy(filtering_action.window);
1167 	filtering_action.window = NULL;
1168 	inc_unlock();
1169 }
1170 
1171 /*!
1172  *\brief	Accept current matchers
1173  */
prefs_filtering_action_ok(void)1174 static void prefs_filtering_action_ok(void)
1175 {
1176         GSList * action_list;
1177         GSList * cur;
1178 
1179 	action_list = prefs_filtering_action_get_list();
1180 
1181         if (action_list == NULL) {
1182                 alertpanel_error(_("No action was defined."));
1183                 return;
1184         }
1185 
1186         if (filtering_action_callback != NULL)
1187                 filtering_action_callback(action_list);
1188         for(cur = action_list ; cur != NULL ; cur = cur->next) {
1189                 filteringaction_free(cur->data);
1190         }
1191         g_slist_free(action_list);
1192 
1193 	gtk_widget_destroy(filtering_action.window);
1194 	filtering_action.window = NULL;
1195         inc_unlock();
1196 }
1197 
1198 /*!
1199  *\brief	Called when closing dialog box
1200  *
1201  *\param	widget Dialog widget
1202  *\param	event Event info
1203  *\param	data User data
1204  *
1205  *\return	gint TRUE
1206  */
prefs_filtering_action_deleted(GtkWidget * widget,GdkEventAny * event,gpointer data)1207 static gint prefs_filtering_action_deleted(GtkWidget *widget,
1208     GdkEventAny *event, gpointer data)
1209 {
1210 	prefs_filtering_action_cancel();
1211 	return TRUE;
1212 }
1213 
1214 /*
1215  * Strings describing exec format strings
1216  *
1217  * When adding new lines, remember to put 2 strings for each line
1218  */
1219 static gchar *exec_desc_strings[] = {
1220 	"%%",	N_("literal %"),
1221 	"%s",	N_("Subject"),
1222 	"%f",	N_("From"),
1223 	"%t",	N_("To"),
1224 	"%c",	N_("Cc"),
1225 	"%d",	N_("Date"),
1226 	"%i",	N_("Message-ID"),
1227 	"%n",	N_("Newsgroups"),
1228 	"%r",	N_("References"),
1229 	"%F",	N_("filename (should not be modified)"),
1230 	"\\n",	N_("new line"),
1231 	"\\",	N_("escape character for quotes"),
1232 	"\\\"", N_("quote character"),
1233 	NULL, NULL
1234 };
1235 
1236 static DescriptionWindow exec_desc_win = {
1237 	NULL,
1238         NULL,
1239 	TRUE,
1240         2,
1241         N_("Filtering Action: 'Execute'"),
1242 	N_("'Execute' allows you to send a message or message element "
1243 	   "to an external program or script.\n"
1244 	   "The following symbols can be used:"),
1245        exec_desc_strings
1246 };
1247 
1248 /*!
1249  *\brief	Show Execute action's info
1250  */
prefs_filtering_action_exec_info(GtkWidget * widget,GtkWidget * parent)1251 static void prefs_filtering_action_exec_info(GtkWidget *widget, GtkWidget *parent)
1252 {
1253 	exec_desc_win.parent = parent;
1254 	description_window_create(&exec_desc_win);
1255 }
1256 
prefs_filtering_action_select_dest(void)1257 static void prefs_filtering_action_select_dest(void)
1258 {
1259 	FolderItem *dest;
1260 	gchar * path;
1261 
1262 	dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE,
1263 			_("Select destination folder"));
1264 	if (!dest) return;
1265 
1266 	path = folder_item_get_identifier(dest);
1267 
1268 	gtk_entry_set_text(GTK_ENTRY(filtering_action.dest_entry), path);
1269 	g_free(path);
1270 }
1271 
1272 #ifndef USE_ALT_ADDRBOOK
prefs_filtering_action_select_addressbook(void)1273 static void prefs_filtering_action_select_addressbook(void)
1274 {
1275 	const gchar *folderpath = NULL;
1276 	gchar *new_path = NULL;
1277 
1278 	folderpath = gtk_entry_get_text(GTK_ENTRY(filtering_action.dest_entry));
1279 	new_path = addressbook_folder_selection(folderpath);
1280 	if (new_path) {
1281 		gtk_entry_set_text(GTK_ENTRY(filtering_action.dest_entry), new_path);
1282 		g_free(new_path);
1283 	}
1284 }
1285 #endif
1286 
prefs_filtering_action_enable_widget(GtkWidget * widget,const gboolean enable)1287 static void prefs_filtering_action_enable_widget(GtkWidget* widget, const gboolean enable)
1288 {
1289 	cm_return_if_fail(widget != NULL);
1290 
1291 	if(enable == TRUE)
1292 	{
1293 		if(GTK_IS_COMBO_BOX(widget))
1294 			gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
1295 		else if(GTK_IS_SPIN_BUTTON(widget))
1296 			gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), 0);
1297 		else if(GTK_IS_ENTRY(widget))
1298 			gtk_entry_set_text(GTK_ENTRY(widget), "");
1299 		else if(GTK_IS_CMOPTION_MENU(widget))
1300 			gtk_cmoption_menu_set_history(GTK_CMOPTION_MENU(widget), 0);
1301 
1302 		gtk_widget_set_sensitive(widget, TRUE);
1303 		gtk_widget_show(widget);
1304 	} else {
1305 		gtk_widget_set_sensitive(widget, FALSE);
1306 		gtk_widget_hide(widget);
1307 	}
1308 }
1309 
1310 #define ACTION_SEND(x)   (x == ACTION_FORWARD || x == ACTION_REDIRECT || \
1311 			  x == ACTION_FORWARD_AS_ATTACHMENT)
1312 #define ACTION_SCORE(x)  (x == ACTION_CHANGE_SCORE || x == ACTION_SET_SCORE)
1313 #define ACTION_TAG(x)    (x == ACTION_SET_TAG || x == ACTION_UNSET_TAG)
1314 #define ACTION_FILEOP(x) (x == ACTION_MOVE || x == ACTION_COPY)
1315 
prefs_filtering_action_type_selection_changed(GtkWidget * combo,gpointer user_data)1316 static void prefs_filtering_action_type_selection_changed(GtkWidget *combo,
1317 							  gpointer user_data)
1318 {
1319 	gint value;
1320 
1321 	value = combobox_get_active_data(GTK_COMBO_BOX(filtering_action.action_combo));
1322 
1323 	if (filtering_action.current_action != value) {
1324 		if (ACTION_SEND(filtering_action.current_action)) {
1325 			debug_print("unregistering address completion entry\n");
1326 			address_completion_unregister_entry(GTK_ENTRY(filtering_action.dest_entry));
1327 			address_completion_end(filtering_action.window);
1328 		}
1329 		if (ACTION_SEND(value)) {
1330 			debug_print("registering address completion entry\n");
1331 			address_completion_start(filtering_action.window);
1332 			address_completion_register_entry(
1333 					GTK_ENTRY(filtering_action.dest_entry),
1334 					TRUE);
1335 		}
1336 		filtering_action.current_action = value;
1337 	}
1338 
1339 	prefs_filtering_action_enable_widget(filtering_action.label2,
1340 		(ACTION_SEND(value) || value == ACTION_ADD_TO_ADDRESSBOOK));
1341 
1342 	prefs_filtering_action_enable_widget(filtering_action.label3,
1343 			(ACTION_SEND(value)    || ACTION_TAG(value) ||
1344 			 ACTION_SCORE(value)   || ACTION_FILEOP(value) ||
1345 			 value == ACTION_COLOR || value == ACTION_EXECUTE ||
1346 			 value == ACTION_ADD_TO_ADDRESSBOOK));
1347 
1348 	prefs_filtering_action_enable_widget(filtering_action.account_combo,
1349 						ACTION_SEND(value));
1350 
1351 	prefs_filtering_action_enable_widget(filtering_action.tags_combo,
1352 						ACTION_TAG(value));
1353 
1354 	prefs_filtering_action_enable_widget(filtering_action.dest_entry,
1355 			(ACTION_FILEOP(value) || value == ACTION_EXECUTE ||
1356 			 ACTION_SEND(value)   || value == ACTION_ADD_TO_ADDRESSBOOK));
1357 
1358 	prefs_filtering_action_enable_widget(filtering_action.dest_btn,
1359 						ACTION_FILEOP(value));
1360 
1361 	prefs_filtering_action_enable_widget(filtering_action.exec_btn,
1362 						(value == ACTION_EXECUTE));
1363 
1364 	prefs_filtering_action_enable_widget(filtering_action.color_optmenu,
1365 						(value == ACTION_COLOR));
1366 
1367 	prefs_filtering_action_enable_widget(filtering_action.header_combo,
1368 					(value == ACTION_ADD_TO_ADDRESSBOOK));
1369 #ifndef USE_ALT_ADDRBOOK
1370 	prefs_filtering_action_enable_widget(filtering_action.addressbook_btn,
1371 					(value == ACTION_ADD_TO_ADDRESSBOOK));
1372 #endif
1373 	prefs_filtering_action_enable_widget(filtering_action.score_entry,
1374 						ACTION_SCORE(value));
1375 
1376 	switch(value) {
1377 	case ACTION_FORWARD:
1378 	case ACTION_FORWARD_AS_ATTACHMENT:
1379 	case ACTION_REDIRECT:
1380 		gtk_label_set_text(GTK_LABEL(filtering_action.label2), _("Account"));
1381 		gtk_label_set_text(GTK_LABEL(filtering_action.label3), _("Recipient"));
1382 		break;
1383 	case ACTION_ADD_TO_ADDRESSBOOK:
1384 		gtk_label_set_text(GTK_LABEL(filtering_action.label2), _("Header Name"));
1385 		gtk_label_set_text(GTK_LABEL(filtering_action.label3), _("Book/Folder"));
1386 		break;
1387 	case ACTION_COPY:
1388 	case ACTION_MOVE:
1389 		gtk_label_set_text(GTK_LABEL(filtering_action.label3), _("Destination"));
1390 		break;
1391 	case ACTION_COLOR:
1392 		gtk_label_set_text(GTK_LABEL(filtering_action.label3), _("Color"));
1393 		break;
1394 	case ACTION_EXECUTE:
1395 		gtk_label_set_text(GTK_LABEL(filtering_action.label3), _("Execute"));
1396 		break;
1397 	case ACTION_SET_SCORE:
1398 	case ACTION_CHANGE_SCORE:
1399 		gtk_label_set_text(GTK_LABEL(filtering_action.label3), _("Score"));
1400 		break;
1401 	case ACTION_SET_TAG:
1402 	case ACTION_UNSET_TAG:
1403 		gtk_label_set_text(GTK_LABEL(filtering_action.label3), _("Tag"));
1404 		break;
1405 	}
1406 }
1407 
prefs_filtering_action_reset_dialog(void)1408 static void prefs_filtering_action_reset_dialog(void)
1409 {
1410 	combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo), ACTION_MOVE);
1411 	gtk_combo_box_set_active(GTK_COMBO_BOX(filtering_action.account_combo), 0);
1412 	gtk_entry_set_text(GTK_ENTRY(filtering_action.dest_entry), "");
1413 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(filtering_action.score_entry), 0);
1414 }
1415 
prefs_filtering_action_create_data_store(void)1416 static GtkListStore* prefs_filtering_action_create_data_store(void)
1417 {
1418 	return gtk_list_store_new(N_PFA_COLUMNS,
1419 				  G_TYPE_STRING,
1420 				  G_TYPE_BOOLEAN,
1421 				  -1);
1422 }
1423 
prefs_filtering_action_list_view_insert_action(GtkWidget * list_view,GtkTreeIter * row,const gchar * action,gboolean is_valid)1424 static void prefs_filtering_action_list_view_insert_action(GtkWidget   *list_view,
1425 							   GtkTreeIter *row,
1426 							   const gchar *action,
1427 							   gboolean	is_valid)
1428 {
1429 	GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model
1430 					(GTK_TREE_VIEW(list_view)));
1431 	GtkTreeIter iter;
1432 
1433 
1434 	/* see if row exists, if not append */
1435 	if (row == NULL)
1436 		gtk_list_store_append(store, &iter);
1437 	else
1438 		iter = *row;
1439 
1440 	gtk_list_store_set(store, &iter,
1441 			   PFA_ACTION, action,
1442 			   PFA_VALID_ACTION, is_valid,
1443 			   -1);
1444 }
1445 
prefs_filtering_action_list_view_create(void)1446 static GtkWidget *prefs_filtering_action_list_view_create(void)
1447 {
1448 	GtkTreeView *list_view;
1449 	GtkTreeModel *model;
1450 	GtkTreeSelection *selector;
1451 
1452 	model = GTK_TREE_MODEL(prefs_filtering_action_create_data_store());
1453 	list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
1454 	g_object_unref(model);
1455 
1456 	gtk_tree_view_set_rules_hint(list_view, prefs_common.use_stripes_everywhere);
1457 	gtk_tree_view_set_reorderable(list_view, TRUE);
1458 
1459 	selector = gtk_tree_view_get_selection(list_view);
1460 	gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
1461 	gtk_tree_selection_set_select_function
1462 		(selector, prefs_filtering_actions_selected, NULL, NULL);
1463 
1464 	/* create the columns */
1465 	prefs_filtering_action_create_list_view_columns(list_view);
1466 
1467 	return GTK_WIDGET(list_view);
1468 }
1469 
prefs_filtering_action_create_list_view_columns(GtkTreeView * list_view)1470 static void prefs_filtering_action_create_list_view_columns(GtkTreeView *list_view)
1471 {
1472 	GtkTreeViewColumn *column;
1473 	GtkCellRenderer *renderer;
1474 
1475 	renderer = gtk_cell_renderer_text_new();
1476 	column = gtk_tree_view_column_new_with_attributes
1477 		(_("Current action list"),
1478 		 renderer,
1479 		 "text", PFA_ACTION,
1480 		 NULL);
1481 	gtk_tree_view_append_column(list_view, column);
1482 }
1483 
prefs_filtering_actions_selected(GtkTreeSelection * selector,GtkTreeModel * model,GtkTreePath * path,gboolean currently_selected,gpointer data)1484 static gboolean prefs_filtering_actions_selected
1485 			(GtkTreeSelection *selector,
1486 			 GtkTreeModel *model,
1487 			 GtkTreePath *path,
1488 			 gboolean currently_selected,
1489 			 gpointer data)
1490 {
1491 	gchar *action_str;
1492 	FilteringAction *action;
1493         GSList * action_list;
1494 	gint list_id;
1495 	GtkTreeIter iter;
1496 	gboolean is_valid;
1497 	GtkWidget *menu;
1498 
1499 	if (currently_selected)
1500 		return TRUE;
1501 
1502 	if (!gtk_tree_model_get_iter(model, &iter, path))
1503 		return TRUE;
1504 
1505 	gtk_tree_model_get(model, &iter,
1506 			   PFA_VALID_ACTION,  &is_valid,
1507 			   -1);
1508 
1509 	if (!is_valid) {
1510 		prefs_filtering_action_reset_dialog();
1511 		return TRUE;
1512 	}
1513 
1514 	gtk_tree_model_get(model, &iter,
1515 			   PFA_ACTION, &action_str,
1516 			   -1);
1517 
1518 	action_list = matcher_parser_get_action_list(action_str);
1519 	g_free(action_str);
1520 
1521 	if (action_list == NULL)
1522 		return TRUE;
1523 
1524         action = action_list->data;
1525         g_slist_free(action_list);
1526 
1527 	switch(action->type) {
1528 	case MATCHACTION_MOVE:
1529 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1530 				     ACTION_MOVE);
1531 		break;
1532 	case MATCHACTION_COPY:
1533 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1534 				     ACTION_COPY);
1535 		break;
1536 	case MATCHACTION_DELETE:
1537 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1538 				     ACTION_DELETE);
1539 		break;
1540 	case MATCHACTION_MARK:
1541 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1542 				     ACTION_MARK);
1543 		break;
1544 	case MATCHACTION_UNMARK:
1545 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1546 				     ACTION_UNMARK);
1547 		break;
1548 	case MATCHACTION_LOCK:
1549 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1550 				     ACTION_LOCK);
1551 		break;
1552 	case MATCHACTION_UNLOCK:
1553 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1554 				     ACTION_UNLOCK);
1555 		break;
1556 	case MATCHACTION_MARK_AS_READ:
1557 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1558 				     ACTION_MARK_AS_READ);
1559 		break;
1560 	case MATCHACTION_MARK_AS_UNREAD:
1561 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1562 				     ACTION_MARK_AS_UNREAD);
1563 		break;
1564 	case MATCHACTION_MARK_AS_SPAM:
1565 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1566 				     ACTION_MARK_AS_SPAM);
1567 		break;
1568 	case MATCHACTION_MARK_AS_HAM:
1569 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1570 				     ACTION_MARK_AS_HAM);
1571 		break;
1572 	case MATCHACTION_FORWARD:
1573 		list_id = get_list_id_from_account_id(action->account_id);
1574 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1575 				     ACTION_FORWARD);
1576 		gtk_combo_box_set_active(GTK_COMBO_BOX(filtering_action.account_combo),
1577 				     list_id);
1578 		break;
1579 	case MATCHACTION_FORWARD_AS_ATTACHMENT:
1580 		list_id = get_list_id_from_account_id(action->account_id);
1581 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1582 				     ACTION_FORWARD_AS_ATTACHMENT);
1583 		gtk_combo_box_set_active(GTK_COMBO_BOX(filtering_action.account_combo),
1584 				     list_id);
1585 		break;
1586 	case MATCHACTION_REDIRECT:
1587 		list_id = get_list_id_from_account_id(action->account_id);
1588 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1589 				     ACTION_REDIRECT);
1590 		gtk_combo_box_set_active(GTK_COMBO_BOX(filtering_action.account_combo),
1591 				     list_id);
1592 		break;
1593 	case MATCHACTION_EXECUTE:
1594 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1595 				     ACTION_EXECUTE);
1596 		break;
1597 	case MATCHACTION_COLOR:
1598 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1599 				     ACTION_COLOR);
1600 		gtk_cmoption_menu_set_history(GTK_CMOPTION_MENU(filtering_action.color_optmenu),
1601 					    action->labelcolor + 1);
1602 		menu = gtk_cmoption_menu_get_menu(GTK_CMOPTION_MENU(
1603 						filtering_action.color_optmenu));
1604 		g_signal_emit_by_name(G_OBJECT(menu), "selection-done", menu);
1605 		break;
1606 	case MATCHACTION_CHANGE_SCORE:
1607 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1608 				     ACTION_CHANGE_SCORE);
1609 		gtk_spin_button_set_value(GTK_SPIN_BUTTON(filtering_action.score_entry),
1610 					  action->score);
1611 		break;
1612 	case MATCHACTION_SET_SCORE:
1613 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1614 				     ACTION_SET_SCORE);
1615 		gtk_spin_button_set_value(GTK_SPIN_BUTTON(filtering_action.score_entry),
1616 					  action->score);
1617 		break;
1618 	case MATCHACTION_STOP:
1619 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1620 				     ACTION_STOP);
1621 		break;
1622 	case MATCHACTION_HIDE:
1623 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1624 				     ACTION_HIDE);
1625 		break;
1626 	case MATCHACTION_IGNORE:
1627 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1628 				     ACTION_IGNORE);
1629 		break;
1630 	case MATCHACTION_WATCH:
1631 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1632 				     ACTION_WATCH);
1633 		break;
1634 	case MATCHACTION_ADD_TO_ADDRESSBOOK:
1635 		if (action->header)
1636 			gtk_entry_set_text(GTK_ENTRY(filtering_action.header_entry), action->header);
1637 		else
1638 			gtk_entry_set_text(GTK_ENTRY(filtering_action.header_entry), "");
1639 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1640 				     ACTION_ADD_TO_ADDRESSBOOK);
1641 		break;
1642 	case MATCHACTION_SET_TAG:
1643 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1644 				     ACTION_SET_TAG);
1645 		combobox_select_by_text(GTK_COMBO_BOX(filtering_action.tags_combo),
1646 					action->destination);
1647 		break;
1648 	case MATCHACTION_UNSET_TAG:
1649 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1650 				     ACTION_UNSET_TAG);
1651 		combobox_select_by_text(GTK_COMBO_BOX(filtering_action.tags_combo),
1652 					action->destination);
1653 		break;
1654 	case MATCHACTION_CLEAR_TAGS:
1655 		combobox_select_by_data(GTK_COMBO_BOX(filtering_action.action_combo),
1656 				     ACTION_CLEAR_TAGS);
1657 		break;
1658 	default:
1659 		g_warning("unhandled action type");
1660 	}
1661 	if (action->destination)
1662 		gtk_entry_set_text(GTK_ENTRY(filtering_action.dest_entry), action->destination);
1663 	else
1664 		gtk_entry_set_text(GTK_ENTRY(filtering_action.dest_entry), "");
1665 
1666 	filteringaction_free(action); /* XXX: memleak */
1667 	return TRUE;
1668 }
1669