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