Statistics
| Revision:

root / libsylph / filter.c @ 999

History | View | Annotate | Download (37.5 kB)

1 1 hiro
/*
2 578 hiro
 * LibSylph -- E-Mail client library
3 905 hiro
 * Copyright (C) 1999-2006 Hiroyuki Yamamoto
4 1 hiro
 *
5 578 hiro
 * This library is free software; you can redistribute it and/or
6 578 hiro
 * modify it under the terms of the GNU Lesser General Public
7 578 hiro
 * License as published by the Free Software Foundation; either
8 578 hiro
 * version 2.1 of the License, or (at your option) any later version.
9 1 hiro
 *
10 578 hiro
 * This library is distributed in the hope that it will be useful,
11 1 hiro
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 578 hiro
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 578 hiro
 * Lesser General Public License for more details.
14 1 hiro
 *
15 578 hiro
 * You should have received a copy of the GNU Lesser General Public
16 578 hiro
 * License along with this library; if not, write to the Free Software
17 578 hiro
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 1 hiro
 */
19 1 hiro
20 1 hiro
#ifdef HAVE_CONFIG_H
21 1 hiro
#  include "config.h"
22 1 hiro
#endif
23 1 hiro
24 1 hiro
#include "defs.h"
25 1 hiro
26 1 hiro
#include <glib.h>
27 92 hiro
#include <glib/gi18n.h>
28 1 hiro
#include <string.h>
29 1 hiro
#include <strings.h>
30 1 hiro
#include <stdlib.h>
31 1 hiro
#include <sys/types.h>
32 471 hiro
#if HAVE_REGEX_H
33 471 hiro
#  include <regex.h>
34 471 hiro
#endif
35 1 hiro
#include <time.h>
36 1 hiro
37 547 hiro
#include "filter.h"
38 547 hiro
#include "procmsg.h"
39 1 hiro
#include "procheader.h"
40 1 hiro
#include "folder.h"
41 1 hiro
#include "utils.h"
42 1 hiro
#include "xml.h"
43 1 hiro
#include "prefs.h"
44 542 hiro
#include "prefs_common.h"
45 1 hiro
#include "prefs_account.h"
46 527 hiro
#include "account.h"
47 1 hiro
48 1 hiro
typedef enum
49 1 hiro
{
50 1 hiro
        FLT_O_CONTAIN        = 1 << 0,
51 1 hiro
        FLT_O_CASE_SENS        = 1 << 1,
52 1 hiro
        FLT_O_REGEX        = 1 << 2
53 1 hiro
} FilterOldFlag;
54 1 hiro
55 1 hiro
static gboolean filter_match_cond        (FilterCond        *cond,
56 1 hiro
                                         MsgInfo        *msginfo,
57 1 hiro
                                         GSList                *hlist,
58 1 hiro
                                         FilterInfo        *fltinfo);
59 1 hiro
static gboolean filter_match_header_cond(FilterCond        *cond,
60 1 hiro
                                         GSList                *hlist);
61 1 hiro
62 1 hiro
static void filter_cond_free                (FilterCond        *cond);
63 1 hiro
static void filter_action_free                (FilterAction        *action);
64 1 hiro
65 1 hiro
66 1 hiro
gint filter_apply(GSList *fltlist, const gchar *file, FilterInfo *fltinfo)
67 1 hiro
{
68 1 hiro
        MsgInfo *msginfo;
69 1 hiro
        gint ret = 0;
70 1 hiro
71 1 hiro
        g_return_val_if_fail(file != NULL, -1);
72 1 hiro
        g_return_val_if_fail(fltinfo != NULL, -1);
73 1 hiro
74 1 hiro
        if (!fltlist) return 0;
75 1 hiro
76 1 hiro
        msginfo = procheader_parse_file(file, fltinfo->flags, FALSE);
77 1 hiro
        if (!msginfo) return 0;
78 1 hiro
        msginfo->file_path = g_strdup(file);
79 1 hiro
80 1 hiro
        ret = filter_apply_msginfo(fltlist, msginfo, fltinfo);
81 1 hiro
82 1 hiro
        procmsg_msginfo_free(msginfo);
83 1 hiro
84 1 hiro
        return ret;
85 1 hiro
}
86 1 hiro
87 1 hiro
gint filter_apply_msginfo(GSList *fltlist, MsgInfo *msginfo,
88 1 hiro
                          FilterInfo *fltinfo)
89 1 hiro
{
90 1 hiro
        gchar *file;
91 1 hiro
        GSList *hlist, *cur;
92 1 hiro
        FilterRule *rule;
93 1 hiro
        gint ret = 0;
94 1 hiro
95 1 hiro
        g_return_val_if_fail(msginfo != NULL, -1);
96 1 hiro
        g_return_val_if_fail(fltinfo != NULL, -1);
97 1 hiro
98 1 hiro
        if (!fltlist) return 0;
99 1 hiro
100 1 hiro
        file = procmsg_get_message_file(msginfo);
101 1 hiro
        hlist = procheader_get_header_list_from_file(file);
102 1 hiro
        if (!hlist) {
103 1 hiro
                g_free(file);
104 1 hiro
                return 0;
105 1 hiro
        }
106 1 hiro
107 1 hiro
        for (cur = fltlist; cur != NULL; cur = cur->next) {
108 1 hiro
                rule = (FilterRule *)cur->data;
109 1 hiro
                if (!rule->enabled) continue;
110 1 hiro
                if (filter_match_rule(rule, msginfo, hlist, fltinfo)) {
111 1 hiro
                        ret = filter_action_exec(rule, msginfo, file, fltinfo);
112 1 hiro
                        if (ret < 0) {
113 1 hiro
                                g_warning("filter_action_exec() returned error\n");
114 1 hiro
                                break;
115 1 hiro
                        }
116 1 hiro
                        if (fltinfo->drop_done == TRUE ||
117 1 hiro
                            fltinfo->actions[FLT_ACTION_STOP_EVAL] == TRUE)
118 1 hiro
                                break;
119 1 hiro
                }
120 1 hiro
        }
121 1 hiro
122 1 hiro
        procheader_header_list_destroy(hlist);
123 1 hiro
        g_free(file);
124 1 hiro
125 1 hiro
        return ret;
126 1 hiro
}
127 1 hiro
128 1 hiro
gint filter_action_exec(FilterRule *rule, MsgInfo *msginfo, const gchar *file,
129 1 hiro
                        FilterInfo *fltinfo)
130 1 hiro
{
131 1 hiro
        FolderItem *dest_folder = NULL;
132 1 hiro
        FilterAction *action;
133 1 hiro
        GSList *cur;
134 1 hiro
        gchar *cmdline;
135 1 hiro
        gboolean copy_to_self = FALSE;
136 1 hiro
137 1 hiro
        g_return_val_if_fail(rule != NULL, -1);
138 1 hiro
        g_return_val_if_fail(msginfo != NULL, -1);
139 1 hiro
        g_return_val_if_fail(file != NULL, -1);
140 1 hiro
        g_return_val_if_fail(fltinfo != NULL, -1);
141 1 hiro
142 1 hiro
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
143 1 hiro
                action = (FilterAction *)cur->data;
144 1 hiro
145 1 hiro
                switch (action->type) {
146 1 hiro
                case FLT_ACTION_MARK:
147 1 hiro
                        debug_print("filter_action_exec(): mark\n");
148 1 hiro
                        MSG_SET_PERM_FLAGS(fltinfo->flags, MSG_MARKED);
149 1 hiro
                        fltinfo->actions[action->type] = TRUE;
150 1 hiro
                        break;
151 1 hiro
                case FLT_ACTION_COLOR_LABEL:
152 1 hiro
                        debug_print("filter_action_exec(): color label: %d\n",
153 1 hiro
                                    action->int_value);
154 1 hiro
                        MSG_UNSET_PERM_FLAGS(fltinfo->flags,
155 1 hiro
                                             MSG_CLABEL_FLAG_MASK);
156 1 hiro
                        MSG_SET_COLORLABEL_VALUE(fltinfo->flags,
157 1 hiro
                                                 action->int_value);
158 1 hiro
                        fltinfo->actions[action->type] = TRUE;
159 1 hiro
                        break;
160 1 hiro
                case FLT_ACTION_MARK_READ:
161 1 hiro
                        debug_print("filter_action_exec(): mark as read\n");
162 1 hiro
                        if (msginfo->folder) {
163 1 hiro
                                if (MSG_IS_NEW(fltinfo->flags))
164 1 hiro
                                        msginfo->folder->new--;
165 1 hiro
                                if (MSG_IS_UNREAD(fltinfo->flags))
166 1 hiro
                                        msginfo->folder->unread--;
167 1 hiro
                        }
168 1 hiro
                        MSG_UNSET_PERM_FLAGS(fltinfo->flags, MSG_NEW|MSG_UNREAD);
169 1 hiro
                        fltinfo->actions[action->type] = TRUE;
170 1 hiro
                        break;
171 1 hiro
                case FLT_ACTION_EXEC:
172 670 hiro
                        cmdline = g_strconcat(action->str_value, " \"", file,
173 670 hiro
                                              "\"", NULL);
174 1 hiro
                        execute_command_line(cmdline, FALSE);
175 1 hiro
                        g_free(cmdline);
176 1 hiro
                        fltinfo->actions[action->type] = TRUE;
177 1 hiro
                        break;
178 1 hiro
                case FLT_ACTION_EXEC_ASYNC:
179 670 hiro
                        cmdline = g_strconcat(action->str_value, " \"", file,
180 670 hiro
                                              "\"", NULL);
181 1 hiro
                        execute_command_line(cmdline, TRUE);
182 1 hiro
                        g_free(cmdline);
183 1 hiro
                        fltinfo->actions[action->type] = TRUE;
184 1 hiro
                        break;
185 1 hiro
                default:
186 1 hiro
                        break;
187 1 hiro
                }
188 1 hiro
        }
189 1 hiro
190 1 hiro
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
191 1 hiro
                action = (FilterAction *)cur->data;
192 1 hiro
193 1 hiro
                switch (action->type) {
194 1 hiro
                case FLT_ACTION_MOVE:
195 1 hiro
                case FLT_ACTION_COPY:
196 1 hiro
                        dest_folder = folder_find_item_from_identifier
197 1 hiro
                                (action->str_value);
198 1 hiro
                        if (!dest_folder) {
199 1 hiro
                                g_warning("dest folder '%s' not found\n",
200 1 hiro
                                          action->str_value);
201 1 hiro
                                return -1;
202 1 hiro
                        }
203 1 hiro
                        debug_print("filter_action_exec(): %s: dest_folder = %s\n",
204 1 hiro
                                    action->type == FLT_ACTION_COPY ?
205 1 hiro
                                    "copy" : "move", action->str_value);
206 1 hiro
207 1 hiro
                        if (msginfo->folder) {
208 1 hiro
                                gint val;
209 1 hiro
210 1 hiro
                                /* local filtering */
211 1 hiro
                                if (msginfo->folder == dest_folder)
212 1 hiro
                                        copy_to_self = TRUE;
213 1 hiro
                                else {
214 1 hiro
                                        if (action->type == FLT_ACTION_COPY) {
215 1 hiro
                                                val = folder_item_copy_msg
216 1 hiro
                                                        (dest_folder, msginfo);
217 1 hiro
                                                if (val == -1)
218 1 hiro
                                                        return -1;
219 1 hiro
                                        }
220 1 hiro
                                        fltinfo->actions[action->type] = TRUE;
221 1 hiro
                                }
222 1 hiro
                        } else {
223 1 hiro
                                if (folder_item_add_msg(dest_folder, file,
224 1 hiro
                                                        &fltinfo->flags,
225 1 hiro
                                                        FALSE) < 0)
226 1 hiro
                                        return -1;
227 1 hiro
                                fltinfo->actions[action->type] = TRUE;
228 1 hiro
                        }
229 1 hiro
230 1 hiro
                        fltinfo->dest_list = g_slist_append(fltinfo->dest_list,
231 1 hiro
                                                            dest_folder);
232 1 hiro
                        if (action->type == FLT_ACTION_MOVE) {
233 1 hiro
                                fltinfo->move_dest = dest_folder;
234 1 hiro
                                fltinfo->drop_done = TRUE;
235 1 hiro
                        }
236 1 hiro
                        break;
237 1 hiro
                default:
238 1 hiro
                        break;
239 1 hiro
                }
240 1 hiro
        }
241 1 hiro
242 1 hiro
        if (fltinfo->drop_done == TRUE)
243 1 hiro
                return 0;
244 1 hiro
245 1 hiro
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
246 1 hiro
                action = (FilterAction *)cur->data;
247 1 hiro
248 1 hiro
                switch (action->type) {
249 1 hiro
                case FLT_ACTION_NOT_RECEIVE:
250 1 hiro
                        debug_print("filter_action_exec(): don't receive\n");
251 1 hiro
                        fltinfo->drop_done = TRUE;
252 1 hiro
                        fltinfo->actions[action->type] = TRUE;
253 1 hiro
                        return 0;
254 1 hiro
                case FLT_ACTION_DELETE:
255 1 hiro
                        debug_print("filter_action_exec(): delete\n");
256 1 hiro
                        if (msginfo->folder) {
257 1 hiro
                                /* local filtering */
258 1 hiro
                                if (copy_to_self == FALSE)
259 1 hiro
                                        fltinfo->actions[action->type] = TRUE;
260 1 hiro
                        } else
261 1 hiro
                                fltinfo->actions[action->type] = TRUE;
262 1 hiro
263 1 hiro
                        fltinfo->drop_done = TRUE;
264 1 hiro
                        return 0;
265 1 hiro
                case FLT_ACTION_STOP_EVAL:
266 1 hiro
                        debug_print("filter_action_exec(): stop evaluation\n");
267 1 hiro
                        fltinfo->actions[action->type] = TRUE;
268 1 hiro
                        return 0;
269 1 hiro
                default:
270 1 hiro
                        break;
271 1 hiro
                }
272 1 hiro
        }
273 1 hiro
274 1 hiro
        return 0;
275 1 hiro
}
276 1 hiro
277 1 hiro
static gboolean strmatch_regex(const gchar *haystack, const gchar *needle)
278 1 hiro
{
279 471 hiro
#if HAVE_REGEX_H && HAVE_REGCOMP
280 1 hiro
        gint ret = 0;
281 1 hiro
        regex_t preg;
282 1 hiro
        regmatch_t pmatch[1];
283 1 hiro
284 1 hiro
        ret = regcomp(&preg, needle, REG_EXTENDED|REG_ICASE);
285 1 hiro
        if (ret != 0) return FALSE;
286 1 hiro
287 1 hiro
        ret = regexec(&preg, haystack, 1, pmatch, 0);
288 1 hiro
        regfree(&preg);
289 1 hiro
290 1 hiro
        if (ret == REG_NOMATCH) return FALSE;
291 1 hiro
292 1 hiro
        if (pmatch[0].rm_so != -1)
293 1 hiro
                return TRUE;
294 1 hiro
        else
295 471 hiro
#endif
296 1 hiro
                return FALSE;
297 1 hiro
}
298 1 hiro
299 1 hiro
gboolean filter_match_rule(FilterRule *rule, MsgInfo *msginfo, GSList *hlist,
300 1 hiro
                           FilterInfo *fltinfo)
301 1 hiro
{
302 1 hiro
        FilterCond *cond;
303 1 hiro
        GSList *cur;
304 1 hiro
        gboolean matched;
305 1 hiro
306 1 hiro
        g_return_val_if_fail(rule->cond_list != NULL, FALSE);
307 1 hiro
308 1 hiro
        switch (rule->timing) {
309 1 hiro
        case FLT_TIMING_ANY:
310 1 hiro
                break;
311 1 hiro
        case FLT_TIMING_ON_RECEIVE:
312 1 hiro
                if (msginfo->folder != NULL)
313 1 hiro
                        return FALSE;
314 1 hiro
                break;
315 1 hiro
        case FLT_TIMING_MANUAL:
316 1 hiro
                if (msginfo->folder == NULL)
317 1 hiro
                        return FALSE;
318 1 hiro
                break;
319 1 hiro
        default:
320 1 hiro
                break;
321 1 hiro
        }
322 1 hiro
323 1 hiro
        if (rule->bool_op == FLT_AND) {
324 1 hiro
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
325 1 hiro
                        cond = (FilterCond *)cur->data;
326 1 hiro
                        matched = filter_match_cond(cond, msginfo, hlist,
327 1 hiro
                                                    fltinfo);
328 1 hiro
                        if (matched == FALSE)
329 1 hiro
                                return FALSE;
330 1 hiro
                }
331 1 hiro
332 1 hiro
                return TRUE;
333 1 hiro
        } else if (rule->bool_op == FLT_OR) {
334 1 hiro
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
335 1 hiro
                        cond = (FilterCond *)cur->data;
336 1 hiro
                        matched = filter_match_cond(cond, msginfo, hlist,
337 1 hiro
                                                    fltinfo);
338 1 hiro
                        if (matched == TRUE)
339 1 hiro
                                return TRUE;
340 1 hiro
                }
341 1 hiro
342 1 hiro
                return FALSE;
343 1 hiro
        }
344 1 hiro
345 1 hiro
        return FALSE;
346 1 hiro
}
347 1 hiro
348 1 hiro
static gboolean filter_match_cond(FilterCond *cond, MsgInfo *msginfo,
349 1 hiro
                                  GSList *hlist, FilterInfo *fltinfo)
350 1 hiro
{
351 1 hiro
        gboolean matched = FALSE;
352 1 hiro
        gchar *file;
353 1 hiro
        gchar *cmdline;
354 1 hiro
        PrefsAccount *cond_ac;
355 1 hiro
356 1 hiro
        switch (cond->type) {
357 1 hiro
        case FLT_COND_HEADER:
358 1 hiro
        case FLT_COND_ANY_HEADER:
359 1 hiro
        case FLT_COND_TO_OR_CC:
360 1 hiro
                return filter_match_header_cond(cond, hlist);
361 1 hiro
        case FLT_COND_BODY:
362 1 hiro
                matched = procmime_find_string(msginfo, cond->str_value,
363 1 hiro
                                               cond->match_func);
364 1 hiro
                break;
365 1 hiro
        case FLT_COND_CMD_TEST:
366 1 hiro
                file = procmsg_get_message_file(msginfo);
367 670 hiro
                cmdline = g_strconcat(cond->str_value, " \"", file, "\"", NULL);
368 1 hiro
                matched = (execute_command_line(cmdline, FALSE) == 0);
369 1 hiro
                g_free(cmdline);
370 1 hiro
                g_free(file);
371 1 hiro
                break;
372 1 hiro
        case FLT_COND_SIZE_GREATER:
373 1 hiro
                matched = (msginfo->size > cond->int_value * 1024);
374 1 hiro
                break;
375 1 hiro
        case FLT_COND_AGE_GREATER:
376 1 hiro
                matched = (time(NULL) - msginfo->date_t >
377 1 hiro
                                cond->int_value * 24 * 60 * 60);
378 1 hiro
                break;
379 905 hiro
        case FLT_COND_UNREAD:
380 905 hiro
                matched = MSG_IS_UNREAD(msginfo->flags);
381 905 hiro
                break;
382 905 hiro
        case FLT_COND_MARK:
383 905 hiro
                matched = MSG_IS_MARKED(msginfo->flags);
384 905 hiro
                break;
385 905 hiro
        case FLT_COND_COLOR_LABEL:
386 905 hiro
                matched = (MSG_GET_COLORLABEL_VALUE(msginfo->flags) != 0);
387 905 hiro
                break;
388 905 hiro
        case FLT_COND_MIME:
389 905 hiro
                matched = MSG_IS_MIME(msginfo->flags);
390 905 hiro
                break;
391 1 hiro
        case FLT_COND_ACCOUNT:
392 1 hiro
                cond_ac = account_find_from_id(cond->int_value);
393 1 hiro
                matched = (cond_ac != NULL && cond_ac == fltinfo->account);
394 1 hiro
                break;
395 1 hiro
        default:
396 1 hiro
                g_warning("filter_match_cond(): unknown condition: %d\n",
397 1 hiro
                          cond->type);
398 1 hiro
                return FALSE;
399 1 hiro
        }
400 1 hiro
401 1 hiro
        if (FLT_IS_NOT_MATCH(cond->match_flag))
402 1 hiro
                matched = !matched;
403 1 hiro
404 1 hiro
        return matched;
405 1 hiro
}
406 1 hiro
407 1 hiro
static gboolean filter_match_header_cond(FilterCond *cond, GSList *hlist)
408 1 hiro
{
409 1 hiro
        gboolean matched = FALSE;
410 1 hiro
        GSList *cur;
411 1 hiro
        Header *header;
412 1 hiro
413 1 hiro
        for (cur = hlist; cur != NULL; cur = cur->next) {
414 1 hiro
                header = (Header *)cur->data;
415 1 hiro
416 1 hiro
                switch (cond->type) {
417 1 hiro
                case FLT_COND_HEADER:
418 333 hiro
                        if (!g_ascii_strcasecmp
419 333 hiro
                                (header->name, cond->header_name)) {
420 1 hiro
                                if (!cond->str_value ||
421 1 hiro
                                    cond->match_func(header->body,
422 1 hiro
                                                     cond->str_value))
423 1 hiro
                                        matched = TRUE;
424 1 hiro
                        }
425 1 hiro
                        break;
426 1 hiro
                case FLT_COND_ANY_HEADER:
427 1 hiro
                        if (!cond->str_value ||
428 1 hiro
                            cond->match_func(header->body, cond->str_value))
429 1 hiro
                                matched = TRUE;
430 1 hiro
                        break;
431 1 hiro
                case FLT_COND_TO_OR_CC:
432 333 hiro
                        if (!g_ascii_strcasecmp(header->name, "To") ||
433 333 hiro
                            !g_ascii_strcasecmp(header->name, "Cc")) {
434 1 hiro
                                if (!cond->str_value ||
435 1 hiro
                                    cond->match_func(header->body,
436 1 hiro
                                                     cond->str_value))
437 1 hiro
                                        matched = TRUE;
438 1 hiro
                        }
439 1 hiro
                        break;
440 1 hiro
                default:
441 1 hiro
                        break;
442 1 hiro
                }
443 1 hiro
444 1 hiro
                if (matched == TRUE)
445 1 hiro
                        break;
446 1 hiro
        }
447 1 hiro
448 1 hiro
        if (FLT_IS_NOT_MATCH(cond->match_flag))
449 1 hiro
                matched = !matched;
450 1 hiro
451 1 hiro
        return matched;
452 1 hiro
}
453 1 hiro
454 814 hiro
gboolean filter_rule_requires_full_headers(FilterRule *rule)
455 814 hiro
{
456 814 hiro
        GSList *cur;
457 814 hiro
458 814 hiro
        for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
459 814 hiro
                FilterCond *cond = (FilterCond *)cur->data;
460 814 hiro
                const gchar *name = cond->header_name;
461 814 hiro
462 814 hiro
                if (cond->type == FLT_COND_HEADER && name) {
463 814 hiro
                        if (g_ascii_strcasecmp(name, "Date") != 0 &&
464 814 hiro
                            g_ascii_strcasecmp(name, "From") != 0 &&
465 814 hiro
                            g_ascii_strcasecmp(name, "To") != 0 &&
466 814 hiro
                            g_ascii_strcasecmp(name, "Newsgroups") != 0 &&
467 814 hiro
                            g_ascii_strcasecmp(name, "Subject") != 0)
468 814 hiro
                                return TRUE;
469 814 hiro
                } else if (cond->type == FLT_COND_ANY_HEADER ||
470 814 hiro
                           cond->type == FLT_COND_TO_OR_CC)
471 814 hiro
                        return TRUE;
472 814 hiro
        }
473 814 hiro
474 814 hiro
        return FALSE;
475 814 hiro
}
476 814 hiro
477 1 hiro
#define RETURN_IF_TAG_NOT_MATCH(tag_name)                        \
478 1 hiro
        if (strcmp2(xmlnode->tag->tag, tag_name) != 0) {        \
479 1 hiro
                g_warning("tag name != \"" tag_name "\"\n");        \
480 1 hiro
                filter_cond_list_free(cond_list);                \
481 1 hiro
                filter_action_list_free(cond_list);                \
482 1 hiro
                return FALSE;                                        \
483 1 hiro
        }                                                        \
484 1 hiro
485 1 hiro
#define STR_SWITCH(sw)                { const gchar *sw_str = sw;
486 1 hiro
#define STR_CASE_BEGIN(str)        if (!strcmp(sw_str, str)) {
487 1 hiro
#define STR_CASE(str)                } else if (!strcmp(sw_str, str)) {
488 1 hiro
#define STR_CASE_END                } }
489 1 hiro
490 1 hiro
static gboolean filter_xml_node_func(GNode *node, gpointer data)
491 1 hiro
{
492 1 hiro
        XMLNode *xmlnode;
493 1 hiro
        GList *list;
494 1 hiro
        const gchar *name = NULL;
495 1 hiro
        FilterTiming timing = FLT_TIMING_ANY;
496 1 hiro
        gboolean enabled = TRUE;
497 1 hiro
        FilterRule *rule;
498 1 hiro
        FilterBoolOp bool_op = FLT_OR;
499 836 hiro
        const gchar *target_folder = NULL;
500 836 hiro
        gboolean recursive = FALSE;
501 1 hiro
        GSList *cond_list = NULL;
502 1 hiro
        GSList *action_list = NULL;
503 1 hiro
        GNode *child, *cond_child, *action_child;
504 1 hiro
        GSList **fltlist = (GSList **)data;
505 1 hiro
506 1 hiro
        if (g_node_depth(node) != 2) return FALSE;
507 1 hiro
        g_return_val_if_fail(node->data != NULL, FALSE);
508 1 hiro
509 1 hiro
        xmlnode = node->data;
510 1 hiro
        RETURN_IF_TAG_NOT_MATCH("rule");
511 1 hiro
512 1 hiro
        for (list = xmlnode->tag->attr; list != NULL; list = list->next) {
513 1 hiro
                XMLAttr *attr = (XMLAttr *)list->data;
514 1 hiro
515 1 hiro
                if (!attr || !attr->name || !attr->value) continue;
516 1 hiro
                if (!strcmp(attr->name, "name"))
517 1 hiro
                        name = attr->value;
518 1 hiro
                else if (!strcmp(attr->name, "timing")) {
519 1 hiro
                        if (!strcmp(attr->value, "any"))
520 1 hiro
                                timing = FLT_TIMING_ANY;
521 1 hiro
                        else if (!strcmp(attr->value, "receive"))
522 1 hiro
                                timing = FLT_TIMING_ON_RECEIVE;
523 1 hiro
                        else if (!strcmp(attr->value, "manual"))
524 1 hiro
                                timing = FLT_TIMING_MANUAL;
525 1 hiro
                } else if (!strcmp(attr->name, "enabled")) {
526 1 hiro
                        if (!strcmp(attr->value, "true"))
527 1 hiro
                                enabled = TRUE;
528 1 hiro
                        else
529 1 hiro
                                enabled = FALSE;
530 1 hiro
                }
531 1 hiro
        }
532 1 hiro
533 1 hiro
        /* condition list */
534 1 hiro
        child = node->children;
535 1 hiro
        if (!child) {
536 1 hiro
                g_warning("<rule> must have children\n");
537 1 hiro
                return FALSE;
538 1 hiro
        }
539 1 hiro
        xmlnode = child->data;
540 1 hiro
        RETURN_IF_TAG_NOT_MATCH("condition-list");
541 1 hiro
        for (list = xmlnode->tag->attr; list != NULL; list = list->next) {
542 1 hiro
                XMLAttr *attr = (XMLAttr *)list->data;
543 1 hiro
544 1 hiro
                if (attr && attr->name && attr->value &&
545 1 hiro
                    !strcmp(attr->name, "bool")) {
546 1 hiro
                        if (!strcmp(attr->value, "or"))
547 1 hiro
                                bool_op = FLT_OR;
548 1 hiro
                        else
549 1 hiro
                                bool_op = FLT_AND;
550 1 hiro
                }
551 1 hiro
        }
552 1 hiro
553 1 hiro
        for (cond_child = child->children; cond_child != NULL;
554 1 hiro
             cond_child = cond_child->next) {
555 1 hiro
                const gchar *type = NULL;
556 1 hiro
                const gchar *name = NULL;
557 1 hiro
                const gchar *value = NULL;
558 848 hiro
                gboolean case_sens = FALSE;
559 1 hiro
                FilterCond *cond;
560 1 hiro
                FilterCondType cond_type = FLT_COND_HEADER;
561 1 hiro
                FilterMatchType match_type = FLT_CONTAIN;
562 1 hiro
                FilterMatchFlag match_flag = 0;
563 1 hiro
564 1 hiro
                xmlnode = cond_child->data;
565 1 hiro
                if (!xmlnode || !xmlnode->tag || !xmlnode->tag->tag) continue;
566 1 hiro
567 1 hiro
                for (list = xmlnode->tag->attr; list != NULL; list = list->next) {
568 1 hiro
                        XMLAttr *attr = (XMLAttr *)list->data;
569 1 hiro
570 1 hiro
                        if (!attr || !attr->name || !attr->value) continue;
571 848 hiro
572 848 hiro
                        STR_SWITCH(attr->name)
573 848 hiro
                        STR_CASE_BEGIN("type")
574 1 hiro
                                type = attr->value;
575 848 hiro
                        STR_CASE("name")
576 1 hiro
                                name = attr->value;
577 848 hiro
                        STR_CASE("case")
578 848 hiro
                                case_sens = TRUE;
579 848 hiro
                        STR_CASE("recursive")
580 836 hiro
                                if (!strcmp(attr->value, "true"))
581 836 hiro
                                        recursive = TRUE;
582 836 hiro
                                else
583 836 hiro
                                        recursive = FALSE;
584 848 hiro
                        STR_CASE_END
585 1 hiro
                }
586 1 hiro
587 1 hiro
                if (type) {
588 1 hiro
                        filter_rule_match_type_str_to_enum
589 1 hiro
                                (type, &match_type, &match_flag);
590 1 hiro
                }
591 848 hiro
                if (case_sens)
592 848 hiro
                        match_flag |= FLT_CASE_SENS;
593 1 hiro
                value = xmlnode->element;
594 1 hiro
595 1 hiro
                STR_SWITCH(xmlnode->tag->tag)
596 1 hiro
                STR_CASE_BEGIN("match-header")
597 1 hiro
                        cond_type = FLT_COND_HEADER;
598 1 hiro
                STR_CASE("match-any-header")
599 1 hiro
                        cond_type = FLT_COND_ANY_HEADER;
600 1 hiro
                STR_CASE("match-to-or-cc")
601 1 hiro
                        cond_type = FLT_COND_TO_OR_CC;
602 1 hiro
                STR_CASE("match-body-text")
603 1 hiro
                        cond_type = FLT_COND_BODY;
604 1 hiro
                STR_CASE("command-test")
605 1 hiro
                        cond_type = FLT_COND_CMD_TEST;
606 1 hiro
                STR_CASE("size")
607 1 hiro
                        cond_type = FLT_COND_SIZE_GREATER;
608 1 hiro
                STR_CASE("age")
609 1 hiro
                        cond_type = FLT_COND_AGE_GREATER;
610 905 hiro
                STR_CASE("unread")
611 905 hiro
                        cond_type = FLT_COND_UNREAD;
612 905 hiro
                STR_CASE("mark")
613 905 hiro
                        cond_type = FLT_COND_MARK;
614 905 hiro
                STR_CASE("color-label")
615 905 hiro
                        cond_type = FLT_COND_COLOR_LABEL;
616 905 hiro
                STR_CASE("mime")
617 905 hiro
                        cond_type = FLT_COND_MIME;
618 1 hiro
                STR_CASE("account-id")
619 1 hiro
                        cond_type = FLT_COND_ACCOUNT;
620 836 hiro
                STR_CASE("target-folder")
621 836 hiro
                        target_folder = value;
622 836 hiro
                        continue;
623 1 hiro
                STR_CASE_END
624 1 hiro
625 1 hiro
                cond = filter_cond_new(cond_type, match_type, match_flag,
626 1 hiro
                                       name, value);
627 1 hiro
                cond_list = g_slist_append(cond_list, cond);
628 1 hiro
        }
629 1 hiro
630 1 hiro
        /* action list */
631 1 hiro
        child = child->next;
632 1 hiro
        if (!child) {
633 1 hiro
                g_warning("<rule> must have multiple children\n");
634 1 hiro
                filter_cond_list_free(cond_list);
635 1 hiro
                return FALSE;
636 1 hiro
        }
637 1 hiro
        xmlnode = child->data;
638 1 hiro
        RETURN_IF_TAG_NOT_MATCH("action-list");
639 1 hiro
640 1 hiro
        for (action_child = child->children; action_child != NULL;
641 1 hiro
             action_child = action_child->next) {
642 1 hiro
                FilterAction *action;
643 1 hiro
                FilterActionType action_type = FLT_ACTION_NONE;
644 1 hiro
645 1 hiro
                xmlnode = action_child->data;
646 1 hiro
                if (!xmlnode || !xmlnode->tag || !xmlnode->tag->tag) continue;
647 1 hiro
648 1 hiro
                STR_SWITCH(xmlnode->tag->tag)
649 1 hiro
                STR_CASE_BEGIN("move")
650 1 hiro
                        action_type = FLT_ACTION_MOVE;
651 1 hiro
                STR_CASE("copy")
652 1 hiro
                        action_type = FLT_ACTION_COPY;
653 1 hiro
                STR_CASE("not-receive")
654 1 hiro
                        action_type = FLT_ACTION_NOT_RECEIVE;
655 1 hiro
                STR_CASE("delete")
656 1 hiro
                        action_type = FLT_ACTION_DELETE;
657 1 hiro
                STR_CASE("exec")
658 1 hiro
                        action_type = FLT_ACTION_EXEC;
659 1 hiro
                STR_CASE("exec-async")
660 1 hiro
                        action_type = FLT_ACTION_EXEC_ASYNC;
661 1 hiro
                STR_CASE("mark")
662 1 hiro
                        action_type = FLT_ACTION_MARK;
663 1 hiro
                STR_CASE("color-label")
664 1 hiro
                        action_type = FLT_ACTION_COLOR_LABEL;
665 1 hiro
                STR_CASE("mark-as-read")
666 1 hiro
                        action_type = FLT_ACTION_MARK_READ;
667 1 hiro
                STR_CASE("forward")
668 1 hiro
                        action_type = FLT_ACTION_FORWARD;
669 1 hiro
                STR_CASE("forward-as-attachment")
670 1 hiro
                        action_type = FLT_ACTION_FORWARD_AS_ATTACHMENT;
671 1 hiro
                STR_CASE("redirect")
672 1 hiro
                        action_type = FLT_ACTION_REDIRECT;
673 1 hiro
                STR_CASE("stop-eval")
674 1 hiro
                        action_type = FLT_ACTION_STOP_EVAL;
675 1 hiro
                STR_CASE_END
676 1 hiro
677 1 hiro
                action = filter_action_new(action_type, xmlnode->element);
678 1 hiro
                action_list = g_slist_append(action_list, action);
679 1 hiro
        }
680 1 hiro
681 836 hiro
        if (name && cond_list) {
682 1 hiro
                rule = filter_rule_new(name, bool_op, cond_list, action_list);
683 1 hiro
                rule->timing = timing;
684 1 hiro
                rule->enabled = enabled;
685 836 hiro
                if (target_folder) {
686 836 hiro
                        rule->target_folder = g_strdup(target_folder);
687 836 hiro
                        rule->recursive = recursive;
688 836 hiro
                }
689 1 hiro
                *fltlist = g_slist_prepend(*fltlist, rule);
690 1 hiro
        }
691 1 hiro
692 1 hiro
        return FALSE;
693 1 hiro
}
694 1 hiro
695 1 hiro
#undef RETURN_IF_TAG_NOT_MATCH
696 1 hiro
#undef STR_SWITCH
697 1 hiro
#undef STR_CASE_BEGIN
698 1 hiro
#undef STR_CASE
699 1 hiro
#undef STR_CASE_END
700 1 hiro
701 1 hiro
GSList *filter_xml_node_to_filter_list(GNode *node)
702 1 hiro
{
703 1 hiro
        GSList *fltlist = NULL;
704 1 hiro
705 1 hiro
        g_return_val_if_fail(node != NULL, NULL);
706 1 hiro
707 1 hiro
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
708 1 hiro
                        filter_xml_node_func, &fltlist);
709 1 hiro
        fltlist = g_slist_reverse(fltlist);
710 1 hiro
711 1 hiro
        return fltlist;
712 1 hiro
}
713 1 hiro
714 836 hiro
GSList *filter_read_file(const gchar *file)
715 836 hiro
{
716 836 hiro
        GNode *node;
717 836 hiro
        GSList *list;
718 836 hiro
719 836 hiro
        g_return_val_if_fail(file != NULL, NULL);
720 836 hiro
721 836 hiro
        debug_print("Reading %s\n", file);
722 836 hiro
723 836 hiro
        if (!is_file_exist(file))
724 836 hiro
                return NULL;
725 836 hiro
726 836 hiro
        node = xml_parse_file(file);
727 836 hiro
        if (!node) {
728 836 hiro
                g_warning("Can't parse %s\n", file);
729 836 hiro
                return NULL;
730 836 hiro
        }
731 836 hiro
732 836 hiro
        list = filter_xml_node_to_filter_list(node);
733 836 hiro
734 836 hiro
        xml_free_tree(node);
735 836 hiro
736 836 hiro
        return list;
737 836 hiro
}
738 836 hiro
739 542 hiro
void filter_read_config(void)
740 542 hiro
{
741 542 hiro
        gchar *rcpath;
742 542 hiro
743 542 hiro
        debug_print("Reading filter configuration...\n");
744 542 hiro
745 542 hiro
        /* remove all previous filter list */
746 542 hiro
        while (prefs_common.fltlist != NULL) {
747 836 hiro
                FilterRule *rule = (FilterRule *)prefs_common.fltlist->data;
748 836 hiro
749 542 hiro
                filter_rule_free(rule);
750 542 hiro
                prefs_common.fltlist = g_slist_remove(prefs_common.fltlist,
751 542 hiro
                                                      rule);
752 542 hiro
        }
753 542 hiro
754 542 hiro
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST,
755 542 hiro
                             NULL);
756 836 hiro
        prefs_common.fltlist = filter_read_file(rcpath);
757 542 hiro
        g_free(rcpath);
758 542 hiro
}
759 542 hiro
760 1 hiro
#define NODE_NEW(tag, text) \
761 1 hiro
        node = xml_node_new(xml_tag_new(tag), text)
762 1 hiro
#define ADD_ATTR(name, value) \
763 1 hiro
        xml_tag_add_attr(node->tag, xml_attr_new(name, value))
764 1 hiro
765 836 hiro
void filter_write_file(GSList *list, const gchar *file)
766 1 hiro
{
767 1 hiro
        PrefFile *pfile;
768 1 hiro
        GSList *cur;
769 1 hiro
770 836 hiro
        g_return_if_fail(file != NULL);
771 1 hiro
772 836 hiro
        if ((pfile = prefs_file_open(file)) == NULL) {
773 836 hiro
                g_warning("failed to write filter configuration to file: %s\n",
774 836 hiro
                          file);
775 1 hiro
                return;
776 1 hiro
        }
777 1 hiro
778 1 hiro
        xml_file_put_xml_decl(pfile->fp);
779 1 hiro
        fputs("\n<filter>\n", pfile->fp);
780 1 hiro
781 836 hiro
        for (cur = list; cur != NULL; cur = cur->next) {
782 1 hiro
                FilterRule *rule = (FilterRule *)cur->data;
783 1 hiro
                GSList *cur_cond;
784 1 hiro
                GSList *cur_action;
785 1 hiro
                gchar match_type[16];
786 1 hiro
787 1 hiro
                fputs("    <rule name=\"", pfile->fp);
788 1 hiro
                xml_file_put_escape_str(pfile->fp, rule->name);
789 1 hiro
                fprintf(pfile->fp, "\" timing=\"%s\"",
790 1 hiro
                        rule->timing == FLT_TIMING_ON_RECEIVE ? "receive" :
791 1 hiro
                        rule->timing == FLT_TIMING_MANUAL ? "manual" : "any");
792 1 hiro
                fprintf(pfile->fp, " enabled=\"%s\">\n",
793 1 hiro
                        rule->enabled ? "true" : "false");
794 1 hiro
795 1 hiro
                fprintf(pfile->fp, "        <condition-list bool=\"%s\">\n",
796 1 hiro
                        rule->bool_op == FLT_OR ? "or" : "and");
797 1 hiro
798 1 hiro
                for (cur_cond = rule->cond_list; cur_cond != NULL;
799 1 hiro
                     cur_cond = cur_cond->next) {
800 1 hiro
                        FilterCond *cond = (FilterCond *)cur_cond->data;
801 1 hiro
                        XMLNode *node = NULL;
802 1 hiro
803 1 hiro
                        switch (cond->match_type) {
804 1 hiro
                        case FLT_CONTAIN:
805 1 hiro
                                strcpy(match_type,
806 1 hiro
                                       FLT_IS_NOT_MATCH(cond->match_flag)
807 1 hiro
                                       ? "not-contain" : "contains");
808 1 hiro
                                break;
809 1 hiro
                        case FLT_EQUAL:
810 1 hiro
                                strcpy(match_type,
811 1 hiro
                                       FLT_IS_NOT_MATCH(cond->match_flag)
812 1 hiro
                                       ? "is-not" : "is");
813 1 hiro
                                break;
814 1 hiro
                        case FLT_REGEX:
815 1 hiro
                                strcpy(match_type,
816 1 hiro
                                       FLT_IS_NOT_MATCH(cond->match_flag)
817 1 hiro
                                       ? "not-regex" : "regex");
818 1 hiro
                                break;
819 1 hiro
                        default:
820 1 hiro
                                match_type[0] = '\0';
821 1 hiro
                                break;
822 1 hiro
                        }
823 1 hiro
824 1 hiro
                        switch (cond->type) {
825 1 hiro
                        case FLT_COND_HEADER:
826 1 hiro
                                NODE_NEW("match-header", cond->str_value);
827 1 hiro
                                ADD_ATTR("type", match_type);
828 1 hiro
                                ADD_ATTR("name", cond->header_name);
829 848 hiro
                                if (FLT_IS_CASE_SENS(cond->match_flag))
830 848 hiro
                                        ADD_ATTR("case", "true");
831 1 hiro
                                break;
832 1 hiro
                        case FLT_COND_ANY_HEADER:
833 1 hiro
                                NODE_NEW("match-any-header", cond->str_value);
834 1 hiro
                                ADD_ATTR("type", match_type);
835 848 hiro
                                if (FLT_IS_CASE_SENS(cond->match_flag))
836 848 hiro
                                        ADD_ATTR("case", "true");
837 1 hiro
                                break;
838 1 hiro
                        case FLT_COND_TO_OR_CC:
839 1 hiro
                                NODE_NEW("match-to-or-cc", cond->str_value);
840 1 hiro
                                ADD_ATTR("type", match_type);
841 848 hiro
                                if (FLT_IS_CASE_SENS(cond->match_flag))
842 848 hiro
                                        ADD_ATTR("case", "true");
843 1 hiro
                                break;
844 1 hiro
                        case FLT_COND_BODY:
845 1 hiro
                                NODE_NEW("match-body-text", cond->str_value);
846 1 hiro
                                ADD_ATTR("type", match_type);
847 848 hiro
                                if (FLT_IS_CASE_SENS(cond->match_flag))
848 848 hiro
                                        ADD_ATTR("case", "true");
849 1 hiro
                                break;
850 1 hiro
                        case FLT_COND_CMD_TEST:
851 1 hiro
                                NODE_NEW("command-test", cond->str_value);
852 1 hiro
                                break;
853 1 hiro
                        case FLT_COND_SIZE_GREATER:
854 1 hiro
                                NODE_NEW("size", itos(cond->int_value));
855 1 hiro
                                ADD_ATTR("type",
856 1 hiro
                                         FLT_IS_NOT_MATCH(cond->match_flag)
857 1 hiro
                                         ? "lt" : "gt");
858 1 hiro
                                break;
859 1 hiro
                        case FLT_COND_AGE_GREATER:
860 1 hiro
                                NODE_NEW("age", itos(cond->int_value));
861 1 hiro
                                ADD_ATTR("type",
862 1 hiro
                                         FLT_IS_NOT_MATCH(cond->match_flag)
863 1 hiro
                                         ? "lt" : "gt");
864 1 hiro
                                break;
865 905 hiro
                        case FLT_COND_UNREAD:
866 905 hiro
                                NODE_NEW("unread", NULL);
867 905 hiro
                                ADD_ATTR("type",
868 905 hiro
                                         FLT_IS_NOT_MATCH(cond->match_flag)
869 905 hiro
                                         ? "is-not" : "is");
870 905 hiro
                                break;
871 905 hiro
                        case FLT_COND_MARK:
872 905 hiro
                                NODE_NEW("mark", NULL);
873 905 hiro
                                ADD_ATTR("type",
874 905 hiro
                                         FLT_IS_NOT_MATCH(cond->match_flag)
875 905 hiro
                                         ? "is-not" : "is");
876 905 hiro
                                break;
877 905 hiro
                        case FLT_COND_COLOR_LABEL:
878 905 hiro
                                NODE_NEW("color-label", NULL);
879 905 hiro
                                ADD_ATTR("type",
880 905 hiro
                                         FLT_IS_NOT_MATCH(cond->match_flag)
881 905 hiro
                                         ? "is-not" : "is");
882 905 hiro
                                break;
883 905 hiro
                        case FLT_COND_MIME:
884 905 hiro
                                NODE_NEW("mime", NULL);
885 905 hiro
                                ADD_ATTR("type",
886 905 hiro
                                         FLT_IS_NOT_MATCH(cond->match_flag)
887 905 hiro
                                         ? "is-not" : "is");
888 905 hiro
                                break;
889 1 hiro
                        case FLT_COND_ACCOUNT:
890 1 hiro
                                 NODE_NEW("account-id", itos(cond->int_value));
891 1 hiro
                                 break;
892 1 hiro
                        default:
893 1 hiro
                                 break;
894 1 hiro
                        }
895 1 hiro
896 1 hiro
                        if (node) {
897 1 hiro
                                fputs("            ", pfile->fp);
898 1 hiro
                                xml_file_put_node(pfile->fp, node);
899 1 hiro
                                xml_free_node(node);
900 1 hiro
                        }
901 1 hiro
                }
902 1 hiro
903 836 hiro
                if (rule->target_folder) {
904 836 hiro
                        XMLNode *node;
905 836 hiro
906 836 hiro
                        NODE_NEW("target-folder", rule->target_folder);
907 836 hiro
                        ADD_ATTR("recursive", rule->recursive
908 836 hiro
                                 ? "true" : "false");
909 836 hiro
                        fputs("            ", pfile->fp);
910 836 hiro
                        xml_file_put_node(pfile->fp, node);
911 836 hiro
                        xml_free_node(node);
912 836 hiro
                }
913 836 hiro
914 1 hiro
                fputs("        </condition-list>\n", pfile->fp);
915 1 hiro
916 1 hiro
                fputs("        <action-list>\n", pfile->fp);
917 1 hiro
918 1 hiro
                for (cur_action = rule->action_list; cur_action != NULL;
919 1 hiro
                     cur_action = cur_action->next) {
920 1 hiro
                        FilterAction *action = (FilterAction *)cur_action->data;
921 1 hiro
                        XMLNode *node = NULL;
922 1 hiro
923 1 hiro
                        switch (action->type) {
924 1 hiro
                        case FLT_ACTION_MOVE:
925 1 hiro
                                NODE_NEW("move", action->str_value);
926 1 hiro
                                break;
927 1 hiro
                        case FLT_ACTION_COPY:
928 1 hiro
                                NODE_NEW("copy", action->str_value);
929 1 hiro
                                break;
930 1 hiro
                        case FLT_ACTION_NOT_RECEIVE:
931 1 hiro
                                NODE_NEW("not-receive", NULL);
932 1 hiro
                                break;
933 1 hiro
                        case FLT_ACTION_DELETE:
934 1 hiro
                                NODE_NEW("delete", NULL);
935 1 hiro
                                break;
936 1 hiro
                        case FLT_ACTION_EXEC:
937 1 hiro
                                NODE_NEW("exec", action->str_value);
938 1 hiro
                                break;
939 1 hiro
                        case FLT_ACTION_EXEC_ASYNC:
940 1 hiro
                                NODE_NEW("exec-async", action->str_value);
941 1 hiro
                                break;
942 1 hiro
                        case FLT_ACTION_MARK:
943 1 hiro
                                NODE_NEW("mark", NULL);
944 1 hiro
                                break;
945 1 hiro
                        case FLT_ACTION_COLOR_LABEL:
946 1 hiro
                                NODE_NEW("color-label", action->str_value);
947 1 hiro
                                break;
948 1 hiro
                        case FLT_ACTION_MARK_READ:
949 1 hiro
                                NODE_NEW("mark-as-read", NULL);
950 1 hiro
                                break;
951 1 hiro
                        case FLT_ACTION_FORWARD:
952 1 hiro
                                NODE_NEW("forward", action->str_value);
953 1 hiro
                                break;
954 1 hiro
                        case FLT_ACTION_FORWARD_AS_ATTACHMENT:
955 1 hiro
                                NODE_NEW("forward-as-attachment",
956 1 hiro
                                         action->str_value);
957 1 hiro
                                break;
958 1 hiro
                        case FLT_ACTION_REDIRECT:
959 1 hiro
                                NODE_NEW("redirect", action->str_value);
960 1 hiro
                                break;
961 1 hiro
                        case FLT_ACTION_STOP_EVAL:
962 1 hiro
                                NODE_NEW("stop-eval", NULL);
963 1 hiro
                                break;
964 1 hiro
                        default:
965 1 hiro
                                break;
966 1 hiro
                        }
967 1 hiro
968 1 hiro
                        if (node) {
969 1 hiro
                                fputs("            ", pfile->fp);
970 1 hiro
                                xml_file_put_node(pfile->fp, node);
971 1 hiro
                                xml_free_node(node);
972 1 hiro
                        }
973 1 hiro
                }
974 1 hiro
975 1 hiro
                fputs("        </action-list>\n", pfile->fp);
976 1 hiro
977 1 hiro
                fputs("    </rule>\n", pfile->fp);
978 1 hiro
        }
979 1 hiro
980 1 hiro
        fputs("</filter>\n", pfile->fp);
981 1 hiro
982 1 hiro
        if (prefs_file_close(pfile) < 0) {
983 836 hiro
                g_warning("failed to write filter configuration to file: %s\n",
984 836 hiro
                          file);
985 1 hiro
                return;
986 1 hiro
        }
987 1 hiro
}
988 1 hiro
989 836 hiro
void filter_write_config(void)
990 836 hiro
{
991 836 hiro
        gchar *rcpath;
992 836 hiro
993 836 hiro
        debug_print("Writing filter configuration...\n");
994 836 hiro
995 836 hiro
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST,
996 836 hiro
                             NULL);
997 836 hiro
        filter_write_file(prefs_common.fltlist, rcpath);
998 836 hiro
        g_free(rcpath);
999 836 hiro
}
1000 836 hiro
1001 1 hiro
#undef NODE_NEW
1002 1 hiro
#undef ADD_ATTR
1003 1 hiro
1004 1 hiro
gchar *filter_get_str(FilterRule *rule)
1005 1 hiro
{
1006 1 hiro
        gchar *str;
1007 1 hiro
        FilterCond *cond1, *cond2;
1008 1 hiro
        FilterAction *action = NULL;
1009 1 hiro
        GSList *cur;
1010 1 hiro
        gint flag1 = 0, flag2 = 0;
1011 1 hiro
1012 1 hiro
        cond1 = (FilterCond *)rule->cond_list->data;
1013 1 hiro
        if (rule->cond_list->next)
1014 1 hiro
                cond2 = (FilterCond *)rule->cond_list->next->data;
1015 1 hiro
        else
1016 1 hiro
                cond2 = NULL;
1017 1 hiro
1018 1 hiro
        /* new -> old flag conversion */
1019 1 hiro
        switch (cond1->match_type) {
1020 1 hiro
        case FLT_CONTAIN:
1021 1 hiro
        case FLT_EQUAL:
1022 1 hiro
                flag1 = FLT_IS_NOT_MATCH(cond1->match_flag) ? 0 : FLT_O_CONTAIN;
1023 1 hiro
                if (FLT_IS_CASE_SENS(cond1->match_flag))
1024 1 hiro
                        flag1 |= FLT_O_CASE_SENS;
1025 1 hiro
                break;
1026 1 hiro
        case FLT_REGEX:
1027 1 hiro
                flag1 = FLT_O_REGEX; break;
1028 1 hiro
        default:
1029 1 hiro
                break;
1030 1 hiro
        }
1031 1 hiro
        if (cond2) {
1032 1 hiro
                switch (cond2->match_type) {
1033 1 hiro
                case FLT_CONTAIN:
1034 1 hiro
                case FLT_EQUAL:
1035 1 hiro
                        flag2 = FLT_IS_NOT_MATCH(cond2->match_flag) ? 0 : FLT_O_CONTAIN;
1036 1 hiro
                        if (FLT_IS_CASE_SENS(cond2->match_flag))
1037 1 hiro
                                flag2 |= FLT_O_CASE_SENS;
1038 1 hiro
                        break;
1039 1 hiro
                case FLT_REGEX:
1040 1 hiro
                        flag2 = FLT_O_REGEX; break;
1041 1 hiro
                default:
1042 1 hiro
                        break;
1043 1 hiro
                }
1044 1 hiro
        } else
1045 1 hiro
                flag2 = FLT_O_CONTAIN;
1046 1 hiro
1047 1 hiro
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
1048 1 hiro
                action = (FilterAction *)cur->data;
1049 1 hiro
                if (action->type == FLT_ACTION_MOVE ||
1050 1 hiro
                    action->type == FLT_ACTION_NOT_RECEIVE ||
1051 1 hiro
                    action->type == FLT_ACTION_DELETE)
1052 1 hiro
                        break;
1053 1 hiro
        }
1054 1 hiro
1055 1 hiro
        str = g_strdup_printf
1056 1 hiro
                ("%s:%s:%c:%s:%s:%s:%d:%d:%c",
1057 1 hiro
                 cond1->header_name, cond1->str_value ? cond1->str_value : "",
1058 1 hiro
                 (cond2 && cond2->header_name) ?
1059 1 hiro
                         (rule->bool_op == FLT_AND ? '&' : '|') : ' ',
1060 1 hiro
                 (cond2 && cond2->header_name) ? cond2->header_name : "",
1061 1 hiro
                 (cond2 && cond2->str_value) ? cond2->str_value : "",
1062 1 hiro
                 (action && action->str_value) ? action->str_value : "",
1063 1 hiro
                 flag1, flag2,
1064 1 hiro
                 (action && action->type == FLT_ACTION_MOVE) ? 'm' :
1065 1 hiro
                 (action && action->type == FLT_ACTION_NOT_RECEIVE) ? 'n' :
1066 1 hiro
                 (action && action->type == FLT_ACTION_DELETE) ? 'd' : ' ');
1067 1 hiro
1068 1 hiro
        return str;
1069 1 hiro
}
1070 1 hiro
1071 1 hiro
#define PARSE_ONE_PARAM(p, srcp) \
1072 1 hiro
{ \
1073 1 hiro
        p = strchr(srcp, '\t'); \
1074 1 hiro
        if (!p) return NULL; \
1075 1 hiro
        else \
1076 1 hiro
                *p++ = '\0'; \
1077 1 hiro
}
1078 1 hiro
1079 1 hiro
FilterRule *filter_read_str(const gchar *str)
1080 1 hiro
{
1081 1 hiro
        FilterRule *rule;
1082 1 hiro
        FilterBoolOp bool_op;
1083 1 hiro
        gint flag;
1084 1 hiro
        FilterCond *cond;
1085 1 hiro
        FilterMatchType match_type;
1086 1 hiro
        FilterMatchFlag match_flag;
1087 1 hiro
        FilterAction *action;
1088 1 hiro
        GSList *cond_list = NULL;
1089 1 hiro
        GSList *action_list = NULL;
1090 1 hiro
        gchar *tmp;
1091 1 hiro
        gchar *rule_name;
1092 1 hiro
        gchar *name1, *body1, *op, *name2, *body2, *dest;
1093 1 hiro
        gchar *flag1 = NULL, *flag2 = NULL, *action1 = NULL;
1094 1 hiro
1095 1 hiro
        Xstrdup_a(tmp, str, return NULL);
1096 1 hiro
1097 1 hiro
        name1 = tmp;
1098 1 hiro
        PARSE_ONE_PARAM(body1, name1);
1099 1 hiro
        PARSE_ONE_PARAM(op, body1);
1100 1 hiro
        PARSE_ONE_PARAM(name2, op);
1101 1 hiro
        PARSE_ONE_PARAM(body2, name2);
1102 1 hiro
        PARSE_ONE_PARAM(dest, body2);
1103 1 hiro
        if (strchr(dest, '\t')) {
1104 1 hiro
                gchar *p;
1105 1 hiro
1106 1 hiro
                PARSE_ONE_PARAM(flag1, dest);
1107 1 hiro
                PARSE_ONE_PARAM(flag2, flag1);
1108 1 hiro
                PARSE_ONE_PARAM(action1, flag2);
1109 1 hiro
                if ((p = strchr(action1, '\t'))) *p = '\0';
1110 1 hiro
        }
1111 1 hiro
1112 1 hiro
        bool_op = (*op == '&') ? FLT_AND : FLT_OR;
1113 1 hiro
1114 1 hiro
        if (*name1) {
1115 1 hiro
                if (flag1)
1116 1 hiro
                        flag = (FilterOldFlag)strtoul(flag1, NULL, 10);
1117 1 hiro
                else
1118 1 hiro
                        flag = FLT_O_CONTAIN;
1119 1 hiro
                match_type = FLT_CONTAIN;
1120 1 hiro
                match_flag = 0;
1121 1 hiro
                if (flag & FLT_O_REGEX)
1122 1 hiro
                        match_type = FLT_REGEX;
1123 1 hiro
                else if (!(flag & FLT_O_CONTAIN))
1124 1 hiro
                        match_flag = FLT_NOT_MATCH;
1125 1 hiro
                if (flag & FLT_O_CASE_SENS)
1126 1 hiro
                        match_flag |= FLT_CASE_SENS;
1127 1 hiro
                cond = filter_cond_new(FLT_COND_HEADER, match_type, match_flag,
1128 1 hiro
                                       name1, body1);
1129 1 hiro
                cond_list = g_slist_append(cond_list, cond);
1130 1 hiro
        }
1131 1 hiro
        if (*name2) {
1132 1 hiro
                if (flag2)
1133 1 hiro
                        flag = (FilterOldFlag)strtoul(flag2, NULL, 10);
1134 1 hiro
                else
1135 1 hiro
                        flag = FLT_O_CONTAIN;
1136 1 hiro
                match_type = FLT_CONTAIN;
1137 1 hiro
                match_flag = 0;
1138 1 hiro
                if (flag & FLT_O_REGEX)
1139 1 hiro
                        match_type = FLT_REGEX;
1140 1 hiro
                else if (!(flag & FLT_O_CONTAIN))
1141 1 hiro
                        match_flag = FLT_NOT_MATCH;
1142 1 hiro
                if (flag & FLT_O_CASE_SENS)
1143 1 hiro
                        match_flag |= FLT_CASE_SENS;
1144 1 hiro
                cond = filter_cond_new(FLT_COND_HEADER, match_type, match_flag,
1145 1 hiro
                                       name2, body2);
1146 1 hiro
                cond_list = g_slist_append(cond_list, cond);
1147 1 hiro
        }
1148 1 hiro
1149 1 hiro
        action = filter_action_new(FLT_ACTION_MOVE,
1150 1 hiro
                                   *dest ? g_strdup(dest) : NULL);
1151 1 hiro
        if (action1) {
1152 1 hiro
                switch (*action1) {
1153 1 hiro
                case 'm': action->type = FLT_ACTION_MOVE;                break;
1154 1 hiro
                case 'n': action->type = FLT_ACTION_NOT_RECEIVE;        break;
1155 1 hiro
                case 'd': action->type = FLT_ACTION_DELETE;                break;
1156 1 hiro
                default:  g_warning("Invalid action: `%c'\n", *action1);
1157 1 hiro
                }
1158 1 hiro
        }
1159 1 hiro
        action_list = g_slist_append(action_list, action);
1160 1 hiro
1161 1 hiro
        Xstrdup_a(rule_name, str, return NULL);
1162 1 hiro
        subst_char(rule_name, '\t', ':');
1163 1 hiro
1164 1 hiro
        rule = filter_rule_new(rule_name, bool_op, cond_list, action_list);
1165 1 hiro
1166 1 hiro
        return rule;
1167 1 hiro
}
1168 1 hiro
1169 1 hiro
FilterRule *filter_rule_new(const gchar *name, FilterBoolOp bool_op,
1170 1 hiro
                            GSList *cond_list, GSList *action_list)
1171 1 hiro
{
1172 1 hiro
        FilterRule *rule;
1173 1 hiro
1174 1 hiro
        rule = g_new0(FilterRule, 1);
1175 1 hiro
        rule->name = g_strdup(name);
1176 1 hiro
        rule->bool_op = bool_op;
1177 1 hiro
        rule->cond_list = cond_list;
1178 1 hiro
        rule->action_list = action_list;
1179 1 hiro
        rule->timing = FLT_TIMING_ANY;
1180 1 hiro
        rule->enabled = TRUE;
1181 1 hiro
1182 1 hiro
        return rule;
1183 1 hiro
}
1184 1 hiro
1185 1 hiro
FilterCond *filter_cond_new(FilterCondType type,
1186 1 hiro
                            FilterMatchType match_type,
1187 1 hiro
                            FilterMatchFlag match_flag,
1188 1 hiro
                            const gchar *header, const gchar *value)
1189 1 hiro
{
1190 1 hiro
        FilterCond *cond;
1191 1 hiro
1192 1 hiro
        cond = g_new0(FilterCond, 1);
1193 1 hiro
        cond->type = type;
1194 1 hiro
        cond->match_type = match_type;
1195 1 hiro
        cond->match_flag = match_flag;
1196 1 hiro
1197 1 hiro
        if (type == FLT_COND_HEADER)
1198 1 hiro
                cond->header_name =
1199 1 hiro
                        (header && *header) ? g_strdup(header) : NULL;
1200 1 hiro
        else
1201 1 hiro
                cond->header_name = NULL;
1202 1 hiro
1203 1 hiro
        cond->str_value = (value && *value) ? g_strdup(value) : NULL;
1204 1 hiro
        if (type == FLT_COND_SIZE_GREATER || type == FLT_COND_AGE_GREATER)
1205 1 hiro
                cond->int_value = atoi(value);
1206 1 hiro
        else
1207 1 hiro
                cond->int_value = 0;
1208 1 hiro
1209 1 hiro
        if (match_type == FLT_REGEX)
1210 1 hiro
                cond->match_func = strmatch_regex;
1211 1 hiro
        else if (match_type == FLT_EQUAL) {
1212 1 hiro
                if (FLT_IS_CASE_SENS(match_flag))
1213 1 hiro
                        cond->match_func = str_find_equal;
1214 1 hiro
                else
1215 1 hiro
                        cond->match_func = str_case_find_equal;
1216 1 hiro
        } else {
1217 1 hiro
                if (FLT_IS_CASE_SENS(match_flag))
1218 1 hiro
                        cond->match_func = str_find;
1219 1 hiro
                else
1220 1 hiro
                        cond->match_func = str_case_find;
1221 1 hiro
        }
1222 1 hiro
1223 1 hiro
        return cond;
1224 1 hiro
}
1225 1 hiro
1226 1 hiro
FilterAction *filter_action_new(FilterActionType type, const gchar *str)
1227 1 hiro
{
1228 1 hiro
        FilterAction *action;
1229 1 hiro
1230 1 hiro
        action = g_new0(FilterAction, 1);
1231 1 hiro
        action->type = type;
1232 1 hiro
1233 1 hiro
        action->str_value = (str && *str) ? g_strdup(str) : NULL;
1234 1 hiro
        if (type == FLT_ACTION_COLOR_LABEL && str)
1235 1 hiro
                action->int_value = atoi(str);
1236 1 hiro
        else
1237 1 hiro
                action->int_value = 0;
1238 1 hiro
1239 1 hiro
        return action;
1240 1 hiro
}
1241 1 hiro
1242 1 hiro
FilterInfo *filter_info_new(void)
1243 1 hiro
{
1244 1 hiro
        FilterInfo *fltinfo;
1245 1 hiro
1246 1 hiro
        fltinfo = g_new0(FilterInfo, 1);
1247 1 hiro
        fltinfo->dest_list = NULL;
1248 1 hiro
        fltinfo->move_dest = NULL;
1249 1 hiro
        fltinfo->drop_done = FALSE;
1250 1 hiro
1251 1 hiro
        return fltinfo;
1252 1 hiro
}
1253 1 hiro
1254 1 hiro
void filter_rule_rename_dest_path(FilterRule *rule, const gchar *old_path,
1255 1 hiro
                                  const gchar *new_path)
1256 1 hiro
{
1257 1 hiro
        FilterAction *action;
1258 1 hiro
        GSList *cur;
1259 1 hiro
        gchar *base;
1260 1 hiro
        gchar *dest_path;
1261 1 hiro
        gint oldpathlen;
1262 1 hiro
1263 388 hiro
        oldpathlen = strlen(old_path);
1264 388 hiro
1265 1 hiro
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
1266 1 hiro
                action = (FilterAction *)cur->data;
1267 1 hiro
1268 1 hiro
                if (action->type != FLT_ACTION_MOVE &&
1269 1 hiro
                    action->type != FLT_ACTION_COPY)
1270 1 hiro
                        continue;
1271 1 hiro
1272 1 hiro
                if (action->str_value &&
1273 1 hiro
                    !strncmp(old_path, action->str_value, oldpathlen)) {
1274 1 hiro
                        base = action->str_value + oldpathlen;
1275 388 hiro
                        if (*base != G_DIR_SEPARATOR && *base != '\0')
1276 388 hiro
                                continue;
1277 1 hiro
                        while (*base == G_DIR_SEPARATOR) base++;
1278 1 hiro
                        if (*base == '\0')
1279 1 hiro
                                dest_path = g_strdup(new_path);
1280 1 hiro
                        else
1281 1 hiro
                                dest_path = g_strconcat(new_path,
1282 1 hiro
                                                        G_DIR_SEPARATOR_S,
1283 1 hiro
                                                        base, NULL);
1284 429 hiro
                        debug_print("filter_rule_rename_dest_path(): "
1285 429 hiro
                                    "renaming %s -> %s\n",
1286 429 hiro
                                    action->str_value, dest_path);
1287 1 hiro
                        g_free(action->str_value);
1288 1 hiro
                        action->str_value = dest_path;
1289 1 hiro
                }
1290 1 hiro
        }
1291 1 hiro
}
1292 1 hiro
1293 1 hiro
void filter_rule_delete_action_by_dest_path(FilterRule *rule, const gchar *path)
1294 1 hiro
{
1295 1 hiro
        FilterAction *action;
1296 1 hiro
        GSList *cur;
1297 1 hiro
        GSList *next;
1298 388 hiro
        gint pathlen;
1299 1 hiro
1300 388 hiro
        pathlen = strlen(path);
1301 388 hiro
1302 1 hiro
        for (cur = rule->action_list; cur != NULL; cur = next) {
1303 1 hiro
                action = (FilterAction *)cur->data;
1304 1 hiro
                next = cur->next;
1305 1 hiro
1306 1 hiro
                if (action->type != FLT_ACTION_MOVE &&
1307 1 hiro
                    action->type != FLT_ACTION_COPY)
1308 1 hiro
                        continue;
1309 1 hiro
1310 1 hiro
                if (action->str_value &&
1311 388 hiro
                    !strncmp(path, action->str_value, pathlen) &&
1312 388 hiro
                    (action->str_value[pathlen] == G_DIR_SEPARATOR ||
1313 388 hiro
                     action->str_value[pathlen] == '\0')) {
1314 429 hiro
                        debug_print("filter_rule_delete_action_by_dest_path(): "
1315 429 hiro
                                    "deleting %s\n", action->str_value);
1316 1 hiro
                        rule->action_list = g_slist_remove
1317 1 hiro
                                (rule->action_list, action);
1318 1 hiro
                        filter_action_free(action);
1319 1 hiro
                }
1320 1 hiro
        }
1321 1 hiro
}
1322 1 hiro
1323 543 hiro
void filter_list_rename_path(const gchar *old_path, const gchar *new_path)
1324 543 hiro
{
1325 543 hiro
        GSList *cur;
1326 543 hiro
1327 543 hiro
        g_return_if_fail(old_path != NULL);
1328 543 hiro
        g_return_if_fail(new_path != NULL);
1329 543 hiro
1330 543 hiro
        for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
1331 543 hiro
                FilterRule *rule = (FilterRule *)cur->data;
1332 543 hiro
                filter_rule_rename_dest_path(rule, old_path, new_path);
1333 543 hiro
        }
1334 543 hiro
1335 543 hiro
        filter_write_config();
1336 543 hiro
}
1337 543 hiro
1338 543 hiro
void filter_list_delete_path(const gchar *path)
1339 543 hiro
{
1340 543 hiro
        GSList *cur;
1341 543 hiro
        GSList *next;
1342 543 hiro
1343 543 hiro
        g_return_if_fail(path != NULL);
1344 543 hiro
1345 543 hiro
        for (cur = prefs_common.fltlist; cur != NULL; cur = next) {
1346 543 hiro
                FilterRule *rule = (FilterRule *)cur->data;
1347 543 hiro
                next = cur->next;
1348 543 hiro
1349 543 hiro
                filter_rule_delete_action_by_dest_path(rule, path);
1350 543 hiro
                if (!rule->action_list) {
1351 543 hiro
                        prefs_common.fltlist =
1352 543 hiro
                                g_slist_remove(prefs_common.fltlist, rule);
1353 543 hiro
                        filter_rule_free(rule);
1354 543 hiro
                }
1355 543 hiro
        }
1356 543 hiro
1357 543 hiro
        filter_write_config();
1358 543 hiro
}
1359 543 hiro
1360 1 hiro
void filter_rule_match_type_str_to_enum(const gchar *match_type,
1361 1 hiro
                                        FilterMatchType *type,
1362 1 hiro
                                        FilterMatchFlag *flag)
1363 1 hiro
{
1364 1 hiro
        g_return_if_fail(match_type != NULL);
1365 1 hiro
1366 1 hiro
        *type = FLT_CONTAIN;
1367 1 hiro
        *flag = 0;
1368 1 hiro
1369 1 hiro
        if (!strcmp(match_type, "contains")) {
1370 1 hiro
                *type = FLT_CONTAIN;
1371 1 hiro
        } else if (!strcmp(match_type, "not-contain")) {
1372 1 hiro
                *type = FLT_CONTAIN;
1373 1 hiro
                *flag = FLT_NOT_MATCH;
1374 1 hiro
        } else if (!strcmp(match_type, "is")) {
1375 1 hiro
                *type = FLT_EQUAL;
1376 1 hiro
        } else if (!strcmp(match_type, "is-not")) {
1377 1 hiro
                *type = FLT_EQUAL;
1378 1 hiro
                *flag = FLT_NOT_MATCH;
1379 1 hiro
        } else if (!strcmp(match_type, "regex")) {
1380 1 hiro
                *type = FLT_REGEX;
1381 1 hiro
        } else if (!strcmp(match_type, "not-regex")) {
1382 1 hiro
                *type = FLT_REGEX;
1383 1 hiro
                *flag = FLT_NOT_MATCH;
1384 1 hiro
        } else if (!strcmp(match_type, "gt")) {
1385 1 hiro
        } else if (!strcmp(match_type, "lt")) {
1386 1 hiro
                *flag = FLT_NOT_MATCH;
1387 1 hiro
        }
1388 1 hiro
}
1389 1 hiro
1390 547 hiro
void filter_get_keyword_from_msg(MsgInfo *msginfo, gchar **header, gchar **key,
1391 547 hiro
                                 FilterCreateType type)
1392 547 hiro
{
1393 547 hiro
        static HeaderEntry hentry[] = {{"List-Id:",           NULL, TRUE},
1394 547 hiro
                                       {"X-ML-Name:",           NULL, TRUE},
1395 547 hiro
                                       {"X-List:",           NULL, TRUE},
1396 547 hiro
                                       {"X-Mailing-list:", NULL, TRUE},
1397 547 hiro
                                       {"X-Sequence:",           NULL, TRUE},
1398 547 hiro
                                       {NULL,                   NULL, FALSE}};
1399 547 hiro
        enum
1400 547 hiro
        {
1401 547 hiro
                H_LIST_ID         = 0,
1402 547 hiro
                H_X_ML_NAME         = 1,
1403 547 hiro
                H_X_LIST         = 2,
1404 547 hiro
                H_X_MAILING_LIST = 3,
1405 547 hiro
                H_X_SEQUENCE         = 4
1406 547 hiro
        };
1407 547 hiro
1408 547 hiro
        FILE *fp;
1409 547 hiro
1410 547 hiro
        g_return_if_fail(msginfo != NULL);
1411 547 hiro
        g_return_if_fail(header != NULL);
1412 547 hiro
        g_return_if_fail(key != NULL);
1413 547 hiro
1414 547 hiro
        *header = NULL;
1415 547 hiro
        *key = NULL;
1416 547 hiro
1417 547 hiro
        switch (type) {
1418 547 hiro
        case FLT_BY_NONE:
1419 547 hiro
                return;
1420 547 hiro
        case FLT_BY_AUTO:
1421 547 hiro
                if ((fp = procmsg_open_message(msginfo)) == NULL)
1422 547 hiro
                        return;
1423 547 hiro
                procheader_get_header_fields(fp, hentry);
1424 547 hiro
                fclose(fp);
1425 547 hiro
1426 547 hiro
#define SET_FILTER_KEY(hstr, idx)        \
1427 547 hiro
{                                        \
1428 547 hiro
        *header = g_strdup(hstr);        \
1429 547 hiro
        *key = hentry[idx].body;        \
1430 547 hiro
        hentry[idx].body = NULL;        \
1431 547 hiro
}
1432 547 hiro
1433 547 hiro
                if (hentry[H_LIST_ID].body != NULL) {
1434 547 hiro
                        SET_FILTER_KEY("List-Id", H_LIST_ID);
1435 547 hiro
                        extract_list_id_str(*key);
1436 547 hiro
                } else if (hentry[H_X_ML_NAME].body != NULL) {
1437 547 hiro
                        SET_FILTER_KEY("X-ML-Name", H_X_ML_NAME);
1438 547 hiro
                } else if (hentry[H_X_LIST].body != NULL) {
1439 547 hiro
                        SET_FILTER_KEY("X-List", H_X_LIST);
1440 547 hiro
                } else if (hentry[H_X_MAILING_LIST].body != NULL) {
1441 547 hiro
                        SET_FILTER_KEY("X-Mailing-list", H_X_MAILING_LIST);
1442 547 hiro
                } else if (hentry[H_X_SEQUENCE].body != NULL) {
1443 547 hiro
                        gchar *p;
1444 547 hiro
1445 547 hiro
                        SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
1446 547 hiro
                        p = *key;
1447 547 hiro
                        while (*p != '\0') {
1448 547 hiro
                                while (*p != '\0' && !g_ascii_isspace(*p)) p++;
1449 547 hiro
                                while (g_ascii_isspace(*p)) p++;
1450 547 hiro
                                if (g_ascii_isdigit(*p)) {
1451 547 hiro
                                        *p = '\0';
1452 547 hiro
                                        break;
1453 547 hiro
                                }
1454 547 hiro
                        }
1455 547 hiro
                        g_strstrip(*key);
1456 547 hiro
                } else if (msginfo->subject) {
1457 547 hiro
                        *header = g_strdup("Subject");
1458 547 hiro
                        *key = g_strdup(msginfo->subject);
1459 547 hiro
                }
1460 547 hiro
1461 547 hiro
#undef SET_FILTER_KEY
1462 547 hiro
1463 547 hiro
                g_free(hentry[H_LIST_ID].body);
1464 547 hiro
                hentry[H_LIST_ID].body = NULL;
1465 547 hiro
                g_free(hentry[H_X_ML_NAME].body);
1466 547 hiro
                hentry[H_X_ML_NAME].body = NULL;
1467 547 hiro
                g_free(hentry[H_X_LIST].body);
1468 547 hiro
                hentry[H_X_LIST].body = NULL;
1469 547 hiro
                g_free(hentry[H_X_MAILING_LIST].body);
1470 547 hiro
                hentry[H_X_MAILING_LIST].body = NULL;
1471 547 hiro
1472 547 hiro
                break;
1473 547 hiro
        case FLT_BY_FROM:
1474 547 hiro
                *header = g_strdup("From");
1475 547 hiro
                *key = g_strdup(msginfo->from);
1476 547 hiro
                break;
1477 547 hiro
        case FLT_BY_TO:
1478 547 hiro
                *header = g_strdup("To");
1479 547 hiro
                *key = g_strdup(msginfo->to);
1480 547 hiro
                break;
1481 547 hiro
        case FLT_BY_SUBJECT:
1482 547 hiro
                *header = g_strdup("Subject");
1483 547 hiro
                *key = g_strdup(msginfo->subject);
1484 547 hiro
                break;
1485 547 hiro
        default:
1486 547 hiro
                break;
1487 547 hiro
        }
1488 547 hiro
}
1489 547 hiro
1490 336 hiro
void filter_rule_list_free(GSList *fltlist)
1491 336 hiro
{
1492 336 hiro
        GSList *cur;
1493 336 hiro
1494 336 hiro
        for (cur = fltlist; cur != NULL; cur = cur->next)
1495 336 hiro
                filter_rule_free((FilterRule *)cur->data);
1496 336 hiro
        g_slist_free(fltlist);
1497 336 hiro
}
1498 336 hiro
1499 1 hiro
void filter_rule_free(FilterRule *rule)
1500 1 hiro
{
1501 1 hiro
        if (!rule) return;
1502 1 hiro
1503 1 hiro
        g_free(rule->name);
1504 836 hiro
        g_free(rule->target_folder);
1505 1 hiro
1506 1 hiro
        filter_cond_list_free(rule->cond_list);
1507 1 hiro
        filter_action_list_free(rule->action_list);
1508 1 hiro
1509 1 hiro
        g_free(rule);
1510 1 hiro
}
1511 1 hiro
1512 1 hiro
void filter_cond_list_free(GSList *cond_list)
1513 1 hiro
{
1514 1 hiro
        GSList *cur;
1515 1 hiro
1516 1 hiro
        for (cur = cond_list; cur != NULL; cur = cur->next)
1517 1 hiro
                filter_cond_free((FilterCond *)cur->data);
1518 1 hiro
        g_slist_free(cond_list);
1519 1 hiro
}
1520 1 hiro
1521 1 hiro
void filter_action_list_free(GSList *action_list)
1522 1 hiro
{
1523 1 hiro
        GSList *cur;
1524 1 hiro
1525 1 hiro
        for (cur = action_list; cur != NULL; cur = cur->next)
1526 1 hiro
                filter_action_free((FilterAction *)cur->data);
1527 1 hiro
        g_slist_free(action_list);
1528 1 hiro
}
1529 1 hiro
1530 1 hiro
static void filter_cond_free(FilterCond *cond)
1531 1 hiro
{
1532 1 hiro
        g_free(cond->header_name);
1533 1 hiro
        g_free(cond->str_value);
1534 1 hiro
        g_free(cond);
1535 1 hiro
}
1536 1 hiro
1537 1 hiro
static void filter_action_free(FilterAction *action)
1538 1 hiro
{
1539 1 hiro
        g_free(action->str_value);
1540 1 hiro
        g_free(action);
1541 1 hiro
}
1542 1 hiro
1543 1 hiro
void filter_info_free(FilterInfo *fltinfo)
1544 1 hiro
{
1545 1 hiro
        g_slist_free(fltinfo->dest_list);
1546 1 hiro
        g_free(fltinfo);
1547 1 hiro
}