1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 * Not Zed <notzed@lostzed.mmc.com.au>
17 * Jepartrey Stedfast <fejj@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #include "evolution-config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30
31 #include "e-filter-file.h"
32 #include "e-filter-input.h"
33 #include "e-filter-part.h"
34 #include "e-rule-context.h"
35
G_DEFINE_TYPE(EFilterPart,e_filter_part,G_TYPE_OBJECT)36 G_DEFINE_TYPE (
37 EFilterPart,
38 e_filter_part,
39 G_TYPE_OBJECT)
40
41 static void
42 filter_part_finalize (GObject *object)
43 {
44 EFilterPart *part = E_FILTER_PART (object);
45
46 g_list_foreach (part->elements, (GFunc) g_object_unref, NULL);
47 g_list_free (part->elements);
48
49 g_free (part->name);
50 g_free (part->title);
51 g_free (part->code);
52
53 /* Chain up to parent's finalize() method. */
54 G_OBJECT_CLASS (e_filter_part_parent_class)->finalize (object);
55 }
56
57 static void
e_filter_part_class_init(EFilterPartClass * class)58 e_filter_part_class_init (EFilterPartClass *class)
59 {
60 GObjectClass *object_class;
61
62 object_class = G_OBJECT_CLASS (class);
63 object_class->finalize = filter_part_finalize;
64 }
65
66 static void
e_filter_part_init(EFilterPart * part)67 e_filter_part_init (EFilterPart *part)
68 {
69 }
70
71 /**
72 * e_filter_part_new:
73 *
74 * Create a new EFilterPart object.
75 *
76 * Return value: A new #EFilterPart object.
77 **/
78 EFilterPart *
e_filter_part_new(void)79 e_filter_part_new (void)
80 {
81 return g_object_new (E_TYPE_FILTER_PART, NULL);
82 }
83
84 gboolean
e_filter_part_validate(EFilterPart * part,EAlert ** alert)85 e_filter_part_validate (EFilterPart *part,
86 EAlert **alert)
87 {
88 GList *link;
89
90 g_return_val_if_fail (E_IS_FILTER_PART (part), FALSE);
91
92 /* The part is valid if all of its elements are valid. */
93 for (link = part->elements; link != NULL; link = g_list_next (link)) {
94 EFilterElement *element = link->data;
95
96 if (!e_filter_element_validate (element, alert))
97 return FALSE;
98 }
99
100 return TRUE;
101 }
102
103 gint
e_filter_part_eq(EFilterPart * part_a,EFilterPart * part_b)104 e_filter_part_eq (EFilterPart *part_a,
105 EFilterPart *part_b)
106 {
107 GList *link_a, *link_b;
108
109 g_return_val_if_fail (E_IS_FILTER_PART (part_a), FALSE);
110 g_return_val_if_fail (E_IS_FILTER_PART (part_b), FALSE);
111
112 if (g_strcmp0 (part_a->name, part_b->name) != 0)
113 return FALSE;
114
115 if (g_strcmp0 (part_a->title, part_b->title) != 0)
116 return FALSE;
117
118 if (g_strcmp0 (part_a->code, part_b->code) != 0)
119 return FALSE;
120
121 link_a = part_a->elements;
122 link_b = part_b->elements;
123
124 while (link_a != NULL && link_b != NULL) {
125 EFilterElement *element_a = link_a->data;
126 EFilterElement *element_b = link_b->data;
127
128 if (!e_filter_element_eq (element_a, element_b))
129 return FALSE;
130
131 link_a = g_list_next (link_a);
132 link_b = g_list_next (link_b);
133 }
134
135 if (link_a != NULL || link_b != NULL)
136 return FALSE;
137
138 return TRUE;
139 }
140
141 gint
e_filter_part_xml_create(EFilterPart * part,xmlNodePtr node,ERuleContext * context)142 e_filter_part_xml_create (EFilterPart *part,
143 xmlNodePtr node,
144 ERuleContext *context)
145 {
146 xmlNodePtr n;
147 gchar *type, *str;
148 EFilterElement *el;
149
150 g_return_val_if_fail (E_IS_FILTER_PART (part), 0);
151 g_return_val_if_fail (node != NULL, 0);
152 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), 0);
153
154 str = (gchar *) xmlGetProp (node, (xmlChar *)"name");
155 part->name = g_strdup (str);
156 if (str)
157 xmlFree (str);
158
159 n = node->children;
160 while (n) {
161 if (!strcmp ((gchar *) n->name, "input")) {
162 type = (gchar *) xmlGetProp (n, (xmlChar *)"type");
163 if (type != NULL
164 && (el = e_rule_context_new_element (context, type)) != NULL) {
165 e_filter_element_xml_create (el, n);
166 xmlFree (type);
167 part->elements = g_list_append (part->elements, el);
168 } else {
169 g_warning ("Invalid xml format, missing/unknown input type");
170 }
171 } else if (!strcmp ((gchar *) n->name, "title") ||
172 !strcmp ((gchar *) n->name, "_title")) {
173 if (!part->title) {
174 str = (gchar *) xmlNodeGetContent (n);
175 part->title = g_strdup (str);
176 if (str)
177 xmlFree (str);
178 }
179 } else if (!strcmp ((gchar *) n->name, "code")) {
180 if (!part->code) {
181 str = (gchar *) xmlNodeGetContent (n);
182 part->code = g_strdup (str);
183 if (str)
184 xmlFree (str);
185 }
186 } else if (n->type == XML_ELEMENT_NODE) {
187 g_warning ("Unknown part element in xml: %s\n", n->name);
188 }
189 n = n->next;
190 }
191
192 return 0;
193 }
194
195 xmlNodePtr
e_filter_part_xml_encode(EFilterPart * part)196 e_filter_part_xml_encode (EFilterPart *part)
197 {
198 xmlNodePtr node;
199 GList *link;
200
201 g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
202
203 node = xmlNewNode (NULL, (xmlChar *)"part");
204 xmlSetProp (node, (xmlChar *)"name", (xmlChar *) part->name);
205
206 for (link = part->elements; link != NULL; link = g_list_next (link)) {
207 EFilterElement *element = link->data;
208 xmlNodePtr value;
209
210 value = e_filter_element_xml_encode (element);
211 xmlAddChild (node, value);
212 }
213
214 return node;
215 }
216
217 gint
e_filter_part_xml_decode(EFilterPart * part,xmlNodePtr node)218 e_filter_part_xml_decode (EFilterPart *part,
219 xmlNodePtr node)
220 {
221 xmlNodePtr child;
222
223 g_return_val_if_fail (E_IS_FILTER_PART (part), -1);
224 g_return_val_if_fail (node != NULL, -1);
225
226 for (child = node->children; child != NULL; child = child->next) {
227 EFilterElement *element;
228 xmlChar *name;
229
230 if (strcmp ((gchar *) child->name, "value") != 0)
231 continue;
232
233 name = xmlGetProp (child, (xmlChar *) "name");
234 element = e_filter_part_find_element (part, (gchar *) name);
235 xmlFree (name);
236
237 if (element != NULL)
238 e_filter_element_xml_decode (element, child);
239 }
240
241 return 0;
242 }
243
244 EFilterPart *
e_filter_part_clone(EFilterPart * part)245 e_filter_part_clone (EFilterPart *part)
246 {
247 EFilterPart *clone;
248 GList *link;
249
250 g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
251
252 clone = g_object_new (G_OBJECT_TYPE (part), NULL, NULL);
253 clone->name = g_strdup (part->name);
254 clone->title = g_strdup (part->title);
255 clone->code = g_strdup (part->code);
256
257 for (link = part->elements; link != NULL; link = g_list_next (link)) {
258 EFilterElement *element = link->data;
259 EFilterElement *clone_element;
260
261 clone_element = e_filter_element_clone (element);
262 clone->elements = g_list_append (clone->elements, clone_element);
263 }
264
265 return clone;
266 }
267
268 /* only copies values of matching parts in the right order */
269 void
e_filter_part_copy_values(EFilterPart * dst_part,EFilterPart * src_part)270 e_filter_part_copy_values (EFilterPart *dst_part,
271 EFilterPart *src_part)
272 {
273 GList *dst_link, *src_link;
274
275 g_return_if_fail (E_IS_FILTER_PART (dst_part));
276 g_return_if_fail (E_IS_FILTER_PART (src_part));
277
278 /* NOTE: we go backwards, it just works better that way */
279
280 /* for each source type, search the dest type for
281 * a matching type in the same order */
282 src_link = g_list_last (src_part->elements);
283 dst_link = g_list_last (dst_part->elements);
284
285 while (src_link != NULL && dst_link != NULL) {
286 EFilterElement *src_element = src_link->data;
287 GList *link = dst_link;
288
289 while (link != NULL) {
290 EFilterElement *dst_element = link->data;
291 GType dst_type = G_OBJECT_TYPE (dst_element);
292 GType src_type = G_OBJECT_TYPE (src_element);
293
294 if (dst_type == src_type) {
295 e_filter_element_copy_value (
296 dst_element, src_element);
297 dst_link = g_list_previous (link);
298 break;
299 }
300
301 link = g_list_previous (link);
302 }
303
304 src_link = g_list_previous (src_link);
305 }
306 }
307
308 EFilterElement *
e_filter_part_find_element(EFilterPart * part,const gchar * name)309 e_filter_part_find_element (EFilterPart *part,
310 const gchar *name)
311 {
312 GList *link;
313
314 g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
315
316 if (name == NULL)
317 return NULL;
318
319 for (link = part->elements; link != NULL; link = g_list_next (link)) {
320 EFilterElement *element = link->data;
321
322 if (g_strcmp0 (element->name, name) == 0)
323 return element;
324 }
325
326 return NULL;
327 }
328
329 GtkWidget *
e_filter_part_get_widget(EFilterPart * part)330 e_filter_part_get_widget (EFilterPart *part)
331 {
332 GtkWidget *hbox;
333 GList *link;
334
335 g_return_val_if_fail (E_IS_FILTER_PART (part), NULL);
336
337 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
338
339 for (link = part->elements; link != NULL; link = g_list_next (link)) {
340 EFilterElement *element = link->data;
341 GtkWidget *widget;
342
343 widget = e_filter_element_get_widget (element);
344 if (widget != NULL) {
345 gboolean expand_fill = E_IS_FILTER_FILE (element) || E_IS_FILTER_INPUT (element);
346 gtk_box_pack_start (
347 GTK_BOX (hbox), widget, expand_fill, expand_fill, 3);
348 }
349 }
350
351 gtk_widget_show_all (hbox);
352
353 return hbox;
354 }
355
356 void
e_filter_part_describe(EFilterPart * part,GString * out)357 e_filter_part_describe (EFilterPart *part,
358 GString *out)
359 {
360 GList *link;
361
362 g_return_if_fail (E_IS_FILTER_PART (part));
363 g_return_if_fail (out != NULL);
364
365 g_string_append (out, _(part->title));
366
367 for (link = part->elements; link != NULL; link = g_list_next (link)) {
368 EFilterElement *element = link->data;
369
370 g_string_append_c (out, ' ');
371 e_filter_element_describe (element, out);
372 }
373 }
374
375 /**
376 * e_filter_part_build_code:
377 * @part:
378 * @out:
379 *
380 * Outputs the code of a part.
381 **/
382 void
e_filter_part_build_code(EFilterPart * part,GString * out)383 e_filter_part_build_code (EFilterPart *part,
384 GString *out)
385 {
386 GList *link;
387
388 g_return_if_fail (E_IS_FILTER_PART (part));
389 g_return_if_fail (out != NULL);
390
391 if (part->code != NULL)
392 e_filter_part_expand_code (part, part->code, out);
393
394 for (link = part->elements; link != NULL; link = g_list_next (link)) {
395 EFilterElement *element = link->data;
396 e_filter_element_build_code (element, out, part);
397 }
398 }
399
400 /**
401 * e_filter_part_build_code_list:
402 * @list:
403 * @out:
404 *
405 * Construct a list of the filter parts code into
406 * a single string.
407 **/
408 void
e_filter_part_build_code_list(GList * list,GString * out)409 e_filter_part_build_code_list (GList *list,
410 GString *out)
411 {
412 GList *link;
413
414 g_return_if_fail (out != NULL);
415
416 for (link = list; link != NULL; link = g_list_next (link)) {
417 EFilterPart *part = link->data;
418
419 e_filter_part_build_code (part, out);
420 g_string_append (out, "\n ");
421 }
422 }
423
424 /**
425 * e_filter_part_find_list:
426 * @list:
427 * @name:
428 *
429 * Find a filter part stored in a list.
430 *
431 * Return value:
432 **/
433 EFilterPart *
e_filter_part_find_list(GList * list,const gchar * name)434 e_filter_part_find_list (GList *list,
435 const gchar *name)
436 {
437 GList *link;
438
439 g_return_val_if_fail (name != NULL, NULL);
440
441 for (link = list; link != NULL; link = g_list_next (link)) {
442 EFilterPart *part = link->data;
443
444 if (g_strcmp0 (part->name, name) == 0)
445 return part;
446 }
447
448 return NULL;
449 }
450
451 /**
452 * e_filter_part_next_list:
453 * @list:
454 * @last: The last item retrieved, or NULL to start
455 * from the beginning of the list.
456 *
457 * Iterate through a filter part list.
458 *
459 * Return value: The next value in the list, or NULL if the
460 * list is expired.
461 **/
462 EFilterPart *
e_filter_part_next_list(GList * list,EFilterPart * last)463 e_filter_part_next_list (GList *list,
464 EFilterPart *last)
465 {
466 GList *link = list;
467
468 if (last != NULL) {
469 link = g_list_find (list, last);
470 if (link == NULL)
471 link = list;
472 else
473 link = link->next;
474 }
475
476 return (link != NULL) ? link->data : NULL;
477 }
478
479 /**
480 * e_filter_part_expand_code:
481 * @part:
482 * @str:
483 * @out:
484 *
485 * Expands the variables in string @str based on the values of the part.
486 **/
487 void
e_filter_part_expand_code(EFilterPart * part,const gchar * source,GString * out)488 e_filter_part_expand_code (EFilterPart *part,
489 const gchar *source,
490 GString *out)
491 {
492 const gchar *newstart, *start, *end;
493 gchar *name = g_alloca (32);
494 gint len, namelen = 32;
495
496 g_return_if_fail (E_IS_FILTER_PART (part));
497 g_return_if_fail (source != NULL);
498 g_return_if_fail (out != NULL);
499
500 start = source;
501
502 while (start && (newstart = strstr (start, "${"))
503 && (end = strstr (newstart + 2, "}"))) {
504 EFilterElement *element;
505
506 len = end - newstart - 2;
507 if (len + 1 > namelen) {
508 namelen = (len + 1) * 2;
509 name = g_alloca (namelen);
510 }
511 memcpy (name, newstart + 2, len);
512 name[len] = 0;
513
514 element = e_filter_part_find_element (part, name);
515 if (element != NULL) {
516 g_string_append_printf (out, "%.*s", (gint)(newstart - start), start);
517 e_filter_element_format_sexp (element, out);
518 #if 0
519 } else if ((val = g_hash_table_lookup (part->globals, name))) {
520 g_string_append_printf (out, "%.*s", newstart - start, start);
521 camel_sexp_encode_string (out, val);
522 #endif
523 } else {
524 g_string_append_printf (out, "%.*s", (gint)(end - start + 1), start);
525 }
526 start = end + 1;
527 }
528
529 g_string_append (out, start);
530 }
531