scoring / expression matcher
authorHoà Viêt Dinh <dinh.viet.hoa@free.fr>
Sun, 6 May 2001 04:27:28 +0000 (04:27 +0000)
committerHoà Viêt Dinh <dinh.viet.hoa@free.fr>
Sun, 6 May 2001 04:27:28 +0000 (04:27 +0000)
22 files changed:
ChangeLog.claws
src/Makefile.am
src/defs.h
src/folder.c
src/folder.h
src/main.c
src/mainwindow.c
src/matcher.c [new file with mode: 0644]
src/matcher.h [new file with mode: 0644]
src/prefs_common.c
src/prefs_common.h
src/prefs_folder_item.c [new file with mode: 0644]
src/prefs_folder_item.h [new file with mode: 0644]
src/procheader.c
src/procheader.h
src/procmsg.c
src/procmsg.h
src/scoring.c [new file with mode: 0644]
src/scoring.h [new file with mode: 0644]
src/summaryview.c
src/summaryview.h
src/textview.c

index 84eac36..4e90f0a 100644 (file)
@@ -1,3 +1,51 @@
+2001-05-06 [hoa]
+
+       * src/Makefile.am
+               added scoring.c scoring.h
+               prefs_folder_item.c prefs_folder_item.h
+               matcher.c matcher.h
+       * src/defs.h
+               added FOLDERITEM_RC SCORING_RC
+       * src/folder.[ch]
+               added prefs field in FolderItem :
+               properties for each folder (PrefsFolderItem),
+               sorting type and mode were added as the first properties.
+       * src/main.c
+               read config for scoring
+       * added src/matcher.[ch]
+               gives functions to parse configuration file to do
+               matching on mails with a list of conditions.
+       * src/mainwindow.c
+               add option to sort by score
+       * src/prefs_common.[ch]
+               added preference option to show score and for
+               the score column size
+       * added src/prefs_folder_item.[ch]
+               properties for each folder (PrefsFolderItem),
+               sorting type and mode were added as the first properties.
+       * src/procheader.[ch]
+               added procheader_parse_header()
+               added procheader_header_free()
+               added procheader_headername_equal()
+               and made modification to use them.
+       * src/procmsg.[ch]
+               added score field to MsgInfo structure
+       * added src/scoring.[ch]
+               gives functions to parse configuration file to do
+               scoring on mails with a list of conditions.
+       * src/summaryview.[ch]
+               save the sorting type and mode for the folder
+               added column to display the score of the mail
+               added function summary_score_clicked()
+               sorting type and mode are restored to the folder
+               when it is reopen
+       * src/textview.c
+               use procheader_parse_header(), procheader_headername_equal()
+               removed memory leak in text_scan_header when freeing headers
+
+       Scoring functions are implemented, an interface is needed
+       to configure that.
+
 2001-05-04 [alfons]
 
        * src/gtkstext.c: 
index 0ffa490..add50ca 100644 (file)
@@ -78,7 +78,10 @@ sylpheed_SOURCES = \
        customheader.c customheader.h \
        prefs_headers.c prefs_headers.h \
        headers_display.c headers_display.h \
-       prefs_display_headers.c prefs_display_headers.h
+       prefs_display_headers.c prefs_display_headers.h \
+       scoring.c scoring.h \
+       prefs_folder_item.c prefs_folder_item.h \
+       matcher.c matcher.h
 
 EXTRA_DIST = \
        pixmaps/clip.xpm \
index 5c277a2..f68eef0 100644 (file)
@@ -48,6 +48,8 @@
 #define FILTER_RC              "filterrc"
 #define HEADERS_RC             "headersrc"
 #define HEADERS_DISPLAY_RC     "headersdisplayrc"
+#define FOLDERITEM_RC           "folderitemrc"
+#define SCORING_RC              "scoringrc"
 #define MENU_RC                        "menurc"
 #define ADDRESS_BOOK           "addressbook.xml"
 #define MANUAL_HTML_INDEX      "sylpheed.html"
index 8461ab7..0e5e588 100644 (file)
@@ -143,6 +143,8 @@ FolderItem *folder_item_new(const gchar *name, const gchar *path)
        item->folder = NULL;
        item->data = NULL;
 
+       item->prefs = prefs_folder_item_new();
+
        return item;
 }
 
@@ -822,6 +824,9 @@ static gboolean folder_build_tree(GNode *node, gpointer data)
        case F_TRASH:  folder->trash  = item; break;
        default:
        }
+
+       prefs_folder_item_read_config(item);
+
        node->data = item;
        xml_free_node(xmlnode);
 
index dcecf87..2951b48 100644 (file)
@@ -36,6 +36,7 @@ typedef struct _FolderItem    FolderItem;
 #include "prefs_account.h"
 #include "session.h"
 #include "procmsg.h"
+#include "prefs_folder_item.h"
 
 #define FOLDER(obj)            ((Folder *)obj)
 #define FOLDER_TYPE(obj)       (FOLDER(obj)->type)
@@ -205,6 +206,8 @@ struct _FolderItem
        Folder *folder;
 
        gpointer data;
+
+       PrefsFolderItem * prefs;
 };
 
 Folder     *folder_new         (FolderType      type,
index 622528f..549da44 100644 (file)
@@ -204,6 +204,7 @@ int main(int argc, char *argv[])
        prefs_filter_write_config();
        prefs_display_headers_read_config();
        prefs_display_headers_write_config();
+       prefs_scoring_read_config();
 
 #if USE_GPGME
        if (gpgme_check_engine()) {  /* Also does some gpgme init */
index f9eb9e7..eacd7d5 100644 (file)
@@ -501,6 +501,7 @@ static GtkItemFactoryEntry mainwin_entries[] =
        {N_("/_Summary/_Sort/Sort by _date"),   NULL, sort_summary_cb, SORT_BY_DATE, NULL},
        {N_("/_Summary/_Sort/Sort by _from"),   NULL, sort_summary_cb, SORT_BY_FROM, NULL},
        {N_("/_Summary/_Sort/Sort by _subject"),NULL, sort_summary_cb, SORT_BY_SUBJECT, NULL},
+       {N_("/_Summary/_Sort/Sort by sco_re"),  NULL, sort_summary_cb, SORT_BY_SCORE, NULL},
        {N_("/_Summary/_Sort/---"),             NULL, NULL, 0, "<Separator>"},
        {N_("/_Summary/_Sort/_Attract by subject"),
                                                NULL, attract_by_subject_cb, 0, NULL},
diff --git a/src/matcher.c b/src/matcher.c
new file mode 100644 (file)
index 0000000..10b3603
--- /dev/null
@@ -0,0 +1,910 @@
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "utils.h"
+#include "defs.h"
+#include "procheader.h"
+#include "matcher.h"
+
+struct _MatchParser {
+       gint id;
+       gchar * str;
+};
+
+typedef struct _MatchParser MatchParser;
+
+MatchParser matchparser_tab[] = {
+       /* msginfo */
+       {SCORING_ALL, "all"},
+       {SCORING_SUBJECT, "subject"},
+       {SCORING_NOT_SUBJECT, "~subject"},
+       {SCORING_FROM, "from"},
+       {SCORING_NOT_FROM, "~from"},
+       {SCORING_TO, "to"},
+       {SCORING_NOT_TO, "~to"},
+       {SCORING_CC, "cc"},
+       {SCORING_NOT_CC, "~cc"},
+       {SCORING_TO_OR_CC, "to_or_cc"},
+       {SCORING_NOT_TO_AND_NOT_CC, "~to_or_cc"},
+       {SCORING_AGE_SUP, "age_sup"},
+       {SCORING_AGE_INF, "age_inf"},
+       {SCORING_NEWSGROUPS, "newsgroups"},
+       {SCORING_NOT_NEWSGROUPS, "~newsgroups"},
+
+       /* content have to be read */
+       {SCORING_HEADER, "header"},
+       {SCORING_NOT_HEADER, "~header"},
+       {SCORING_MESSAGEHEADERS, "messageheaders"},
+       {SCORING_NOT_MESSAGEHEADERS, "~messageheaders"},
+       {SCORING_MESSAGE, "message"},
+       {SCORING_NOT_MESSAGE, "~message"},
+       {SCORING_BODY, "body"},
+       {SCORING_NOT_BODY, "~body"},
+
+       /* match type */
+       {SCORING_MATCHCASE, "matchcase"},
+       {SCORING_MATCH, "match"},
+       {SCORING_REGEXPCASE, "regexpcase"},
+       {SCORING_REGEXP, "regexp"},
+
+       /* actions */
+       {SCORING_SCORE, "score"},
+};
+
+/*
+  syntax for matcher
+
+  header "x-mailing" match "toto"
+  subject match "regexp" & to regexp "regexp"
+  subject match "regexp" | to regexpcase "regexp" | age_sup 5
+ */
+
+static gboolean matcher_is_blank(gchar ch);
+
+/* ******************* parser *********************** */
+
+static gboolean matcher_is_blank(gchar ch)
+{
+       return (ch == ' ') || (ch == '\t');
+}
+
+/* parse for one condition */
+
+MatcherProp * matcherprop_parse(gchar ** str)
+{
+       MatcherProp * prop;
+       gchar * tmp;
+       gint key;
+       gint age;
+       gchar * expr;
+       gint match;
+       gchar * header = NULL;
+       
+       tmp = * str;
+       key = matcher_parse_keyword(&tmp);
+       if (tmp == NULL) {
+               * str = NULL;
+               return NULL;
+       }
+
+       switch (key) {
+       case SCORING_AGE_INF:
+       case SCORING_AGE_SUP:
+               age = matcher_parse_number(&tmp);
+               if (tmp == NULL) {
+                       * str = NULL;
+                       return NULL;
+               }
+               *str = tmp;
+
+               prop = matcherprop_new(key, NULL, 0, NULL, age);
+
+               return prop;
+
+       case SCORING_ALL:
+               prop = matcherprop_new(key, NULL, 0, NULL, 0);
+               *str = tmp;
+
+               return prop;
+
+       case SCORING_SUBJECT:
+       case SCORING_NOT_SUBJECT:
+       case SCORING_FROM:
+       case SCORING_NOT_FROM:
+       case SCORING_TO:
+       case SCORING_NOT_TO:
+       case SCORING_CC:
+       case SCORING_NOT_CC:
+       case SCORING_TO_OR_CC:
+       case SCORING_NOT_TO_AND_NOT_CC:
+       case SCORING_NEWSGROUPS:
+       case SCORING_NOT_NEWSGROUPS:
+       case SCORING_MESSAGE:
+       case SCORING_NOT_MESSAGE:
+       case SCORING_MESSAGEHEADERS:
+       case SCORING_NOT_MESSAGEHEADERS:
+       case SCORING_BODY:
+       case SCORING_NOT_BODY:
+       case SCORING_HEADER:
+       case SCORING_NOT_HEADER:
+               if ((key == SCORING_HEADER) || (key == SCORING_NOT_HEADER)) {
+                       header = matcher_parse_str(&tmp);
+                       if (tmp == NULL) {
+                               * str = NULL;
+                               return NULL;
+                       }
+               }
+
+               match = matcher_parse_keyword(&tmp);
+               if (tmp == NULL) {
+                       if (header)
+                               g_free(header);
+                       * str = NULL;
+                       return NULL;
+               }
+
+               switch(match) {
+               case SCORING_REGEXP:
+               case SCORING_REGEXPCASE:
+                       expr = matcher_parse_regexp(&tmp);
+                       if (tmp == NULL) {
+                               if (header)
+                                       g_free(header);
+                               * str = NULL;
+                               return NULL;
+                       }
+                       *str = tmp;
+                       prop = matcherprop_new(key, header, match, expr, 0);
+                       g_free(expr);
+
+                       return prop;
+               case SCORING_MATCH:
+               case SCORING_MATCHCASE:
+                       expr = matcher_parse_str(&tmp);
+                       if (tmp == NULL) {
+                               if (header)
+                                       g_free(header);
+                               * str = NULL;
+                               return NULL;
+                       }
+                       *str = tmp;
+                       prop = matcherprop_new(key, header, match, expr, 0);
+                       g_free(expr);
+
+                       return prop;
+               default:
+                       if (header)
+                               g_free(header);
+                       * str = NULL;
+                       return NULL;
+               }
+       default:
+               * str = NULL;
+               return NULL;
+       }
+}
+
+gint matcher_parse_keyword(gchar ** str)
+{
+       gchar * p;
+       gchar * dup;
+       gchar * start;
+       gint i;
+       gint match;
+
+       dup = alloca(strlen(* str) + 1);
+       p = dup;
+       strcpy(dup, * str);
+
+       while (matcher_is_blank(*p))
+               p++;
+
+       start = p;
+       
+       match = -1;
+       for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
+           i++) {
+               if (strncasecmp(matchparser_tab[i].str, p,
+                               strlen(matchparser_tab[i].str)) == 0) {
+                       p += strlen(matchparser_tab[i].str);
+                       match = i;
+                       break;
+               }
+       }
+
+       if (match == -1) {
+               * str = NULL;
+               return 0;
+       }
+
+       *p = '\0';
+
+       *str += p - dup + 1;
+       return matchparser_tab[match].id;
+}
+
+gint matcher_parse_number(gchar ** str)
+{
+       gchar * p;
+       gchar * dup;
+       gchar * start;
+
+       dup = alloca(strlen(* str) + 1);
+       p = dup;
+       strcpy(dup, * str);
+
+       while (matcher_is_blank(*p))
+               p++;
+
+       start = p;
+
+       if (!isdigit(*p) && *p != '-' && *p != '+') {
+               *str = NULL;
+               return 0;
+       }
+       if (*p == '-' || *p == '+')
+               p++;
+       while (isdigit(*p))
+               p++;
+
+       *p = '\0';
+
+       *str += p - dup + 1;
+       return atoi(start);
+}
+
+gboolean matcher_parse_boolean_op(gchar ** str)
+{
+       gchar * p;
+
+       p = * str;
+
+       while (matcher_is_blank(*p))
+               p++;
+
+       if (*p == '|') {
+               *str += p - * str + 1;
+               return FALSE;
+       }
+       else if (*p == '&') {
+               *str += p - * str + 1;
+               return TRUE;
+       }
+       else {
+               *str = NULL;
+               return FALSE;
+       }
+}
+
+gchar * matcher_parse_regexp(gchar ** str)
+{
+       gchar * p;
+       gchar * dup;
+       gchar * start;
+
+       dup = alloca(strlen(* str) + 1);
+       p = dup;
+       strcpy(dup, * str);
+
+       while (matcher_is_blank(*p))
+               p++;
+
+       if (*p != '/') {
+               * str = NULL;
+               return NULL;
+       }
+
+       p ++;
+       start = p;
+       while (*p != '/') {
+               if (*p == '\\')
+                       p++;
+               p++;
+       }
+       *p = '\0';
+
+       *str += p - dup + 2;
+       return g_strdup(start);
+}
+
+gchar * matcher_parse_str(gchar ** str)
+{
+       gchar * p;
+       gchar * dup;
+       gchar * start;
+       gchar * dest;
+
+       dup = alloca(strlen(* str) + 1);
+       p = dup;
+       strcpy(dup, * str);
+
+       while (matcher_is_blank(*p))
+               p++;
+
+       if (*p != '"') {
+               * str = NULL;
+               return NULL;
+       }
+       
+       p ++;
+       start = p;
+       dest = p;
+       while (*p != '"') {
+               if (*p == '\\') {
+                       p++;
+                       *dest = *p;
+               }
+               else
+                       *dest = *p;
+               dest++;
+               p++;
+       }
+       *dest = '\0';
+
+       *str += dest - dup + 2;
+       return g_strdup(start);
+}
+
+/* **************** data structure allocation **************** */
+
+
+MatcherProp * matcherprop_new(gint criteria, gchar * header,
+                             gint matchtype, gchar * expr,
+                             int age)
+{
+       MatcherProp * prop;
+
+       prop = g_new0(MatcherProp, 1);
+       prop->criteria = criteria;
+       if (header != NULL)
+               prop->header = g_strdup(header);
+       else
+               prop->header = NULL;
+       if (expr != NULL)
+               prop->expr = g_strdup(expr);
+       else
+               prop->expr = NULL;
+       prop->matchtype = matchtype;
+       prop->preg = NULL;
+       prop->age = age;
+       prop->error = 0;
+
+       return prop;
+}
+
+void matcherprop_free(MatcherProp * prop)
+{
+       g_free(prop->expr);
+       if (prop->preg != NULL) {
+               regfree(prop->preg);
+               g_free(prop->preg);
+       }
+       g_free(prop);
+}
+
+
+/* ************** match ******************************/
+
+
+/* match the given string */
+
+static gboolean matcherprop_string_match(MatcherProp * prop, gchar * str)
+{
+       gchar * str1;
+
+       if (str == NULL)
+               str = "";
+
+       switch(prop->matchtype) {
+       case SCORING_REGEXP:
+       case SCORING_REGEXPCASE:
+               if (!prop->preg && (prop->error == 0)) {
+                       prop->preg = g_new0(regex_t, 1);
+                       if (regcomp(prop->preg, prop->expr,
+                                   REG_NOSUB | REG_EXTENDED
+                                   | ((prop->matchtype == SCORING_REGEXPCASE)
+                                   ? REG_ICASE : 0)) != 0) {
+                               prop->error = 1;
+                               g_free(prop->preg);
+                       }
+               }
+               if (prop->preg == NULL)
+                       return 0;
+               
+               if (regexec(prop->preg, str, 0, NULL, 0) == 0)
+                       return 1;
+               else
+                       return 0;
+
+       case SCORING_MATCH:
+               return (strstr(str, prop->expr) != NULL);
+
+       case SCORING_MATCHCASE:
+               g_strup(prop->expr);
+               str1 = alloca(strlen(str) + 1);
+               strcpy(str1, str);
+               g_strup(str1);
+               return (strstr(str1, prop->expr) != NULL);
+               
+       default:
+               return 0;
+       }
+}
+
+/* match a message and his headers, hlist can be NULL if you don't
+   want to use headers */
+
+gboolean matcherprop_match(MatcherProp * prop, MsgInfo * info)
+{
+       time_t t;
+
+       switch(prop->criteria) {
+       case SCORING_ALL:
+               return 1;
+       case SCORING_SUBJECT:
+               return matcherprop_string_match(prop, info->subject);
+       case SCORING_NOT_SUBJECT:
+               return !matcherprop_string_match(prop, info->subject);
+       case SCORING_FROM:
+               return matcherprop_string_match(prop, info->from);
+       case SCORING_NOT_FROM:
+               return !matcherprop_string_match(prop, info->from);
+       case SCORING_TO:
+               return matcherprop_string_match(prop, info->to);
+       case SCORING_NOT_TO:
+               return !matcherprop_string_match(prop, info->to);
+       case SCORING_CC:
+               return matcherprop_string_match(prop, info->cc);
+       case SCORING_NOT_CC:
+               return !matcherprop_string_match(prop, info->cc);
+       case SCORING_TO_OR_CC:
+               return matcherprop_string_match(prop, info->to)
+                       || matcherprop_string_match(prop, info->cc);
+       case SCORING_NOT_TO_AND_NOT_CC:
+               return !(matcherprop_string_match(prop, info->to)
+               || matcherprop_string_match(prop, info->cc));
+       case SCORING_AGE_SUP:
+               t = time(NULL);
+               return (t - info->date_t) > prop->age;
+       case SCORING_AGE_INF:
+               t = time(NULL);
+               return (t - info->date_t) < prop->age;
+       case SCORING_NEWSGROUPS:
+               return matcherprop_string_match(prop, info->newsgroups);
+       case SCORING_NOT_NEWSGROUPS:
+               return !matcherprop_string_match(prop, info->newsgroups);
+       case SCORING_HEADER:
+       default:
+               return 0;
+       }
+}
+
+/* ********************* MatcherList *************************** */
+
+
+/* parse for a list of conditions */
+
+MatcherList * matcherlist_parse(gchar ** str)
+{
+       gchar * tmp;
+       MatcherProp * matcher;
+       GSList * matchers_list = NULL;
+       gboolean bool_and = TRUE;
+       gchar * save;
+       MatcherList * cond;
+       gboolean main_bool_and;
+
+       tmp = * str;
+
+       matcher = matcherprop_parse(&tmp);
+       if (tmp == NULL) {
+               * str = NULL;
+               return NULL;
+       }
+       matchers_list = g_slist_append(matchers_list, matcher);
+       while (matcher) {
+               save = tmp;
+               bool_and = matcher_parse_boolean_op(&tmp);
+               if (tmp == NULL) {
+                       tmp = save;
+                       matcher = NULL;
+               }
+               else {
+                       main_bool_and = bool_and;
+                       matcher = matcherprop_parse(&tmp);
+                       if (tmp != NULL) {
+                               matchers_list =
+                                       g_slist_append(matchers_list, matcher);
+                       }
+                       else {
+                               * str = NULL;
+                               return NULL;
+                       }
+               }
+       }
+
+       cond = matcherlist_new(matchers_list, main_bool_and);
+
+       * str = tmp;
+
+       return cond;
+}
+
+MatcherList * matcherlist_new(GSList * matchers, gboolean bool_and)
+{
+       MatcherList * cond;
+
+       cond = g_new0(MatcherList, 1);
+
+       cond->matchers = matchers;
+       cond->bool_and = bool_and;
+
+       return cond;
+}
+
+void matcherlist_free(MatcherList * cond)
+{
+       GSList * l;
+       for(l = cond->matchers ; l != NULL ; l = g_slist_next(l)) {
+               matcherprop_free((MatcherProp *) l->data);
+       }
+       g_free(cond);
+}
+
+/*
+  skip the headers
+ */
+
+static void matcherprop_skip_headers(FILE *fp)
+{
+       gchar buf[BUFFSIZE];
+
+       while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
+       }
+}
+
+/*
+  matcherprop_match_one_header
+  returns TRUE if buf matchs the MatchersProp criteria
+ */
+
+static gboolean matcherprop_match_one_header(MatcherProp * matcher,
+                                            gchar * buf)
+{
+       gboolean result;
+       Header *header;
+
+       switch(matcher->criteria) {
+       case SCORING_HEADER:
+       case SCORING_NOT_HEADER:
+               header = procheader_parse_header(buf);
+               if (procheader_headername_equal(header->name,
+                                               matcher->header)) {
+                       if (matcher->criteria == SCORING_HEADER)
+                               result = matcherprop_string_match(matcher, header->body);
+                       else
+                               result = !matcherprop_string_match(matcher, header->body);
+                       procheader_header_free(header);
+                       return result;
+               }
+               break;
+       case SCORING_MESSAGEHEADERS:
+       case SCORING_MESSAGE:
+               return matcherprop_string_match(matcher, buf);
+       case SCORING_NOT_MESSAGE:
+       case SCORING_NOT_MESSAGEHEADERS:
+               return !matcherprop_string_match(matcher, buf);
+       }
+       return FALSE;
+}
+
+/*
+  matcherprop_criteria_header
+  returns TRUE if the headers must be matched
+ */
+
+static gboolean matcherprop_criteria_headers(MatcherProp * matcher)
+{
+       switch(matcher->criteria) {
+       case SCORING_HEADER:
+       case SCORING_NOT_HEADER:
+       case SCORING_MESSAGEHEADERS:
+       case SCORING_NOT_MESSAGEHEADERS:
+       case SCORING_MESSAGE:
+       case SCORING_NOT_MESSAGE:
+               return TRUE;
+       default:
+               return FALSE;
+       }
+}
+
+/*
+  matcherlist_match_one_header
+  returns TRUE if buf matchs the MatchersList criteria
+ */
+
+static gboolean matcherlist_match_one_header(MatcherList * matchers,
+                                            gchar * buf, gboolean result)
+{
+       GSList * l;
+       
+       for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
+               MatcherProp * matcher = (MatcherProp *) l->data;
+               gboolean matched = FALSE;
+               gboolean partial_result;
+
+               if (matcherprop_criteria_headers(matcher)) {
+                       if (matcherprop_match_one_header(matcher, buf)) {
+                               if (!matchers->bool_and)
+                                       return TRUE;
+                       }
+                       else {
+                               if (matchers->bool_and)
+                                       return FALSE;
+                       }
+               }
+       }
+
+       return result;
+}
+
+/*
+  matcherlist_match_headers
+  returns TRUE if one of the headers matchs the MatcherList criteria
+ */
+
+static gboolean matcherlist_match_headers(MatcherList * matchers, FILE * fp,
+                                         gboolean result)
+{
+       gchar buf[BUFFSIZE];
+
+       while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
+               if (matcherlist_match_one_header(matchers, buf, result)) {
+                       if (!matchers->bool_and)
+                               return TRUE;
+               }
+               else {
+                       if (matchers->bool_and)
+                               return FALSE;
+               }
+       }
+       return result;
+}
+
+/*
+  matcherprop_criteria_body
+  returns TRUE if the body must be matched
+ */
+
+static gboolean matcherprop_criteria_body(MatcherProp * matcher)
+{
+       switch(matcher->criteria) {
+       case SCORING_BODY:
+       case SCORING_NOT_BODY:
+       case SCORING_MESSAGE:
+       case SCORING_NOT_MESSAGE:
+               return TRUE;
+       default:
+               return FALSE;
+       }
+}
+
+/*
+  matcherprop_match_line
+  returns TRUE if the string matchs the MatcherProp criteria
+ */
+
+static gboolean matcherprop_match_line(MatcherProp * matcher, gchar * line)
+{
+       switch(matcher->criteria) {
+       case SCORING_BODY:
+       case SCORING_MESSAGE:
+               return matcherprop_string_match(matcher, line);
+       case SCORING_NOT_BODY:
+       case SCORING_NOT_MESSAGE:
+               return !matcherprop_string_match(matcher, line);
+       }
+}
+
+/*
+  matcherlist_match_line
+  returns TRUE if the string matchs the MatcherList criteria
+ */
+
+static gboolean matcherlist_match_line(MatcherList * matchers, gchar * line,
+                                      gboolean result)
+{
+       GSList * l;
+
+       for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
+               MatcherProp * matcher = (MatcherProp *) l->data;
+
+               if (matcherprop_criteria_body(matcher)) {
+                       if (matcherprop_match_line(matcher, line)) {
+                               if (!matchers->bool_and)
+                                       return TRUE;
+                       }
+                       else {
+                               if (matchers->bool_and)
+                                       return FALSE;
+                       }
+               }
+       }
+       return result;
+}
+
+/*
+  matcherlist_match_body
+  returns TRUE if one line of the body matchs the MatcherList criteria
+ */
+
+static gboolean matcherlist_match_body(MatcherList * matchers, FILE * fp,
+                                      gboolean result)
+{
+       gchar buf[BUFFSIZE];
+
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               if (matcherlist_match_line(matchers, buf, result)) {
+                       if (!matchers->bool_and)
+                               return TRUE;
+               }
+               else {
+                       if (matchers->bool_and)
+                               return FALSE;
+               }
+       }
+       return result;
+}
+
+gboolean matcherlist_match_file(MatcherList * matchers, MsgInfo * info,
+                               gboolean result)
+{
+       gboolean read_headers;
+       gboolean read_body;
+       GSList * l;
+       FILE * fp;
+       gchar * file;
+
+       /* file need to be read ? */
+
+       read_headers = FALSE;
+       read_body = FALSE;
+       for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
+               MatcherProp * matcher = (MatcherProp *) l->data;
+
+               if (matcherprop_criteria_headers(matcher))
+                       read_headers = TRUE;
+               if (matcherprop_criteria_body(matcher))
+                       read_body = TRUE;
+       }
+
+       if (!read_headers && !read_body)
+               return result;
+
+       file = procmsg_get_message_file_path(info);
+       g_return_if_fail(file != NULL);
+
+       if ((fp = fopen(file, "r")) == NULL) {
+               FILE_OP_ERROR(file, "fopen");
+               g_free(file);
+               return result;
+       }
+       
+       /* read the headers */
+
+       if (read_headers) {
+               if (matcherlist_match_headers(matchers, fp, result)) {
+                       if (!matchers->bool_and)
+                               result = TRUE;
+               }
+               else {
+                       if (matchers->bool_and)
+                               result = FALSE;
+               }
+       }
+       
+       /* read the body */
+       if (read_body) {
+               if (matcherlist_match_body(matchers, fp, result)) {
+                       if (!matchers->bool_and)
+                               result = TRUE;
+               }
+               else {
+                       if (matchers->bool_and)
+                               result = FALSE;
+               }
+       }
+
+       g_free(file);
+
+       fclose(fp);
+       
+       return result;
+}
+
+/* test a list of condition */
+
+gboolean matcherlist_match(MatcherList * matchers, MsgInfo * info)
+{
+       GSList * l;
+       gboolean result;
+
+       if (matchers->bool_and)
+               result = TRUE;
+       else
+               result = FALSE;
+
+       /* test the condition on the file */
+
+       if (matcherlist_match_file(matchers, info, result)) {
+               if (!matchers->bool_and)
+                       return TRUE;
+       }
+       else {
+               if (matchers->bool_and)
+                       return FALSE;
+       }
+
+       /* test the cached elements */
+
+       for(l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
+               MatcherProp * matcher = (MatcherProp *) l->data;
+
+               if (matcherprop_match(matcher, info)) {
+                       if (!matchers->bool_and) {
+                               result = TRUE;
+                               break;
+                       }
+               }
+               else {
+                       if (matchers->bool_and) {
+                               result = FALSE;
+                               break;
+                       }
+               }
+       }
+
+       return result;
+}
+
+#ifdef 0
+static void matcherprop_print(MatcherProp * matcher)
+{
+  int i;
+
+       if (matcher == NULL) {
+               printf("no matcher\n");
+               return;
+       }
+
+       switch (matcher->matchtype) {
+       case SCORING_MATCH:
+               printf("match\n");
+               break;
+       case SCORING_REGEXP:
+               printf("regexp\n");
+               break;
+       case SCORING_MATCHCASE:
+               printf("matchcase\n");
+               break;
+       case SCORING_REGEXPCASE:
+               printf("regexpcase\n");
+               break;
+       }
+
+       for(i = 0 ; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)) ;
+           i++) {
+               if (matchparser_tab[i].id == matcher->criteria)
+                       printf("%s\n", matchparser_tab[i].str);
+       }
+
+       if (matcher->expr)
+               printf("expr : %s\n", matcher->expr);
+
+       printf("age: %i\n", matcher->age);
+
+       printf("compiled : %s\n", matcher->preg != NULL ? "yes" : "no");
+       printf("error: %i\n",  matcher->error);
+}
+#endif
diff --git a/src/matcher.h b/src/matcher.h
new file mode 100644 (file)
index 0000000..ff54bfb
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef MATCHER_H
+
+#define MATCHER_H
+
+#include <sys/types.h>
+#include <regex.h>
+#include <glib.h>
+#include "procmsg.h"
+
+enum {
+       SCORING_ALL,
+       SCORING_SUBJECT,
+       SCORING_NOT_SUBJECT,
+       SCORING_FROM,
+       SCORING_NOT_FROM,
+       SCORING_TO,
+       SCORING_NOT_TO,
+       SCORING_CC,
+       SCORING_NOT_CC,
+       SCORING_TO_OR_CC,
+       SCORING_NOT_TO_AND_NOT_CC,
+       SCORING_AGE_SUP,
+       SCORING_AGE_INF,
+       SCORING_NEWSGROUPS,
+       SCORING_NOT_NEWSGROUPS,
+       SCORING_HEADER,
+       SCORING_NOT_HEADER,
+       SCORING_MESSAGE,
+       SCORING_NOT_MESSAGE,
+       SCORING_MESSAGEHEADERS,
+       SCORING_NOT_MESSAGEHEADERS,
+       SCORING_BODY,
+       SCORING_NOT_BODY,
+       SCORING_SCORE,
+       SCORING_MATCH,
+       SCORING_REGEXP,
+       SCORING_MATCHCASE,
+       SCORING_REGEXPCASE
+};
+
+struct _MatcherProp {
+       int matchtype;
+       int criteria;
+       gchar * header;
+       gchar * expr;
+       int age;
+       regex_t * preg;
+       int error;
+};
+
+typedef struct _MatcherProp MatcherProp;
+
+struct _MatcherList {
+       GSList * matchers;
+       gboolean bool_and;
+};
+
+typedef struct _MatcherList MatcherList;
+
+MatcherProp * matcherprop_new(gint criteria, gchar * header,
+                             gint matchtype, gchar * expr,
+                             int age);
+void matcherprop_free(MatcherProp * prop);
+MatcherProp * matcherprop_parse(gchar ** str);
+
+gboolean matcherprop_match(MatcherProp * prop, MsgInfo * info);
+
+MatcherList * matcherlist_new(GSList * matchers, gboolean bool_and);
+void matcherlist_free(MatcherList * cond);
+MatcherList * matcherlist_parse(gchar ** str);
+
+gboolean matcherlist_match(MatcherList * cond, MsgInfo * info);
+
+gint matcher_parse_keyword(gchar ** str);
+gint matcher_parse_number(gchar ** str);
+gboolean matcher_parse_boolean_op(gchar ** str);
+gchar * matcher_parse_regexp(gchar ** str);
+gchar * matcher_parse_str(gchar ** str);
+
+#endif
index ef505e4..5f9c597 100644 (file)
@@ -309,6 +309,8 @@ static PrefParam param[] = {
         NULL, NULL, NULL},
        {"show_number", "TRUE", &prefs_common.show_number, P_BOOL,
         NULL, NULL, NULL},
+       {"show_score", "TRUE", &prefs_common.show_score, P_BOOL,
+        NULL, NULL, NULL},
        {"show_size", "FALSE", &prefs_common.show_size, P_BOOL,
         NULL, NULL, NULL},
        {"show_date", "TRUE", &prefs_common.show_date, P_BOOL,
@@ -343,6 +345,8 @@ static PrefParam param[] = {
         NULL, NULL, NULL},
        {"summary_col_number", "40", &prefs_common.summary_col_number, P_INT,
         NULL, NULL, NULL},
+       {"summary_col_score", "40", &prefs_common.summary_col_score,
+        P_INT, NULL, NULL, NULL},
        {"summary_col_size", "48", &prefs_common.summary_col_size, P_INT,
         NULL, NULL, NULL},
        {"summary_col_date", "120", &prefs_common.summary_col_date, P_INT,
@@ -2243,6 +2247,7 @@ void prefs_summary_display_item_set(void)
        SET_ACTIVE(S_COL_UNREAD, show_unread);
        SET_ACTIVE(S_COL_MIME, show_mime);
        SET_ACTIVE(S_COL_NUMBER, show_number);
+       SET_ACTIVE(S_COL_SCORE, show_score);
        SET_ACTIVE(S_COL_SIZE, show_size);
        SET_ACTIVE(S_COL_DATE, show_date);
        SET_ACTIVE(S_COL_FROM, show_from);
@@ -2256,6 +2261,7 @@ void prefs_summary_display_item_set(void)
                GET_ACTIVE(S_COL_UNREAD, show_unread);
                GET_ACTIVE(S_COL_MIME, show_mime);
                GET_ACTIVE(S_COL_NUMBER, show_number);
+               GET_ACTIVE(S_COL_SCORE, show_score);
                GET_ACTIVE(S_COL_SIZE, show_size);
                GET_ACTIVE(S_COL_DATE, show_date);
                GET_ACTIVE(S_COL_FROM, show_from);
@@ -2306,6 +2312,7 @@ static void prefs_summary_display_item_dialog_create(gboolean *cancelled)
        SET_CHECK_BUTTON(S_COL_UNREAD, _("Unread"));
        SET_CHECK_BUTTON(S_COL_MIME, _("MIME"));
        SET_CHECK_BUTTON(S_COL_NUMBER, _("Number"));
+       SET_CHECK_BUTTON(S_COL_SCORE, _("Score"));
        SET_CHECK_BUTTON(S_COL_SIZE, _("Size"));
        SET_CHECK_BUTTON(S_COL_DATE, _("Date"));
        SET_CHECK_BUTTON(S_COL_FROM, _("From"));
index cfb9d01..86595b1 100644 (file)
@@ -82,6 +82,7 @@ struct _PrefsCommon
        gboolean show_unread;
        gboolean show_mime;
        gboolean show_number;
+       gboolean show_score;
        gboolean show_size;
        gboolean show_date;
        gboolean show_from;
@@ -107,6 +108,7 @@ struct _PrefsCommon
        gint summary_col_date;
        gint summary_col_from;
        gint summary_col_subject;
+       gint summary_col_score;
 
        gint mainview_x;
        gint mainview_y;
diff --git a/src/prefs_folder_item.c b/src/prefs_folder_item.c
new file mode 100644 (file)
index 0000000..c2bbdf0
--- /dev/null
@@ -0,0 +1,137 @@
+#include "folder.h"
+#include "prefs_folder_item.h"
+#include "summaryview.h"
+#include "prefs.h"
+#include "defs.h"
+
+PrefsFolderItem tmp_prefs;
+
+static PrefParam param[] = {
+       {"sort_by_number", "FALSE", &tmp_prefs.sort_by_number, P_BOOL,
+        NULL, NULL, NULL},
+       {"sort_by_size", "FALSE", &tmp_prefs.sort_by_size, P_BOOL,
+        NULL, NULL, NULL},
+       {"sort_by_date", "FALSE", &tmp_prefs.sort_by_date, P_BOOL,
+        NULL, NULL, NULL},
+       {"sort_by_from", "FALSE", &tmp_prefs.sort_by_from, P_BOOL,
+        NULL, NULL, NULL},
+       {"sort_by_subject", "FALSE", &tmp_prefs.sort_by_subject, P_BOOL,
+        NULL, NULL, NULL},
+       {"sort_by_score", "FALSE", &tmp_prefs.sort_by_score, P_BOOL,
+        NULL, NULL, NULL},
+       {"sort_descending", "FALSE", &tmp_prefs.sort_descending, P_BOOL,
+        NULL, NULL, NULL},
+       {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
+};
+
+void prefs_folder_item_read_config(FolderItem * item)
+{
+       gchar * path;
+
+       path = folder_item_get_path(item);
+       prefs_read_config(param, path, FOLDERITEM_RC);
+       g_free(path);
+
+       * item->prefs = tmp_prefs;
+}
+
+void prefs_folder_item_save_config(FolderItem * item)
+{      
+       gchar * path;
+
+       tmp_prefs = * item->prefs;
+
+       path = folder_item_get_path(item);
+       prefs_save_config(param, path, FOLDERITEM_RC);
+       g_free(path);
+}
+
+void prefs_folder_item_set_config(FolderItem * item,
+                                 int sort_type, gint sort_mode)
+{
+       tmp_prefs = * item->prefs;
+
+       tmp_prefs.sort_by_number = FALSE;
+       tmp_prefs.sort_by_size = FALSE;
+       tmp_prefs.sort_by_date = FALSE;
+       tmp_prefs.sort_by_from = FALSE;
+       tmp_prefs.sort_by_subject = FALSE;
+       tmp_prefs.sort_by_score = FALSE;
+
+       switch (sort_mode) {
+       case SORT_BY_NUMBER:
+               tmp_prefs.sort_by_number = TRUE;
+               break;
+       case SORT_BY_SIZE:
+               tmp_prefs.sort_by_size = TRUE;
+               break;
+       case SORT_BY_DATE:
+               tmp_prefs.sort_by_date = TRUE;
+               break;
+       case SORT_BY_FROM:
+               tmp_prefs.sort_by_from = TRUE;
+               break;
+       case SORT_BY_SUBJECT:
+               tmp_prefs.sort_by_subject = TRUE;
+               break;
+       case SORT_BY_SCORE:
+               tmp_prefs.sort_by_score = TRUE;
+               break;
+       }
+       tmp_prefs.sort_descending = (sort_type == GTK_SORT_DESCENDING);
+
+       * item->prefs = tmp_prefs;
+}
+
+PrefsFolderItem * prefs_folder_item_new(void)
+{
+       PrefsFolderItem * prefs;
+
+       prefs = g_new0(PrefsFolderItem, 1);
+
+       tmp_prefs.sort_by_number = FALSE;
+       tmp_prefs.sort_by_size = FALSE;
+       tmp_prefs.sort_by_date = FALSE;
+       tmp_prefs.sort_by_from = FALSE;
+       tmp_prefs.sort_by_subject = FALSE;
+       tmp_prefs.sort_by_score = FALSE;
+       tmp_prefs.sort_descending = FALSE;
+
+       * prefs = tmp_prefs;
+       
+       return prefs;
+}
+
+void prefs_folder_item_free(PrefsFolderItem * prefs)
+{
+       g_free(prefs);
+}
+
+gint prefs_folder_item_get_sort_mode(FolderItem * item)
+{
+       tmp_prefs = * item->prefs;
+
+       if (tmp_prefs.sort_by_number)
+               return SORT_BY_NUMBER;
+       if (tmp_prefs.sort_by_size)
+               return SORT_BY_SIZE;
+       if (tmp_prefs.sort_by_date)
+               return SORT_BY_DATE;
+       if (tmp_prefs.sort_by_from)
+               return SORT_BY_FROM;
+       if (tmp_prefs.sort_by_subject)
+               return SORT_BY_SUBJECT;
+       if (tmp_prefs.sort_by_score)
+               return SORT_BY_SCORE;
+       return SORT_BY_NONE;
+}
+
+gint prefs_folder_item_get_sort_type(FolderItem * item)
+{
+       tmp_prefs = * item->prefs;
+
+       if (tmp_prefs.sort_descending)
+               return GTK_SORT_DESCENDING;
+       else
+               return GTK_SORT_ASCENDING;
+}
diff --git a/src/prefs_folder_item.h b/src/prefs_folder_item.h
new file mode 100644 (file)
index 0000000..84fa4d5
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef PREFS_FOLDER_ITEM_H
+
+#define PREFS_FOLDER_ITEM_H
+
+#include "folder.h"
+#include <glib.h>
+
+struct _PrefsFolderItem {
+       gchar * directory;
+
+       gboolean sort_by_number;
+       gboolean sort_by_size;
+       gboolean sort_by_date;
+       gboolean sort_by_from;
+       gboolean sort_by_subject;
+       gboolean sort_by_score;
+
+       gboolean sort_descending;
+
+       GSList * scoring;
+};
+
+typedef struct _PrefsFolderItem PrefsFolderItem;
+
+void prefs_folder_item_read_config(FolderItem * item);
+void prefs_folder_item_save_config(FolderItem * item);
+void prefs_folder_item_set_config(FolderItem * item,
+                                 int sort_type, gint sort_mode);
+PrefsFolderItem * prefs_folder_item_new(void);
+void prefs_folder_item_free(PrefsFolderItem * prefs);
+
+#endif
index 76ba140..ba5133f 100644 (file)
@@ -192,6 +192,63 @@ gchar *procheader_get_unfolded_line(gchar *buf, gint len, FILE *fp)
        return buf;
 }
 
+/*
+  tests whether two headers are equal
+*/
+
+gboolean procheader_headername_equal(char * hdr1, char * hdr2)
+{
+       int len1;
+       int len2;
+
+       len1 = strlen(hdr1);
+       len2 = strlen(hdr2);
+       if ((hdr1[len1 - 1] == ':') || (hdr1[len1 - 1] == ' '))
+               len1--;
+       if ((hdr2[len2 - 1] == ':') || (hdr2[len2 - 1] == ' '))
+               len2--;
+       if (len1 != len2)
+               return 0;
+       return (strncasecmp(hdr1, hdr2, len1) == 0);
+}
+
+void procheader_header_free(Header * header)
+{
+       g_free(header->name);
+       g_free(header->body);
+       g_free(header);
+}
+
+/*
+  parse headers, for example :
+  From: dinh@enseirb.fr becomes :
+  header->name = "From:"
+  header->body = "dinh@enseirb.fr"
+ */
+
+Header * procheader_parse_header(gchar * buf)
+{
+       gchar tmp[BUFFSIZE];
+       gchar *p = buf;
+       Header * header;
+
+       if ((*buf == ':') || (*buf == ' '))
+               return NULL;
+
+       for (p = buf; *p ; p++) {
+               if ((*p == ':') || (*p == ' ')) {
+                       header = g_new(Header, 1);
+                       header->name = g_strndup(buf, p - buf + 1);
+                       p++;
+                       while (*p == ' ' || *p == '\t') p++;
+                       conv_unmime_header(tmp, sizeof(tmp), p, NULL);
+                       header->body = g_strdup(tmp);
+                       return header;
+               }
+       }
+       return NULL;
+}
+
 GSList *procheader_get_header_list(const gchar *file)
 {
        FILE *fp;
@@ -206,20 +263,9 @@ GSList *procheader_get_header_list(const gchar *file)
        }
 
        while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) {
-               if (*buf == ':') continue;
-               for (p = buf; *p && *p != ' '; p++) {
-                       if (*p == ':') {
-                               header = g_new(Header, 1);
-                               header->name = g_strndup(buf, p - buf);
-                               p++;
-                               while (*p == ' ' || *p == '\t') p++;
-                               conv_unmime_header(tmp, sizeof(tmp), p, NULL);
-                               header->body = g_strdup(tmp);
-
-                               hlist = g_slist_append(hlist, header);
-                               break;
-                       }
-               }
+               header = procheader_parse_header(buf);
+               if (header != NULL)
+                       hlist = g_slist_append(hlist, header);
        }
 
        fclose(fp);
@@ -233,9 +279,7 @@ void procheader_header_list_destroy(GSList *hlist)
        while (hlist != NULL) {
                header = hlist->data;
 
-               g_free(header->name);
-               g_free(header->body);
-               g_free(header);
+               procheader_header_free(header);
                hlist = g_slist_remove(hlist, header);
        }
 }
index 49f17e7..5875327 100644 (file)
@@ -66,5 +66,9 @@ time_t procheader_date_parse          (gchar          *dest,
 void procheader_date_get_localtime     (gchar          *dest,
                                         gint            len,
                                         const time_t    timer);
+Header * procheader_parse_header        (gchar * buf);
+
+gboolean procheader_headername_equal    (char * hdr1, char * hdr2);
+void procheader_header_free             (Header * header);
 
 #endif /* __PROCHEADER_H__ */
index ec70158..000d4c3 100644 (file)
@@ -755,6 +755,8 @@ MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo)
        MEMBDUP(xface);
        MEMBDUP(dispositionnotificationto);
 
+       MEMBCOPY(score);
+
        return newmsginfo;
 }
 
index ddc716d..f4e80e8 100644 (file)
@@ -124,6 +124,8 @@ struct _MsgInfo
 
        gchar *dispositionnotificationto;
 
+       int score;
+
        /* used only for encrypted messages */
        gchar *plaintext_file;
        guint decryption_failed : 1;
diff --git a/src/scoring.c b/src/scoring.c
new file mode 100644 (file)
index 0000000..8511e10
--- /dev/null
@@ -0,0 +1,184 @@
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include "intl.h"
+#include "utils.h"
+#include "defs.h"
+#include "procheader.h"
+#include "matcher.h"
+#include "scoring.h"
+
+#define PREFSBUFSIZE           1024
+
+GSList * prefs_scoring = NULL;
+
+ScoringProp * scoringprop_parse(gchar ** str)
+{
+       gchar * tmp;
+       gchar * save;
+       gint key;
+       ScoringProp * scoring;
+       gint score;
+       MatcherList * matchers;
+
+       tmp = * str;
+
+       matchers = matcherlist_parse(&tmp);
+       if (tmp == NULL) {
+               * str = NULL;
+               return NULL;
+       }
+
+       key = matcher_parse_keyword(&tmp);
+
+       if (tmp == NULL) {
+               matcherlist_free(matchers);
+               * str = NULL;
+               return NULL;
+       }
+
+       if (key != SCORING_SCORE) {
+               matcherlist_free(matchers);
+               * str = NULL;
+               return NULL;
+       }
+
+       score = matcher_parse_number(&tmp);
+
+       if (tmp == NULL) {
+               matcherlist_free(matchers);
+               * str = NULL;
+               return NULL;
+       }
+
+       scoring = scoringprop_new(matchers, score);
+
+       * str = tmp;
+       return scoring;
+}
+
+
+ScoringProp * scoringprop_new(MatcherList * matchers, int score)
+{
+       ScoringProp * scoring;
+
+       scoring = g_new0(ScoringProp, 1);
+       scoring->matchers = matchers;
+       scoring->score = score;
+
+       return scoring;
+}
+
+void scoringprop_free(ScoringProp * prop)
+{
+       matcherlist_free(prop->matchers);
+       g_free(prop);
+}
+
+gint scoringprop_score_message(ScoringProp * scoring, MsgInfo * info)
+{
+       if (matcherlist_match(scoring->matchers, info))
+               return scoring->score;
+       else
+               return 0;
+}
+
+gint score_message(GSList * scoring_list, MsgInfo * info)
+{
+       gint score = 0;
+       gint add_score;
+       GSList * l;
+
+       for(l = scoring_list ; l != NULL ; l = g_slist_next(l)) {
+               ScoringProp * scoring = (ScoringProp *) l->data;
+               
+               add_score = (scoringprop_score_message(scoring, info));
+               if (add_score == MAX_SCORE || add_score == MIN_SCORE) {
+                       score = add_score;
+                       break;
+               }
+               score += add_score;
+       }
+       return score;
+}
+
+#ifdef 0
+static void scoringprop_print(ScoringProp * prop)
+{
+       GSList * l;
+
+       if (prop == NULL) {
+               printf("no scoring\n");
+               return;
+       }
+
+       printf("----- scoring ------\n");
+       for(l = prop->matchers ; l != NULL ; l = g_slist_next(l)) {
+               matcherprop_print((MatcherProp *) l->data);
+       }
+       printf("cond: %s\n", prop->bool_and ? "and" : "or");
+       printf("score: %i\n", prop->score);
+}
+#endif
+
+/*
+  syntax for scoring config
+
+  file ~/.sylpheed/scoringrc
+
+  header "x-mailing" match "toto" score -10
+  subject match "regexp" & to regexp "regexp" score 50
+  subject match "regexp" | to regexpcase "regexp" | age_sup 5 score 30
+
+  if score is = MIN_SCORE (-999), no more match is done in the list
+  if score is = MAX_SCORE (-999), no more match is done in the list
+ */
+
+void prefs_scoring_read_config(void)
+{
+       gchar *rcpath;
+       FILE *fp;
+       gchar buf[PREFSBUFSIZE];
+
+       debug_print(_("Reading headers configuration...\n"));
+
+       rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, SCORING_RC, NULL);
+       if ((fp = fopen(rcpath, "r")) == NULL) {
+               if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
+               g_free(rcpath);
+               prefs_scoring = NULL;
+               return;
+       }
+       g_free(rcpath);
+
+       /* remove all scoring */
+       while (prefs_scoring != NULL) {
+               ScoringProp * scoring = (ScoringProp *) prefs_scoring->data;
+               scoringprop_free(scoring);
+               prefs_scoring = g_slist_remove(prefs_scoring, scoring);
+       }
+
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               ScoringProp * scoring;
+               gchar * tmp;
+
+               g_strchomp(buf);
+
+               if (*buf != '#') {
+                       tmp = buf;
+                       scoring = scoringprop_parse(&tmp);
+                       if (tmp != NULL) {
+                               prefs_scoring = g_slist_append(prefs_scoring,
+                                                              scoring);
+                       }
+                       else {
+                               // debug
+                               g_warning(_("syntax error : %s\n"), buf);
+                       }
+               }
+       }
+
+       fclose(fp);
+}
diff --git a/src/scoring.h b/src/scoring.h
new file mode 100644 (file)
index 0000000..2d7122f
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef SCORING_H
+
+#define SCORING_H
+
+#include <glib.h>
+#include "matcher.h"
+#include "procmsg.h"
+
+#define MAX_SCORE 999
+#define MIN_SCORE -999
+
+struct _ScoringProp {
+       MatcherList * matchers;
+       int score;
+};
+
+typedef struct _ScoringProp ScoringProp;
+
+extern GSList * prefs_scoring;
+
+
+ScoringProp * scoringprop_new(MatcherList * matchers, int score);
+void scoringprop_free(ScoringProp * prop);
+gint scoringprop_score_message(ScoringProp * scoring, MsgInfo * info);
+
+ScoringProp * scoringprop_parse(gchar ** str);
+
+
+gint score_message(GSList * scoring_list, MsgInfo * info);
+
+void prefs_scoring_read_config(void);
+
+#endif
index 20ffd22..13bc81d 100644 (file)
@@ -72,6 +72,7 @@
 #include "filter.h"
 #include "folder.h"
 #include "addressbook.h"
+#include "scoring.h"
 
 #include "pixmaps/dir-open.xpm"
 #include "pixmaps/mark.xpm"
@@ -253,6 +254,8 @@ static void summary_add_sender_to_cb (SummaryView                   *summaryview,
 
 static void summary_num_clicked                (GtkWidget              *button,
                                         SummaryView            *summaryview);
+static void summary_score_clicked       (GtkWidget *button,
+                                        SummaryView *summaryview);
 static void summary_size_clicked       (GtkWidget              *button,
                                         SummaryView            *summaryview);
 static void summary_date_clicked       (GtkWidget              *button,
@@ -290,6 +293,9 @@ static gint summary_cmp_by_from             (GtkCList               *clist,
 static gint summary_cmp_by_subject     (GtkCList               *clist,
                                         gconstpointer           ptr1,
                                         gconstpointer           ptr2);
+static gint summary_cmp_by_score       (GtkCList               *clist,
+                                        gconstpointer           ptr1,
+                                        gconstpointer           ptr2);
 
 GtkTargetEntry summary_drag_types[1] =
 {
@@ -371,7 +377,8 @@ SummaryView *summary_create(void)
                titles[S_COL_FROM]    = "From";
                titles[S_COL_SUBJECT] = "Subject";
        }
-       titles[S_COL_SIZE] = _("Size");
+       titles[S_COL_SIZE]  = _("Size");
+       titles[S_COL_SCORE] = _("Score");
 
        ctree = gtk_sctree_new_with_titles(N_SUMMARY_COLS, S_COL_SUBJECT, titles);
        gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
@@ -388,6 +395,8 @@ SummaryView *summary_create(void)
                                           GTK_JUSTIFY_CENTER);
        gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_NUMBER,
                                           GTK_JUSTIFY_RIGHT);
+       gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_SCORE,
+                                          GTK_JUSTIFY_RIGHT);
        gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_SIZE,
                                           GTK_JUSTIFY_RIGHT);
        gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_MARK,
@@ -398,6 +407,8 @@ SummaryView *summary_create(void)
                                   SUMMARY_COL_MIME_WIDTH);
        gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_NUMBER,
                                   prefs_common.summary_col_number);
+       gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_SCORE,
+                                  prefs_common.summary_col_score);
        gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_SIZE,
                                   prefs_common.summary_col_size);
        gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_DATE,
@@ -428,6 +439,11 @@ SummaryView *summary_create(void)
                 "clicked",
                 GTK_SIGNAL_FUNC(summary_num_clicked),
                 summaryview);
+       gtk_signal_connect
+               (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_SCORE].button),
+                "clicked",
+                GTK_SIGNAL_FUNC(summary_score_clicked),
+                summaryview);
        gtk_signal_connect
                (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_SIZE].button),
                 "clicked",
@@ -580,6 +596,9 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item,
        gchar *buf;
        gboolean is_refresh;
        guint prev_msgnum = 0;
+       GSList *cur;
+       gint sort_mode;
+       gint sort_type;
 
        STATUSBAR_POP(summaryview->mainwin);
 
@@ -641,6 +660,12 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item,
 
        mlist = item->folder->get_msg_list(item->folder, item, !update_cache);
 
+       for(cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
+               MsgInfo * msginfo = (MsgInfo *) cur->data;
+
+               msginfo->score = score_message(prefs_scoring, msginfo);
+       }
+
        STATUSBAR_POP(summaryview->mainwin);
 
        /* set ctree and hash table from the msginfo list
@@ -705,6 +730,20 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item,
 
        main_window_cursor_normal(summaryview->mainwin);
 
+       /* sort before */
+       sort_mode = prefs_folder_item_get_sort_mode(item);
+       sort_type = prefs_folder_item_get_sort_type(item);
+
+       if (sort_mode != SORT_BY_NONE) {
+               summaryview->sort_mode = sort_mode;
+               if (sort_type == GTK_SORT_DESCENDING)
+                       summaryview->sort_type = GTK_SORT_ASCENDING;
+               else
+                       summaryview->sort_type = GTK_SORT_DESCENDING;
+
+               summary_sort(summaryview, sort_mode);
+       }
+
        return TRUE;
 }
 
@@ -1298,6 +1337,9 @@ void summary_sort(SummaryView *summaryview, SummarySortType type)
        case SORT_BY_SUBJECT:
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_subject;
                break;
+       case SORT_BY_SCORE:
+               cmp_func = (GtkCListCompareFunc)summary_cmp_by_score;
+               break;
        default:
                return;
        }
@@ -1324,6 +1366,11 @@ void summary_sort(SummaryView *summaryview, SummarySortType type)
        gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
        //gtkut_ctree_set_focus_row(ctree, summaryview->selected);
 
+       prefs_folder_item_set_config(summaryview->folder_item,
+                                    summaryview->sort_type,
+                                    summaryview->sort_mode);
+       prefs_folder_item_save_config(summaryview->folder_item);
+
        debug_print(_("done.\n"));
        STATUSBAR_POP(summaryview->mainwin);
 
@@ -1495,6 +1542,7 @@ static void summary_set_header(gchar *text[], MsgInfo *msginfo)
        text[S_COL_MIME] = NULL;
        text[S_COL_NUMBER] = itos(msginfo->msgnum);
        text[S_COL_SIZE]   = to_human_readable(msginfo->size);
+       text[S_COL_SCORE]  = itos(msginfo->score);
 
        if (msginfo->date_t) {
                procheader_date_get_localtime(date_modified,
@@ -2842,6 +2890,9 @@ static void summary_col_resized(GtkCList *clist, gint column, gint width,
        case S_COL_NUMBER:
                prefs_common.summary_col_number = width;
                break;
+       case S_COL_SCORE:
+               prefs_common.summary_col_score = width;
+               break;
        case S_COL_SIZE:
                prefs_common.summary_col_size = width;
                break;
@@ -2962,6 +3013,12 @@ static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
        summary_sort(summaryview, SORT_BY_NUMBER);
 }
 
+static void summary_score_clicked(GtkWidget *button,
+                                 SummaryView *summaryview)
+{
+       summary_sort(summaryview, SORT_BY_SCORE);
+}
+
 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
 {
        summary_sort(summaryview, SORT_BY_SIZE);
@@ -2991,6 +3048,7 @@ void summary_change_display_item(SummaryView *summaryview)
        gtk_clist_set_column_visibility(clist, S_COL_UNREAD, prefs_common.show_unread);
        gtk_clist_set_column_visibility(clist, S_COL_MIME, prefs_common.show_mime);
        gtk_clist_set_column_visibility(clist, S_COL_NUMBER, prefs_common.show_number);
+       gtk_clist_set_column_visibility(clist, S_COL_SCORE, prefs_common.show_score);
        gtk_clist_set_column_visibility(clist, S_COL_SIZE, prefs_common.show_size);
        gtk_clist_set_column_visibility(clist, S_COL_DATE, prefs_common.show_date);
        gtk_clist_set_column_visibility(clist, S_COL_FROM, prefs_common.show_from);
@@ -3122,3 +3180,19 @@ static gint summary_cmp_by_subject(GtkCList *clist,
 
        return strcasecmp(msginfo1->subject, msginfo2->subject);
 }
+
+static gint summary_cmp_by_score(GtkCList *clist,
+                                gconstpointer ptr1, gconstpointer ptr2)
+{
+       MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
+       MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
+       int diff;
+
+       /* if score are equal, sort by date */
+
+       diff = msginfo1->score - msginfo2->score;
+       if (diff != 0)
+               return diff;
+       else
+               return summary_cmp_by_date(clist, ptr1, ptr2);
+}
index 42d5042..19f5008 100644 (file)
@@ -43,13 +43,14 @@ typedef enum
        S_COL_UNREAD    = 1,
        S_COL_MIME      = 2,
        S_COL_NUMBER    = 3,
-       S_COL_SIZE      = 4,
-       S_COL_DATE      = 5,
-       S_COL_FROM      = 6,
-       S_COL_SUBJECT   = 7
+       S_COL_SCORE     = 4,
+       S_COL_SIZE      = 5,
+       S_COL_DATE      = 6,
+       S_COL_FROM      = 7,
+       S_COL_SUBJECT   = 8
 } SummaryColumnPos;
 
-#define N_SUMMARY_COLS 8
+#define N_SUMMARY_COLS 9
 
 typedef enum
 {
@@ -58,7 +59,8 @@ typedef enum
        SORT_BY_SIZE,
        SORT_BY_DATE,
        SORT_BY_FROM,
-       SORT_BY_SUBJECT
+       SORT_BY_SUBJECT,
+       SORT_BY_SCORE
 } SummarySortType;
 
 typedef enum
index 49e6170..82906f0 100644 (file)
@@ -938,22 +938,6 @@ enum
        H_ORGANIZATION  = 11,
 };
 
-static gboolean hdrequal(char * hdr1, char * hdr2)
-{
-       int len1;
-       int len2;
-
-       len1 = strlen(hdr1);
-       len2 = strlen(hdr2);
-       if (hdr1[len1 - 1] == ':')
-               len1--;
-       if (hdr2[len2 - 1] == ':')
-               len2--;
-       if (len1 != len2)
-               return 0;
-       return (strncasecmp(hdr1, hdr2, len1) == 0);
-}
-
 static GPtrArray *textview_scan_header(TextView *textview, FILE *fp)
 {
        /*
@@ -1011,20 +995,9 @@ static GPtrArray *textview_scan_header(TextView *textview, FILE *fp)
                gchar * p;
                Header *header;
 
-               if (*buf == ':') continue;
-               for (p = buf; *p && *p != ' '; p++) {
-                       if (*p == ':') {
-                               header = g_new(Header, 1);
-                               header->name = g_strndup(buf, p - buf + 1);
-                               p++;
-                               /* while (*p == ' ' || *p == '\t') p++; */
-                               conv_unmime_header(tmp, sizeof(tmp), p, NULL);
-                               header->body = g_strdup(tmp);
-
-                               g_ptr_array_add(headers, header);
-                               break;
-                       }
-               }
+               header = procheader_parse_header(buf);
+               if (header != NULL)
+                       g_ptr_array_add(headers, header);
        }
 
        sorted_headers = g_ptr_array_new();
@@ -1033,12 +1006,11 @@ static GPtrArray *textview_scan_header(TextView *textview, FILE *fp)
                HeaderDisplayProp * dp = (HeaderDisplayProp *) l->data;
                for(i = 0 ; i < headers->len ; i++) {
                        Header * header = g_ptr_array_index(headers, i);
-                       if (hdrequal(header->name, dp->name)) {
+                       if (procheader_headername_equal(header->name,
+                                                       dp->name)) {
                                if (dp->hidden) {
                                        g_ptr_array_remove_index(headers, i);
-                                       g_free(header->body);
-                                       g_free(header->name);
-                                       g_free(header);
+                                       procheader_header_free(header);
                                        i--;
                                }
                                else {
@@ -1052,14 +1024,20 @@ static GPtrArray *textview_scan_header(TextView *textview, FILE *fp)
        }
 
        if (prefs_display_headers.show_other_headers) {
-               for(i = 0 ; i < headers->len ; i++) {
-                       Header * header = g_ptr_array_index(headers, i);
+               while (headers->len != 0) {
+                       Header * header = g_ptr_array_index(headers, 0);
                        g_ptr_array_add(sorted_headers, header);
+                       g_ptr_array_remove_index(headers, 0);
                }
        }
 
-       g_ptr_array_free(headers, TRUE);
-       
+       for(i = 0 ; i < headers->len ; i++) {
+               Header * header = g_ptr_array_index(headers, i);
+               procheader_header_free(header);
+       }
+
+       g_ptr_array_free(headers, FALSE);
+
        return sorted_headers;
 }
 
@@ -1079,6 +1057,8 @@ static void textview_show_header(TextView *textview, GPtrArray *headers)
 
                gtk_text_insert(text, textview->boldfont, NULL, NULL,
                                header->name, -1);
+               gtk_text_insert(text, textview->boldfont, NULL, NULL,
+                               " ", -1);
                if (prefs_common.enable_color &&
                    (strncmp(header->name, "X-Mailer", 8) == 0 ||
                     strncmp(header->name, "X-Newsreader", 12) == 0) &&