7 #include "procheader.h"
15 typedef struct _MatchParser MatchParser;
17 MatchParser matchparser_tab[] = {
19 {MATCHING_ALL, "all"},
20 {MATCHING_UNREAD, "unread"},
21 {MATCHING_NOT_UNREAD, "~unread"},
22 {MATCHING_NEW, "new"},
23 {MATCHING_NOT_NEW, "~new"},
24 {MATCHING_MARKED, "marked"},
25 {MATCHING_NOT_MARKED, "~marked"},
26 {MATCHING_DELETED, "deleted"},
27 {MATCHING_NOT_DELETED, "~deleted"},
28 {MATCHING_REPLIED, "replied"},
29 {MATCHING_NOT_REPLIED, "~replied"},
30 {MATCHING_FORWARDED, "forwarded"},
31 {MATCHING_NOT_FORWARDED, "~forwarded"},
34 {MATCHING_SUBJECT, "subject"},
35 {MATCHING_NOT_SUBJECT, "~subject"},
36 {MATCHING_FROM, "from"},
37 {MATCHING_NOT_FROM, "~from"},
39 {MATCHING_NOT_TO, "~to"},
41 {MATCHING_NOT_CC, "~cc"},
42 {MATCHING_TO_OR_CC, "to_or_cc"},
43 {MATCHING_NOT_TO_AND_NOT_CC, "~to_or_cc"},
44 {MATCHING_AGE_GREATER, "age_greater"},
45 {MATCHING_AGE_LOWER, "age_lower"},
46 {MATCHING_NEWSGROUPS, "newsgroups"},
47 {MATCHING_NOT_NEWSGROUPS, "~newsgroups"},
49 /* content have to be read */
50 {MATCHING_HEADER, "header"},
51 {MATCHING_NOT_HEADER, "~header"},
52 {MATCHING_HEADERS_PART, "headers_part"},
53 {MATCHING_NOT_HEADERS_PART, "~headers_part"},
54 {MATCHING_MESSAGE, "message"},
55 {MATCHING_NOT_MESSAGE, "~message"},
56 {MATCHING_BODY_PART, "body_part"},
57 {MATCHING_NOT_BODY_PART, "~body_part"},
60 {MATCHING_MATCHCASE, "matchcase"},
61 {MATCHING_MATCH, "match"},
62 {MATCHING_REGEXPCASE, "regexpcase"},
63 {MATCHING_REGEXP, "regexp"},
66 {MATCHING_SCORE, "score"},
72 header "x-mailing" match "toto"
73 subject match "regexp" & to regexp "regexp"
74 subject match "regexp" | to regexpcase "regexp" | age_sup 5
77 static gboolean matcher_is_blank(gchar ch);
79 /* ******************* parser *********************** */
81 static gboolean matcher_is_blank(gchar ch)
83 return (ch == ' ') || (ch == '\t');
86 /* parse for one condition */
88 MatcherProp * matcherprop_parse(gchar ** str)
96 gchar * header = NULL;
99 key = matcher_parse_keyword(&tmp);
106 case MATCHING_AGE_LOWER:
107 case MATCHING_AGE_GREATER:
108 age = matcher_parse_number(&tmp);
115 prop = matcherprop_new(key, NULL, 0, NULL, age);
120 case MATCHING_UNREAD:
121 case MATCHING_NOT_UNREAD:
123 case MATCHING_NOT_NEW:
124 case MATCHING_MARKED:
125 case MATCHING_NOT_MARKED:
126 case MATCHING_DELETED:
127 case MATCHING_NOT_DELETED:
128 case MATCHING_REPLIED:
129 case MATCHING_NOT_REPLIED:
130 case MATCHING_FORWARDED:
131 case MATCHING_NOT_FORWARDED:
132 prop = matcherprop_new(key, NULL, 0, NULL, 0);
137 case MATCHING_SUBJECT:
138 case MATCHING_NOT_SUBJECT:
140 case MATCHING_NOT_FROM:
142 case MATCHING_NOT_TO:
144 case MATCHING_NOT_CC:
145 case MATCHING_TO_OR_CC:
146 case MATCHING_NOT_TO_AND_NOT_CC:
147 case MATCHING_NEWSGROUPS:
148 case MATCHING_NOT_NEWSGROUPS:
149 case MATCHING_MESSAGE:
150 case MATCHING_NOT_MESSAGE:
151 case MATCHING_HEADERS_PART:
152 case MATCHING_NOT_HEADERS_PART:
153 case MATCHING_BODY_PART:
154 case MATCHING_NOT_BODY_PART:
155 case MATCHING_HEADER:
156 case MATCHING_NOT_HEADER:
157 if ((key == MATCHING_HEADER) || (key == MATCHING_NOT_HEADER)) {
158 header = matcher_parse_str(&tmp);
165 match = matcher_parse_keyword(&tmp);
174 case MATCHING_REGEXP:
175 case MATCHING_REGEXPCASE:
176 expr = matcher_parse_regexp(&tmp);
184 prop = matcherprop_new(key, header, match, expr, 0);
189 case MATCHING_MATCHCASE:
190 expr = matcher_parse_str(&tmp);
198 prop = matcherprop_new(key, header, match, expr, 0);
214 gint matcher_parse_keyword(gchar ** str)
222 dup = alloca(strlen(* str) + 1);
226 while (matcher_is_blank(*p))
231 while (!matcher_is_blank(*p) && (*p != '\0'))
235 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
237 if (strncasecmp(matchparser_tab[i].str, start,
252 return matchparser_tab[match].id;
255 gint matcher_parse_number(gchar ** str)
261 dup = alloca(strlen(* str) + 1);
265 while (matcher_is_blank(*p))
270 if (!isdigit(*p) && *p != '-' && *p != '+') {
274 if (*p == '-' || *p == '+')
285 gboolean matcher_parse_boolean_op(gchar ** str)
291 while (matcher_is_blank(*p))
295 *str += p - * str + 1;
298 else if (*p == '&') {
299 *str += p - * str + 1;
308 gchar * matcher_parse_regexp(gchar ** str)
314 dup = alloca(strlen(* str) + 1);
318 while (matcher_is_blank(*p))
336 return g_strdup(start);
339 gchar * matcher_parse_str(gchar ** str)
346 dup = alloca(strlen(* str) + 1);
350 while (matcher_is_blank(*p))
373 *str += dest - dup + 2;
374 return g_strdup(start);
377 /* **************** data structure allocation **************** */
380 MatcherProp * matcherprop_new(gint criteria, gchar * header,
381 gint matchtype, gchar * expr,
386 prop = g_new0(MatcherProp, 1);
387 prop->criteria = criteria;
389 prop->header = g_strdup(header);
393 prop->expr = g_strdup(expr);
396 prop->matchtype = matchtype;
404 void matcherprop_free(MatcherProp * prop)
407 if (prop->preg != NULL) {
415 /* ************** match ******************************/
418 /* match the given string */
420 static gboolean matcherprop_string_match(MatcherProp * prop, gchar * str)
428 switch(prop->matchtype) {
429 case MATCHING_REGEXP:
430 case MATCHING_REGEXPCASE:
431 if (!prop->preg && (prop->error == 0)) {
432 prop->preg = g_new0(regex_t, 1);
433 if (regcomp(prop->preg, prop->expr,
434 REG_NOSUB | REG_EXTENDED
435 | ((prop->matchtype == MATCHING_REGEXPCASE)
436 ? REG_ICASE : 0)) != 0) {
441 if (prop->preg == NULL)
444 if (regexec(prop->preg, str, 0, NULL, 0) == 0)
450 return (strstr(str, prop->expr) != NULL);
452 case MATCHING_MATCHCASE:
453 str2 = alloca(strlen(prop->expr) + 1);
454 strcpy(str2, prop->expr);
456 str1 = alloca(strlen(str) + 1);
459 return (strstr(str1, str2) != NULL);
466 /* match a message and his headers, hlist can be NULL if you don't
467 want to use headers */
469 gboolean matcherprop_match(MatcherProp * prop, MsgInfo * info)
473 switch(prop->criteria) {
476 case MATCHING_UNREAD:
477 return MSG_IS_UNREAD(info->flags);
478 case MATCHING_NOT_UNREAD:
479 return !MSG_IS_UNREAD(info->flags);
481 return MSG_IS_NEW(info->flags);
482 case MATCHING_NOT_NEW:
483 return !MSG_IS_NEW(info->flags);
484 case MATCHING_MARKED:
485 return MSG_IS_MARKED(info->flags);
486 case MATCHING_NOT_MARKED:
487 return !MSG_IS_MARKED(info->flags);
488 case MATCHING_DELETED:
489 return MSG_IS_DELETED(info->flags);
490 case MATCHING_NOT_DELETED:
491 return !MSG_IS_DELETED(info->flags);
492 case MATCHING_REPLIED:
493 return MSG_IS_REPLIED(info->flags);
494 case MATCHING_NOT_REPLIED:
495 return !MSG_IS_REPLIED(info->flags);
496 case MATCHING_FORWARDED:
497 return MSG_IS_FORWARDED(info->flags);
498 case MATCHING_NOT_FORWARDED:
499 return !MSG_IS_FORWARDED(info->flags);
500 case MATCHING_SUBJECT:
501 return matcherprop_string_match(prop, info->subject);
502 case MATCHING_NOT_SUBJECT:
503 return !matcherprop_string_match(prop, info->subject);
505 return matcherprop_string_match(prop, info->from);
506 case MATCHING_NOT_FROM:
507 return !matcherprop_string_match(prop, info->from);
509 return matcherprop_string_match(prop, info->to);
510 case MATCHING_NOT_TO:
511 return !matcherprop_string_match(prop, info->to);
513 return matcherprop_string_match(prop, info->cc);
514 case MATCHING_NOT_CC:
515 return !matcherprop_string_match(prop, info->cc);
516 case MATCHING_TO_OR_CC:
517 return matcherprop_string_match(prop, info->to)
518 || matcherprop_string_match(prop, info->cc);
519 case MATCHING_NOT_TO_AND_NOT_CC:
520 return !(matcherprop_string_match(prop, info->to)
521 || matcherprop_string_match(prop, info->cc));
522 case MATCHING_AGE_GREATER:
524 return ((t - info->date_t) / (60 * 60 * 24)) >= prop->age;
525 case MATCHING_AGE_LOWER:
527 return ((t - info->date_t) / (60 * 60 * 24)) <= prop->age;
528 case MATCHING_NEWSGROUPS:
529 return matcherprop_string_match(prop, info->newsgroups);
530 case MATCHING_NOT_NEWSGROUPS:
531 return !matcherprop_string_match(prop, info->newsgroups);
532 case MATCHING_HEADER:
538 /* ********************* MatcherList *************************** */
541 /* parse for a list of conditions */
543 MatcherList * matcherlist_parse(gchar ** str)
546 MatcherProp * matcher;
547 GSList * matchers_list = NULL;
548 gboolean bool_and = TRUE;
551 gboolean main_bool_and = TRUE;
556 matcher = matcherprop_parse(&tmp);
562 matchers_list = g_slist_append(matchers_list, matcher);
565 bool_and = matcher_parse_boolean_op(&tmp);
571 main_bool_and = bool_and;
572 matcher = matcherprop_parse(&tmp);
575 g_slist_append(matchers_list, matcher);
578 for(l = matchers_list ; l != NULL ;
580 matcherprop_free((MatcherProp *)
582 g_slist_free(matchers_list);
589 cond = matcherlist_new(matchers_list, main_bool_and);
596 MatcherList * matcherlist_new(GSList * matchers, gboolean bool_and)
600 cond = g_new0(MatcherList, 1);
602 cond->matchers = matchers;
603 cond->bool_and = bool_and;
608 void matcherlist_free(MatcherList * cond)
612 for(l = cond->matchers ; l != NULL ; l = g_slist_next(l)) {
613 matcherprop_free((MatcherProp *) l->data);
622 static void matcherlist_skip_headers(FILE *fp)
626 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
631 matcherprop_match_one_header
632 returns TRUE if buf matchs the MatchersProp criteria
635 static gboolean matcherprop_match_one_header(MatcherProp * matcher,
641 switch(matcher->criteria) {
642 case MATCHING_HEADER:
643 case MATCHING_NOT_HEADER:
644 header = procheader_parse_header(buf);
645 if (procheader_headername_equal(header->name,
647 if (matcher->criteria == MATCHING_HEADER)
648 result = matcherprop_string_match(matcher, header->body);
650 result = !matcherprop_string_match(matcher, header->body);
651 procheader_header_free(header);
655 case MATCHING_HEADERS_PART:
656 case MATCHING_MESSAGE:
657 return matcherprop_string_match(matcher, buf);
658 case MATCHING_NOT_MESSAGE:
659 case MATCHING_NOT_HEADERS_PART:
660 return !matcherprop_string_match(matcher, buf);
666 matcherprop_criteria_header
667 returns TRUE if the headers must be matched
670 static gboolean matcherprop_criteria_headers(MatcherProp * matcher)
672 switch(matcher->criteria) {
673 case MATCHING_HEADER:
674 case MATCHING_NOT_HEADER:
675 case MATCHING_HEADERS_PART:
676 case MATCHING_NOT_HEADERS_PART:
677 case MATCHING_MESSAGE:
678 case MATCHING_NOT_MESSAGE:
686 matcherlist_match_one_header
687 returns TRUE if buf matchs the MatchersList criteria
690 static gboolean matcherlist_match_one_header(MatcherList * matchers,
691 gchar * buf, gboolean result)
695 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
696 MatcherProp * matcher = (MatcherProp *) l->data;
698 if (matcherprop_criteria_headers(matcher)) {
699 if (matcherprop_match_one_header(matcher, buf)) {
700 if (!matchers->bool_and)
704 if (matchers->bool_and)
714 matcherlist_match_headers
715 returns TRUE if one of the headers matchs the MatcherList criteria
718 static gboolean matcherlist_match_headers(MatcherList * matchers, FILE * fp,
723 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
724 if (matcherlist_match_one_header(matchers, buf, result)) {
725 if (!matchers->bool_and)
729 if (matchers->bool_and)
737 matcherprop_criteria_body
738 returns TRUE if the body must be matched
741 static gboolean matcherprop_criteria_body(MatcherProp * matcher)
743 switch(matcher->criteria) {
744 case MATCHING_BODY_PART:
745 case MATCHING_NOT_BODY_PART:
746 case MATCHING_MESSAGE:
747 case MATCHING_NOT_MESSAGE:
755 matcherprop_match_line
756 returns TRUE if the string matchs the MatcherProp criteria
759 static gboolean matcherprop_match_line(MatcherProp * matcher, gchar * line)
761 switch(matcher->criteria) {
762 case MATCHING_BODY_PART:
763 case MATCHING_MESSAGE:
764 return matcherprop_string_match(matcher, line);
765 case MATCHING_NOT_BODY_PART:
766 case MATCHING_NOT_MESSAGE:
767 return !matcherprop_string_match(matcher, line);
773 matcherlist_match_line
774 returns TRUE if the string matchs the MatcherList criteria
777 static gboolean matcherlist_match_line(MatcherList * matchers, gchar * line,
782 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
783 MatcherProp * matcher = (MatcherProp *) l->data;
785 if (matcherprop_criteria_body(matcher)) {
786 if (matcherprop_match_line(matcher, line)) {
787 if (!matchers->bool_and)
791 if (matchers->bool_and)
800 matcherlist_match_body
801 returns TRUE if one line of the body matchs the MatcherList criteria
804 static gboolean matcherlist_match_body(MatcherList * matchers, FILE * fp,
809 while (fgets(buf, sizeof(buf), fp) != NULL) {
810 if (matcherlist_match_line(matchers, buf, result)) {
811 if (!matchers->bool_and)
815 if (matchers->bool_and)
822 gboolean matcherlist_match_file(MatcherList * matchers, MsgInfo * info,
825 gboolean read_headers;
831 /* file need to be read ? */
833 read_headers = FALSE;
835 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
836 MatcherProp * matcher = (MatcherProp *) l->data;
838 if (matcherprop_criteria_headers(matcher))
840 if (matcherprop_criteria_body(matcher))
844 if (!read_headers && !read_body)
847 file = procmsg_get_message_file(info);
851 if ((fp = fopen(file, "r")) == NULL) {
852 FILE_OP_ERROR(file, "fopen");
857 /* read the headers */
860 if (matcherlist_match_headers(matchers, fp, result)) {
861 if (!matchers->bool_and)
865 if (matchers->bool_and)
870 matcherlist_skip_headers(fp);
875 if (matcherlist_match_body(matchers, fp, result)) {
876 if (!matchers->bool_and)
880 if (matchers->bool_and)
892 /* test a list of condition */
894 gboolean matcherlist_match(MatcherList * matchers, MsgInfo * info)
899 if (matchers->bool_and)
904 /* test the condition on the file */
906 if (matcherlist_match_file(matchers, info, result)) {
907 if (!matchers->bool_and)
911 if (matchers->bool_and)
915 /* test the cached elements */
917 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
918 MatcherProp * matcher = (MatcherProp *) l->data;
920 if (matcherprop_match(matcher, info)) {
921 if (!matchers->bool_and) {
927 if (matchers->bool_and) {
938 static void matcherprop_print(MatcherProp * matcher)
942 if (matcher == NULL) {
943 printf("no matcher\n");
947 switch (matcher->matchtype) {
951 case MATCHING_REGEXP:
954 case MATCHING_MATCHCASE:
955 printf("matchcase\n");
957 case MATCHING_REGEXPCASE:
958 printf("regexpcase\n");
962 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
964 if (matchparser_tab[i].id == matcher->criteria)
965 printf("%s\n", matchparser_tab[i].str);
969 printf("expr : %s\n", matcher->expr);
971 printf("age: %i\n", matcher->age);
973 printf("compiled : %s\n", matcher->preg != NULL ? "yes" : "no");
974 printf("error: %i\n", matcher->error);
978 gchar * matcherprop_to_string(MatcherProp * matcher)
980 gchar * matcher_str = NULL;
981 gchar * criteria_str;
982 gchar * matchtype_str;
990 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
992 if (matchparser_tab[i].id == matcher->criteria)
993 criteria_str = matchparser_tab[i].str;
995 if (criteria_str == NULL)
998 switch(matcher->criteria) {
999 case MATCHING_AGE_GREATER:
1000 case MATCHING_AGE_LOWER:
1001 return g_strdup_printf("%s %i", criteria_str, matcher->age);
1004 case MATCHING_UNREAD:
1005 case MATCHING_NOT_UNREAD:
1007 case MATCHING_NOT_NEW:
1008 case MATCHING_MARKED:
1009 case MATCHING_NOT_MARKED:
1010 case MATCHING_DELETED:
1011 case MATCHING_NOT_DELETED:
1012 case MATCHING_REPLIED:
1013 case MATCHING_NOT_REPLIED:
1014 case MATCHING_FORWARDED:
1015 case MATCHING_NOT_FORWARDED:
1016 return g_strdup(criteria_str);
1019 matchtype_str = NULL;
1020 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
1022 if (matchparser_tab[i].id == matcher->matchtype)
1023 matchtype_str = matchparser_tab[i].str;
1026 if (matchtype_str == NULL)
1029 switch (matcher->matchtype) {
1030 case MATCHING_MATCH:
1031 case MATCHING_MATCHCASE:
1033 for(p = matcher->expr; *p != 0 ; p++)
1034 if (*p == '\"') count ++;
1036 expr_str = g_new(char, strlen(matcher->expr) + count + 1);
1038 for(p = matcher->expr, out = expr_str ; *p != 0 ; p++, out++) {
1048 if (matcher->header)
1050 g_strdup_printf("%s \"%s\" %s \"%s\"",
1051 criteria_str, matcher->header,
1052 matchtype_str, expr_str);
1055 g_strdup_printf("%s %s \"%s\"", criteria_str,
1056 matchtype_str, expr_str);
1062 case MATCHING_REGEXP:
1063 case MATCHING_REGEXPCASE:
1065 if (matcher->header)
1067 g_strdup_printf("%s \"%s\" %s /%s/",
1068 criteria_str, matcher->header,
1069 matchtype_str, matcher->expr);
1072 g_strdup_printf("%s %s /%s/", criteria_str,
1073 matchtype_str, matcher->expr);
1081 gchar * matcherlist_to_string(MatcherList * matchers)
1089 count = g_slist_length(matchers->matchers);
1090 vstr = g_new(gchar *, count + 1);
1092 for (l = matchers->matchers, cur_str = vstr ; l != NULL ;
1093 l = g_slist_next(l), cur_str ++) {
1094 *cur_str = matcherprop_to_string((MatcherProp *) l->data);
1095 if (*cur_str == NULL)
1100 if (matchers->bool_and)
1101 result = g_strjoinv(" & ", vstr);
1103 result = g_strjoinv(" | ", vstr);
1105 for(cur_str = vstr ; *cur_str != NULL ; cur_str ++)