2006-09-26 [colin] 2.5.1cvs5
[claws.git] / src / matcher.c
index d116a93f15d79e5c7531f50450a26b216ad116c2..f6d4c2415f216d7643e9385054fec4008ac64994 100644 (file)
  *
  * 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.
  */
 
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <errno.h>
 
+#ifdef USE_PTHREAD
+#include <pthread.h>
+#endif
+
 #include "defs.h"
 #include "utils.h"
 #include "procheader.h"
@@ -31,6 +39,9 @@
 #include "matcher_parser.h"
 #include "prefs_gtk.h"
 #include "addr_compl.h"
+#include "codeconv.h"
+#include "quoted-printable.h"
+#include "sylpheed.h"
 #include <ctype.h>
 
 /*!
@@ -92,6 +103,8 @@ static const MatchParser matchparser_tab[] = {
        {MATCHCRITERIA_SCORE_EQUAL, "score_equal"},
        {MATCHCRITERIA_PARTIAL, "partial"},
        {MATCHCRITERIA_NOT_PARTIAL, "~partial"},
+       {MATCHCRITERIA_FOUND_IN_ADDRESSBOOK, "found_in_addressbook"},
+       {MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK, "~found_in_addressbook"},
 
        {MATCHCRITERIA_SIZE_GREATER, "size_greater"},
        {MATCHCRITERIA_SIZE_SMALLER, "size_smaller"},
@@ -114,8 +127,6 @@ static const MatchParser matchparser_tab[] = {
        {MATCHTYPE_MATCH, "match"},
        {MATCHTYPE_REGEXPCASE, "regexpcase"},
        {MATCHTYPE_REGEXP, "regexp"},
-       {MATCHTYPE_ANY_IN_ADDRESSBOOK, "any_in_addressbook"},
-       {MATCHTYPE_ALL_IN_ADDRESSBOOK, "all_in_addressbook"},
 
        /* actions */
        {MATCHACTION_SCORE, "score"},    /* for backward compatibility */
@@ -137,6 +148,13 @@ static const MatchParser matchparser_tab[] = {
        {MATCHACTION_SET_SCORE, "set_score"},
        {MATCHACTION_STOP, "stop"},
        {MATCHACTION_HIDE, "hide"},
+       {MATCHACTION_IGNORE, "ignore"},
+};
+
+enum {
+       MATCH_ANY = 0,
+       MATCH_ALL = 1,
+       MATCH_ONE = 2
 };
 
 /*!
@@ -201,7 +219,8 @@ gint get_matchparser_tab_id(const gchar *str)
  *             "condition" (a matcher structure)
  *
  *\param       criteria Criteria ID (MATCHCRITERIA_XXXX)
- *\param       header Header string (if criteria is MATCHCRITERIA_HEADER)
+ *\param       header Header string (if criteria is MATCHCRITERIA_HEADER
+                       or MATCHCRITERIA_FOUND_IN_ADDRESSBOOK)
  *\param       matchtype Type of action (MATCHTYPE_XXX)
  *\param       expr String value or expression to check
  *\param       value Integer value to check
@@ -234,10 +253,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);
@@ -270,38 +287,59 @@ MatcherProp *matcherprop_copy(const MatcherProp *src)
 /* ************** match ******************************/
 
 static gboolean match_with_addresses_in_addressbook
-       (MatcherProp *prop, const gchar *str, gint type)
+       (MatcherProp *prop, GSList *address_list, gint type,
+        gchar* folderpath, gint match)
 {
-       GSList *address_list = NULL;
-       GSList *walk;
-       gboolean res = FALSE;
+       GSList *walk = NULL;
+       gboolean found = FALSE;
+       gchar *path = NULL;
 
-       if (str == NULL || *str == 0) 
-               return FALSE;
+       g_return_val_if_fail(address_list != NULL, FALSE);
+
+       debug_print("match_with_addresses_in_addressbook(%d, %s)\n",
+                               g_slist_length(address_list), folderpath);
+
+       if (folderpath == NULL ||
+               strcasecmp(folderpath, _("Any")) == 0 ||
+               *folderpath == '\0')
+               path = NULL;
+       else
+               path = folderpath;
        
-       /* XXX: perhaps complete with comments too */
-       address_list = address_list_append(address_list, str);
-       if (!address_list) 
-               return FALSE;
+       start_address_completion(path);
 
-       start_address_completion();             
-       res = FALSE;
        for (walk = address_list; walk != NULL; walk = walk->next) {
-               gboolean found = complete_address(walk->data) ? TRUE : FALSE;
-               
+               /* exact matching of email address */
+               guint num_addr = complete_address(walk->data);
+               found = FALSE;
+               if (num_addr > 1) {
+                       /* skip first item (this is the search string itself) */
+                       int i = 1;
+                       for (; i < num_addr && !found; i++) {
+                               gchar *addr = get_complete_address(i);
+                               extract_address(addr);
+                               if (strcasecmp(addr, walk->data) == 0)
+                                       found = TRUE;
+                               g_free(addr);
+                       }
+               }
                g_free(walk->data);
-               if (!found && type == MATCHTYPE_ALL_IN_ADDRESSBOOK) {
-                       res = FALSE;
+
+               if (match == MATCH_ALL) {
+                       /* if matching all addresses, stop if one doesn't match */
+                       if (!found)
                        break;
-               } else if (found) 
-                       res = TRUE;
+               } else if (match == MATCH_ANY) {
+                       /* if matching any address, stop if one does match */
+                       if (found)
+                               break;
+       }
+               /* MATCH_ONE: there should be only one loop iteration */
        }
-
-       g_slist_free(address_list);
 
        end_address_completion();
        
-       return res;
+       return found;
 }
 
 /*!
@@ -344,11 +382,6 @@ static gboolean matcherprop_string_match(MatcherProp *prop, const gchar *str)
                else
                        return FALSE;
                        
-       case MATCHTYPE_ALL_IN_ADDRESSBOOK:      
-       case MATCHTYPE_ANY_IN_ADDRESSBOOK:
-               return match_with_addresses_in_addressbook
-                       (prop, str, prop->matchtype);
-
        case MATCHTYPE_MATCH:
                return (strstr(str, prop->expr) != NULL);
 
@@ -367,6 +400,73 @@ 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, '_')
+                           || strchr(str, '=') || strchr(str, '_'))) {
+               /* 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);
+                       g_free(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;
+}
+
+#ifdef USE_PTHREAD
+typedef struct _thread_data {
+       const gchar *cmd;
+       gboolean done;
+} thread_data;
+#endif
+
+#ifdef USE_PTHREAD
+void *matcher_test_thread(void *data)
+{
+       thread_data *td = (thread_data *)data;
+       int result = -1;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+       pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+       result = system(td->cmd);
+       if (result) perror("system");
+       td->done = TRUE; /* let the caller thread join() */
+       return GINT_TO_POINTER(result);
+}
+#endif
+
 /*!
  *\brief       Execute a command defined in the matcher structure
  *
@@ -381,6 +481,12 @@ static gboolean matcherprop_match_test(const MatcherProp *prop,
        gchar *file;
        gchar *cmd;
        gint retval;
+#ifdef USE_PTHREAD
+       pthread_t pt;
+       thread_data *td = g_new0(thread_data, 1);
+       void *res = NULL;
+       time_t start_time = time(NULL);
+#endif
 
        file = procmsg_get_message_file(info);
        if (file == NULL)
@@ -391,7 +497,31 @@ static gboolean matcherprop_match_test(const MatcherProp *prop,
        if (cmd == NULL)
                return FALSE;
 
+#if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
+       td->cmd = cmd;
+       td->done = FALSE;
+       if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, 
+                       matcher_test_thread, td) != 0)
+               retval = system(cmd);
+       else {
+               printf("waiting for test thread\n");
+               while(!td->done) {
+                       /* don't let the interface freeze while waiting */
+                       sylpheed_do_idle();
+                       if (time(NULL) - start_time > 30) {
+                               pthread_cancel(pt);
+                               td->done = TRUE;
+                               retval = -1;
+                       }
+               }
+               pthread_join(pt, &res);
+               retval = GPOINTER_TO_INT(res);
+               printf(" test thread returned %d\n", retval);
+       }
+       g_free(td);
+#else
        retval = system(cmd);
+#endif
        debug_print("Command exit code: %d\n", retval);
 
        g_free(cmd);
@@ -475,14 +605,14 @@ gboolean matcherprop_match(MatcherProp *prop,
                || matcherprop_string_match(prop, info->cc));
        case MATCHCRITERIA_AGE_GREATER:
                t = time(NULL);
-               return ((t - info->date_t) / (60 * 60 * 24)) >= prop->value;
+               return ((t - info->date_t) / (60 * 60 * 24)) > prop->value;
        case MATCHCRITERIA_AGE_LOWER:
                t = time(NULL);
-               return ((t - info->date_t) / (60 * 60 * 24)) <= prop->value;
+               return ((t - info->date_t) / (60 * 60 * 24)) < prop->value;
        case MATCHCRITERIA_SCORE_GREATER:
-               return info->score >= prop->value;
+               return info->score > prop->value;
        case MATCHCRITERIA_SCORE_LOWER:
-               return info->score <= prop->value;
+               return info->score < prop->value;
        case MATCHCRITERIA_SCORE_EQUAL:
                return info->score == prop->value;
        case MATCHCRITERIA_SIZE_GREATER:
@@ -508,16 +638,18 @@ gboolean matcherprop_match(MatcherProp *prop,
                return matcherprop_string_match(prop, info->inreplyto);
        case MATCHCRITERIA_NOT_INREPLYTO:
                return !matcherprop_string_match(prop, info->inreplyto);
-       case MATCHCRITERIA_REFERENCES:
-               return matcherprop_string_match(prop, info->references);
+       /* FIXME: Using inreplyto, but matching the (newly implemented)
+         * list of references is better */
+        case MATCHCRITERIA_REFERENCES:
+               return matcherprop_string_match(prop, info->inreplyto);
        case MATCHCRITERIA_NOT_REFERENCES:
-               return !matcherprop_string_match(prop, info->references);
+               return !matcherprop_string_match(prop, info->inreplyto);
        case MATCHCRITERIA_TEST:
                return matcherprop_match_test(prop, info);
        case MATCHCRITERIA_NOT_TEST:
                return !matcherprop_match_test(prop, info);
        default:
-               return 0;
+               return FALSE;
        }
 }
 
@@ -583,8 +715,8 @@ static void matcherlist_skip_headers(FILE *fp)
 static gboolean matcherprop_match_one_header(MatcherProp *matcher,
                                             gchar *buf)
 {
-       gboolean result;
-       Header *header;
+       gboolean result = FALSE;
+       Header *header = NULL;
 
        switch (matcher->criteria) {
        case MATCHCRITERIA_HEADER:
@@ -606,12 +738,64 @@ 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);
+       case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
+       case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
+               {
+                       GSList *address_list = NULL;
+                       gint match = MATCH_ONE;
+                       gboolean found = FALSE;
+
+                       /* how many address headers are me trying to mach? */
+                       if (strcasecmp(matcher->header, _("Any")) == 0)
+                               match = MATCH_ANY;
+                       else if (strcasecmp(matcher->header, Q_("Filtering Matcher Menu|All")) == 0)
+                                       match = MATCH_ALL;
+
+                       if (match == MATCH_ONE) {
+                               /* matching one address header exactly, is that the right one? */
+                               header = procheader_parse_header(buf);
+                               if (!header ||
+                                               !procheader_headername_equal(header->name, matcher->header))
+                                       return FALSE;
+                               address_list = address_list_append(address_list, header->body);
+                               if (address_list == NULL)
+                                       return FALSE;
+
+                       } else {
+                               header = procheader_parse_header(buf);
+                               if (!header)
+                                       return FALSE;
+                               /* address header is one of the headers we have to match when checking
+                                  for any address header or all address headers? */
+                               if (procheader_headername_equal(header->name, "From") ||
+                                        procheader_headername_equal(header->name, "To") ||
+                                        procheader_headername_equal(header->name, "Cc") ||
+                                        procheader_headername_equal(header->name, "Reply-To") ||
+                                        procheader_headername_equal(header->name, "Sender"))
+                                       address_list = address_list_append(address_list, header->body);
+                               if (address_list == NULL)
+                                       return FALSE;
+                       }
+
+                       found = match_with_addresses_in_addressbook
+                                                       (matcher, address_list, matcher->criteria,
+                                                        matcher->expr, match);
+                       g_slist_free(address_list);
+
+                       if (matcher->criteria == MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK)
+                               return !found;
+                       else
+                               return found;
        }
+       }
+
        return FALSE;
 }
 
@@ -631,6 +815,8 @@ static gboolean matcherprop_criteria_headers(const MatcherProp *matcher)
        case MATCHCRITERIA_NOT_HEADER:
        case MATCHCRITERIA_HEADERS_PART:
        case MATCHCRITERIA_NOT_HEADERS_PART:
+       case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
+       case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
                return TRUE;
        default:
                return FALSE;
@@ -658,41 +844,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.
@@ -705,11 +856,76 @@ 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;
+                       gint match = MATCH_ANY;
+
+                       if (matcher->done)
+                               continue;
+
+                       /* determine the match range (all, any are our concern here) */
+                       if (matcher->criteria == MATCHCRITERIA_NOT_HEADERS_PART ||
+                           matcher->criteria == MATCHCRITERIA_NOT_MESSAGE) {
+                               match = MATCH_ALL;
+
+                       } else if (matcher->criteria == MATCHCRITERIA_FOUND_IN_ADDRESSBOOK ||
+                                          matcher->criteria == MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK) {
+                               Header *header = NULL;
+
+                               /* address header is one of the headers we have to match when checking
+                                  for any address header or all address headers? */
+                               header = procheader_parse_header(buf);
+                               if (header &&
+                                       (procheader_headername_equal(header->name, "From") ||
+                                        procheader_headername_equal(header->name, "To") ||
+                                        procheader_headername_equal(header->name, "Cc") ||
+                                        procheader_headername_equal(header->name, "Reply-To") ||
+                                        procheader_headername_equal(header->name, "Sender"))) {
+
+                                       if (strcasecmp(matcher->header, _("Any")) == 0)
+                                               match = MATCH_ANY;
+                                       else if (strcasecmp(matcher->header, Q_("Filtering Matcher Menu|All")) == 0)
+                                               match = MATCH_ALL;
+                                       else
+                                               match = MATCH_ONE;
+                               } else {
+                                       /* further call to matcherprop_match_one_header() can't match
+                                          and it irrelevant, so: don't alter the match result */
+                                       continue;
+                               }
+                       }
+
+                       /* ZERO line must NOT match for the rule to match.
+                        */
+                       if (match == MATCH_ALL) {
+                               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;
 }
@@ -746,40 +962,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;
 }
@@ -795,12 +981,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;
 }
 
@@ -838,16 +1055,17 @@ gboolean matcherlist_match_file(MatcherList *matchers, MsgInfo *info,
                        read_body = TRUE;
                }
                matcher->result = FALSE;
+               matcher->done = FALSE;
        }
 
        if (!read_headers && !read_body)
                return result;
 
-       file = procmsg_get_message_file(info);
+       file = procmsg_get_message_file_full(info, read_headers, read_body);
        if (file == NULL)
                return FALSE;
 
-       if ((fp = fopen(file, "rb")) == NULL) {
+       if ((fp = g_fopen(file, "rb")) == NULL) {
                FILE_OP_ERROR(file, "fopen");
                g_free(file);
                return result;
@@ -858,8 +1076,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);
        }
 
@@ -909,6 +1126,9 @@ gboolean matcherlist_match(MatcherList *matchers, MsgInfo *info)
        GSList *l;
        gboolean result;
 
+       if (!matchers)
+               return FALSE;
+
        if (matchers->bool_and)
                result = TRUE;
        else
@@ -1070,6 +1290,7 @@ gchar *matcherprop_to_string(MatcherProp *matcher)
        const gchar *matchtype_str;
        int i;
        gchar * quoted_expr;
+       gchar * quoted_header;
        
        criteria_str = NULL;
        for (i = 0; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)); i++) {
@@ -1118,6 +1339,15 @@ gchar *matcherprop_to_string(MatcherProp *matcher)
                                              criteria_str, quoted_expr);
                g_free(quoted_expr);
                 return matcher_str;
+       case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
+       case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
+               quoted_header = matcher_quote_str(matcher->header);
+               quoted_expr = matcher_quote_str(matcher->expr);
+               matcher_str = g_strdup_printf("%s \"%s\" in \"%s\"",
+                                             criteria_str, quoted_header, quoted_expr);
+               g_free(quoted_header);
+               g_free(quoted_expr);
+               return matcher_str;
        }
 
        matchtype_str = NULL;
@@ -1134,12 +1364,8 @@ gchar *matcherprop_to_string(MatcherProp *matcher)
        case MATCHTYPE_MATCHCASE:
        case MATCHTYPE_REGEXP:
        case MATCHTYPE_REGEXPCASE:
-       case MATCHTYPE_ALL_IN_ADDRESSBOOK:
-       case MATCHTYPE_ANY_IN_ADDRESSBOOK:
                quoted_expr = matcher_quote_str(matcher->expr);
                if (matcher->header) {
-                       gchar * quoted_header;
-                       
                        quoted_header = matcher_quote_str(matcher->header);
                        matcher_str = g_strdup_printf
                                        ("%s \"%s\" %s \"%s\"",
@@ -1273,7 +1499,8 @@ gchar *matching_build_command(const gchar *cmd, MsgInfo *info)
                                size += STRLEN_DEFAULT(info->newsgroups, no_newsgroups) - 2;
                                break;
                        case 'r': /* references */
-                               size += STRLEN_DEFAULT(info->references, no_references) - 2;
+                                /* FIXME: using the inreplyto header for reference */
+                               size += STRLEN_DEFAULT(info->inreplyto, no_references) - 2;
                                break;
                        case 'F': /* file */
                                if (filename == NULL)
@@ -1337,8 +1564,8 @@ gchar *matching_build_command(const gchar *cmd, MsgInfo *info)
                                                no_newsgroups);
                                break;
                        case 'r': /* references */
-                               add_str_default(&p, info->references,
-                                               no_references);
+                                /* FIXME: using the inreplyto header for references */
+                               add_str_default(&p, info->inreplyto, no_references);
                                break;
                        case 'F': /* file */
                                if (filename != NULL)
@@ -1377,19 +1604,73 @@ gchar *matching_build_command(const gchar *cmd, MsgInfo *info)
  */
 static void prefs_filtering_write(FILE *fp, GSList *prefs_filtering)
 {
-       GSList *cur;
+       GSList *cur = NULL;
 
        for (cur = prefs_filtering; cur != NULL; cur = cur->next) {
-               gchar *filtering_str;
-               FilteringProp *prop;
+               gchar *filtering_str = NULL;
+               gchar *tmp_name = NULL;
+               FilteringProp *prop = NULL;
 
                if (NULL == (prop = (FilteringProp *) cur->data))
                        continue;
                
                if (NULL == (filtering_str = filteringprop_to_string(prop)))
                        continue;
-               
-               if (fputs(filtering_str, fp) == EOF ||
+
+               if (prop->enabled) {
+                       if (fputs("enabled ", fp) == EOF) {
+                               FILE_OP_ERROR("filtering config", "fputs");
+                               return;
+                       }
+               } else {
+                       if (fputs("disabled ", fp) == EOF) {
+                               FILE_OP_ERROR("filtering config", "fputs");
+                               return;
+                       }
+               }
+
+               if (fputs("rulename \"", fp) == EOF) {
+                       FILE_OP_ERROR("filtering config", "fputs");
+                       g_free(filtering_str);
+                       return;
+               }
+               tmp_name = prop->name;
+               while (tmp_name && *tmp_name != '\0') {
+                       if (*tmp_name != '"') {
+                               if (fputc(*tmp_name, fp) == EOF) {
+                                       FILE_OP_ERROR("filtering config", "fputs || fputc");
+                                       g_free(filtering_str);
+                                       return;
+                               }
+                       } else if (*tmp_name == '"') {
+                               if (fputc('\\', fp) == EOF ||
+                                   fputc('"', fp) == EOF) {
+                                       FILE_OP_ERROR("filtering config", "fputs || fputc");
+                                       g_free(filtering_str);
+                                       return;
+                               }
+                       }
+                       tmp_name ++;
+               }
+               if (fputs("\" ", fp) == EOF) {
+                       FILE_OP_ERROR("filtering config", "fputs");
+                       g_free(filtering_str);
+                       return;
+               }
+
+               if (prop->account_id != 0) {
+                       gchar *tmp = NULL;
+
+                       tmp = g_strdup_printf("account %d ", prop->account_id);
+                       if (fputs(tmp, fp) == EOF) {
+                               FILE_OP_ERROR("filtering config", "fputs");
+                               g_free(tmp);
+                               return;
+                       }
+                       g_free(tmp);
+               }
+
+               if(fputs(filtering_str, fp) == EOF ||
                    fputc('\n', fp) == EOF) {
                        FILE_OP_ERROR("filtering config", "fputs || fputc");
                        g_free(filtering_str);
@@ -1499,19 +1780,60 @@ void prefs_matcher_write_config(void)
 
 /* ******************************************************************* */
 
+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];
+
+       if (dst == NULL) {
+               perror("fopen");
+               g_free(newpath);
+               return;
+       }
+
+       while (fgets (buf, sizeof(buf), src) != NULL) {
+               if (strlen(buf) > 2 && buf[0] != '['
+               && strncmp(buf, "rulename \"", 10)) {
+                       fwrite("rulename \"\" ",
+                               strlen("rulename \"\" "), 1, dst);
+               }
+               fwrite(buf, strlen(buf), 1, dst);
+       }
+       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);
-       f = fopen(rcpath, "rb");
+       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);
 
        if (f != NULL) {
@@ -1519,12 +1841,12 @@ void prefs_matcher_read_config(void)
                fclose(matcher_parserin);
        }
        else {
-               /* previous version compatibily */
+               /* previous version compatibility */
 
                /* printf("reading filtering\n"); */
                rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
                                     FILTERING_RC, NULL);
-               f = fopen(rcpath, "rb");
+               f = g_fopen(rcpath, "rb");
                g_free(rcpath);
                
                if (f != NULL) {
@@ -1535,7 +1857,7 @@ void prefs_matcher_read_config(void)
                /* printf("reading scoring\n"); */
                rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
                                     SCORING_RC, NULL);
-               f = fopen(rcpath, "rb");
+               f = g_fopen(rcpath, "rb");
                g_free(rcpath);
                
                if (f != NULL) {