No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the 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 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29
30 #include <gtk/gtk.h>
31 #include <glib/gi18n.h>
32
33 #include <libevolution-utils/e-alert.h>
34
35 #include <libemail-engine/e-mail-folder-utils.h>
36
37 #include "em-vfolder-context.h"
38 #include "em-vfolder-rule.h"
39
40 #define EM_VFOLDER_RULE_GET_PRIVATE(obj) \
41 (G_TYPE_INSTANCE_GET_PRIVATE \
42 ((obj), EM_TYPE_VFOLDER_RULE, EMVFolderRulePrivate))
43
44 #define EM_VFOLDER_RULE_GET_PRIVATE(obj) \
45 (G_TYPE_INSTANCE_GET_PRIVATE \
46 ((obj), EM_TYPE_VFOLDER_RULE, EMVFolderRulePrivate))
47
48 struct _EMVFolderRulePrivate {
49 em_vfolder_rule_with_t with;
50 GQueue sources; /* uri's of the source folders */
51 gboolean autoupdate;
52 GHashTable *include_subfolders;
53 };
54
55 static gint validate (EFilterRule *, EAlert **alert);
56 static gint vfolder_eq (EFilterRule *fr, EFilterRule *cm);
57 static xmlNodePtr xml_encode (EFilterRule *);
58 static gint xml_decode (EFilterRule *, xmlNodePtr, ERuleContext *f);
59 static void rule_copy (EFilterRule *dest, EFilterRule *src);
60 static GtkWidget *get_widget (EFilterRule *fr, ERuleContext *f);
61
62 /* DO NOT internationalise these strings */
63 static const gchar *with_names[] = {
64 "specific",
65 "local_remote_active",
66 "remote_active",
67 "local"
68 };
69
70 G_DEFINE_TYPE (
71 EMVFolderRule,
72 em_vfolder_rule,
73 E_TYPE_FILTER_RULE)
74
75 static void
76 vfolder_rule_finalize (GObject *object)
77 {
78 EMVFolderRule *rule = EM_VFOLDER_RULE (object);
79 gchar *uri;
80
81 while ((uri = g_queue_pop_head (&rule->priv->sources)) != NULL)
82 g_free (uri);
83
84 g_hash_table_destroy (rule->priv->include_subfolders);
85
86 /* Chain up to parent's finalize() method. */
87 G_OBJECT_CLASS (em_vfolder_rule_parent_class)->finalize (object);
88 }
89
90 static void
91 em_vfolder_rule_class_init (EMVFolderRuleClass *class)
92 {
93 GObjectClass *object_class;
94 EFilterRuleClass *filter_rule_class;
95
96 g_type_class_add_private (class, sizeof (EMVFolderRulePrivate));
97
98 object_class = G_OBJECT_CLASS (class);
99 object_class->finalize = vfolder_rule_finalize;
100
101 filter_rule_class = E_FILTER_RULE_CLASS (class);
102 filter_rule_class->validate = validate;
103 filter_rule_class->eq = vfolder_eq;
104 filter_rule_class->xml_encode = xml_encode;
105 filter_rule_class->xml_decode = xml_decode;
106 filter_rule_class->copy = rule_copy;
107 filter_rule_class->get_widget = get_widget;
108 }
109
110 static void
111 em_vfolder_rule_init (EMVFolderRule *rule)
112 {
113 rule->priv = EM_VFOLDER_RULE_GET_PRIVATE (rule);
114 rule->priv->with = EM_VFOLDER_RULE_WITH_SPECIFIC;
115 rule->priv->autoupdate = TRUE;
116 /* it's using pointers from priv::sources, and those
117 * included has include_subfolders set to true */
118 rule->priv->include_subfolders = g_hash_table_new (g_direct_hash, g_direct_equal);
119
120 rule->rule.source = g_strdup ("incoming");
121 }
122
123 EFilterRule *
124 em_vfolder_rule_new (void)
125 {
126 return g_object_new (
127 EM_TYPE_VFOLDER_RULE, NULL);
128 }
129
130 void
131 em_vfolder_rule_add_source (EMVFolderRule *rule,
132 const gchar *uri)
133 {
134 g_return_if_fail (EM_IS_VFOLDER_RULE (rule));
135 g_return_if_fail (uri);
136
137 g_queue_push_tail (&rule->priv->sources, g_strdup (uri));
138
139 e_filter_rule_emit_changed (E_FILTER_RULE (rule));
140 }
141
142 const gchar *
143 em_vfolder_rule_find_source (EMVFolderRule *rule,
144 const gchar *uri)
145 {
146 GList *link;
147
148 g_return_val_if_fail (EM_IS_VFOLDER_RULE (rule), NULL);
149
150 /* only does a simple string or address comparison, should
151 * probably do a decoded url comparison */
152 link = g_queue_find_custom (
153 &rule->priv->sources, uri, (GCompareFunc) strcmp);
154
155 return (link != NULL) ? link->data : NULL;
156 }
157
158 void
159 em_vfolder_rule_remove_source (EMVFolderRule *rule,
160 const gchar *uri)
161 {
162 gchar *found;
163
164 g_return_if_fail (EM_IS_VFOLDER_RULE (rule));
165
166 found =(gchar *) em_vfolder_rule_find_source (rule, uri);
167 if (found != NULL) {
168 g_queue_remove (&rule->priv->sources, found);
169 g_hash_table_remove (rule->priv->include_subfolders, found);
170 g_free (found);
171 e_filter_rule_emit_changed (E_FILTER_RULE (rule));
172 }
173 }
174
175 const gchar *
176 em_vfolder_rule_next_source (EMVFolderRule *rule,
177 const gchar *last)
178 {
179 GList *link;
180
181 if (last == NULL) {
182 link = g_queue_peek_head_link (&rule->priv->sources);
183 } else {
184 link = g_queue_find (&rule->priv->sources, last);
185 if (link == NULL)
186 link = g_queue_peek_head_link (&rule->priv->sources);
187 else
188 link = g_list_next (link);
189 }
190
191 return (link != NULL) ? link->data : NULL;
192 }
193
194 GQueue *
195 em_vfolder_rule_get_sources (EMVFolderRule *rule)
196 {
197 g_return_val_if_fail (rule != NULL, NULL);
198
199 return &rule->priv->sources;
200 }
201
202 static gboolean
203 check_queue_has_key (gpointer key,
204 gpointer value,
205 gpointer user_data)
206 {
207 EMVFolderRule *rule = user_data;
208
209 g_return_val_if_fail (rule != NULL, FALSE);
210
211 return g_queue_find (&rule->priv->sources, key) == NULL;
212 }
213
214 void
215 em_vfolder_rule_sources_changed (EMVFolderRule *rule)
216 {
217 g_return_if_fail (rule != NULL);
218
219 g_hash_table_foreach_remove (rule->priv->include_subfolders,
220 check_queue_has_key, rule);
221 }
222
223 gboolean
224 em_vfolder_rule_source_get_include_subfolders (EMVFolderRule *rule,
225 const gchar *source)
226 {
227 g_return_val_if_fail (rule != NULL, FALSE);
228 g_return_val_if_fail (source != NULL, FALSE);
229
230 source = em_vfolder_rule_find_source (rule, source);
231
232 return source && g_hash_table_lookup (rule->priv->include_subfolders, source);
233 }
234
235 void
236 em_vfolder_rule_source_set_include_subfolders (EMVFolderRule *rule,
237 const gchar *source,
238 gboolean include_subfolders)
239 {
240 g_return_if_fail (rule != NULL);
241 g_return_if_fail (source != NULL);
242
243 source = em_vfolder_rule_find_source (rule, source);
244 g_return_if_fail (source != NULL);
245
246 if (include_subfolders)
247 g_hash_table_insert (rule->priv->include_subfolders, (gpointer) source, GINT_TO_POINTER (1));
248 else
249 g_hash_table_remove (rule->priv->include_subfolders, (gpointer) source);
250 }
251
252 void
253 em_vfolder_rule_set_with (EMVFolderRule *rule,
254 em_vfolder_rule_with_t with)
255 {
256 g_return_if_fail (rule != NULL);
257
258 rule->priv->with = with;
259 }
260
261 em_vfolder_rule_with_t
262 em_vfolder_rule_get_with (EMVFolderRule *rule)
263 {
264 g_return_val_if_fail (rule != NULL, FALSE);
265
266 return rule->priv->with;
267 }
268
269 void
270 em_vfolder_rule_set_autoupdate (EMVFolderRule *rule,
271 gboolean autoupdate)
272 {
273 g_return_if_fail (rule != NULL);
274
275 rule->priv->autoupdate = autoupdate;
276 }
277
278 gboolean
279 em_vfolder_rule_get_autoupdate (EMVFolderRule *rule)
280 {
281 g_return_val_if_fail (rule != NULL, EM_VFOLDER_RULE_WITH_SPECIFIC);
282
283 return rule->priv->autoupdate;
284 }
285
286 static gint
287 validate (EFilterRule *fr,
288 EAlert **alert)
289 {
290 g_return_val_if_fail (fr != NULL, 0);
291 g_warn_if_fail (alert == NULL || *alert == NULL);
292
293 if (!fr->name || !*fr->name) {
294 if (alert)
295 *alert = e_alert_new ("mail:no-name-vfolder", NULL);
296 return 0;
297 }
298
299 /* We have to have at least one source set in the "specific" case.
300 * Do not translate this string! */
301 if (((EMVFolderRule *) fr)->priv->with == EM_VFOLDER_RULE_WITH_SPECIFIC &&
302 g_queue_is_empty (&((EMVFolderRule *) fr)->priv->sources)) {
303 if (alert)
304 *alert = e_alert_new ("mail:vfolder-no-source", NULL);
305 return 0;
306 }
307
308 return E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->validate (fr, alert);
309 }
310
311 static gint
312 queue_eq (GQueue *queue_a,
313 GQueue *queue_b)
314 {
315 GList *link_a;
316 GList *link_b;
317 gint truth = TRUE;
318
319 link_a = g_queue_peek_head_link (queue_a);
320 link_b = g_queue_peek_head_link (queue_b);
321
322 while (truth && link_a != NULL && link_b != NULL) {
323 gchar *uri_a = link_a->data;
324 gchar *uri_b = link_b->data;
325
326 truth = (strcmp (uri_a, uri_b)== 0);
327
328 link_a = g_list_next (link_a);
329 link_b = g_list_next (link_b);
330 }
331
332 return truth && link_a == NULL && link_b == NULL;
333 }
334
335 static gint
336 vfolder_eq (EFilterRule *fr,
337 EFilterRule *cm)
338 {
339 return E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->eq (fr, cm)
340 && queue_eq (
341 &((EMVFolderRule *) fr)->priv->sources,
342 &((EMVFolderRule *) cm)->priv->sources);
343 }
344
345 static xmlNodePtr
346 xml_encode (EFilterRule *fr)
347 {
348 EMVFolderRule *vr =(EMVFolderRule *) fr;
349 xmlNodePtr node, set, work;
350 GList *head, *link;
351
352 node = E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->xml_encode (fr);
353 g_return_val_if_fail (node != NULL, NULL);
354 g_return_val_if_fail (vr->priv->with < G_N_ELEMENTS (with_names), NULL);
355
356 set = xmlNewNode (NULL, (const guchar *)"sources");
357 xmlAddChild (node, set);
358 xmlSetProp (set, (const guchar *)"with", (guchar *) with_names[vr->priv->with]);
359 xmlSetProp (set, (const guchar *)"autoupdate", (guchar *) (vr->priv->autoupdate ? "true" : "false"));
360
361 head = g_queue_peek_head_link (&vr->priv->sources);
362 for (link = head; link != NULL; link = g_list_next (link)) {
363 const gchar *uri = link->data;
364
365 work = xmlNewNode (NULL, (const guchar *) "folder");
366 xmlSetProp (work, (const guchar *) "uri", (guchar *) uri);
367 xmlSetProp (work, (const guchar *) "include-subfolders", (guchar *)
368 (em_vfolder_rule_source_get_include_subfolders (vr, uri) ? "true" : "false"));
369 xmlAddChild (set, work);
370 }
371
372 return node;
373 }
374
375 static void
376 set_with (EMVFolderRule *vr,
377 const gchar *name)
378 {
379 gint i;
380
381 for (i = 0; i < G_N_ELEMENTS (with_names); i++) {
382 if (!strcmp (name, with_names[i])) {
383 vr->priv->with = i;
384 return;
385 }
386 }
387
388 vr->priv->with = 0;
389 }
390
391 static gint
392 xml_decode (EFilterRule *fr,
393 xmlNodePtr node,
394 ERuleContext *f)
395 {
396 xmlNodePtr set, work;
397 gint result;
398 EMVFolderRule *vr =(EMVFolderRule *) fr;
399 gchar *tmp;
400
401 result = E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->
402 xml_decode (fr, node, f);
403 if (result != 0)
404 return result;
405
406 /* handle old format file, vfolder source is in filterrule */
407 if (strcmp (fr->source, "incoming")!= 0) {
408 set_with (vr, fr->source);
409 g_free (fr->source);
410 fr->source = g_strdup ("incoming");
411 }
412
413 set = node->children;
414 while (set) {
415 if (!strcmp ((gchar *) set->name, "sources")) {
416 tmp = (gchar *) xmlGetProp (set, (const guchar *)"with");
417 if (tmp) {
418 set_with (vr, tmp);
419 xmlFree (tmp);
420 }
421 tmp = (gchar *) xmlGetProp (set, (const guchar *) "autoupdate");
422 if (tmp) {
423 vr->priv->autoupdate = g_str_equal (tmp, "true");
424 xmlFree (tmp);
425 }
426 work = set->children;
427 while (work) {
428 if (!strcmp ((gchar *) work->name, "folder")) {
429 tmp = (gchar *) xmlGetProp (work, (const guchar *)"uri");
430 if (tmp) {
431 gchar *include_subfolders;
432
433 g_queue_push_tail (&vr->priv->sources, g_strdup (tmp));
434
435 include_subfolders = (gchar *) xmlGetProp (work, (const guchar *) "include-subfolders");
436 if (include_subfolders) {
437 em_vfolder_rule_source_set_include_subfolders (
438 vr,
439 tmp, g_str_equal (include_subfolders, "true"));
440 xmlFree (include_subfolders);
441 }
442
443 xmlFree (tmp);
444 }
445 }
446 work = work->next;
447 }
448 }
449 set = set->next;
450 }
451 return 0;
452 }
453
454 static void
455 rule_copy (EFilterRule *dest,
456 EFilterRule *src)
457 {
458 EMVFolderRule *vdest, *vsrc;
459 GList *head, *link;
460 gchar *uri;
461
462 vdest =(EMVFolderRule *) dest;
463 vsrc =(EMVFolderRule *) src;
464
465 while ((uri = g_queue_pop_head (&vdest->priv->sources)) != NULL)
466 g_free (uri);
467
468 em_vfolder_rule_sources_changed (vdest);
469
470 head = g_queue_peek_head_link (&vsrc->priv->sources);
471 for (link = head; link != NULL; link = g_list_next (link)) {
472 const gchar *uri = link->data;
473 g_queue_push_tail (&vdest->priv->sources, g_strdup (uri));
474
475 em_vfolder_rule_source_set_include_subfolders (
476 vdest, uri,
477 em_vfolder_rule_source_get_include_subfolders (vsrc, uri));
478 }
479
480 vdest->priv->with = vsrc->priv->with;
481 vdest->priv->autoupdate = vsrc->priv->autoupdate;
482
483 E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->copy (dest, src);
484 }
485
486 static GtkWidget *
487 get_widget (EFilterRule *fr,
488 ERuleContext *rc)
489 {
490 GtkWidget *widget;
491
492 widget = E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->
493 get_widget (fr, rc);
494
495 return widget;
496 }