Statistics
| Revision:

root / libsylph / filter.c @ 3222

History | View | Annotate | Download (47.7 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2011 Hiroyuki Yamamoto
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library 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
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <string.h>
29
#include <strings.h>
30
#include <stdlib.h>
31
#include <sys/types.h>
32
#if USE_ONIGURUMA
33
#  include <onigposix.h>
34
#elif HAVE_REGEX_H
35
#  include <regex.h>
36
#endif
37
#include <time.h>
38

    
39
#include "filter.h"
40
#include "procmsg.h"
41
#include "procheader.h"
42
#include "folder.h"
43
#include "utils.h"
44
#include "xml.h"
45
#include "prefs.h"
46
#include "prefs_common.h"
47
#include "prefs_account.h"
48
#include "account.h"
49

    
50
typedef enum
51
{
52
        FLT_O_CONTAIN        = 1 << 0,
53
        FLT_O_CASE_SENS        = 1 << 1,
54
        FLT_O_REGEX        = 1 << 2
55
} FilterOldFlag;
56

    
57
static FilterInAddressBookFunc default_addrbook_func = NULL;
58

    
59
static gboolean filter_match_cond        (FilterCond        *cond,
60
                                         MsgInfo        *msginfo,
61
                                         GSList                *hlist,
62
                                         FilterInfo        *fltinfo);
63
static gboolean filter_match_header_cond(FilterCond        *cond,
64
                                         GSList                *hlist);
65
static gboolean filter_match_in_addressbook
66
                                        (FilterCond        *cond,
67
                                         GSList                *hlist,
68
                                         FilterInfo        *fltinfo);
69

    
70
static void filter_cond_free                (FilterCond        *cond);
71
static void filter_action_free                (FilterAction        *action);
72

    
73

    
74
gint filter_apply(GSList *fltlist, const gchar *file, FilterInfo *fltinfo)
75
{
76
        MsgInfo *msginfo;
77
        gint ret = 0;
78

    
79
        g_return_val_if_fail(file != NULL, -1);
80
        g_return_val_if_fail(fltinfo != NULL, -1);
81

    
82
        if (!fltlist) return 0;
83

    
84
        msginfo = procheader_parse_file(file, fltinfo->flags, FALSE);
85
        if (!msginfo) return 0;
86
        msginfo->file_path = g_strdup(file);
87

    
88
        /* inherit MIME flag */
89
        fltinfo->flags.tmp_flags =
90
                (fltinfo->flags.tmp_flags & ~MSG_CACHED_FLAG_MASK) |
91
                (msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK);
92

    
93
        ret = filter_apply_msginfo(fltlist, msginfo, fltinfo);
94

    
95
        procmsg_msginfo_free(msginfo);
96

    
97
        return ret;
98
}
99

    
100
gint filter_apply_msginfo(GSList *fltlist, MsgInfo *msginfo,
101
                          FilterInfo *fltinfo)
102
{
103
        gchar *file;
104
        GSList *hlist, *cur;
105
        FilterRule *rule;
106
        gint ret = 0;
107

    
108
        g_return_val_if_fail(msginfo != NULL, -1);
109
        g_return_val_if_fail(fltinfo != NULL, -1);
110

    
111
        fltinfo->error = FLT_ERROR_OK;
112

    
113
        if (!fltlist) return 0;
114

    
115
        file = procmsg_get_message_file(msginfo);
116
        if (!file)
117
                return -1;
118
        hlist = procheader_get_header_list_from_file(file);
119
        if (!hlist) {
120
                g_free(file);
121
                return 0;
122
        }
123

    
124
        procmsg_set_auto_decrypt_message(FALSE);
125

    
126
        for (cur = fltlist; cur != NULL; cur = cur->next) {
127
                gboolean matched;
128

    
129
                rule = (FilterRule *)cur->data;
130
                if (!rule->enabled) continue;
131
                matched = filter_match_rule(rule, msginfo, hlist, fltinfo);
132
                if (fltinfo->error != FLT_ERROR_OK) {
133
                        g_warning("filter_match_rule() returned error (code: %d)\n", fltinfo->error);
134
                }
135
                if (matched) {
136
                        debug_print("filter-log: %s: rule [%s] matched\n",
137
                                    G_STRFUNC, rule->name ? rule->name : "(No name)");
138
                        ret = filter_action_exec(rule, msginfo, file, fltinfo);
139
                        if (ret < 0) {
140
                                g_warning("filter_action_exec() returned error (code: %d)\n", fltinfo->error);
141
                                break;
142
                        }
143
                        if (fltinfo->drop_done == TRUE ||
144
                            fltinfo->actions[FLT_ACTION_STOP_EVAL] == TRUE)
145
                                break;
146
                }
147
        }
148

    
149
        procmsg_set_auto_decrypt_message(TRUE);
150

    
151
        procheader_header_list_destroy(hlist);
152
        g_free(file);
153

    
154
        return ret;
155
}
156

    
157
gint filter_action_exec(FilterRule *rule, MsgInfo *msginfo, const gchar *file,
158
                        FilterInfo *fltinfo)
159
{
160
        FolderItem *dest_folder = NULL;
161
        FilterAction *action;
162
        GSList *cur;
163
        gchar *cmdline;
164
        gboolean copy_to_self = FALSE;
165
        gint ret;
166

    
167
        g_return_val_if_fail(rule != NULL, -1);
168
        g_return_val_if_fail(msginfo != NULL, -1);
169
        g_return_val_if_fail(file != NULL, -1);
170
        g_return_val_if_fail(fltinfo != NULL, -1);
171

    
172
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
173
                action = (FilterAction *)cur->data;
174

    
175
                switch (action->type) {
176
                case FLT_ACTION_MARK:
177
                        debug_print("filter_action_exec(): mark\n");
178
                        MSG_SET_PERM_FLAGS(fltinfo->flags, MSG_MARKED);
179
                        fltinfo->actions[action->type] = TRUE;
180
                        break;
181
                case FLT_ACTION_COLOR_LABEL:
182
                        debug_print("filter_action_exec(): color label: %d\n",
183
                                    action->int_value);
184
                        MSG_UNSET_PERM_FLAGS(fltinfo->flags,
185
                                             MSG_CLABEL_FLAG_MASK);
186
                        MSG_SET_COLORLABEL_VALUE(fltinfo->flags,
187
                                                 action->int_value);
188
                        fltinfo->actions[action->type] = TRUE;
189
                        break;
190
                case FLT_ACTION_MARK_READ:
191
                        debug_print("filter_action_exec(): mark as read\n");
192
                        if (msginfo->folder) {
193
                                if (MSG_IS_NEW(fltinfo->flags))
194
                                        msginfo->folder->new--;
195
                                if (MSG_IS_UNREAD(fltinfo->flags))
196
                                        msginfo->folder->unread--;
197
                        }
198
                        MSG_UNSET_PERM_FLAGS(fltinfo->flags, MSG_NEW|MSG_UNREAD);
199
                        fltinfo->actions[action->type] = TRUE;
200
                        break;
201
                case FLT_ACTION_EXEC:
202
                        cmdline = g_strconcat(action->str_value, " \"", file,
203
                                              "\"", NULL);
204
                        ret = execute_command_line(cmdline, FALSE);
205
                        fltinfo->last_exec_exit_status = ret;
206
                        if (ret == -1) {
207
                                fltinfo->error = FLT_ERROR_EXEC_FAILED;
208
                                g_warning("filter_action_exec: cannot execute command: %s", cmdline);
209
                                g_free(cmdline);
210
                                return -1;
211
                        }
212
                        g_free(cmdline);
213
                        fltinfo->actions[action->type] = TRUE;
214
                        break;
215
                case FLT_ACTION_EXEC_ASYNC:
216
                        cmdline = g_strconcat(action->str_value, " \"", file,
217
                                              "\"", NULL);
218
                        ret = execute_command_line(cmdline, TRUE);
219
                        fltinfo->last_exec_exit_status = ret;
220
                        if (ret == -1) {
221
                                fltinfo->error = FLT_ERROR_EXEC_FAILED;
222
                                g_warning("filter_action_exec: cannot execute command: %s", cmdline);
223
                                g_free(cmdline);
224
                                return -1;
225
                        }
226
                        g_free(cmdline);
227
                        fltinfo->actions[action->type] = TRUE;
228
                        break;
229
                default:
230
                        break;
231
                }
232
        }
233

    
234
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
235
                action = (FilterAction *)cur->data;
236

    
237
                switch (action->type) {
238
                case FLT_ACTION_MOVE:
239
                case FLT_ACTION_COPY:
240
                        dest_folder = folder_find_item_from_identifier
241
                                (action->str_value);
242
                        if (!dest_folder) {
243
                                g_warning("dest folder '%s' not found\n",
244
                                          action->str_value);
245
                                fltinfo->error = FLT_ERROR_ERROR;
246
                                return -1;
247
                        }
248
                        debug_print("filter_action_exec(): %s: dest_folder = %s\n",
249
                                    action->type == FLT_ACTION_COPY ?
250
                                    "copy" : "move", action->str_value);
251

    
252
                        if (msginfo->folder) {
253
                                gint val;
254

    
255
                                /* local filtering */
256
                                if (msginfo->folder == dest_folder)
257
                                        copy_to_self = TRUE;
258
                                else {
259
                                        if (action->type == FLT_ACTION_COPY) {
260
                                                MsgFlags save_flags;
261

    
262
                                                save_flags = msginfo->flags;
263
                                                msginfo->flags = fltinfo->flags;
264
                                                val = folder_item_copy_msg
265
                                                        (dest_folder, msginfo);
266
                                                msginfo->flags = save_flags;
267
                                                if (val == -1) {
268
                                                        fltinfo->error = FLT_ERROR_ERROR;
269
                                                        return -1;
270
                                                }
271
                                        }
272
                                        fltinfo->actions[action->type] = TRUE;
273
                                }
274
                        } else {
275
                                MsgFlags save_flags;
276

    
277
                                save_flags = msginfo->flags;
278
                                msginfo->flags = fltinfo->flags;
279
                                if (folder_item_add_msg_msginfo
280
                                        (dest_folder, msginfo, FALSE) < 0) {
281
                                        msginfo->flags = save_flags;
282
                                        fltinfo->error = FLT_ERROR_ERROR;
283
                                        return -1;
284
                                }
285
                                msginfo->flags = save_flags;
286
                                fltinfo->actions[action->type] = TRUE;
287
                        }
288

    
289
                        fltinfo->dest_list = g_slist_append(fltinfo->dest_list,
290
                                                            dest_folder);
291
                        if (action->type == FLT_ACTION_MOVE) {
292
                                fltinfo->move_dest = dest_folder;
293
                                fltinfo->drop_done = TRUE;
294
                        }
295
                        break;
296
                default:
297
                        break;
298
                }
299
        }
300

    
301
        if (fltinfo->drop_done == TRUE)
302
                return 0;
303

    
304
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
305
                action = (FilterAction *)cur->data;
306

    
307
                switch (action->type) {
308
                case FLT_ACTION_NOT_RECEIVE:
309
                        debug_print("filter_action_exec(): don't receive\n");
310
                        fltinfo->drop_done = TRUE;
311
                        fltinfo->actions[action->type] = TRUE;
312
                        return 0;
313
                case FLT_ACTION_DELETE:
314
                        debug_print("filter_action_exec(): delete\n");
315
                        if (msginfo->folder) {
316
                                /* local filtering */
317
                                if (copy_to_self == FALSE)
318
                                        fltinfo->actions[action->type] = TRUE;
319
                        } else
320
                                fltinfo->actions[action->type] = TRUE;
321

    
322
                        fltinfo->drop_done = TRUE;
323
                        return 0;
324
                case FLT_ACTION_STOP_EVAL:
325
                        debug_print("filter_action_exec(): stop evaluation\n");
326
                        fltinfo->actions[action->type] = TRUE;
327
                        return 0;
328
                default:
329
                        break;
330
                }
331
        }
332

    
333
        return 0;
334
}
335

    
336
static gboolean strmatch_regex(const gchar *haystack, const gchar *needle)
337
{
338
#if defined(USE_ONIGURUMA) || defined(HAVE_REGCOMP)
339
        gint ret = 0;
340
        regex_t preg;
341

    
342
#if USE_ONIGURUMA
343
        reg_set_encoding(REG_POSIX_ENCODING_UTF8);
344
#endif
345
        ret = regcomp(&preg, needle, REG_ICASE|REG_EXTENDED);
346
        if (ret != 0)
347
                return FALSE;
348

    
349
        ret = regexec(&preg, haystack, 0, NULL, 0);
350
        regfree(&preg);
351

    
352
        if (ret == 0)
353
                return TRUE;
354
        else
355
#endif
356
                return FALSE;
357
}
358

    
359
gboolean filter_match_rule(FilterRule *rule, MsgInfo *msginfo, GSList *hlist,
360
                           FilterInfo *fltinfo)
361
{
362
        FilterCond *cond;
363
        GSList *cur;
364
        gboolean matched;
365

    
366
        g_return_val_if_fail(rule->cond_list != NULL, FALSE);
367

    
368
        switch (rule->timing) {
369
        case FLT_TIMING_ANY:
370
                break;
371
        case FLT_TIMING_ON_RECEIVE:
372
                if (msginfo->folder != NULL)
373
                        return FALSE;
374
                break;
375
        case FLT_TIMING_MANUAL:
376
                if (msginfo->folder == NULL)
377
                        return FALSE;
378
                break;
379
        default:
380
                break;
381
        }
382

    
383
        if (rule->bool_op == FLT_AND) {
384
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
385
                        cond = (FilterCond *)cur->data;
386
                        if (cond->type >= FLT_COND_SIZE_GREATER) {
387
                                matched = filter_match_cond
388
                                        (cond, msginfo, hlist, fltinfo);
389
                                if (matched == FALSE)
390
                                        return FALSE;
391
                        }
392
                }
393

    
394
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
395
                        cond = (FilterCond *)cur->data;
396
                        if (cond->type <= FLT_COND_TO_OR_CC) {
397
                                matched = filter_match_cond
398
                                        (cond, msginfo, hlist, fltinfo);
399
                                if (matched == FALSE)
400
                                        return FALSE;
401
                        }
402
                }
403

    
404
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
405
                        cond = (FilterCond *)cur->data;
406
                        if (cond->type == FLT_COND_BODY ||
407
                            cond->type == FLT_COND_CMD_TEST) {
408
                                matched = filter_match_cond
409
                                        (cond, msginfo, hlist, fltinfo);
410
                                if (matched == FALSE)
411
                                        return FALSE;
412
                        }
413
                }
414

    
415
                return TRUE;
416
        } else if (rule->bool_op == FLT_OR) {
417
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
418
                        cond = (FilterCond *)cur->data;
419
                        if (cond->type >= FLT_COND_SIZE_GREATER) {
420
                                matched = filter_match_cond
421
                                        (cond, msginfo, hlist, fltinfo);
422
                                if (matched == TRUE)
423
                                        return TRUE;
424
                        }
425
                }
426

    
427
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
428
                        cond = (FilterCond *)cur->data;
429
                        if (cond->type <= FLT_COND_TO_OR_CC) {
430
                                matched = filter_match_cond
431
                                        (cond, msginfo, hlist, fltinfo);
432
                                if (matched == TRUE)
433
                                        return TRUE;
434
                        }
435
                }
436

    
437
                for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
438
                        cond = (FilterCond *)cur->data;
439
                        if (cond->type == FLT_COND_BODY ||
440
                            cond->type == FLT_COND_CMD_TEST) {
441
                                matched = filter_match_cond
442
                                        (cond, msginfo, hlist, fltinfo);
443
                                if (matched == TRUE)
444
                                        return TRUE;
445
                        }
446
                }
447

    
448
                return FALSE;
449
        }
450

    
451
        return FALSE;
452
}
453

    
454
static gboolean filter_match_cond(FilterCond *cond, MsgInfo *msginfo,
455
                                  GSList *hlist, FilterInfo *fltinfo)
456
{
457
        gint ret;
458
        gboolean matched = FALSE;
459
        gboolean not_match = FALSE;
460
        gchar *file;
461
        gchar *cmdline;
462
        PrefsAccount *cond_ac;
463

    
464
        switch (cond->type) {
465
        case FLT_COND_HEADER:
466
                if (cond->match_type == FLT_IN_ADDRESSBOOK)
467
                        return filter_match_in_addressbook(cond, hlist, fltinfo);
468
                else
469
                        return filter_match_header_cond(cond, hlist);
470
        case FLT_COND_ANY_HEADER:
471
                return filter_match_header_cond(cond, hlist);
472
        case FLT_COND_TO_OR_CC:
473
                if (cond->match_type == FLT_IN_ADDRESSBOOK)
474
                        return filter_match_in_addressbook(cond, hlist, fltinfo);
475
                else
476
                        return filter_match_header_cond(cond, hlist);
477
        case FLT_COND_BODY:
478
                matched = procmime_find_string(msginfo, cond->str_value,
479
                                               cond->match_func);
480
                break;
481
        case FLT_COND_CMD_TEST:
482
                file = procmsg_get_message_file(msginfo);
483
                if (!file)
484
                        return FALSE;
485
                cmdline = g_strconcat(cond->str_value, " \"", file, "\"", NULL);
486
                ret = execute_command_line_async_wait(cmdline);
487
                fltinfo->last_exec_exit_status = ret;
488
                matched = (ret == 0);
489
                if (ret == -1)
490
                        fltinfo->error = FLT_ERROR_EXEC_FAILED;
491
                g_free(cmdline);
492
                g_free(file);
493
                break;
494
        case FLT_COND_SIZE_GREATER:
495
                matched = (msginfo->size > cond->int_value * 1024);
496
                break;
497
        case FLT_COND_AGE_GREATER:
498
                matched = (time(NULL) - msginfo->date_t >
499
                                cond->int_value * 24 * 60 * 60);
500
                break;
501
        case FLT_COND_UNREAD:
502
                matched = MSG_IS_UNREAD(msginfo->flags);
503
                break;
504
        case FLT_COND_MARK:
505
                matched = MSG_IS_MARKED(msginfo->flags);
506
                break;
507
        case FLT_COND_COLOR_LABEL:
508
                matched = (MSG_GET_COLORLABEL_VALUE(msginfo->flags) != 0);
509
                break;
510
        case FLT_COND_MIME:
511
                matched = MSG_IS_MIME(msginfo->flags);
512
                break;
513
        case FLT_COND_ACCOUNT:
514
                cond_ac = account_find_from_id(cond->int_value);
515
                matched = (cond_ac != NULL && cond_ac == fltinfo->account);
516
                break;
517
        default:
518
                g_warning("filter_match_cond(): unknown condition: %d\n",
519
                          cond->type);
520
                fltinfo->error = FLT_ERROR_ERROR;
521
                return FALSE;
522
        }
523

    
524
        if (FLT_IS_NOT_MATCH(cond->match_flag)) {
525
                not_match = TRUE;
526
                matched = !matched;
527
        }
528

    
529
        if (matched && get_debug_mode()) {
530
                gchar *sv = cond->str_value ? cond->str_value : "";
531
                gchar *nm = not_match ? " (reverse match)" : "";
532

    
533
                switch (cond->type) {
534
                case FLT_COND_BODY:
535
                        debug_print("filter-log: %s: BODY, str_value: [%s]%s\n", G_STRFUNC, sv, nm);
536
                        break;
537
                case FLT_COND_CMD_TEST:
538
                        debug_print("filter-log: %s: CMD_TEST, str_value: [%s]%s\n", G_STRFUNC, sv, nm);
539
                        break;
540
                case FLT_COND_SIZE_GREATER:
541
                        debug_print("filter-log: %s: SIZE_GREATER: %u %s %d (KB)%s\n", G_STRFUNC, msginfo->size, not_match ? "<=" : ">", cond->int_value, nm);
542
                        break;
543
                case FLT_COND_AGE_GREATER:
544
                        debug_print("filter-log: %s: AGE_GREATER: %ld (sec) %s %d (day)%s\n", G_STRFUNC, time(NULL) - msginfo->date_t, not_match ? "<=" : ">", cond->int_value, nm);
545
                        break;
546
                case FLT_COND_UNREAD:
547
                        debug_print("filter-log: %s: UNREAD%s\n", G_STRFUNC, nm);
548
                        break;
549
                case FLT_COND_MARK:
550
                        debug_print("filter-log: %s: MARK%s\n", G_STRFUNC, nm);
551
                        break;
552
                case FLT_COND_COLOR_LABEL:
553
                        debug_print("filter-log: %s: COLOR_LABEL%s\n", G_STRFUNC, nm);
554
                        break;
555
                case FLT_COND_MIME:
556
                        debug_print("filter-log: %s: MIME%s\n", G_STRFUNC, nm);
557
                        break;
558
                case FLT_COND_ACCOUNT:
559
                        debug_print("filter-log: %s: ACCOUNT [%d]%s\n", G_STRFUNC, cond->int_value, nm);
560
                        break;
561
                default:
562
                        break;
563
                }
564
        }
565

    
566
        return matched;
567
}
568

    
569
static gboolean filter_match_header_cond(FilterCond *cond, GSList *hlist)
570
{
571
        gboolean matched = FALSE;
572
        gboolean not_match = FALSE;
573
        GSList *cur;
574
        Header *header;
575

    
576
        for (cur = hlist; cur != NULL; cur = cur->next) {
577
                header = (Header *)cur->data;
578

    
579
                switch (cond->type) {
580
                case FLT_COND_HEADER:
581
                        if (!g_ascii_strcasecmp
582
                                (header->name, cond->header_name)) {
583
                                if (!cond->str_value ||
584
                                    cond->match_func(header->body,
585
                                                     cond->str_value))
586
                                        matched = TRUE;
587
                        }
588
                        break;
589
                case FLT_COND_ANY_HEADER:
590
                        if (!cond->str_value ||
591
                            cond->match_func(header->body, cond->str_value))
592
                                matched = TRUE;
593
                        break;
594
                case FLT_COND_TO_OR_CC:
595
                        if (!g_ascii_strcasecmp(header->name, "To") ||
596
                            !g_ascii_strcasecmp(header->name, "Cc")) {
597
                                if (!cond->str_value ||
598
                                    cond->match_func(header->body,
599
                                                     cond->str_value))
600
                                        matched = TRUE;
601
                        }
602
                        break;
603
                default:
604
                        break;
605
                }
606

    
607
                if (matched == TRUE)
608
                        break;
609
        }
610

    
611
        if (FLT_IS_NOT_MATCH(cond->match_flag)) {
612
                not_match = TRUE;
613
                matched = !matched;
614
        }
615

    
616
        if (matched && get_debug_mode()) {
617
                gchar *sv = cond->str_value ? cond->str_value : "";
618
                gchar *nm = not_match ? " (reverse match)" : "";
619

    
620
                switch (cond->type) {
621
                case FLT_COND_HEADER:
622
                        debug_print("filter-log: %s: HEADER [%s], str_value: [%s]%s\n", G_STRFUNC, cond->header_name, sv, nm);
623
                        break;
624
                case FLT_COND_ANY_HEADER:
625
                        debug_print("filter-log: %s: ANY_HEADER, str_value: [%s]%s\n", G_STRFUNC, sv, nm);
626
                        break;
627
                case FLT_COND_TO_OR_CC:
628
                        debug_print("filter-log: %s: TO_OR_CC, str_value: [%s]%s\n", G_STRFUNC, sv, nm);
629
                        break;
630
                default:
631
                        break;
632
                }
633
        }
634

    
635
        return matched;
636
}
637

    
638
static gboolean filter_match_in_addressbook(FilterCond *cond, GSList *hlist,
639
                                            FilterInfo *fltinfo)
640
{
641
        gboolean matched = FALSE;
642
        gboolean not_match = FALSE;
643
        GSList *cur;
644
        Header *header;
645

    
646
        if (!default_addrbook_func)
647
                return FALSE;
648
        if (cond->type != FLT_COND_HEADER && cond->type != FLT_COND_TO_OR_CC)
649
                return FALSE;
650

    
651
        for (cur = hlist; cur != NULL; cur = cur->next) {
652
                header = (Header *)cur->data;
653

    
654
                if (cond->type == FLT_COND_HEADER) {
655
                        if (!g_ascii_strcasecmp
656
                                (header->name, cond->header_name)) {
657
                                if (default_addrbook_func(header->body))
658
                                        matched = TRUE;
659
                        }
660
                } else if (cond->type == FLT_COND_TO_OR_CC) {
661
                        if (!g_ascii_strcasecmp(header->name, "To") ||
662
                            !g_ascii_strcasecmp(header->name, "Cc")) {
663
                                if (default_addrbook_func(header->body))
664
                                        matched = TRUE;
665
                        }
666
                }
667

    
668
                if (matched == TRUE)
669
                        break;
670
        }
671

    
672
        if (FLT_IS_NOT_MATCH(cond->match_flag)) {
673
                not_match = TRUE;
674
                matched = !matched;
675
        }
676

    
677
        if (matched && get_debug_mode()) {
678
                gchar *nm = not_match ? " (reverse match)" : "";
679

    
680
                switch (cond->type) {
681
                case FLT_COND_HEADER:
682
                        debug_print("filter-log: %s: HEADER [%s], IN_ADDRESSBOOK%s\n", G_STRFUNC, cond->header_name, nm);
683
                        break;
684
                case FLT_COND_TO_OR_CC:
685
                        debug_print("filter-log: %s: TO_OR_CC, IN_ADDRESSBOOK%s\n", G_STRFUNC, nm);
686
                        break;
687
                default:
688
                        break;
689
                }
690
        }
691

    
692
        return matched;
693
}
694

    
695
gboolean filter_rule_requires_full_headers(FilterRule *rule)
696
{
697
        GSList *cur;
698

    
699
        for (cur = rule->cond_list; cur != NULL; cur = cur->next) {
700
                FilterCond *cond = (FilterCond *)cur->data;
701
                const gchar *name = cond->header_name;
702

    
703
                if (cond->type == FLT_COND_HEADER && name) {
704
                        if (g_ascii_strcasecmp(name, "Date") != 0 &&
705
                            g_ascii_strcasecmp(name, "From") != 0 &&
706
                            g_ascii_strcasecmp(name, "To") != 0 &&
707
                            g_ascii_strcasecmp(name, "Newsgroups") != 0 &&
708
                            g_ascii_strcasecmp(name, "Subject") != 0)
709
                                return TRUE;
710
                } else if (cond->type == FLT_COND_ANY_HEADER ||
711
                           cond->type == FLT_COND_TO_OR_CC)
712
                        return TRUE;
713
        }
714

    
715
        return FALSE;
716
}
717

    
718
#define RETURN_IF_TAG_NOT_MATCH(tag_name)                        \
719
        if (strcmp2(xmlnode->tag->tag, tag_name) != 0) {        \
720
                g_warning("tag name != \"" tag_name "\"\n");        \
721
                filter_cond_list_free(cond_list);                \
722
                filter_action_list_free(cond_list);                \
723
                return FALSE;                                        \
724
        }                                                        \
725

    
726
#define STR_SWITCH(sw)                { const gchar *sw_str = sw;
727
#define STR_CASE_BEGIN(str)        if (!strcmp(sw_str, str)) {
728
#define STR_CASE(str)                } else if (!strcmp(sw_str, str)) {
729
#define STR_CASE_END                } }
730

    
731
static gboolean filter_xml_node_func(GNode *node, gpointer data)
732
{
733
        XMLNode *xmlnode;
734
        GList *list;
735
        const gchar *name = NULL;
736
        FilterTiming timing = FLT_TIMING_ANY;
737
        gboolean enabled = TRUE;
738
        FilterRule *rule;
739
        FilterBoolOp bool_op = FLT_OR;
740
        const gchar *target_folder = NULL;
741
        gboolean recursive = FALSE;
742
        GSList *cond_list = NULL;
743
        GSList *action_list = NULL;
744
        GNode *child, *cond_child, *action_child;
745
        GSList **fltlist = (GSList **)data;
746

    
747
        if (g_node_depth(node) != 2) return FALSE;
748
        g_return_val_if_fail(node->data != NULL, FALSE);
749

    
750
        xmlnode = node->data;
751
        RETURN_IF_TAG_NOT_MATCH("rule");
752

    
753
        for (list = xmlnode->tag->attr; list != NULL; list = list->next) {
754
                XMLAttr *attr = (XMLAttr *)list->data;
755

    
756
                if (!attr || !attr->name || !attr->value) continue;
757
                if (!strcmp(attr->name, "name"))
758
                        name = attr->value;
759
                else if (!strcmp(attr->name, "timing")) {
760
                        if (!strcmp(attr->value, "any"))
761
                                timing = FLT_TIMING_ANY;
762
                        else if (!strcmp(attr->value, "receive"))
763
                                timing = FLT_TIMING_ON_RECEIVE;
764
                        else if (!strcmp(attr->value, "manual"))
765
                                timing = FLT_TIMING_MANUAL;
766
                } else if (!strcmp(attr->name, "enabled")) {
767
                        if (!strcmp(attr->value, "true"))
768
                                enabled = TRUE;
769
                        else
770
                                enabled = FALSE;
771
                }
772
        }
773

    
774
        /* condition list */
775
        child = node->children;
776
        if (!child) {
777
                g_warning("<rule> must have children\n");
778
                return FALSE;
779
        }
780
        xmlnode = child->data;
781
        RETURN_IF_TAG_NOT_MATCH("condition-list");
782
        for (list = xmlnode->tag->attr; list != NULL; list = list->next) {
783
                XMLAttr *attr = (XMLAttr *)list->data;
784

    
785
                if (attr && attr->name && attr->value &&
786
                    !strcmp(attr->name, "bool")) {
787
                        if (!strcmp(attr->value, "or"))
788
                                bool_op = FLT_OR;
789
                        else
790
                                bool_op = FLT_AND;
791
                }
792
        }
793

    
794
        for (cond_child = child->children; cond_child != NULL;
795
             cond_child = cond_child->next) {
796
                const gchar *type = NULL;
797
                const gchar *name = NULL;
798
                const gchar *value = NULL;
799
                gboolean case_sens = FALSE;
800
                FilterCond *cond;
801
                FilterCondType cond_type = FLT_COND_HEADER;
802
                FilterMatchType match_type = FLT_CONTAIN;
803
                FilterMatchFlag match_flag = 0;
804

    
805
                xmlnode = cond_child->data;
806
                if (!xmlnode || !xmlnode->tag || !xmlnode->tag->tag) continue;
807

    
808
                for (list = xmlnode->tag->attr; list != NULL; list = list->next) {
809
                        XMLAttr *attr = (XMLAttr *)list->data;
810

    
811
                        if (!attr || !attr->name || !attr->value) continue;
812

    
813
                        STR_SWITCH(attr->name)
814
                        STR_CASE_BEGIN("type")
815
                                type = attr->value;
816
                        STR_CASE("name")
817
                                name = attr->value;
818
                        STR_CASE("case")
819
                                case_sens = TRUE;
820
                        STR_CASE("recursive")
821
                                if (!strcmp(attr->value, "true"))
822
                                        recursive = TRUE;
823
                                else
824
                                        recursive = FALSE;
825
                        STR_CASE_END
826
                }
827

    
828
                if (type) {
829
                        filter_rule_match_type_str_to_enum
830
                                (type, &match_type, &match_flag);
831
                }
832
                if (case_sens)
833
                        match_flag |= FLT_CASE_SENS;
834
                value = xmlnode->element;
835

    
836
                STR_SWITCH(xmlnode->tag->tag)
837
                STR_CASE_BEGIN("match-header")
838
                        cond_type = FLT_COND_HEADER;
839
                STR_CASE("match-any-header")
840
                        cond_type = FLT_COND_ANY_HEADER;
841
                STR_CASE("match-to-or-cc")
842
                        cond_type = FLT_COND_TO_OR_CC;
843
                STR_CASE("match-body-text")
844
                        cond_type = FLT_COND_BODY;
845
                STR_CASE("command-test")
846
                        cond_type = FLT_COND_CMD_TEST;
847
                STR_CASE("size")
848
                        cond_type = FLT_COND_SIZE_GREATER;
849
                STR_CASE("age")
850
                        cond_type = FLT_COND_AGE_GREATER;
851
                STR_CASE("unread")
852
                        cond_type = FLT_COND_UNREAD;
853
                STR_CASE("mark")
854
                        cond_type = FLT_COND_MARK;
855
                STR_CASE("color-label")
856
                        cond_type = FLT_COND_COLOR_LABEL;
857
                STR_CASE("mime")
858
                        cond_type = FLT_COND_MIME;
859
                STR_CASE("account-id")
860
                        cond_type = FLT_COND_ACCOUNT;
861
                STR_CASE("target-folder")
862
                        target_folder = value;
863
                        continue;
864
                STR_CASE_END
865

    
866
                cond = filter_cond_new(cond_type, match_type, match_flag,
867
                                       name, value);
868
                cond_list = g_slist_append(cond_list, cond);
869
        }
870

    
871
        /* action list */
872
        child = child->next;
873
        if (!child) {
874
                g_warning("<rule> must have multiple children\n");
875
                filter_cond_list_free(cond_list);
876
                return FALSE;
877
        }
878
        xmlnode = child->data;
879
        RETURN_IF_TAG_NOT_MATCH("action-list");
880

    
881
        for (action_child = child->children; action_child != NULL;
882
             action_child = action_child->next) {
883
                FilterAction *action;
884
                FilterActionType action_type = FLT_ACTION_NONE;
885

    
886
                xmlnode = action_child->data;
887
                if (!xmlnode || !xmlnode->tag || !xmlnode->tag->tag) continue;
888

    
889
                STR_SWITCH(xmlnode->tag->tag)
890
                STR_CASE_BEGIN("move")
891
                        action_type = FLT_ACTION_MOVE;
892
                STR_CASE("copy")
893
                        action_type = FLT_ACTION_COPY;
894
                STR_CASE("not-receive")
895
                        action_type = FLT_ACTION_NOT_RECEIVE;
896
                STR_CASE("delete")
897
                        action_type = FLT_ACTION_DELETE;
898
                STR_CASE("exec")
899
                        action_type = FLT_ACTION_EXEC;
900
                STR_CASE("exec-async")
901
                        action_type = FLT_ACTION_EXEC_ASYNC;
902
                STR_CASE("mark")
903
                        action_type = FLT_ACTION_MARK;
904
                STR_CASE("color-label")
905
                        action_type = FLT_ACTION_COLOR_LABEL;
906
                STR_CASE("mark-as-read")
907
                        action_type = FLT_ACTION_MARK_READ;
908
                STR_CASE("forward")
909
                        action_type = FLT_ACTION_FORWARD;
910
                STR_CASE("forward-as-attachment")
911
                        action_type = FLT_ACTION_FORWARD_AS_ATTACHMENT;
912
                STR_CASE("redirect")
913
                        action_type = FLT_ACTION_REDIRECT;
914
                STR_CASE("stop-eval")
915
                        action_type = FLT_ACTION_STOP_EVAL;
916
                STR_CASE_END
917

    
918
                action = filter_action_new(action_type, xmlnode->element);
919
                action_list = g_slist_append(action_list, action);
920
        }
921

    
922
        if (name && cond_list) {
923
                rule = filter_rule_new(name, bool_op, cond_list, action_list);
924
                rule->timing = timing;
925
                rule->enabled = enabled;
926
                if (target_folder) {
927
                        rule->target_folder = g_strdup(target_folder);
928
                        rule->recursive = recursive;
929
                }
930
                *fltlist = g_slist_prepend(*fltlist, rule);
931
        }
932

    
933
        return FALSE;
934
}
935

    
936
#undef RETURN_IF_TAG_NOT_MATCH
937
#undef STR_SWITCH
938
#undef STR_CASE_BEGIN
939
#undef STR_CASE
940
#undef STR_CASE_END
941

    
942
GSList *filter_xml_node_to_filter_list(GNode *node)
943
{
944
        GSList *fltlist = NULL;
945

    
946
        g_return_val_if_fail(node != NULL, NULL);
947

    
948
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
949
                        filter_xml_node_func, &fltlist);
950
        fltlist = g_slist_reverse(fltlist);
951

    
952
        return fltlist;
953
}
954

    
955
GSList *filter_read_file(const gchar *file)
956
{
957
        GNode *node;
958
        GSList *list;
959

    
960
        g_return_val_if_fail(file != NULL, NULL);
961

    
962
        debug_print("Reading %s\n", file);
963

    
964
        if (!is_file_exist(file))
965
                return NULL;
966

    
967
        node = xml_parse_file(file);
968
        if (!node) {
969
                g_warning("Can't parse %s\n", file);
970
                return NULL;
971
        }
972

    
973
        list = filter_xml_node_to_filter_list(node);
974

    
975
        xml_free_tree(node);
976

    
977
        return list;
978
}
979

    
980
void filter_read_config(void)
981
{
982
        gchar *rcpath;
983

    
984
        debug_print("Reading filter configuration...\n");
985

    
986
        /* remove all previous filter list */
987
        while (prefs_common.fltlist != NULL) {
988
                FilterRule *rule = (FilterRule *)prefs_common.fltlist->data;
989

    
990
                filter_rule_free(rule);
991
                prefs_common.fltlist = g_slist_remove(prefs_common.fltlist,
992
                                                      rule);
993
        }
994

    
995
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST,
996
                             NULL);
997
        prefs_common.fltlist = filter_read_file(rcpath);
998
        g_free(rcpath);
999
}
1000

    
1001
#define NODE_NEW(tag, text) \
1002
        node = xml_node_new(xml_tag_new(tag), text)
1003
#define ADD_ATTR(name, value) \
1004
        xml_tag_add_attr(node->tag, xml_attr_new(name, value))
1005

    
1006
void filter_write_file(GSList *list, const gchar *file)
1007
{
1008
        PrefFile *pfile;
1009
        GSList *cur;
1010

    
1011
        g_return_if_fail(file != NULL);
1012

    
1013
        if ((pfile = prefs_file_open(file)) == NULL) {
1014
                g_warning("failed to write filter configuration to file: %s\n",
1015
                          file);
1016
                return;
1017
        }
1018

    
1019
        xml_file_put_xml_decl(pfile->fp);
1020
        fputs("\n<filter>\n", pfile->fp);
1021

    
1022
        for (cur = list; cur != NULL; cur = cur->next) {
1023
                FilterRule *rule = (FilterRule *)cur->data;
1024
                GSList *cur_cond;
1025
                GSList *cur_action;
1026
                gchar match_type[64];
1027
                gchar nstr[16];
1028

    
1029
                fputs("    <rule name=\"", pfile->fp);
1030
                xml_file_put_escape_str(pfile->fp, rule->name);
1031
                fprintf(pfile->fp, "\" timing=\"%s\"",
1032
                        rule->timing == FLT_TIMING_ON_RECEIVE ? "receive" :
1033
                        rule->timing == FLT_TIMING_MANUAL ? "manual" : "any");
1034
                fprintf(pfile->fp, " enabled=\"%s\">\n",
1035
                        rule->enabled ? "true" : "false");
1036

    
1037
                fprintf(pfile->fp, "        <condition-list bool=\"%s\">\n",
1038
                        rule->bool_op == FLT_OR ? "or" : "and");
1039

    
1040
                for (cur_cond = rule->cond_list; cur_cond != NULL;
1041
                     cur_cond = cur_cond->next) {
1042
                        FilterCond *cond = (FilterCond *)cur_cond->data;
1043
                        XMLNode *node = NULL;
1044

    
1045
                        switch (cond->match_type) {
1046
                        case FLT_CONTAIN:
1047
                                strncpy2(match_type,
1048
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1049
                                         ? "not-contain" : "contains",
1050
                                         sizeof(match_type));
1051
                                break;
1052
                        case FLT_EQUAL:
1053
                                strncpy2(match_type,
1054
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1055
                                         ? "is-not" : "is", sizeof(match_type));
1056
                                break;
1057
                        case FLT_REGEX:
1058
                                strncpy2(match_type,
1059
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1060
                                         ? "not-regex" : "regex",
1061
                                         sizeof(match_type));
1062
                                break;
1063
                        case FLT_IN_ADDRESSBOOK:
1064
                                strncpy2(match_type,
1065
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1066
                                         ? "not-in-addressbook" : "in-addressbook",
1067
                                         sizeof(match_type));
1068
                                break;
1069
                        default:
1070
                                match_type[0] = '\0';
1071
                                break;
1072
                        }
1073

    
1074
                        switch (cond->type) {
1075
                        case FLT_COND_HEADER:
1076
                                NODE_NEW("match-header", cond->str_value);
1077
                                ADD_ATTR("type", match_type);
1078
                                ADD_ATTR("name", cond->header_name);
1079
                                if (FLT_IS_CASE_SENS(cond->match_flag))
1080
                                        ADD_ATTR("case", "true");
1081
                                break;
1082
                        case FLT_COND_ANY_HEADER:
1083
                                NODE_NEW("match-any-header", cond->str_value);
1084
                                ADD_ATTR("type", match_type);
1085
                                if (FLT_IS_CASE_SENS(cond->match_flag))
1086
                                        ADD_ATTR("case", "true");
1087
                                break;
1088
                        case FLT_COND_TO_OR_CC:
1089
                                NODE_NEW("match-to-or-cc", cond->str_value);
1090
                                ADD_ATTR("type", match_type);
1091
                                if (FLT_IS_CASE_SENS(cond->match_flag))
1092
                                        ADD_ATTR("case", "true");
1093
                                break;
1094
                        case FLT_COND_BODY:
1095
                                NODE_NEW("match-body-text", cond->str_value);
1096
                                ADD_ATTR("type", match_type);
1097
                                if (FLT_IS_CASE_SENS(cond->match_flag))
1098
                                        ADD_ATTR("case", "true");
1099
                                break;
1100
                        case FLT_COND_CMD_TEST:
1101
                                NODE_NEW("command-test", cond->str_value);
1102
                                break;
1103
                        case FLT_COND_SIZE_GREATER:
1104
                                NODE_NEW("size", itos_buf(nstr, cond->int_value));
1105
                                ADD_ATTR("type",
1106
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1107
                                         ? "lt" : "gt");
1108
                                break;
1109
                        case FLT_COND_AGE_GREATER:
1110
                                NODE_NEW("age", itos_buf(nstr, cond->int_value));
1111
                                ADD_ATTR("type",
1112
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1113
                                         ? "lt" : "gt");
1114
                                break;
1115
                        case FLT_COND_UNREAD:
1116
                                NODE_NEW("unread", NULL);
1117
                                ADD_ATTR("type",
1118
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1119
                                         ? "is-not" : "is");
1120
                                break;
1121
                        case FLT_COND_MARK:
1122
                                NODE_NEW("mark", NULL);
1123
                                ADD_ATTR("type",
1124
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1125
                                         ? "is-not" : "is");
1126
                                break;
1127
                        case FLT_COND_COLOR_LABEL:
1128
                                NODE_NEW("color-label", NULL);
1129
                                ADD_ATTR("type",
1130
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1131
                                         ? "is-not" : "is");
1132
                                break;
1133
                        case FLT_COND_MIME:
1134
                                NODE_NEW("mime", NULL);
1135
                                ADD_ATTR("type",
1136
                                         FLT_IS_NOT_MATCH(cond->match_flag)
1137
                                         ? "is-not" : "is");
1138
                                break;
1139
                        case FLT_COND_ACCOUNT:
1140
                                 NODE_NEW("account-id", itos_buf(nstr, cond->int_value));
1141
                                 break;
1142
                        default:
1143
                                 break;
1144
                        }
1145

    
1146
                        if (node) {
1147
                                fputs("            ", pfile->fp);
1148
                                xml_file_put_node(pfile->fp, node);
1149
                                xml_free_node(node);
1150
                        }
1151
                }
1152

    
1153
                if (rule->target_folder) {
1154
                        XMLNode *node;
1155

    
1156
                        NODE_NEW("target-folder", rule->target_folder);
1157
                        ADD_ATTR("recursive", rule->recursive
1158
                                 ? "true" : "false");
1159
                        fputs("            ", pfile->fp);
1160
                        xml_file_put_node(pfile->fp, node);
1161
                        xml_free_node(node);
1162
                }
1163

    
1164
                fputs("        </condition-list>\n", pfile->fp);
1165

    
1166
                fputs("        <action-list>\n", pfile->fp);
1167

    
1168
                for (cur_action = rule->action_list; cur_action != NULL;
1169
                     cur_action = cur_action->next) {
1170
                        FilterAction *action = (FilterAction *)cur_action->data;
1171
                        XMLNode *node = NULL;
1172

    
1173
                        switch (action->type) {
1174
                        case FLT_ACTION_MOVE:
1175
                                NODE_NEW("move", action->str_value);
1176
                                break;
1177
                        case FLT_ACTION_COPY:
1178
                                NODE_NEW("copy", action->str_value);
1179
                                break;
1180
                        case FLT_ACTION_NOT_RECEIVE:
1181
                                NODE_NEW("not-receive", NULL);
1182
                                break;
1183
                        case FLT_ACTION_DELETE:
1184
                                NODE_NEW("delete", NULL);
1185
                                break;
1186
                        case FLT_ACTION_EXEC:
1187
                                NODE_NEW("exec", action->str_value);
1188
                                break;
1189
                        case FLT_ACTION_EXEC_ASYNC:
1190
                                NODE_NEW("exec-async", action->str_value);
1191
                                break;
1192
                        case FLT_ACTION_MARK:
1193
                                NODE_NEW("mark", NULL);
1194
                                break;
1195
                        case FLT_ACTION_COLOR_LABEL:
1196
                                NODE_NEW("color-label", action->str_value);
1197
                                break;
1198
                        case FLT_ACTION_MARK_READ:
1199
                                NODE_NEW("mark-as-read", NULL);
1200
                                break;
1201
                        case FLT_ACTION_FORWARD:
1202
                                NODE_NEW("forward", action->str_value);
1203
                                break;
1204
                        case FLT_ACTION_FORWARD_AS_ATTACHMENT:
1205
                                NODE_NEW("forward-as-attachment",
1206
                                         action->str_value);
1207
                                break;
1208
                        case FLT_ACTION_REDIRECT:
1209
                                NODE_NEW("redirect", action->str_value);
1210
                                break;
1211
                        case FLT_ACTION_STOP_EVAL:
1212
                                NODE_NEW("stop-eval", NULL);
1213
                                break;
1214
                        default:
1215
                                break;
1216
                        }
1217

    
1218
                        if (node) {
1219
                                fputs("            ", pfile->fp);
1220
                                xml_file_put_node(pfile->fp, node);
1221
                                xml_free_node(node);
1222
                        }
1223
                }
1224

    
1225
                fputs("        </action-list>\n", pfile->fp);
1226

    
1227
                fputs("    </rule>\n", pfile->fp);
1228
        }
1229

    
1230
        fputs("</filter>\n", pfile->fp);
1231

    
1232
        if (prefs_file_close(pfile) < 0) {
1233
                g_warning("failed to write filter configuration to file: %s\n",
1234
                          file);
1235
                return;
1236
        }
1237
}
1238

    
1239
void filter_write_config(void)
1240
{
1241
        gchar *rcpath;
1242

    
1243
        debug_print("Writing filter configuration...\n");
1244

    
1245
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST,
1246
                             NULL);
1247
        filter_write_file(prefs_common.fltlist, rcpath);
1248
        g_free(rcpath);
1249
}
1250

    
1251
#undef NODE_NEW
1252
#undef ADD_ATTR
1253

    
1254
gchar *filter_get_str(FilterRule *rule)
1255
{
1256
        gchar *str;
1257
        FilterCond *cond1, *cond2;
1258
        FilterAction *action = NULL;
1259
        GSList *cur;
1260
        gint flag1 = 0, flag2 = 0;
1261

    
1262
        cond1 = (FilterCond *)rule->cond_list->data;
1263
        if (rule->cond_list->next)
1264
                cond2 = (FilterCond *)rule->cond_list->next->data;
1265
        else
1266
                cond2 = NULL;
1267

    
1268
        /* new -> old flag conversion */
1269
        switch (cond1->match_type) {
1270
        case FLT_CONTAIN:
1271
        case FLT_EQUAL:
1272
                flag1 = FLT_IS_NOT_MATCH(cond1->match_flag) ? 0 : FLT_O_CONTAIN;
1273
                if (FLT_IS_CASE_SENS(cond1->match_flag))
1274
                        flag1 |= FLT_O_CASE_SENS;
1275
                break;
1276
        case FLT_REGEX:
1277
                flag1 = FLT_O_REGEX; break;
1278
        default:
1279
                break;
1280
        }
1281
        if (cond2) {
1282
                switch (cond2->match_type) {
1283
                case FLT_CONTAIN:
1284
                case FLT_EQUAL:
1285
                        flag2 = FLT_IS_NOT_MATCH(cond2->match_flag) ? 0 : FLT_O_CONTAIN;
1286
                        if (FLT_IS_CASE_SENS(cond2->match_flag))
1287
                                flag2 |= FLT_O_CASE_SENS;
1288
                        break;
1289
                case FLT_REGEX:
1290
                        flag2 = FLT_O_REGEX; break;
1291
                default:
1292
                        break;
1293
                }
1294
        } else
1295
                flag2 = FLT_O_CONTAIN;
1296

    
1297
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
1298
                action = (FilterAction *)cur->data;
1299
                if (action->type == FLT_ACTION_MOVE ||
1300
                    action->type == FLT_ACTION_NOT_RECEIVE ||
1301
                    action->type == FLT_ACTION_DELETE)
1302
                        break;
1303
        }
1304

    
1305
        str = g_strdup_printf
1306
                ("%s:%s:%c:%s:%s:%s:%d:%d:%c",
1307
                 cond1->header_name, cond1->str_value ? cond1->str_value : "",
1308
                 (cond2 && cond2->header_name) ?
1309
                         (rule->bool_op == FLT_AND ? '&' : '|') : ' ',
1310
                 (cond2 && cond2->header_name) ? cond2->header_name : "",
1311
                 (cond2 && cond2->str_value) ? cond2->str_value : "",
1312
                 (action && action->str_value) ? action->str_value : "",
1313
                 flag1, flag2,
1314
                 (action && action->type == FLT_ACTION_MOVE) ? 'm' :
1315
                 (action && action->type == FLT_ACTION_NOT_RECEIVE) ? 'n' :
1316
                 (action && action->type == FLT_ACTION_DELETE) ? 'd' : ' ');
1317

    
1318
        return str;
1319
}
1320

    
1321
#define PARSE_ONE_PARAM(p, srcp) \
1322
{ \
1323
        p = strchr(srcp, '\t'); \
1324
        if (!p) return NULL; \
1325
        else \
1326
                *p++ = '\0'; \
1327
}
1328

    
1329
FilterRule *filter_read_str(const gchar *str)
1330
{
1331
        FilterRule *rule;
1332
        FilterBoolOp bool_op;
1333
        gint flag;
1334
        FilterCond *cond;
1335
        FilterMatchType match_type;
1336
        FilterMatchFlag match_flag;
1337
        FilterAction *action;
1338
        GSList *cond_list = NULL;
1339
        GSList *action_list = NULL;
1340
        gchar *tmp;
1341
        gchar *rule_name;
1342
        gchar *name1, *body1, *op, *name2, *body2, *dest;
1343
        gchar *flag1 = NULL, *flag2 = NULL, *action1 = NULL;
1344

    
1345
        Xstrdup_a(tmp, str, return NULL);
1346

    
1347
        name1 = tmp;
1348
        PARSE_ONE_PARAM(body1, name1);
1349
        PARSE_ONE_PARAM(op, body1);
1350
        PARSE_ONE_PARAM(name2, op);
1351
        PARSE_ONE_PARAM(body2, name2);
1352
        PARSE_ONE_PARAM(dest, body2);
1353
        if (strchr(dest, '\t')) {
1354
                gchar *p;
1355

    
1356
                PARSE_ONE_PARAM(flag1, dest);
1357
                PARSE_ONE_PARAM(flag2, flag1);
1358
                PARSE_ONE_PARAM(action1, flag2);
1359
                if ((p = strchr(action1, '\t'))) *p = '\0';
1360
        }
1361

    
1362
        bool_op = (*op == '&') ? FLT_AND : FLT_OR;
1363

    
1364
        if (*name1) {
1365
                if (flag1)
1366
                        flag = (FilterOldFlag)strtoul(flag1, NULL, 10);
1367
                else
1368
                        flag = FLT_O_CONTAIN;
1369
                match_type = FLT_CONTAIN;
1370
                match_flag = 0;
1371
                if (flag & FLT_O_REGEX)
1372
                        match_type = FLT_REGEX;
1373
                else if (!(flag & FLT_O_CONTAIN))
1374
                        match_flag = FLT_NOT_MATCH;
1375
                if (flag & FLT_O_CASE_SENS)
1376
                        match_flag |= FLT_CASE_SENS;
1377
                cond = filter_cond_new(FLT_COND_HEADER, match_type, match_flag,
1378
                                       name1, body1);
1379
                cond_list = g_slist_append(cond_list, cond);
1380
        }
1381
        if (*name2) {
1382
                if (flag2)
1383
                        flag = (FilterOldFlag)strtoul(flag2, NULL, 10);
1384
                else
1385
                        flag = FLT_O_CONTAIN;
1386
                match_type = FLT_CONTAIN;
1387
                match_flag = 0;
1388
                if (flag & FLT_O_REGEX)
1389
                        match_type = FLT_REGEX;
1390
                else if (!(flag & FLT_O_CONTAIN))
1391
                        match_flag = FLT_NOT_MATCH;
1392
                if (flag & FLT_O_CASE_SENS)
1393
                        match_flag |= FLT_CASE_SENS;
1394
                cond = filter_cond_new(FLT_COND_HEADER, match_type, match_flag,
1395
                                       name2, body2);
1396
                cond_list = g_slist_append(cond_list, cond);
1397
        }
1398

    
1399
        action = filter_action_new(FLT_ACTION_MOVE,
1400
                                   *dest ? g_strdup(dest) : NULL);
1401
        if (action1) {
1402
                switch (*action1) {
1403
                case 'm': action->type = FLT_ACTION_MOVE;                break;
1404
                case 'n': action->type = FLT_ACTION_NOT_RECEIVE;        break;
1405
                case 'd': action->type = FLT_ACTION_DELETE;                break;
1406
                default:  g_warning("Invalid action: `%c'\n", *action1);
1407
                }
1408
        }
1409
        action_list = g_slist_append(action_list, action);
1410

    
1411
        Xstrdup_a(rule_name, str, return NULL);
1412
        subst_char(rule_name, '\t', ':');
1413

    
1414
        rule = filter_rule_new(rule_name, bool_op, cond_list, action_list);
1415

    
1416
        return rule;
1417
}
1418

    
1419
void filter_set_addressbook_func(FilterInAddressBookFunc func)
1420
{
1421
        default_addrbook_func = func;
1422
}
1423

    
1424
FilterInAddressBookFunc filter_get_addressbook_func(void)
1425
{
1426
        return default_addrbook_func;
1427
}
1428

    
1429
FilterRule *filter_rule_new(const gchar *name, FilterBoolOp bool_op,
1430
                            GSList *cond_list, GSList *action_list)
1431
{
1432
        FilterRule *rule;
1433

    
1434
        rule = g_new0(FilterRule, 1);
1435
        rule->name = g_strdup(name);
1436
        rule->bool_op = bool_op;
1437
        rule->cond_list = cond_list;
1438
        rule->action_list = action_list;
1439
        rule->timing = FLT_TIMING_ANY;
1440
        rule->enabled = TRUE;
1441

    
1442
        return rule;
1443
}
1444

    
1445
FilterCond *filter_cond_new(FilterCondType type,
1446
                            FilterMatchType match_type,
1447
                            FilterMatchFlag match_flag,
1448
                            const gchar *header, const gchar *value)
1449
{
1450
        FilterCond *cond;
1451

    
1452
        cond = g_new0(FilterCond, 1);
1453
        cond->type = type;
1454
        cond->match_type = match_type;
1455
        cond->match_flag = match_flag;
1456

    
1457
        if (type == FLT_COND_HEADER)
1458
                cond->header_name =
1459
                        (header && *header) ? g_strdup(header) : NULL;
1460
        else
1461
                cond->header_name = NULL;
1462

    
1463
        cond->str_value = (value && *value) ? g_strdup(value) : NULL;
1464
        if (type == FLT_COND_SIZE_GREATER || type == FLT_COND_AGE_GREATER ||
1465
            type == FLT_COND_ACCOUNT)
1466
                cond->int_value = atoi(value);
1467
        else
1468
                cond->int_value = 0;
1469

    
1470
        if (match_type == FLT_REGEX)
1471
                cond->match_func = strmatch_regex;
1472
        else if (match_type == FLT_EQUAL) {
1473
                if (FLT_IS_CASE_SENS(match_flag))
1474
                        cond->match_func = str_find_equal;
1475
                else
1476
                        cond->match_func = str_case_find_equal;
1477
        } else if (match_type == FLT_IN_ADDRESSBOOK) {
1478
                cond->match_func = str_case_find_equal;
1479
        } else {
1480
                if (FLT_IS_CASE_SENS(match_flag))
1481
                        cond->match_func = str_find;
1482
                else
1483
                        cond->match_func = str_case_find;
1484
        }
1485

    
1486
        return cond;
1487
}
1488

    
1489
FilterAction *filter_action_new(FilterActionType type, const gchar *str)
1490
{
1491
        FilterAction *action;
1492

    
1493
        action = g_new0(FilterAction, 1);
1494
        action->type = type;
1495

    
1496
        action->str_value = (str && *str) ? g_strdup(str) : NULL;
1497
        if (type == FLT_ACTION_COLOR_LABEL && str)
1498
                action->int_value = atoi(str);
1499
        else
1500
                action->int_value = 0;
1501

    
1502
        return action;
1503
}
1504

    
1505
FilterInfo *filter_info_new(void)
1506
{
1507
        FilterInfo *fltinfo;
1508

    
1509
        fltinfo = g_new0(FilterInfo, 1);
1510
        fltinfo->dest_list = NULL;
1511
        fltinfo->move_dest = NULL;
1512
        fltinfo->drop_done = FALSE;
1513
        fltinfo->error = FLT_ERROR_OK;
1514
        fltinfo->last_exec_exit_status = 0;
1515

    
1516
        return fltinfo;
1517
}
1518

    
1519
FilterRule *filter_junk_rule_create(PrefsAccount *account,
1520
                                    FolderItem *default_junk,
1521
                                    gboolean is_manual)
1522
{
1523
        FilterRule *rule;
1524
        FilterCond *cond;
1525
        FilterAction *action;
1526
        GSList *cond_list = NULL, *action_list = NULL;
1527
        gchar *junk_id = NULL;
1528
        FolderItem *item = NULL;
1529

    
1530
        if (!prefs_common.junk_classify_cmd)
1531
                return NULL;
1532

    
1533
        if (prefs_common.junk_folder)
1534
                item = folder_find_item_from_identifier(prefs_common.junk_folder);
1535

    
1536
        if (!item && account) {
1537
                Folder *folder = NULL;
1538
                GList *list;
1539

    
1540
                /* find most suitable Junk folder for account */
1541

    
1542
                if (account->inbox && *account->inbox == '#') {
1543
                        FolderItem *inbox;
1544
                        inbox = folder_find_item_from_identifier(account->inbox);
1545
                        if (inbox) {
1546
                                folder = inbox->folder;
1547
                                if (folder)
1548
                                        item = folder_get_junk(folder);
1549
                        }
1550
                }
1551
                if (!item) {
1552
                        folder = FOLDER(account->folder);
1553
                        if (folder)
1554
                                item = folder_get_junk(folder);
1555
                }
1556
                if (!item) {
1557
                        for (list = folder_get_list(); list != NULL; list = list->next) {
1558
                                folder = FOLDER(list->data);
1559
                                if (FOLDER_IS_LOCAL(folder)) {
1560
                                        if (folder->account == account)
1561
                                                item = folder_get_junk(folder);
1562
                                        if (!item && folder->node) {
1563
                                                item = FOLDER_ITEM(folder->node->data);
1564
                                                if (item && item->account == account && item->folder)
1565
                                                        item = folder_get_junk(item->folder);
1566
                                                else
1567
                                                        item = NULL;
1568
                                        }
1569
                                }
1570
                                if (item)
1571
                                        break;
1572
                        }
1573
                }
1574
        }
1575

    
1576
        if (!item)
1577
                item = default_junk;
1578
        if (!item)
1579
                item = folder_get_default_junk();
1580
        if (!item)
1581
                return NULL;
1582
        junk_id = folder_item_get_identifier(item);
1583
        if (!junk_id)
1584
                return NULL;
1585

    
1586
        debug_print("filter_junk_rule_create: junk folder: %s\n",
1587
                    junk_id);
1588

    
1589
        if (prefs_common.nofilter_junk_sender_in_book) {
1590
                cond = filter_cond_new(FLT_COND_HEADER, FLT_IN_ADDRESSBOOK,
1591
                                       FLT_NOT_MATCH, "From", NULL);
1592
                cond_list = g_slist_append(cond_list, cond);
1593
        }
1594

    
1595
        cond = filter_cond_new(FLT_COND_CMD_TEST, 0, 0, NULL,
1596
                               prefs_common.junk_classify_cmd);
1597
        cond_list = g_slist_append(cond_list, cond);
1598

    
1599
        if (prefs_common.delete_junk_on_recv && !is_manual) {
1600
                action = filter_action_new(FLT_ACTION_COPY, junk_id);
1601
                action_list = g_slist_append(NULL, action);
1602
                action = filter_action_new(FLT_ACTION_DELETE, NULL);
1603
                action_list = g_slist_append(action_list, action);
1604
        } else {
1605
                action = filter_action_new(FLT_ACTION_MOVE, junk_id);
1606
                action_list = g_slist_append(NULL, action);
1607
        }
1608

    
1609
        if (prefs_common.mark_junk_as_read) {
1610
                action = filter_action_new(FLT_ACTION_MARK_READ, NULL);
1611
                action_list = g_slist_append(action_list, action);
1612
        }
1613

    
1614
        if (is_manual)
1615
                rule = filter_rule_new(_("Junk mail filter (manual)"), FLT_AND,
1616
                                       cond_list, action_list);
1617
        else
1618
                rule = filter_rule_new(_("Junk mail filter"), FLT_AND,
1619
                                       cond_list, action_list);
1620

    
1621
        g_free(junk_id);
1622

    
1623
        return rule;
1624
}
1625

    
1626
void filter_rule_rename_dest_path(FilterRule *rule, const gchar *old_path,
1627
                                  const gchar *new_path)
1628
{
1629
        FilterAction *action;
1630
        GSList *cur;
1631
        gchar *base;
1632
        gchar *dest_path;
1633
        gint oldpathlen;
1634

    
1635
        oldpathlen = strlen(old_path);
1636

    
1637
        for (cur = rule->action_list; cur != NULL; cur = cur->next) {
1638
                action = (FilterAction *)cur->data;
1639

    
1640
                if (action->type != FLT_ACTION_MOVE &&
1641
                    action->type != FLT_ACTION_COPY)
1642
                        continue;
1643

    
1644
                if (action->str_value &&
1645
                    !strncmp(old_path, action->str_value, oldpathlen)) {
1646
                        base = action->str_value + oldpathlen;
1647
                        if (*base != '/' && *base != '\0')
1648
                                continue;
1649
                        while (*base == '/') base++;
1650
                        if (*base == '\0')
1651
                                dest_path = g_strdup(new_path);
1652
                        else
1653
                                dest_path = g_strconcat(new_path, "/", base,
1654
                                                        NULL);
1655
                        debug_print("filter_rule_rename_dest_path(): "
1656
                                    "renaming %s -> %s\n",
1657
                                    action->str_value, dest_path);
1658
                        g_free(action->str_value);
1659
                        action->str_value = dest_path;
1660
                }
1661
        }
1662
}
1663

    
1664
void filter_rule_delete_action_by_dest_path(FilterRule *rule, const gchar *path)
1665
{
1666
        FilterAction *action;
1667
        GSList *cur;
1668
        GSList *next;
1669
        gint pathlen;
1670

    
1671
        pathlen = strlen(path);
1672

    
1673
        for (cur = rule->action_list; cur != NULL; cur = next) {
1674
                action = (FilterAction *)cur->data;
1675
                next = cur->next;
1676

    
1677
                if (action->type != FLT_ACTION_MOVE &&
1678
                    action->type != FLT_ACTION_COPY)
1679
                        continue;
1680

    
1681
                if (action->str_value &&
1682
                    !strncmp(path, action->str_value, pathlen) &&
1683
                    (action->str_value[pathlen] == '/' ||
1684
                     action->str_value[pathlen] == '\0')) {
1685
                        debug_print("filter_rule_delete_action_by_dest_path(): "
1686
                                    "deleting %s\n", action->str_value);
1687
                        rule->action_list = g_slist_remove
1688
                                (rule->action_list, action);
1689
                        filter_action_free(action);
1690
                }
1691
        }
1692
}
1693

    
1694
void filter_list_rename_path(const gchar *old_path, const gchar *new_path)
1695
{
1696
        GSList *cur;
1697

    
1698
        g_return_if_fail(old_path != NULL);
1699
        g_return_if_fail(new_path != NULL);
1700

    
1701
        for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
1702
                FilterRule *rule = (FilterRule *)cur->data;
1703
                filter_rule_rename_dest_path(rule, old_path, new_path);
1704
        }
1705

    
1706
        filter_write_config();
1707
}
1708

    
1709
void filter_list_delete_path(const gchar *path)
1710
{
1711
        GSList *cur;
1712
        GSList *next;
1713

    
1714
        g_return_if_fail(path != NULL);
1715

    
1716
        for (cur = prefs_common.fltlist; cur != NULL; cur = next) {
1717
                FilterRule *rule = (FilterRule *)cur->data;
1718
                next = cur->next;
1719

    
1720
                filter_rule_delete_action_by_dest_path(rule, path);
1721
                if (!rule->action_list) {
1722
                        prefs_common.fltlist =
1723
                                g_slist_remove(prefs_common.fltlist, rule);
1724
                        filter_rule_free(rule);
1725
                }
1726
        }
1727

    
1728
        filter_write_config();
1729
}
1730

    
1731
void filter_rule_match_type_str_to_enum(const gchar *match_type,
1732
                                        FilterMatchType *type,
1733
                                        FilterMatchFlag *flag)
1734
{
1735
        g_return_if_fail(match_type != NULL);
1736

    
1737
        *type = FLT_CONTAIN;
1738
        *flag = 0;
1739

    
1740
        if (!strcmp(match_type, "contains")) {
1741
                *type = FLT_CONTAIN;
1742
        } else if (!strcmp(match_type, "not-contain")) {
1743
                *type = FLT_CONTAIN;
1744
                *flag = FLT_NOT_MATCH;
1745
        } else if (!strcmp(match_type, "is")) {
1746
                *type = FLT_EQUAL;
1747
        } else if (!strcmp(match_type, "is-not")) {
1748
                *type = FLT_EQUAL;
1749
                *flag = FLT_NOT_MATCH;
1750
        } else if (!strcmp(match_type, "regex")) {
1751
                *type = FLT_REGEX;
1752
        } else if (!strcmp(match_type, "not-regex")) {
1753
                *type = FLT_REGEX;
1754
                *flag = FLT_NOT_MATCH;
1755
        } else if (!strcmp(match_type, "in-addressbook")) {
1756
                *type = FLT_IN_ADDRESSBOOK;
1757
        } else if (!strcmp(match_type, "not-in-addressbook")) {
1758
                *type = FLT_IN_ADDRESSBOOK;
1759
                *flag = FLT_NOT_MATCH;
1760
        } else if (!strcmp(match_type, "gt")) {
1761
        } else if (!strcmp(match_type, "lt")) {
1762
                *flag = FLT_NOT_MATCH;
1763
        }
1764
}
1765

    
1766
void filter_get_keyword_from_msg(MsgInfo *msginfo, gchar **header, gchar **key,
1767
                                 FilterCreateType type)
1768
{
1769
        static HeaderEntry hentry[] = {{"List-Id:",           NULL, TRUE},
1770
                                       {"X-ML-Name:",           NULL, TRUE},
1771
                                       {"X-List:",           NULL, TRUE},
1772
                                       {"X-Mailing-list:", NULL, TRUE},
1773
                                       {"X-Sequence:",           NULL, TRUE},
1774
                                       {NULL,                   NULL, FALSE}};
1775
        enum
1776
        {
1777
                H_LIST_ID         = 0,
1778
                H_X_ML_NAME         = 1,
1779
                H_X_LIST         = 2,
1780
                H_X_MAILING_LIST = 3,
1781
                H_X_SEQUENCE         = 4
1782
        };
1783

    
1784
        FILE *fp;
1785

    
1786
        g_return_if_fail(msginfo != NULL);
1787
        g_return_if_fail(header != NULL);
1788
        g_return_if_fail(key != NULL);
1789

    
1790
        *header = NULL;
1791
        *key = NULL;
1792

    
1793
        switch (type) {
1794
        case FLT_BY_NONE:
1795
                return;
1796
        case FLT_BY_AUTO:
1797
                if ((fp = procmsg_open_message(msginfo)) == NULL)
1798
                        return;
1799
                procheader_get_header_fields(fp, hentry);
1800
                fclose(fp);
1801

    
1802
#define SET_FILTER_KEY(hstr, idx)        \
1803
{                                        \
1804
        *header = g_strdup(hstr);        \
1805
        *key = hentry[idx].body;        \
1806
        hentry[idx].body = NULL;        \
1807
}
1808

    
1809
                if (hentry[H_LIST_ID].body != NULL) {
1810
                        SET_FILTER_KEY("List-Id", H_LIST_ID);
1811
                        extract_list_id_str(*key);
1812
                } else if (hentry[H_X_ML_NAME].body != NULL) {
1813
                        SET_FILTER_KEY("X-ML-Name", H_X_ML_NAME);
1814
                } else if (hentry[H_X_LIST].body != NULL) {
1815
                        SET_FILTER_KEY("X-List", H_X_LIST);
1816
                } else if (hentry[H_X_MAILING_LIST].body != NULL) {
1817
                        SET_FILTER_KEY("X-Mailing-list", H_X_MAILING_LIST);
1818
                } else if (hentry[H_X_SEQUENCE].body != NULL) {
1819
                        gchar *p;
1820

    
1821
                        SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE);
1822
                        p = *key;
1823
                        while (*p != '\0') {
1824
                                while (*p != '\0' && !g_ascii_isspace(*p)) p++;
1825
                                while (g_ascii_isspace(*p)) p++;
1826
                                if (g_ascii_isdigit(*p)) {
1827
                                        *p = '\0';
1828
                                        break;
1829
                                }
1830
                        }
1831
                        g_strstrip(*key);
1832
                } else if (msginfo->subject) {
1833
                        *header = g_strdup("Subject");
1834
                        *key = g_strdup(msginfo->subject);
1835
                }
1836

    
1837
#undef SET_FILTER_KEY
1838

    
1839
                g_free(hentry[H_LIST_ID].body);
1840
                hentry[H_LIST_ID].body = NULL;
1841
                g_free(hentry[H_X_ML_NAME].body);
1842
                hentry[H_X_ML_NAME].body = NULL;
1843
                g_free(hentry[H_X_LIST].body);
1844
                hentry[H_X_LIST].body = NULL;
1845
                g_free(hentry[H_X_MAILING_LIST].body);
1846
                hentry[H_X_MAILING_LIST].body = NULL;
1847

    
1848
                break;
1849
        case FLT_BY_FROM:
1850
                *header = g_strdup("From");
1851
                *key = g_strdup(msginfo->from);
1852
                break;
1853
        case FLT_BY_TO:
1854
                *header = g_strdup("To");
1855
                *key = g_strdup(msginfo->to);
1856
                break;
1857
        case FLT_BY_SUBJECT:
1858
                *header = g_strdup("Subject");
1859
                *key = g_strdup(msginfo->subject);
1860
                break;
1861
        default:
1862
                break;
1863
        }
1864
}
1865

    
1866
void filter_rule_list_free(GSList *fltlist)
1867
{
1868
        GSList *cur;
1869

    
1870
        for (cur = fltlist; cur != NULL; cur = cur->next)
1871
                filter_rule_free((FilterRule *)cur->data);
1872
        g_slist_free(fltlist);
1873
}
1874

    
1875
void filter_rule_free(FilterRule *rule)
1876
{
1877
        if (!rule) return;
1878

    
1879
        g_free(rule->name);
1880
        g_free(rule->target_folder);
1881

    
1882
        filter_cond_list_free(rule->cond_list);
1883
        filter_action_list_free(rule->action_list);
1884

    
1885
        g_free(rule);
1886
}
1887

    
1888
void filter_cond_list_free(GSList *cond_list)
1889
{
1890
        GSList *cur;
1891

    
1892
        for (cur = cond_list; cur != NULL; cur = cur->next)
1893
                filter_cond_free((FilterCond *)cur->data);
1894
        g_slist_free(cond_list);
1895
}
1896

    
1897
void filter_action_list_free(GSList *action_list)
1898
{
1899
        GSList *cur;
1900

    
1901
        for (cur = action_list; cur != NULL; cur = cur->next)
1902
                filter_action_free((FilterAction *)cur->data);
1903
        g_slist_free(action_list);
1904
}
1905

    
1906
static void filter_cond_free(FilterCond *cond)
1907
{
1908
        g_free(cond->header_name);
1909
        g_free(cond->str_value);
1910
        g_free(cond);
1911
}
1912

    
1913
static void filter_action_free(FilterAction *action)
1914
{
1915
        g_free(action->str_value);
1916
        g_free(action);
1917
}
1918

    
1919
void filter_info_free(FilterInfo *fltinfo)
1920
{
1921
        g_slist_free(fltinfo->dest_list);
1922
        g_free(fltinfo);
1923
}