* src/mimeview.[ch]
[claws.git] / src / matcher_parser_parse.y
1
2 %{
3
4 /*
5  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
6  * Copyright (c) 2001-2002 by Hiroyuki Yamamoto & The Sylpheed Claws Team.
7  *
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.
12  *
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.
17  *
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.
21  */
22
23 #include "defs.h"
24
25 #include <glib.h>
26
27 #include "intl.h"
28 #include "utils.h"
29 #include "filtering.h"
30 #include "scoring.h"
31 #include "matcher.h"
32 #include "matcher_parser.h"
33 #include "matcher_parser_lex.h"
34 #include "procmsg.h"
35
36 #define MAX_COLORLABELS (MSG_CLABEL_7 - MSG_CLABEL_NONE)
37
38 static gint error = 0;
39 static gint bool_op = 0;
40 static gint match_type = 0;
41 static gchar *header = NULL;
42
43 static MatcherProp *prop;
44
45 static GSList *matchers_list = NULL;
46
47 static MatcherList *cond;
48 static gint score = 0;
49 static FilteringAction *action = NULL;
50
51 static FilteringProp *filtering;
52 static ScoringProp *scoring = NULL;
53
54 static GSList **prefs_scoring = NULL;
55 static GSList **prefs_filtering = NULL;
56
57 static int matcher_parser_dialog = 0;
58
59 /* ******************************************************************** */
60
61 void matcher_parser_start_parsing(FILE *f)
62 {
63         matcher_parserrestart(f);
64         matcher_parserparse();
65 }
66  
67 FilteringProp *matcher_parser_get_filtering(gchar *str)
68 {
69         void *bufstate;
70
71         /* bad coding to enable the sub-grammar matching
72            in yacc */
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)
80                 filtering = NULL;
81         matcher_parser_dialog = 0;
82         matcher_parser_delete_buffer(bufstate);
83         return filtering;
84 }
85
86 ScoringProp *matcher_parser_get_scoring(gchar *str)
87 {
88         void *bufstate;
89
90         /* bad coding to enable the sub-grammar matching
91            in yacc */
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)
99                 scoring = NULL;
100         matcher_parser_dialog = 0;
101         matcher_parser_delete_buffer(bufstate);
102         return scoring;
103 }
104
105 static gboolean check_quote_symetry(gchar *str)
106 {
107         gchar *walk;
108         int ret = 0;
109         
110         if (str == NULL)
111                 return TRUE; /* heh, that's symetric */
112         if (*str == '\0')
113                 return TRUE;
114         for (walk = str; *walk; walk++) {
115                 if (*walk == '\"') {
116                         if (walk == str         /* first char */
117                         || *(walk - 1) != '\\') /* not escaped */
118                                 ret ++;
119                 }
120         }
121         return !(ret % 2);
122 }
123
124 MatcherList *matcher_parser_get_cond(gchar *str)
125 {
126         void *bufstate;
127
128         if (!check_quote_symetry(str)) {
129                 cond = NULL;
130                 return cond;
131         }
132         
133         /* bad coding to enable the sub-grammar matching
134            in yacc */
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);
143         return cond;
144 }
145
146 MatcherProp *matcher_parser_get_prop(gchar *str)
147 {
148         MatcherList *list;
149         MatcherProp *prop;
150
151         matcher_parserlineno = 1;
152         list = matcher_parser_get_cond(str);
153         if (list == NULL)
154                 return NULL;
155
156         if (list->matchers == NULL)
157                 return NULL;
158
159         if (list->matchers->next != NULL)
160                 return NULL;
161
162         prop = list->matchers->data;
163
164         g_slist_free(list->matchers);
165         g_free(list);
166
167         return prop;
168 }
169
170 void matcher_parsererror(char *str)
171 {
172         GSList *l;
173
174         if (matchers_list) {
175                 for (l = matchers_list; l != NULL; l = g_slist_next(l)) {
176                         matcherprop_free((MatcherProp *)
177                                          l->data);
178                         l->data = NULL;
179                 }
180                 g_slist_free(matchers_list);
181                 matchers_list = NULL;
182         }
183         cond = NULL;
184         g_warning("scoring / filtering parsing: %i: %s\n",
185                   matcher_parserlineno, str);
186         error = 1;
187 }
188
189 int matcher_parserwrap(void)
190 {
191         return 1;
192 }
193 %}
194
195 %union {
196         char *str;
197         int value;
198 }
199
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 MATCHER_LOCK MATCHER_UNLOCK
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 
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
225
226 %start file
227
228 %token <str> MATCHER_STRING
229 %token <str> MATCHER_SECTION
230 %token <str> MATCHER_INTEGER
231
232 %%
233
234 file:
235 {
236         if (!matcher_parser_dialog) {
237                 prefs_scoring = &global_scoring;
238                 prefs_filtering = &global_processing;
239         }
240 }
241 file_line_list;
242
243 file_line_list:
244 file_line
245 file_line_list
246 | file_line
247 ;
248
249 file_line:
250 section_notification
251 | instruction
252 | error MATCHER_EOL
253 {
254         yyerrok;
255 };
256
257 section_notification:
258 MATCHER_SECTION MATCHER_EOL
259 {
260         gchar *folder = $1;
261         FolderItem *item = NULL;
262
263         if (!matcher_parser_dialog) {
264                 if (!strcmp(folder, "global")) {
265                         prefs_scoring = &global_scoring;
266                         prefs_filtering = &global_processing;
267                 } else {
268                         item = folder_find_item_from_identifier(folder);
269                         if (item != NULL) {
270                                 prefs_scoring = &item->prefs->scoring;
271                                 prefs_filtering = &item->prefs->processing;
272                         } else {
273                                 prefs_scoring = NULL;
274                                 prefs_filtering = NULL;
275                         }
276                 }
277         }
278 }
279 ;
280
281 instruction:
282 condition end_instr_opt
283 | MATCHER_EOL
284 ;
285
286 end_instr_opt:
287 filtering_or_scoring end_action
288 |
289 {
290         if (matcher_parser_dialog)
291                 YYACCEPT;
292         else {
293                 matcher_parsererror("parse error");
294                 YYERROR;
295         }
296 }
297 ;
298
299 end_action:
300 MATCHER_EOL
301 |
302 {
303         if (matcher_parser_dialog)
304                 YYACCEPT;
305         else {
306                 matcher_parsererror("parse error");
307                 YYERROR;
308         }
309 }
310 ;
311
312 filtering_or_scoring:
313 filtering_action
314 {
315         filtering = filteringprop_new(cond, action);
316         cond = NULL;
317         action = NULL;
318         if (!matcher_parser_dialog && (prefs_filtering != NULL)) {
319                 *prefs_filtering = g_slist_append(*prefs_filtering,
320                                                   filtering);
321                 filtering = NULL;
322         }
323 }
324 | scoring_rule
325 {
326         scoring = scoringprop_new(cond, score);
327         cond = NULL;
328         score = 0;
329         if (!matcher_parser_dialog && (prefs_scoring != NULL)) {
330                 *prefs_scoring = g_slist_append(*prefs_scoring, scoring);
331                 scoring = NULL;
332         }
333 }
334 ;
335
336 match_type:
337 MATCHER_MATCHCASE
338 {
339         match_type = MATCHTYPE_MATCHCASE;
340 }
341 | MATCHER_MATCH
342 {
343         match_type = MATCHTYPE_MATCH;
344 }
345 | MATCHER_REGEXPCASE
346 {
347         match_type = MATCHTYPE_REGEXPCASE;
348 }
349 | MATCHER_REGEXP
350 {
351         match_type = MATCHTYPE_REGEXP;
352 }
353 ;
354
355 condition:
356 condition_list
357 {
358         cond = matcherlist_new(matchers_list, (bool_op == MATCHERBOOL_AND));
359         matchers_list = NULL;
360 }
361 ;
362
363 condition_list:
364 condition_list bool_op one_condition
365 {
366         matchers_list = g_slist_append(matchers_list, prop);
367 }
368 | one_condition
369 {
370         matchers_list = NULL;
371         matchers_list = g_slist_append(matchers_list, prop);
372 }
373 ;
374
375 bool_op:
376 MATCHER_AND
377 {
378         bool_op = MATCHERBOOL_AND;
379 }
380 | MATCHER_OR
381 {
382         bool_op = MATCHERBOOL_OR;
383 }
384 ;
385
386 one_condition:
387 MATCHER_ALL
388 {
389         gint criteria = 0;
390
391         criteria = MATCHCRITERIA_ALL;
392         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
393 }
394 | MATCHER_UNREAD
395 {
396         gint criteria = 0;
397
398         criteria = MATCHCRITERIA_UNREAD;
399         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
400 }
401 | MATCHER_NOT_UNREAD 
402 {
403         gint criteria = 0;
404
405         criteria = MATCHCRITERIA_NOT_UNREAD;
406         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
407 }
408 | MATCHER_NEW
409 {
410         gint criteria = 0;
411
412         criteria = MATCHCRITERIA_NEW;
413         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
414 }
415 | MATCHER_NOT_NEW
416 {
417         gint criteria = 0;
418
419         criteria = MATCHCRITERIA_NOT_NEW;
420         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
421 }
422 | MATCHER_MARKED
423 {
424         gint criteria = 0;
425
426         criteria = MATCHCRITERIA_MARKED;
427         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
428 }
429 | MATCHER_NOT_MARKED
430 {
431         gint criteria = 0;
432
433         criteria = MATCHCRITERIA_NOT_MARKED;
434         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
435 }
436 | MATCHER_DELETED
437 {
438         gint criteria = 0;
439
440         criteria = MATCHCRITERIA_DELETED;
441         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
442 }
443 | MATCHER_NOT_DELETED
444 {
445         gint criteria = 0;
446
447         criteria = MATCHCRITERIA_NOT_DELETED;
448         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
449 }
450 | MATCHER_REPLIED
451 {
452         gint criteria = 0;
453
454         criteria = MATCHCRITERIA_REPLIED;
455         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
456 }
457 | MATCHER_NOT_REPLIED
458 {
459         gint criteria = 0;
460
461         criteria = MATCHCRITERIA_NOT_REPLIED;
462         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
463 }
464 | MATCHER_FORWARDED
465 {
466         gint criteria = 0;
467
468         criteria = MATCHCRITERIA_FORWARDED;
469         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
470 }
471 | MATCHER_NOT_FORWARDED
472 {
473         gint criteria = 0;
474
475         criteria = MATCHCRITERIA_NOT_FORWARDED;
476         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
477 }
478 | MATCHER_LOCKED
479 {
480         gint criteria = 0;
481
482         criteria = MATCHCRITERIA_LOCKED;
483         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
484 }
485 | MATCHER_NOT_LOCKED
486 {
487         gint criteria = 0;
488
489         criteria = MATCHCRITERIA_NOT_LOCKED;
490         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
491 }
492 | MATCHER_COLORLABEL MATCHER_INTEGER
493 {
494         gint criteria = 0;
495         gint value = 0;
496
497         criteria = MATCHCRITERIA_COLORLABEL;
498         value = atoi($2);
499         if (value < 1) value = 1;
500         else if (value > MAX_COLORLABELS) value = MAX_COLORLABELS;
501         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
502 }
503 | MATCHER_NOT_COLORLABEL MATCHER_INTEGER
504 {
505         gint criteria = 0;
506         gint value = 0;
507
508         criteria = MATCHCRITERIA_NOT_COLORLABEL;
509         value = atoi($2);
510         if (value < 1) value = 1;
511         else if (value > MAX_COLORLABELS) value = MAX_COLORLABELS;
512         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
513 }
514 | MATCHER_IGNORE_THREAD
515 {
516         gint criteria = 0;
517
518         criteria = MATCHCRITERIA_IGNORE_THREAD;
519         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
520 }
521 | MATCHER_NOT_IGNORE_THREAD
522 {
523         gint criteria = 0;
524
525         criteria = MATCHCRITERIA_NOT_IGNORE_THREAD;
526         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
527 }
528 | MATCHER_SUBJECT match_type MATCHER_STRING
529 {
530         gint criteria = 0;
531         gchar *expr = NULL;
532
533         criteria = MATCHCRITERIA_SUBJECT;
534         expr = $3;
535         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
536 }
537 | MATCHER_NOT_SUBJECT match_type MATCHER_STRING
538 {
539         gint criteria = 0;
540         gchar *expr = NULL;
541
542         criteria = MATCHCRITERIA_NOT_SUBJECT;
543         expr = $3;
544         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
545 }
546 | MATCHER_FROM match_type MATCHER_STRING
547 {
548         gint criteria = 0;
549         gchar *expr = NULL;
550
551         criteria = MATCHCRITERIA_FROM;
552         expr = $3;
553         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
554 }
555 | MATCHER_NOT_FROM match_type MATCHER_STRING
556 {
557         gint criteria = 0;
558         gchar *expr = NULL;
559
560         criteria = MATCHCRITERIA_NOT_FROM;
561         expr = $3;
562         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
563 }
564 | MATCHER_TO match_type MATCHER_STRING
565 {
566         gint criteria = 0;
567         gchar *expr = NULL;
568
569         criteria = MATCHCRITERIA_TO;
570         expr = $3;
571         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
572 }
573 | MATCHER_NOT_TO match_type MATCHER_STRING
574 {
575         gint criteria = 0;
576         gchar *expr = NULL;
577
578         criteria = MATCHCRITERIA_NOT_TO;
579         expr = $3;
580         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
581 }
582 | MATCHER_CC match_type MATCHER_STRING
583 {
584         gint criteria = 0;
585         gchar *expr = NULL;
586
587         criteria = MATCHCRITERIA_CC;
588         expr = $3;
589         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
590 }
591 | MATCHER_NOT_CC match_type MATCHER_STRING
592 {
593         gint criteria = 0;
594         gchar *expr = NULL;
595
596         criteria = MATCHCRITERIA_NOT_CC;
597         expr = $3;
598         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
599 }
600 | MATCHER_TO_OR_CC match_type MATCHER_STRING
601 {
602         gint criteria = 0;
603         gchar *expr = NULL;
604
605         criteria = MATCHCRITERIA_TO_OR_CC;
606         expr = $3;
607         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
608 }
609 | MATCHER_NOT_TO_AND_NOT_CC match_type MATCHER_STRING
610 {
611         gint criteria = 0;
612         gchar *expr = NULL;
613
614         criteria = MATCHCRITERIA_NOT_TO_AND_NOT_CC;
615         expr = $3;
616         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
617 }
618 | MATCHER_AGE_GREATER MATCHER_INTEGER
619 {
620         gint criteria = 0;
621         gint value = 0;
622
623         criteria = MATCHCRITERIA_AGE_GREATER;
624         value = atoi($2);
625         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
626 }
627 | MATCHER_AGE_LOWER MATCHER_INTEGER
628 {
629         gint criteria = 0;
630         gint value = 0;
631
632         criteria = MATCHCRITERIA_AGE_LOWER;
633         value = atoi($2);
634         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
635 }
636 | MATCHER_NEWSGROUPS match_type MATCHER_STRING
637 {
638         gint criteria = 0;
639         gchar *expr = NULL;
640
641         criteria = MATCHCRITERIA_NEWSGROUPS;
642         expr = $3;
643         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
644 }
645 | MATCHER_NOT_NEWSGROUPS match_type MATCHER_STRING
646 {
647         gint criteria = 0;
648         gchar *expr = NULL;
649
650         criteria = MATCHCRITERIA_NOT_NEWSGROUPS;
651         expr = $3;
652         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
653 }
654 | MATCHER_INREPLYTO match_type MATCHER_STRING
655 {
656         gint criteria = 0;
657         gchar *expr = NULL;
658
659         criteria = MATCHCRITERIA_INREPLYTO;
660         expr = $3;
661         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
662 }
663 | MATCHER_NOT_INREPLYTO match_type MATCHER_STRING
664 {
665         gint criteria = 0;
666         gchar *expr = NULL;
667
668         criteria = MATCHCRITERIA_NOT_INREPLYTO;
669         expr = $3;
670         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
671 }
672 | MATCHER_REFERENCES match_type MATCHER_STRING
673 {
674         gint criteria = 0;
675         gchar *expr = NULL;
676
677         criteria = MATCHCRITERIA_REFERENCES;
678         expr = $3;
679         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
680 }
681 | MATCHER_NOT_REFERENCES match_type MATCHER_STRING
682 {
683         gint criteria = 0;
684         gchar *expr = NULL;
685
686         criteria = MATCHCRITERIA_NOT_REFERENCES;
687         expr = $3;
688         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
689 }
690 | MATCHER_SCORE_GREATER MATCHER_INTEGER
691 {
692         gint criteria = 0;
693         gint value = 0;
694
695         criteria = MATCHCRITERIA_SCORE_GREATER;
696         value = atoi($2);
697         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
698 }
699 | MATCHER_SCORE_LOWER MATCHER_INTEGER
700 {
701         gint criteria = 0;
702         gint value = 0;
703
704         criteria = MATCHCRITERIA_SCORE_LOWER;
705         value = atoi($2);
706         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
707 }
708 | MATCHER_SCORE_EQUAL MATCHER_INTEGER
709 {
710         gint criteria = 0;
711         gint value = 0;
712
713         criteria = MATCHCRITERIA_SCORE_EQUAL;
714         value = atoi($2);
715         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
716 }
717 | MATCHER_SIZE_GREATER MATCHER_INTEGER 
718 {
719         gint criteria = 0;
720         gint value    = 0;
721         criteria = MATCHCRITERIA_SIZE_GREATER;
722         value = atoi($2);
723         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
724 }
725 | MATCHER_SIZE_SMALLER MATCHER_INTEGER
726 {
727         gint criteria = 0;
728         gint value    = 0;
729         criteria = MATCHCRITERIA_SIZE_SMALLER;
730         value = atoi($2);
731         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
732 }
733 | MATCHER_SIZE_EQUAL MATCHER_INTEGER
734 {
735         gint criteria = 0;
736         gint value    = 0;
737         criteria = MATCHCRITERIA_SIZE_EQUAL;
738         value = atoi($2);
739         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
740 }
741 | MATCHER_HEADER MATCHER_STRING
742 {
743         header = g_strdup($2);
744 } match_type MATCHER_STRING
745 {
746         gint criteria = 0;
747         gchar *expr = NULL;
748
749         criteria = MATCHCRITERIA_HEADER;
750         expr = $2;
751         prop = matcherprop_unquote_new(criteria, header, match_type, expr, 0);
752         g_free(header);
753 }
754 | MATCHER_NOT_HEADER MATCHER_STRING
755 {
756         header = g_strdup($2);
757 } match_type MATCHER_STRING
758 {
759         gint criteria = 0;
760         gchar *expr = NULL;
761
762         criteria = MATCHCRITERIA_NOT_HEADER;
763         expr = $2;
764         prop = matcherprop_unquote_new(criteria, header, match_type, expr, 0);
765         g_free(header);
766 }
767 | MATCHER_HEADERS_PART match_type MATCHER_STRING
768 {
769         gint criteria = 0;
770         gchar *expr = NULL;
771
772         criteria = MATCHCRITERIA_HEADERS_PART;
773         expr = $3;
774         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
775 }
776 | MATCHER_NOT_HEADERS_PART match_type MATCHER_STRING
777 {
778         gint criteria = 0;
779         gchar *expr = NULL;
780
781         criteria = MATCHCRITERIA_NOT_HEADERS_PART;
782         expr = $3;
783         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
784 }
785 | MATCHER_MESSAGE match_type MATCHER_STRING
786 {
787         gint criteria = 0;
788         gchar *expr = NULL;
789
790         criteria = MATCHCRITERIA_MESSAGE;
791         expr = $3;
792         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
793 }
794 | MATCHER_NOT_MESSAGE match_type MATCHER_STRING
795 {
796         gint criteria = 0;
797         gchar *expr = NULL;
798
799         criteria = MATCHCRITERIA_NOT_MESSAGE;
800         expr = $3;
801         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
802 }
803 | MATCHER_BODY_PART match_type MATCHER_STRING
804 {
805         gint criteria = 0;
806         gchar *expr = NULL;
807
808         criteria = MATCHCRITERIA_BODY_PART;
809         expr = $3;
810         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
811 }
812 | MATCHER_NOT_BODY_PART match_type MATCHER_STRING
813 {
814         gint criteria = 0;
815         gchar *expr = NULL;
816
817         criteria = MATCHCRITERIA_NOT_BODY_PART;
818         expr = $3;
819         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
820 }
821 | MATCHER_EXECUTE MATCHER_STRING
822 {
823         gint criteria = 0;
824         gchar *expr = NULL;
825
826         criteria = MATCHCRITERIA_EXECUTE;
827         expr = $2;
828         prop = matcherprop_unquote_new(criteria, NULL, 0, expr, 0);
829 }
830 | MATCHER_NOT_EXECUTE MATCHER_STRING
831 {
832         gint criteria = 0;
833         gchar *expr = NULL;
834
835         criteria = MATCHCRITERIA_NOT_EXECUTE;
836         expr = $2;
837         prop = matcherprop_unquote_new(criteria, NULL, 0, expr, 0);
838 }
839 ;
840
841 filtering_action:
842 MATCHER_EXECUTE MATCHER_STRING
843 {
844         gchar *cmd = NULL;
845         gint action_type = 0;
846
847         action_type = MATCHACTION_EXECUTE;
848         cmd = $2;
849         action = filteringaction_new(action_type, 0, cmd, 0);
850 }
851 | MATCHER_MOVE MATCHER_STRING
852 {
853         gchar *destination = NULL;
854         gint action_type = 0;
855
856         action_type = MATCHACTION_MOVE;
857         destination = $2;
858         action = filteringaction_new(action_type, 0, destination, 0);
859 }
860 | MATCHER_COPY MATCHER_STRING
861 {
862         gchar *destination = NULL;
863         gint action_type = 0;
864
865         action_type = MATCHACTION_COPY;
866         destination = $2;
867         action = filteringaction_new(action_type, 0, destination, 0);
868 }
869 | MATCHER_DELETE
870 {
871         gint action_type = 0;
872
873         action_type = MATCHACTION_DELETE;
874         action = filteringaction_new(action_type, 0, NULL, 0);
875 }
876 | MATCHER_MARK
877 {
878         gint action_type = 0;
879
880         action_type = MATCHACTION_MARK;
881         action = filteringaction_new(action_type, 0, NULL, 0);
882 }
883 | MATCHER_UNMARK
884 {
885         gint action_type = 0;
886
887         action_type = MATCHACTION_UNMARK;
888         action = filteringaction_new(action_type, 0, NULL, 0);
889 }
890 | MATCHER_LOCK
891 {
892         gint action_type = 0;
893
894         action_type = MATCHACTION_LOCK;
895         action = filteringaction_new(action_type, 0, NULL, 0);
896 }
897 | MATCHER_UNLOCK
898 {
899         gint action_type = 0;
900
901         action_type = MATCHACTION_UNLOCK;
902         action = filteringaction_new(action_type, 0, NULL, 0);
903 }
904 | MATCHER_MARK_AS_READ
905 {
906         gint action_type = 0;
907
908         action_type = MATCHACTION_MARK_AS_READ;
909         action = filteringaction_new(action_type, 0, NULL, 0);
910 }
911 | MATCHER_MARK_AS_UNREAD
912 {
913         gint action_type = 0;
914
915         action_type = MATCHACTION_MARK_AS_UNREAD;
916         action = filteringaction_new(action_type, 0, NULL, 0);
917 }
918 | MATCHER_FORWARD MATCHER_INTEGER MATCHER_STRING
919 {
920         gchar *destination = NULL;
921         gint action_type = 0;
922         gint account_id = 0;
923
924         action_type = MATCHACTION_FORWARD;
925         account_id = atoi($2);
926         destination = $3;
927         action = filteringaction_new(action_type, account_id, destination, 0);
928 }
929 | MATCHER_FORWARD_AS_ATTACHMENT MATCHER_INTEGER MATCHER_STRING
930 {
931         gchar *destination = NULL;
932         gint action_type = 0;
933         gint account_id = 0;
934
935         action_type = MATCHACTION_FORWARD_AS_ATTACHMENT;
936         account_id = atoi($2);
937         destination = $3;
938         action = filteringaction_new(action_type, account_id, destination, 0);
939 }
940 | MATCHER_REDIRECT MATCHER_INTEGER MATCHER_STRING
941 {
942         gchar *destination = NULL;
943         gint action_type = 0;
944         gint account_id = 0;
945
946         action_type = MATCHACTION_REDIRECT;
947         account_id = atoi($2);
948         destination = $3;
949         action = filteringaction_new(action_type, account_id, destination, 0);
950 }
951 | MATCHER_COLOR MATCHER_INTEGER
952 {
953         gint action_type = 0;
954         gint color = 0;
955
956         action_type = MATCHACTION_COLOR;
957         color = atoi($2);
958         action = filteringaction_new(action_type, 0, NULL, color);
959 }
960 | MATCHER_CHANGE_SCORE MATCHER_STRING
961 {
962         gint action_type = MATCHACTION_CHANGE_SCORE;
963         char *last_tok = NULL;
964         const gchar *first_tok;
965         long int chscore;
966         int change_type; /* -1 == decrement, 0 == assign, 1 == increment */     
967
968 #define is_number(x) ( ((x) == '+') || ((x) == '-') || (isdigit((x))) ) 
969         /* find start */
970         for (first_tok = $2; *first_tok && !is_number(*first_tok); first_tok++)
971                 ;            
972 #undef is_number                
973         
974         chscore = strtol(first_tok, &last_tok, 10);
975         if (last_tok == first_tok || *last_tok == 0) 
976                 chscore = 0;
977         change_type = *first_tok == '+' ? 1 : *first_tok == '-' ? -1 : 0;
978         action = filteringaction_new(MATCHACTION_CHANGE_SCORE, change_type,
979                                      NULL, chscore);
980 }
981 ;
982
983 scoring_rule:
984 MATCHER_SCORE MATCHER_INTEGER
985 {
986         score = atoi($2);
987 }
988 ;