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_EXECUTE, "execute"}, */
117 gchar * get_matchparser_tab_str(gint id)
121 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
123 if (matchparser_tab[i].id == id)
124 return matchparser_tab[i].str;
134 header "x-mailing" match "toto"
135 subject match "regexp" & to regexp "regexp"
136 subject match "regexp" | to regexpcase "regexp" | age_sup 5
139 static gboolean matcher_is_blank(gchar ch);
141 /* ******************* parser *********************** */
143 static gboolean matcher_is_blank(gchar ch)
145 return (ch == ' ') || (ch == '\t');
148 /* parse for one condition */
150 MatcherProp * matcherprop_parse(gchar ** str)
158 gchar * header = NULL;
161 key = matcher_parse_keyword(&tmp);
168 case MATCHING_AGE_LOWER:
169 case MATCHING_AGE_GREATER:
170 case MATCHING_SCORE_LOWER:
171 case MATCHING_SCORE_GREATER:
172 value = matcher_parse_number(&tmp);
179 prop = matcherprop_new(key, NULL, 0, NULL, value);
184 case MATCHING_UNREAD:
185 case MATCHING_NOT_UNREAD:
187 case MATCHING_NOT_NEW:
188 case MATCHING_MARKED:
189 case MATCHING_NOT_MARKED:
190 case MATCHING_DELETED:
191 case MATCHING_NOT_DELETED:
192 case MATCHING_REPLIED:
193 case MATCHING_NOT_REPLIED:
194 case MATCHING_FORWARDED:
195 case MATCHING_NOT_FORWARDED:
196 prop = matcherprop_new(key, NULL, 0, NULL, 0);
201 case MATCHING_SUBJECT:
202 case MATCHING_NOT_SUBJECT:
204 case MATCHING_NOT_FROM:
206 case MATCHING_NOT_TO:
208 case MATCHING_NOT_CC:
209 case MATCHING_TO_OR_CC:
210 case MATCHING_NOT_TO_AND_NOT_CC:
211 case MATCHING_NEWSGROUPS:
212 case MATCHING_NOT_NEWSGROUPS:
213 case MATCHING_INREPLYTO:
214 case MATCHING_NOT_REFERENCES:
215 case MATCHING_REFERENCES:
216 case MATCHING_NOT_INREPLYTO:
217 case MATCHING_MESSAGE:
218 case MATCHING_NOT_MESSAGE:
219 case MATCHING_EXECUTE:
220 case MATCHING_NOT_EXECUTE:
221 case MATCHING_HEADERS_PART:
222 case MATCHING_NOT_HEADERS_PART:
223 case MATCHING_BODY_PART:
224 case MATCHING_NOT_BODY_PART:
225 case MATCHING_HEADER:
226 case MATCHING_NOT_HEADER:
227 if ((key == MATCHING_HEADER) || (key == MATCHING_NOT_HEADER)) {
228 header = matcher_parse_str(&tmp);
235 match = matcher_parse_keyword(&tmp);
244 case MATCHING_REGEXP:
245 case MATCHING_REGEXPCASE:
246 expr = matcher_parse_regexp(&tmp);
254 prop = matcherprop_new(key, header, match, expr, 0);
259 case MATCHING_MATCHCASE:
260 expr = matcher_parse_str(&tmp);
268 prop = matcherprop_new(key, header, match, expr, 0);
284 gint matcher_parse_keyword(gchar ** str)
292 dup = alloca(strlen(* str) + 1);
296 while (matcher_is_blank(*p))
301 while (!matcher_is_blank(*p) && (*p != '\0'))
305 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
307 if ((strlen(matchparser_tab[i].str) == p - start) &&
308 (strncasecmp(matchparser_tab[i].str, start,
323 return matchparser_tab[match].id;
326 gint matcher_parse_number(gchar ** str)
332 dup = alloca(strlen(* str) + 1);
336 while (matcher_is_blank(*p))
341 if (!isdigit(*p) && *p != '-' && *p != '+') {
345 if (*p == '-' || *p == '+')
356 gboolean matcher_parse_boolean_op(gchar ** str)
362 while (matcher_is_blank(*p))
366 *str += p - * str + 1;
369 else if (*p == '&') {
370 *str += p - * str + 1;
379 gchar * matcher_parse_regexp(gchar ** str)
385 dup = alloca(strlen(* str) + 1);
389 while (matcher_is_blank(*p))
407 return g_strdup(start);
410 gchar * matcher_parse_str(gchar ** str)
417 dup = alloca(strlen(* str) + 1);
421 while (matcher_is_blank(*p))
444 *str += dest - dup + 2;
445 return g_strdup(start);
448 /* **************** data structure allocation **************** */
451 MatcherProp * matcherprop_new(gint criteria, gchar * header,
452 gint matchtype, gchar * expr,
457 prop = g_new0(MatcherProp, 1);
458 prop->criteria = criteria;
460 prop->header = g_strdup(header);
464 prop->expr = g_strdup(expr);
467 prop->matchtype = matchtype;
475 void matcherprop_free(MatcherProp * prop)
478 if (prop->preg != NULL) {
486 /* ************** match ******************************/
489 /* match the given string */
491 static gboolean matcherprop_string_match(MatcherProp * prop, gchar * str)
499 switch(prop->matchtype) {
500 case MATCHING_REGEXPCASE:
501 case MATCHING_REGEXP:
502 if (!prop->preg && (prop->error == 0)) {
503 prop->preg = g_new0(regex_t, 1);
504 if (regcomp(prop->preg, prop->expr,
505 REG_NOSUB | REG_EXTENDED
506 | ((prop->matchtype == MATCHING_REGEXPCASE)
507 ? REG_ICASE : 0)) != 0) {
512 if (prop->preg == NULL)
515 if (regexec(prop->preg, str, 0, NULL, 0) == 0)
521 return (strstr(str, prop->expr) != NULL);
523 case MATCHING_MATCHCASE:
524 str2 = alloca(strlen(prop->expr) + 1);
525 strcpy(str2, prop->expr);
527 str1 = alloca(strlen(str) + 1);
530 return (strstr(str1, str2) != NULL);
537 gboolean matcherprop_match_execute(MatcherProp * prop, MsgInfo * info)
541 cmd = matching_build_command(prop->expr, info);
545 return (system(cmd) == 0);
548 /* match a message and his headers, hlist can be NULL if you don't
549 want to use headers */
551 gboolean matcherprop_match(MatcherProp * prop, MsgInfo * info)
555 switch(prop->criteria) {
558 case MATCHING_UNREAD:
559 return MSG_IS_UNREAD(info->flags);
560 case MATCHING_NOT_UNREAD:
561 return !MSG_IS_UNREAD(info->flags);
563 return MSG_IS_NEW(info->flags);
564 case MATCHING_NOT_NEW:
565 return !MSG_IS_NEW(info->flags);
566 case MATCHING_MARKED:
567 return MSG_IS_MARKED(info->flags);
568 case MATCHING_NOT_MARKED:
569 return !MSG_IS_MARKED(info->flags);
570 case MATCHING_DELETED:
571 return MSG_IS_DELETED(info->flags);
572 case MATCHING_NOT_DELETED:
573 return !MSG_IS_DELETED(info->flags);
574 case MATCHING_REPLIED:
575 return MSG_IS_REPLIED(info->flags);
576 case MATCHING_NOT_REPLIED:
577 return !MSG_IS_REPLIED(info->flags);
578 case MATCHING_FORWARDED:
579 return MSG_IS_FORWARDED(info->flags);
580 case MATCHING_NOT_FORWARDED:
581 return !MSG_IS_FORWARDED(info->flags);
582 case MATCHING_SUBJECT:
583 return matcherprop_string_match(prop, info->subject);
584 case MATCHING_NOT_SUBJECT:
585 return !matcherprop_string_match(prop, info->subject);
587 return matcherprop_string_match(prop, info->from);
588 case MATCHING_NOT_FROM:
589 return !matcherprop_string_match(prop, info->from);
591 return matcherprop_string_match(prop, info->to);
592 case MATCHING_NOT_TO:
593 return !matcherprop_string_match(prop, info->to);
595 return matcherprop_string_match(prop, info->cc);
596 case MATCHING_NOT_CC:
597 return !matcherprop_string_match(prop, info->cc);
598 case MATCHING_TO_OR_CC:
599 return matcherprop_string_match(prop, info->to)
600 || matcherprop_string_match(prop, info->cc);
601 case MATCHING_NOT_TO_AND_NOT_CC:
602 return !(matcherprop_string_match(prop, info->to)
603 || matcherprop_string_match(prop, info->cc));
604 case MATCHING_AGE_GREATER:
606 return ((t - info->date_t) / (60 * 60 * 24)) >= prop->value;
607 case MATCHING_AGE_LOWER:
609 return ((t - info->date_t) / (60 * 60 * 24)) <= prop->value;
610 case MATCHING_SCORE_GREATER:
611 return info->score >= prop->value;
612 case MATCHING_SCORE_LOWER:
613 return info->score <= prop->value;
614 case MATCHING_NEWSGROUPS:
615 return matcherprop_string_match(prop, info->newsgroups);
616 case MATCHING_NOT_NEWSGROUPS:
617 return !matcherprop_string_match(prop, info->newsgroups);
618 case MATCHING_INREPLYTO:
619 return matcherprop_string_match(prop, info->inreplyto);
620 case MATCHING_NOT_INREPLYTO:
621 return !matcherprop_string_match(prop, info->inreplyto);
622 case MATCHING_REFERENCES:
623 return matcherprop_string_match(prop, info->references);
624 case MATCHING_NOT_REFERENCES:
625 return !matcherprop_string_match(prop, info->references);
626 case MATCHING_EXECUTE:
627 return matcherprop_match_execute(prop, info);
628 case MATCHING_NOT_EXECUTE:
629 return !matcherprop_match_execute(prop, info);
635 /* ********************* MatcherList *************************** */
638 /* parse for a list of conditions */
640 MatcherList * matcherlist_parse(gchar ** str)
643 MatcherProp * matcher;
644 GSList * matchers_list = NULL;
645 gboolean bool_and = TRUE;
648 gboolean main_bool_and = TRUE;
653 matcher = matcherprop_parse(&tmp);
659 matchers_list = g_slist_append(matchers_list, matcher);
662 bool_and = matcher_parse_boolean_op(&tmp);
668 main_bool_and = bool_and;
669 matcher = matcherprop_parse(&tmp);
672 g_slist_append(matchers_list, matcher);
675 for(l = matchers_list ; l != NULL ;
677 matcherprop_free((MatcherProp *)
679 g_slist_free(matchers_list);
686 cond = matcherlist_new(matchers_list, main_bool_and);
693 MatcherList * matcherlist_new(GSList * matchers, gboolean bool_and)
697 cond = g_new0(MatcherList, 1);
699 cond->matchers = matchers;
700 cond->bool_and = bool_and;
705 void matcherlist_free(MatcherList * cond)
709 for(l = cond->matchers ; l != NULL ; l = g_slist_next(l)) {
710 matcherprop_free((MatcherProp *) l->data);
719 static void matcherlist_skip_headers(FILE *fp)
723 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
728 matcherprop_match_one_header
729 returns TRUE if buf matchs the MatchersProp criteria
732 static gboolean matcherprop_match_one_header(MatcherProp * matcher,
738 switch(matcher->criteria) {
739 case MATCHING_HEADER:
740 case MATCHING_NOT_HEADER:
741 header = procheader_parse_header(buf);
744 if (procheader_headername_equal(header->name,
746 if (matcher->criteria == MATCHING_HEADER)
747 result = matcherprop_string_match(matcher, header->body);
749 result = !matcherprop_string_match(matcher, header->body);
750 procheader_header_free(header);
754 procheader_header_free(header);
757 case MATCHING_HEADERS_PART:
758 case MATCHING_MESSAGE:
759 return matcherprop_string_match(matcher, buf);
760 case MATCHING_NOT_MESSAGE:
761 case MATCHING_NOT_HEADERS_PART:
762 return !matcherprop_string_match(matcher, buf);
768 matcherprop_criteria_header
769 returns TRUE if the headers must be matched
772 static gboolean matcherprop_criteria_headers(MatcherProp * matcher)
774 switch(matcher->criteria) {
775 case MATCHING_HEADER:
776 case MATCHING_NOT_HEADER:
777 case MATCHING_HEADERS_PART:
778 case MATCHING_NOT_HEADERS_PART:
785 static gboolean matcherprop_criteria_message(MatcherProp * matcher)
787 switch(matcher->criteria) {
788 case MATCHING_MESSAGE:
789 case MATCHING_NOT_MESSAGE:
797 matcherlist_match_one_header
798 returns TRUE if match should stop
801 static gboolean matcherlist_match_one_header(MatcherList * matchers,
806 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
807 MatcherProp * matcher = (MatcherProp *) l->data;
809 if (matcherprop_criteria_headers(matcher) ||
810 matcherprop_criteria_message(matcher)) {
811 if (matcherprop_match_one_header(matcher, buf)) {
812 matcher->result = TRUE;
816 if (matcherprop_criteria_headers(matcher)) {
817 if (matcher->result) {
818 if (!matchers->bool_and)
828 matcherlist_match_headers
829 returns TRUE if one of the headers matchs the MatcherList criteria
832 static gboolean matcherlist_match_headers(MatcherList * matchers, FILE * fp)
836 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1)
837 if (matcherlist_match_one_header(matchers, buf))
844 matcherprop_criteria_body
845 returns TRUE if the body must be matched
848 static gboolean matcherprop_criteria_body(MatcherProp * matcher)
850 switch(matcher->criteria) {
851 case MATCHING_BODY_PART:
852 case MATCHING_NOT_BODY_PART:
860 matcherprop_match_line
861 returns TRUE if the string matchs the MatcherProp criteria
864 static gboolean matcherprop_match_line(MatcherProp * matcher, gchar * line)
866 switch(matcher->criteria) {
867 case MATCHING_BODY_PART:
868 case MATCHING_MESSAGE:
869 return matcherprop_string_match(matcher, line);
870 case MATCHING_NOT_BODY_PART:
871 case MATCHING_NOT_MESSAGE:
872 return !matcherprop_string_match(matcher, line);
878 matcherlist_match_line
879 returns TRUE if the string matchs the MatcherList criteria
882 static gboolean matcherlist_match_line(MatcherList * matchers, gchar * line)
886 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
887 MatcherProp * matcher = (MatcherProp *) l->data;
889 if (matcherprop_criteria_body(matcher) ||
890 matcherprop_criteria_message(matcher)) {
891 if (matcherprop_match_line(matcher, line)) {
892 matcher->result = TRUE;
896 if (matcher->result) {
897 if (!matchers->bool_and)
905 matcherlist_match_body
906 returns TRUE if one line of the body matchs the MatcherList criteria
909 static gboolean matcherlist_match_body(MatcherList * matchers, FILE * fp)
913 while (fgets(buf, sizeof(buf), fp) != NULL)
914 if (matcherlist_match_line(matchers, buf))
920 gboolean matcherlist_match_file(MatcherList * matchers, MsgInfo * info,
923 gboolean read_headers;
928 gboolean is_incorporating = MSG_IS_FILTERING(info->flags);
930 /* check which things we need to do */
931 read_headers = FALSE;
934 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
935 MatcherProp * matcher = (MatcherProp *) l->data;
937 if (matcherprop_criteria_headers(matcher))
939 if (matcherprop_criteria_body(matcher))
941 if (matcherprop_criteria_message(matcher)) {
945 matcher->result = FALSE;
948 if (!read_headers && !read_body)
951 file = is_incorporating ? g_strdup(info->folder)
952 : procmsg_get_message_file(info);
957 if ((fp = fopen(file, "r")) == NULL) {
958 FILE_OP_ERROR(file, "fopen");
963 /* read the headers */
966 if (matcherlist_match_headers(matchers, fp))
970 matcherlist_skip_headers(fp);
975 matcherlist_match_body(matchers, fp);
978 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
979 MatcherProp * matcher = (MatcherProp *) l->data;
981 if (matcherprop_criteria_headers(matcher) ||
982 matcherprop_criteria_body(matcher) ||
983 matcherprop_criteria_message(matcher))
984 if (matcher->result) {
985 if (!matchers->bool_and) {
991 if (matchers->bool_and) {
1005 /* test a list of condition */
1007 gboolean matcherlist_match(MatcherList * matchers, MsgInfo * info)
1012 if (matchers->bool_and)
1017 /* test the cached elements */
1019 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
1020 MatcherProp * matcher = (MatcherProp *) l->data;
1022 switch(matcher->criteria) {
1024 case MATCHING_UNREAD:
1025 case MATCHING_NOT_UNREAD:
1027 case MATCHING_NOT_NEW:
1028 case MATCHING_MARKED:
1029 case MATCHING_NOT_MARKED:
1030 case MATCHING_DELETED:
1031 case MATCHING_NOT_DELETED:
1032 case MATCHING_REPLIED:
1033 case MATCHING_NOT_REPLIED:
1034 case MATCHING_FORWARDED:
1035 case MATCHING_NOT_FORWARDED:
1036 case MATCHING_SUBJECT:
1037 case MATCHING_NOT_SUBJECT:
1039 case MATCHING_NOT_FROM:
1041 case MATCHING_NOT_TO:
1043 case MATCHING_NOT_CC:
1044 case MATCHING_TO_OR_CC:
1045 case MATCHING_NOT_TO_AND_NOT_CC:
1046 case MATCHING_AGE_GREATER:
1047 case MATCHING_AGE_LOWER:
1048 case MATCHING_NEWSGROUPS:
1049 case MATCHING_NOT_NEWSGROUPS:
1050 case MATCHING_INREPLYTO:
1051 case MATCHING_NOT_INREPLYTO:
1052 case MATCHING_REFERENCES:
1053 case MATCHING_NOT_REFERENCES:
1054 case MATCHING_SCORE_GREATER:
1055 case MATCHING_SCORE_LOWER:
1056 case MATCHING_EXECUTE:
1057 case MATCHING_NOT_EXECUTE:
1058 if (matcherprop_match(matcher, info)) {
1059 if (!matchers->bool_and) {
1064 if (matchers->bool_and) {
1071 /* test the condition on the file */
1073 if (matcherlist_match_file(matchers, info, result)) {
1074 if (!matchers->bool_and)
1078 if (matchers->bool_and)
1086 static void matcherprop_print(MatcherProp * matcher)
1090 if (matcher == NULL) {
1091 printf("no matcher\n");
1095 switch (matcher->matchtype) {
1096 case MATCHING_MATCH:
1099 case MATCHING_REGEXP:
1102 case MATCHING_MATCHCASE:
1103 printf("matchcase\n");
1105 case MATCHING_REGEXPCASE:
1106 printf("regexpcase\n");
1110 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
1112 if (matchparser_tab[i].id == matcher->criteria)
1113 printf("%s\n", matchparser_tab[i].str);
1117 printf("expr : %s\n", matcher->expr);
1119 printf("age: %i\n", matcher->value;
1121 printf("compiled : %s\n", matcher->preg != NULL ? "yes" : "no");
1122 printf("error: %i\n", matcher->error);
1126 gchar * matcherprop_to_string(MatcherProp * matcher)
1128 gchar * matcher_str = NULL;
1129 gchar * criteria_str;
1130 gchar * matchtype_str;
1137 criteria_str = NULL;
1138 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
1140 if (matchparser_tab[i].id == matcher->criteria)
1141 criteria_str = matchparser_tab[i].str;
1143 if (criteria_str == NULL)
1146 switch(matcher->criteria) {
1147 case MATCHING_AGE_GREATER:
1148 case MATCHING_AGE_LOWER:
1149 case MATCHING_SCORE_GREATER:
1150 case MATCHING_SCORE_LOWER:
1151 return g_strdup_printf("%s %i", criteria_str, matcher->value);
1154 case MATCHING_UNREAD:
1155 case MATCHING_NOT_UNREAD:
1157 case MATCHING_NOT_NEW:
1158 case MATCHING_MARKED:
1159 case MATCHING_NOT_MARKED:
1160 case MATCHING_DELETED:
1161 case MATCHING_NOT_DELETED:
1162 case MATCHING_REPLIED:
1163 case MATCHING_NOT_REPLIED:
1164 case MATCHING_FORWARDED:
1165 case MATCHING_NOT_FORWARDED:
1166 return g_strdup(criteria_str);
1169 matchtype_str = NULL;
1170 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
1172 if (matchparser_tab[i].id == matcher->matchtype)
1173 matchtype_str = matchparser_tab[i].str;
1176 if (matchtype_str == NULL)
1179 switch (matcher->matchtype) {
1180 case MATCHING_MATCH:
1181 case MATCHING_MATCHCASE:
1183 for(p = matcher->expr; *p != 0 ; p++)
1184 if (*p == '\"') count ++;
1186 expr_str = g_new(char, strlen(matcher->expr) + count + 1);
1188 for(p = matcher->expr, out = expr_str ; *p != 0 ; p++, out++) {
1198 if (matcher->header)
1200 g_strdup_printf("%s \"%s\" %s \"%s\"",
1201 criteria_str, matcher->header,
1202 matchtype_str, expr_str);
1205 g_strdup_printf("%s %s \"%s\"", criteria_str,
1206 matchtype_str, expr_str);
1212 case MATCHING_REGEXP:
1213 case MATCHING_REGEXPCASE:
1215 if (matcher->header)
1217 g_strdup_printf("%s \"%s\" %s /%s/",
1218 criteria_str, matcher->header,
1219 matchtype_str, matcher->expr);
1222 g_strdup_printf("%s %s /%s/", criteria_str,
1223 matchtype_str, matcher->expr);
1231 gchar * matcherlist_to_string(MatcherList * matchers)
1239 count = g_slist_length(matchers->matchers);
1240 vstr = g_new(gchar *, count + 1);
1242 for (l = matchers->matchers, cur_str = vstr ; l != NULL ;
1243 l = g_slist_next(l), cur_str ++) {
1244 *cur_str = matcherprop_to_string((MatcherProp *) l->data);
1245 if (*cur_str == NULL)
1250 if (matchers->bool_and)
1251 result = g_strjoinv(" & ", vstr);
1253 result = g_strjoinv(" | ", vstr);
1255 for(cur_str = vstr ; *cur_str != NULL ; cur_str ++)
1262 static inline gint strlen_with_check(const gchar *expr, gint fline, const gchar *str)
1267 debug_print("%s(%d) - invalid string %s\n", __FILE__, fline, expr);
1272 #define STRLEN_WITH_CHECK(expr) \
1273 strlen_with_check(#expr, __LINE__, expr)
1275 gchar * matching_build_command(gchar * cmd, MsgInfo * info)
1278 gchar * filename = NULL;
1279 gchar * processed_cmd;
1283 size = strlen(cmd) + 1;
1284 while (*s != '\0') {
1291 case 's': /* subject */
1292 size += STRLEN_WITH_CHECK(info->subject) - 2;
1294 case 'f': /* from */
1295 size += STRLEN_WITH_CHECK(info->from) - 2;
1298 size += STRLEN_WITH_CHECK(info->to) - 2;
1301 size += STRLEN_WITH_CHECK(info->cc) - 2;
1303 case 'd': /* date */
1304 size += STRLEN_WITH_CHECK(info->date) - 2;
1306 case 'i': /* message-id */
1307 size += STRLEN_WITH_CHECK(info->msgid) - 2;
1309 case 'n': /* newsgroups */
1310 size += STRLEN_WITH_CHECK(info->newsgroups) - 2;
1312 case 'r': /* references */
1313 size += STRLEN_WITH_CHECK(info->references) - 2;
1315 case 'F': /* file */
1316 if (MSG_IS_FILTERING(info->flags))
1317 filename = g_strdup(info->folder);
1319 filename = folder_item_fetch_msg(info->folder, info->msgnum);
1321 if (filename == NULL) {
1322 debug_print(_("%s(%d) - filename is not set"), __FILE__, __LINE__);
1326 size += strlen(filename) - 2;
1334 processed_cmd = g_new0(gchar, size);
1338 while (*s != '\0') {
1346 case 's': /* subject */
1347 if (info->subject != NULL)
1348 strcpy(p, info->subject);
1350 strcpy(p, "(none)");
1353 case 'f': /* from */
1354 if (info->from != NULL)
1355 strcpy(p, info->from);
1357 strcpy(p, "(none)");
1361 if (info->to != NULL)
1362 strcpy(p, info->to);
1364 strcpy(p, "(none)");
1368 if (info->cc != NULL)
1369 strcpy(p, info->cc);
1371 strcpy(p, "(none)");
1374 case 'd': /* date */
1375 if (info->date != NULL)
1376 strcpy(p, info->date);
1378 strcpy(p, "(none)");
1381 case 'i': /* message-id */
1382 if (info->msgid != NULL)
1383 strcpy(p, info->msgid);
1385 strcpy(p, "(none)");
1388 case 'n': /* newsgroups */
1389 if (info->newsgroups != NULL)
1390 strcpy(p, info->newsgroups);
1392 strcpy(p, "(none)");
1395 case 'r': /* references */
1396 if (info->references != NULL)
1397 strcpy(p, info->references);
1399 strcpy(p, "(none)");
1402 case 'F': /* file */
1403 strcpy(p, filename);
1423 return processed_cmd;
1427 /* ************************************************************ */
1430 static void matcher_parse (gchar * str)
1432 matcher_parser_scan_string(str);
1433 matcher_parserparse();