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"},
82 {MATCHING_SCORE_EQUAL, "score_equal"},
84 /* content have to be read */
85 {MATCHING_HEADER, "header"},
86 {MATCHING_NOT_HEADER, "~header"},
87 {MATCHING_HEADERS_PART, "headers_part"},
88 {MATCHING_NOT_HEADERS_PART, "~headers_part"},
89 {MATCHING_MESSAGE, "message"},
90 {MATCHING_NOT_MESSAGE, "~message"},
91 {MATCHING_BODY_PART, "body_part"},
92 {MATCHING_NOT_BODY_PART, "~body_part"},
93 {MATCHING_EXECUTE, "execute"},
94 {MATCHING_NOT_EXECUTE, "~execute"},
97 {MATCHING_MATCHCASE, "matchcase"},
98 {MATCHING_MATCH, "match"},
99 {MATCHING_REGEXPCASE, "regexpcase"},
100 {MATCHING_REGEXP, "regexp"},
103 {MATCHING_SCORE, "score"},
106 {MATCHING_ACTION_MOVE, "move"},
107 {MATCHING_ACTION_COPY, "copy"},
108 {MATCHING_ACTION_DELETE, "delete"},
109 {MATCHING_ACTION_MARK, "mark"},
110 {MATCHING_ACTION_UNMARK, "unmark"},
111 {MATCHING_ACTION_MARK_AS_READ, "mark_as_read"},
112 {MATCHING_ACTION_MARK_AS_UNREAD, "mark_as_unread"},
113 {MATCHING_ACTION_FORWARD, "forward"},
114 {MATCHING_ACTION_FORWARD_AS_ATTACHMENT, "forward_as_attachment"},
115 {MATCHING_ACTION_COLOR, "color"}
116 /* {MATCHING_EXECUTE, "execute"}, */
119 gchar * get_matchparser_tab_str(gint id)
123 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
125 if (matchparser_tab[i].id == id)
126 return matchparser_tab[i].str;
136 header "x-mailing" match "toto"
137 subject match "regexp" & to regexp "regexp"
138 subject match "regexp" | to regexpcase "regexp" | age_sup 5
141 static gboolean matcher_is_blank(gchar ch);
143 /* ******************* parser *********************** */
145 static gboolean matcher_is_blank(gchar ch)
147 return (ch == ' ') || (ch == '\t');
150 /* parse for one condition */
152 MatcherProp * matcherprop_parse(gchar ** str)
160 gchar * header = NULL;
163 key = matcher_parse_keyword(&tmp);
170 case MATCHING_AGE_LOWER:
171 case MATCHING_AGE_GREATER:
172 case MATCHING_SCORE_LOWER:
173 case MATCHING_SCORE_GREATER:
174 case MATCHING_SCORE_EQUAL:
175 value = matcher_parse_number(&tmp);
182 prop = matcherprop_new(key, NULL, 0, NULL, value);
187 case MATCHING_UNREAD:
188 case MATCHING_NOT_UNREAD:
190 case MATCHING_NOT_NEW:
191 case MATCHING_MARKED:
192 case MATCHING_NOT_MARKED:
193 case MATCHING_DELETED:
194 case MATCHING_NOT_DELETED:
195 case MATCHING_REPLIED:
196 case MATCHING_NOT_REPLIED:
197 case MATCHING_FORWARDED:
198 case MATCHING_NOT_FORWARDED:
199 prop = matcherprop_new(key, NULL, 0, NULL, 0);
204 case MATCHING_SUBJECT:
205 case MATCHING_NOT_SUBJECT:
207 case MATCHING_NOT_FROM:
209 case MATCHING_NOT_TO:
211 case MATCHING_NOT_CC:
212 case MATCHING_TO_OR_CC:
213 case MATCHING_NOT_TO_AND_NOT_CC:
214 case MATCHING_NEWSGROUPS:
215 case MATCHING_NOT_NEWSGROUPS:
216 case MATCHING_INREPLYTO:
217 case MATCHING_NOT_REFERENCES:
218 case MATCHING_REFERENCES:
219 case MATCHING_NOT_INREPLYTO:
220 case MATCHING_MESSAGE:
221 case MATCHING_NOT_MESSAGE:
222 case MATCHING_EXECUTE:
223 case MATCHING_NOT_EXECUTE:
224 case MATCHING_HEADERS_PART:
225 case MATCHING_NOT_HEADERS_PART:
226 case MATCHING_BODY_PART:
227 case MATCHING_NOT_BODY_PART:
228 case MATCHING_HEADER:
229 case MATCHING_NOT_HEADER:
230 if ((key == MATCHING_HEADER) || (key == MATCHING_NOT_HEADER)) {
231 header = matcher_parse_str(&tmp);
238 match = matcher_parse_keyword(&tmp);
247 case MATCHING_REGEXP:
248 case MATCHING_REGEXPCASE:
249 expr = matcher_parse_regexp(&tmp);
257 prop = matcherprop_new(key, header, match, expr, 0);
262 case MATCHING_MATCHCASE:
263 expr = matcher_parse_str(&tmp);
271 prop = matcherprop_new(key, header, match, expr, 0);
287 gint matcher_parse_keyword(gchar ** str)
295 dup = alloca(strlen(* str) + 1);
299 while (matcher_is_blank(*p))
304 while (!matcher_is_blank(*p) && (*p != '\0'))
308 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
310 if ((strlen(matchparser_tab[i].str) == p - start) &&
311 (strncasecmp(matchparser_tab[i].str, start,
326 return matchparser_tab[match].id;
329 gint matcher_parse_number(gchar ** str)
335 dup = alloca(strlen(* str) + 1);
339 while (matcher_is_blank(*p))
344 if (!isdigit(*p) && *p != '-' && *p != '+') {
348 if (*p == '-' || *p == '+')
359 gboolean matcher_parse_boolean_op(gchar ** str)
365 while (matcher_is_blank(*p))
369 *str += p - * str + 1;
372 else if (*p == '&') {
373 *str += p - * str + 1;
382 gchar * matcher_parse_regexp(gchar ** str)
388 dup = alloca(strlen(* str) + 1);
392 while (matcher_is_blank(*p))
410 return g_strdup(start);
413 /* matcher_parse_str() - parses a string until it hits a
414 * terminating \". to unescape characters use \. The string
415 * should not be used directly: matcher_unescape_str()
416 * returns a string that can be used directly. */
417 gchar * matcher_parse_str(gchar ** str)
424 dup = alloca(strlen(* str) + 1);
428 while (matcher_is_blank(*p))
440 for ( ; *p && *p != '\"'; p++, dest++) {
450 *str += dest - dup + 2;
451 return g_strdup(start);
454 gchar *matcher_unescape_str(gchar *str)
456 gchar *tmp = alloca(strlen(str) + 1);
457 register gchar *src = tmp;
458 register gchar *dst = str;
462 for ( ; *src; src++) {
469 else if (*src == 'n')
471 else if (*src == 'r')
473 else if (*src == '\'' || *src == '\"')
485 /* **************** data structure allocation **************** */
488 MatcherProp * matcherprop_new(gint criteria, gchar * header,
489 gint matchtype, gchar * expr,
494 prop = g_new0(MatcherProp, 1);
495 prop->criteria = criteria;
497 prop->header = g_strdup(header);
501 prop->expr = g_strdup(expr);
504 prop->matchtype = matchtype;
512 void matcherprop_free(MatcherProp * prop)
515 if (prop->preg != NULL) {
523 /* ************** match ******************************/
526 /* match the given string */
528 static gboolean matcherprop_string_match(MatcherProp * prop, gchar * str)
536 switch(prop->matchtype) {
537 case MATCHING_REGEXPCASE:
538 case MATCHING_REGEXP:
539 if (!prop->preg && (prop->error == 0)) {
540 prop->preg = g_new0(regex_t, 1);
541 if (regcomp(prop->preg, prop->expr,
542 REG_NOSUB | REG_EXTENDED
543 | ((prop->matchtype == MATCHING_REGEXPCASE)
544 ? REG_ICASE : 0)) != 0) {
549 if (prop->preg == NULL)
552 if (regexec(prop->preg, str, 0, NULL, 0) == 0)
558 return (strstr(str, prop->expr) != NULL);
560 case MATCHING_MATCHCASE:
561 str2 = alloca(strlen(prop->expr) + 1);
562 strcpy(str2, prop->expr);
564 str1 = alloca(strlen(str) + 1);
567 return (strstr(str1, str2) != NULL);
574 gboolean matcherprop_match_execute(MatcherProp * prop, MsgInfo * info)
578 cmd = matching_build_command(prop->expr, info);
582 return (system(cmd) == 0);
585 /* match a message and his headers, hlist can be NULL if you don't
586 want to use headers */
588 gboolean matcherprop_match(MatcherProp * prop, MsgInfo * info)
592 switch(prop->criteria) {
595 case MATCHING_UNREAD:
596 return MSG_IS_UNREAD(info->flags);
597 case MATCHING_NOT_UNREAD:
598 return !MSG_IS_UNREAD(info->flags);
600 return MSG_IS_NEW(info->flags);
601 case MATCHING_NOT_NEW:
602 return !MSG_IS_NEW(info->flags);
603 case MATCHING_MARKED:
604 return MSG_IS_MARKED(info->flags);
605 case MATCHING_NOT_MARKED:
606 return !MSG_IS_MARKED(info->flags);
607 case MATCHING_DELETED:
608 return MSG_IS_DELETED(info->flags);
609 case MATCHING_NOT_DELETED:
610 return !MSG_IS_DELETED(info->flags);
611 case MATCHING_REPLIED:
612 return MSG_IS_REPLIED(info->flags);
613 case MATCHING_NOT_REPLIED:
614 return !MSG_IS_REPLIED(info->flags);
615 case MATCHING_FORWARDED:
616 return MSG_IS_FORWARDED(info->flags);
617 case MATCHING_NOT_FORWARDED:
618 return !MSG_IS_FORWARDED(info->flags);
619 case MATCHING_SUBJECT:
620 return matcherprop_string_match(prop, info->subject);
621 case MATCHING_NOT_SUBJECT:
622 return !matcherprop_string_match(prop, info->subject);
624 return matcherprop_string_match(prop, info->from);
625 case MATCHING_NOT_FROM:
626 return !matcherprop_string_match(prop, info->from);
628 return matcherprop_string_match(prop, info->to);
629 case MATCHING_NOT_TO:
630 return !matcherprop_string_match(prop, info->to);
632 return matcherprop_string_match(prop, info->cc);
633 case MATCHING_NOT_CC:
634 return !matcherprop_string_match(prop, info->cc);
635 case MATCHING_TO_OR_CC:
636 return matcherprop_string_match(prop, info->to)
637 || matcherprop_string_match(prop, info->cc);
638 case MATCHING_NOT_TO_AND_NOT_CC:
639 return !(matcherprop_string_match(prop, info->to)
640 || matcherprop_string_match(prop, info->cc));
641 case MATCHING_AGE_GREATER:
643 return ((t - info->date_t) / (60 * 60 * 24)) >= prop->value;
644 case MATCHING_AGE_LOWER:
646 return ((t - info->date_t) / (60 * 60 * 24)) <= prop->value;
647 case MATCHING_SCORE_GREATER:
648 return info->score >= prop->value;
649 case MATCHING_SCORE_LOWER:
650 return info->score <= prop->value;
651 case MATCHING_SCORE_EQUAL:
652 return info->score == prop->value;
653 case MATCHING_NEWSGROUPS:
654 return matcherprop_string_match(prop, info->newsgroups);
655 case MATCHING_NOT_NEWSGROUPS:
656 return !matcherprop_string_match(prop, info->newsgroups);
657 case MATCHING_INREPLYTO:
658 return matcherprop_string_match(prop, info->inreplyto);
659 case MATCHING_NOT_INREPLYTO:
660 return !matcherprop_string_match(prop, info->inreplyto);
661 case MATCHING_REFERENCES:
662 return matcherprop_string_match(prop, info->references);
663 case MATCHING_NOT_REFERENCES:
664 return !matcherprop_string_match(prop, info->references);
665 case MATCHING_EXECUTE:
666 return matcherprop_match_execute(prop, info);
667 case MATCHING_NOT_EXECUTE:
668 return !matcherprop_match_execute(prop, info);
674 /* ********************* MatcherList *************************** */
677 /* parse for a list of conditions */
679 MatcherList * matcherlist_parse(gchar ** str)
682 MatcherProp * matcher;
683 GSList * matchers_list = NULL;
684 gboolean bool_and = TRUE;
687 gboolean main_bool_and = TRUE;
692 matcher = matcherprop_parse(&tmp);
698 matchers_list = g_slist_append(matchers_list, matcher);
701 bool_and = matcher_parse_boolean_op(&tmp);
707 main_bool_and = bool_and;
708 matcher = matcherprop_parse(&tmp);
711 g_slist_append(matchers_list, matcher);
714 for(l = matchers_list ; l != NULL ;
716 matcherprop_free((MatcherProp *)
718 g_slist_free(matchers_list);
725 cond = matcherlist_new(matchers_list, main_bool_and);
732 MatcherList * matcherlist_new(GSList * matchers, gboolean bool_and)
736 cond = g_new0(MatcherList, 1);
738 cond->matchers = matchers;
739 cond->bool_and = bool_and;
744 void matcherlist_free(MatcherList * cond)
748 for(l = cond->matchers ; l != NULL ; l = g_slist_next(l)) {
749 matcherprop_free((MatcherProp *) l->data);
758 static void matcherlist_skip_headers(FILE *fp)
762 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
767 matcherprop_match_one_header
768 returns TRUE if buf matchs the MatchersProp criteria
771 static gboolean matcherprop_match_one_header(MatcherProp * matcher,
777 switch(matcher->criteria) {
778 case MATCHING_HEADER:
779 case MATCHING_NOT_HEADER:
780 header = procheader_parse_header(buf);
783 if (procheader_headername_equal(header->name,
785 if (matcher->criteria == MATCHING_HEADER)
786 result = matcherprop_string_match(matcher, header->body);
788 result = !matcherprop_string_match(matcher, header->body);
789 procheader_header_free(header);
793 procheader_header_free(header);
796 case MATCHING_HEADERS_PART:
797 case MATCHING_MESSAGE:
798 return matcherprop_string_match(matcher, buf);
799 case MATCHING_NOT_MESSAGE:
800 case MATCHING_NOT_HEADERS_PART:
801 return !matcherprop_string_match(matcher, buf);
807 matcherprop_criteria_header
808 returns TRUE if the headers must be matched
811 static gboolean matcherprop_criteria_headers(MatcherProp * matcher)
813 switch(matcher->criteria) {
814 case MATCHING_HEADER:
815 case MATCHING_NOT_HEADER:
816 case MATCHING_HEADERS_PART:
817 case MATCHING_NOT_HEADERS_PART:
824 static gboolean matcherprop_criteria_message(MatcherProp * matcher)
826 switch(matcher->criteria) {
827 case MATCHING_MESSAGE:
828 case MATCHING_NOT_MESSAGE:
836 matcherlist_match_one_header
837 returns TRUE if match should stop
840 static gboolean matcherlist_match_one_header(MatcherList * matchers,
845 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
846 MatcherProp * matcher = (MatcherProp *) l->data;
848 if (matcherprop_criteria_headers(matcher) ||
849 matcherprop_criteria_message(matcher)) {
850 if (matcherprop_match_one_header(matcher, buf)) {
851 matcher->result = TRUE;
855 if (matcherprop_criteria_headers(matcher)) {
856 if (matcher->result) {
857 if (!matchers->bool_and)
867 matcherlist_match_headers
868 returns TRUE if one of the headers matchs the MatcherList criteria
871 static gboolean matcherlist_match_headers(MatcherList * matchers, FILE * fp)
875 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1)
876 if (matcherlist_match_one_header(matchers, buf))
883 matcherprop_criteria_body
884 returns TRUE if the body must be matched
887 static gboolean matcherprop_criteria_body(MatcherProp * matcher)
889 switch(matcher->criteria) {
890 case MATCHING_BODY_PART:
891 case MATCHING_NOT_BODY_PART:
899 matcherprop_match_line
900 returns TRUE if the string matchs the MatcherProp criteria
903 static gboolean matcherprop_match_line(MatcherProp * matcher, gchar * line)
905 switch(matcher->criteria) {
906 case MATCHING_BODY_PART:
907 case MATCHING_MESSAGE:
908 return matcherprop_string_match(matcher, line);
909 case MATCHING_NOT_BODY_PART:
910 case MATCHING_NOT_MESSAGE:
911 return !matcherprop_string_match(matcher, line);
917 matcherlist_match_line
918 returns TRUE if the string matchs the MatcherList criteria
921 static gboolean matcherlist_match_line(MatcherList * matchers, gchar * line)
925 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
926 MatcherProp * matcher = (MatcherProp *) l->data;
928 if (matcherprop_criteria_body(matcher) ||
929 matcherprop_criteria_message(matcher)) {
930 if (matcherprop_match_line(matcher, line)) {
931 matcher->result = TRUE;
935 if (matcher->result) {
936 if (!matchers->bool_and)
944 matcherlist_match_body
945 returns TRUE if one line of the body matchs the MatcherList criteria
948 static gboolean matcherlist_match_body(MatcherList * matchers, FILE * fp)
952 while (fgets(buf, sizeof(buf), fp) != NULL)
953 if (matcherlist_match_line(matchers, buf))
959 gboolean matcherlist_match_file(MatcherList * matchers, MsgInfo * info,
962 gboolean read_headers;
967 gboolean is_incorporating = MSG_IS_FILTERING(info->flags);
969 /* check which things we need to do */
970 read_headers = FALSE;
973 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
974 MatcherProp * matcher = (MatcherProp *) l->data;
976 if (matcherprop_criteria_headers(matcher))
978 if (matcherprop_criteria_body(matcher))
980 if (matcherprop_criteria_message(matcher)) {
984 matcher->result = FALSE;
987 if (!read_headers && !read_body)
990 file = is_incorporating ? g_strdup(info->folder)
991 : procmsg_get_message_file(info);
996 if ((fp = fopen(file, "r")) == NULL) {
997 FILE_OP_ERROR(file, "fopen");
1002 /* read the headers */
1005 if (matcherlist_match_headers(matchers, fp))
1009 matcherlist_skip_headers(fp);
1014 matcherlist_match_body(matchers, fp);
1017 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
1018 MatcherProp * matcher = (MatcherProp *) l->data;
1020 if (matcherprop_criteria_headers(matcher) ||
1021 matcherprop_criteria_body(matcher) ||
1022 matcherprop_criteria_message(matcher))
1023 if (matcher->result) {
1024 if (!matchers->bool_and) {
1030 if (matchers->bool_and) {
1044 /* test a list of condition */
1046 gboolean matcherlist_match(MatcherList * matchers, MsgInfo * info)
1051 if (matchers->bool_and)
1056 /* test the cached elements */
1058 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
1059 MatcherProp * matcher = (MatcherProp *) l->data;
1061 switch(matcher->criteria) {
1063 case MATCHING_UNREAD:
1064 case MATCHING_NOT_UNREAD:
1066 case MATCHING_NOT_NEW:
1067 case MATCHING_MARKED:
1068 case MATCHING_NOT_MARKED:
1069 case MATCHING_DELETED:
1070 case MATCHING_NOT_DELETED:
1071 case MATCHING_REPLIED:
1072 case MATCHING_NOT_REPLIED:
1073 case MATCHING_FORWARDED:
1074 case MATCHING_NOT_FORWARDED:
1075 case MATCHING_SUBJECT:
1076 case MATCHING_NOT_SUBJECT:
1078 case MATCHING_NOT_FROM:
1080 case MATCHING_NOT_TO:
1082 case MATCHING_NOT_CC:
1083 case MATCHING_TO_OR_CC:
1084 case MATCHING_NOT_TO_AND_NOT_CC:
1085 case MATCHING_AGE_GREATER:
1086 case MATCHING_AGE_LOWER:
1087 case MATCHING_NEWSGROUPS:
1088 case MATCHING_NOT_NEWSGROUPS:
1089 case MATCHING_INREPLYTO:
1090 case MATCHING_NOT_INREPLYTO:
1091 case MATCHING_REFERENCES:
1092 case MATCHING_NOT_REFERENCES:
1093 case MATCHING_SCORE_GREATER:
1094 case MATCHING_SCORE_LOWER:
1095 case MATCHING_SCORE_EQUAL:
1096 case MATCHING_EXECUTE:
1097 case MATCHING_NOT_EXECUTE:
1098 if (matcherprop_match(matcher, info)) {
1099 if (!matchers->bool_and) {
1104 if (matchers->bool_and) {
1111 /* test the condition on the file */
1113 if (matcherlist_match_file(matchers, info, result)) {
1114 if (!matchers->bool_and)
1118 if (matchers->bool_and)
1126 static void matcherprop_print(MatcherProp * matcher)
1130 if (matcher == NULL) {
1131 printf("no matcher\n");
1135 switch (matcher->matchtype) {
1136 case MATCHING_MATCH:
1139 case MATCHING_REGEXP:
1142 case MATCHING_MATCHCASE:
1143 printf("matchcase\n");
1145 case MATCHING_REGEXPCASE:
1146 printf("regexpcase\n");
1150 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
1152 if (matchparser_tab[i].id == matcher->criteria)
1153 printf("%s\n", matchparser_tab[i].str);
1157 printf("expr : %s\n", matcher->expr);
1159 printf("age: %i\n", matcher->value;
1161 printf("compiled : %s\n", matcher->preg != NULL ? "yes" : "no");
1162 printf("error: %i\n", matcher->error);
1166 gchar * matcherprop_to_string(MatcherProp * matcher)
1168 gchar * matcher_str = NULL;
1169 gchar * criteria_str;
1170 gchar * matchtype_str;
1177 criteria_str = NULL;
1178 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
1180 if (matchparser_tab[i].id == matcher->criteria)
1181 criteria_str = matchparser_tab[i].str;
1183 if (criteria_str == NULL)
1186 switch(matcher->criteria) {
1187 case MATCHING_AGE_GREATER:
1188 case MATCHING_AGE_LOWER:
1189 case MATCHING_SCORE_GREATER:
1190 case MATCHING_SCORE_LOWER:
1191 case MATCHING_SCORE_EQUAL:
1192 return g_strdup_printf("%s %i", criteria_str, matcher->value);
1195 case MATCHING_UNREAD:
1196 case MATCHING_NOT_UNREAD:
1198 case MATCHING_NOT_NEW:
1199 case MATCHING_MARKED:
1200 case MATCHING_NOT_MARKED:
1201 case MATCHING_DELETED:
1202 case MATCHING_NOT_DELETED:
1203 case MATCHING_REPLIED:
1204 case MATCHING_NOT_REPLIED:
1205 case MATCHING_FORWARDED:
1206 case MATCHING_NOT_FORWARDED:
1207 return g_strdup(criteria_str);
1210 matchtype_str = NULL;
1211 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
1213 if (matchparser_tab[i].id == matcher->matchtype)
1214 matchtype_str = matchparser_tab[i].str;
1217 if (matchtype_str == NULL)
1220 switch (matcher->matchtype) {
1221 case MATCHING_MATCH:
1222 case MATCHING_MATCHCASE:
1224 for(p = matcher->expr; *p != 0 ; p++)
1225 if (*p == '\"') count ++;
1227 expr_str = g_new(char, strlen(matcher->expr) + count + 1);
1229 for(p = matcher->expr, out = expr_str ; *p != 0 ; p++, out++) {
1239 if (matcher->header)
1241 g_strdup_printf("%s \"%s\" %s \"%s\"",
1242 criteria_str, matcher->header,
1243 matchtype_str, expr_str);
1246 g_strdup_printf("%s %s \"%s\"", criteria_str,
1247 matchtype_str, expr_str);
1253 case MATCHING_REGEXP:
1254 case MATCHING_REGEXPCASE:
1256 if (matcher->header)
1258 g_strdup_printf("%s \"%s\" %s /%s/",
1259 criteria_str, matcher->header,
1260 matchtype_str, matcher->expr);
1263 g_strdup_printf("%s %s /%s/", criteria_str,
1264 matchtype_str, matcher->expr);
1272 gchar * matcherlist_to_string(MatcherList * matchers)
1280 count = g_slist_length(matchers->matchers);
1281 vstr = g_new(gchar *, count + 1);
1283 for (l = matchers->matchers, cur_str = vstr ; l != NULL ;
1284 l = g_slist_next(l), cur_str ++) {
1285 *cur_str = matcherprop_to_string((MatcherProp *) l->data);
1286 if (*cur_str == NULL)
1291 if (matchers->bool_and)
1292 result = g_strjoinv(" & ", vstr);
1294 result = g_strjoinv(" | ", vstr);
1296 for(cur_str = vstr ; *cur_str != NULL ; cur_str ++)
1303 static inline gint strlen_with_check(const gchar *expr, gint fline, const gchar *str)
1308 debug_print("%s(%d) - invalid string %s\n", __FILE__, fline, expr);
1313 #define STRLEN_WITH_CHECK(expr) \
1314 strlen_with_check(#expr, __LINE__, expr)
1316 gchar * matching_build_command(gchar * cmd, MsgInfo * info)
1319 gchar * filename = NULL;
1320 gchar * processed_cmd;
1324 matcher_unescape_str(cmd);
1326 size = strlen(cmd) + 1;
1327 while (*s != '\0') {
1334 case 's': /* subject */
1335 size += STRLEN_WITH_CHECK(info->subject) - 2;
1337 case 'f': /* from */
1338 size += STRLEN_WITH_CHECK(info->from) - 2;
1341 size += STRLEN_WITH_CHECK(info->to) - 2;
1344 size += STRLEN_WITH_CHECK(info->cc) - 2;
1346 case 'd': /* date */
1347 size += STRLEN_WITH_CHECK(info->date) - 2;
1349 case 'i': /* message-id */
1350 size += STRLEN_WITH_CHECK(info->msgid) - 2;
1352 case 'n': /* newsgroups */
1353 size += STRLEN_WITH_CHECK(info->newsgroups) - 2;
1355 case 'r': /* references */
1356 size += STRLEN_WITH_CHECK(info->references) - 2;
1358 case 'F': /* file */
1359 if (MSG_IS_FILTERING(info->flags))
1360 filename = g_strdup(info->folder);
1362 filename = folder_item_fetch_msg(info->folder, info->msgnum);
1364 if (filename == NULL) {
1365 debug_print(_("%s(%d) - filename is not set"), __FILE__, __LINE__);
1369 size += strlen(filename) - 2;
1377 processed_cmd = g_new0(gchar, size);
1381 while (*s != '\0') {
1389 case 's': /* subject */
1390 if (info->subject != NULL)
1391 strcpy(p, info->subject);
1393 strcpy(p, "(none)");
1396 case 'f': /* from */
1397 if (info->from != NULL)
1398 strcpy(p, info->from);
1400 strcpy(p, "(none)");
1404 if (info->to != NULL)
1405 strcpy(p, info->to);
1407 strcpy(p, "(none)");
1411 if (info->cc != NULL)
1412 strcpy(p, info->cc);
1414 strcpy(p, "(none)");
1417 case 'd': /* date */
1418 if (info->date != NULL)
1419 strcpy(p, info->date);
1421 strcpy(p, "(none)");
1424 case 'i': /* message-id */
1425 if (info->msgid != NULL)
1426 strcpy(p, info->msgid);
1428 strcpy(p, "(none)");
1431 case 'n': /* newsgroups */
1432 if (info->newsgroups != NULL)
1433 strcpy(p, info->newsgroups);
1435 strcpy(p, "(none)");
1438 case 'r': /* references */
1439 if (info->references != NULL)
1440 strcpy(p, info->references);
1442 strcpy(p, "(none)");
1445 case 'F': /* file */
1446 strcpy(p, filename);
1465 debug_print("*** exec string \"%s\"\n", processed_cmd);
1468 return processed_cmd;
1472 /* ************************************************************ */
1475 static void matcher_parse (gchar * str)
1477 matcher_parser_scan_string(str);
1478 matcher_parserparse();