7 #include "procheader.h"
10 #include "matcher_parser.h"
18 typedef struct _MatchParser MatchParser;
20 static MatchParser matchparser_tab[] = {
22 {MATCHCRITERIA_ALL, "all"},
23 {MATCHCRITERIA_UNREAD, "unread"},
24 {MATCHCRITERIA_NOT_UNREAD, "~unread"},
25 {MATCHCRITERIA_NEW, "new"},
26 {MATCHCRITERIA_NOT_NEW, "~new"},
27 {MATCHCRITERIA_MARKED, "marked"},
28 {MATCHCRITERIA_NOT_MARKED, "~marked"},
29 {MATCHCRITERIA_DELETED, "deleted"},
30 {MATCHCRITERIA_NOT_DELETED, "~deleted"},
31 {MATCHCRITERIA_REPLIED, "replied"},
32 {MATCHCRITERIA_NOT_REPLIED, "~replied"},
33 {MATCHCRITERIA_FORWARDED, "forwarded"},
34 {MATCHCRITERIA_NOT_FORWARDED, "~forwarded"},
37 {MATCHCRITERIA_SUBJECT, "subject"},
38 {MATCHCRITERIA_NOT_SUBJECT, "~subject"},
39 {MATCHCRITERIA_FROM, "from"},
40 {MATCHCRITERIA_NOT_FROM, "~from"},
41 {MATCHCRITERIA_TO, "to"},
42 {MATCHCRITERIA_NOT_TO, "~to"},
43 {MATCHCRITERIA_CC, "cc"},
44 {MATCHCRITERIA_NOT_CC, "~cc"},
45 {MATCHCRITERIA_TO_OR_CC, "to_or_cc"},
46 {MATCHCRITERIA_NOT_TO_AND_NOT_CC, "~to_or_cc"},
47 {MATCHCRITERIA_AGE_GREATER, "age_greater"},
48 {MATCHCRITERIA_AGE_LOWER, "age_lower"},
49 {MATCHCRITERIA_NEWSGROUPS, "newsgroups"},
50 {MATCHCRITERIA_NOT_NEWSGROUPS, "~newsgroups"},
51 {MATCHCRITERIA_INREPLYTO, "inreplyto"},
52 {MATCHCRITERIA_NOT_INREPLYTO, "~inreplyto"},
53 {MATCHCRITERIA_REFERENCES, "references"},
54 {MATCHCRITERIA_NOT_REFERENCES, "~references"},
55 {MATCHCRITERIA_SCORE_GREATER, "score_greater"},
56 {MATCHCRITERIA_SCORE_LOWER, "score_lower"},
57 {MATCHCRITERIA_SCORE_EQUAL, "score_equal"},
59 {MATCHCRITERIA_SIZE_GREATER, "size_greater"},
60 {MATCHCRITERIA_SIZE_SMALLER, "size_smaller"},
61 {MATCHCRITERIA_SIZE_EQUAL, "size_equal"},
63 /* content have to be read */
64 {MATCHCRITERIA_HEADER, "header"},
65 {MATCHCRITERIA_NOT_HEADER, "~header"},
66 {MATCHCRITERIA_HEADERS_PART, "headers_part"},
67 {MATCHCRITERIA_NOT_HEADERS_PART, "~headers_part"},
68 {MATCHCRITERIA_MESSAGE, "message"},
69 {MATCHCRITERIA_NOT_MESSAGE, "~message"},
70 {MATCHCRITERIA_BODY_PART, "body_part"},
71 {MATCHCRITERIA_NOT_BODY_PART, "~body_part"},
72 {MATCHCRITERIA_EXECUTE, "execute"},
73 {MATCHCRITERIA_NOT_EXECUTE, "~execute"},
76 {MATCHTYPE_MATCHCASE, "matchcase"},
77 {MATCHTYPE_MATCH, "match"},
78 {MATCHTYPE_REGEXPCASE, "regexpcase"},
79 {MATCHTYPE_REGEXP, "regexp"},
82 {MATCHACTION_SCORE, "score"},
83 {MATCHACTION_MOVE, "move"},
84 {MATCHACTION_COPY, "copy"},
85 {MATCHACTION_DELETE, "delete"},
86 {MATCHACTION_MARK, "mark"},
87 {MATCHACTION_UNMARK, "unmark"},
88 {MATCHACTION_MARK_AS_READ, "mark_as_read"},
89 {MATCHACTION_MARK_AS_UNREAD, "mark_as_unread"},
90 {MATCHACTION_FORWARD, "forward"},
91 {MATCHACTION_FORWARD_AS_ATTACHMENT, "forward_as_attachment"},
92 {MATCHACTION_EXECUTE, "execute"},
93 {MATCHACTION_COLOR, "color"},
94 {MATCHACTION_REDIRECT, "redirect"},
95 {MATCHACTION_DELETE_ON_SERVER, "delete_on_server"}
98 /* get_matchparser_tab_str() - used by filtering.c to translate
99 * actions to debug strings */
100 gchar * get_matchparser_tab_str(gint id)
104 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
106 if (matchparser_tab[i].id == id)
107 return matchparser_tab[i].str;
112 /* matcher_escape_str() - escapes a string returns newly allocated escaped string */
113 gchar *matcher_escape_str(const gchar *str)
115 register const gchar *walk;
118 register char *reswalk;
123 for (escape = 0, walk = str; *walk; walk++)
124 if (*walk == '\'' || *walk == '\"')
128 return g_strdup(str);
130 reswalk = res = g_new0(gchar, (walk - str) + escape + 1);
131 for (walk = str; *walk; walk++, reswalk++) {
132 if (*walk == '\'' || *walk == '\"')
141 /* matcher_unescape_str() - assumes that unescaping frees up room
142 * in the string, so it returns the unescaped string in the
144 gchar *matcher_unescape_str(gchar *str)
146 gchar *tmp = alloca(strlen(str) + 1);
147 register gchar *src = tmp;
148 register gchar *dst = str;
152 for ( ; *src; src++) {
158 *dst++ = '\\'; /* insert backslash */
159 else if (*src == 'n') /* insert control characters */
161 else if (*src == 'r')
163 else if (*src == 't')
165 else if (*src == 'r')
167 else if (*src == 'b')
169 else if (*src == 'f')
171 else if (*src == '\'' || *src == '\"') /* insert \' or \" */
174 /* FIXME: should perhaps escape character... */
184 /* **************** data structure allocation **************** */
186 /* matcherprop_new() - allocates a structure for one condition
188 MatcherProp * matcherprop_new(gint criteria, gchar * header,
189 gint matchtype, gchar * expr,
194 prop = g_new0(MatcherProp, 1);
195 prop->criteria = criteria;
196 if (header != NULL) {
197 prop->header = g_strdup(header);
198 prop->unesc_header = matcher_unescape_str(g_strdup(header));
202 prop->unesc_header = NULL;
205 prop->expr = g_strdup(expr);
206 prop->unesc_expr = matcher_unescape_str(g_strdup(expr));
210 prop->unesc_expr = NULL;
212 prop->matchtype = matchtype;
220 /* matcherprop_free()
222 void matcherprop_free(MatcherProp * prop)
226 if (prop->unesc_expr)
227 g_free(prop->unesc_expr);
229 g_free(prop->header);
230 if (prop->unesc_header)
231 g_free(prop->unesc_header);
232 if (prop->preg != NULL) {
240 /* ************** match ******************************/
243 /* matcherprop_string_match() - finds out if a string matches
244 * with a criterium */
245 static gboolean matcherprop_string_match(MatcherProp * prop, gchar * str)
253 switch(prop->matchtype) {
254 case MATCHTYPE_REGEXPCASE:
255 case MATCHTYPE_REGEXP:
256 if (!prop->preg && (prop->error == 0)) {
257 prop->preg = g_new0(regex_t, 1);
258 /* if regexp then don't use the escaped string */
259 if (regcomp(prop->preg, prop->expr,
260 REG_NOSUB | REG_EXTENDED
261 | ((prop->matchtype == MATCHTYPE_REGEXPCASE)
262 ? REG_ICASE : 0)) != 0) {
268 if (prop->preg == NULL)
271 if (regexec(prop->preg, str, 0, NULL, 0) == 0)
276 case MATCHTYPE_MATCH:
277 return (strstr(str, prop->unesc_expr) != NULL);
279 /* FIXME: put upper in unesc_str */
280 case MATCHTYPE_MATCHCASE:
281 str2 = alloca(strlen(prop->unesc_expr) + 1);
282 strcpy(str2, prop->unesc_expr);
284 str1 = alloca(strlen(str) + 1);
287 return (strstr(str1, str2) != NULL);
294 gboolean matcherprop_match_execute(MatcherProp * prop, MsgInfo * info)
300 file = procmsg_get_message_file(info);
304 cmd = matching_build_command(prop->unesc_expr, info);
308 retval = system(cmd);
309 debug_print(_("Command exit code: %d\n"), retval);
311 return (retval == 0);
314 /* match a message and his headers, hlist can be NULL if you don't
315 want to use headers */
317 gboolean matcherprop_match(MatcherProp * prop, MsgInfo * info)
321 switch(prop->criteria) {
322 case MATCHCRITERIA_ALL:
324 case MATCHCRITERIA_UNREAD:
325 return MSG_IS_UNREAD(info->flags);
326 case MATCHCRITERIA_NOT_UNREAD:
327 return !MSG_IS_UNREAD(info->flags);
328 case MATCHCRITERIA_NEW:
329 return MSG_IS_NEW(info->flags);
330 case MATCHCRITERIA_NOT_NEW:
331 return !MSG_IS_NEW(info->flags);
332 case MATCHCRITERIA_MARKED:
333 return MSG_IS_MARKED(info->flags);
334 case MATCHCRITERIA_NOT_MARKED:
335 return !MSG_IS_MARKED(info->flags);
336 case MATCHCRITERIA_DELETED:
337 return MSG_IS_DELETED(info->flags);
338 case MATCHCRITERIA_NOT_DELETED:
339 return !MSG_IS_DELETED(info->flags);
340 case MATCHCRITERIA_REPLIED:
341 return MSG_IS_REPLIED(info->flags);
342 case MATCHCRITERIA_NOT_REPLIED:
343 return !MSG_IS_REPLIED(info->flags);
344 case MATCHCRITERIA_FORWARDED:
345 return MSG_IS_FORWARDED(info->flags);
346 case MATCHCRITERIA_NOT_FORWARDED:
347 return !MSG_IS_FORWARDED(info->flags);
348 case MATCHCRITERIA_SUBJECT:
349 return matcherprop_string_match(prop, info->subject);
350 case MATCHCRITERIA_NOT_SUBJECT:
351 return !matcherprop_string_match(prop, info->subject);
352 case MATCHCRITERIA_FROM:
353 return matcherprop_string_match(prop, info->from);
354 case MATCHCRITERIA_NOT_FROM:
355 return !matcherprop_string_match(prop, info->from);
356 case MATCHCRITERIA_TO:
357 return matcherprop_string_match(prop, info->to);
358 case MATCHCRITERIA_NOT_TO:
359 return !matcherprop_string_match(prop, info->to);
360 case MATCHCRITERIA_CC:
361 return matcherprop_string_match(prop, info->cc);
362 case MATCHCRITERIA_NOT_CC:
363 return !matcherprop_string_match(prop, info->cc);
364 case MATCHCRITERIA_TO_OR_CC:
365 return matcherprop_string_match(prop, info->to)
366 || matcherprop_string_match(prop, info->cc);
367 case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
368 return !(matcherprop_string_match(prop, info->to)
369 || matcherprop_string_match(prop, info->cc));
370 case MATCHCRITERIA_AGE_GREATER:
372 return ((t - info->date_t) / (60 * 60 * 24)) >= prop->value;
373 case MATCHCRITERIA_AGE_LOWER:
375 return ((t - info->date_t) / (60 * 60 * 24)) <= prop->value;
376 case MATCHCRITERIA_SCORE_GREATER:
377 return info->score >= prop->value;
378 case MATCHCRITERIA_SCORE_LOWER:
379 return info->score <= prop->value;
380 case MATCHCRITERIA_SCORE_EQUAL:
381 return info->score == prop->value;
382 case MATCHCRITERIA_SIZE_GREATER:
383 /* FIXME: info->size is an off_t */
384 return info->size > (off_t) prop->value;
385 case MATCHCRITERIA_SIZE_EQUAL:
386 /* FIXME: info->size is an off_t */
387 return info->size == (off_t) prop->value;
388 case MATCHCRITERIA_SIZE_SMALLER:
389 /* FIXME: info->size is an off_t */
390 return info->size < (off_t) prop->value;
391 case MATCHCRITERIA_NEWSGROUPS:
392 return matcherprop_string_match(prop, info->newsgroups);
393 case MATCHCRITERIA_NOT_NEWSGROUPS:
394 return !matcherprop_string_match(prop, info->newsgroups);
395 case MATCHCRITERIA_INREPLYTO:
396 return matcherprop_string_match(prop, info->inreplyto);
397 case MATCHCRITERIA_NOT_INREPLYTO:
398 return !matcherprop_string_match(prop, info->inreplyto);
399 case MATCHCRITERIA_REFERENCES:
400 return matcherprop_string_match(prop, info->references);
401 case MATCHCRITERIA_NOT_REFERENCES:
402 return !matcherprop_string_match(prop, info->references);
403 case MATCHCRITERIA_EXECUTE:
404 return matcherprop_match_execute(prop, info);
405 case MATCHCRITERIA_NOT_EXECUTE:
406 return !matcherprop_match_execute(prop, info);
412 /* ********************* MatcherList *************************** */
415 MatcherList * matcherlist_new(GSList * matchers, gboolean bool_and)
419 cond = g_new0(MatcherList, 1);
421 cond->matchers = matchers;
422 cond->bool_and = bool_and;
427 void matcherlist_free(MatcherList * cond)
431 for(l = cond->matchers ; l != NULL ; l = g_slist_next(l)) {
432 matcherprop_free((MatcherProp *) l->data);
441 static void matcherlist_skip_headers(FILE *fp)
445 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
450 matcherprop_match_one_header
451 returns TRUE if buf matchs the MatchersProp criteria
454 static gboolean matcherprop_match_one_header(MatcherProp * matcher,
460 switch(matcher->criteria) {
461 case MATCHCRITERIA_HEADER:
462 case MATCHCRITERIA_NOT_HEADER:
463 header = procheader_parse_header(buf);
466 if (procheader_headername_equal(header->name,
468 if (matcher->criteria == MATCHCRITERIA_HEADER)
469 result = matcherprop_string_match(matcher, header->body);
471 result = !matcherprop_string_match(matcher, header->body);
472 procheader_header_free(header);
476 procheader_header_free(header);
479 case MATCHCRITERIA_HEADERS_PART:
480 case MATCHCRITERIA_MESSAGE:
481 return matcherprop_string_match(matcher, buf);
482 case MATCHCRITERIA_NOT_MESSAGE:
483 case MATCHCRITERIA_NOT_HEADERS_PART:
484 return !matcherprop_string_match(matcher, buf);
490 matcherprop_criteria_header
491 returns TRUE if the headers must be matched
494 static gboolean matcherprop_criteria_headers(MatcherProp * matcher)
496 switch(matcher->criteria) {
497 case MATCHCRITERIA_HEADER:
498 case MATCHCRITERIA_NOT_HEADER:
499 case MATCHCRITERIA_HEADERS_PART:
500 case MATCHCRITERIA_NOT_HEADERS_PART:
507 static gboolean matcherprop_criteria_message(MatcherProp * matcher)
509 switch(matcher->criteria) {
510 case MATCHCRITERIA_MESSAGE:
511 case MATCHCRITERIA_NOT_MESSAGE:
519 matcherlist_match_one_header
520 returns TRUE if match should stop
523 static gboolean matcherlist_match_one_header(MatcherList * matchers,
528 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
529 MatcherProp * matcher = (MatcherProp *) l->data;
531 if (matcherprop_criteria_headers(matcher) ||
532 matcherprop_criteria_message(matcher)) {
533 if (matcherprop_match_one_header(matcher, buf)) {
534 matcher->result = TRUE;
538 if (matcherprop_criteria_headers(matcher)) {
539 if (matcher->result) {
540 if (!matchers->bool_and)
550 matcherlist_match_headers
551 returns TRUE if one of the headers matchs the MatcherList criteria
554 static gboolean matcherlist_match_headers(MatcherList * matchers, FILE * fp)
558 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1)
559 if (matcherlist_match_one_header(matchers, buf))
566 matcherprop_criteria_body
567 returns TRUE if the body must be matched
570 static gboolean matcherprop_criteria_body(MatcherProp * matcher)
572 switch(matcher->criteria) {
573 case MATCHCRITERIA_BODY_PART:
574 case MATCHCRITERIA_NOT_BODY_PART:
582 matcherprop_match_line
583 returns TRUE if the string matchs the MatcherProp criteria
586 static gboolean matcherprop_match_line(MatcherProp * matcher, gchar * line)
588 switch(matcher->criteria) {
589 case MATCHCRITERIA_BODY_PART:
590 case MATCHCRITERIA_MESSAGE:
591 return matcherprop_string_match(matcher, line);
592 case MATCHCRITERIA_NOT_BODY_PART:
593 case MATCHCRITERIA_NOT_MESSAGE:
594 return !matcherprop_string_match(matcher, line);
600 matcherlist_match_line
601 returns TRUE if the string matchs the MatcherList criteria
604 static gboolean matcherlist_match_line(MatcherList * matchers, gchar * line)
608 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
609 MatcherProp * matcher = (MatcherProp *) l->data;
611 if (matcherprop_criteria_body(matcher) ||
612 matcherprop_criteria_message(matcher)) {
613 if (matcherprop_match_line(matcher, line)) {
614 matcher->result = TRUE;
618 if (matcher->result) {
619 if (!matchers->bool_and)
627 matcherlist_match_body
628 returns TRUE if one line of the body matchs the MatcherList criteria
631 static gboolean matcherlist_match_body(MatcherList * matchers, FILE * fp)
635 while (fgets(buf, sizeof(buf), fp) != NULL)
636 if (matcherlist_match_line(matchers, buf))
642 gboolean matcherlist_match_file(MatcherList * matchers, MsgInfo * info,
645 gboolean read_headers;
651 /* file need to be read ? */
653 read_headers = FALSE;
655 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
656 MatcherProp * matcher = (MatcherProp *) l->data;
658 if (matcherprop_criteria_headers(matcher))
660 if (matcherprop_criteria_body(matcher))
662 if (matcherprop_criteria_message(matcher)) {
666 matcher->result = FALSE;
669 if (!read_headers && !read_body)
672 file = procmsg_get_message_file(info);
676 if ((fp = fopen(file, "rb")) == NULL) {
677 FILE_OP_ERROR(file, "fopen");
682 /* read the headers */
685 if (matcherlist_match_headers(matchers, fp))
689 matcherlist_skip_headers(fp);
694 matcherlist_match_body(matchers, fp);
697 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
698 MatcherProp * matcher = (MatcherProp *) l->data;
700 if (matcherprop_criteria_headers(matcher) ||
701 matcherprop_criteria_body(matcher) ||
702 matcherprop_criteria_message(matcher)) {
703 if (matcher->result) {
704 if (!matchers->bool_and) {
710 if (matchers->bool_and) {
725 /* test a list of condition */
727 gboolean matcherlist_match(MatcherList * matchers, MsgInfo * info)
732 if (matchers->bool_and)
737 /* test the cached elements */
739 for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
740 MatcherProp * matcher = (MatcherProp *) l->data;
742 switch(matcher->criteria) {
743 case MATCHCRITERIA_ALL:
744 case MATCHCRITERIA_UNREAD:
745 case MATCHCRITERIA_NOT_UNREAD:
746 case MATCHCRITERIA_NEW:
747 case MATCHCRITERIA_NOT_NEW:
748 case MATCHCRITERIA_MARKED:
749 case MATCHCRITERIA_NOT_MARKED:
750 case MATCHCRITERIA_DELETED:
751 case MATCHCRITERIA_NOT_DELETED:
752 case MATCHCRITERIA_REPLIED:
753 case MATCHCRITERIA_NOT_REPLIED:
754 case MATCHCRITERIA_FORWARDED:
755 case MATCHCRITERIA_NOT_FORWARDED:
756 case MATCHCRITERIA_SUBJECT:
757 case MATCHCRITERIA_NOT_SUBJECT:
758 case MATCHCRITERIA_FROM:
759 case MATCHCRITERIA_NOT_FROM:
760 case MATCHCRITERIA_TO:
761 case MATCHCRITERIA_NOT_TO:
762 case MATCHCRITERIA_CC:
763 case MATCHCRITERIA_NOT_CC:
764 case MATCHCRITERIA_TO_OR_CC:
765 case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
766 case MATCHCRITERIA_AGE_GREATER:
767 case MATCHCRITERIA_AGE_LOWER:
768 case MATCHCRITERIA_NEWSGROUPS:
769 case MATCHCRITERIA_NOT_NEWSGROUPS:
770 case MATCHCRITERIA_INREPLYTO:
771 case MATCHCRITERIA_NOT_INREPLYTO:
772 case MATCHCRITERIA_REFERENCES:
773 case MATCHCRITERIA_NOT_REFERENCES:
774 case MATCHCRITERIA_SCORE_GREATER:
775 case MATCHCRITERIA_SCORE_LOWER:
776 case MATCHCRITERIA_SCORE_EQUAL:
777 case MATCHCRITERIA_SIZE_GREATER:
778 case MATCHCRITERIA_SIZE_SMALLER:
779 case MATCHCRITERIA_SIZE_EQUAL:
780 case MATCHCRITERIA_EXECUTE:
781 case MATCHCRITERIA_NOT_EXECUTE:
782 if (matcherprop_match(matcher, info)) {
783 if (!matchers->bool_and) {
788 if (matchers->bool_and) {
795 /* test the condition on the file */
797 if (matcherlist_match_file(matchers, info, result)) {
798 if (!matchers->bool_and)
802 if (matchers->bool_and)
810 gchar * matcherprop_to_string(MatcherProp * matcher)
812 gchar * matcher_str = NULL;
813 gchar * criteria_str;
814 gchar * matchtype_str;
822 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
824 if (matchparser_tab[i].id == matcher->criteria)
825 criteria_str = matchparser_tab[i].str;
827 if (criteria_str == NULL)
830 switch(matcher->criteria) {
831 case MATCHCRITERIA_AGE_GREATER:
832 case MATCHCRITERIA_AGE_LOWER:
833 case MATCHCRITERIA_SCORE_GREATER:
834 case MATCHCRITERIA_SCORE_LOWER:
835 case MATCHCRITERIA_SCORE_EQUAL:
836 case MATCHCRITERIA_SIZE_GREATER:
837 case MATCHCRITERIA_SIZE_SMALLER:
838 case MATCHCRITERIA_SIZE_EQUAL:
839 return g_strdup_printf("%s %i", criteria_str, matcher->value);
840 case MATCHCRITERIA_ALL:
841 case MATCHCRITERIA_UNREAD:
842 case MATCHCRITERIA_NOT_UNREAD:
843 case MATCHCRITERIA_NEW:
844 case MATCHCRITERIA_NOT_NEW:
845 case MATCHCRITERIA_MARKED:
846 case MATCHCRITERIA_NOT_MARKED:
847 case MATCHCRITERIA_DELETED:
848 case MATCHCRITERIA_NOT_DELETED:
849 case MATCHCRITERIA_REPLIED:
850 case MATCHCRITERIA_NOT_REPLIED:
851 case MATCHCRITERIA_FORWARDED:
852 case MATCHCRITERIA_NOT_FORWARDED:
853 return g_strdup(criteria_str);
854 case MATCHCRITERIA_EXECUTE:
855 case MATCHCRITERIA_NOT_EXECUTE:
856 return g_strdup_printf("%s \"%s\"", criteria_str, matcher->expr);
859 matchtype_str = NULL;
860 for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
862 if (matchparser_tab[i].id == matcher->matchtype)
863 matchtype_str = matchparser_tab[i].str;
866 if (matchtype_str == NULL)
869 switch (matcher->matchtype) {
870 case MATCHTYPE_MATCH:
871 case MATCHTYPE_MATCHCASE:
872 case MATCHTYPE_REGEXP:
873 case MATCHTYPE_REGEXPCASE:
876 g_strdup_printf("%s \"%s\" %s \"%s\"",
877 criteria_str, matcher->header,
878 matchtype_str, matcher->expr);
881 g_strdup_printf("%s %s \"%s\"", criteria_str,
882 matchtype_str, matcher->expr);
889 gchar * matcherlist_to_string(MatcherList * matchers)
897 count = g_slist_length(matchers->matchers);
898 vstr = g_new(gchar *, count + 1);
900 for (l = matchers->matchers, cur_str = vstr ; l != NULL ;
901 l = g_slist_next(l), cur_str ++) {
902 *cur_str = matcherprop_to_string((MatcherProp *) l->data);
903 if (*cur_str == NULL)
908 if (matchers->bool_and)
909 result = g_strjoinv(" & ", vstr);
911 result = g_strjoinv(" | ", vstr);
913 for(cur_str = vstr ; *cur_str != NULL ; cur_str ++)
920 #define STRLEN_ZERO(s) ((s) ? strlen(s) : 0)
921 #define STRLEN_DEFAULT(s,d) ((s) ? strlen(s) : STRLEN_ZERO(d))
922 /* matching_build_command() - preferably cmd should be unescaped */
923 gchar * matching_build_command(gchar * cmd, MsgInfo * info)
926 gchar * filename = NULL;
927 gchar * processed_cmd;
931 const gchar *const no_subject = _("(none)") ;
932 const gchar *const no_from = _("(none)") ;
933 const gchar *const no_to = _("(none)") ;
934 const gchar *const no_cc = _("(none)") ;
935 const gchar *const no_date = _("(none)") ;
936 const gchar *const no_msgid = _("(none)") ;
937 const gchar *const no_newsgroups = _("(none)") ;
938 const gchar *const no_references = _("(none)") ;
940 size = STRLEN_ZERO(cmd) + 1;
948 case 's': /* subject */
949 size += STRLEN_DEFAULT(info->subject, no_subject) - 2;
952 size += STRLEN_DEFAULT(info->from, no_from) - 2;
955 size += STRLEN_DEFAULT(info->to, no_to) - 2;
958 size += STRLEN_DEFAULT(info->cc, no_cc) - 2;
961 size += STRLEN_DEFAULT(info->date, no_date) - 2;
963 case 'i': /* message-id */
964 size += STRLEN_DEFAULT(info->msgid, no_msgid) - 2;
966 case 'n': /* newsgroups */
967 size += STRLEN_DEFAULT(info->newsgroups, no_newsgroups) - 2;
969 case 'r': /* references */
970 size += STRLEN_DEFAULT(info->references, no_references) - 2;
973 filename = folder_item_fetch_msg(info->folder,
976 if (filename == NULL) {
977 g_warning(_("filename is not set"));
981 size += strlen(filename) - 2;
990 processed_cmd = g_new0(gchar, size);
1002 case 's': /* subject */
1003 if (info->subject != NULL)
1004 strcpy(p, info->subject);
1006 strcpy(p, no_subject);
1009 case 'f': /* from */
1010 if (info->from != NULL)
1011 strcpy(p, info->from);
1017 if (info->to != NULL)
1018 strcpy(p, info->to);
1024 if (info->cc != NULL)
1025 strcpy(p, info->cc);
1030 case 'd': /* date */
1031 if (info->date != NULL)
1032 strcpy(p, info->date);
1037 case 'i': /* message-id */
1038 if (info->msgid != NULL)
1039 strcpy(p, info->msgid);
1041 strcpy(p, no_msgid);
1044 case 'n': /* newsgroups */
1045 if (info->newsgroups != NULL)
1046 strcpy(p, info->newsgroups);
1048 strcpy(p, no_newsgroups);
1051 case 'r': /* references */
1052 if (info->references != NULL)
1053 strcpy(p, info->references);
1055 strcpy(p, no_references);
1058 case 'F': /* file */
1059 strcpy(p, filename);
1078 debug_print("*** exec string \"%s\"\n", processed_cmd);
1079 return processed_cmd;
1081 #undef STRLEN_DEFAULT
1084 /* ************************************************************ */
1086 static void prefs_scoring_write(FILE * fp, GSList * prefs_scoring)
1090 for (cur = prefs_scoring; cur != NULL; cur = cur->next) {
1094 prop = (ScoringProp *) cur->data;
1095 scoring_str = scoringprop_to_string(prop);
1096 if (fputs(scoring_str, fp) == EOF ||
1097 fputc('\n', fp) == EOF) {
1098 FILE_OP_ERROR("scoring config", "fputs || fputc");
1099 g_free(scoring_str);
1102 g_free(scoring_str);
1106 static void prefs_filtering_write(FILE * fp, GSList * prefs_scoring)
1110 for (cur = prefs_scoring; cur != NULL; cur = cur->next) {
1111 gchar *filtering_str;
1112 FilteringProp * prop;
1114 if (NULL == (prop = (FilteringProp *) cur->data))
1117 if (NULL == (filtering_str = filteringprop_to_string(prop)))
1120 if (fputs(filtering_str, fp) == EOF ||
1121 fputc('\n', fp) == EOF) {
1122 FILE_OP_ERROR("filtering config", "fputs || fputc");
1123 g_free(filtering_str);
1126 g_free(filtering_str);
1130 static gboolean prefs_matcher_write_func(GNode *node, gpointer data)
1135 GSList * prefs_scoring;
1136 GSList * prefs_filtering;
1140 /* prevent from the warning */
1141 if (item->path == NULL)
1143 id = folder_item_get_identifier(item);
1146 prefs_scoring = item->prefs->scoring;
1147 prefs_filtering = item->prefs->processing;
1151 id = g_strdup("global"); /* because it is g_freed */
1152 prefs_scoring = global_scoring;
1153 prefs_filtering = global_processing;
1156 if (prefs_filtering != NULL || prefs_scoring != NULL) {
1157 fprintf(fp, "[%s]\n", id);
1159 prefs_filtering_write(fp, prefs_filtering);
1160 prefs_scoring_write(fp, prefs_scoring);
1170 static void prefs_matcher_save(FILE * fp)
1174 for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
1177 folder = (Folder *) cur->data;
1178 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1179 prefs_matcher_write_func, fp);
1181 prefs_matcher_write_func(NULL, fp);
1185 void prefs_matcher_write_config(void)
1192 debug_print(_("Writing matcher configuration...\n"));
1194 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1197 if ((pfile = prefs_write_open(rcpath)) == NULL) {
1198 g_warning(_("failed to write configuration to file\n"));
1204 prefs_matcher_save(pfile->fp);
1208 if (prefs_write_close(pfile) < 0) {
1209 g_warning(_("failed to write configuration to file\n"));
1214 /* ******************************************************************* */
1216 void prefs_matcher_read_config(void)
1221 prefs_scoring_clear();
1222 prefs_filtering_clear();
1224 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MATCHER_RC, NULL);
1225 f = fopen(rcpath, "rb");
1229 matcher_parser_start_parsing(f);
1233 /* previous version compatibily */
1235 /* printf("reading filtering\n"); */
1236 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1237 FILTERING_RC, NULL);
1238 f = fopen(rcpath, "rb");
1242 matcher_parser_start_parsing(f);
1243 fclose(matcher_parserin);
1246 /* printf("reading scoring\n"); */
1247 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1249 f = fopen(rcpath, "rb");
1253 matcher_parser_start_parsing(f);
1254 fclose(matcher_parserin);