evolution-3.6.4/mail/mail-autofilter.c

No issues found

  1 /*
  2  * Code for autogenerating rules or filters from a message.
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  * Authors:
 18  *		Michael Zucchi <notzed@ximian.com>
 19  *		Ettore Perazzoli <ettore@ximian.com>
 20  *
 21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 22  */
 23 #ifdef HAVE_CONFIG_H
 24 #include <config.h>
 25 #endif
 26 
 27 #include <ctype.h>
 28 #include <string.h>
 29 
 30 #include <glib/gi18n.h>
 31 
 32 #include <libemail-engine/e-mail-folder-utils.h>
 33 #include <libemail-engine/e-mail-session.h>
 34 
 35 #include "mail-vfolder-ui.h"
 36 #include "mail-autofilter.h"
 37 #include "em-utils.h"
 38 #include "libevolution-utils/e-alert-dialog.h"
 39 #include "e-util/e-util-private.h"
 40 
 41 #include "em-vfolder-editor-context.h"
 42 #include "em-vfolder-editor-rule.h"
 43 #include "em-vfolder-editor.h"
 44 
 45 #include "em-filter-context.h"
 46 #include "em-filter-rule.h"
 47 #include "em-filter-editor.h"
 48 #include "filter/e-filter-option.h"
 49 #include "filter/e-filter-input.h"
 50 
 51 #define d(x)
 52 
 53 static void
 54 rule_match_recipients (ERuleContext *context,
 55                        EFilterRule *rule,
 56                        CamelInternetAddress *iaddr)
 57 {
 58 	EFilterPart *part;
 59 	EFilterElement *element;
 60 	gint i;
 61 	const gchar *real, *addr;
 62 	gchar *namestr;
 63 
 64 	/* address types etc should handle multiple values */
 65 	for (i = 0; camel_internet_address_get (iaddr, i, &real, &addr); i++) {
 66 		part = e_rule_context_create_part (context, "to");
 67 		e_filter_rule_add_part ((EFilterRule *) rule, part);
 68 		element = e_filter_part_find_element (part, "recipient-type");
 69 		e_filter_option_set_current ((EFilterOption *) element, "contains");
 70 		element = e_filter_part_find_element (part, "recipient");
 71 		e_filter_input_set_value ((EFilterInput *) element, addr);
 72 
 73 		namestr = g_strdup_printf (_("Mail to %s"), real && real[0] ? real : addr);
 74 		e_filter_rule_set_name (rule, namestr);
 75 		g_free (namestr);
 76 	}
 77 }
 78 
 79 /* remove 're' part of a subject */
 80 static const gchar *
 81 strip_re (const gchar *subject)
 82 {
 83 	const guchar *s, *p;
 84 
 85 	s = (guchar *) subject;
 86 
 87 	while (*s) {
 88 		while (isspace (*s))
 89 			s++;
 90 		if (s[0] == 0)
 91 			break;
 92 		if ((s[0] == 'r' || s[0] == 'R')
 93 		    && (s[1] == 'e' || s[1] == 'E')) {
 94 			p = s + 2;
 95 			while (isdigit (*p) || (ispunct (*p) && (*p != ':')))
 96 				p++;
 97 			if (*p == ':') {
 98 				s = p + 1;
 99 			} else
100 				break;
101 		} else
102 			break;
103 	}
104 
105 	return (gchar *) s;
106 }
107 
108 static void
109 rule_add_subject (ERuleContext *context,
110                   EFilterRule *rule,
111                   const gchar *text)
112 {
113 	EFilterPart *part;
114 	EFilterElement *element;
115 
116 	/* dont match on empty strings ever */
117 	if (*text == 0)
118 		return;
119 	part = e_rule_context_create_part (context, "subject");
120 	e_filter_rule_add_part ((EFilterRule *) rule, part);
121 	element = e_filter_part_find_element (part, "subject-type");
122 	e_filter_option_set_current ((EFilterOption *) element, "contains");
123 	element = e_filter_part_find_element (part, "subject");
124 	e_filter_input_set_value ((EFilterInput *) element, text);
125 }
126 
127 static void
128 rule_add_sender (ERuleContext *context,
129                  EFilterRule *rule,
130                  const gchar *text)
131 {
132 	EFilterPart *part;
133 	EFilterElement *element;
134 
135 	/* dont match on empty strings ever */
136 	if (*text == 0)
137 		return;
138 	part = e_rule_context_create_part (context, "sender");
139 	e_filter_rule_add_part ((EFilterRule *) rule, part);
140 	element = e_filter_part_find_element (part, "sender-type");
141 	e_filter_option_set_current ((EFilterOption *) element, "contains");
142 	element = e_filter_part_find_element (part, "sender");
143 	e_filter_input_set_value ((EFilterInput *) element, text);
144 }
145 
146 /* do a bunch of things on the subject to try and detect mailing lists, remove
147  * unneeded stuff, etc */
148 static void
149 rule_match_subject (ERuleContext *context,
150                     EFilterRule *rule,
151                     const gchar *subject)
152 {
153 	const gchar *s;
154 	const gchar *s1, *s2;
155 	gchar *tmp;
156 
157 	s = strip_re (subject);
158 	/* dont match on empty subject */
159 	if (*s == 0)
160 		return;
161 
162 	/* [blahblah] is probably a mailing list, match on it separately */
163 	s1 = strchr (s, '[');
164 	s2 = strchr (s, ']');
165 	if (s1 && s2 && s1 < s2) {
166 		/* probably a mailing list, match on the mailing list name */
167 		tmp = g_alloca (s2 - s1 + 2);
168 		memcpy (tmp, s1, s2 - s1 + 1);
169 		tmp[s2 - s1 + 1] = 0;
170 		g_strstrip (tmp);
171 		rule_add_subject (context, rule, tmp);
172 		s = s2 + 1;
173 	}
174 	/* Froblah: at the start is probably something important (e.g. bug number) */
175 	s1 = strchr (s, ':');
176 	if (s1) {
177 		tmp = g_alloca (s1 - s + 1);
178 		memcpy (tmp, s, s1 - s);
179 		tmp[s1 - s] = 0;
180 		g_strstrip (tmp);
181 		rule_add_subject (context, rule, tmp);
182 		s = s1 + 1;
183 	}
184 
185 	/* just lump the rest together */
186 	tmp = g_alloca (strlen (s) + 1);
187 	strcpy (tmp, s);
188 	g_strstrip (tmp);
189 	rule_add_subject (context, rule, tmp);
190 }
191 
192 static void
193 rule_match_mlist (ERuleContext *context,
194                   EFilterRule *rule,
195                   const gchar *mlist)
196 {
197 	EFilterPart *part;
198 	EFilterElement *element;
199 
200 	if (mlist[0] == 0)
201 		return;
202 
203 	part = e_rule_context_create_part (context, "mlist");
204 	e_filter_rule_add_part (rule, part);
205 
206 	element = e_filter_part_find_element (part, "mlist-type");
207 	e_filter_option_set_current ((EFilterOption *) element, "is");
208 
209 	element = e_filter_part_find_element (part, "mlist");
210 	e_filter_input_set_value ((EFilterInput *) element, mlist);
211 }
212 
213 static void
214 rule_from_address (EFilterRule *rule,
215                    ERuleContext *context,
216                    CamelInternetAddress *addr,
217                    gint flags)
218 {
219 	rule->grouping = E_FILTER_GROUP_ALL;
220 
221 	if (flags & AUTO_FROM) {
222 		const gchar *name, *address;
223 		gchar *namestr;
224 
225 		camel_internet_address_get (addr, 0, &name, &address);
226 		rule_add_sender (context, rule, address);
227 		if (name == NULL || name[0] == '\0')
228 			name = address;
229 		namestr = g_strdup_printf (_("Mail from %s"), name);
230 		e_filter_rule_set_name (rule, namestr);
231 		g_free (namestr);
232 	}
233 	if (flags & AUTO_TO) {
234 		rule_match_recipients (context, rule, addr);
235 	}
236 
237 }
238 
239 static void
240 rule_from_message (EFilterRule *rule,
241                    ERuleContext *context,
242                    CamelMimeMessage *msg,
243                    gint flags)
244 {
245 	CamelInternetAddress *addr;
246 
247 	rule->grouping = E_FILTER_GROUP_ALL;
248 
249 	if (flags & AUTO_SUBJECT) {
250 		const gchar *subject = msg->subject ? msg->subject : "";
251 		gchar *namestr;
252 
253 		rule_match_subject (context, rule, subject);
254 
255 		namestr = g_strdup_printf (_("Subject is %s"), strip_re (subject));
256 		e_filter_rule_set_name (rule, namestr);
257 		g_free (namestr);
258 	}
259 	/* should parse the from address into an internet address? */
260 	if (flags & AUTO_FROM) {
261 		CamelInternetAddress *from;
262 		gint i;
263 		const gchar *name, *address;
264 		gchar *namestr;
265 
266 		from = camel_mime_message_get_from (msg);
267 		for (i = 0; from && camel_internet_address_get (
268 				from, i, &name, &address); i++) {
269 			rule_add_sender (context, rule, address);
270 			if (name == NULL || name[0] == '\0')
271 				name = address;
272 			namestr = g_strdup_printf (_("Mail from %s"), name);
273 			e_filter_rule_set_name (rule, namestr);
274 			g_free (namestr);
275 		}
276 	}
277 	if (flags & AUTO_TO) {
278 		addr = (CamelInternetAddress *)
279 			camel_mime_message_get_recipients (
280 			msg, CAMEL_RECIPIENT_TYPE_TO);
281 		if (addr)
282 			rule_match_recipients (context, rule, addr);
283 		addr = (CamelInternetAddress *)
284 			camel_mime_message_get_recipients (
285 			msg, CAMEL_RECIPIENT_TYPE_CC);
286 		if (addr)
287 			rule_match_recipients (context, rule, addr);
288 	}
289 	if (flags & AUTO_MLIST) {
290 		gchar *name, *mlist;
291 
292 		mlist = camel_header_raw_check_mailing_list (
293 			&((CamelMimePart *) msg)->headers);
294 		if (mlist) {
295 			rule_match_mlist (context, rule, mlist);
296 			name = g_strdup_printf (_("%s mailing list"), mlist);
297 			e_filter_rule_set_name (rule, name);
298 			g_free (name);
299 		}
300 		g_free (mlist);
301 	}
302 }
303 
304 EFilterRule *
305 em_vfolder_rule_from_message (EMVFolderContext *context,
306                               CamelMimeMessage *msg,
307                               gint flags,
308                               CamelFolder *folder)
309 {
310 	EFilterRule *rule;
311 	EMailSession *session;
312 	gchar *uri;
313 
314 	g_return_val_if_fail (EM_IS_VFOLDER_CONTEXT (context), NULL);
315 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (msg), NULL);
316 	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
317 
318 	uri = e_mail_folder_uri_from_folder (folder);
319 
320 	session = em_vfolder_editor_context_get_session ((EMVFolderEditorContext *) context);
321 
322 	rule = em_vfolder_editor_rule_new (session);
323 	em_vfolder_rule_add_source (EM_VFOLDER_RULE (rule), uri);
324 	rule_from_message (rule, E_RULE_CONTEXT (context), msg, flags);
325 
326 	g_free (uri);
327 
328 	return rule;
329 }
330 
331 EFilterRule *
332 em_vfolder_rule_from_address (EMVFolderContext *context,
333                               CamelInternetAddress *addr,
334                               gint flags,
335                               CamelFolder *folder)
336 {
337 	EFilterRule *rule;
338 	EMailSession *session;
339 	gchar *uri;
340 
341 	g_return_val_if_fail (EM_IS_VFOLDER_CONTEXT (context), NULL);
342 	g_return_val_if_fail (CAMEL_IS_INTERNET_ADDRESS (addr), NULL);
343 	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
344 
345 	uri = e_mail_folder_uri_from_folder (folder);
346 
347 	session = em_vfolder_editor_context_get_session ((EMVFolderEditorContext *) context);
348 
349 	rule = em_vfolder_editor_rule_new (session);
350 	em_vfolder_rule_add_source (EM_VFOLDER_RULE (rule), uri);
351 	rule_from_address (rule, E_RULE_CONTEXT (context), addr, flags);
352 
353 	g_free (uri);
354 
355 	return rule;
356 }
357 
358 EFilterRule *
359 filter_rule_from_message (EMFilterContext *context,
360                           CamelMimeMessage *msg,
361                           gint flags)
362 {
363 	EFilterRule *rule;
364 	EFilterPart *part;
365 
366 	g_return_val_if_fail (EM_IS_FILTER_CONTEXT (context), NULL);
367 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (msg), NULL);
368 
369 	rule = em_filter_rule_new ();
370 	rule_from_message (rule, E_RULE_CONTEXT (context), msg, flags);
371 
372 	part = em_filter_context_next_action (context, NULL);
373 
374 	em_filter_rule_add_action (
375 		EM_FILTER_RULE (rule), e_filter_part_clone (part));
376 
377 	return rule;
378 }
379 
380 void
381 filter_gui_add_from_message (EMailSession *session,
382                              CamelMimeMessage *msg,
383                              const gchar *source,
384                              gint flags)
385 {
386 	EMFilterContext *fc;
387 	const gchar *config_dir;
388 	gchar *user, *system;
389 	EFilterRule *rule;
390 
391 	g_return_if_fail (E_IS_MAIL_SESSION (session));
392 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg));
393 
394 	fc = em_filter_context_new (session);
395 	config_dir = mail_session_get_config_dir ();
396 	user = g_build_filename (config_dir, "filters.xml", NULL);
397 	system = g_build_filename (EVOLUTION_PRIVDATADIR, "filtertypes.xml", NULL);
398 	e_rule_context_load ((ERuleContext *) fc, system, user);
399 	g_free (system);
400 
401 	rule = filter_rule_from_message (fc, msg, flags);
402 
403 	e_filter_rule_set_source (rule, source);
404 
405 	e_rule_context_add_rule_gui (
406 		(ERuleContext *) fc, rule, _("Add Filter Rule"), user);
407 	g_free (user);
408 	g_object_unref (fc);
409 }
410 
411 void
412 mail_filter_rename_folder (CamelStore *store,
413                            const gchar *old_folder_name,
414                            const gchar *new_folder_name)
415 {
416 	CamelSession *session;
417 	EMFilterContext *fc;
418 	const gchar *config_dir;
419 	gchar *user, *system;
420 	GList *changed;
421 	gchar *old_uri;
422 	gchar *new_uri;
423 
424 	g_return_if_fail (CAMEL_IS_STORE (store));
425 	g_return_if_fail (old_folder_name != NULL);
426 	g_return_if_fail (new_folder_name != NULL);
427 
428 	session = camel_service_get_session (CAMEL_SERVICE (store));
429 
430 	old_uri = e_mail_folder_uri_build (store, old_folder_name);
431 	new_uri = e_mail_folder_uri_build (store, new_folder_name);
432 
433 	fc = em_filter_context_new (E_MAIL_SESSION (session));
434 	config_dir = mail_session_get_config_dir ();
435 	user = g_build_filename (config_dir, "filters.xml", NULL);
436 	system = g_build_filename (EVOLUTION_PRIVDATADIR, "filtertypes.xml", NULL);
437 	e_rule_context_load ((ERuleContext *) fc, system, user);
438 	g_free (system);
439 
440 	changed = e_rule_context_rename_uri (
441 		(ERuleContext *) fc, old_uri, new_uri, g_str_equal);
442 	if (changed) {
443 		if (e_rule_context_save ((ERuleContext *) fc, user) == -1)
444 			g_warning ("Could not write out changed filter rules\n");
445 		e_rule_context_free_uri_list ((ERuleContext *) fc, changed);
446 	}
447 
448 	g_free (user);
449 	g_object_unref (fc);
450 
451 	g_free (old_uri);
452 	g_free (new_uri);
453 }
454 
455 void
456 mail_filter_delete_folder (CamelStore *store,
457                            const gchar *folder_name,
458                            EAlertSink *alert_sink)
459 {
460 	CamelSession *session;
461 	EMFilterContext *fc;
462 	const gchar *config_dir;
463 	gchar *user, *system;
464 	GList *deleted;
465 	gchar *uri;
466 
467 	g_return_if_fail (CAMEL_IS_STORE (store));
468 	g_return_if_fail (folder_name != NULL);
469 	g_return_if_fail (E_IS_ALERT_SINK (alert_sink));
470 
471 	session = camel_service_get_session (CAMEL_SERVICE (store));
472 
473 	uri = e_mail_folder_uri_build (store, folder_name);
474 
475 	fc = em_filter_context_new (E_MAIL_SESSION (session));
476 	config_dir = mail_session_get_config_dir ();
477 	user = g_build_filename (config_dir, "filters.xml", NULL);
478 	system = g_build_filename (EVOLUTION_PRIVDATADIR, "filtertypes.xml", NULL);
479 	e_rule_context_load ((ERuleContext *) fc, system, user);
480 	g_free (system);
481 
482 	deleted = e_rule_context_delete_uri (
483 		(ERuleContext *) fc, uri, g_str_equal);
484 	if (deleted) {
485 		GString *s;
486 		guint s_count;
487 		gchar *info;
488 		GList *l;
489 
490 		s = g_string_new ("");
491 		s_count = 0;
492 		for (l = deleted; l; l = l->next) {
493 			const gchar *name = (const gchar *) l->data;
494 
495 			if (s_count == 0) {
496 				g_string_append (s, name);
497 			} else {
498 				if (s_count == 1) {
499 					g_string_prepend (s, "    ");
500 					g_string_append (s, "\n");
501 				}
502 				g_string_append_printf (s, "    %s\n", name);
503 			}
504 			s_count++;
505 		}
506 
507 		info = g_strdup_printf (ngettext (
508 			/* Translators: The first %s is name of the affected
509 			 * filter rule(s), the second %s is URI of the removed
510 			 * folder. For more than one filter rule is each of
511 			 * them on a separate line, with four spaces in front
512 			 * of its name, without quotes. */
513 			"The filter rule \"%s\" has been modified to account "
514 			"for the deleted folder\n\"%s\".",
515 			"The following filter rules\n%s have been modified "
516 			"to account for the deleted folder\n\"%s\".",
517 			s_count), s->str, folder_name);
518 		e_alert_submit (
519 			alert_sink, "mail:filter-updated", info, NULL);
520 		g_string_free (s, TRUE);
521 		g_free (info);
522 
523 		if (e_rule_context_save ((ERuleContext *) fc, user) == -1)
524 			g_warning ("Could not write out changed filter rules\n");
525 		e_rule_context_free_uri_list ((ERuleContext *) fc, deleted);
526 	}
527 
528 	g_free (user);
529 	g_object_unref (fc);
530 	g_free (uri);
531 }