fix crash introduced in c7e1e0bbdf72269708a53075a4accecb61ebebe2
[claws.git] / src / matcher.c
index c95655cf49889b1e5a05b94d0ff8c7bbb3d5f4bc..83047c06e8aec138673d1c26e978b890474b9f64 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 2002-2012 by the Claws Mail Team and Hiroyuki Yamamoto
+ * Copyright (C) 2002-2014 by the Claws Mail Team and Hiroyuki Yamamoto
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -114,6 +114,8 @@ static const MatchParser matchparser_tab[] = {
        {MATCHCRITERIA_AGE_LOWER_HOURS, "age_lower_hours"},
        {MATCHCRITERIA_NEWSGROUPS, "newsgroups"},
        {MATCHCRITERIA_NOT_NEWSGROUPS, "~newsgroups"},
+       {MATCHCRITERIA_MESSAGEID, "messageid"},
+       {MATCHCRITERIA_NOT_MESSAGEID, "~messageid"},
        {MATCHCRITERIA_INREPLYTO, "inreplyto"},
        {MATCHCRITERIA_NOT_INREPLYTO, "~inreplyto"},
        {MATCHCRITERIA_REFERENCES, "references"},
@@ -135,6 +137,8 @@ static const MatchParser matchparser_tab[] = {
        {MATCHCRITERIA_NOT_HEADER, "~header"},
        {MATCHCRITERIA_HEADERS_PART, "headers_part"},
        {MATCHCRITERIA_NOT_HEADERS_PART, "~headers_part"},
+       {MATCHCRITERIA_HEADERS_CONT, "headers_cont"},
+       {MATCHCRITERIA_NOT_HEADERS_CONT, "~headers_cont"},
        {MATCHCRITERIA_MESSAGE, "message"},
        {MATCHCRITERIA_NOT_MESSAGE, "~message"},
        {MATCHCRITERIA_BODY_PART, "body_part"},
@@ -190,6 +194,7 @@ enum {
        CONTEXT_TO,
        CONTEXT_CC,
        CONTEXT_NEWSGROUPS,
+       CONTEXT_MESSAGEID,
        CONTEXT_IN_REPLY_TO,
        CONTEXT_REFERENCES,
        CONTEXT_HEADER,
@@ -211,6 +216,7 @@ void matcher_init(void)
        context_str[CONTEXT_TO] = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("To:"));
        context_str[CONTEXT_CC] = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("Cc:"));
        context_str[CONTEXT_NEWSGROUPS] = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("Newsgroups:"));
+       context_str[CONTEXT_MESSAGEID] = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("Message-ID:"));
        context_str[CONTEXT_IN_REPLY_TO] = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("In-Reply-To:"));
        context_str[CONTEXT_REFERENCES] = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("References:"));
        context_str[CONTEXT_HEADER] = g_strdup(_("header"));
@@ -313,7 +319,9 @@ MatcherProp *matcherprop_new(gint criteria, const gchar *header,
        prop->expr = expr != NULL ? g_strdup(expr) : NULL;
 
        prop->matchtype = matchtype;
+#ifndef G_OS_WIN32
        prop->preg = NULL;
+#endif
        prop->value = value;
        prop->error = 0;
 
@@ -330,10 +338,12 @@ void matcherprop_free(MatcherProp *prop)
 {
        g_free(prop->expr);
        g_free(prop->header);
+#ifndef G_OS_WIN32
        if (prop->preg != NULL) {
                regfree(prop->preg);
                g_free(prop->preg);
        }
+#endif
        g_free(prop);
 }
 
@@ -353,7 +363,9 @@ MatcherProp *matcherprop_copy(const MatcherProp *src)
        prop->expr = src->expr ? g_strdup(src->expr) : NULL;
        prop->matchtype = src->matchtype;
        
+#ifndef G_OS_WIN32
        prop->preg = NULL; /* will be re-evaluated */
+#endif
        prop->value = src->value;
        prop->error = src->error;       
        return prop;            
@@ -481,7 +493,6 @@ static gboolean matcherprop_string_match(MatcherProp *prop, const gchar *str,
        switch (prop->matchtype) {
        case MATCHTYPE_REGEXPCASE:
        case MATCHTYPE_REGEXP:
-#ifndef G_OS_WIN32
                if (!prop->preg && (prop->error == 0)) {
                        prop->preg = g_new0(regex_t, 1);
                        /* if regexp then don't use the escaped string */
@@ -524,7 +535,6 @@ static gboolean matcherprop_string_match(MatcherProp *prop, const gchar *str,
                        g_free(stripped);
                }
                break;
-#endif                 
        case MATCHTYPE_MATCHCASE:
        case MATCHTYPE_MATCH:
                ret = (strstr(str1, down_expr) != NULL);
@@ -614,18 +624,32 @@ const gchar *debug_context)
 }
 
 static gboolean matcherprop_header_line_match(MatcherProp *prop, const gchar *hdr,
-                                             const gchar *str, const gchar *debug_context)
+                                        const gchar *str, const gboolean both,
+                                        const gchar *debug_context)
 {
-       gchar *line = NULL;
        gboolean res = FALSE;
 
        if (hdr == NULL || str == NULL)
                return FALSE;
 
-       line = g_strdup_printf("%s %s", hdr, str);
-       res = matcherprop_string_match(prop, line, debug_context);
-       g_free(line);
-       
+       if (both) {
+               /* Search in all header names and content.
+                */
+               gchar *line = g_strdup_printf("%s %s", hdr, str);
+               res = matcherprop_string_match(prop, line, debug_context);
+               g_free(line);
+       } else {
+               /* Search only in content and exclude private headers.
+                * E.g.: searching for "H foo" in folder x/foo would return
+                * *all* mail as SCF and RMID will match.
+                * Searching for "H sent" would return all resent messages
+                * as "Resent-From: whatever" will match.
+                */
+               if (procheader_header_is_internal(hdr))
+                       return FALSE;
+               res = matcherprop_string_match(prop, str, debug_context);
+       }
+
        return res;
 }
 
@@ -1095,6 +1119,10 @@ static gboolean matcherprop_match(MatcherProp *prop,
                return matcherprop_string_match(prop, info->newsgroups, context_str[CONTEXT_NEWSGROUPS]);
        case MATCHCRITERIA_NOT_NEWSGROUPS:
                return !matcherprop_string_match(prop, info->newsgroups, context_str[CONTEXT_NEWSGROUPS]);
+       case MATCHCRITERIA_MESSAGEID:
+               return matcherprop_string_match(prop, info->msgid, context_str[CONTEXT_MESSAGEID]);
+       case MATCHCRITERIA_NOT_MESSAGEID:
+               return !matcherprop_string_match(prop, info->msgid, context_str[CONTEXT_MESSAGEID]);
        case MATCHCRITERIA_INREPLYTO:
                return matcherprop_string_match(prop, info->inreplyto, context_str[CONTEXT_IN_REPLY_TO]);
        case MATCHCRITERIA_NOT_INREPLYTO:
@@ -1188,10 +1216,12 @@ static gchar *build_complete_regexp(gchar **strings)
  *
  *\param       lines String with "\n"-separated expressions
  *\param       bool_and Operator
+ *\param       case_sensitive If the matching is case sensitive or not
  *
  *\return      MatcherList * New matcher list
  */
-MatcherList *matcherlist_new_from_lines(gchar *lines, gboolean bool_and)
+MatcherList *matcherlist_new_from_lines(gchar *lines, gboolean bool_and, 
+                                       gboolean case_sensitive)
 {
        MatcherProp *m = NULL;
        GSList *matchers = NULL;
@@ -1202,8 +1232,9 @@ MatcherList *matcherlist_new_from_lines(gchar *lines, gboolean bool_and)
        expr = build_complete_regexp(strings);
        debug_print("building matcherprop for expr '%s'\n", expr?expr:"NULL");
        
-       m = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL, MATCHTYPE_REGEXP, 
-                           expr, 0);
+       m = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL,
+                       case_sensitive? MATCHTYPE_REGEXP: MATCHTYPE_REGEXPCASE,
+                       expr, 0);
        if (m == NULL) {
                /* print error message */
                debug_print("failed to allocate memory for matcherprop\n");
@@ -1215,8 +1246,9 @@ MatcherList *matcherlist_new_from_lines(gchar *lines, gboolean bool_and)
 #else
        int i = 0;
        while (strings && strings[i] && *strings[i]) {
-               m = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL, MATCHTYPE_MATCHCASE, 
-                           strings[i], 0);
+               m = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL,
+                       case_sensitive? MATCHTYPE_MATCH: MATCHTYPE_MATCHCASE,
+                       strings[i], 0);
                if (m == NULL) {
                        /* print error message */
                        debug_print("failed to allocate memory for matcherprop\n");
@@ -1295,21 +1327,27 @@ static gboolean matcherprop_match_one_header(MatcherProp *matcher,
                }
                break;
        case MATCHCRITERIA_HEADERS_PART:
+       case MATCHCRITERIA_HEADERS_CONT:
        case MATCHCRITERIA_MESSAGE:
                header = procheader_parse_header(buf);
                if (!header)
                        return FALSE;
                result = matcherprop_header_line_match(matcher, 
-                              header->name, header->body, context_str[CONTEXT_HEADER_LINE]);
+                              header->name, header->body,
+                              (matcher->criteria == MATCHCRITERIA_HEADERS_PART),
+                              context_str[CONTEXT_HEADER_LINE]);
                procheader_header_free(header);
                return result;
+       case MATCHCRITERIA_NOT_HEADERS_CONT:
        case MATCHCRITERIA_NOT_HEADERS_PART:
        case MATCHCRITERIA_NOT_MESSAGE:
                header = procheader_parse_header(buf);
                if (!header)
                        return FALSE;
                result = !matcherprop_header_line_match(matcher, 
-                              header->name, header->body, context_str[CONTEXT_HEADER_LINE]);
+                              header->name, header->body,
+                              (matcher->criteria == MATCHCRITERIA_NOT_HEADERS_PART),
+                              context_str[CONTEXT_HEADER_LINE]);
                procheader_header_free(header);
                return result;
        case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
@@ -1319,7 +1357,7 @@ static gboolean matcherprop_match_one_header(MatcherProp *matcher,
                        gint match = MATCH_ONE;
                        gboolean found = FALSE;
 
-                       /* how many address headers are me trying to mach? */
+                       /* how many address headers are we trying to match? */
                        if (strcasecmp(matcher->header, "Any") == 0)
                                match = MATCH_ANY;
                        else if (strcasecmp(matcher->header, "All") == 0)
@@ -1381,7 +1419,9 @@ static gboolean matcherprop_criteria_headers(const MatcherProp *matcher)
        case MATCHCRITERIA_HEADER:
        case MATCHCRITERIA_NOT_HEADER:
        case MATCHCRITERIA_HEADERS_PART:
+       case MATCHCRITERIA_HEADERS_CONT:
        case MATCHCRITERIA_NOT_HEADERS_PART:
+       case MATCHCRITERIA_NOT_HEADERS_CONT:
        case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
        case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
                return TRUE;
@@ -1436,6 +1476,7 @@ static gboolean matcherlist_match_headers(MatcherList *matchers, FILE *fp)
 
                        /* determine the match range (all, any are our concern here) */
                        if (matcher->criteria == MATCHCRITERIA_NOT_HEADERS_PART ||
+                           matcher->criteria == MATCHCRITERIA_NOT_HEADERS_CONT ||
                            matcher->criteria == MATCHCRITERIA_NOT_MESSAGE) {
                                match = MATCH_ALL;
 
@@ -1521,7 +1562,7 @@ static gboolean matcherlist_match_binary_content(MatcherList *matchers, MimeInfo
        gchar buf[BUFFSIZE];
        GSList *l;
 
-       if (partinfo->type == MIMETYPE_TEXT)
+       if (!partinfo || partinfo->type == MIMETYPE_TEXT)
                return FALSE;
        else
                outfp = procmime_get_binary_content(partinfo);
@@ -1541,9 +1582,8 @@ static gboolean matcherlist_match_binary_content(MatcherList *matchers, MimeInfo
                        /* Don't scan non-text parts when looking in body, only
                         * when looking in whole message
                         */
-                       if (partinfo && partinfo->type != MIMETYPE_TEXT &&
-                       (matcher->criteria == MATCHCRITERIA_NOT_BODY_PART ||
-                       matcher->criteria == MATCHCRITERIA_BODY_PART))
+                       if (matcher->criteria == MATCHCRITERIA_NOT_BODY_PART ||
+                           matcher->criteria == MATCHCRITERIA_BODY_PART)
                                continue;
 
                        /* if the criteria is ~body_part or ~message, ZERO lines
@@ -1667,18 +1707,18 @@ static gboolean matcherlist_match_body(MatcherList *matchers, gboolean body_only
                if (partinfo->type == MIMETYPE_TEXT) {
                        first_text_found = TRUE;
                        if (matcherlist_match_text_content(matchers, partinfo)) {
-                               procmime_mimeinfo_free_all(mimeinfo);
+                               procmime_mimeinfo_free_all(&mimeinfo);
                                return TRUE;
                        }
                } else if (matcherlist_match_binary_content(matchers, partinfo)) {
-                       procmime_mimeinfo_free_all(mimeinfo);
+                       procmime_mimeinfo_free_all(&mimeinfo);
                        return TRUE;
                }
 
                if (body_only && first_text_found)
                        break;
        }
-       procmime_mimeinfo_free_all(mimeinfo);
+       procmime_mimeinfo_free_all(&mimeinfo);
 
        return FALSE;
 }
@@ -1858,6 +1898,8 @@ gboolean matcherlist_match(MatcherList *matchers, MsgInfo *info)
                case MATCHCRITERIA_AGE_LOWER_HOURS:
                case MATCHCRITERIA_NEWSGROUPS:
                case MATCHCRITERIA_NOT_NEWSGROUPS:
+               case MATCHCRITERIA_MESSAGEID:
+               case MATCHCRITERIA_NOT_MESSAGEID:
                case MATCHCRITERIA_INREPLYTO:
                case MATCHCRITERIA_NOT_INREPLYTO:
                case MATCHCRITERIA_REFERENCES:
@@ -2502,7 +2544,7 @@ void prefs_matcher_write_config(void)
                             MATCHER_RC, NULL);
 
        if ((pfile = prefs_write_open(rcpath)) == NULL) {
-               g_warning("failed to write configuration to file\n");
+               g_warning("failed to write configuration to file");
                g_free(rcpath);
                return;
        }
@@ -2510,85 +2552,25 @@ void prefs_matcher_write_config(void)
        g_free(rcpath);
 
        if (prefs_matcher_save(pfile->fp) < 0) {
-               g_warning("failed to write configuration to file\n");
+               g_warning("failed to write configuration to file");
                prefs_file_close_revert(pfile);
        } else if (prefs_file_close(pfile) < 0) {
-               g_warning("failed to save configuration to file\n");
+               g_warning("failed to save configuration to file");
        }
 }
 
-/* ******************************************************************* */
-
-static void matcher_add_rulenames(const gchar *rcpath)
-{
-       gchar *newpath = g_strconcat(rcpath, ".new", NULL);
-       FILE *src = g_fopen(rcpath, "rb");
-       FILE *dst = g_fopen(newpath, "wb");
-       gchar buf[BUFFSIZE];
-       int r;
-
-       if (src == NULL) {
-               perror("fopen");
-               if (dst)
-                       fclose(dst);
-               g_free(newpath);
-               return;
-       }
-       if (dst == NULL) {
-               perror("fopen");
-               if (src)
-                       fclose(src);
-               g_free(newpath);
-               return;
-       }
-
-       while (fgets (buf, sizeof(buf), src) != NULL) {
-               if (strlen(buf) > 2 && buf[0] != '['
-               && strncmp(buf, "rulename \"", 10)
-               && strncmp(buf, "enabled rulename \"", 18)
-               && strncmp(buf, "disabled rulename \"", 18)) {
-                       r = fwrite("enabled rulename \"\" ",
-                               strlen("enabled rulename \"\" "), 1, dst);
-                       if (r != 1) {
-                               g_message("cannot fwrite rulename\n");
-                       }
-               }
-               r = fwrite(buf, strlen(buf), 1, dst);
-               if (r != 1) {
-                       g_message("cannot fwrite rule\n");
-               }
-       }
-       fclose(dst);
-       fclose(src);
-       move_file(newpath, rcpath, TRUE);
-       g_free(newpath);
-}
-
 /*!
  *\brief       Read matcher configuration
  */
 void prefs_matcher_read_config(void)
 {
        gchar *rcpath;
-       gchar *rc_old_format;
        FILE *f;
 
        create_matchparser_hashtab();
        prefs_filtering_clear();
 
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MATCHER_RC, NULL);
-       rc_old_format = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MATCHER_RC, 
-                               ".pre_names", NULL);
-       
-       if (!is_file_exist(rc_old_format) && is_file_exist(rcpath)) {
-               /* backup file with no rules names, in case 
-                * anything goes wrong */
-               copy_file(rcpath, rc_old_format, FALSE);
-               /* now hack the file in order to have it to the new format */
-               matcher_add_rulenames(rcpath);
-       }
-       
-       g_free(rc_old_format);
 
        f = g_fopen(rcpath, "rb");
        g_free(rcpath);
@@ -2597,29 +2579,4 @@ void prefs_matcher_read_config(void)
                matcher_parser_start_parsing(f);
                fclose(matcher_parserin);
        }
-       else {
-               /* previous version compatibility */
-
-               /* g_print("reading filtering\n"); */
-               rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
-                                    FILTERING_RC, NULL);
-               f = g_fopen(rcpath, "rb");
-               g_free(rcpath);
-               
-               if (f != NULL) {
-                       matcher_parser_start_parsing(f);
-                       fclose(matcher_parserin);
-               }
-               
-               /* g_print("reading scoring\n"); */
-               rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
-                                    SCORING_RC, NULL);
-               f = g_fopen(rcpath, "rb");
-               g_free(rcpath);
-               
-               if (f != NULL) {
-                       matcher_parser_start_parsing(f);
-                       fclose(matcher_parserin);
-               }
-       }
 }