2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto & The Sylpheed Claws Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * initital Hoa initial
22 * 07/16/01 Alfons fix bugs
23 * 07/18/01 Alfons rewrite things
28 * filter_message() is no longer the entry point for filtering. it's
29 * replaced with filter_incoming_message() which does not trust
30 * on MsgInfo's from a folder. instead it directly works with
32 * updating the mark file is a lot easier now, because we can do that
33 * right after a call to folder_item_add_msg().
45 #include "procheader.h"
47 #include "filtering.h"
51 #define PREFSBUFSIZE 1024
53 GSList
* prefs_filtering
= NULL
;
55 FilteringAction
* filteringaction_new(int type
, int account_id
,
59 FilteringAction
* action
;
61 action
= g_new0(FilteringAction
, 1);
64 action
->account_id
= account_id
;
66 action
->destination
= g_strdup(destination
);
67 action
->labelcolor
= labelcolor
;
71 void filteringaction_free(FilteringAction
* action
)
73 if (action
->destination
)
74 g_free(action
->destination
);
78 FilteringAction
* filteringaction_parse(gchar
** str
)
80 FilteringAction
* action
;
82 gchar
* destination
= NULL
;
89 key
= matcher_parse_keyword(&tmp
);
92 case MATCHING_ACTION_MOVE
:
93 case MATCHING_ACTION_COPY
:
94 case MATCHING_EXECUTE
:
95 destination
= matcher_parse_str(&tmp
);
101 case MATCHING_ACTION_DELETE
:
103 case MATCHING_ACTION_MARK
:
105 case MATCHING_ACTION_MARK_AS_READ
:
107 case MATCHING_ACTION_UNMARK
:
109 case MATCHING_ACTION_MARK_AS_UNREAD
:
111 case MATCHING_ACTION_FORWARD
:
112 case MATCHING_ACTION_FORWARD_AS_ATTACHMENT
:
113 account_id
= matcher_parse_number(&tmp
);
119 destination
= matcher_parse_str(&tmp
);
126 case MATCHING_ACTION_COLOR
:
127 labelcolor
= matcher_parse_number(&tmp
);
141 action
= filteringaction_new(key
, account_id
, destination
, labelcolor
);
146 FilteringProp
* filteringprop_parse(gchar
** str
)
150 FilteringProp
* filtering
;
151 MatcherList
* matchers
;
152 FilteringAction
* action
;
156 matchers
= matcherlist_parse(&tmp
);
163 matcherlist_free(matchers
);
168 action
= filteringaction_parse(&tmp
);
170 matcherlist_free(matchers
);
175 filtering
= filteringprop_new(matchers
, action
);
182 FilteringProp
* filteringprop_new(MatcherList
* matchers
,
183 FilteringAction
* action
)
185 FilteringProp
* filtering
;
187 filtering
= g_new0(FilteringProp
, 1);
188 filtering
->matchers
= matchers
;
189 filtering
->action
= action
;
194 void filteringprop_free(FilteringProp
* prop
)
196 matcherlist_free(prop
->matchers
);
197 filteringaction_free(prop
->action
);
201 /* filteringaction_update_mark() - updates a mark for a message. note that
202 * the message should not have been moved or copied. remember that the
203 * procmsg_open_mark_file(PATH, TRUE) actually _appends_ a new record.
205 static gboolean
filteringaction_update_mark(MsgInfo
* info
)
210 if (info
->folder
->folder
->type
== F_MH
) {
211 dest_path
= folder_item_get_path(info
->folder
);
212 if (!is_dir_exist(dest_path
))
213 make_dir_hier(dest_path
);
215 if (dest_path
== NULL
) {
216 g_warning(_("Can't open mark file.\n"));
220 if ((fp
= procmsg_open_mark_file(dest_path
, TRUE
))
222 g_warning(_("Can't open mark file.\n"));
226 procmsg_write_flags(info
, fp
);
233 static inline gint
strlen_with_check(const gchar
*expr
, gint fline
, const gchar
*str
)
238 debug_print("%s(%d) - invalid string %s\n", __FILE__
, fline
, expr
);
243 #define STRLEN_WITH_CHECK(expr) \
244 strlen_with_check(#expr, __LINE__, expr)
246 static gchar
* filteringaction_execute_command(gchar
* cmd
, MsgInfo
* info
)
249 gchar
* filename
= NULL
;
250 gchar
* processed_cmd
;
254 matcher_unescape_str(cmd
);
256 size
= strlen(cmd
) + 1;
264 case 's': /* subject */
265 size
+= STRLEN_WITH_CHECK(info
->subject
) - 2;
268 size
+= STRLEN_WITH_CHECK(info
->from
) - 2;
271 size
+= STRLEN_WITH_CHECK(info
->to
) - 2;
274 size
+= STRLEN_WITH_CHECK(info
->cc
) - 2;
277 size
+= STRLEN_WITH_CHECK(info
->date
) - 2;
279 case 'i': /* message-id */
280 size
+= STRLEN_WITH_CHECK(info
->msgid
) - 2;
282 case 'n': /* newsgroups */
283 size
+= STRLEN_WITH_CHECK(info
->newsgroups
) - 2;
285 case 'r': /* references */
286 size
+= STRLEN_WITH_CHECK(info
->references
) - 2;
289 if (MSG_IS_FILTERING(info
->flags
))
290 filename
= g_strdup((gchar
*)info
->folder
);
292 filename
= folder_item_fetch_msg(info
->folder
, info
->msgnum
);
294 if (filename
== NULL
) {
295 g_warning(_("filename is not set"));
299 size
+= strlen(filename
) - 2;
308 processed_cmd
= g_new0(gchar
, size
);
320 case 's': /* subject */
321 if (info
->subject
!= NULL
)
322 strcpy(p
, info
->subject
);
328 if (info
->from
!= NULL
)
329 strcpy(p
, info
->from
);
335 if (info
->to
!= NULL
)
342 if (info
->cc
!= NULL
)
349 if (info
->date
!= NULL
)
350 strcpy(p
, info
->date
);
355 case 'i': /* message-id */
356 if (info
->msgid
!= NULL
)
357 strcpy(p
, info
->msgid
);
362 case 'n': /* newsgroups */
363 if (info
->newsgroups
!= NULL
)
364 strcpy(p
, info
->newsgroups
);
369 case 'r': /* references */
370 if (info
->references
!= NULL
)
371 strcpy(p
, info
->references
);
397 return processed_cmd
;
401 fitleringaction_apply
402 runs the action on one MsgInfo
403 return value : return TRUE if the action could be applied
406 #define CHANGE_FLAGS(msginfo) \
408 if (msginfo->folder->folder->change_flags != NULL) \
409 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
414 static gboolean
filteringaction_apply(FilteringAction
* action
, MsgInfo
* info
,
415 GHashTable
*folder_table
)
417 FolderItem
* dest_folder
;
420 PrefsAccount
* account
;
423 switch(action
->type
) {
424 case MATCHING_ACTION_MOVE
:
426 folder_find_item_from_identifier(action
->destination
);
430 if (folder_item_move_msg(dest_folder
, info
) == -1)
433 /* WRONG: can not update the mark, because the message has
434 * been moved. info pertains to original location.
435 * folder_item_move_msg() already updated the mark for the
436 * destination folder.
438 filteringaction_update_mark(info);
441 val
= GPOINTER_TO_INT(g_hash_table_lookup
442 (folder_table
, dest_folder
));
444 folder_item_scan(dest_folder
);
445 g_hash_table_insert(folder_table
, dest_folder
,
448 val
= GPOINTER_TO_INT(g_hash_table_lookup
449 (folder_table
, info
->folder
));
451 folder_item_scan(info
->folder
);
452 g_hash_table_insert(folder_table
, info
->folder
,
458 case MATCHING_ACTION_COPY
:
460 folder_find_item_from_identifier(action
->destination
);
465 /* NOTE: the following call *will* update the mark file for
466 * the destination folder. but the original message will
467 * still be there in the inbox. */
469 if (folder_item_copy_msg(dest_folder
, info
) == -1)
473 val
= GPOINTER_TO_INT(g_hash_table_lookup
474 (folder_table
, dest_folder
));
476 folder_item_scan(dest_folder
);
477 g_hash_table_insert(folder_table
, dest_folder
,
484 case MATCHING_ACTION_DELETE
:
485 if (folder_item_remove_msg(info
->folder
, info
->msgnum
) == -1)
488 /* WRONG: can not update the mark. this would actually add
489 * a bogus record to the mark file for the message's original
492 filteringaction_update_mark(info);
497 case MATCHING_ACTION_MARK
:
498 MSG_SET_PERM_FLAGS(info
->flags
, MSG_MARKED
);
499 debug_print("*** MARKING message %d, in folder %s, \"%s\"\n",
500 info
->msgnum
, info
->folder
->name
,
501 info
->subject
? info
->subject
: "<none>");
502 filteringaction_update_mark(info
);
508 case MATCHING_ACTION_UNMARK
:
509 MSG_UNSET_PERM_FLAGS(info
->flags
, MSG_MARKED
);
510 filteringaction_update_mark(info
);
516 case MATCHING_ACTION_MARK_AS_READ
:
517 MSG_UNSET_PERM_FLAGS(info
->flags
, MSG_UNREAD
| MSG_NEW
);
518 filteringaction_update_mark(info
);
524 case MATCHING_ACTION_MARK_AS_UNREAD
:
525 MSG_SET_PERM_FLAGS(info
->flags
, MSG_UNREAD
| MSG_NEW
);
526 filteringaction_update_mark(info
);
532 case MATCHING_ACTION_FORWARD
:
534 account
= account_find_from_id(action
->account_id
);
535 compose
= compose_forward(account
, info
, FALSE
);
536 if (compose
->account
->protocol
== A_NNTP
)
537 compose_entry_append(compose
, action
->destination
,
540 compose_entry_append(compose
, action
->destination
,
543 val
= compose_send(compose
);
545 gtk_widget_destroy(compose
->window
);
549 gtk_widget_destroy(compose
->window
);
552 case MATCHING_ACTION_FORWARD_AS_ATTACHMENT
:
554 account
= account_find_from_id(action
->account_id
);
555 compose
= compose_forward(account
, info
, TRUE
);
556 if (compose
->account
->protocol
== A_NNTP
)
557 compose_entry_append(compose
, action
->destination
,
560 compose_entry_append(compose
, action
->destination
,
563 val
= compose_send(compose
);
565 gtk_widget_destroy(compose
->window
);
569 gtk_widget_destroy(compose
->window
);
572 case MATCHING_EXECUTE
:
574 cmd
= matching_build_command(action
->destination
, info
);
589 /* filteringprop_apply() - runs the action on one MsgInfo if it matches the
590 * criterium. certain actions can be followed by other actions. in this
591 * case the function returns FALSE. if an action can not be followed
592 * by others, the function returns TRUE.
594 * remember that this is because of the fact that msg flags are always
595 * _appended_ to mark files. currently sylpheed does not insert messages
596 * at a certain index.
597 * now, after having performed a certain action, the MsgInfo is still
598 * valid for the message. in *this* case the function returns FALSE.
600 static gboolean
filteringprop_apply(FilteringProp
* filtering
, MsgInfo
* info
,
601 GHashTable
*folder_table
)
603 if (matcherlist_match(filtering
->matchers
, info
)) {
609 result
= filteringaction_apply(filtering
->action
, info
,
612 filteringaction_to_string(filtering
->action
);
614 g_warning(_("action %s could not be applied"),
618 debug_print(_("message %i %s..."),
619 info
->msgnum
, action_str
);
624 switch(filtering
->action
->type
) {
625 case MATCHING_ACTION_MOVE
:
626 case MATCHING_ACTION_DELETE
:
627 return TRUE
; /* MsgInfo invalid for message */
628 case MATCHING_EXECUTE
:
629 case MATCHING_ACTION_COPY
:
630 case MATCHING_ACTION_MARK
:
631 case MATCHING_ACTION_MARK_AS_READ
:
632 case MATCHING_ACTION_UNMARK
:
633 case MATCHING_ACTION_MARK_AS_UNREAD
:
634 case MATCHING_ACTION_FORWARD
:
635 case MATCHING_ACTION_FORWARD_AS_ATTACHMENT
:
636 return FALSE
; /* MsgInfo still valid for message */
645 void filter_msginfo(GSList
* filtering_list
, MsgInfo
* info
,
646 GHashTable
*folder_table
)
651 g_warning(_("msginfo is not set"));
655 for(l
= filtering_list
; l
!= NULL
; l
= g_slist_next(l
)) {
656 FilteringProp
* filtering
= (FilteringProp
*) l
->data
;
658 if (filteringprop_apply(filtering
, info
, folder_table
))
663 void filter_msginfo_move_or_delete(GSList
* filtering_list
, MsgInfo
* info
,
664 GHashTable
*folder_table
)
669 g_warning(_("msginfo is not set"));
673 for(l
= filtering_list
; l
!= NULL
; l
= g_slist_next(l
)) {
674 FilteringProp
* filtering
= (FilteringProp
*) l
->data
;
676 switch (filtering
->action
->type
) {
677 case MATCHING_ACTION_MOVE
:
678 case MATCHING_ACTION_DELETE
:
679 if (filteringprop_apply(filtering
, info
, folder_table
))
685 void filter_message(GSList
* filtering_list
, FolderItem
* item
,
686 gint msgnum
, GHashTable
*folder_table
)
690 MsgFlags msgflags
= { 0, 0 };
693 g_warning(_("folderitem not set"));
697 filename
= folder_item_fetch_msg(item
, msgnum
);
699 if (filename
== NULL
) {
700 g_warning(_("filename is not set"));
704 msginfo
= procheader_parse(filename
, msgflags
, TRUE
);
708 if (msginfo
== NULL
) {
709 g_warning(_("could not get info for %s"), filename
);
713 msginfo
->folder
= item
;
714 msginfo
->msgnum
= msgnum
;
716 filter_msginfo(filtering_list
, msginfo
, folder_table
);
720 /******************************************************************************/
722 /* revised filtering system.
724 * 07/18/01 alfons initial revisement
729 * the revised filtering system does not rely on a message being "registered"
730 * in one of sylpheed's folders. it now uses the file sylpheed created on
731 * incorporation of mail.
732 * i do use MsgInfo data; that's needed by the matcher, but for the rest
733 * the MsgInfo is not really used. */
736 /* add_mark() - adds a mark for a file */
737 static void add_mark(FolderItem
*folder
, gint msgnum
, MsgPermFlags flags
)
742 if (folder
->folder
->type
== F_MH
) {
743 dest_path
= folder_item_get_path(folder
);
744 if (!is_dir_exist(dest_path
))
745 make_dir_hier(dest_path
);
747 if (dest_path
== NULL
) {
748 g_warning(_("Can't open mark file.\n"));
752 if ((fp
= procmsg_open_mark_file(dest_path
, TRUE
))
754 g_warning(_("Can't open mark file.\n"));
758 /* TODO: straight from procmsg.c:procmsg_write_flags()
759 * should update this when mark file version changes */
760 #if MARK_VERSION == 2
761 WRITE_CACHE_DATA_INT(msgnum
, fp
);
762 WRITE_CACHE_DATA_INT(flags
, fp
);
764 #error should rewrite the above for new mark version
765 /* paste the source code of procmsg.c:procmsg_write_flags() */
773 /* prepare_destination() - prepares destination folder by registering it
774 * in the global folder table (if not already there). it returns TRUE if
775 * successful. it also returns the FolderItem for the destination */
776 static gboolean
prepare_destination(const gchar
*destination
, FolderItem
**item
,
777 GHashTable
*folder_table
)
782 result
= folder_find_item_from_identifier(destination
);
787 val
= GPOINTER_TO_INT(g_hash_table_lookup(folder_table
, *item
));
789 folder_item_scan(*item
);
790 g_hash_table_insert(folder_table
, *item
, GINT_TO_POINTER(1));
796 /* filter_incoming_perform_actions() - performs actions on incoming
797 * message. this function handles updating marks a little bit smarter;
798 * remember that marks can only be appended. */
799 static gboolean
filter_incoming_perform_actions(FolderItem
*default_folder
,
801 GHashTable
*folder_table
)
803 /* matching actions for msginfo */
804 struct matching_action
{
805 FilteringAction
*action
;
806 struct matching_action
*next
;
809 struct matching_action ma_head
= { NULL
, NULL
};
810 struct matching_action
*ma_tail
= &ma_head
;
812 /* need this for the forwarding stuff */
813 PrefsAccount
*account
;
820 MsgFlags markflags
= { 0, 0 };
822 /* use the global prefs_filtering list */
823 GSList
* list
= prefs_filtering
;
825 /* things we get after having added a message to a folder */
826 FolderItem
*dest_folder
;
829 /* after performing certain action, we may have to copy
830 * the message to inbox too */
831 gboolean copy_to_inbox_too
= TRUE
;
833 /* filename is only put in msginfo->folder if MSG_FILTERING is set. */
834 const gchar
*filename
= (gchar
*) msginfo
->folder
;
840 /* build list of matching actions */
841 for (; list
!= NULL
; list
= g_slist_next(list
)) {
842 FilteringProp
*prop
= (FilteringProp
*) list
->data
;
844 if (!matcherlist_match(prop
->matchers
, msginfo
))
847 if (NULL
== (ma_tail
->next
= alloca(sizeof(struct matching_action
)))) {
848 debug_print(_("action allocation error\n"));
852 debug_print("*** action %d\n", prop
->action
->type
);
854 ma_tail
->action
= prop
->action
;
855 ma_tail
->next
->action
= NULL
;
856 ma_tail
->next
->next
= NULL
;
857 ma_tail
= ma_tail
->next
;
860 debug_print("*** collecting marks\n");
862 /* collect all marks */
863 for (ma_tail
= &ma_head
, stop
= FALSE
; ma_tail
->action
&& !stop
; ma_tail
= ma_tail
->next
) {
864 switch (ma_tail
->action
->type
) {
866 case MATCHING_ACTION_MARK
:
867 MSG_SET_PERM_FLAGS(markflags
, MSG_MARKED
);
870 case MATCHING_ACTION_UNMARK
:
871 MSG_UNSET_PERM_FLAGS(markflags
, MSG_MARKED
);
874 case MATCHING_ACTION_MARK_AS_READ
:
875 MSG_UNSET_PERM_FLAGS(markflags
, MSG_UNREAD
);
878 case MATCHING_ACTION_MARK_AS_UNREAD
:
879 MSG_SET_PERM_FLAGS(markflags
, MSG_UNREAD
);
882 case MATCHING_ACTION_COLOR
:
883 MSG_SET_LABEL_VALUE(markflags
, ma_tail
->action
->labelcolor
);
887 case MATCHING_ACTION_FORWARD
:
888 case MATCHING_ACTION_FORWARD_AS_ATTACHMENT
:
889 case MATCHING_ACTION_MOVE
:
890 case MATCHING_ACTION_DELETE
:
900 /* msgnum & dest_folder should have valid values after a succesful
901 * drop; in this case there is a MsgInfo available */
905 debug_print("*** performing actions\n");
907 for (ma_tail
= &ma_head
; ma_tail
->action
; ma_tail
= ma_tail
->next
) {
909 /* there are variables you have to use when defining new actions:
911 * copy_to_inbox_too - if the original message should be copied to the inbox
912 * (default_folder) too.
914 * also note that after dropping it to a folder (folder_item_add_msg()) you have
915 * to mark the message, just to make sure any defined mark actions are applied. */
917 #define ACTION (ma_tail->action->type)
919 /* C O N T I N U A B L E */
921 if (MATCHING_ACTION_COPY
== ACTION
) {
922 debug_print("*** performing copy\n");
923 copy_to_inbox_too
= TRUE
;
924 if (!prepare_destination(ma_tail
->action
->destination
, &dest_folder
, folder_table
)) {
925 debug_print("Rule failed: unknown destination %s\n", ma_tail
->action
->destination
);
926 continue; /* try next action */
928 if (0 > (msgnum
= folder_item_add_msg(dest_folder
, filename
, FALSE
))) {
929 debug_print(_("Rule failed: could not copy to folder %s\n"),
930 ma_tail
->action
->destination
);
931 continue; /* try next action */
933 flags
= msginfo
->flags
.perm_flags
| markflags
.perm_flags
;
934 add_mark(dest_folder
, msgnum
, flags
);
936 else if (MATCHING_EXECUTE
== ACTION
) {
937 debug_print("*** performing exec\n");
938 copy_to_inbox_too
= TRUE
;
940 /* matching_build_command() knows about filtering */
941 cmd
= matching_build_command(ma_tail
->action
->destination
, msginfo
);
947 debug_print(_("Rule failed: no command line\n"));
950 /* U N C O N T I N U A B L E */
952 else if (MATCHING_ACTION_FORWARD
== ACTION
953 || MATCHING_ACTION_FORWARD_AS_ATTACHMENT
== ACTION
) {
954 debug_print("*** performing forward\n");
956 /* forwarding messages is complicated because there's currently no
957 * way to forward a message using "filenames"; you can only forward
958 * a message if you have its MsgInfo. this means we have to drop
959 * the message first */
960 if (0 > (msgnum
= folder_item_add_msg(default_folder
, filename
, FALSE
))) {
961 debug_print(_("Rule failed: could not forward\n"));
962 copy_to_inbox_too
= TRUE
;
966 flags
= msginfo
->flags
.perm_flags
| markflags
.perm_flags
;
967 flags
|= MSG_FORWARDED
;
968 add_mark(default_folder
, msgnum
, flags
);
970 /* grab the dropped message */
971 fwd_msg_name
= folder_item_fetch_msg(default_folder
, msgnum
);
973 tmp
.perm_flags
= tmp
.tmp_flags
= 0;
974 fwd_msg
= procheader_parse(fwd_msg_name
, tmp
, TRUE
);
976 fwd_msg
->folder
= default_folder
;
977 fwd_msg
->msgnum
= msgnum
;
979 /* do the compose_XXX stuff */
980 account
= account_find_from_id(ma_tail
->action
->account_id
);
981 compose
= compose_forward(account
, fwd_msg
, ACTION
== MATCHING_ACTION_FORWARD
? FALSE
: TRUE
);
982 if (compose
->account
->protocol
== A_NNTP
)
983 compose_entry_append(compose
, ma_tail
->action
->destination
,
986 compose_entry_append(compose
, ma_tail
->action
->destination
,
989 compose_send(compose
);
991 procmsg_msginfo_free(fwd_msg
);
993 gtk_widget_destroy(compose
->window
);
996 else if (MATCHING_ACTION_DELETE
== ACTION
) {
997 debug_print("*** performing delete\n");
998 copy_to_inbox_too
= FALSE
;
1001 dest_folder
= folder_get_default_trash();
1002 msgnum
= folder_item_add_msg(dest_folder
, filename
, FALSE
);
1004 debug_print(_("Rule failed: could not move to trash"));
1005 copy_to_inbox_too
= TRUE
;
1008 flags
= msginfo
->flags
.perm_flags
| markflags
.perm_flags
;
1009 add_mark(dest_folder
, msgnum
, flags
);
1013 else if (MATCHING_ACTION_MOVE
== ACTION
) {
1014 debug_print("*** performing move\n");
1015 copy_to_inbox_too
= FALSE
;
1017 if (!prepare_destination(ma_tail
->action
->destination
, &dest_folder
, folder_table
)) {
1018 copy_to_inbox_too
= TRUE
;
1022 msgnum
= folder_item_add_msg(dest_folder
, filename
, FALSE
);
1024 debug_print(_("Rule failed: could not move to folder %s\n"),
1025 ma_tail
->action
->destination
);
1026 copy_to_inbox_too
= TRUE
;
1030 flags
= msginfo
->flags
.perm_flags
| markflags
.perm_flags
;
1031 add_mark(dest_folder
, msgnum
, flags
);
1036 /* may need to copy it to inbox too */
1037 if (copy_to_inbox_too
) {
1038 debug_print("*** performing inbox copy\n");
1039 msgnum
= folder_item_add_msg(default_folder
, filename
, TRUE
);
1041 debug_print(_("error copying incoming file %s to inbox\n"),
1045 flags
= msginfo
->flags
.perm_flags
| markflags
.perm_flags
;
1046 add_mark(default_folder
, msgnum
, flags
);
1049 debug_print("*** unlinking\n");
1050 if (unlink(filename
) < 0)
1051 debug_print(_("error deleting incoming message file\n"));
1059 static void filter_incoming_msginfo(FolderItem
*default_folder
, MsgInfo
*msginfo
,
1060 GHashTable
*folder_table
)
1062 filter_incoming_perform_actions(default_folder
, msginfo
, folder_table
);
1065 /* filter_incoming_message() - tries to apply a filter on one incoming message.
1066 * it also handles the case when there's no filter match */
1067 void filter_incoming_message(FolderItem
*default_folder
, const gchar
*file_name
,
1068 GHashTable
*folder_table
)
1071 MsgFlags msgflags
= { 0, 0 };
1073 /* make an "uncomplete" msginfo. it's incomplete because it doesn't
1074 * have a message number / folder yet. */
1075 if (NULL
== (msginfo
= procheader_parse(file_name
, msgflags
, TRUE
))) {
1076 g_warning(_("error filtering incoming message %s\n"),
1081 /* let matcher know that this is a message that has no
1082 * valid body data yet. */
1083 MSG_SET_TMP_FLAGS(msginfo
->flags
, MSG_FILTERING
);
1084 msginfo
->folder
= (FolderItem
*) g_strdup(file_name
); /* actually storing a pointer to a string */
1085 msginfo
->msgnum
= 0;
1087 filter_incoming_msginfo(default_folder
, msginfo
, folder_table
);
1089 g_free(msginfo
->folder
);
1090 procmsg_msginfo_free(msginfo
);
1093 /******************************************************************************/
1095 void prefs_filtering_read_config(void)
1099 gchar buf
[PREFSBUFSIZE
];
1101 debug_print(_("Reading filtering configuration...\n"));
1103 rcpath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
1104 FILTERING_RC
, NULL
);
1105 if ((fp
= fopen(rcpath
, "r")) == NULL
) {
1106 if (ENOENT
!= errno
) FILE_OP_ERROR(rcpath
, "fopen");
1108 prefs_filtering
= NULL
;
1113 /* remove all filtering */
1114 while (prefs_filtering
!= NULL
) {
1115 FilteringProp
* filtering
=
1116 (FilteringProp
*) prefs_filtering
->data
;
1117 filteringprop_free(filtering
);
1118 prefs_filtering
= g_slist_remove(prefs_filtering
, filtering
);
1121 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1122 FilteringProp
* filtering
;
1127 if ((*buf
!= '#') && (*buf
!= '\0')) {
1129 filtering
= filteringprop_parse(&tmp
);
1132 g_slist_append(prefs_filtering
,
1137 g_warning(_("syntax error : %s\n"), buf
);
1145 gchar
* filteringaction_to_string(FilteringAction
* action
)
1147 gchar
* command_str
;
1149 gchar
* account_id_str
;
1150 gchar
* labelcolor_str
;
1152 /* FIXME: use g_sprintf() throughout */
1155 command_str
= get_matchparser_tab_str(action
->type
);
1157 if (command_str
== NULL
)
1160 switch(action
->type
) {
1161 case MATCHING_ACTION_MOVE
:
1162 case MATCHING_ACTION_COPY
:
1163 case MATCHING_EXECUTE
:
1164 return g_strconcat(command_str
, " \"", action
->destination
,
1167 case MATCHING_ACTION_DELETE
:
1168 case MATCHING_ACTION_MARK
:
1169 case MATCHING_ACTION_UNMARK
:
1170 case MATCHING_ACTION_MARK_AS_READ
:
1171 case MATCHING_ACTION_MARK_AS_UNREAD
:
1172 return g_strdup(command_str
);
1175 case MATCHING_ACTION_FORWARD
:
1176 case MATCHING_ACTION_FORWARD_AS_ATTACHMENT
:
1177 account_id_str
= itos(action
->account_id
);
1178 return g_strconcat(command_str
, " ", account_id_str
,
1179 " \"", action
->destination
, "\"", NULL
);
1181 case MATCHING_ACTION_COLOR
:
1182 labelcolor_str
= itos(action
->labelcolor
);
1183 return g_strconcat(command_str
, " ", labelcolor_str
, NULL
);
1190 gchar
* filteringprop_to_string(FilteringProp
* prop
)
1194 gchar
* filtering_str
;
1196 action_str
= filteringaction_to_string(prop
->action
);
1198 if (action_str
== NULL
)
1201 list_str
= matcherlist_to_string(prop
->matchers
);
1203 if (list_str
== NULL
) {
1208 filtering_str
= g_strconcat(list_str
, " ", action_str
, NULL
);
1212 return filtering_str
;
1215 void prefs_filtering_write_config(void)
1220 FilteringProp
* prop
;
1222 debug_print(_("Writing filtering configuration...\n"));
1224 rcpath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, FILTERING_RC
, NULL
);
1226 if ((pfile
= prefs_write_open(rcpath
)) == NULL
) {
1227 g_warning(_("failed to write configuration to file\n"));
1232 for (cur
= prefs_filtering
; cur
!= NULL
; cur
= cur
->next
) {
1233 gchar
*filtering_str
;
1235 prop
= (FilteringProp
*) cur
->data
;
1236 filtering_str
= filteringprop_to_string(prop
);
1238 if (fputs(filtering_str
, pfile
->fp
) == EOF
||
1239 fputc('\n', pfile
->fp
) == EOF
) {
1240 FILE_OP_ERROR(rcpath
, "fputs || fputc");
1241 prefs_write_close_revert(pfile
);
1243 g_free(filtering_str
);
1246 g_free(filtering_str
);
1251 if (prefs_write_close(pfile
) < 0) {
1252 g_warning(_("failed to write configuration to file\n"));