1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful, but
8  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10  * for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public License
13  * along with this program; if not, see <http://www.gnu.org/licenses/>.
14  *
15  *
16  * Authors:
17  *		Not Zed <notzed@lostzed.mmc.com.au>
18  *      Jeffrey Stedfast <fejj@ximian.com>
19  *
20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21  *
22  */
23 
24 #include "evolution-config.h"
25 
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 
33 #include <glib/gstdio.h>
34 
35 #include <gtk/gtk.h>
36 
37 #include <glib/gi18n.h>
38 
39 #include <libedataserver/libedataserver.h>
40 
41 #include "e-alert-dialog.h"
42 #include "e-filter-code.h"
43 #include "e-filter-color.h"
44 #include "e-filter-datespec.h"
45 #include "e-filter-file.h"
46 #include "e-filter-input.h"
47 #include "e-filter-int.h"
48 #include "e-filter-label.h"
49 #include "e-filter-option.h"
50 #include "e-filter-rule.h"
51 #include "e-rule-context.h"
52 #include "e-xml-utils.h"
53 
54 #define E_RULE_CONTEXT_GET_PRIVATE(obj) \
55 	(G_TYPE_INSTANCE_GET_PRIVATE \
56 	((obj), E_TYPE_RULE_CONTEXT, ERuleContextPrivate))
57 
58 struct _ERuleContextPrivate {
59 	gint frozen;
60 };
61 
62 enum {
63 	RULE_ADDED,
64 	RULE_REMOVED,
65 	CHANGED,
66 	LAST_SIGNAL
67 };
68 
69 static guint signals[LAST_SIGNAL];
70 
71 struct _revert_data {
72 	GHashTable *rules;
73 	gint rank;
74 };
75 
G_DEFINE_TYPE(ERuleContext,e_rule_context,G_TYPE_OBJECT)76 G_DEFINE_TYPE (
77 	ERuleContext,
78 	e_rule_context,
79 	G_TYPE_OBJECT)
80 
81 static void
82 rule_context_set_error (ERuleContext *context,
83                         gchar *error)
84 {
85 	g_free (context->error);
86 	context->error = error;
87 }
88 
89 static void
new_rule_response(GtkWidget * dialog,gint button,ERuleContext * context)90 new_rule_response (GtkWidget *dialog,
91                    gint button,
92                    ERuleContext *context)
93 {
94 	if (button == GTK_RESPONSE_OK) {
95 		EFilterRule *rule = g_object_get_data ((GObject *) dialog, "rule");
96 		gchar *user = g_object_get_data ((GObject *) dialog, "path");
97 		EAlert *alert = NULL;
98 
99 		if (!e_filter_rule_validate (rule, &alert)) {
100 			e_alert_run_dialog (GTK_WINDOW (dialog), alert);
101 			g_object_unref (alert);
102 			return;
103 		}
104 
105 		if (e_rule_context_find_rule (context, rule->name, rule->source)) {
106 			e_alert_run_dialog_for_args ((GtkWindow *) dialog,
107 						     "filter:bad-name-notunique",
108 						     rule->name, NULL);
109 
110 			return;
111 		}
112 
113 		g_object_ref (rule);
114 		e_rule_context_add_rule (context, rule);
115 		if (user)
116 			e_rule_context_save (context, user);
117 	}
118 
119 	gtk_widget_destroy (dialog);
120 }
121 
122 static void
revert_rule_remove(gpointer key,EFilterRule * rule,ERuleContext * context)123 revert_rule_remove (gpointer key,
124                     EFilterRule *rule,
125                     ERuleContext *context)
126 {
127 	e_rule_context_remove_rule (context, rule);
128 	g_object_unref (rule);
129 }
130 
131 static void
revert_source_remove(gpointer key,struct _revert_data * rest_data,ERuleContext * context)132 revert_source_remove (gpointer key,
133                       struct _revert_data *rest_data,
134                       ERuleContext *context)
135 {
136 	g_hash_table_foreach (
137 		rest_data->rules, (GHFunc) revert_rule_remove, context);
138 	g_hash_table_destroy (rest_data->rules);
139 	g_free (rest_data);
140 }
141 
142 static guint
source_hashf(const gchar * a)143 source_hashf (const gchar *a)
144 {
145 	return (a != NULL) ? g_str_hash (a) : 0;
146 }
147 
148 static gint
source_eqf(const gchar * a,const gchar * b)149 source_eqf (const gchar *a,
150             const gchar *b)
151 {
152 	return (g_strcmp0 (a, b) == 0);
153 }
154 
155 static void
free_part_set(struct _part_set_map * map)156 free_part_set (struct _part_set_map *map)
157 {
158 	g_free (map->name);
159 	g_free (map);
160 }
161 
162 static void
free_rule_set(struct _rule_set_map * map)163 free_rule_set (struct _rule_set_map *map)
164 {
165 	g_free (map->name);
166 	g_free (map);
167 }
168 
169 static void
rule_context_finalize(GObject * obj)170 rule_context_finalize (GObject *obj)
171 {
172 	ERuleContext *context =(ERuleContext *) obj;
173 
174 	g_list_foreach (context->rule_set_list, (GFunc) free_rule_set, NULL);
175 	g_list_free (context->rule_set_list);
176 	g_hash_table_destroy (context->rule_set_map);
177 
178 	g_list_foreach (context->part_set_list, (GFunc) free_part_set, NULL);
179 	g_list_free (context->part_set_list);
180 	g_hash_table_destroy (context->part_set_map);
181 
182 	g_free (context->error);
183 
184 	g_list_foreach (context->parts, (GFunc) g_object_unref, NULL);
185 	g_list_free (context->parts);
186 
187 	g_list_foreach (context->rules, (GFunc) g_object_unref, NULL);
188 	g_list_free (context->rules);
189 
190 	G_OBJECT_CLASS (e_rule_context_parent_class)->finalize (obj);
191 }
192 
193 static gint
rule_context_load(ERuleContext * context,const gchar * system,const gchar * user)194 rule_context_load (ERuleContext *context,
195                    const gchar *system,
196                    const gchar *user)
197 {
198 	xmlNodePtr set, rule, root;
199 	xmlDocPtr systemdoc, userdoc;
200 	struct _part_set_map *part_map;
201 	struct _rule_set_map *rule_map;
202 
203 	rule_context_set_error (context, NULL);
204 
205 	systemdoc = e_xml_parse_file (system);
206 	if (systemdoc == NULL) {
207 		gchar * err_msg;
208 
209 		err_msg = g_strdup_printf (
210 			"Unable to load system rules '%s': %s",
211 			system, g_strerror (errno));
212 		g_warning ("%s: %s", G_STRFUNC, err_msg);
213 		rule_context_set_error (context, err_msg);
214 		/* no need to free err_msg here */
215 		return -1;
216 	}
217 
218 	root = xmlDocGetRootElement (systemdoc);
219 	if (root == NULL || strcmp ((gchar *) root->name, "filterdescription")) {
220 		gchar * err_msg;
221 
222 		err_msg = g_strdup_printf (
223 			"Unable to load system rules '%s': "
224 			"Invalid format", system);
225 		g_warning ("%s: %s", G_STRFUNC, err_msg);
226 		rule_context_set_error (context, err_msg);
227 		/* no need to free err_msg here */
228 		xmlFreeDoc (systemdoc);
229 		return -1;
230 	}
231 	/* doesn't matter if this doens't exist */
232 	userdoc = NULL;
233 	if (g_file_test (user, G_FILE_TEST_IS_REGULAR))
234 		userdoc = e_xml_parse_file (user);
235 
236 	/* now parse structure */
237 	/* get rule parts */
238 	set = root->children;
239 	while (set) {
240 		part_map = g_hash_table_lookup (context->part_set_map, set->name);
241 		if (part_map) {
242 			rule = set->children;
243 			while (rule) {
244 				if (!strcmp ((gchar *) rule->name, "part")) {
245 					EFilterPart *part =
246 						E_FILTER_PART (g_object_new (
247 						part_map->type, NULL, NULL));
248 
249 					if (e_filter_part_xml_create (part, rule, context) == 0) {
250 						part_map->append (context, part);
251 					} else {
252 						g_object_unref (part);
253 						g_warning ("Cannot load filter part");
254 					}
255 				}
256 				rule = rule->next;
257 			}
258 		} else if ((rule_map = g_hash_table_lookup (
259 				context->rule_set_map, set->name))) {
260 			rule = set->children;
261 			while (rule) {
262 				if (!strcmp ((gchar *) rule->name, "rule")) {
263 					EFilterRule *part =
264 						E_FILTER_RULE (g_object_new (
265 						rule_map->type, NULL, NULL));
266 
267 					if (e_filter_rule_xml_decode (part, rule, context) == 0) {
268 						part->system = TRUE;
269 						rule_map->append (context, part);
270 					} else {
271 						g_object_unref (part);
272 						g_warning ("Cannot load filter part");
273 					}
274 				}
275 				rule = rule->next;
276 			}
277 		}
278 		set = set->next;
279 	}
280 
281 	/* now load actual rules */
282 	if (userdoc) {
283 		root = xmlDocGetRootElement (userdoc);
284 		set = root ? root->children : NULL;
285 		while (set) {
286 			rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
287 			if (rule_map) {
288 				rule = set->children;
289 				while (rule) {
290 					if (!strcmp ((gchar *) rule->name, "rule")) {
291 						EFilterRule *part =
292 							E_FILTER_RULE (g_object_new (
293 							rule_map->type, NULL, NULL));
294 
295 						if (e_filter_rule_xml_decode (part, rule, context) == 0) {
296 							rule_map->append (context, part);
297 						} else {
298 							g_object_unref (part);
299 							g_warning ("Cannot load filter part");
300 						}
301 					}
302 					rule = rule->next;
303 				}
304 			}
305 			set = set->next;
306 		}
307 	}
308 
309 	xmlFreeDoc (userdoc);
310 	xmlFreeDoc (systemdoc);
311 
312 	return 0;
313 }
314 
315 static gint
rule_context_save(ERuleContext * context,const gchar * user)316 rule_context_save (ERuleContext *context,
317                    const gchar *user)
318 {
319 	xmlDocPtr doc;
320 	xmlNodePtr root, rules, work;
321 	GList *l;
322 	EFilterRule *rule;
323 	struct _rule_set_map *map;
324 	gint ret;
325 
326 	doc = xmlNewDoc ((xmlChar *)"1.0");
327 	/* FIXME: set character encoding to UTF-8? */
328 	root = xmlNewDocNode (doc, NULL, (xmlChar *)"filteroptions", NULL);
329 	xmlDocSetRootElement (doc, root);
330 	l = context->rule_set_list;
331 	while (l) {
332 		map = l->data;
333 		rules = xmlNewDocNode (doc, NULL, (xmlChar *) map->name, NULL);
334 		xmlAddChild (root, rules);
335 		rule = NULL;
336 		while ((rule = map->next (context, rule, NULL))) {
337 			if (!rule->system) {
338 				work = e_filter_rule_xml_encode (rule);
339 				xmlAddChild (rules, work);
340 			}
341 		}
342 		l = g_list_next (l);
343 	}
344 
345 	ret = e_xml_save_file (user, doc);
346 
347 	xmlFreeDoc (doc);
348 
349 	return ret;
350 }
351 
352 static gint
rule_context_revert(ERuleContext * context,const gchar * user)353 rule_context_revert (ERuleContext *context,
354                      const gchar *user)
355 {
356 	xmlNodePtr set, rule;
357 	/*struct _part_set_map *part_map;*/
358 	struct _rule_set_map *rule_map;
359 	struct _revert_data *rest_data;
360 	GHashTable *source_hash;
361 	xmlDocPtr userdoc;
362 	EFilterRule *frule;
363 
364 	rule_context_set_error (context, NULL);
365 
366 	userdoc = e_xml_parse_file (user);
367 	if (userdoc == NULL)
368 		/* clear out anythign we have? */
369 		return 0;
370 
371 	source_hash = g_hash_table_new (
372 		(GHashFunc) source_hashf,
373 		(GCompareFunc) source_eqf);
374 
375 	/* setup stuff we have now */
376 	/* Note that we assume there is only 1 set of rules in a given rule context,
377 	 * although other parts of the code dont assume this */
378 	frule = NULL;
379 	while ((frule = e_rule_context_next_rule (context, frule, NULL))) {
380 		rest_data = g_hash_table_lookup (source_hash, frule->source);
381 		if (rest_data == NULL) {
382 			rest_data = g_malloc0 (sizeof (*rest_data));
383 			rest_data->rules = g_hash_table_new (g_str_hash, g_str_equal);
384 			g_hash_table_insert (source_hash, frule->source, rest_data);
385 		}
386 		g_hash_table_insert (rest_data->rules, frule->name, frule);
387 	}
388 
389 	/* make what we have, match what we load */
390 	set = xmlDocGetRootElement (userdoc);
391 	set = set ? set->children : NULL;
392 	while (set) {
393 		rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
394 		if (rule_map) {
395 			rule = set->children;
396 			while (rule) {
397 				if (!strcmp ((gchar *) rule->name, "rule")) {
398 					EFilterRule *part =
399 						E_FILTER_RULE (g_object_new (
400 						rule_map->type, NULL, NULL));
401 
402 					if (e_filter_rule_xml_decode (part, rule, context) == 0) {
403 						/* Use the revert data to keep
404 						 * track of the right rank of
405 						 * this rule part. */
406 						rest_data = g_hash_table_lookup (source_hash, part->source);
407 						if (rest_data == NULL) {
408 							rest_data = g_malloc0 (sizeof (*rest_data));
409 							rest_data->rules = g_hash_table_new (
410 								g_str_hash,
411 								g_str_equal);
412 							g_hash_table_insert (
413 								source_hash,
414 								part->source,
415 								rest_data);
416 						}
417 						frule = g_hash_table_lookup (
418 							rest_data->rules,
419 							part->name);
420 						if (frule) {
421 							if (context->priv->frozen == 0 &&
422 							    !e_filter_rule_eq (frule, part))
423 								e_filter_rule_copy (frule, part);
424 
425 							g_object_unref (part);
426 							e_rule_context_rank_rule (
427 								context, frule,
428 								frule->source,
429 								rest_data->rank);
430 							g_hash_table_remove (rest_data->rules, frule->name);
431 						} else {
432 							e_rule_context_add_rule (context, part);
433 							e_rule_context_rank_rule (
434 								context,
435 								part,
436 								part->source,
437 								rest_data->rank);
438 						}
439 						rest_data->rank++;
440 					} else {
441 						g_object_unref (part);
442 						g_warning ("Cannot load filter part");
443 					}
444 				}
445 				rule = rule->next;
446 			}
447 		}
448 		set = set->next;
449 	}
450 
451 	xmlFreeDoc (userdoc);
452 
453 	/* remove any we still have that weren't in the file */
454 	g_hash_table_foreach (source_hash, (GHFunc) revert_source_remove, context);
455 	g_hash_table_destroy (source_hash);
456 
457 	return 0;
458 }
459 
460 static EFilterElement *
rule_context_new_element(ERuleContext * context,const gchar * type)461 rule_context_new_element (ERuleContext *context,
462                           const gchar *type)
463 {
464 	if (!strcmp (type, "label")) {
465 		return (EFilterElement *) e_filter_label_new ();
466 	} else if (!strcmp (type, "string")) {
467 		return (EFilterElement *) e_filter_input_new ();
468 	} else if (!strcmp (type, "address")) {
469 		/* FIXME: temporary ... need real address type */
470 		return (EFilterElement *) e_filter_input_new_type_name (type);
471 	} else if (!strcmp (type, "code")) {
472 		return (EFilterElement *) e_filter_code_new (FALSE);
473 	} else if (!strcmp (type, "rawcode")) {
474 		return (EFilterElement *) e_filter_code_new (TRUE);
475 	} else if (!strcmp (type, "colour")) {
476 		return (EFilterElement *) e_filter_color_new ();
477 	} else if (!strcmp (type, "optionlist")) {
478 		return (EFilterElement *) e_filter_option_new ();
479 	} else if (!strcmp (type, "datespec")) {
480 		return (EFilterElement *) e_filter_datespec_new ();
481 	} else if (!strcmp (type, "command")) {
482 		return (EFilterElement *) e_filter_file_new_type_name (type);
483 	} else if (!strcmp (type, "file")) {
484 		return (EFilterElement *) e_filter_file_new_type_name (type);
485 	} else if (!strcmp (type, "integer")) {
486 		return (EFilterElement *) e_filter_int_new ();
487 	} else if (!strcmp (type, "regex")) {
488 		return (EFilterElement *) e_filter_input_new_type_name (type);
489 	} else if (!strcmp (type, "completedpercent")) {
490 		return (EFilterElement *) e_filter_int_new_type (
491 			"completedpercent", 0,100);
492 	} else {
493 		g_warning ("Unknown filter type '%s'", type);
494 		return NULL;
495 	}
496 }
497 
498 static void
e_rule_context_class_init(ERuleContextClass * class)499 e_rule_context_class_init (ERuleContextClass *class)
500 {
501 	GObjectClass *object_class;
502 
503 	g_type_class_add_private (class, sizeof (ERuleContextPrivate));
504 
505 	object_class = G_OBJECT_CLASS (class);
506 	object_class->finalize = rule_context_finalize;
507 
508 	class->load = rule_context_load;
509 	class->save = rule_context_save;
510 	class->revert = rule_context_revert;
511 	class->new_element = rule_context_new_element;
512 
513 	signals[RULE_ADDED] = g_signal_new (
514 		"rule-added",
515 		E_TYPE_RULE_CONTEXT,
516 		G_SIGNAL_RUN_LAST,
517 		G_STRUCT_OFFSET (ERuleContextClass, rule_added),
518 		NULL,
519 		NULL,
520 		g_cclosure_marshal_VOID__POINTER,
521 		G_TYPE_NONE, 1,
522 		G_TYPE_POINTER);
523 
524 	signals[RULE_REMOVED] = g_signal_new (
525 		"rule-removed",
526 		E_TYPE_RULE_CONTEXT,
527 		G_SIGNAL_RUN_LAST,
528 		G_STRUCT_OFFSET (ERuleContextClass, rule_removed),
529 		NULL,
530 		NULL,
531 		g_cclosure_marshal_VOID__POINTER,
532 		G_TYPE_NONE, 1,
533 		G_TYPE_POINTER);
534 
535 	signals[CHANGED] = g_signal_new (
536 		"changed",
537 		E_TYPE_RULE_CONTEXT,
538 		G_SIGNAL_RUN_LAST,
539 		G_STRUCT_OFFSET (ERuleContextClass, changed),
540 		NULL,
541 		NULL,
542 		g_cclosure_marshal_VOID__VOID,
543 		G_TYPE_NONE, 0);
544 }
545 
546 static void
e_rule_context_init(ERuleContext * context)547 e_rule_context_init (ERuleContext *context)
548 {
549 	context->priv = E_RULE_CONTEXT_GET_PRIVATE (context);
550 
551 	context->part_set_map = g_hash_table_new (g_str_hash, g_str_equal);
552 	context->rule_set_map = g_hash_table_new (g_str_hash, g_str_equal);
553 
554 	context->flags = E_RULE_CONTEXT_GROUPING;
555 }
556 
557 /**
558  * e_rule_context_new:
559  *
560  * Create a new ERuleContext object.
561  *
562  * Return value: A new #ERuleContext object.
563  **/
564 ERuleContext *
e_rule_context_new(void)565 e_rule_context_new (void)
566 {
567 	return g_object_new (E_TYPE_RULE_CONTEXT, NULL);
568 }
569 
570 void
e_rule_context_add_part_set(ERuleContext * context,const gchar * setname,GType part_type,ERuleContextPartFunc append,ERuleContextNextPartFunc next)571 e_rule_context_add_part_set (ERuleContext *context,
572                              const gchar *setname,
573                              GType part_type,
574                              ERuleContextPartFunc append,
575                              ERuleContextNextPartFunc next)
576 {
577 	struct _part_set_map *map;
578 
579 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
580 	g_return_if_fail (setname != NULL);
581 	g_return_if_fail (append != NULL);
582 	g_return_if_fail (next != NULL);
583 
584 	map = g_hash_table_lookup (context->part_set_map, setname);
585 	if (map != NULL) {
586 		g_hash_table_remove (context->part_set_map, setname);
587 		context->part_set_list = g_list_remove (context->part_set_list, map);
588 		free_part_set (map);
589 		map = NULL;
590 	}
591 
592 	map = g_malloc0 (sizeof (*map));
593 	map->type = part_type;
594 	map->append = append;
595 	map->next = next;
596 	map->name = g_strdup (setname);
597 	g_hash_table_insert (context->part_set_map, map->name, map);
598 	context->part_set_list = g_list_append (context->part_set_list, map);
599 }
600 
601 void
e_rule_context_add_rule_set(ERuleContext * context,const gchar * setname,GType rule_type,ERuleContextRuleFunc append,ERuleContextNextRuleFunc next)602 e_rule_context_add_rule_set (ERuleContext *context,
603                              const gchar *setname,
604                              GType rule_type,
605                              ERuleContextRuleFunc append,
606                              ERuleContextNextRuleFunc next)
607 {
608 	struct _rule_set_map *map;
609 
610 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
611 	g_return_if_fail (setname != NULL);
612 	g_return_if_fail (append != NULL);
613 	g_return_if_fail (next != NULL);
614 
615 	map = g_hash_table_lookup (context->rule_set_map, setname);
616 	if (map != NULL) {
617 		g_hash_table_remove (context->rule_set_map, setname);
618 		context->rule_set_list = g_list_remove (context->rule_set_list, map);
619 		free_rule_set (map);
620 		map = NULL;
621 	}
622 
623 	map = g_malloc0 (sizeof (*map));
624 	map->type = rule_type;
625 	map->append = append;
626 	map->next = next;
627 	map->name = g_strdup (setname);
628 	g_hash_table_insert (context->rule_set_map, map->name, map);
629 	context->rule_set_list = g_list_append (context->rule_set_list, map);
630 }
631 
632 /**
633  * e_rule_context_load:
634  * @context:
635  * @system:
636  * @user:
637  *
638  * Load a rule context from a system and user description file.
639  *
640  * Return value:
641  **/
642 gint
e_rule_context_load(ERuleContext * context,const gchar * system,const gchar * user)643 e_rule_context_load (ERuleContext *context,
644                      const gchar *system,
645                      const gchar *user)
646 {
647 	ERuleContextClass *class;
648 	gint result;
649 
650 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
651 	g_return_val_if_fail (system != NULL, -1);
652 	g_return_val_if_fail (user != NULL, -1);
653 
654 	class = E_RULE_CONTEXT_GET_CLASS (context);
655 	g_return_val_if_fail (class != NULL, -1);
656 	g_return_val_if_fail (class->load != NULL, -1);
657 
658 	context->priv->frozen++;
659 	result = class->load (context, system, user);
660 	context->priv->frozen--;
661 
662 	return result;
663 }
664 
665 /**
666  * e_rule_context_save:
667  * @context:
668  * @user:
669  *
670  * Save a rule context to disk.
671  *
672  * Return value:
673  **/
674 gint
e_rule_context_save(ERuleContext * context,const gchar * user)675 e_rule_context_save (ERuleContext *context,
676                      const gchar *user)
677 {
678 	ERuleContextClass *class;
679 
680 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
681 	g_return_val_if_fail (user != NULL, -1);
682 
683 	class = E_RULE_CONTEXT_GET_CLASS (context);
684 	g_return_val_if_fail (class != NULL, -1);
685 	g_return_val_if_fail (class->save != NULL, -1);
686 
687 	return class->save (context, user);
688 }
689 
690 /**
691  * e_rule_context_revert:
692  * @context:
693  * @user:
694  *
695  * Reverts a rule context from a user description file.  Assumes the
696  * system description file is unchanged from when it was loaded.
697  *
698  * Return value:
699  **/
700 gint
e_rule_context_revert(ERuleContext * context,const gchar * user)701 e_rule_context_revert (ERuleContext *context,
702                        const gchar *user)
703 {
704 	ERuleContextClass *class;
705 
706 	g_return_val_if_fail (E_RULE_CONTEXT (context), 0);
707 	g_return_val_if_fail (user != NULL, 0);
708 
709 	class = E_RULE_CONTEXT_GET_CLASS (context);
710 	g_return_val_if_fail (class != NULL, 0);
711 	g_return_val_if_fail (class->revert != NULL, 0);
712 
713 	return class->revert (context, user);
714 }
715 
716 EFilterPart *
e_rule_context_find_part(ERuleContext * context,const gchar * name)717 e_rule_context_find_part (ERuleContext *context,
718                           const gchar *name)
719 {
720 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
721 	g_return_val_if_fail (name != NULL, NULL);
722 
723 	return e_filter_part_find_list (context->parts, name);
724 }
725 
726 EFilterPart *
e_rule_context_create_part(ERuleContext * context,const gchar * name)727 e_rule_context_create_part (ERuleContext *context,
728                             const gchar *name)
729 {
730 	EFilterPart *part;
731 
732 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
733 	g_return_val_if_fail (name != NULL, NULL);
734 
735 	part = e_rule_context_find_part (context, name);
736 
737 	if (part == NULL)
738 		return NULL;
739 
740 	return e_filter_part_clone (part);
741 }
742 
743 EFilterPart *
e_rule_context_next_part(ERuleContext * context,EFilterPart * last)744 e_rule_context_next_part (ERuleContext *context,
745                           EFilterPart *last)
746 {
747 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
748 
749 	return e_filter_part_next_list (context->parts, last);
750 }
751 
752 EFilterRule *
e_rule_context_next_rule(ERuleContext * context,EFilterRule * last,const gchar * source)753 e_rule_context_next_rule (ERuleContext *context,
754                           EFilterRule *last,
755                           const gchar *source)
756 {
757 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
758 
759 	return e_filter_rule_next_list (context->rules, last, source);
760 }
761 
762 EFilterRule *
e_rule_context_find_rule(ERuleContext * context,const gchar * name,const gchar * source)763 e_rule_context_find_rule (ERuleContext *context,
764                           const gchar *name,
765                           const gchar *source)
766 {
767 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
768 	g_return_val_if_fail (name != NULL, NULL);
769 
770 	return e_filter_rule_find_list (context->rules, name, source);
771 }
772 
773 void
e_rule_context_add_part(ERuleContext * context,EFilterPart * part)774 e_rule_context_add_part (ERuleContext *context,
775                          EFilterPart *part)
776 {
777 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
778 	g_return_if_fail (E_IS_FILTER_PART (part));
779 
780 	context->parts = g_list_append (context->parts, part);
781 }
782 
783 void
e_rule_context_add_rule(ERuleContext * context,EFilterRule * rule)784 e_rule_context_add_rule (ERuleContext *context,
785                          EFilterRule *rule)
786 {
787 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
788 	g_return_if_fail (E_IS_FILTER_RULE (rule));
789 
790 	context->rules = g_list_append (context->rules, rule);
791 
792 	if (context->priv->frozen == 0) {
793 		g_signal_emit (context, signals[RULE_ADDED], 0, rule);
794 		g_signal_emit (context, signals[CHANGED], 0);
795 	}
796 }
797 
798 /* Add a rule, with a gui, asking for confirmation first,
799  * and optionally save to path. */
800 void
e_rule_context_add_rule_gui(ERuleContext * context,EFilterRule * rule,const gchar * title,const gchar * path)801 e_rule_context_add_rule_gui (ERuleContext *context,
802                              EFilterRule *rule,
803                              const gchar *title,
804                              const gchar *path)
805 {
806 	GtkDialog *dialog;
807 	GtkWidget *widget;
808 	GtkWidget *content_area;
809 
810 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
811 	g_return_if_fail (E_IS_FILTER_RULE (rule));
812 
813 	widget = e_filter_rule_get_widget (rule, context);
814 	gtk_widget_show (widget);
815 
816 	dialog =(GtkDialog *) gtk_dialog_new ();
817 	gtk_dialog_add_buttons (
818 		dialog,
819 		_("_Cancel"), GTK_RESPONSE_CANCEL,
820 		_("_OK"), GTK_RESPONSE_OK,
821 		NULL);
822 
823 	gtk_window_set_title ((GtkWindow *) dialog, title);
824 	gtk_window_set_default_size ((GtkWindow *) dialog, 600, 400);
825 	gtk_window_set_resizable ((GtkWindow *) dialog, TRUE);
826 
827 	content_area = gtk_dialog_get_content_area (dialog);
828 	gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
829 
830 	g_object_set_data_full ((GObject *) dialog, "rule", rule, g_object_unref);
831 	if (path)
832 		g_object_set_data_full ((GObject *) dialog, "path", g_strdup (path), g_free);
833 
834 	g_signal_connect (
835 		dialog, "response",
836 		G_CALLBACK (new_rule_response), context);
837 
838 	g_object_ref (context);
839 
840 	g_object_set_data_full ((GObject *) dialog, "context", context, g_object_unref);
841 
842 	gtk_widget_show ((GtkWidget *) dialog);
843 }
844 
845 void
e_rule_context_remove_rule(ERuleContext * context,EFilterRule * rule)846 e_rule_context_remove_rule (ERuleContext *context,
847                             EFilterRule *rule)
848 {
849 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
850 	g_return_if_fail (E_IS_FILTER_RULE (rule));
851 
852 	context->rules = g_list_remove (context->rules, rule);
853 
854 	if (context->priv->frozen == 0) {
855 		g_signal_emit (context, signals[RULE_REMOVED], 0, rule);
856 		g_signal_emit (context, signals[CHANGED], 0);
857 	}
858 }
859 
860 void
e_rule_context_rank_rule(ERuleContext * context,EFilterRule * rule,const gchar * source,gint rank)861 e_rule_context_rank_rule (ERuleContext *context,
862                           EFilterRule *rule,
863                           const gchar *source,
864                           gint rank)
865 {
866 	GList *node;
867 	gint i = 0, index = 0;
868 
869 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
870 	g_return_if_fail (E_IS_FILTER_RULE (rule));
871 
872 	if (e_rule_context_get_rank_rule (context, rule, source) == rank)
873 		return;
874 
875 	context->rules = g_list_remove (context->rules, rule);
876 	node = context->rules;
877 	while (node) {
878 		EFilterRule *r = node->data;
879 
880 		if (i == rank) {
881 			context->rules = g_list_insert (context->rules, rule, index);
882 			if (context->priv->frozen == 0)
883 				g_signal_emit (context, signals[CHANGED], 0);
884 
885 			return;
886 		}
887 
888 		index++;
889 		if (source == NULL || (r->source && strcmp (r->source, source) == 0))
890 			i++;
891 
892 		node = node->next;
893 	}
894 
895 	context->rules = g_list_append (context->rules, rule);
896 	if (context->priv->frozen == 0)
897 		g_signal_emit (context, signals[CHANGED], 0);
898 }
899 
900 gint
e_rule_context_get_rank_rule(ERuleContext * context,EFilterRule * rule,const gchar * source)901 e_rule_context_get_rank_rule (ERuleContext *context,
902                               EFilterRule *rule,
903                               const gchar *source)
904 {
905 	GList *node;
906 	gint i = 0;
907 
908 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
909 	g_return_val_if_fail (E_IS_FILTER_RULE (rule), -1);
910 
911 	node = context->rules;
912 	while (node) {
913 		EFilterRule *r = node->data;
914 
915 		if (r == rule)
916 			return i;
917 
918 		if (source == NULL || (r->source && strcmp (r->source, source) == 0))
919 			i++;
920 
921 		node = node->next;
922 	}
923 
924 	return -1;
925 }
926 
927 EFilterRule *
e_rule_context_find_rank_rule(ERuleContext * context,gint rank,const gchar * source)928 e_rule_context_find_rank_rule (ERuleContext *context,
929                                gint rank,
930                                const gchar *source)
931 {
932 	GList *node;
933 	gint i = 0;
934 
935 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
936 
937 	node = context->rules;
938 	while (node) {
939 		EFilterRule *r = node->data;
940 
941 		if (source == NULL || (r->source && strcmp (r->source, source) == 0)) {
942 			if (rank == i)
943 				return r;
944 			i++;
945 		}
946 
947 		node = node->next;
948 	}
949 
950 	return NULL;
951 }
952 
953 GList *
e_rule_context_rename_uri(ERuleContext * context,const gchar * old_uri,const gchar * new_uri,GCompareFunc compare)954 e_rule_context_rename_uri (ERuleContext *context,
955                            const gchar *old_uri,
956                            const gchar *new_uri,
957                            GCompareFunc compare)
958 {
959 	ERuleContextClass *class;
960 
961 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
962 	g_return_val_if_fail (old_uri != NULL, NULL);
963 	g_return_val_if_fail (new_uri != NULL, NULL);
964 	g_return_val_if_fail (compare != NULL, NULL);
965 
966 	class = E_RULE_CONTEXT_GET_CLASS (context);
967 	g_return_val_if_fail (class != NULL, NULL);
968 
969 	/* This method is optional. */
970 	if (class->rename_uri == NULL)
971 		return NULL;
972 
973 	return class->rename_uri (context, old_uri, new_uri, compare);
974 }
975 
976 GList *
e_rule_context_delete_uri(ERuleContext * context,const gchar * uri,GCompareFunc compare)977 e_rule_context_delete_uri (ERuleContext *context,
978                            const gchar *uri,
979                            GCompareFunc compare)
980 {
981 	ERuleContextClass *class;
982 
983 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
984 	g_return_val_if_fail (uri != NULL, NULL);
985 	g_return_val_if_fail (compare != NULL, NULL);
986 
987 	class = E_RULE_CONTEXT_GET_CLASS (context);
988 	g_return_val_if_fail (class != NULL, NULL);
989 
990 	/* This method is optional. */
991 	if (class->delete_uri == NULL)
992 		return NULL;
993 
994 	return class->delete_uri (context, uri, compare);
995 }
996 
997 void
e_rule_context_free_uri_list(ERuleContext * context,GList * uris)998 e_rule_context_free_uri_list (ERuleContext *context,
999                               GList *uris)
1000 {
1001 	g_return_if_fail (E_IS_RULE_CONTEXT (context));
1002 
1003 	/* TODO: should be virtual */
1004 
1005 	g_list_foreach (uris, (GFunc) g_free, NULL);
1006 	g_list_free (uris);
1007 }
1008 
1009 /**
1010  * e_rule_context_new_element:
1011  * @context:
1012  * @name:
1013  *
1014  * create a new filter element based on name.
1015  *
1016  * Return value:
1017  **/
1018 EFilterElement *
e_rule_context_new_element(ERuleContext * context,const gchar * name)1019 e_rule_context_new_element (ERuleContext *context,
1020                             const gchar *name)
1021 {
1022 	ERuleContextClass *class;
1023 
1024 	g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
1025 	g_return_val_if_fail (name != NULL, NULL);
1026 
1027 	class = E_RULE_CONTEXT_GET_CLASS (context);
1028 	g_return_val_if_fail (class != NULL, NULL);
1029 	g_return_val_if_fail (class->new_element != NULL, NULL);
1030 
1031 	return class->new_element (context, name);
1032 }
1033