1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 * Copyright (C) 2000 Ximian, Inc.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 * Michael Zucchi <notzed@ximian.com>
22 * Ettore Perazzoli <ettore@ximian.com>
25 /* Code for autogenerating rules or filters from a message. */
35 #include <libgnome/gnome-i18n.h>
36 #include <libgnomeui/gnome-app.h>
37 #include <libgnomeui/gnome-app-helper.h>
38 #include <libgnomeui/gnome-popup-menu.h>
40 #include "mail-vfolder.h"
41 #include "mail-autofilter.h"
42 #include "mail-component.h"
44 #include "e-util/e-error.h"
46 #include "em-vfolder-context.h"
47 #include "em-vfolder-rule.h"
48 #include "em-vfolder-editor.h"
50 #include "em-filter-context.h"
51 #include "em-filter-rule.h"
52 #include "em-filter-editor.h"
53 #include "filter/filter-option.h"
55 #include <camel/camel-internet-address.h>
56 #include <camel/camel-mime-message.h>
61 rule_match_recipients (RuleContext
*context
, FilterRule
*rule
, CamelInternetAddress
*iaddr
)
64 FilterElement
*element
;
66 const char *real
, *addr
;
69 /* address types etc should handle multiple values */
70 for (i
= 0; camel_internet_address_get (iaddr
, i
, &real
, &addr
); i
++) {
71 part
= rule_context_create_part (context
, "to");
72 filter_rule_add_part ((FilterRule
*)rule
, part
);
73 element
= filter_part_find_element (part
, "recipient-type");
74 filter_option_set_current ((FilterOption
*)element
, "contains");
75 element
= filter_part_find_element (part
, "recipient");
76 filter_input_set_value ((FilterInput
*)element
, addr
);
78 namestr
= g_strdup_printf (_("Mail to %s"), real
&& real
[0] ? real
: addr
);
79 filter_rule_set_name (rule
, namestr
);
85 /* remove 're' part of a subject */
87 strip_re (const char *subject
)
89 const unsigned char *s
, *p
;
91 s
= (unsigned char *) subject
;
98 if ((s
[0] == 'r' || s
[0] == 'R')
99 && (s
[1] == 'e' || s
[1] == 'E')) {
101 while (isdigit (*p
) || (ispunct (*p
) && (*p
!= ':')))
116 reg_match (char *str
, char *regstr
)
122 error
= regcomp(®
, regstr
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
126 error
= regexec(®
, str
, 0, NULL
, 0);
133 rule_add_subject (RuleContext
*context
, FilterRule
*rule
, const char *text
)
136 FilterElement
*element
;
138 /* dont match on empty strings ever */
141 part
= rule_context_create_part (context
, "subject");
142 filter_rule_add_part ((FilterRule
*)rule
, part
);
143 element
= filter_part_find_element (part
, "subject-type");
144 filter_option_set_current ((FilterOption
*)element
, "contains");
145 element
= filter_part_find_element (part
, "subject");
146 filter_input_set_value ((FilterInput
*)element
, text
);
150 rule_add_sender (RuleContext
*context
, FilterRule
*rule
, const char *text
)
153 FilterElement
*element
;
155 /* dont match on empty strings ever */
158 part
= rule_context_create_part (context
, "sender");
159 filter_rule_add_part ((FilterRule
*)rule
, part
);
160 element
= filter_part_find_element (part
, "sender-type");
161 filter_option_set_current ((FilterOption
*)element
, "contains");
162 element
= filter_part_find_element (part
, "sender");
163 filter_input_set_value ((FilterInput
*)element
, text
);
166 /* do a bunch of things on the subject to try and detect mailing lists, remove
167 unneeded stuff, etc */
169 rule_match_subject (RuleContext
*context
, FilterRule
*rule
, const char *subject
)
175 s
= strip_re (subject
);
176 /* dont match on empty subject */
180 /* [blahblah] is probably a mailing list, match on it separately */
181 s1
= strchr (s
, '[');
182 s2
= strchr (s
, ']');
183 if (s1
&& s2
&& s1
< s2
) {
184 /* probably a mailing list, match on the mailing list name */
185 tmp
= g_alloca (s2
- s1
+ 2);
186 memcpy (tmp
, s1
, s2
- s1
+ 1);
187 tmp
[s2
- s1
+ 1] = 0;
189 rule_add_subject (context
, rule
, tmp
);
192 /* Froblah: at the start is probably something important (e.g. bug number) */
193 s1
= strchr (s
, ':');
195 tmp
= g_alloca (s1
- s
+ 1);
196 memcpy (tmp
, s
, s1
-s
);
199 rule_add_subject (context
, rule
, tmp
);
203 /* just lump the rest together */
204 tmp
= g_alloca (strlen (s
) + 1);
207 rule_add_subject (context
, rule
, tmp
);
211 rule_match_mlist(RuleContext
*context
, FilterRule
*rule
, const char *mlist
)
214 FilterElement
*element
;
219 part
= rule_context_create_part(context
, "mlist");
220 filter_rule_add_part(rule
, part
);
222 element
= filter_part_find_element(part
, "mlist-type");
223 filter_option_set_current((FilterOption
*)element
, "is");
225 element
= filter_part_find_element (part
, "mlist");
226 filter_input_set_value((FilterInput
*)element
, mlist
);
230 rule_from_address (FilterRule
*rule
, RuleContext
*context
, CamelInternetAddress
* addr
, int flags
)
232 rule
->grouping
= FILTER_GROUP_ANY
;
234 if (flags
& AUTO_FROM
) {
235 const char *name
, *address
;
238 camel_internet_address_get (addr
, 0, &name
, &address
);
239 rule_add_sender (context
, rule
, address
);
240 if (name
== NULL
|| name
[0] == '\0')
242 namestr
= g_strdup_printf(_("Mail from %s"), name
);
243 filter_rule_set_name (rule
, namestr
);
246 if (flags
& AUTO_TO
) {
247 rule_match_recipients (context
, rule
, addr
);
253 rule_from_message (FilterRule
*rule
, RuleContext
*context
, CamelMimeMessage
*msg
, int flags
)
255 CamelInternetAddress
*addr
;
257 rule
->grouping
= FILTER_GROUP_ANY
;
259 if (flags
& AUTO_SUBJECT
) {
260 const char *subject
= msg
->subject
? msg
->subject
: "";
263 rule_match_subject (context
, rule
, subject
);
265 namestr
= g_strdup_printf (_("Subject is %s"), strip_re (subject
));
266 filter_rule_set_name (rule
, namestr
);
269 /* should parse the from address into an internet address? */
270 if (flags
& AUTO_FROM
) {
271 const CamelInternetAddress
*from
;
273 const char *name
, *addr
;
276 from
= camel_mime_message_get_from (msg
);
277 for (i
= 0; from
&& camel_internet_address_get (from
, i
, &name
, &addr
); i
++) {
278 rule_add_sender(context
, rule
, addr
);
279 if (name
== NULL
|| name
[0] == '\0')
281 namestr
= g_strdup_printf(_("Mail from %s"), name
);
282 filter_rule_set_name (rule
, namestr
);
286 if (flags
& AUTO_TO
) {
287 addr
= (CamelInternetAddress
*)camel_mime_message_get_recipients (msg
, CAMEL_RECIPIENT_TYPE_TO
);
289 rule_match_recipients (context
, rule
, addr
);
290 addr
= (CamelInternetAddress
*)camel_mime_message_get_recipients (msg
, CAMEL_RECIPIENT_TYPE_CC
);
292 rule_match_recipients (context
, rule
, addr
);
294 if (flags
& AUTO_MLIST
) {
297 mlist
= camel_header_raw_check_mailing_list (&((CamelMimePart
*)msg
)->headers
);
299 rule_match_mlist(context
, rule
, mlist
);
300 name
= g_strdup_printf (_("%s mailing list"), mlist
);
301 filter_rule_set_name(rule
, name
);
309 em_vfolder_rule_from_message (EMVFolderContext
*context
, CamelMimeMessage
*msg
, int flags
, const char *source
)
312 char *euri
= em_uri_from_camel(source
);
314 rule
= em_vfolder_rule_new ();
315 em_vfolder_rule_add_source (rule
, euri
);
316 rule_from_message ((FilterRule
*)rule
, (RuleContext
*)context
, msg
, flags
);
319 return (FilterRule
*)rule
;
323 em_vfolder_rule_from_address (EMVFolderContext
*context
, CamelInternetAddress
*addr
, int flags
, const char *source
)
326 char *euri
= em_uri_from_camel(source
);
328 rule
= em_vfolder_rule_new ();
329 em_vfolder_rule_add_source (rule
, euri
);
330 rule_from_address ((FilterRule
*)rule
, (RuleContext
*)context
, addr
, flags
);
333 return (FilterRule
*)rule
;
337 filter_rule_from_message (EMFilterContext
*context
, CamelMimeMessage
*msg
, int flags
)
342 rule
= em_filter_rule_new ();
343 rule_from_message ((FilterRule
*)rule
, (RuleContext
*)context
, msg
, flags
);
345 part
= em_filter_context_next_action (context
, NULL
);
346 em_filter_rule_add_action (rule
, filter_part_clone (part
));
348 return (FilterRule
*)rule
;
352 filter_gui_add_from_message (CamelMimeMessage
*msg
, const char *source
, int flags
)
358 g_return_if_fail (msg
!= NULL
);
360 fc
= em_filter_context_new ();
361 user
= g_strdup_printf ("%s/mail/filters.xml",
362 mail_component_peek_base_directory (mail_component_peek ()));
363 system
= EVOLUTION_PRIVDATADIR
"/filtertypes.xml";
364 rule_context_load ((RuleContext
*)fc
, system
, user
);
365 rule
= filter_rule_from_message (fc
, msg
, flags
);
367 filter_rule_set_source (rule
, source
);
369 rule_context_add_rule_gui ((RuleContext
*)fc
, rule
, _("Add Filter Rule"), user
);
375 mail_filter_rename_uri(CamelStore
*store
, const char *olduri
, const char *newuri
)
380 char *eolduri
, *enewuri
;
382 eolduri
= em_uri_from_camel(olduri
);
383 enewuri
= em_uri_from_camel(newuri
);
385 fc
= em_filter_context_new ();
386 user
= g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
387 system
= EVOLUTION_PRIVDATADIR
"/filtertypes.xml";
388 rule_context_load ((RuleContext
*)fc
, system
, user
);
390 changed
= rule_context_rename_uri((RuleContext
*)fc
, eolduri
, enewuri
, g_str_equal
);
392 d(printf("Folder rename '%s' -> '%s' changed filters, resaving\n", olduri
, newuri
));
393 if (rule_context_save((RuleContext
*)fc
, user
) == -1)
394 g_warning("Could not write out changed filter rules\n");
395 rule_context_free_uri_list((RuleContext
*)fc
, changed
);
406 mail_filter_delete_uri(CamelStore
*store
, const char *uri
)
413 euri
= em_uri_from_camel(uri
);
415 fc
= em_filter_context_new ();
416 user
= g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
417 system
= EVOLUTION_PRIVDATADIR
"/filtertypes.xml";
418 rule_context_load ((RuleContext
*)fc
, system
, user
);
420 deleted
= rule_context_delete_uri ((RuleContext
*) fc
, euri
, g_str_equal
);
426 s
= g_string_new("");
429 g_string_append_printf (s
, " %s\n", (char *)l
->data
);
433 dialog
= e_error_new(NULL
, "mail:filter-updated", s
->str
, euri
, NULL
);
434 g_signal_connect_swapped (dialog
, "response", G_CALLBACK (gtk_widget_destroy
), dialog
);
435 g_string_free(s
, TRUE
);
436 gtk_widget_show(dialog
);
438 d(printf("Folder delete/rename '%s' changed filters, resaving\n", euri
));
439 if (rule_context_save ((RuleContext
*) fc
, user
) == -1)
440 g_warning ("Could not write out changed filter rules\n");
441 rule_context_free_uri_list ((RuleContext
*) fc
, deleted
);