5 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
6 * Copyright (c) 2001-2002 by Hiroyuki Yamamoto & The Sylpheed Claws Team.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "filtering.h"
32 #include "matcher_parser.h"
33 #include "matcher_parser_lex.h"
36 #define MAX_COLORLABELS (MSG_CLABEL_7 - MSG_CLABEL_NONE)
38 static gint error = 0;
39 static gint bool_op = 0;
40 static gint match_type = 0;
41 static gchar *header = NULL;
43 static MatcherProp *prop;
45 static GSList *matchers_list = NULL;
47 static MatcherList *cond;
48 static gint score = 0;
49 static FilteringAction *action = NULL;
51 static FilteringProp *filtering;
52 static ScoringProp *scoring = NULL;
54 static GSList **prefs_scoring = NULL;
55 static GSList **prefs_filtering = NULL;
57 static int matcher_parser_dialog = 0;
59 /* ******************************************************************** */
61 void matcher_parser_start_parsing(FILE *f)
63 matcher_parserrestart(f);
64 matcher_parserparse();
67 FilteringProp *matcher_parser_get_filtering(gchar *str)
71 /* bad coding to enable the sub-grammar matching
73 matcher_parserlineno = 1;
74 matcher_parser_dialog = 1;
75 matcher_parserrestart(NULL);
76 matcher_parser_init();
77 bufstate = matcher_parser_scan_string(str);
78 matcher_parser_switch_to_buffer(bufstate);
79 if (matcher_parserparse() != 0)
81 matcher_parser_dialog = 0;
82 matcher_parser_delete_buffer(bufstate);
86 ScoringProp *matcher_parser_get_scoring(gchar *str)
90 /* bad coding to enable the sub-grammar matching
92 matcher_parserlineno = 1;
93 matcher_parser_dialog = 1;
94 matcher_parserrestart(NULL);
95 matcher_parser_init();
96 bufstate = matcher_parser_scan_string(str);
97 matcher_parser_switch_to_buffer(bufstate);
98 if (matcher_parserparse() != 0)
100 matcher_parser_dialog = 0;
101 matcher_parser_delete_buffer(bufstate);
105 static gboolean check_quote_symetry(gchar *str)
111 return TRUE; /* heh, that's symetric */
114 for (walk = str; *walk; walk++) {
116 if (walk == str /* first char */
117 || *(walk - 1) != '\\') /* not escaped */
124 MatcherList *matcher_parser_get_cond(gchar *str)
128 if (!check_quote_symetry(str)) {
133 /* bad coding to enable the sub-grammar matching
135 matcher_parserlineno = 1;
136 matcher_parser_dialog = 1;
137 matcher_parserrestart(NULL);
138 matcher_parser_init();
139 bufstate = matcher_parser_scan_string(str);
140 matcher_parserparse();
141 matcher_parser_dialog = 0;
142 matcher_parser_delete_buffer(bufstate);
146 MatcherProp *matcher_parser_get_prop(gchar *str)
151 matcher_parserlineno = 1;
152 list = matcher_parser_get_cond(str);
156 if (list->matchers == NULL)
159 if (list->matchers->next != NULL)
162 prop = list->matchers->data;
164 g_slist_free(list->matchers);
170 void matcher_parsererror(char *str)
175 for (l = matchers_list; l != NULL; l = g_slist_next(l)) {
176 matcherprop_free((MatcherProp *)
180 g_slist_free(matchers_list);
181 matchers_list = NULL;
184 g_warning("scoring / filtering parsing: %i: %s\n",
185 matcher_parserlineno, str);
189 int matcher_parserwrap(void)
200 %token MATCHER_ALL MATCHER_UNREAD MATCHER_NOT_UNREAD
201 %token MATCHER_NEW MATCHER_NOT_NEW MATCHER_MARKED
202 %token MATCHER_NOT_MARKED MATCHER_DELETED MATCHER_NOT_DELETED
203 %token MATCHER_REPLIED MATCHER_NOT_REPLIED MATCHER_FORWARDED
204 %token MATCHER_NOT_FORWARDED MATCHER_SUBJECT MATCHER_NOT_SUBJECT
205 %token MATCHER_FROM MATCHER_NOT_FROM MATCHER_TO MATCHER_NOT_TO
206 %token MATCHER_CC MATCHER_NOT_CC MATCHER_TO_OR_CC MATCHER_NOT_TO_AND_NOT_CC
207 %token MATCHER_AGE_GREATER MATCHER_AGE_LOWER MATCHER_NEWSGROUPS
208 %token MATCHER_NOT_NEWSGROUPS MATCHER_INREPLYTO MATCHER_NOT_INREPLYTO
209 %token MATCHER_REFERENCES MATCHER_NOT_REFERENCES MATCHER_SCORE_GREATER
210 %token MATCHER_SCORE_LOWER MATCHER_HEADER MATCHER_NOT_HEADER
211 %token MATCHER_HEADERS_PART MATCHER_NOT_HEADERS_PART MATCHER_MESSAGE
212 %token MATCHER_NOT_MESSAGE MATCHER_BODY_PART MATCHER_NOT_BODY_PART
213 %token MATCHER_EXECUTE MATCHER_NOT_EXECUTE MATCHER_MATCHCASE MATCHER_MATCH
214 %token MATCHER_REGEXPCASE MATCHER_REGEXP MATCHER_SCORE MATCHER_MOVE
215 %token MATCHER_COPY MATCHER_DELETE MATCHER_MARK MATCHER_UNMARK
216 %token MATCHER_MARK_AS_READ MATCHER_MARK_AS_UNREAD MATCHER_FORWARD
217 %token MATCHER_FORWARD_AS_ATTACHMENT MATCHER_EOL MATCHER_STRING
218 %token MATCHER_OR MATCHER_AND
219 %token MATCHER_COLOR MATCHER_SCORE_EQUAL MATCHER_REDIRECT MATCHER_DELETE_ON_SERVER
220 %token MATCHER_SIZE_GREATER MATCHER_SIZE_SMALLER MATCHER_SIZE_EQUAL
221 %token MATCHER_LOCKED MATCHER_NOT_LOCKED
222 %token MATCHER_COLORLABEL MATCHER_NOT_COLORLABEL
223 %token MATCHER_IGNORE_THREAD MATCHER_NOT_IGNORE_THREAD
224 %token MATCHER_CHANGE_SCORE
228 %token <str> MATCHER_STRING
229 %token <str> MATCHER_SECTION
230 %token <str> MATCHER_INTEGER
236 if (!matcher_parser_dialog) {
237 prefs_scoring = &global_scoring;
238 prefs_filtering = &global_processing;
257 section_notification:
258 MATCHER_SECTION MATCHER_EOL
261 FolderItem *item = NULL;
263 if (!matcher_parser_dialog) {
264 item = folder_find_item_from_identifier(folder);
266 prefs_scoring = &global_scoring;
267 prefs_filtering = &global_processing;
270 prefs_scoring = &item->prefs->scoring;
271 prefs_filtering = &item->prefs->processing;
278 condition end_instr_opt
283 filtering_or_scoring end_action
286 if (matcher_parser_dialog)
289 matcher_parsererror("parse error");
299 if (matcher_parser_dialog)
302 matcher_parsererror("parse error");
308 filtering_or_scoring:
311 filtering = filteringprop_new(cond, action);
314 if (!matcher_parser_dialog) {
315 *prefs_filtering = g_slist_append(*prefs_filtering,
322 scoring = scoringprop_new(cond, score);
325 if (!matcher_parser_dialog) {
326 *prefs_scoring = g_slist_append(*prefs_scoring, scoring);
335 match_type = MATCHTYPE_MATCHCASE;
339 match_type = MATCHTYPE_MATCH;
343 match_type = MATCHTYPE_REGEXPCASE;
347 match_type = MATCHTYPE_REGEXP;
354 cond = matcherlist_new(matchers_list, (bool_op == MATCHERBOOL_AND));
355 matchers_list = NULL;
360 condition_list bool_op one_condition
362 matchers_list = g_slist_append(matchers_list, prop);
366 matchers_list = NULL;
367 matchers_list = g_slist_append(matchers_list, prop);
374 bool_op = MATCHERBOOL_AND;
378 bool_op = MATCHERBOOL_OR;
387 criteria = MATCHCRITERIA_ALL;
388 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
394 criteria = MATCHCRITERIA_UNREAD;
395 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
401 criteria = MATCHCRITERIA_NOT_UNREAD;
402 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
408 criteria = MATCHCRITERIA_NEW;
409 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
415 criteria = MATCHCRITERIA_NOT_NEW;
416 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
422 criteria = MATCHCRITERIA_MARKED;
423 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
429 criteria = MATCHCRITERIA_NOT_MARKED;
430 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
436 criteria = MATCHCRITERIA_DELETED;
437 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
439 | MATCHER_NOT_DELETED
443 criteria = MATCHCRITERIA_NOT_DELETED;
444 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
450 criteria = MATCHCRITERIA_REPLIED;
451 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
453 | MATCHER_NOT_REPLIED
457 criteria = MATCHCRITERIA_NOT_REPLIED;
458 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
464 criteria = MATCHCRITERIA_FORWARDED;
465 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
467 | MATCHER_NOT_FORWARDED
471 criteria = MATCHCRITERIA_NOT_FORWARDED;
472 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
478 criteria = MATCHCRITERIA_LOCKED;
479 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
485 criteria = MATCHCRITERIA_NOT_LOCKED;
486 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
488 | MATCHER_COLORLABEL MATCHER_INTEGER
493 criteria = MATCHCRITERIA_COLORLABEL;
495 if (value < 1) value = 1;
496 else if (value > MAX_COLORLABELS) value = MAX_COLORLABELS;
497 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
499 | MATCHER_NOT_COLORLABEL MATCHER_INTEGER
504 criteria = MATCHCRITERIA_NOT_COLORLABEL;
506 if (value < 1) value = 1;
507 else if (value > MAX_COLORLABELS) value = MAX_COLORLABELS;
508 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
510 | MATCHER_IGNORE_THREAD
514 criteria = MATCHCRITERIA_IGNORE_THREAD;
515 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
517 | MATCHER_NOT_IGNORE_THREAD
521 criteria = MATCHCRITERIA_NOT_IGNORE_THREAD;
522 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
524 | MATCHER_SUBJECT match_type MATCHER_STRING
529 criteria = MATCHCRITERIA_SUBJECT;
531 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
533 | MATCHER_NOT_SUBJECT match_type MATCHER_STRING
538 criteria = MATCHCRITERIA_NOT_SUBJECT;
540 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
542 | MATCHER_FROM match_type MATCHER_STRING
547 criteria = MATCHCRITERIA_FROM;
549 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
551 | MATCHER_NOT_FROM match_type MATCHER_STRING
556 criteria = MATCHCRITERIA_NOT_FROM;
558 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
560 | MATCHER_TO match_type MATCHER_STRING
565 criteria = MATCHCRITERIA_TO;
567 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
569 | MATCHER_NOT_TO match_type MATCHER_STRING
574 criteria = MATCHCRITERIA_NOT_TO;
576 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
578 | MATCHER_CC match_type MATCHER_STRING
583 criteria = MATCHCRITERIA_CC;
585 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
587 | MATCHER_NOT_CC match_type MATCHER_STRING
592 criteria = MATCHCRITERIA_NOT_CC;
594 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
596 | MATCHER_TO_OR_CC match_type MATCHER_STRING
601 criteria = MATCHCRITERIA_TO_OR_CC;
603 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
605 | MATCHER_NOT_TO_AND_NOT_CC match_type MATCHER_STRING
610 criteria = MATCHCRITERIA_NOT_TO_AND_NOT_CC;
612 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
614 | MATCHER_AGE_GREATER MATCHER_INTEGER
619 criteria = MATCHCRITERIA_AGE_GREATER;
621 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
623 | MATCHER_AGE_LOWER MATCHER_INTEGER
628 criteria = MATCHCRITERIA_AGE_LOWER;
630 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
632 | MATCHER_NEWSGROUPS match_type MATCHER_STRING
637 criteria = MATCHCRITERIA_NEWSGROUPS;
639 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
641 | MATCHER_NOT_NEWSGROUPS match_type MATCHER_STRING
646 criteria = MATCHCRITERIA_NOT_NEWSGROUPS;
648 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
650 | MATCHER_INREPLYTO match_type MATCHER_STRING
655 criteria = MATCHCRITERIA_INREPLYTO;
657 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
659 | MATCHER_NOT_INREPLYTO match_type MATCHER_STRING
664 criteria = MATCHCRITERIA_NOT_INREPLYTO;
666 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
668 | MATCHER_REFERENCES match_type MATCHER_STRING
673 criteria = MATCHCRITERIA_REFERENCES;
675 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
677 | MATCHER_NOT_REFERENCES match_type MATCHER_STRING
682 criteria = MATCHCRITERIA_NOT_REFERENCES;
684 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
686 | MATCHER_SCORE_GREATER MATCHER_INTEGER
691 criteria = MATCHCRITERIA_SCORE_GREATER;
693 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
695 | MATCHER_SCORE_LOWER MATCHER_INTEGER
700 criteria = MATCHCRITERIA_SCORE_LOWER;
702 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
704 | MATCHER_SCORE_EQUAL MATCHER_INTEGER
709 criteria = MATCHCRITERIA_SCORE_EQUAL;
711 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
713 | MATCHER_SIZE_GREATER MATCHER_INTEGER
717 criteria = MATCHCRITERIA_SIZE_GREATER;
719 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
721 | MATCHER_SIZE_SMALLER MATCHER_INTEGER
725 criteria = MATCHCRITERIA_SIZE_SMALLER;
727 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
729 | MATCHER_SIZE_EQUAL MATCHER_INTEGER
733 criteria = MATCHCRITERIA_SIZE_EQUAL;
735 prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
737 | MATCHER_HEADER MATCHER_STRING
739 header = g_strdup($2);
740 } match_type MATCHER_STRING
745 criteria = MATCHCRITERIA_HEADER;
747 prop = matcherprop_unquote_new(criteria, header, match_type, expr, 0);
750 | MATCHER_NOT_HEADER MATCHER_STRING
752 header = g_strdup($2);
753 } match_type MATCHER_STRING
758 criteria = MATCHCRITERIA_NOT_HEADER;
760 prop = matcherprop_unquote_new(criteria, header, match_type, expr, 0);
763 | MATCHER_HEADERS_PART match_type MATCHER_STRING
768 criteria = MATCHCRITERIA_HEADERS_PART;
770 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
772 | MATCHER_NOT_HEADERS_PART match_type MATCHER_STRING
777 criteria = MATCHCRITERIA_NOT_HEADERS_PART;
779 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
781 | MATCHER_MESSAGE match_type MATCHER_STRING
786 criteria = MATCHCRITERIA_MESSAGE;
788 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
790 | MATCHER_NOT_MESSAGE match_type MATCHER_STRING
795 criteria = MATCHCRITERIA_NOT_MESSAGE;
797 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
799 | MATCHER_BODY_PART match_type MATCHER_STRING
804 criteria = MATCHCRITERIA_BODY_PART;
806 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
808 | MATCHER_NOT_BODY_PART match_type MATCHER_STRING
813 criteria = MATCHCRITERIA_NOT_BODY_PART;
815 prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
817 | MATCHER_EXECUTE MATCHER_STRING
822 criteria = MATCHCRITERIA_EXECUTE;
824 prop = matcherprop_unquote_new(criteria, NULL, 0, expr, 0);
826 | MATCHER_NOT_EXECUTE MATCHER_STRING
831 criteria = MATCHCRITERIA_NOT_EXECUTE;
833 prop = matcherprop_unquote_new(criteria, NULL, 0, expr, 0);
838 MATCHER_EXECUTE MATCHER_STRING
841 gint action_type = 0;
843 action_type = MATCHACTION_EXECUTE;
845 action = filteringaction_new(action_type, 0, cmd, 0);
847 | MATCHER_MOVE MATCHER_STRING
849 gchar *destination = NULL;
850 gint action_type = 0;
852 action_type = MATCHACTION_MOVE;
854 action = filteringaction_new(action_type, 0, destination, 0);
856 | MATCHER_COPY MATCHER_STRING
858 gchar *destination = NULL;
859 gint action_type = 0;
861 action_type = MATCHACTION_COPY;
863 action = filteringaction_new(action_type, 0, destination, 0);
867 gint action_type = 0;
869 action_type = MATCHACTION_DELETE;
870 action = filteringaction_new(action_type, 0, NULL, 0);
874 gint action_type = 0;
876 action_type = MATCHACTION_MARK;
877 action = filteringaction_new(action_type, 0, NULL, 0);
881 gint action_type = 0;
883 action_type = MATCHACTION_UNMARK;
884 action = filteringaction_new(action_type, 0, NULL, 0);
886 | MATCHER_MARK_AS_READ
888 gint action_type = 0;
890 action_type = MATCHACTION_MARK_AS_READ;
891 action = filteringaction_new(action_type, 0, NULL, 0);
893 | MATCHER_MARK_AS_UNREAD
895 gint action_type = 0;
897 action_type = MATCHACTION_MARK_AS_UNREAD;
898 action = filteringaction_new(action_type, 0, NULL, 0);
900 | MATCHER_FORWARD MATCHER_INTEGER MATCHER_STRING
902 gchar *destination = NULL;
903 gint action_type = 0;
906 action_type = MATCHACTION_FORWARD;
907 account_id = atoi($2);
909 action = filteringaction_new(action_type, account_id, destination, 0);
911 | MATCHER_FORWARD_AS_ATTACHMENT MATCHER_INTEGER MATCHER_STRING
913 gchar *destination = NULL;
914 gint action_type = 0;
917 action_type = MATCHACTION_FORWARD_AS_ATTACHMENT;
918 account_id = atoi($2);
920 action = filteringaction_new(action_type, account_id, destination, 0);
922 | MATCHER_REDIRECT MATCHER_INTEGER MATCHER_STRING
924 gchar *destination = NULL;
925 gint action_type = 0;
928 action_type = MATCHACTION_REDIRECT;
929 account_id = atoi($2);
931 action = filteringaction_new(action_type, account_id, destination, 0);
933 | MATCHER_COLOR MATCHER_INTEGER
935 gint action_type = 0;
938 action_type = MATCHACTION_COLOR;
940 action = filteringaction_new(action_type, 0, NULL, color);
942 | MATCHER_DELETE_ON_SERVER
944 gint action_type = 0;
945 action_type = MATCHACTION_DELETE_ON_SERVER;
946 action = filteringaction_new(action_type, 0, NULL, 0);
948 | MATCHER_CHANGE_SCORE MATCHER_STRING
950 gint action_type = MATCHACTION_CHANGE_SCORE;
951 char *last_tok = NULL;
952 const gchar *first_tok;
954 int change_type; /* -1 == decrement, 0 == assign, 1 == increment */
956 #define is_number(x) ( ((x) == '+') || ((x) == '-') || (isdigit((x))) )
958 for (first_tok = $2; *first_tok && !is_number(*first_tok); first_tok++)
962 chscore = strtol(first_tok, &last_tok, 10);
963 if (last_tok == first_tok || *last_tok == 0)
965 change_type = *first_tok == '+' ? 1 : *first_tok == '-' ? -1 : 0;
966 action = filteringaction_new(MATCHACTION_CHANGE_SCORE, change_type,
972 MATCHER_SCORE MATCHER_INTEGER