*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* GNU General Public License for more details.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
*/
#ifdef HAVE_CONFIG_H
#include <ctype.h>
#include "prefs_common.h"
#include "log.h"
+#include "tags.h"
/*!
*\brief Keyword lookup element
{MATCHCRITERIA_NOT_COLORLABEL, "~colorlabel"},
{MATCHCRITERIA_IGNORE_THREAD, "ignore_thread"},
{MATCHCRITERIA_NOT_IGNORE_THREAD, "~ignore_thread"},
+ {MATCHCRITERIA_WATCH_THREAD, "watch_thread"},
+ {MATCHCRITERIA_NOT_WATCH_THREAD, "~watch_thread"},
{MATCHCRITERIA_SPAM, "spam"},
{MATCHCRITERIA_NOT_SPAM, "~spam"},
{MATCHCRITERIA_NOT_CC, "~cc"},
{MATCHCRITERIA_TO_OR_CC, "to_or_cc"},
{MATCHCRITERIA_NOT_TO_AND_NOT_CC, "~to_or_cc"},
+ {MATCHCRITERIA_TAG, "tag"},
+ {MATCHCRITERIA_NOT_TAG, "~tag"},
+ {MATCHCRITERIA_TAGGED, "tagged"},
+ {MATCHCRITERIA_NOT_TAGGED, "~tagged"},
{MATCHCRITERIA_AGE_GREATER, "age_greater"},
{MATCHCRITERIA_AGE_LOWER, "age_lower"},
{MATCHCRITERIA_NEWSGROUPS, "newsgroups"},
{MATCHACTION_STOP, "stop"},
{MATCHACTION_HIDE, "hide"},
{MATCHACTION_IGNORE, "ignore"},
- {MATCHACTION_ADD_TO_ADDRESSBOOK, "add_to_addressbook"}
+ {MATCHACTION_WATCH, "watch"},
+ {MATCHACTION_ADD_TO_ADDRESSBOOK, "add_to_addressbook"},
+ {MATCHACTION_SET_TAG, "set_tag"},
+ {MATCHACTION_UNSET_TAG, "unset_tag"},
+ {MATCHACTION_CLEAR_TAGS, "clear_tags"},
};
enum {
prop = g_new0(MatcherProp, 1);
prop->criteria = criteria;
prop->header = header != NULL ? g_strdup(header) : NULL;
- prop->expr = expr != NULL ? g_strdup(expr) : NULL;
+
+ if (matchtype == MATCHTYPE_MATCHCASE ||
+ matchtype == MATCHTYPE_REGEXPCASE)
+ prop->expr = expr != NULL ? g_utf8_casefold(expr, -1) : NULL;
+ else
+ prop->expr = expr != NULL ? g_strdup(expr) : NULL;
+
prop->matchtype = matchtype;
prop->preg = NULL;
prop->value = value;
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);
+ g_slist_length(address_list), folderpath?folderpath:"(null)");
if (folderpath == NULL ||
strcasecmp(folderpath, _("Any")) == 0 ||
* matcher structure
*/
static gboolean matcherprop_string_match(MatcherProp *prop, const gchar *str,
- const gchar *debug_context)
+ const gchar *debug_context)
{
gchar *str1;
- gchar *str2;
gboolean ret = FALSE;
-
+ gboolean should_free = FALSE;
if (str == NULL)
return FALSE;
+ if (prop->matchtype == MATCHTYPE_REGEXPCASE ||
+ prop->matchtype == MATCHTYPE_MATCHCASE) {
+ str1 = g_utf8_casefold(str, -1);
+ should_free = TRUE;
+ } else {
+ str1 = (gchar *)str;
+ should_free = FALSE;
+ }
+
switch (prop->matchtype) {
case MATCHTYPE_REGEXPCASE:
case MATCHTYPE_REGEXP:
if (prop->preg == NULL)
return FALSE;
- if (regexec(prop->preg, str, 0, NULL, 0) == 0)
+ if (regexec(prop->preg, str1, 0, NULL, 0) == 0)
ret = TRUE;
else
ret = FALSE;
strretchomp(stripped);
if (ret) {
log_print(LOG_DEBUG_FILTERING,
- "%s value [ %s ] matches regular expression [ %s ]\n",
- debug_context, stripped, prop->expr);
+ "%s value [ %s ] matches regular expression [ %s ] (%s)\n",
+ debug_context, stripped, prop->expr,
+ prop->matchtype == MATCHTYPE_REGEXP ? _("Case sensitive"):_("Case unsensitive"));
} else {
log_print(LOG_DEBUG_FILTERING,
- "%s value [ %s ] doesn't matches regular expression [ %s ]\n",
- debug_context, stripped, prop->expr);
+ "%s value [ %s ] doesn't matches regular expression [ %s ] (%s)\n",
+ debug_context, stripped, prop->expr,
+ prop->matchtype == MATCHTYPE_REGEXP ? _("Case sensitive"):_("Case unsensitive"));
}
g_free(stripped);
}
break;
+ case MATCHTYPE_MATCHCASE:
case MATCHTYPE_MATCH:
- ret = (strstr(str, prop->expr) != NULL);
+ ret = (strstr(str1, prop->expr) != NULL);
/* debug output */
if (debug_filtering_session
strretchomp(stripped);
if (ret) {
log_print(LOG_DEBUG_FILTERING,
- "%s value [ %s ] contains [ %s ] (case sensitive)\n",
- debug_context, stripped, prop->expr);
+ "%s value [ %s ] contains [ %s ] (%s)\n",
+ debug_context, stripped, prop->expr,
+ prop->matchtype == MATCHTYPE_MATCH ? _("Case sensitive"):_("Case unsensitive"));
} else {
log_print(LOG_DEBUG_FILTERING,
- "%s value [ %s ] doesn't contains [ %s ] (case sensitive)\n",
- debug_context, stripped, prop->expr);
+ "%s value [ %s ] doesn't contains [ %s ] (%s)\n",
+ debug_context, stripped, prop->expr,
+ prop->matchtype == MATCHTYPE_MATCH ? _("Case sensitive"):_("Case unsensitive"));
}
g_free(stripped);
}
break;
- /* FIXME: put upper in unesc_str */
- case MATCHTYPE_MATCHCASE:
- str2 = alloca(strlen(prop->expr) + 1);
- strcpy(str2, prop->expr);
- g_strup(str2);
- str1 = alloca(strlen(str) + 1);
- strcpy(str1, str);
- g_strup(str1);
- ret = (strstr(str1, str2) != NULL);
+ default:
+ break;
+ }
+
+ if (should_free) {
+ g_free(str1);
+ }
+ return ret;
+}
- /* debug output */
- if (debug_filtering_session
- && prefs_common.filtering_debug_level >= FILTERING_DEBUG_LEVEL_HIGH) {
- gchar *stripped = g_strdup(str);
+/*!
+ *\brief Find out if a tag matches a condition
+ *
+ *\param prop Matcher structure
+ *\param msginfo message to check
+ *
+ *\return gboolean TRUE if msginfo matches the condition in the
+ * matcher structure
+ */
+static gboolean matcherprop_tag_match(MatcherProp *prop, MsgInfo *msginfo,
+ const gchar *debug_context)
+{
+ gboolean ret = FALSE;
+ GSList *cur;
- strretchomp(stripped);
- if (ret) {
- log_print(LOG_DEBUG_FILTERING,
- "%s value [ %s ] contains [ %s ] (case insensitive)\n",
- debug_context, stripped, prop->expr);
- } else {
- log_print(LOG_DEBUG_FILTERING,
- "%s [ %s ] doesn't contains [ %s ] (case insensitive)\n",
- debug_context, stripped, prop->expr);
- }
- g_free(stripped);
+ if (msginfo == NULL || msginfo->tags == NULL)
+ return FALSE;
+
+ for (cur = msginfo->tags; cur; cur = cur->next) {
+ const gchar *str = tags_get_tag(GPOINTER_TO_INT(cur->data));
+ if (!str)
+ continue;
+ if (matcherprop_string_match(prop, str, debug_context)) {
+ ret = TRUE;
+ break;
}
- break;
-
- default:
- break;
}
return ret;
}
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);
}
if (cmd == NULL)
return FALSE;
-#if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
+#if (defined USE_PTHREAD && ((defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3))) || !defined __GLIBC__))
/* debug output */
if (debug_filtering_session
&& prefs_common.filtering_debug_level >= FILTERING_DEBUG_LEVEL_HIGH) {
matcher_test_thread, td) != 0)
retval = system(cmd);
else {
- printf("waiting for test thread\n");
+ debug_print("waiting for test thread\n");
while(!td->done) {
/* don't let the interface freeze while waiting */
claws_do_idle();
}
pthread_join(pt, &res);
retval = GPOINTER_TO_INT(res);
- printf(" test thread returned %d\n", retval);
+ debug_print(" test thread returned %d\n", retval);
}
g_free(td);
#else
return MSG_IS_IGNORE_THREAD(info->flags);
case MATCHCRITERIA_NOT_IGNORE_THREAD:
return !MSG_IS_IGNORE_THREAD(info->flags);
+ case MATCHCRITERIA_WATCH_THREAD:
+ return MSG_IS_WATCH_THREAD(info->flags);
+ case MATCHCRITERIA_NOT_WATCH_THREAD:
+ return !MSG_IS_WATCH_THREAD(info->flags);
case MATCHCRITERIA_SUBJECT:
- return matcherprop_string_match(prop, info->subject, _("subject"));
+ return matcherprop_string_match(prop, info->subject,
+ prefs_common_translated_header_name("Subject:"));
case MATCHCRITERIA_NOT_SUBJECT:
- return !matcherprop_string_match(prop, info->subject, _("subject"));
+ return !matcherprop_string_match(prop, info->subject,
+ prefs_common_translated_header_name("Subject:"));
case MATCHCRITERIA_FROM:
- return matcherprop_string_match(prop, info->from, _("From: header"));
case MATCHCRITERIA_NOT_FROM:
- return !matcherprop_string_match(prop, info->from, _("From: header"));
+ {
+ gchar *context;
+ gboolean ret;
+
+ context = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("From:"));
+ ret = matcherprop_string_match(prop, info->from, context);
+ g_free(context);
+ return (prop->criteria == MATCHCRITERIA_FROM)? ret : !ret;
+ }
case MATCHCRITERIA_TO:
- return matcherprop_string_match(prop, info->to, _("To: header"));
case MATCHCRITERIA_NOT_TO:
- return !matcherprop_string_match(prop, info->to, _("To: header"));
+ {
+ gchar *context;
+ gboolean ret;
+
+ context = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("To:"));
+ ret = matcherprop_string_match(prop, info->to, context);
+ g_free(context);
+ return (prop->criteria == MATCHCRITERIA_TO)? ret : !ret;
+ }
case MATCHCRITERIA_CC:
- return matcherprop_string_match(prop, info->cc, _("Cc: header"));
case MATCHCRITERIA_NOT_CC:
- return !matcherprop_string_match(prop, info->cc, _("Cc: header"));
+ {
+ gchar *context;
+ gboolean ret;
+
+ context = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("Cc:"));
+ ret = matcherprop_string_match(prop, info->cc, context);
+ g_free(context);
+ return (prop->criteria == MATCHCRITERIA_CC)? ret : !ret;
+ }
case MATCHCRITERIA_TO_OR_CC:
- return matcherprop_string_match(prop, info->to, _("To: header"))
- || matcherprop_string_match(prop, info->cc, _("Cc: header"));
+ {
+ gchar *context1, *context2;
+ gboolean ret;
+
+ context1 = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("To:"));
+ context2 = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("Cc:"));
+ ret = matcherprop_string_match(prop, info->to, context1)
+ || matcherprop_string_match(prop, info->cc, context2);
+ g_free(context1);
+ g_free(context2);
+ return ret;
+ }
case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
- return !(matcherprop_string_match(prop, info->to, _("To:"))
- || matcherprop_string_match(prop, info->cc, _("Cc:")));
+ {
+ gchar *context1, *context2;
+ gboolean ret;
+
+ context1 = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("To:"));
+ context2 = g_strdup_printf(_("%s header"), prefs_common_translated_header_name("Cc:"));
+ ret = !(matcherprop_string_match(prop, info->to, context1)
+ || matcherprop_string_match(prop, info->cc, context2));
+ g_free(context1);
+ g_free(context2);
+ return ret;
+ }
+ case MATCHCRITERIA_TAG:
+ case MATCHCRITERIA_NOT_TAG:
+ {
+ gboolean ret;
+
+ ret = matcherprop_tag_match(prop, info, _("Tag"));
+ return (prop->criteria == MATCHCRITERIA_TAG)? ret : !ret;
+ }
+ case MATCHCRITERIA_TAGGED:
+ case MATCHCRITERIA_NOT_TAGGED:
+ {
+ gboolean ret;
+
+ ret = (info->tags != NULL);
+ return (prop->criteria == MATCHCRITERIA_TAGGED)? ret : !ret;
+ }
case MATCHCRITERIA_AGE_GREATER:
{
gboolean ret;
return ret;
}
case MATCHCRITERIA_NEWSGROUPS:
- return matcherprop_string_match(prop, info->newsgroups, _("Newsgroups: header"));
case MATCHCRITERIA_NOT_NEWSGROUPS:
- return !matcherprop_string_match(prop, info->newsgroups, _("Newsgroups: header"));
+ {
+ gchar *context;
+ gboolean ret;
+
+ context = g_strdup_printf(_("%s header"),
+ prefs_common_translated_header_name("Newsgroups:"));
+ ret = matcherprop_string_match(prop, info->newsgroups, context);
+ g_free(context);
+ return (prop->criteria == MATCHCRITERIA_NEWSGROUPS)? ret : !ret;
+ }
case MATCHCRITERIA_INREPLYTO:
- return matcherprop_string_match(prop, info->inreplyto, _("InReplyTo: header"));
case MATCHCRITERIA_NOT_INREPLYTO:
- return !matcherprop_string_match(prop, info->inreplyto, _("InReplyTo: header"));
+ {
+ gchar *context;
+ gboolean ret;
+
+ context = g_strdup_printf(_("%s header"),
+ prefs_common_translated_header_name("In-Reply-To:"));
+ ret = matcherprop_string_match(prop, info->inreplyto, context);
+ g_free(context);
+ return (prop->criteria == MATCHCRITERIA_INREPLYTO)? ret : !ret;
+ }
case MATCHCRITERIA_REFERENCES:
- return matcherprop_list_match(prop, info->references, _("References: header"));
case MATCHCRITERIA_NOT_REFERENCES:
- return !matcherprop_list_match(prop, info->references, _("References: header"));
+ {
+ gchar *context;
+ gboolean ret;
+
+ context = g_strdup_printf(_("%s header"),
+ prefs_common_translated_header_name("References:"));
+ ret = matcherprop_list_match(prop, info->references, context);
+ g_free(context);
+ return (prop->criteria == MATCHCRITERIA_REFERENCES)? ret : !ret;
+ }
case MATCHCRITERIA_TEST:
return matcherprop_match_test(prop, info);
case MATCHCRITERIA_NOT_TEST:
case MATCHCRITERIA_NOT_COLORLABEL:
case MATCHCRITERIA_IGNORE_THREAD:
case MATCHCRITERIA_NOT_IGNORE_THREAD:
+ case MATCHCRITERIA_WATCH_THREAD:
+ case MATCHCRITERIA_NOT_WATCH_THREAD:
case MATCHCRITERIA_SUBJECT:
case MATCHCRITERIA_NOT_SUBJECT:
case MATCHCRITERIA_FROM:
case MATCHCRITERIA_NOT_CC:
case MATCHCRITERIA_TO_OR_CC:
case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
+ case MATCHCRITERIA_TAG:
+ case MATCHCRITERIA_NOT_TAG:
+ case MATCHCRITERIA_TAGGED:
+ case MATCHCRITERIA_NOT_TAGGED:
case MATCHCRITERIA_AGE_GREATER:
case MATCHCRITERIA_AGE_LOWER:
case MATCHCRITERIA_NEWSGROUPS:
case MATCHCRITERIA_NOT_PARTIAL:
case MATCHCRITERIA_IGNORE_THREAD:
case MATCHCRITERIA_NOT_IGNORE_THREAD:
+ case MATCHCRITERIA_WATCH_THREAD:
+ case MATCHCRITERIA_NOT_WATCH_THREAD:
+ case MATCHCRITERIA_TAGGED:
+ case MATCHCRITERIA_NOT_TAGGED:
return g_strdup(criteria_str);
case MATCHCRITERIA_TEST:
case MATCHCRITERIA_NOT_TEST:
*\param fp File
*\param prefs_filtering List of filtering conditions
*/
-static void prefs_filtering_write(FILE *fp, GSList *prefs_filtering)
+static int prefs_filtering_write(FILE *fp, GSList *prefs_filtering)
{
GSList *cur = NULL;
if (prop->enabled) {
if (fputs("enabled ", fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs");
- return;
+ return -1;
}
} else {
if (fputs("disabled ", fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs");
- return;
+ return -1;
}
}
if (fputs("rulename \"", fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs");
g_free(filtering_str);
- return;
+ return -1;
}
tmp_name = prop->name;
while (tmp_name && *tmp_name != '\0') {
if (fputc(*tmp_name, fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs || fputc");
g_free(filtering_str);
- return;
+ return -1;
}
} else if (*tmp_name == '"') {
if (fputc('\\', fp) == EOF ||
fputc('"', fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs || fputc");
g_free(filtering_str);
- return;
+ return -1;
}
}
tmp_name ++;
if (fputs("\" ", fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs");
g_free(filtering_str);
- return;
+ return -1;
}
if (prop->account_id != 0) {
if (fputs(tmp, fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs");
g_free(tmp);
- return;
+ return -1;
}
g_free(tmp);
}
fputc('\n', fp) == EOF) {
FILE_OP_ERROR("filtering config", "fputs || fputc");
g_free(filtering_str);
- return;
+ return -1;
}
g_free(filtering_str);
}
+
+ return 0;
}
+typedef struct _NodeLoopData {
+ FILE *fp;
+ gboolean error;
+} NodeLoopData;
+
/*!
*\brief Write matchers from a folder item
*
*
*\return gboolean FALSE
*/
-static gboolean prefs_matcher_write_func(GNode *node, gpointer data)
+static gboolean prefs_matcher_write_func(GNode *node, gpointer d)
{
FolderItem *item;
- FILE *fp = data;
+ NodeLoopData *data = (NodeLoopData *)d;
gchar *id;
GSList *prefs_filtering;
prefs_filtering = item->prefs->processing;
if (prefs_filtering != NULL) {
- fprintf(fp, "[%s]\n", id);
- prefs_filtering_write(fp, prefs_filtering);
- fputc('\n', fp);
+ if (fprintf(data->fp, "[%s]\n", id) < 0) {
+ data->error = TRUE;
+ goto fail;
+ }
+ if (prefs_filtering_write(data->fp, prefs_filtering) < 0) {
+ data->error = TRUE;
+ goto fail;
+ }
+ if (fputc('\n', data->fp) == EOF) {
+ data->error = TRUE;
+ goto fail;
+ }
}
-
+fail:
g_free(id);
return FALSE;
*
*\param fp File
*/
-static void prefs_matcher_save(FILE *fp)
+static int prefs_matcher_save(FILE *fp)
{
GList *cur;
+ NodeLoopData data;
+
+ data.fp = fp;
+ data.error = FALSE;
for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
Folder *folder;
folder = (Folder *) cur->data;
g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
- prefs_matcher_write_func, fp);
+ prefs_matcher_write_func, &data);
}
+ if (data.error == TRUE)
+ return -1;
+
/* pre global rules */
- fprintf(fp, "[preglobal]\n");
- prefs_filtering_write(fp, pre_global_processing);
- fputc('\n', fp);
+ if (fprintf(fp, "[preglobal]\n") < 0 ||
+ prefs_filtering_write(fp, pre_global_processing) < 0 ||
+ fputc('\n', fp) == EOF)
+ return -1;
/* post global rules */
- fprintf(fp, "[postglobal]\n");
- prefs_filtering_write(fp, post_global_processing);
- fputc('\n', fp);
+ if (fprintf(fp, "[postglobal]\n") < 0 ||
+ prefs_filtering_write(fp, post_global_processing) < 0 ||
+ fputc('\n', fp) == EOF)
+ return -1;
/* filtering rules */
- fprintf(fp, "[filtering]\n");
- prefs_filtering_write(fp, filtering_rules);
- fputc('\n', fp);
+ if (fprintf(fp, "[filtering]\n") < 0 ||
+ prefs_filtering_write(fp, filtering_rules) < 0 ||
+ fputc('\n', fp) == EOF)
+ return -1;
+
+ return 0;
}
/*!
return;
}
-
- prefs_matcher_save(pfile->fp);
-
g_free(rcpath);
- if (prefs_file_close(pfile) < 0) {
+ if (prefs_matcher_save(pfile->fp) < 0) {
g_warning("failed to write configuration to file\n");
- return;
+ prefs_file_close_revert(pfile);
+ } else if (prefs_file_close(pfile) < 0) {
+ g_warning("failed to save configuration to file\n");
}
}
FILE *src = g_fopen(rcpath, "rb");
FILE *dst = g_fopen(newpath, "wb");
gchar buf[BUFFSIZE];
-
+ int r;
if (dst == NULL) {
perror("fopen");
g_free(newpath);
&& strncmp(buf, "rulename \"", 10)
&& strncmp(buf, "enabled rulename \"", 18)
&& strncmp(buf, "disabled rulename \"", 18)) {
- fwrite("enabled rulename \"\" ",
+ r = fwrite("enabled rulename \"\" ",
strlen("enabled rulename \"\" "), 1, dst);
}
- fwrite(buf, strlen(buf), 1, dst);
+ r = fwrite(buf, strlen(buf), 1, dst);
}
fclose(dst);
fclose(src);
else {
/* previous version compatibility */
- /* printf("reading filtering\n"); */
+ /* g_print("reading filtering\n"); */
rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
FILTERING_RC, NULL);
f = g_fopen(rcpath, "rb");
fclose(matcher_parserin);
}
- /* printf("reading scoring\n"); */
+ /* g_print("reading scoring\n"); */
rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
SCORING_RC, NULL);
f = g_fopen(rcpath, "rb");