2 * Code for autogenerating rules or filters from a message.
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.
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.
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/>
18 * Michael Zucchi <notzed@ximian.com>
19 * Ettore Perazzoli <ettore@ximian.com>
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
30 #include <glib/gi18n.h>
32 #include <libemail-engine/e-mail-folder-utils.h>
33 #include <libemail-engine/e-mail-session.h>
35 #include "mail-vfolder-ui.h"
36 #include "mail-autofilter.h"
38 #include "libevolution-utils/e-alert-dialog.h"
39 #include "e-util/e-util-private.h"
41 #include "em-vfolder-editor-context.h"
42 #include "em-vfolder-editor-rule.h"
43 #include "em-vfolder-editor.h"
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"
54 rule_match_recipients (ERuleContext
*context
,
56 CamelInternetAddress
*iaddr
)
59 EFilterElement
*element
;
61 const gchar
*real
, *addr
;
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
);
73 namestr
= g_strdup_printf (_("Mail to %s"), real
&& real
[0] ? real
: addr
);
74 e_filter_rule_set_name (rule
, namestr
);
79 /* remove 're' part of a subject */
81 strip_re (const gchar
*subject
)
85 s
= (guchar
*) subject
;
92 if ((s
[0] == 'r' || s
[0] == 'R')
93 && (s
[1] == 'e' || s
[1] == 'E')) {
95 while (isdigit (*p
) || (ispunct (*p
) && (*p
!= ':')))
109 rule_add_subject (ERuleContext
*context
,
114 EFilterElement
*element
;
116 /* dont match on empty strings ever */
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
);
128 rule_add_sender (ERuleContext
*context
,
133 EFilterElement
*element
;
135 /* dont match on empty strings ever */
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
);
146 /* do a bunch of things on the subject to try and detect mailing lists, remove
147 * unneeded stuff, etc */
149 rule_match_subject (ERuleContext
*context
,
151 const gchar
*subject
)
154 const gchar
*s1
, *s2
;
157 s
= strip_re (subject
);
158 /* dont match on empty subject */
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;
171 rule_add_subject (context
, rule
, tmp
);
174 /* Froblah: at the start is probably something important (e.g. bug number) */
175 s1
= strchr (s
, ':');
177 tmp
= g_alloca (s1
- s
+ 1);
178 memcpy (tmp
, s
, s1
- s
);
181 rule_add_subject (context
, rule
, tmp
);
185 /* just lump the rest together */
186 tmp
= g_alloca (strlen (s
) + 1);
189 rule_add_subject (context
, rule
, tmp
);
193 rule_match_mlist (ERuleContext
*context
,
198 EFilterElement
*element
;
203 part
= e_rule_context_create_part (context
, "mlist");
204 e_filter_rule_add_part (rule
, part
);
206 element
= e_filter_part_find_element (part
, "mlist-type");
207 e_filter_option_set_current ((EFilterOption
*) element
, "is");
209 element
= e_filter_part_find_element (part
, "mlist");
210 e_filter_input_set_value ((EFilterInput
*) element
, mlist
);
214 rule_from_address (EFilterRule
*rule
,
215 ERuleContext
*context
,
216 CamelInternetAddress
*addr
,
219 rule
->grouping
= E_FILTER_GROUP_ALL
;
221 if (flags
& AUTO_FROM
) {
222 const gchar
*name
, *address
;
225 camel_internet_address_get (addr
, 0, &name
, &address
);
226 rule_add_sender (context
, rule
, address
);
227 if (name
== NULL
|| name
[0] == '\0')
229 namestr
= g_strdup_printf (_("Mail from %s"), name
);
230 e_filter_rule_set_name (rule
, namestr
);
233 if (flags
& AUTO_TO
) {
234 rule_match_recipients (context
, rule
, addr
);
240 rule_from_message (EFilterRule
*rule
,
241 ERuleContext
*context
,
242 CamelMimeMessage
*msg
,
245 CamelInternetAddress
*addr
;
247 rule
->grouping
= E_FILTER_GROUP_ALL
;
249 if (flags
& AUTO_SUBJECT
) {
250 const gchar
*subject
= msg
->subject
? msg
->subject
: "";
253 rule_match_subject (context
, rule
, subject
);
255 namestr
= g_strdup_printf (_("Subject is %s"), strip_re (subject
));
256 e_filter_rule_set_name (rule
, namestr
);
259 /* should parse the from address into an internet address? */
260 if (flags
& AUTO_FROM
) {
261 CamelInternetAddress
*from
;
263 const gchar
*name
, *address
;
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')
272 namestr
= g_strdup_printf (_("Mail from %s"), name
);
273 e_filter_rule_set_name (rule
, namestr
);
277 if (flags
& AUTO_TO
) {
278 addr
= (CamelInternetAddress
*)
279 camel_mime_message_get_recipients (
280 msg
, CAMEL_RECIPIENT_TYPE_TO
);
282 rule_match_recipients (context
, rule
, addr
);
283 addr
= (CamelInternetAddress
*)
284 camel_mime_message_get_recipients (
285 msg
, CAMEL_RECIPIENT_TYPE_CC
);
287 rule_match_recipients (context
, rule
, addr
);
289 if (flags
& AUTO_MLIST
) {
292 mlist
= camel_header_raw_check_mailing_list (
293 &((CamelMimePart
*) msg
)->headers
);
295 rule_match_mlist (context
, rule
, mlist
);
296 name
= g_strdup_printf (_("%s mailing list"), mlist
);
297 e_filter_rule_set_name (rule
, name
);
305 em_vfolder_rule_from_message (EMVFolderContext
*context
,
306 CamelMimeMessage
*msg
,
311 EMailSession
*session
;
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
);
318 uri
= e_mail_folder_uri_from_folder (folder
);
320 session
= em_vfolder_editor_context_get_session ((EMVFolderEditorContext
*) context
);
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
);
332 em_vfolder_rule_from_address (EMVFolderContext
*context
,
333 CamelInternetAddress
*addr
,
338 EMailSession
*session
;
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
);
345 uri
= e_mail_folder_uri_from_folder (folder
);
347 session
= em_vfolder_editor_context_get_session ((EMVFolderEditorContext
*) context
);
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
);
359 filter_rule_from_message (EMFilterContext
*context
,
360 CamelMimeMessage
*msg
,
366 g_return_val_if_fail (EM_IS_FILTER_CONTEXT (context
), NULL
);
367 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (msg
), NULL
);
369 rule
= em_filter_rule_new ();
370 rule_from_message (rule
, E_RULE_CONTEXT (context
), msg
, flags
);
372 part
= em_filter_context_next_action (context
, NULL
);
374 em_filter_rule_add_action (
375 EM_FILTER_RULE (rule
), e_filter_part_clone (part
));
381 filter_gui_add_from_message (EMailSession
*session
,
382 CamelMimeMessage
*msg
,
387 const gchar
*config_dir
;
388 gchar
*user
, *system
;
391 g_return_if_fail (E_IS_MAIL_SESSION (session
));
392 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg
));
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
);
401 rule
= filter_rule_from_message (fc
, msg
, flags
);
403 e_filter_rule_set_source (rule
, source
);
405 e_rule_context_add_rule_gui (
406 (ERuleContext
*) fc
, rule
, _("Add Filter Rule"), user
);
412 mail_filter_rename_folder (CamelStore
*store
,
413 const gchar
*old_folder_name
,
414 const gchar
*new_folder_name
)
416 CamelSession
*session
;
418 const gchar
*config_dir
;
419 gchar
*user
, *system
;
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
);
428 session
= camel_service_get_session (CAMEL_SERVICE (store
));
430 old_uri
= e_mail_folder_uri_build (store
, old_folder_name
);
431 new_uri
= e_mail_folder_uri_build (store
, new_folder_name
);
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
);
440 changed
= e_rule_context_rename_uri (
441 (ERuleContext
*) fc
, old_uri
, new_uri
, g_str_equal
);
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
);
456 mail_filter_delete_folder (CamelStore
*store
,
457 const gchar
*folder_name
,
458 EAlertSink
*alert_sink
)
460 CamelSession
*session
;
462 const gchar
*config_dir
;
463 gchar
*user
, *system
;
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
));
471 session
= camel_service_get_session (CAMEL_SERVICE (store
));
473 uri
= e_mail_folder_uri_build (store
, folder_name
);
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
);
482 deleted
= e_rule_context_delete_uri (
483 (ERuleContext
*) fc
, uri
, g_str_equal
);
490 s
= g_string_new ("");
492 for (l
= deleted
; l
; l
= l
->next
) {
493 const gchar
*name
= (const gchar
*) l
->data
;
496 g_string_append (s
, name
);
499 g_string_prepend (s
, " ");
500 g_string_append (s
, "\n");
502 g_string_append_printf (s
, " %s\n", name
);
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
);
519 alert_sink
, "mail:filter-updated", info
, NULL
);
520 g_string_free (s
, TRUE
);
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
);