Fix typos reported on irc (thanks!)
[claws.git] / src / matcher.c
index 19fa12a3aef646e5dfd22b075ecd4a179ea9f9fe..ad58ed26c8f6ba352d109b8798e46f1ca109409f 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
@@ -135,6 +135,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"},
@@ -313,7 +315,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 +334,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 +359,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;            
@@ -614,18 +622,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;
 }
 
@@ -1134,6 +1156,107 @@ MatcherList *matcherlist_new(GSList *matchers, gboolean bool_and)
        return cond;
 }
 
+#ifdef G_OS_UNIX
+/*!
+ *\brief       Builds a single regular expresion from an array of srings.
+ *
+ *\param       strings The lines containing the different sub-regexp.
+ *
+ *\return      The newly allocated regexp string.
+ */
+static gchar *build_complete_regexp(gchar **strings)
+{
+       int i = 0;
+       gchar *expr = NULL;
+       while (strings && strings[i] && *strings[i]) {
+               int old_len = expr ? strlen(expr):0;
+               int new_len = 0;
+               gchar *tmpstr = NULL;
+
+               if (g_utf8_validate(strings[i], -1, NULL))
+                       tmpstr = g_strdup(strings[i]);
+               else
+                       tmpstr = conv_codeset_strdup(strings[i], 
+                                       conv_get_locale_charset_str_no_utf8(),
+                                       CS_INTERNAL);
+
+               if (strstr(tmpstr, "\n"))
+                       *(strstr(tmpstr, "\n")) = '\0';
+
+               new_len = strlen(tmpstr);
+
+               expr = g_realloc(expr, 
+                       expr ? (old_len + strlen("|()") + new_len + 1)
+                            : (strlen("()") + new_len + 1));
+               
+               if (old_len) {
+                       strcpy(expr + old_len, "|(");
+                       strcpy(expr + old_len + 2, tmpstr);
+                       strcpy(expr + old_len + 2 + new_len, ")");
+               } else {
+                       strcpy(expr+old_len, "(");
+                       strcpy(expr+old_len + 1, tmpstr);
+                       strcpy(expr+old_len + 1 + new_len, ")");
+               }
+               g_free(tmpstr);
+               i++;
+       }
+       return expr;
+}
+#endif
+
+/*!
+ *\brief       Create a new list of matchers from a multi-line string
+ *
+ *\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, 
+                                       gboolean case_sensitive)
+{
+       MatcherProp *m = NULL;
+       GSList *matchers = NULL;
+       gchar **strings = g_strsplit(lines, "\n", -1);
+
+#ifdef G_OS_UNIX
+       gchar *expr = NULL;
+       expr = build_complete_regexp(strings);
+       debug_print("building matcherprop for expr '%s'\n", expr?expr:"NULL");
+       
+       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");
+       } else {
+               matchers = g_slist_append(matchers, m);
+       }
+
+       g_free(expr);
+#else
+       int i = 0;
+       while (strings && strings[i] && *strings[i]) {
+               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");
+               } else {
+                       matchers = g_slist_append(matchers, m);
+               }
+               i++;
+       }
+#endif
+       g_strfreev(strings);
+
+       return matcherlist_new(matchers, bool_and);
+}
+
 /*!
  *\brief       Frees a list of matchers
  *
@@ -1198,21 +1321,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:
@@ -1222,7 +1351,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)
@@ -1284,7 +1413,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;
@@ -1339,6 +1470,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;
 
@@ -1424,7 +1556,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);
@@ -1444,9 +1576,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