#include "matcher_parser.h"
#include "prefs_gtk.h"
#include "addr_compl.h"
+#include "codeconv.h"
+#include "quoted-printable.h"
#include <ctype.h>
/*!
{MATCHACTION_SET_SCORE, "set_score"},
{MATCHACTION_STOP, "stop"},
{MATCHACTION_HIDE, "hide"},
+ {MATCHACTION_IGNORE, "ignore"},
};
/*!
*/
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);
}
}
+/* FIXME body search is a hack. */
+static gboolean matcherprop_string_decode_match(MatcherProp *prop, const gchar *str)
+{
+ gchar *utf = NULL;
+ gchar tmp[BUFFSIZE];
+ gboolean res = FALSE;
+
+ if (str == NULL)
+ return FALSE;
+
+ /* we try to decode QP first, because it's faster than base64 */
+ qp_decode_const(tmp, BUFFSIZE-1, str);
+ if (!g_utf8_validate(tmp, -1, NULL)) {
+ utf = conv_codeset_strdup
+ (tmp, conv_get_locale_charset_str_no_utf8(),
+ CS_INTERNAL);
+ res = matcherprop_string_match(prop, utf);
+ g_free(utf);
+ } else {
+ res = matcherprop_string_match(prop, tmp);
+ }
+
+ if (res == FALSE && (strchr(prop->expr, '=') || strchr(prop->expr, '_')) ) {
+ /* if searching for something with an equal char, maybe
+ * we should try to match the non-decoded string.
+ * In case it was not qp-encoded. */
+ if (!g_utf8_validate(str, -1, NULL)) {
+ utf = conv_codeset_strdup
+ (str, conv_get_locale_charset_str_no_utf8(),
+ CS_INTERNAL);
+ res = matcherprop_string_match(prop, utf);
+ } else {
+ res = matcherprop_string_match(prop, str);
+ }
+ }
+
+ /* FIXME base64 decoding is too slow, especially since text can
+ * easily be handled as base64. Don't even try now. */
+
+ return res;
+}
+
/*!
*\brief Execute a command defined in the matcher structure
*
}
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);
}
}
}
-/*!
- *\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.
*/
static gboolean matcherlist_match_headers(MatcherList *matchers, FILE *fp)
{
+ GSList *l;
gchar buf[BUFFSIZE];
- while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1)
- if (matcherlist_match_one_header(matchers, buf))
- return TRUE;
-
+ while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
+ for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
+ MatcherProp *matcher = (MatcherProp *) l->data;
+
+ if (matcher->done)
+ continue;
+
+ /* if the criteria is ~headers_part or ~message, ZERO lines
+ * must NOT match for the rule to match. */
+ if (matcher->criteria == MATCHCRITERIA_NOT_HEADERS_PART ||
+ matcher->criteria == MATCHCRITERIA_NOT_MESSAGE) {
+ if (matcherprop_match_one_header(matcher, buf)) {
+ matcher->result = TRUE;
+ } else {
+ matcher->result = FALSE;
+ matcher->done = TRUE;
+ }
+ /* else, just one line matching is enough for the rule to match
+ */
+ } else if (matcherprop_criteria_headers(matcher) ||
+ matcherprop_criteria_message(matcher)){
+ if (matcherprop_match_one_header(matcher, buf)) {
+ matcher->result = TRUE;
+ matcher->done = TRUE;
+ }
+ }
+
+ /* if the rule matched and the matchers are OR, no need to
+ * check the others */
+ if (matcher->result && matcher->done) {
+ if (!matchers->bool_and)
+ return TRUE;
+ }
+ }
+ }
return FALSE;
}
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;
}
*/
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;
}
read_body = TRUE;
}
matcher->result = FALSE;
+ matcher->done = FALSE;
}
if (!read_headers && !read_body)
if (read_headers) {
if (matcherlist_match_headers(matchers, fp))
read_body = FALSE;
- }
- else {
+ } else {
matcherlist_skip_headers(fp);
}
GSList *l;
gboolean result;
+ if (!matchers)
+ return FALSE;
+
if (matchers->bool_and)
result = TRUE;
else
if (NULL == (filtering_str = filteringprop_to_string(prop)))
continue;
-
+
+ if (prop->enabled) {
+ if (fputs("enabled ", fp) == EOF) {
+ FILE_OP_ERROR("filtering config", "fputs || fputc");
+ return;
+ }
+ } else {
+ if (fputs("disabled ", fp) == EOF) {
+ FILE_OP_ERROR("filtering config", "fputs || fputc");
+ return;
+ }
+ }
+
if (fputs("rulename \"", fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs || fputc");
g_free(filtering_str);
/* 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);