2006-05-16 [wwp] 2.2.0cvs20
[claws.git] / src / matcher.c
index 2652313cb4ead9ce2e6390e61354bb7f74891a0f..1c38987195c52e1eb459675fb4ae4ef5775c713c 100644 (file)
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #include <glib.h>
@@ -31,6 +31,8 @@
 #include "matcher_parser.h"
 #include "prefs_gtk.h"
 #include "addr_compl.h"
+#include "codeconv.h"
+#include "quoted-printable.h"
 #include <ctype.h>
 
 /*!
@@ -137,6 +139,7 @@ static const MatchParser matchparser_tab[] = {
        {MATCHACTION_SET_SCORE, "set_score"},
        {MATCHACTION_STOP, "stop"},
        {MATCHACTION_HIDE, "hide"},
+       {MATCHACTION_IGNORE, "ignore"},
 };
 
 /*!
@@ -234,10 +237,8 @@ MatcherProp *matcherprop_new(gint criteria, const gchar *header,
  */
 void matcherprop_free(MatcherProp *prop)
 {
-       if (prop->expr) 
-               g_free(prop->expr);
-       if (prop->header)
-               g_free(prop->header);
+       g_free(prop->expr);
+       g_free(prop->header);
        if (prop->preg != NULL) {
                regfree(prop->preg);
                g_free(prop->preg);
@@ -367,6 +368,48 @@ static gboolean matcherprop_string_match(MatcherProp *prop, const gchar *str)
        }
 }
 
+/* FIXME body search is a hack. */
+static gboolean matcherprop_string_decode_match(MatcherProp *prop, const gchar *str)
+{
+       gchar *utf = NULL;
+       gchar tmp[BUFFSIZE];
+       gboolean res = FALSE;
+
+       if (str == NULL)
+               return FALSE;
+
+       /* we try to decode QP first, because it's faster than base64 */
+       qp_decode_const(tmp, BUFFSIZE-1, str);
+       if (!g_utf8_validate(tmp, -1, NULL)) {
+               utf = conv_codeset_strdup
+                       (tmp, conv_get_locale_charset_str_no_utf8(),
+                        CS_INTERNAL);
+               res = matcherprop_string_match(prop, utf);
+               g_free(utf);
+       } else {
+               res = matcherprop_string_match(prop, tmp);
+       }
+       
+       if (res == FALSE && (strchr(prop->expr, '=') || strchr(prop->expr, '_')) ) {
+               /* if searching for something with an equal char, maybe 
+                * we should try to match the non-decoded string. 
+                * In case it was not qp-encoded. */
+               if (!g_utf8_validate(str, -1, NULL)) {
+                       utf = conv_codeset_strdup
+                               (str, conv_get_locale_charset_str_no_utf8(),
+                                CS_INTERNAL);
+                       res = matcherprop_string_match(prop, utf);
+               } else {
+                       res = matcherprop_string_match(prop, str);
+               }
+       }
+
+       /* FIXME base64 decoding is too slow, especially since text can 
+        * easily be handled as base64. Don't even try now. */
+
+       return res;
+}
+
 /*!
  *\brief       Execute a command defined in the matcher structure
  *
@@ -608,9 +651,11 @@ static gboolean matcherprop_match_one_header(MatcherProp *matcher,
                }
                break;
        case MATCHCRITERIA_HEADERS_PART:
-       case MATCHCRITERIA_MESSAGE:
                return matcherprop_string_match(matcher, buf);
+       case MATCHCRITERIA_MESSAGE:
+               return matcherprop_string_decode_match(matcher, buf);
        case MATCHCRITERIA_NOT_MESSAGE:
+               return !matcherprop_string_decode_match(matcher, buf);
        case MATCHCRITERIA_NOT_HEADERS_PART:
                return !matcherprop_string_match(matcher, buf);
        }
@@ -660,41 +705,6 @@ static gboolean matcherprop_criteria_message(MatcherProp *matcher)
        }
 }
 
-/*!
- *\brief       Check if a list of conditions match a header
- *
- *\param       matchers One set of conditions
- *\param       buf Name of header
- *
- *\return      gboolean TRUE if matching should stop
- */
-static gboolean matcherlist_match_one_header(MatcherList *matchers,
-                                            gchar *buf)
-{
-       GSList *l;
-
-       for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
-               MatcherProp *matcher = (MatcherProp *) l->data;
-               
-               /* see if a single condition matches */
-               if (matcherprop_criteria_headers(matcher) ||
-                   matcherprop_criteria_message(matcher)) {
-                       if (matcherprop_match_one_header(matcher, buf)) {
-                               matcher->result = TRUE;
-                       }
-               }
-
-               if (matcherprop_criteria_headers(matcher)) {
-                       if (matcher->result) {
-                               if (!matchers->bool_and)
-                                       return TRUE;
-                       }
-               }
-       }
-
-       return FALSE;
-}
-
 /*!
  *\brief       Check if a list of conditions matches one header in
  *             a message file.
@@ -707,12 +717,44 @@ static gboolean matcherlist_match_one_header(MatcherList *matchers,
  */
 static gboolean matcherlist_match_headers(MatcherList *matchers, FILE *fp)
 {
+       GSList *l;
        gchar buf[BUFFSIZE];
 
-       while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1)
-               if (matcherlist_match_one_header(matchers, buf))
-                       return TRUE;
-
+       while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
+               for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
+                       MatcherProp *matcher = (MatcherProp *) l->data;
+
+                       if (matcher->done)
+                               continue;
+
+                       /* if the criteria is ~headers_part or ~message, ZERO lines
+                        * must NOT match for the rule to match. */
+                       if (matcher->criteria == MATCHCRITERIA_NOT_HEADERS_PART ||
+                           matcher->criteria == MATCHCRITERIA_NOT_MESSAGE) {
+                               if (matcherprop_match_one_header(matcher, buf)) {
+                                       matcher->result = TRUE;
+                               } else {
+                                       matcher->result = FALSE;
+                                       matcher->done = TRUE;
+                               }
+                       /* else, just one line matching is enough for the rule to match
+                        */
+                       } else if (matcherprop_criteria_headers(matcher) ||
+                                  matcherprop_criteria_message(matcher)){
+                               if (matcherprop_match_one_header(matcher, buf)) {
+                                       matcher->result = TRUE;
+                                       matcher->done = TRUE;
+                               }
+                       }
+                       
+                       /* if the rule matched and the matchers are OR, no need to
+                        * check the others */
+                       if (matcher->result && matcher->done) {
+                               if (!matchers->bool_and)
+                                       return TRUE;
+                       }
+               }
+       }
        return FALSE;
 }
 
@@ -748,40 +790,10 @@ static gboolean matcherprop_match_line(MatcherProp *matcher, const gchar *line)
        switch (matcher->criteria) {
        case MATCHCRITERIA_BODY_PART:
        case MATCHCRITERIA_MESSAGE:
-               return matcherprop_string_match(matcher, line);
+               return matcherprop_string_decode_match(matcher, line);
        case MATCHCRITERIA_NOT_BODY_PART:
        case MATCHCRITERIA_NOT_MESSAGE:
-               return !matcherprop_string_match(matcher, line);
-       }
-       return FALSE;
-}
-
-/*!
- *\brief       Check if a list of conditions matches a (line) string
- *
- *\param       matchers List of matchers
- *\param       line String to match
- *
- *\return      gboolean TRUE if string matches list of criteria
- */
-static gboolean matcherlist_match_line(MatcherList *matchers, const gchar *line)
-{
-       GSList *l;
-
-       for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
-               MatcherProp *matcher = (MatcherProp *) l->data;
-
-               if (matcherprop_criteria_body(matcher) ||
-                   matcherprop_criteria_message(matcher)) {
-                       if (matcherprop_match_line(matcher, line)) {
-                               matcher->result = TRUE;
-                       }
-               }
-                       
-               if (matcher->result) {
-                       if (!matchers->bool_and)
-                               return TRUE;
-               }
+               return !matcherprop_string_decode_match(matcher, line);
        }
        return FALSE;
 }
@@ -797,12 +809,43 @@ static gboolean matcherlist_match_line(MatcherList *matchers, const gchar *line)
  */
 static gboolean matcherlist_match_body(MatcherList *matchers, FILE *fp)
 {
+       GSList *l;
        gchar buf[BUFFSIZE];
+       
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
+                       MatcherProp *matcher = (MatcherProp *) l->data;
+                       
+                       if (matcher->done) 
+                               continue;
+
+                       /* if the criteria is ~body_part or ~message, ZERO lines
+                        * must NOT match for the rule to match. */
+                       if (matcher->criteria == MATCHCRITERIA_NOT_BODY_PART ||
+                           matcher->criteria == MATCHCRITERIA_NOT_MESSAGE) {
+                               if (matcherprop_match_line(matcher, buf)) {
+                                       matcher->result = TRUE;
+                               } else {
+                                       matcher->result = FALSE;
+                                       matcher->done = TRUE;
+                               }
+                       /* else, just one line has to match */
+                       } else if (matcherprop_criteria_body(matcher) ||
+                                  matcherprop_criteria_message(matcher)) {
+                               if (matcherprop_match_line(matcher, buf)) {
+                                       matcher->result = TRUE;
+                                       matcher->done = TRUE;
+                               }
+                       }
 
-       while (fgets(buf, sizeof(buf), fp) != NULL)
-               if (matcherlist_match_line(matchers, buf))
-                       return TRUE;
-
+                       /* if the matchers are OR'ed and the rule matched,
+                        * no need to check the others. */
+                       if (matcher->result && matcher->done) {
+                               if (!matchers->bool_and)
+                                       return TRUE;
+                       }
+               }
+       }
        return FALSE;
 }
 
@@ -840,6 +883,7 @@ gboolean matcherlist_match_file(MatcherList *matchers, MsgInfo *info,
                        read_body = TRUE;
                }
                matcher->result = FALSE;
+               matcher->done = FALSE;
        }
 
        if (!read_headers && !read_body)
@@ -860,8 +904,7 @@ gboolean matcherlist_match_file(MatcherList *matchers, MsgInfo *info,
        if (read_headers) {
                if (matcherlist_match_headers(matchers, fp))
                        read_body = FALSE;
-       }
-       else {
+       } else {
                matcherlist_skip_headers(fp);
        }
 
@@ -911,6 +954,9 @@ gboolean matcherlist_match(MatcherList *matchers, MsgInfo *info)
        GSList *l;
        gboolean result;
 
+       if (!matchers)
+               return FALSE;
+
        if (matchers->bool_and)
                result = TRUE;
        else
@@ -1392,7 +1438,19 @@ static void prefs_filtering_write(FILE *fp, GSList *prefs_filtering)
                
                if (NULL == (filtering_str = filteringprop_to_string(prop)))
                        continue;
-                               
+
+               if (prop->enabled) {
+                       if (fputs("enabled ", fp) == EOF) {
+                               FILE_OP_ERROR("filtering config", "fputs || fputc");
+                               return;
+                       }
+               } else {
+                       if (fputs("disabled ", fp) == EOF) {
+                               FILE_OP_ERROR("filtering config", "fputs || fputc");
+                               return;
+                       }
+               }
+
                if (fputs("rulename \"", fp) == EOF) {
                        FILE_OP_ERROR("filtering config", "fputs || fputc");
                        g_free(filtering_str);
@@ -1577,6 +1635,9 @@ void prefs_matcher_read_config(void)
                /* 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);