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