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.
23 * 07/18/01 Alfons when we want a file name from a MsgInfo, get that
24 * from MsgInfo->folder if the message is being filtered
25 * from incorporation. also some more safe string checking.
34 #include "procheader.h"
43 typedef struct _MatchParser MatchParser
;
45 static MatchParser matchparser_tab
[] = {
47 {MATCHING_ALL
, "all"},
48 {MATCHING_UNREAD
, "unread"},
49 {MATCHING_NOT_UNREAD
, "~unread"},
50 {MATCHING_NEW
, "new"},
51 {MATCHING_NOT_NEW
, "~new"},
52 {MATCHING_MARKED
, "marked"},
53 {MATCHING_NOT_MARKED
, "~marked"},
54 {MATCHING_DELETED
, "deleted"},
55 {MATCHING_NOT_DELETED
, "~deleted"},
56 {MATCHING_REPLIED
, "replied"},
57 {MATCHING_NOT_REPLIED
, "~replied"},
58 {MATCHING_FORWARDED
, "forwarded"},
59 {MATCHING_NOT_FORWARDED
, "~forwarded"},
62 {MATCHING_SUBJECT
, "subject"},
63 {MATCHING_NOT_SUBJECT
, "~subject"},
64 {MATCHING_FROM
, "from"},
65 {MATCHING_NOT_FROM
, "~from"},
67 {MATCHING_NOT_TO
, "~to"},
69 {MATCHING_NOT_CC
, "~cc"},
70 {MATCHING_TO_OR_CC
, "to_or_cc"},
71 {MATCHING_NOT_TO_AND_NOT_CC
, "~to_or_cc"},
72 {MATCHING_AGE_GREATER
, "age_greater"},
73 {MATCHING_AGE_LOWER
, "age_lower"},
74 {MATCHING_NEWSGROUPS
, "newsgroups"},
75 {MATCHING_NOT_NEWSGROUPS
, "~newsgroups"},
76 {MATCHING_INREPLYTO
, "inreplyto"},
77 {MATCHING_NOT_INREPLYTO
, "~inreplyto"},
78 {MATCHING_REFERENCES
, "references"},
79 {MATCHING_NOT_REFERENCES
, "~references"},
80 {MATCHING_SCORE_GREATER
, "score_greater"},
81 {MATCHING_SCORE_LOWER
, "score_lower"},
83 /* content have to be read */
84 {MATCHING_HEADER
, "header"},
85 {MATCHING_NOT_HEADER
, "~header"},
86 {MATCHING_HEADERS_PART
, "headers_part"},
87 {MATCHING_NOT_HEADERS_PART
, "~headers_part"},
88 {MATCHING_MESSAGE
, "message"},
89 {MATCHING_NOT_MESSAGE
, "~message"},
90 {MATCHING_BODY_PART
, "body_part"},
91 {MATCHING_NOT_BODY_PART
, "~body_part"},
92 {MATCHING_EXECUTE
, "execute"},
93 {MATCHING_NOT_EXECUTE
, "~execute"},
96 {MATCHING_MATCHCASE
, "matchcase"},
97 {MATCHING_MATCH
, "match"},
98 {MATCHING_REGEXPCASE
, "regexpcase"},
99 {MATCHING_REGEXP
, "regexp"},
102 {MATCHING_SCORE
, "score"},
105 {MATCHING_ACTION_MOVE
, "move"},
106 {MATCHING_ACTION_COPY
, "copy"},
107 {MATCHING_ACTION_DELETE
, "delete"},
108 {MATCHING_ACTION_MARK
, "mark"},
109 {MATCHING_ACTION_UNMARK
, "unmark"},
110 {MATCHING_ACTION_MARK_AS_READ
, "mark_as_read"},
111 {MATCHING_ACTION_MARK_AS_UNREAD
, "mark_as_unread"},
112 {MATCHING_ACTION_FORWARD
, "forward"},
113 {MATCHING_ACTION_FORWARD_AS_ATTACHMENT
, "forward_as_attachment"},
114 {MATCHING_ACTION_COLOR
, "color"}
115 /* {MATCHING_EXECUTE, "execute"}, */
118 gchar
* get_matchparser_tab_str(gint id
)
122 for(i
= 0 ; i
< (int) (sizeof(matchparser_tab
) / sizeof(MatchParser
)) ;
124 if (matchparser_tab
[i
].id
== id
)
125 return matchparser_tab
[i
].str
;
135 header "x-mailing" match "toto"
136 subject match "regexp" & to regexp "regexp"
137 subject match "regexp" | to regexpcase "regexp" | age_sup 5
140 static gboolean
matcher_is_blank(gchar ch
);
142 /* ******************* parser *********************** */
144 static gboolean
matcher_is_blank(gchar ch
)
146 return (ch
== ' ') || (ch
== '\t');
149 /* parse for one condition */
151 MatcherProp
* matcherprop_parse(gchar
** str
)
159 gchar
* header
= NULL
;
162 key
= matcher_parse_keyword(&tmp
);
169 case MATCHING_AGE_LOWER
:
170 case MATCHING_AGE_GREATER
:
171 case MATCHING_SCORE_LOWER
:
172 case MATCHING_SCORE_GREATER
:
173 value
= matcher_parse_number(&tmp
);
180 prop
= matcherprop_new(key
, NULL
, 0, NULL
, value
);
185 case MATCHING_UNREAD
:
186 case MATCHING_NOT_UNREAD
:
188 case MATCHING_NOT_NEW
:
189 case MATCHING_MARKED
:
190 case MATCHING_NOT_MARKED
:
191 case MATCHING_DELETED
:
192 case MATCHING_NOT_DELETED
:
193 case MATCHING_REPLIED
:
194 case MATCHING_NOT_REPLIED
:
195 case MATCHING_FORWARDED
:
196 case MATCHING_NOT_FORWARDED
:
197 prop
= matcherprop_new(key
, NULL
, 0, NULL
, 0);
202 case MATCHING_SUBJECT
:
203 case MATCHING_NOT_SUBJECT
:
205 case MATCHING_NOT_FROM
:
207 case MATCHING_NOT_TO
:
209 case MATCHING_NOT_CC
:
210 case MATCHING_TO_OR_CC
:
211 case MATCHING_NOT_TO_AND_NOT_CC
:
212 case MATCHING_NEWSGROUPS
:
213 case MATCHING_NOT_NEWSGROUPS
:
214 case MATCHING_INREPLYTO
:
215 case MATCHING_NOT_REFERENCES
:
216 case MATCHING_REFERENCES
:
217 case MATCHING_NOT_INREPLYTO
:
218 case MATCHING_MESSAGE
:
219 case MATCHING_NOT_MESSAGE
:
220 case MATCHING_EXECUTE
:
221 case MATCHING_NOT_EXECUTE
:
222 case MATCHING_HEADERS_PART
:
223 case MATCHING_NOT_HEADERS_PART
:
224 case MATCHING_BODY_PART
:
225 case MATCHING_NOT_BODY_PART
:
226 case MATCHING_HEADER
:
227 case MATCHING_NOT_HEADER
:
228 if ((key
== MATCHING_HEADER
) || (key
== MATCHING_NOT_HEADER
)) {
229 header
= matcher_parse_str(&tmp
);
236 match
= matcher_parse_keyword(&tmp
);
245 case MATCHING_REGEXP
:
246 case MATCHING_REGEXPCASE
:
247 expr
= matcher_parse_regexp(&tmp
);
255 prop
= matcherprop_new(key
, header
, match
, expr
, 0);
260 case MATCHING_MATCHCASE
:
261 expr
= matcher_parse_str(&tmp
);
269 prop
= matcherprop_new(key
, header
, match
, expr
, 0);
285 gint
matcher_parse_keyword(gchar
** str
)
293 dup
= alloca(strlen(* str
) + 1);
297 while (matcher_is_blank(*p
))
302 while (!matcher_is_blank(*p
) && (*p
!= '\0'))
306 for(i
= 0 ; i
< (int) (sizeof(matchparser_tab
) / sizeof(MatchParser
)) ;
308 if ((strlen(matchparser_tab
[i
].str
) == p
- start
) &&
309 (strncasecmp(matchparser_tab
[i
].str
, start
,
324 return matchparser_tab
[match
].id
;
327 gint
matcher_parse_number(gchar
** str
)
333 dup
= alloca(strlen(* str
) + 1);
337 while (matcher_is_blank(*p
))
342 if (!isdigit(*p
) && *p
!= '-' && *p
!= '+') {
346 if (*p
== '-' || *p
== '+')
357 gboolean
matcher_parse_boolean_op(gchar
** str
)
363 while (matcher_is_blank(*p
))
367 *str
+= p
- * str
+ 1;
370 else if (*p
== '&') {
371 *str
+= p
- * str
+ 1;
380 gchar
* matcher_parse_regexp(gchar
** str
)
386 dup
= alloca(strlen(* str
) + 1);
390 while (matcher_is_blank(*p
))
408 return g_strdup(start
);
411 /* matcher_parse_str() - parses a string until it hits a
412 * terminating \". to unescape characters use \. The string
413 * should not be used directly: matcher_unescape_str()
414 * returns a string that can be used directly. */
415 gchar
* matcher_parse_str(gchar
** str
)
422 dup
= alloca(strlen(* str
) + 1);
426 while (matcher_is_blank(*p
))
438 for ( ; *p
&& *p
!= '\"'; p
++, dest
++) {
448 *str
+= dest
- dup
+ 2;
449 return g_strdup(start
);
452 gchar
*matcher_unescape_str(gchar
*str
)
454 gchar
*tmp
= alloca(strlen(str
) + 1);
455 register gchar
*src
= tmp
;
456 register gchar
*dst
= str
;
460 for ( ; *src
; src
++) {
467 else if (*src
== 'n')
469 else if (*src
== 'r')
471 else if (*src
== '\'' || *src
== '\"')
483 /* **************** data structure allocation **************** */
486 MatcherProp
* matcherprop_new(gint criteria
, gchar
* header
,
487 gint matchtype
, gchar
* expr
,
492 prop
= g_new0(MatcherProp
, 1);
493 prop
->criteria
= criteria
;
495 prop
->header
= g_strdup(header
);
499 prop
->expr
= g_strdup(expr
);
502 prop
->matchtype
= matchtype
;
510 void matcherprop_free(MatcherProp
* prop
)
513 if (prop
->preg
!= NULL
) {
521 /* ************** match ******************************/
524 /* match the given string */
526 static gboolean
matcherprop_string_match(MatcherProp
* prop
, gchar
* str
)
534 switch(prop
->matchtype
) {
535 case MATCHING_REGEXPCASE
:
536 case MATCHING_REGEXP
:
537 if (!prop
->preg
&& (prop
->error
== 0)) {
538 prop
->preg
= g_new0(regex_t
, 1);
539 if (regcomp(prop
->preg
, prop
->expr
,
540 REG_NOSUB
| REG_EXTENDED
541 | ((prop
->matchtype
== MATCHING_REGEXPCASE
)
542 ? REG_ICASE
: 0)) != 0) {
547 if (prop
->preg
== NULL
)
550 if (regexec(prop
->preg
, str
, 0, NULL
, 0) == 0)
556 return (strstr(str
, prop
->expr
) != NULL
);
558 case MATCHING_MATCHCASE
:
559 str2
= alloca(strlen(prop
->expr
) + 1);
560 strcpy(str2
, prop
->expr
);
562 str1
= alloca(strlen(str
) + 1);
565 return (strstr(str1
, str2
) != NULL
);
572 gboolean
matcherprop_match_execute(MatcherProp
* prop
, MsgInfo
* info
)
576 cmd
= matching_build_command(prop
->expr
, info
);
580 return (system(cmd
) == 0);
583 /* match a message and his headers, hlist can be NULL if you don't
584 want to use headers */
586 gboolean
matcherprop_match(MatcherProp
* prop
, MsgInfo
* info
)
590 switch(prop
->criteria
) {
593 case MATCHING_UNREAD
:
594 return MSG_IS_UNREAD(info
->flags
);
595 case MATCHING_NOT_UNREAD
:
596 return !MSG_IS_UNREAD(info
->flags
);
598 return MSG_IS_NEW(info
->flags
);
599 case MATCHING_NOT_NEW
:
600 return !MSG_IS_NEW(info
->flags
);
601 case MATCHING_MARKED
:
602 return MSG_IS_MARKED(info
->flags
);
603 case MATCHING_NOT_MARKED
:
604 return !MSG_IS_MARKED(info
->flags
);
605 case MATCHING_DELETED
:
606 return MSG_IS_DELETED(info
->flags
);
607 case MATCHING_NOT_DELETED
:
608 return !MSG_IS_DELETED(info
->flags
);
609 case MATCHING_REPLIED
:
610 return MSG_IS_REPLIED(info
->flags
);
611 case MATCHING_NOT_REPLIED
:
612 return !MSG_IS_REPLIED(info
->flags
);
613 case MATCHING_FORWARDED
:
614 return MSG_IS_FORWARDED(info
->flags
);
615 case MATCHING_NOT_FORWARDED
:
616 return !MSG_IS_FORWARDED(info
->flags
);
617 case MATCHING_SUBJECT
:
618 return matcherprop_string_match(prop
, info
->subject
);
619 case MATCHING_NOT_SUBJECT
:
620 return !matcherprop_string_match(prop
, info
->subject
);
622 return matcherprop_string_match(prop
, info
->from
);
623 case MATCHING_NOT_FROM
:
624 return !matcherprop_string_match(prop
, info
->from
);
626 return matcherprop_string_match(prop
, info
->to
);
627 case MATCHING_NOT_TO
:
628 return !matcherprop_string_match(prop
, info
->to
);
630 return matcherprop_string_match(prop
, info
->cc
);
631 case MATCHING_NOT_CC
:
632 return !matcherprop_string_match(prop
, info
->cc
);
633 case MATCHING_TO_OR_CC
:
634 return matcherprop_string_match(prop
, info
->to
)
635 || matcherprop_string_match(prop
, info
->cc
);
636 case MATCHING_NOT_TO_AND_NOT_CC
:
637 return !(matcherprop_string_match(prop
, info
->to
)
638 || matcherprop_string_match(prop
, info
->cc
));
639 case MATCHING_AGE_GREATER
:
641 return ((t
- info
->date_t
) / (60 * 60 * 24)) >= prop
->value
;
642 case MATCHING_AGE_LOWER
:
644 return ((t
- info
->date_t
) / (60 * 60 * 24)) <= prop
->value
;
645 case MATCHING_SCORE_GREATER
:
646 return info
->score
>= prop
->value
;
647 case MATCHING_SCORE_LOWER
:
648 return info
->score
<= prop
->value
;
649 case MATCHING_NEWSGROUPS
:
650 return matcherprop_string_match(prop
, info
->newsgroups
);
651 case MATCHING_NOT_NEWSGROUPS
:
652 return !matcherprop_string_match(prop
, info
->newsgroups
);
653 case MATCHING_INREPLYTO
:
654 return matcherprop_string_match(prop
, info
->inreplyto
);
655 case MATCHING_NOT_INREPLYTO
:
656 return !matcherprop_string_match(prop
, info
->inreplyto
);
657 case MATCHING_REFERENCES
:
658 return matcherprop_string_match(prop
, info
->references
);
659 case MATCHING_NOT_REFERENCES
:
660 return !matcherprop_string_match(prop
, info
->references
);
661 case MATCHING_EXECUTE
:
662 return matcherprop_match_execute(prop
, info
);
663 case MATCHING_NOT_EXECUTE
:
664 return !matcherprop_match_execute(prop
, info
);
670 /* ********************* MatcherList *************************** */
673 /* parse for a list of conditions */
675 MatcherList
* matcherlist_parse(gchar
** str
)
678 MatcherProp
* matcher
;
679 GSList
* matchers_list
= NULL
;
680 gboolean bool_and
= TRUE
;
683 gboolean main_bool_and
= TRUE
;
688 matcher
= matcherprop_parse(&tmp
);
694 matchers_list
= g_slist_append(matchers_list
, matcher
);
697 bool_and
= matcher_parse_boolean_op(&tmp
);
703 main_bool_and
= bool_and
;
704 matcher
= matcherprop_parse(&tmp
);
707 g_slist_append(matchers_list
, matcher
);
710 for(l
= matchers_list
; l
!= NULL
;
712 matcherprop_free((MatcherProp
*)
714 g_slist_free(matchers_list
);
721 cond
= matcherlist_new(matchers_list
, main_bool_and
);
728 MatcherList
* matcherlist_new(GSList
* matchers
, gboolean bool_and
)
732 cond
= g_new0(MatcherList
, 1);
734 cond
->matchers
= matchers
;
735 cond
->bool_and
= bool_and
;
740 void matcherlist_free(MatcherList
* cond
)
744 for(l
= cond
->matchers
; l
!= NULL
; l
= g_slist_next(l
)) {
745 matcherprop_free((MatcherProp
*) l
->data
);
754 static void matcherlist_skip_headers(FILE *fp
)
758 while (procheader_get_one_field(buf
, sizeof(buf
), fp
, NULL
) != -1) {
763 matcherprop_match_one_header
764 returns TRUE if buf matchs the MatchersProp criteria
767 static gboolean
matcherprop_match_one_header(MatcherProp
* matcher
,
773 switch(matcher
->criteria
) {
774 case MATCHING_HEADER
:
775 case MATCHING_NOT_HEADER
:
776 header
= procheader_parse_header(buf
);
779 if (procheader_headername_equal(header
->name
,
781 if (matcher
->criteria
== MATCHING_HEADER
)
782 result
= matcherprop_string_match(matcher
, header
->body
);
784 result
= !matcherprop_string_match(matcher
, header
->body
);
785 procheader_header_free(header
);
789 procheader_header_free(header
);
792 case MATCHING_HEADERS_PART
:
793 case MATCHING_MESSAGE
:
794 return matcherprop_string_match(matcher
, buf
);
795 case MATCHING_NOT_MESSAGE
:
796 case MATCHING_NOT_HEADERS_PART
:
797 return !matcherprop_string_match(matcher
, buf
);
803 matcherprop_criteria_header
804 returns TRUE if the headers must be matched
807 static gboolean
matcherprop_criteria_headers(MatcherProp
* matcher
)
809 switch(matcher
->criteria
) {
810 case MATCHING_HEADER
:
811 case MATCHING_NOT_HEADER
:
812 case MATCHING_HEADERS_PART
:
813 case MATCHING_NOT_HEADERS_PART
:
820 static gboolean
matcherprop_criteria_message(MatcherProp
* matcher
)
822 switch(matcher
->criteria
) {
823 case MATCHING_MESSAGE
:
824 case MATCHING_NOT_MESSAGE
:
832 matcherlist_match_one_header
833 returns TRUE if match should stop
836 static gboolean
matcherlist_match_one_header(MatcherList
* matchers
,
841 for(l
= matchers
->matchers
; l
!= NULL
; l
= g_slist_next(l
)) {
842 MatcherProp
* matcher
= (MatcherProp
*) l
->data
;
844 if (matcherprop_criteria_headers(matcher
) ||
845 matcherprop_criteria_message(matcher
)) {
846 if (matcherprop_match_one_header(matcher
, buf
)) {
847 matcher
->result
= TRUE
;
851 if (matcherprop_criteria_headers(matcher
)) {
852 if (matcher
->result
) {
853 if (!matchers
->bool_and
)
863 matcherlist_match_headers
864 returns TRUE if one of the headers matchs the MatcherList criteria
867 static gboolean
matcherlist_match_headers(MatcherList
* matchers
, FILE * fp
)
871 while (procheader_get_one_field(buf
, sizeof(buf
), fp
, NULL
) != -1)
872 if (matcherlist_match_one_header(matchers
, buf
))
879 matcherprop_criteria_body
880 returns TRUE if the body must be matched
883 static gboolean
matcherprop_criteria_body(MatcherProp
* matcher
)
885 switch(matcher
->criteria
) {
886 case MATCHING_BODY_PART
:
887 case MATCHING_NOT_BODY_PART
:
895 matcherprop_match_line
896 returns TRUE if the string matchs the MatcherProp criteria
899 static gboolean
matcherprop_match_line(MatcherProp
* matcher
, gchar
* line
)
901 switch(matcher
->criteria
) {
902 case MATCHING_BODY_PART
:
903 case MATCHING_MESSAGE
:
904 return matcherprop_string_match(matcher
, line
);
905 case MATCHING_NOT_BODY_PART
:
906 case MATCHING_NOT_MESSAGE
:
907 return !matcherprop_string_match(matcher
, line
);
913 matcherlist_match_line
914 returns TRUE if the string matchs the MatcherList criteria
917 static gboolean
matcherlist_match_line(MatcherList
* matchers
, gchar
* line
)
921 for(l
= matchers
->matchers
; l
!= NULL
; l
= g_slist_next(l
)) {
922 MatcherProp
* matcher
= (MatcherProp
*) l
->data
;
924 if (matcherprop_criteria_body(matcher
) ||
925 matcherprop_criteria_message(matcher
)) {
926 if (matcherprop_match_line(matcher
, line
)) {
927 matcher
->result
= TRUE
;
931 if (matcher
->result
) {
932 if (!matchers
->bool_and
)
940 matcherlist_match_body
941 returns TRUE if one line of the body matchs the MatcherList criteria
944 static gboolean
matcherlist_match_body(MatcherList
* matchers
, FILE * fp
)
948 while (fgets(buf
, sizeof(buf
), fp
) != NULL
)
949 if (matcherlist_match_line(matchers
, buf
))
955 gboolean
matcherlist_match_file(MatcherList
* matchers
, MsgInfo
* info
,
958 gboolean read_headers
;
963 gboolean is_incorporating
= MSG_IS_FILTERING(info
->flags
);
965 /* check which things we need to do */
966 read_headers
= FALSE
;
969 for(l
= matchers
->matchers
; l
!= NULL
; l
= g_slist_next(l
)) {
970 MatcherProp
* matcher
= (MatcherProp
*) l
->data
;
972 if (matcherprop_criteria_headers(matcher
))
974 if (matcherprop_criteria_body(matcher
))
976 if (matcherprop_criteria_message(matcher
)) {
980 matcher
->result
= FALSE
;
983 if (!read_headers
&& !read_body
)
986 file
= is_incorporating
? g_strdup(info
->folder
)
987 : procmsg_get_message_file(info
);
992 if ((fp
= fopen(file
, "r")) == NULL
) {
993 FILE_OP_ERROR(file
, "fopen");
998 /* read the headers */
1001 if (matcherlist_match_headers(matchers
, fp
))
1005 matcherlist_skip_headers(fp
);
1010 matcherlist_match_body(matchers
, fp
);
1013 for(l
= matchers
->matchers
; l
!= NULL
; l
= g_slist_next(l
)) {
1014 MatcherProp
* matcher
= (MatcherProp
*) l
->data
;
1016 if (matcherprop_criteria_headers(matcher
) ||
1017 matcherprop_criteria_body(matcher
) ||
1018 matcherprop_criteria_message(matcher
))
1019 if (matcher
->result
) {
1020 if (!matchers
->bool_and
) {
1026 if (matchers
->bool_and
) {
1040 /* test a list of condition */
1042 gboolean
matcherlist_match(MatcherList
* matchers
, MsgInfo
* info
)
1047 if (matchers
->bool_and
)
1052 /* test the cached elements */
1054 for(l
= matchers
->matchers
; l
!= NULL
; l
= g_slist_next(l
)) {
1055 MatcherProp
* matcher
= (MatcherProp
*) l
->data
;
1057 switch(matcher
->criteria
) {
1059 case MATCHING_UNREAD
:
1060 case MATCHING_NOT_UNREAD
:
1062 case MATCHING_NOT_NEW
:
1063 case MATCHING_MARKED
:
1064 case MATCHING_NOT_MARKED
:
1065 case MATCHING_DELETED
:
1066 case MATCHING_NOT_DELETED
:
1067 case MATCHING_REPLIED
:
1068 case MATCHING_NOT_REPLIED
:
1069 case MATCHING_FORWARDED
:
1070 case MATCHING_NOT_FORWARDED
:
1071 case MATCHING_SUBJECT
:
1072 case MATCHING_NOT_SUBJECT
:
1074 case MATCHING_NOT_FROM
:
1076 case MATCHING_NOT_TO
:
1078 case MATCHING_NOT_CC
:
1079 case MATCHING_TO_OR_CC
:
1080 case MATCHING_NOT_TO_AND_NOT_CC
:
1081 case MATCHING_AGE_GREATER
:
1082 case MATCHING_AGE_LOWER
:
1083 case MATCHING_NEWSGROUPS
:
1084 case MATCHING_NOT_NEWSGROUPS
:
1085 case MATCHING_INREPLYTO
:
1086 case MATCHING_NOT_INREPLYTO
:
1087 case MATCHING_REFERENCES
:
1088 case MATCHING_NOT_REFERENCES
:
1089 case MATCHING_SCORE_GREATER
:
1090 case MATCHING_SCORE_LOWER
:
1091 case MATCHING_EXECUTE
:
1092 case MATCHING_NOT_EXECUTE
:
1093 if (matcherprop_match(matcher
, info
)) {
1094 if (!matchers
->bool_and
) {
1099 if (matchers
->bool_and
) {
1106 /* test the condition on the file */
1108 if (matcherlist_match_file(matchers
, info
, result
)) {
1109 if (!matchers
->bool_and
)
1113 if (matchers
->bool_and
)
1121 static void matcherprop_print(MatcherProp
* matcher
)
1125 if (matcher
== NULL
) {
1126 printf("no matcher\n");
1130 switch (matcher
->matchtype
) {
1131 case MATCHING_MATCH
:
1134 case MATCHING_REGEXP
:
1137 case MATCHING_MATCHCASE
:
1138 printf("matchcase\n");
1140 case MATCHING_REGEXPCASE
:
1141 printf("regexpcase\n");
1145 for(i
= 0 ; i
< (int) (sizeof(matchparser_tab
) / sizeof(MatchParser
)) ;
1147 if (matchparser_tab
[i
].id
== matcher
->criteria
)
1148 printf("%s\n", matchparser_tab
[i
].str
);
1152 printf("expr : %s\n", matcher
->expr
);
1154 printf("age: %i\n", matcher
->value
;
1156 printf("compiled : %s\n", matcher
->preg
!= NULL
? "yes" : "no");
1157 printf("error: %i\n", matcher
->error
);
1161 gchar
* matcherprop_to_string(MatcherProp
* matcher
)
1163 gchar
* matcher_str
= NULL
;
1164 gchar
* criteria_str
;
1165 gchar
* matchtype_str
;
1172 criteria_str
= NULL
;
1173 for(i
= 0 ; i
< (int) (sizeof(matchparser_tab
) / sizeof(MatchParser
)) ;
1175 if (matchparser_tab
[i
].id
== matcher
->criteria
)
1176 criteria_str
= matchparser_tab
[i
].str
;
1178 if (criteria_str
== NULL
)
1181 switch(matcher
->criteria
) {
1182 case MATCHING_AGE_GREATER
:
1183 case MATCHING_AGE_LOWER
:
1184 case MATCHING_SCORE_GREATER
:
1185 case MATCHING_SCORE_LOWER
:
1186 return g_strdup_printf("%s %i", criteria_str
, matcher
->value
);
1189 case MATCHING_UNREAD
:
1190 case MATCHING_NOT_UNREAD
:
1192 case MATCHING_NOT_NEW
:
1193 case MATCHING_MARKED
:
1194 case MATCHING_NOT_MARKED
:
1195 case MATCHING_DELETED
:
1196 case MATCHING_NOT_DELETED
:
1197 case MATCHING_REPLIED
:
1198 case MATCHING_NOT_REPLIED
:
1199 case MATCHING_FORWARDED
:
1200 case MATCHING_NOT_FORWARDED
:
1201 return g_strdup(criteria_str
);
1204 matchtype_str
= NULL
;
1205 for(i
= 0 ; i
< (int) (sizeof(matchparser_tab
) / sizeof(MatchParser
)) ;
1207 if (matchparser_tab
[i
].id
== matcher
->matchtype
)
1208 matchtype_str
= matchparser_tab
[i
].str
;
1211 if (matchtype_str
== NULL
)
1214 switch (matcher
->matchtype
) {
1215 case MATCHING_MATCH
:
1216 case MATCHING_MATCHCASE
:
1218 for(p
= matcher
->expr
; *p
!= 0 ; p
++)
1219 if (*p
== '\"') count
++;
1221 expr_str
= g_new(char, strlen(matcher
->expr
) + count
+ 1);
1223 for(p
= matcher
->expr
, out
= expr_str
; *p
!= 0 ; p
++, out
++) {
1233 if (matcher
->header
)
1235 g_strdup_printf("%s \"%s\" %s \"%s\"",
1236 criteria_str
, matcher
->header
,
1237 matchtype_str
, expr_str
);
1240 g_strdup_printf("%s %s \"%s\"", criteria_str
,
1241 matchtype_str
, expr_str
);
1247 case MATCHING_REGEXP
:
1248 case MATCHING_REGEXPCASE
:
1250 if (matcher
->header
)
1252 g_strdup_printf("%s \"%s\" %s /%s/",
1253 criteria_str
, matcher
->header
,
1254 matchtype_str
, matcher
->expr
);
1257 g_strdup_printf("%s %s /%s/", criteria_str
,
1258 matchtype_str
, matcher
->expr
);
1266 gchar
* matcherlist_to_string(MatcherList
* matchers
)
1274 count
= g_slist_length(matchers
->matchers
);
1275 vstr
= g_new(gchar
*, count
+ 1);
1277 for (l
= matchers
->matchers
, cur_str
= vstr
; l
!= NULL
;
1278 l
= g_slist_next(l
), cur_str
++) {
1279 *cur_str
= matcherprop_to_string((MatcherProp
*) l
->data
);
1280 if (*cur_str
== NULL
)
1285 if (matchers
->bool_and
)
1286 result
= g_strjoinv(" & ", vstr
);
1288 result
= g_strjoinv(" | ", vstr
);
1290 for(cur_str
= vstr
; *cur_str
!= NULL
; cur_str
++)
1297 static inline gint
strlen_with_check(const gchar
*expr
, gint fline
, const gchar
*str
)
1302 debug_print("%s(%d) - invalid string %s\n", __FILE__
, fline
, expr
);
1307 #define STRLEN_WITH_CHECK(expr) \
1308 strlen_with_check(#expr, __LINE__, expr)
1310 gchar
* matching_build_command(gchar
* cmd
, MsgInfo
* info
)
1313 gchar
* filename
= NULL
;
1314 gchar
* processed_cmd
;
1318 matcher_unescape_str(cmd
);
1320 size
= strlen(cmd
) + 1;
1321 while (*s
!= '\0') {
1328 case 's': /* subject */
1329 size
+= STRLEN_WITH_CHECK(info
->subject
) - 2;
1331 case 'f': /* from */
1332 size
+= STRLEN_WITH_CHECK(info
->from
) - 2;
1335 size
+= STRLEN_WITH_CHECK(info
->to
) - 2;
1338 size
+= STRLEN_WITH_CHECK(info
->cc
) - 2;
1340 case 'd': /* date */
1341 size
+= STRLEN_WITH_CHECK(info
->date
) - 2;
1343 case 'i': /* message-id */
1344 size
+= STRLEN_WITH_CHECK(info
->msgid
) - 2;
1346 case 'n': /* newsgroups */
1347 size
+= STRLEN_WITH_CHECK(info
->newsgroups
) - 2;
1349 case 'r': /* references */
1350 size
+= STRLEN_WITH_CHECK(info
->references
) - 2;
1352 case 'F': /* file */
1353 if (MSG_IS_FILTERING(info
->flags
))
1354 filename
= g_strdup(info
->folder
);
1356 filename
= folder_item_fetch_msg(info
->folder
, info
->msgnum
);
1358 if (filename
== NULL
) {
1359 debug_print(_("%s(%d) - filename is not set"), __FILE__
, __LINE__
);
1363 size
+= strlen(filename
) - 2;
1371 processed_cmd
= g_new0(gchar
, size
);
1375 while (*s
!= '\0') {
1383 case 's': /* subject */
1384 if (info
->subject
!= NULL
)
1385 strcpy(p
, info
->subject
);
1387 strcpy(p
, "(none)");
1390 case 'f': /* from */
1391 if (info
->from
!= NULL
)
1392 strcpy(p
, info
->from
);
1394 strcpy(p
, "(none)");
1398 if (info
->to
!= NULL
)
1399 strcpy(p
, info
->to
);
1401 strcpy(p
, "(none)");
1405 if (info
->cc
!= NULL
)
1406 strcpy(p
, info
->cc
);
1408 strcpy(p
, "(none)");
1411 case 'd': /* date */
1412 if (info
->date
!= NULL
)
1413 strcpy(p
, info
->date
);
1415 strcpy(p
, "(none)");
1418 case 'i': /* message-id */
1419 if (info
->msgid
!= NULL
)
1420 strcpy(p
, info
->msgid
);
1422 strcpy(p
, "(none)");
1425 case 'n': /* newsgroups */
1426 if (info
->newsgroups
!= NULL
)
1427 strcpy(p
, info
->newsgroups
);
1429 strcpy(p
, "(none)");
1432 case 'r': /* references */
1433 if (info
->references
!= NULL
)
1434 strcpy(p
, info
->references
);
1436 strcpy(p
, "(none)");
1439 case 'F': /* file */
1440 strcpy(p
, filename
);
1459 debug_print("*** exec string \"%s\"\n", processed_cmd
);
1462 return processed_cmd
;
1466 /* ************************************************************ */
1469 static void matcher_parse (gchar * str)
1471 matcher_parser_scan_string(str);
1472 matcher_parserparse();