5a6839ecd1cd0b9da182ee0dadc65d96632081ab
[claws.git] / src / matcher_parser_parse.y
1 %{
2 /*
3  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
4  * Copyright (c) 2001-2007 by Hiroyuki Yamamoto & The Claws Mail Team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  * 
19  */
20
21 #include "defs.h"
22
23 #include <glib.h>
24 #include <glib/gi18n.h>
25
26 #include "utils.h"
27 #include "filtering.h"
28 #include "matcher.h"
29 #include "matcher_parser.h"
30 #include "matcher_parser_lex.h"
31 #include "colorlabel.h"
32
33 static gint error = 0;
34 static gint bool_op = 0;
35 static gint match_type = 0;
36 static gchar *header = NULL;
37
38 static MatcherProp *prop;
39
40 static GSList *matchers_list = NULL;
41
42 static gboolean enabled = TRUE;
43 static gchar *name = NULL;
44 static gint account_id = 0;
45 static MatcherList *cond;
46 static GSList *action_list = NULL;
47 static FilteringAction *action = NULL;
48 static gboolean matcher_is_fast = TRUE;
49
50 static FilteringProp *filtering;
51
52 static GSList **prefs_filtering = NULL;
53 static int enable_compatibility = 0;
54
55 enum {
56         MATCHER_PARSE_FILE,
57         MATCHER_PARSE_NO_EOL,
58         MATCHER_PARSE_ENABLED,
59         MATCHER_PARSE_NAME,
60         MATCHER_PARSE_ACCOUNT,
61         MATCHER_PARSE_CONDITION,
62         MATCHER_PARSE_FILTERING_ACTION,
63 };
64
65 static int matcher_parse_op = MATCHER_PARSE_FILE;
66
67
68 /* ******************************************************************** */
69 /* redeclarations to avoid warnings */
70 void matcher_parserrestart(FILE *input_file);
71 void matcher_parser_init(void);
72 void matcher_parser_switch_to_buffer(void * new_buffer);
73 void matcher_parser_delete_buffer(void * b);
74 void matcher_parserpop_buffer_state(void);
75 int matcher_parserlex(void);
76
77 void matcher_parser_start_parsing(FILE *f)
78 {
79         matcher_parserlineno = 1;
80         matcher_parserrestart(f);
81         account_id = 0;
82         matcher_parserparse();
83 }
84
85  
86 void * matcher_parser_scan_string(const char * str);
87  
88 FilteringProp *matcher_parser_get_filtering(gchar *str)
89 {
90         void *bufstate;
91         void *tmp_str = NULL;
92         
93         /* little hack to allow passing rules with no names */
94         if (!strncmp(str, "rulename ", 9))
95                 tmp_str = g_strdup(str);
96         else 
97                 tmp_str = g_strconcat("rulename \"\" ", str, NULL);
98
99         /* bad coding to enable the sub-grammar matching
100            in yacc */
101         matcher_parserlineno = 1;
102         matcher_parse_op = MATCHER_PARSE_NO_EOL;
103         matcher_parserrestart(NULL);
104         matcher_parserpop_buffer_state();
105         matcher_parser_init();
106         bufstate = matcher_parser_scan_string((const char *) tmp_str);
107         matcher_parser_switch_to_buffer(bufstate);
108         if (matcher_parserparse() != 0)
109                 filtering = NULL;
110         matcher_parse_op = MATCHER_PARSE_FILE;
111         matcher_parser_delete_buffer(bufstate);
112         g_free(tmp_str);
113         return filtering;
114 }
115
116 static gboolean check_quote_symetry(gchar *str)
117 {
118         const gchar *walk;
119         int ret = 0;
120         
121         if (str == NULL)
122                 return TRUE; /* heh, that's symetric */
123         if (*str == '\0')
124                 return TRUE;
125         for (walk = str; *walk; walk++) {
126                 if (*walk == '\"') {
127                         if (walk == str         /* first char */
128                         || *(walk - 1) != '\\') /* not escaped */
129                                 ret ++;
130                 }
131         }
132         return !(ret % 2);
133 }
134
135 MatcherList *matcher_parser_get_name(gchar *str)
136 {
137         void *bufstate;
138
139         if (!check_quote_symetry(str)) {
140                 cond = NULL;
141                 return cond;
142         }
143         
144         /* bad coding to enable the sub-grammar matching
145            in yacc */
146         matcher_parserlineno = 1;
147         matcher_parse_op = MATCHER_PARSE_NAME;
148         matcher_parserrestart(NULL);
149         matcher_parserpop_buffer_state();
150         matcher_parser_init();
151         bufstate = matcher_parser_scan_string(str);
152         matcher_parserparse();
153         matcher_parse_op = MATCHER_PARSE_FILE;
154         matcher_parser_delete_buffer(bufstate);
155         return cond;
156 }
157
158 MatcherList *matcher_parser_get_enabled(gchar *str)
159 {
160         void *bufstate;
161
162         if (!check_quote_symetry(str)) {
163                 cond = NULL;
164                 return cond;
165         }
166         
167         /* bad coding to enable the sub-grammar matching
168            in yacc */
169         matcher_parserlineno = 1;
170         matcher_parse_op = MATCHER_PARSE_ENABLED;
171         matcher_parserrestart(NULL);
172         matcher_parserpop_buffer_state();
173         matcher_parser_init();
174         bufstate = matcher_parser_scan_string(str);
175         matcher_parserparse();
176         matcher_parse_op = MATCHER_PARSE_FILE;
177         matcher_parser_delete_buffer(bufstate);
178         return cond;
179 }
180
181 MatcherList *matcher_parser_get_account(gchar *str)
182 {
183         void *bufstate;
184
185         if (!check_quote_symetry(str)) {
186                 cond = NULL;
187                 return cond;
188         }
189         
190         /* bad coding to enable the sub-grammar matching
191            in yacc */
192         matcher_parserlineno = 1;
193         matcher_parse_op = MATCHER_PARSE_ACCOUNT;
194         matcher_parserrestart(NULL);
195         matcher_parserpop_buffer_state();
196         matcher_parser_init();
197         bufstate = matcher_parser_scan_string(str);
198         matcher_parserparse();
199         matcher_parse_op = MATCHER_PARSE_FILE;
200         matcher_parser_delete_buffer(bufstate);
201         return cond;
202 }
203
204 MatcherList *matcher_parser_get_cond(gchar *str, gboolean *is_fast)
205 {
206         void *bufstate;
207
208         if (!check_quote_symetry(str)) {
209                 cond = NULL;
210                 return cond;
211         }
212         
213         matcher_is_fast = TRUE;
214         /* bad coding to enable the sub-grammar matching
215            in yacc */
216         matcher_parserlineno = 1;
217         matcher_parse_op = MATCHER_PARSE_CONDITION;
218         matcher_parserrestart(NULL);
219         matcher_parserpop_buffer_state();
220         matcher_parser_init();
221         bufstate = matcher_parser_scan_string(str);
222         matcher_parserparse();
223         matcher_parse_op = MATCHER_PARSE_FILE;
224         matcher_parser_delete_buffer(bufstate);
225         if (is_fast)
226                 *is_fast = matcher_is_fast;
227         return cond;
228 }
229
230 GSList *matcher_parser_get_action_list(gchar *str)
231 {
232         void *bufstate;
233
234         if (!check_quote_symetry(str)) {
235                 action_list = NULL;
236                 return action_list;
237         }
238         
239         /* bad coding to enable the sub-grammar matching
240            in yacc */
241         matcher_parserlineno = 1;
242         matcher_parse_op = MATCHER_PARSE_FILTERING_ACTION;
243         matcher_parserrestart(NULL);
244         matcher_parserpop_buffer_state();
245         matcher_parser_init();
246         bufstate = matcher_parser_scan_string(str);
247         matcher_parserparse();
248         matcher_parse_op = MATCHER_PARSE_FILE;
249         matcher_parser_delete_buffer(bufstate);
250         return action_list;
251 }
252
253 MatcherProp *matcher_parser_get_prop(gchar *str)
254 {
255         MatcherList *list;
256         MatcherProp *prop;
257
258         matcher_parserlineno = 1;
259         list = matcher_parser_get_cond(str, NULL);
260         if (list == NULL)
261                 return NULL;
262
263         if (list->matchers == NULL)
264                 return NULL;
265
266         if (list->matchers->next != NULL)
267                 return NULL;
268
269         prop = list->matchers->data;
270
271         g_slist_free(list->matchers);
272         g_free(list);
273
274         return prop;
275 }
276
277 void matcher_parsererror(char *str)
278 {
279         GSList *l;
280
281         if (matchers_list) {
282                 for (l = matchers_list; l != NULL; l = g_slist_next(l)) {
283                         matcherprop_free((MatcherProp *)
284                                          l->data);
285                         l->data = NULL;
286                 }
287                 g_slist_free(matchers_list);
288                 matchers_list = NULL;
289         }
290         cond = NULL;
291         g_warning("filtering parsing: %i: %s\n",
292                   matcher_parserlineno, str);
293         error = 1;
294 }
295
296 int matcher_parserwrap(void)
297 {
298         return 1;
299 }
300 %}
301
302 %union {
303         char *str;
304         int value;
305 }
306 %token MATCHER_ALL MATCHER_UNREAD  MATCHER_NOT_UNREAD 
307 %token MATCHER_NEW  MATCHER_NOT_NEW  MATCHER_MARKED
308 %token MATCHER_NOT_MARKED  MATCHER_DELETED  MATCHER_NOT_DELETED
309 %token MATCHER_REPLIED  MATCHER_NOT_REPLIED  MATCHER_FORWARDED
310 %token MATCHER_NOT_FORWARDED  MATCHER_SUBJECT  MATCHER_NOT_SUBJECT
311 %token MATCHER_FROM  MATCHER_NOT_FROM  MATCHER_TO  MATCHER_NOT_TO
312 %token MATCHER_CC  MATCHER_NOT_CC  MATCHER_TO_OR_CC  MATCHER_NOT_TO_AND_NOT_CC
313 %token MATCHER_AGE_GREATER  MATCHER_AGE_LOWER  MATCHER_NEWSGROUPS
314 %token MATCHER_NOT_NEWSGROUPS  MATCHER_INREPLYTO  MATCHER_NOT_INREPLYTO
315 %token MATCHER_REFERENCES  MATCHER_NOT_REFERENCES  MATCHER_SCORE_GREATER
316 %token MATCHER_SCORE_LOWER  MATCHER_HEADER  MATCHER_NOT_HEADER
317 %token MATCHER_HEADERS_PART  MATCHER_NOT_HEADERS_PART  MATCHER_MESSAGE
318 %token MATCHER_NOT_MESSAGE  MATCHER_BODY_PART  MATCHER_NOT_BODY_PART
319 %token MATCHER_TEST  MATCHER_NOT_TEST  MATCHER_MATCHCASE  MATCHER_MATCH
320 %token MATCHER_REGEXPCASE  MATCHER_REGEXP  MATCHER_SCORE  MATCHER_MOVE
321 %token MATCHER_FOUND_IN_ADDRESSBOOK MATCHER_NOT_FOUND_IN_ADDRESSBOOK MATCHER_IN
322 %token MATCHER_COPY  MATCHER_DELETE  MATCHER_MARK  MATCHER_UNMARK
323 %token MATCHER_LOCK MATCHER_UNLOCK
324 %token MATCHER_EXECUTE
325 %token MATCHER_MARK_AS_READ  MATCHER_MARK_AS_UNREAD  MATCHER_FORWARD
326 %token MATCHER_MARK_AS_SPAM MATCHER_MARK_AS_HAM
327 %token MATCHER_FORWARD_AS_ATTACHMENT  MATCHER_EOL
328 %token MATCHER_OR MATCHER_AND  
329 %token MATCHER_COLOR MATCHER_SCORE_EQUAL MATCHER_REDIRECT 
330 %token MATCHER_SIZE_GREATER MATCHER_SIZE_SMALLER MATCHER_SIZE_EQUAL
331 %token MATCHER_LOCKED MATCHER_NOT_LOCKED
332 %token MATCHER_PARTIAL MATCHER_NOT_PARTIAL
333 %token MATCHER_COLORLABEL MATCHER_NOT_COLORLABEL
334 %token MATCHER_IGNORE_THREAD MATCHER_NOT_IGNORE_THREAD
335 %token MATCHER_CHANGE_SCORE MATCHER_SET_SCORE
336 %token MATCHER_ADD_TO_ADDRESSBOOK
337 %token MATCHER_STOP MATCHER_HIDE MATCHER_IGNORE
338 %token MATCHER_SPAM MATCHER_NOT_SPAM
339 %token MATCHER_TAG MATCHER_NOT_TAG MATCHER_SET_TAG MATCHER_UNSET_TAG
340 %token MATCHER_TAGGED MATCHER_NOT_TAGGED MATCHER_CLEAR_TAGS
341
342 %start file
343
344 %token MATCHER_ENABLED MATCHER_DISABLED
345 %token MATCHER_RULENAME
346 %token MATCHER_ACCOUNT
347 %token <str> MATCHER_STRING
348 %token <str> MATCHER_SECTION
349 %token <str> MATCHER_INTEGER
350
351 %%
352
353 file:
354 {
355         if (matcher_parse_op == MATCHER_PARSE_FILE) {
356                 prefs_filtering = &pre_global_processing;
357         }
358 }
359 file_line_list;
360
361 file_line_list:
362 file_line
363 file_line_list
364 | file_line
365 ;
366
367 file_line:
368 section_notification
369
370 { action_list = NULL; }
371 instruction
372 | error MATCHER_EOL
373 {
374         yyerrok;
375 };
376
377 section_notification:
378 MATCHER_SECTION MATCHER_EOL
379 {
380         gchar *folder = $1;
381         FolderItem *item = NULL;
382
383         if (matcher_parse_op == MATCHER_PARSE_FILE) {
384                 enable_compatibility = 0;
385                 if (!strcmp(folder, "global")) {
386                         /* backward compatibility */
387                         enable_compatibility = 1;
388                 }
389                 else if (!strcmp(folder, "preglobal")) {
390                         prefs_filtering = &pre_global_processing;
391                 }
392                 else if (!strcmp(folder, "postglobal")) {
393                         prefs_filtering = &post_global_processing;
394                 }
395                 else if (!strcmp(folder, "filtering")) {
396                         prefs_filtering = &filtering_rules;
397                 }
398                 else {
399                         item = folder_find_item_from_identifier(folder);
400                         if (item != NULL) {
401                                 prefs_filtering = &item->prefs->processing;
402                         } else {
403                                 prefs_filtering = NULL;
404                         }
405                 }
406         }
407 }
408 ;
409
410 instruction:
411 enabled name account condition filtering MATCHER_EOL
412 | enabled name account condition filtering
413 | enabled name condition filtering MATCHER_EOL
414 | enabled name condition filtering
415 | name condition filtering MATCHER_EOL
416 | name condition filtering
417 {
418         if (matcher_parse_op == MATCHER_PARSE_NO_EOL)
419                 YYACCEPT;
420         else {
421                 matcher_parsererror("parse error [no eol]");
422                 YYERROR;
423         }
424 }
425 | enabled
426 {
427         if (matcher_parse_op == MATCHER_PARSE_ENABLED)
428                 YYACCEPT;
429         else {
430                 matcher_parsererror("parse error [enabled]");
431                 YYERROR;
432         }
433 }
434 | account
435 {
436         if (matcher_parse_op == MATCHER_PARSE_ACCOUNT)
437                 YYACCEPT;
438         else {
439                 matcher_parsererror("parse error [account]");
440                 YYERROR;
441         }
442 }
443 | name
444 {
445         if (matcher_parse_op == MATCHER_PARSE_NAME)
446                 YYACCEPT;
447         else {
448                 matcher_parsererror("parse error [name]");
449                 YYERROR;
450         }
451 }
452 | condition
453 {
454         if (matcher_parse_op == MATCHER_PARSE_CONDITION)
455                 YYACCEPT;
456         else {
457                 matcher_parsererror("parse error [condition]");
458                 YYERROR;
459         }
460 }
461 | filtering_action_list
462 {
463         if (matcher_parse_op == MATCHER_PARSE_FILTERING_ACTION)
464                 YYACCEPT;
465         else {
466                 matcher_parsererror("parse error [filtering action]");
467                 YYERROR;
468         }
469 }
470 | MATCHER_EOL
471 ;
472
473 enabled:
474 MATCHER_ENABLED
475 {
476         enabled = TRUE;
477 }
478 | MATCHER_DISABLED
479 {
480         enabled = FALSE;
481 }
482 ;
483
484 name:
485 MATCHER_RULENAME MATCHER_STRING
486 {
487         name = g_strdup($2);
488 }
489 ;
490
491 account:
492 MATCHER_ACCOUNT MATCHER_INTEGER
493 {
494         account_id = strtol($2, NULL, 10);
495 }
496 ;
497
498 filtering:
499 filtering_action_list
500 {
501         filtering = filteringprop_new(enabled, name, account_id, cond, action_list);
502         enabled = TRUE;
503         account_id = 0;
504         g_free(name);
505         name = NULL;
506         if (enable_compatibility) {
507                 prefs_filtering = &filtering_rules;
508                 if (action_list != NULL) {
509                         FilteringAction * first_action;
510                         
511                         first_action = action_list->data;
512                         
513                         if (first_action->type == MATCHACTION_CHANGE_SCORE)
514                                 prefs_filtering = &pre_global_processing;
515                 }
516         }
517         
518         cond = NULL;
519         action_list = NULL;
520         
521         if ((matcher_parse_op == MATCHER_PARSE_FILE) &&
522             (prefs_filtering != NULL)) {
523                 *prefs_filtering = g_slist_append(*prefs_filtering,
524                                                   filtering);
525                 filtering = NULL;
526         }
527 }
528 ;
529
530 filtering_action_list:
531 filtering_action_b filtering_action_list
532 | filtering_action_b
533 ;
534
535 filtering_action_b:
536 filtering_action
537 {
538         action_list = g_slist_append(action_list, action);
539         action = NULL;
540 }
541 ;
542
543 match_type:
544 MATCHER_MATCHCASE
545 {
546         match_type = MATCHTYPE_MATCHCASE;
547 }
548 | MATCHER_MATCH
549 {
550         match_type = MATCHTYPE_MATCH;
551 }
552 | MATCHER_REGEXPCASE
553 {
554         match_type = MATCHTYPE_REGEXPCASE;
555 }
556 | MATCHER_REGEXP
557 {
558         match_type = MATCHTYPE_REGEXP;
559 }
560 ;
561
562 condition:
563 condition_list
564 {
565         cond = matcherlist_new(matchers_list, (bool_op == MATCHERBOOL_AND));
566         matchers_list = NULL;
567 }
568 ;
569
570 condition_list:
571 condition_list bool_op one_condition
572 {
573         matchers_list = g_slist_append(matchers_list, prop);
574 }
575 | one_condition
576 {
577         matchers_list = NULL;
578         matchers_list = g_slist_append(matchers_list, prop);
579 }
580 ;
581
582 bool_op:
583 MATCHER_AND
584 {
585         bool_op = MATCHERBOOL_AND;
586 }
587 | MATCHER_OR
588 {
589         bool_op = MATCHERBOOL_OR;
590 }
591 ;
592
593 one_condition:
594 MATCHER_ALL
595 {
596         gint criteria = 0;
597
598         criteria = MATCHCRITERIA_ALL;
599         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
600 }
601 | MATCHER_UNREAD
602 {
603         gint criteria = 0;
604
605         criteria = MATCHCRITERIA_UNREAD;
606         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
607 }
608 | MATCHER_NOT_UNREAD 
609 {
610         gint criteria = 0;
611
612         criteria = MATCHCRITERIA_NOT_UNREAD;
613         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
614 }
615 | MATCHER_NEW
616 {
617         gint criteria = 0;
618
619         criteria = MATCHCRITERIA_NEW;
620         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
621 }
622 | MATCHER_NOT_NEW
623 {
624         gint criteria = 0;
625
626         criteria = MATCHCRITERIA_NOT_NEW;
627         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
628 }
629 | MATCHER_MARKED
630 {
631         gint criteria = 0;
632
633         criteria = MATCHCRITERIA_MARKED;
634         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
635 }
636 | MATCHER_NOT_MARKED
637 {
638         gint criteria = 0;
639
640         criteria = MATCHCRITERIA_NOT_MARKED;
641         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
642 }
643 | MATCHER_DELETED
644 {
645         gint criteria = 0;
646
647         criteria = MATCHCRITERIA_DELETED;
648         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
649 }
650 | MATCHER_NOT_DELETED
651 {
652         gint criteria = 0;
653
654         criteria = MATCHCRITERIA_NOT_DELETED;
655         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
656 }
657 | MATCHER_REPLIED
658 {
659         gint criteria = 0;
660
661         criteria = MATCHCRITERIA_REPLIED;
662         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
663 }
664 | MATCHER_NOT_REPLIED
665 {
666         gint criteria = 0;
667
668         criteria = MATCHCRITERIA_NOT_REPLIED;
669         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
670 }
671 | MATCHER_FORWARDED
672 {
673         gint criteria = 0;
674
675         criteria = MATCHCRITERIA_FORWARDED;
676         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
677 }
678 | MATCHER_NOT_FORWARDED
679 {
680         gint criteria = 0;
681
682         criteria = MATCHCRITERIA_NOT_FORWARDED;
683         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
684 }
685 | MATCHER_LOCKED
686 {
687         gint criteria = 0;
688
689         criteria = MATCHCRITERIA_LOCKED;
690         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
691 }
692 | MATCHER_NOT_LOCKED
693 {
694         gint criteria = 0;
695
696         criteria = MATCHCRITERIA_NOT_LOCKED;
697         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
698 }
699 | MATCHER_SPAM
700 {
701         gint criteria = 0;
702
703         criteria = MATCHCRITERIA_SPAM;
704         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
705 }
706 | MATCHER_NOT_SPAM 
707 {
708         gint criteria = 0;
709
710         criteria = MATCHCRITERIA_NOT_SPAM;
711         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
712 }
713 | MATCHER_PARTIAL
714 {
715         gint criteria = 0;
716
717         criteria = MATCHCRITERIA_PARTIAL;
718         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
719 }
720 | MATCHER_NOT_PARTIAL
721 {
722         gint criteria = 0;
723
724         criteria = MATCHCRITERIA_NOT_PARTIAL;
725         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
726 }
727 | MATCHER_COLORLABEL MATCHER_INTEGER
728 {
729         gint criteria = 0;
730         gint value = 0;
731
732         criteria = MATCHCRITERIA_COLORLABEL;
733         value = strtol($2, NULL, 10);
734         if (value < 0) value = 0;
735         else if (value > COLORLABELS) value = COLORLABELS;
736         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
737 }
738 | MATCHER_NOT_COLORLABEL MATCHER_INTEGER
739 {
740         gint criteria = 0;
741         gint value = 0;
742
743         criteria = MATCHCRITERIA_NOT_COLORLABEL;
744         value = strtol($2, NULL, 0);
745         if (value < 0) value = 0;
746         else if (value > COLORLABELS) value = COLORLABELS;
747         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
748 }
749 | MATCHER_IGNORE_THREAD
750 {
751         gint criteria = 0;
752
753         criteria = MATCHCRITERIA_IGNORE_THREAD;
754         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
755 }
756 | MATCHER_NOT_IGNORE_THREAD
757 {
758         gint criteria = 0;
759
760         criteria = MATCHCRITERIA_NOT_IGNORE_THREAD;
761         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
762 }
763 | MATCHER_SUBJECT match_type MATCHER_STRING
764 {
765         gint criteria = 0;
766         gchar *expr = NULL;
767
768         criteria = MATCHCRITERIA_SUBJECT;
769         expr = $3;
770         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
771 }
772 | MATCHER_NOT_SUBJECT match_type MATCHER_STRING
773 {
774         gint criteria = 0;
775         gchar *expr = NULL;
776
777         criteria = MATCHCRITERIA_NOT_SUBJECT;
778         expr = $3;
779         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
780 }
781 | MATCHER_FROM match_type MATCHER_STRING
782 {
783         gint criteria = 0;
784         gchar *expr = NULL;
785
786         criteria = MATCHCRITERIA_FROM;
787         expr = $3;
788         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
789 }
790 | MATCHER_NOT_FROM match_type MATCHER_STRING
791 {
792         gint criteria = 0;
793         gchar *expr = NULL;
794
795         criteria = MATCHCRITERIA_NOT_FROM;
796         expr = $3;
797         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
798 }
799 | MATCHER_TO match_type MATCHER_STRING
800 {
801         gint criteria = 0;
802         gchar *expr = NULL;
803
804         criteria = MATCHCRITERIA_TO;
805         expr = $3;
806         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
807 }
808 | MATCHER_NOT_TO match_type MATCHER_STRING
809 {
810         gint criteria = 0;
811         gchar *expr = NULL;
812
813         criteria = MATCHCRITERIA_NOT_TO;
814         expr = $3;
815         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
816 }
817 | MATCHER_CC match_type MATCHER_STRING
818 {
819         gint criteria = 0;
820         gchar *expr = NULL;
821
822         criteria = MATCHCRITERIA_CC;
823         expr = $3;
824         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
825 }
826 | MATCHER_NOT_CC match_type MATCHER_STRING
827 {
828         gint criteria = 0;
829         gchar *expr = NULL;
830
831         criteria = MATCHCRITERIA_NOT_CC;
832         expr = $3;
833         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
834 }
835 | MATCHER_TO_OR_CC match_type MATCHER_STRING
836 {
837         gint criteria = 0;
838         gchar *expr = NULL;
839
840         criteria = MATCHCRITERIA_TO_OR_CC;
841         expr = $3;
842         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
843 }
844 | MATCHER_NOT_TO_AND_NOT_CC match_type MATCHER_STRING
845 {
846         gint criteria = 0;
847         gchar *expr = NULL;
848
849         criteria = MATCHCRITERIA_NOT_TO_AND_NOT_CC;
850         expr = $3;
851         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
852 }
853 | MATCHER_TAG match_type MATCHER_STRING
854 {
855         gint criteria = 0;
856         gchar *expr = NULL;
857
858         criteria = MATCHCRITERIA_TAG;
859         expr = $3;
860         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
861 }
862 | MATCHER_NOT_TAG match_type MATCHER_STRING
863 {
864         gint criteria = 0;
865         gchar *expr = NULL;
866
867         criteria = MATCHCRITERIA_NOT_TAG;
868         expr = $3;
869         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
870 }
871 | MATCHER_TAGGED
872 {
873         gint criteria = 0;
874
875         criteria = MATCHCRITERIA_TAGGED;
876         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
877 }
878 | MATCHER_NOT_TAGGED
879 {
880         gint criteria = 0;
881
882         criteria = MATCHCRITERIA_NOT_TAGGED;
883         prop = matcherprop_new(criteria, NULL, 0, NULL, 0);
884 }
885 | MATCHER_AGE_GREATER MATCHER_INTEGER
886 {
887         gint criteria = 0;
888         gint value = 0;
889
890         criteria = MATCHCRITERIA_AGE_GREATER;
891         value = strtol($2, NULL, 0);
892         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
893 }
894 | MATCHER_AGE_LOWER MATCHER_INTEGER
895 {
896         gint criteria = 0;
897         gint value = 0;
898
899         criteria = MATCHCRITERIA_AGE_LOWER;
900         value = strtol($2, NULL, 0);
901         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
902 }
903 | MATCHER_NEWSGROUPS match_type MATCHER_STRING
904 {
905         gint criteria = 0;
906         gchar *expr = NULL;
907
908         criteria = MATCHCRITERIA_NEWSGROUPS;
909         expr = $3;
910         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
911 }
912 | MATCHER_NOT_NEWSGROUPS match_type MATCHER_STRING
913 {
914         gint criteria = 0;
915         gchar *expr = NULL;
916
917         criteria = MATCHCRITERIA_NOT_NEWSGROUPS;
918         expr = $3;
919         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
920 }
921 | MATCHER_INREPLYTO match_type MATCHER_STRING
922 {
923         gint criteria = 0;
924         gchar *expr = NULL;
925
926         criteria = MATCHCRITERIA_INREPLYTO;
927         expr = $3;
928         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
929 }
930 | MATCHER_NOT_INREPLYTO match_type MATCHER_STRING
931 {
932         gint criteria = 0;
933         gchar *expr = NULL;
934
935         criteria = MATCHCRITERIA_NOT_INREPLYTO;
936         expr = $3;
937         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
938 }
939 | MATCHER_REFERENCES match_type MATCHER_STRING
940 {
941         gint criteria = 0;
942         gchar *expr = NULL;
943
944         criteria = MATCHCRITERIA_REFERENCES;
945         expr = $3;
946         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
947 }
948 | MATCHER_NOT_REFERENCES match_type MATCHER_STRING
949 {
950         gint criteria = 0;
951         gchar *expr = NULL;
952
953         criteria = MATCHCRITERIA_NOT_REFERENCES;
954         expr = $3;
955         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
956 }
957 | MATCHER_SCORE_GREATER MATCHER_INTEGER
958 {
959         gint criteria = 0;
960         gint value = 0;
961
962         criteria = MATCHCRITERIA_SCORE_GREATER;
963         value = strtol($2, NULL, 0);
964         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
965 }
966 | MATCHER_SCORE_LOWER MATCHER_INTEGER
967 {
968         gint criteria = 0;
969         gint value = 0;
970
971         criteria = MATCHCRITERIA_SCORE_LOWER;
972         value = strtol($2, NULL, 0);
973         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
974 }
975 | MATCHER_SCORE_EQUAL MATCHER_INTEGER
976 {
977         gint criteria = 0;
978         gint value = 0;
979
980         criteria = MATCHCRITERIA_SCORE_EQUAL;
981         value = strtol($2, NULL, 0);
982         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
983 }
984 | MATCHER_SIZE_GREATER MATCHER_INTEGER 
985 {
986         gint criteria = 0;
987         gint value    = 0;
988         criteria = MATCHCRITERIA_SIZE_GREATER;
989         value = strtol($2, NULL, 0);
990         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
991 }
992 | MATCHER_SIZE_SMALLER MATCHER_INTEGER
993 {
994         gint criteria = 0;
995         gint value    = 0;
996         criteria = MATCHCRITERIA_SIZE_SMALLER;
997         value = strtol($2, NULL, 0);
998         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
999 }
1000 | MATCHER_SIZE_EQUAL MATCHER_INTEGER
1001 {
1002         gint criteria = 0;
1003         gint value    = 0;
1004         criteria = MATCHCRITERIA_SIZE_EQUAL;
1005         value = strtol($2, NULL, 0);
1006         prop = matcherprop_new(criteria, NULL, 0, NULL, value);
1007 }
1008 | MATCHER_HEADER MATCHER_STRING
1009 {
1010         header = g_strdup($2);
1011 } match_type MATCHER_STRING
1012 {
1013         gint criteria = 0;
1014         gchar *expr = NULL;
1015         matcher_is_fast = FALSE;
1016         criteria = MATCHCRITERIA_HEADER;
1017         expr = $2;
1018         prop = matcherprop_new(criteria, header, match_type, expr, 0);
1019         g_free(header);
1020 }
1021 | MATCHER_NOT_HEADER MATCHER_STRING
1022 {
1023         header = g_strdup($2);
1024 } match_type MATCHER_STRING
1025 {
1026         gint criteria = 0;
1027         gchar *expr = NULL;
1028         matcher_is_fast = FALSE;
1029         criteria = MATCHCRITERIA_NOT_HEADER;
1030         expr = $2;
1031         prop = matcherprop_new(criteria, header, match_type, expr, 0);
1032         g_free(header);
1033 }
1034 | MATCHER_HEADERS_PART match_type MATCHER_STRING
1035 {
1036         gint criteria = 0;
1037         gchar *expr = NULL;
1038         matcher_is_fast = FALSE;
1039         criteria = MATCHCRITERIA_HEADERS_PART;
1040         expr = $3;
1041         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
1042 }
1043 | MATCHER_NOT_HEADERS_PART match_type MATCHER_STRING
1044 {
1045         gint criteria = 0;
1046         gchar *expr = NULL;
1047         matcher_is_fast = FALSE;
1048         criteria = MATCHCRITERIA_NOT_HEADERS_PART;
1049         expr = $3;
1050         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
1051 }
1052 | MATCHER_FOUND_IN_ADDRESSBOOK MATCHER_STRING
1053 {
1054         header = g_strdup($2);
1055 } MATCHER_IN MATCHER_STRING
1056 {
1057         gint criteria = 0;
1058         gchar *expr = NULL;
1059
1060         criteria = MATCHCRITERIA_FOUND_IN_ADDRESSBOOK;
1061         expr = $2;
1062         prop = matcherprop_new(criteria, header, match_type, expr, 0);
1063         g_free(header);
1064 }
1065 | MATCHER_NOT_FOUND_IN_ADDRESSBOOK MATCHER_STRING
1066 {
1067         header = g_strdup($2);
1068 } MATCHER_IN MATCHER_STRING
1069 {
1070         gint criteria = 0;
1071         gchar *expr = NULL;
1072
1073         criteria = MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK;
1074         expr = $2;
1075         prop = matcherprop_new(criteria, header, match_type, expr, 0);
1076         g_free(header);
1077 }
1078 | MATCHER_MESSAGE match_type MATCHER_STRING
1079 {
1080         gint criteria = 0;
1081         gchar *expr = NULL;
1082         matcher_is_fast = FALSE;
1083         criteria = MATCHCRITERIA_MESSAGE;
1084         expr = $3;
1085         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
1086 }
1087 | MATCHER_NOT_MESSAGE match_type MATCHER_STRING
1088 {
1089         gint criteria = 0;
1090         gchar *expr = NULL;
1091         matcher_is_fast = FALSE;
1092         criteria = MATCHCRITERIA_NOT_MESSAGE;
1093         expr = $3;
1094         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
1095 }
1096 | MATCHER_BODY_PART match_type MATCHER_STRING
1097 {
1098         gint criteria = 0;
1099         gchar *expr = NULL;
1100         matcher_is_fast = FALSE;
1101         criteria = MATCHCRITERIA_BODY_PART;
1102         expr = $3;
1103         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
1104 }
1105 | MATCHER_NOT_BODY_PART match_type MATCHER_STRING
1106 {
1107         gint criteria = 0;
1108         gchar *expr = NULL;
1109         matcher_is_fast = FALSE;
1110         criteria = MATCHCRITERIA_NOT_BODY_PART;
1111         expr = $3;
1112         prop = matcherprop_new(criteria, NULL, match_type, expr, 0);
1113 }
1114 | MATCHER_TEST MATCHER_STRING
1115 {
1116         gint criteria = 0;
1117         gchar *expr = NULL;
1118         matcher_is_fast = FALSE;
1119         criteria = MATCHCRITERIA_TEST;
1120         expr = $2;
1121         prop = matcherprop_new(criteria, NULL, 0, expr, 0);
1122 }
1123 | MATCHER_NOT_TEST MATCHER_STRING
1124 {
1125         gint criteria = 0;
1126         gchar *expr = NULL;
1127         matcher_is_fast = FALSE;
1128         criteria = MATCHCRITERIA_NOT_TEST;
1129         expr = $2;
1130         prop = matcherprop_new(criteria, NULL, 0, expr, 0);
1131 }
1132 ;
1133
1134 filtering_action:
1135 MATCHER_EXECUTE MATCHER_STRING
1136 {
1137         gchar *cmd = NULL;
1138         gint action_type = 0;
1139
1140         action_type = MATCHACTION_EXECUTE;
1141         cmd = $2;
1142         action = filteringaction_new(action_type, 0, cmd, 0, 0, NULL);
1143 }
1144 | MATCHER_MOVE MATCHER_STRING
1145 {
1146         gchar *destination = NULL;
1147         gint action_type = 0;
1148
1149         action_type = MATCHACTION_MOVE;
1150         destination = $2;
1151         action = filteringaction_new(action_type, 0, destination, 0, 0, NULL);
1152 }
1153 | MATCHER_SET_TAG MATCHER_STRING
1154 {
1155         gchar *destination = NULL;
1156         gint action_type = 0;
1157
1158         action_type = MATCHACTION_SET_TAG;
1159         destination = $2;
1160         action = filteringaction_new(action_type, 0, destination, 0, 0, NULL);
1161 }
1162 | MATCHER_UNSET_TAG MATCHER_STRING
1163 {
1164         gchar *destination = NULL;
1165         gint action_type = 0;
1166
1167         action_type = MATCHACTION_UNSET_TAG;
1168         destination = $2;
1169         action = filteringaction_new(action_type, 0, destination, 0, 0, NULL);
1170 }
1171 | MATCHER_CLEAR_TAGS
1172 {
1173         gint action_type = 0;
1174
1175         action_type = MATCHACTION_CLEAR_TAGS;
1176         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1177 }
1178 | MATCHER_COPY MATCHER_STRING
1179 {
1180         gchar *destination = NULL;
1181         gint action_type = 0;
1182
1183         action_type = MATCHACTION_COPY;
1184         destination = $2;
1185         action = filteringaction_new(action_type, 0, destination, 0, 0, NULL);
1186 }
1187 | MATCHER_DELETE
1188 {
1189         gint action_type = 0;
1190
1191         action_type = MATCHACTION_DELETE;
1192         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1193 }
1194 | MATCHER_MARK
1195 {
1196         gint action_type = 0;
1197
1198         action_type = MATCHACTION_MARK;
1199         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1200 }
1201 | MATCHER_UNMARK
1202 {
1203         gint action_type = 0;
1204
1205         action_type = MATCHACTION_UNMARK;
1206         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1207 }
1208 | MATCHER_LOCK
1209 {
1210         gint action_type = 0;
1211
1212         action_type = MATCHACTION_LOCK;
1213         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1214 }
1215 | MATCHER_UNLOCK
1216 {
1217         gint action_type = 0;
1218
1219         action_type = MATCHACTION_UNLOCK;
1220         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1221 }
1222 | MATCHER_MARK_AS_READ
1223 {
1224         gint action_type = 0;
1225
1226         action_type = MATCHACTION_MARK_AS_READ;
1227         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1228 }
1229 | MATCHER_MARK_AS_UNREAD
1230 {
1231         gint action_type = 0;
1232
1233         action_type = MATCHACTION_MARK_AS_UNREAD;
1234         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1235 }
1236 | MATCHER_MARK_AS_SPAM
1237 {
1238         gint action_type = 0;
1239
1240         action_type = MATCHACTION_MARK_AS_SPAM;
1241         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1242 }
1243 | MATCHER_MARK_AS_HAM
1244 {
1245         gint action_type = 0;
1246
1247         action_type = MATCHACTION_MARK_AS_HAM;
1248         action = filteringaction_new(action_type, 0, NULL, 0, 0, NULL);
1249 }
1250 | MATCHER_FORWARD MATCHER_INTEGER MATCHER_STRING
1251 {
1252         gchar *destination = NULL;
1253         gint action_type = 0;
1254         gint account_id = 0;
1255
1256         action_type = MATCHACTION_FORWARD;
1257         account_id = strtol($2, NULL, 10);
1258         destination = $3;
1259         action = filteringaction_new(action_type,
1260             account_id, destination, 0, 0, NULL);
1261 }
1262 | MATCHER_FORWARD_AS_ATTACHMENT MATCHER_INTEGER MATCHER_STRING
1263 {
1264         gchar *destination = NULL;
1265         gint action_type = 0;
1266         gint account_id = 0;
1267
1268         action_type = MATCHACTION_FORWARD_AS_ATTACHMENT;
1269         account_id = strtol($2, NULL, 10);
1270         destination = $3;
1271         action = filteringaction_new(action_type,
1272             account_id, destination, 0, 0, NULL);
1273 }
1274 | MATCHER_REDIRECT MATCHER_INTEGER MATCHER_STRING
1275 {
1276         gchar *destination = NULL;
1277         gint action_type = 0;
1278         gint account_id = 0;
1279
1280         action_type = MATCHACTION_REDIRECT;
1281         account_id = strtol($2, NULL, 10);
1282         destination = $3;
1283         action = filteringaction_new(action_type,
1284             account_id, destination, 0, 0, NULL);
1285 }
1286 | MATCHER_COLOR MATCHER_INTEGER
1287 {
1288         gint action_type = 0;
1289         gint color = 0;
1290
1291         action_type = MATCHACTION_COLOR;
1292         color = strtol($2, NULL, 10);
1293         action = filteringaction_new(action_type, 0, NULL, color, 0, NULL);
1294 }
1295 | MATCHER_CHANGE_SCORE MATCHER_INTEGER
1296 {
1297         gint score = 0;
1298         
1299         score = strtol($2, NULL, 10);
1300         action = filteringaction_new(MATCHACTION_CHANGE_SCORE, 0,
1301                                      NULL, 0, score, NULL);
1302 }
1303 /* backward compatibility */
1304 | MATCHER_SCORE MATCHER_INTEGER
1305 {
1306         gint score = 0;
1307         
1308         score = strtol($2, NULL, 10);
1309         action = filteringaction_new(MATCHACTION_CHANGE_SCORE, 0,
1310                                      NULL, 0, score, NULL);
1311 }
1312 | MATCHER_SET_SCORE MATCHER_INTEGER
1313 {
1314         gint score = 0;
1315         
1316         score = strtol($2, NULL, 10);
1317         action = filteringaction_new(MATCHACTION_SET_SCORE, 0,
1318                                      NULL, 0, score, NULL);
1319 }
1320 | MATCHER_HIDE
1321 {
1322         action = filteringaction_new(MATCHACTION_HIDE, 0, NULL, 0, 0, NULL);
1323 }
1324 | MATCHER_IGNORE
1325 {
1326         action = filteringaction_new(MATCHACTION_IGNORE, 0, NULL, 0, 0, NULL);
1327 }
1328 | MATCHER_ADD_TO_ADDRESSBOOK MATCHER_STRING
1329 {
1330         header = g_strdup($2);
1331 } MATCHER_STRING
1332 {
1333         gchar *addressbook = NULL;
1334         gint action_type = 0;
1335
1336         action_type = MATCHACTION_ADD_TO_ADDRESSBOOK;
1337         addressbook = $2;
1338         action = filteringaction_new(action_type, 0, addressbook, 0, 0, header);
1339         g_free(header);
1340 }
1341 | MATCHER_STOP
1342 {
1343         action = filteringaction_new(MATCHACTION_STOP, 0, NULL, 0, 0, NULL);
1344 }
1345 ;