From 8e1251fa17b522d0539a8fcfb7463ba8cef1b31a Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 12 Nov 2003 21:13:05 +0000 Subject: merged spam filtering branch svn path=/trunk/; revision=23302 --- mail/Makefile.am | 4 + mail/em-folder-tree.c | 6 +- mail/em-folder-view.c | 20 +++ mail/em-junk-filter.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++ mail/em-junk-filter.h | 30 +++++ mail/em-junk-plugin.c | 24 ++++ mail/em-junk-plugin.h | 47 +++++++ mail/em-popup.c | 5 + mail/em-popup.h | 4 +- mail/mail-component.c | 11 ++ mail/mail-folder-cache.c | 14 +- mail/mail-ops.c | 151 +++++++++++++++++++--- mail/mail-ops.h | 4 + mail/mail-session.c | 7 +- mail/mail-tools.c | 13 +- mail/mail-vfolder.c | 6 +- mail/message-list.c | 112 ++++++++++------ mail/message-list.h | 3 + 18 files changed, 717 insertions(+), 71 deletions(-) create mode 100644 mail/em-junk-filter.c create mode 100644 mail/em-junk-filter.h create mode 100644 mail/em-junk-plugin.c create mode 100644 mail/em-junk-plugin.h (limited to 'mail') diff --git a/mail/Makefile.am b/mail/Makefile.am index 0d9a101462..417ee0be24 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -109,6 +109,10 @@ libevolution_mail_la_SOURCES = \ em-sync-stream.h \ em-icon-stream.c \ em-icon-stream.h \ + em-junk-filter.c \ + em-junk-filter.h \ + em-junk-plugin.c \ + em-junk-plugin.h \ em-html-stream.c \ em-html-stream.h \ folder-browser-factory.c \ diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c index 7dcdc1d9e8..e6274abe37 100644 --- a/mail/em-folder-tree.c +++ b/mail/em-folder-tree.c @@ -144,7 +144,8 @@ enum { FOLDER_ICON_NORMAL, FOLDER_ICON_INBOX, FOLDER_ICON_OUTBOX, - FOLDER_ICON_TRASH + FOLDER_ICON_TRASH, + FOLDER_ICON_JUNK }; static GdkPixbuf *folder_icons[4]; @@ -163,6 +164,7 @@ render_pixbuf (GtkTreeViewColumn *column, GtkCellRenderer *renderer, folder_icons[1] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/inbox-mini.png"); folder_icons[2] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/outbox-mini.png"); folder_icons[3] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/evolution-trash-mini.png"); + folder_icons[4] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/evolution-junk-mini.png"); initialised = TRUE; } @@ -176,6 +178,8 @@ render_pixbuf (GtkTreeViewColumn *column, GtkCellRenderer *renderer, pixbuf = folder_icons[FOLDER_ICON_OUTBOX]; else if (!strcasecmp (name, "/Trash")) pixbuf = folder_icons[FOLDER_ICON_TRASH]; + else if (!strcasecmp (name, "/Junk")) + pixbuf = folder_icons[FOLDER_ICON_JUNK]; else pixbuf = folder_icons[FOLDER_ICON_NORMAL]; } diff --git a/mail/em-folder-view.c b/mail/em-folder-view.c index 0c52a49fe1..45fe46dbbb 100644 --- a/mail/em-folder-view.c +++ b/mail/em-folder-view.c @@ -523,6 +523,18 @@ emfv_popup_mark_unimportant(GtkWidget *w, EMFolderView *emfv) em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_FLAGGED, 0); } +static void +emfv_popup_mark_junk (GtkWidget *w, EMFolderView *emfv) +{ + mail_mark_junk (emfv->folder, emfv->list, TRUE); +} + +static void +emfv_popup_mark_nojunk (GtkWidget *w, EMFolderView *emfv) +{ + mail_mark_junk (emfv->folder, emfv->list, FALSE); +} + static void emfv_popup_delete(GtkWidget *w, EMFolderView *emfv) { @@ -676,6 +688,8 @@ static EMPopupItem emfv_popup_menu[] = { { EM_POPUP_ITEM, "30.emfv.01", N_("Mark as _Unread"), G_CALLBACK(emfv_popup_mark_unread), NULL, "mail-new.xpm", EM_POPUP_SELECT_MARK_UNREAD }, { EM_POPUP_ITEM, "30.emfv.02", N_("Mark as _Important"), G_CALLBACK(emfv_popup_mark_important), NULL, "priority-high.xpm", EM_POPUP_SELECT_MARK_IMPORTANT }, { EM_POPUP_ITEM, "30.emfv.03", N_("_Mark as Unimportant"), G_CALLBACK(emfv_popup_mark_unimportant), NULL, NULL, EM_POPUP_SELECT_MARK_UNIMPORTANT }, + { EM_POPUP_ITEM, "30.emfv.04", N_("Mark as _Junk"), G_CALLBACK(emfv_popup_mark_junk), NULL, NULL, EM_POPUP_SELECT_MARK_JUNK }, + { EM_POPUP_ITEM, "30.emfv.05", N_("Mark as _Not Junk"), G_CALLBACK(emfv_popup_mark_nojunk), NULL, NULL, EM_POPUP_SELECT_MARK_NOJUNK }, { EM_POPUP_BAR, "40.emfv" }, { EM_POPUP_ITEM, "40.emfv.00", N_("_Delete"), G_CALLBACK(emfv_popup_delete), NULL, "evolution-trash-mini.png", EM_POPUP_SELECT_DELETE }, @@ -821,6 +835,8 @@ EMFV_MAP_CALLBACK(emfv_message_mark_read, emfv_popup_mark_read) EMFV_MAP_CALLBACK(emfv_message_mark_unread, emfv_popup_mark_unread) EMFV_MAP_CALLBACK(emfv_message_mark_important, emfv_popup_mark_important) EMFV_MAP_CALLBACK(emfv_message_mark_unimportant, emfv_popup_mark_unimportant) +EMFV_MAP_CALLBACK(emfv_message_mark_junk, emfv_popup_mark_junk) +EMFV_MAP_CALLBACK(emfv_message_mark_nojunk, emfv_popup_mark_nojunk) EMFV_MAP_CALLBACK(emfv_message_delete, emfv_popup_delete) EMFV_MAP_CALLBACK(emfv_message_undelete, emfv_popup_undelete) EMFV_MAP_CALLBACK(emfv_message_followup_flag, emfv_popup_flag_followup) @@ -1246,6 +1262,8 @@ static BonoboUIVerb emfv_message_verbs[] = { BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnRead", emfv_message_mark_unread), BONOBO_UI_UNSAFE_VERB ("MessageMarkAsImportant", emfv_message_mark_important), BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnimportant", emfv_message_mark_unimportant), + BONOBO_UI_UNSAFE_VERB ("MessageMarkAsJunk", emfv_message_mark_junk), + BONOBO_UI_UNSAFE_VERB ("MessageMarkAsNojunk", emfv_message_mark_nojunk), BONOBO_UI_UNSAFE_VERB ("MessageFollowUpFlag", emfv_message_followup_flag), BONOBO_UI_UNSAFE_VERB ("MessageMove", emfv_message_move), BONOBO_UI_UNSAFE_VERB ("MessageOpen", emfv_message_open), @@ -1348,6 +1366,8 @@ static const EMFolderViewEnable emfv_enable_map[] = { { "MessageMarkAsUnRead", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNREAD }, { "MessageMarkAsImportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_IMPORTANT }, { "MessageMarkAsUnimportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNIMPORTANT }, + { "MessageMarkAsJunk", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_JUNK }, + { "MessageMarkAsNojunk", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_NOJUNK }, { "MessageFollowUpFlag", EM_POPUP_SELECT_MANY }, { "MessageMove", EM_POPUP_SELECT_MANY }, { "MessageOpen", EM_POPUP_SELECT_MANY }, diff --git a/mail/em-junk-filter.c b/mail/em-junk-filter.c new file mode 100644 index 0000000000..04bbadc2ba --- /dev/null +++ b/mail/em-junk-filter.c @@ -0,0 +1,327 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "em-junk-filter.h" + +#define LOCK(x) pthread_mutex_lock(&x) +#define UNLOCK(x) pthread_mutex_unlock(&x) + +static pthread_mutex_t em_junk_sa_test_lock = PTHREAD_MUTEX_INITIALIZER; + +static const char * em_junk_sa_get_name (void); +static gboolean em_junk_sa_check_junk (CamelMimeMessage *msg); +static void em_junk_sa_report_junk (CamelMimeMessage *msg); +static void em_junk_sa_report_notjunk (CamelMimeMessage *msg); +static void em_junk_sa_commit_reports (void); + +static EMJunkPlugin spam_assassin_plugin = +{ + { + em_junk_sa_get_name, + 1, + em_junk_sa_check_junk, + em_junk_sa_report_junk, + em_junk_sa_report_notjunk, + em_junk_sa_commit_reports, + }, + NULL, + NULL +}; + +static gboolean em_junk_sa_spamd_tested = FALSE; +static gboolean em_junk_sa_use_spamc = FALSE; +static gint em_junk_sa_spamd_port = -1; + +#define d(x) x + +static const char * +em_junk_sa_get_name (void) +{ + return _("Spamassassin (built-in)"); +} + +static int +pipe_to_sa (CamelMimeMessage *msg, gchar *in, int argc, gchar **argv) +{ + CamelStream *stream; + int result, status; + int in_fds[2]; + pid_t pid; + + if (argc < 1 || argv[0] == '\0') + return 0; + + if (pipe (in_fds) == -1) { + /* camel_exception_setv (fms->ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to create pipe to '%s': %s"), + argv[0]->value.string, g_strerror (errno)); */ + return -1; + } + + if (!(pid = fork ())) { + /* child process */ + int maxfd, fd; + + fd = open ("/dev/null", O_WRONLY); + + if (dup2 (in_fds[0], STDIN_FILENO) < 0 || + dup2 (fd, STDOUT_FILENO) < 0 || + dup2 (fd, STDERR_FILENO) < 0) + _exit (255); + + setsid (); + + maxfd = sysconf (_SC_OPEN_MAX); + if (maxfd > 0) { + for (fd = 0; fd < maxfd; fd++) { + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + close (fd); + } + } + + execvp (argv [0], argv); + + d(printf ("Could not execute %s: %s\n", argv [0], g_strerror (errno))); + _exit (255); + } else if (pid < 0) { + /* camel_exception_setv (fms->ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to create create child process '%s': %s"), + argv[0]->value.string, g_strerror (errno)); */ + return -1; + } + + /* parent process */ + close (in_fds[0]); + fcntl (in_fds[1], F_SETFL, O_NONBLOCK); + + if (msg) { + stream = camel_stream_fs_new_with_fd (in_fds[1]); + + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (msg), stream); + camel_stream_flush (stream); + camel_object_unref (CAMEL_OBJECT (stream)); + } else if (in) { + write (in_fds [1], in, strlen (in)); + close (in_fds [1]); + } + + result = waitpid (pid, &status, 0); + + if (result == -1 && errno == EINTR) { + /* child process is hanging... */ + kill (pid, SIGTERM); + sleep (1); + result = waitpid (pid, &status, WNOHANG); + if (result == 0) { + /* ...still hanging, set phasers to KILL */ + kill (pid, SIGKILL); + sleep (1); + result = waitpid (pid, &status, WNOHANG); + } + } + + if (result != -1 && WIFEXITED (status)) + return WEXITSTATUS (status); + else + return -1; +} + + +#define NPORTS 1 + +static int +em_junk_sa_test_spamd_running (gint port) +{ + static gchar *sac_args [3] = { + "/bin/sh", + "-c", + NULL + }; + int retval; + + d(fprintf (stderr, "test if spamd is running (port %d)\n", port);) + sac_args [2] = port > 0 ? g_strdup_printf ("spamc -x -p %d", port) : g_strdup_printf ("spamc -x"); + + retval = pipe_to_sa (NULL, "From test@127.0.0.1", 3, sac_args) == 0; + g_free (sac_args [2]); + + return retval; +} + +static void +em_junk_sa_test_spamd () +{ + gint i, port = 7830; + + em_junk_sa_use_spamc = FALSE; + + /* if (em_junk_sa_test_spamd_running (-1)) { + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = -1; + } else { */ + for (i = 0; i < NPORTS; i ++) { + if (em_junk_sa_test_spamd_running (port)) { + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = port; + break; + } + port ++; + } + /* } */ + + if (!em_junk_sa_use_spamc) { + static gchar *sad_args [3] = { + "/bin/sh", + "-c", + NULL + }; + gint i, port = 7830; + + d(fprintf (stderr, "looks like spamd is not running\n");) + + for (i = 0; i < NPORTS; i ++) { + d(fprintf (stderr, "trying to run spamd at port %d\n", port)); + + sad_args [2] = g_strdup_printf ("spamd --port %d --local --daemonize", port); + if (!pipe_to_sa (NULL, NULL, 3, sad_args)) { + g_free (sad_args [2]); + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = port; + d(fprintf (stderr, "success at port %d\n", port)); + break; + } + g_free (sad_args [2]); + port ++; + } + } + + d(fprintf (stderr, "use spamd %d at port %d\n", em_junk_sa_use_spamc, em_junk_sa_spamd_port);) + + em_junk_sa_spamd_tested = TRUE; +} + +static gboolean +em_junk_sa_check_junk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + NULL + }; + gint retval; + + d(fprintf (stderr, "em_junk_sa_check_junk\n")); + + LOCK (em_junk_sa_test_lock); + if (!em_junk_sa_spamd_tested) + em_junk_sa_test_spamd (); + UNLOCK (em_junk_sa_test_lock); + + args [2] = em_junk_sa_use_spamc + ? (em_junk_sa_spamd_port == -1 + ? g_strdup ("spamc -c") /* Exit with a non-zero exit code if the + tested message was junk */ + : g_strdup_printf ("spamc" + " -c" /* Exit with a non-zero exit code if the + tested message was junk */ + " -p %d", em_junk_sa_spamd_port)) + : g_strdup ("spamassassin" + " --exit-code" /* Exit with a non-zero exit code if the + tested message was junk */ + " --local"); /* Local tests only (no online tests) */ + + retval = pipe_to_sa (msg, NULL, 3, args); + + g_free (args [2]); + + return retval; +} + +static void +em_junk_sa_report_junk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --no-rebuild" /* do not rebuild db */ + " --spam" /* report junk */ + " --single" /* single message */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_report_junk\n");) + + pipe_to_sa (msg, NULL, 3, args) > 0; +} + +static void +em_junk_sa_report_notjunk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --no-rebuild" /* do not rebuild db */ + " --ham" /* report notjunk */ + " --single" /* single message */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_report_notjunk\n");) + + pipe_to_sa (msg, NULL, 3, args) > 0; +} + +static void +em_junk_sa_commit_reports (void) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --rebuild" /* do not rebuild db */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_commit_reports\n");) + + pipe_to_sa (NULL, NULL, 3, args) > 0; +} + +const EMJunkPlugin * +em_junk_filter_get_plugin (void) +{ + return &spam_assassin_plugin; +} diff --git a/mail/em-junk-filter.h b/mail/em-junk-filter.h new file mode 100644 index 0000000000..f62ca7bfad --- /dev/null +++ b/mail/em-junk-filter.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _EM_JUNK_FILTER_H +#define _EM_JUNK_FILTER_H + +#include "em-junk-plugin.h" + +const EMJunkPlugin * em_junk_filter_get_plugin (void); + +#endif diff --git a/mail/em-junk-plugin.c b/mail/em-junk-plugin.c new file mode 100644 index 0000000000..5ea2a40119 --- /dev/null +++ b/mail/em-junk-plugin.c @@ -0,0 +1,24 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "em-junk-plugin.h" + diff --git a/mail/em-junk-plugin.h b/mail/em-junk-plugin.h new file mode 100644 index 0000000000..0c7eacd165 --- /dev/null +++ b/mail/em-junk-plugin.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _EM_JUNK_PLUGIN_H +#define _EM_JUNK_PLUGIN_H + +#include +#include + +#define EM_JUNK_PLUGIN(x) ((EMJunkPlugin *) x) + +typedef struct _EMJunkPlugin EMJunkPlugin; + +struct _EMJunkPlugin +{ + CamelJunkPlugin csp; + + /* when called, it should insert own GUI configuration into supplied. + container. returns data pointer which is later passed to apply, + plugin has to call (*changed_cb) (); whenever configuration + is changed to notify settings dialog about a change. + if setup_config_ui is NULL, it means there are no options */ + + gpointer (*setup_config_ui) (GtkWidget *container, void (*changed_cb) ()); + void (*apply) (gpointer data); +}; + +#endif diff --git a/mail/em-popup.c b/mail/em-popup.c index 63159848bf..760aed48d5 100644 --- a/mail/em-popup.c +++ b/mail/em-popup.c @@ -459,6 +459,11 @@ em_popup_target_new_select(struct _CamelFolder *folder, const char *folder_uri, mask &= ~EM_POPUP_SELECT_MARK_UNIMPORTANT; else mask &= ~EM_POPUP_SELECT_MARK_IMPORTANT; + + if (info->flags & CAMEL_MESSAGE_JUNK) + mask &= ~EM_POPUP_SELECT_MARK_NOJUNK; + else + mask &= ~EM_POPUP_SELECT_MARK_JUNK; tmp = camel_tag_get (&info->user_tags, "follow-up"); if (tmp && *tmp) { diff --git a/mail/em-popup.h b/mail/em-popup.h index 0ad1d0015c..831d586efe 100644 --- a/mail/em-popup.h +++ b/mail/em-popup.h @@ -88,7 +88,9 @@ enum { EM_POPUP_SELECT_FLAG_COMPLETED = 1<<12, EM_POPUP_SELECT_FLAG_CLEAR = 1<<13, EM_POPUP_SELECT_ADD_SENDER = 1<<14, - EM_POPUP_SELECT_LAST = 1<<16 /* reserve 2 slots */ + EM_POPUP_SELECT_MARK_JUNK = 1<<15, + EM_POPUP_SELECT_MARK_NOJUNK = 1<<16, + EM_POPUP_SELECT_LAST = 1<<18 /* reserve 2 slots */ }; /* Flags that describe a TARGET_URI */ diff --git a/mail/mail-component.c b/mail/mail-component.c index f22cb656b3..ea20dae624 100644 --- a/mail/mail-component.c +++ b/mail/mail-component.c @@ -269,6 +269,12 @@ type_is_vtrash (const char *type) return !strcmp (type, "vtrash"); } +static inline gboolean +type_is_vjunk (const char *type) +{ + return !strcmp (type, "vjunk"); +} + static void storage_go_online (gpointer key, gpointer value, gpointer data) { @@ -410,6 +416,11 @@ create_view_callback (EStorageBrowser *browser, control = folder_browser_factory_new_control ("vtrash:file:/"); else control = folder_browser_factory_new_control (physical_uri); + } else if (type_is_vjunk (folder_type)) { + if (!strncasecmp (physical_uri, "file:", 5)) + control = folder_browser_factory_new_control ("vjunk:file:/"); + else + control = folder_browser_factory_new_control (physical_uri); } else return NULL; diff --git a/mail/mail-folder-cache.c b/mail/mail-folder-cache.c index 3d4eb96d53..a75ed046e9 100644 --- a/mail/mail-folder-cache.c +++ b/mail/mail-folder-cache.c @@ -240,9 +240,17 @@ real_flush_updates(void *o, void *event_data, void *data) g_warning ("No folder at %s ?!", up->path); } } else if (storage != NULL) { - char *type = (strncmp(up->uri, "vtrash:", 7)==0)?"vtrash":"mail"; - EFolder *new_folder = e_folder_new (up->name, type, NULL); + char *type; + EFolder *new_folder; + if (strncmp(up->uri, "vtrash:", 7)==0) { + type = "vtrash"; + } else if (strncmp(up->uri, "vjunk:", 6)==0) { + type = "vjunk"; + } else + type = "mail"; + + new_folder = e_folder_new (up->name, type, NULL); d(printf("Adding new folder: %s\n", up->path)); e_folder_set_physical_uri (new_folder, up->uri); @@ -461,7 +469,7 @@ folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) if (mfi->folder != folder) return; - if (!CAMEL_IS_VTRASH_FOLDER (folder) && folder != outbox_folder && folder != sent_folder && changes && changes->uid_added) + if (!CAMEL_IS_VTRASH_FOLDER (folder) && !CAMEL_IS_VJUNK_FOLDER (folder) && folder != outbox_folder && folder != sent_folder && changes && changes->uid_added) new = changes->uid_added->len; LOCK(info_lock); diff --git a/mail/mail-ops.c b/mail/mail-ops.c index 8d3711ff4e..b7aad2e55b 100644 --- a/mail/mail-ops.c +++ b/mail/mail-ops.c @@ -1063,9 +1063,9 @@ get_folderinfo_desc (struct _mail_msg *mm, int done) } static void -add_vtrash_info (CamelStore *store, CamelFolderInfo *info) +add_vtrash_or_vjunk_info (CamelStore *store, CamelFolderInfo *info, gchar *name, gchar *full_name, gchar *url_base, gboolean unread_count) { - CamelFolderInfo *fi, *vtrash, *parent; + CamelFolderInfo *fi, *vinfo, *parent; char *uri, *path; CamelURL *url; @@ -1073,14 +1073,14 @@ add_vtrash_info (CamelStore *store, CamelFolderInfo *info) parent = NULL; for (fi = info; fi; fi = fi->sibling) { - if (!strcmp (fi->name, CAMEL_VTRASH_NAME)) + if (!strcmp (fi->name, name)) break; parent = fi; } - /* create our vTrash URL */ + /* create our vTrash/vJunk URL */ url = camel_url_new (info->url, NULL); - path = g_strdup_printf ("/%s", CAMEL_VTRASH_NAME); + path = g_strdup_printf ("/%s", name); if (url->fragment) camel_url_set_fragment (url, path); else @@ -1090,31 +1090,44 @@ add_vtrash_info (CamelStore *store, CamelFolderInfo *info) camel_url_free (url); if (fi) { - /* We're going to replace the physical Trash folder with our vTrash folder */ - vtrash = fi; - g_free (vtrash->full_name); - g_free (vtrash->name); - g_free (vtrash->url); + /* We're going to replace the physical Trash/Junk folder with our vTrash/vJunk folder */ + vinfo = fi; + g_free (vinfo->full_name); + g_free (vinfo->name); + g_free (vinfo->url); } else { - /* There wasn't a Trash folder so create a new folder entry */ - vtrash = g_new0 (CamelFolderInfo, 1); + /* There wasn't a Trash/Junk folder so create a new folder entry */ + vinfo = g_new0 (CamelFolderInfo, 1); g_assert(parent != NULL); /* link it into the right spot */ - vtrash->sibling = parent->sibling; - parent->sibling = vtrash; + vinfo->sibling = parent->sibling; + parent->sibling = vinfo; } /* Fill in the new fields */ - vtrash->full_name = g_strdup (_("Trash")); - vtrash->name = g_strdup(vtrash->full_name); - vtrash->url = g_strdup_printf ("vtrash:%s", uri); - vtrash->unread_message_count = -1; - vtrash->path = g_strdup_printf("/%s", vtrash->name); + vinfo->full_name = g_strdup (full_name); + vinfo->name = g_strdup(vinfo->full_name); + vinfo->url = g_strdup_printf ("%s:%s", url_base, uri); + if (!unread_count) + vinfo->unread_message_count = -1; + vinfo->path = g_strdup_printf("/%s", vinfo->name); g_free (uri); } +static void +add_vtrash_info (CamelStore *store, CamelFolderInfo *info) +{ + add_vtrash_or_vjunk_info (store, info, CAMEL_VTRASH_NAME, _("Trash"), "vtrash", FALSE); +} + +static void +add_vjunk_info (CamelStore *store, CamelFolderInfo *info) +{ + add_vtrash_or_vjunk_info (store, info, CAMEL_VJUNK_NAME, _("Junk"), "vjunk", TRUE); +} + static void add_unmatched_info(CamelFolderInfo *fi) { @@ -1142,6 +1155,8 @@ get_folderinfo_get (struct _mail_msg *mm) if (m->info) { if (m->info->url && (m->store->flags & CAMEL_STORE_VTRASH)) add_vtrash_info(m->store, m->info); + if (m->info->url && (m->store->flags & CAMEL_STORE_VJUNK)) + add_vjunk_info(m->store, m->info); if (CAMEL_IS_VEE_STORE(m->store)) add_unmatched_info(m->info); } @@ -2329,3 +2344,101 @@ mail_execute_shell_command (CamelFilterDriver *driver, int argc, char **argv, vo gnome_execute_async_fds (NULL, argc, argv, TRUE); } + +/* [Un]mark junk flag */ + +struct _mark_junk_mail_msg { + struct _mail_msg msg; + + CamelFolder *folder; + MessageList *list; + gboolean junk; +}; + +static char * +mark_junk_describe (struct _mail_msg *mm, int complete) +{ + return g_strdup (_("Changing junk status")); +} + +/* filter a folder, or a subset thereof, uses source_folder/source_uids */ +/* this is shared with fetch_mail */ +static void +mark_junk_mark (struct _mail_msg *mm) +{ + struct _mark_junk_mail_msg *m = (struct _mark_junk_mail_msg *) mm; + CamelJunkPlugin *csp = NULL; + GPtrArray *uids; + gboolean commit_reports = FALSE; + int i; + + if (m->folder == NULL) + return; + + uids = message_list_get_selected (m->list); + camel_folder_freeze (m->folder); + + for (i=0; ilen; i++) { + guint32 flags; + + flags = camel_folder_get_message_flags (m->folder, uids->pdata[i]); + if (((flags & CAMEL_MESSAGE_JUNK) == CAMEL_MESSAGE_JUNK) != m->junk) { + CamelMimeMessage *msg = camel_folder_get_message (m->folder, uids->pdata[i], NULL); + + if (msg) { + csp = CAMEL_SERVICE (m->folder->parent_store)->session->junk_plugin; + if (m->junk) + camel_junk_plugin_report_junk (csp, msg); + else + camel_junk_plugin_report_notjunk (csp, msg); + + commit_reports = TRUE; + camel_object_unref (msg); + } + } + camel_folder_set_message_flags(m->folder, uids->pdata[i], + CAMEL_MESSAGE_JUNK | (m->junk ? CAMEL_MESSAGE_DELETED : 0), + m->junk ? CAMEL_MESSAGE_JUNK : 0); + } + + if (commit_reports) + camel_junk_plugin_commit_reports (csp); + + message_list_free_uids(m->list, uids); + camel_folder_thaw(m->folder); +} + +static void +mark_junk_marked (struct _mail_msg *mm) +{ +} + +static void +mark_junk_free (struct _mail_msg *mm) +{ + struct _mark_junk_mail_msg *m = (struct _mark_junk_mail_msg *)mm; + + if (m->folder) + camel_object_unref (m->folder); +} + +static struct _mail_msg_op mark_junk_op = { + mark_junk_describe, + mark_junk_mark, + mark_junk_marked, + mark_junk_free, +}; + +void +mail_mark_junk (CamelFolder *folder, MessageList *list, gboolean junk) +{ + struct _mark_junk_mail_msg *m; + + m = mail_msg_new (&mark_junk_op, NULL, sizeof (*m)); + m->folder = folder; + camel_object_ref (folder); + m->list = list; + m->junk = junk; + + e_thread_put (mail_thread_new, (EMsg *) m); +} diff --git a/mail/mail-ops.h b/mail/mail-ops.h index e15d7729c2..081e1a1425 100644 --- a/mail/mail-ops.h +++ b/mail/mail-ops.h @@ -36,6 +36,8 @@ extern "C" { #include "camel/camel-mime-message.h" #include "camel/camel-operation.h" +#include "message-list.h" + #include "evolution-storage.h" /*EvolutionStorage */ #include "e-util/e-msgport.h" #include "e-util/e-account.h" @@ -164,6 +166,8 @@ int mail_store_set_offline(CamelStore *store, gboolean offline, /* filter driver execute shell command async callback */ void mail_execute_shell_command (CamelFilterDriver *driver, int argc, char **argv, void *data); +void mail_mark_junk (CamelFolder *folder, MessageList *list, gboolean junk); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/mail/mail-session.c b/mail/mail-session.c index 58773513c4..0db6f38ba8 100644 --- a/mail/mail-session.c +++ b/mail/mail-session.c @@ -48,6 +48,7 @@ #include "mail-ops.h" #include "e-util/e-passwords.h" #include "e-util/e-msgport.h" +#include "em-junk-filter.h" #define d(x) @@ -626,6 +627,9 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException fsearch = g_string_new (""); faction = g_string_new (""); + + /* implicit junk check as 1st rule */ + camel_filter_driver_add_rule (driver, "Junk check", "(junk-test)", "(begin (set-system-flag \"junk\"))"); /* add the user-defined rules next */ while ((rule = rule_context_next_rule (fc, rule, type))) { @@ -634,7 +638,6 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException filter_rule_build_code (rule, fsearch); filter_filter_build_action ((FilterFilter *) rule, faction); - camel_filter_driver_add_rule (driver, rule->name, fsearch->str, faction->str); } @@ -755,6 +758,8 @@ mail_session_init (const char *base_directory) camel_dir = g_strdup_printf ("%s/mail", base_directory); camel_session_construct (session, camel_dir); + + session->junk_plugin = CAMEL_JUNK_PLUGIN (em_junk_filter_get_plugin ()); /* The shell will tell us to go online. */ camel_session_set_online ((CamelSession *) session, FALSE); diff --git a/mail/mail-tools.c b/mail/mail-tools.c index b85a18c12f..0538efd354 100644 --- a/mail/mail-tools.c +++ b/mail/mail-tools.c @@ -312,6 +312,8 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex) /* This hack is still needed for file:/ since it's its own EvolutionStorage type */ if (!strncmp (uri, "vtrash:", 7)) offset = 7; + else if (!strncmp (uri, "vjunk:", 6)) + offset = 6; url = camel_url_new (uri + offset, ex); if (!url) { @@ -333,9 +335,14 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex) name = ""; } - if (offset) - folder = camel_store_get_trash (store, ex); - else + if (offset) { + if (offset == 7) + folder = camel_store_get_trash (store, ex); + else if (offset == 6) + folder = camel_store_get_junk (store, ex); + else + g_assert (FALSE); + } else folder = camel_store_get_folder (store, name, flags, ex); camel_object_unref (store); } diff --git a/mail/mail-vfolder.c b/mail/mail-vfolder.c index f47fe4c660..2bb08a587e 100644 --- a/mail/mail-vfolder.c +++ b/mail/mail-vfolder.c @@ -345,7 +345,7 @@ mail_vfolder_add_uri(CamelStore *store, const char *uri, int remove) GCompareFunc uri_cmp = CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name; int is_ignore; - if (CAMEL_IS_VEE_STORE(store) || !strncmp(uri, "vtrash:", 7) || context == NULL) + if (CAMEL_IS_VEE_STORE(store) || !strncmp(uri, "vtrash:", 7) || !strncmp(uri, "vjunk:", 6) || context == NULL) return; g_assert(pthread_self() == mail_gui_thread); @@ -426,7 +426,7 @@ mail_vfolder_delete_uri(CamelStore *store, const char *uri) CamelVeeFolder *vf; GString *changed; - if (context == NULL || !strncmp(uri, "vtrash:", 7)) + if (context == NULL || !strncmp(uri, "vtrash:", 7) || !strncmp(uri, "vjunk:", 6)) return; d(printf ("Deleting uri to check: %s\n", uri)); @@ -492,7 +492,7 @@ mail_vfolder_rename_uri(CamelStore *store, const char *from, const char *to) d(printf("vfolder rename uri: %s to %s\n", from, to)); - if (context == NULL || !strncmp(from, "vtrash:", 7) || !strncmp(to, "vtrash:", 7)) + if (context == NULL || !strncmp(from, "vtrash:", 7) || !strncmp(to, "vtrash:", 7) || !strncmp(from, "vjunk:", 6) || !strncmp(to, "vjunk:", 6)) return; g_assert(pthread_self() == mail_gui_thread); diff --git a/mail/message-list.c b/mail/message-list.c index a16c40f265..430d2f4896 100644 --- a/mail/message-list.c +++ b/mail/message-list.c @@ -188,6 +188,9 @@ static struct { { NULL, NULL } }; +/* FIXME: junk prefs */ +static gboolean junk_folder = TRUE; + #ifdef SMART_ADDRESS_COMPARE static EMailAddress * e_mail_address_new (const char *address) @@ -2376,12 +2379,45 @@ build_flat_diff(MessageList *ml, CamelFolderChangeInfo *changes) #endif /* ! BROKEN_ETREE */ +static void +mail_folder_hide_by_flag (CamelFolder *folder, MessageList *ml, CamelFolderChangeInfo **changes, int flag) +{ + CamelFolderChangeInfo *newchanges, *oldchanges = *changes; + CamelMessageInfo *info; + int i; + + newchanges = camel_folder_change_info_new (); + + for (i = 0; i < oldchanges->uid_changed->len; i++) { + ETreePath node = g_hash_table_lookup (ml->uid_nodemap, oldchanges->uid_changed->pdata[i]); + + info = camel_folder_get_message_info (folder, oldchanges->uid_changed->pdata[i]); + if (node != NULL && info != NULL && (info->flags & flag) != 0) + camel_folder_change_info_remove_uid (newchanges, oldchanges->uid_changed->pdata[i]); + else if (node == NULL && info != NULL && (info->flags & flag) == 0) + camel_folder_change_info_add_uid (newchanges, oldchanges->uid_changed->pdata[i]); + else + camel_folder_change_info_change_uid (newchanges, oldchanges->uid_changed->pdata[i]); + camel_folder_free_message_info (folder, info); + } + + if (newchanges->uid_added->len > 0 || newchanges->uid_removed->len > 0) { + for (i = 0; i < oldchanges->uid_added->len; i++) + camel_folder_change_info_add_uid (newchanges, oldchanges->uid_added->pdata[i]); + for (i = 0; i < oldchanges->uid_removed->len; i++) + camel_folder_change_info_remove_uid (newchanges, oldchanges->uid_removed->pdata[i]); + camel_folder_change_info_free (oldchanges); + *changes = newchanges; + } else { + camel_folder_change_info_free (newchanges); + } +} + static void main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) { MessageList *ml = MESSAGE_LIST (user_data); - CamelFolderChangeInfo *changes = (CamelFolderChangeInfo *)event_data, *newchanges; - CamelMessageInfo *info; + CamelFolderChangeInfo *changes = (CamelFolderChangeInfo *)event_data; CamelFolder *folder = (CamelFolder *)o; int i; @@ -2402,35 +2438,9 @@ main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) } /* check if the hidden state has changed, if so modify accordingly, then regenerate */ - if (ml->hidedeleted) { - newchanges = camel_folder_change_info_new (); - - for (i = 0; i < changes->uid_changed->len; i++) { - ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]); - - info = camel_folder_get_message_info (folder, changes->uid_changed->pdata[i]); - if (node != NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) != 0) { - camel_folder_change_info_remove_uid (newchanges, changes->uid_changed->pdata[i]); - } else if (node == NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) == 0) { - camel_folder_change_info_add_uid (newchanges, changes->uid_changed->pdata[i]); - } else { - camel_folder_change_info_change_uid (newchanges, changes->uid_changed->pdata[i]); - } - camel_folder_free_message_info (folder, info); - } - - if (newchanges->uid_added->len > 0 || newchanges->uid_removed->len > 0) { - for (i = 0; i < changes->uid_added->len; i++) - camel_folder_change_info_add_uid (newchanges, changes->uid_added->pdata[i]); - for (i = 0; i < changes->uid_removed->len; i++) - camel_folder_change_info_remove_uid (newchanges, changes->uid_removed->pdata[i]); - camel_folder_change_info_free (changes); - changes = newchanges; - } else { - camel_folder_change_info_free (newchanges); - } - } - + if (ml->hidejunk || ml->hidedeleted) + mail_folder_hide_by_flag (folder, ml, &changes, (ml->hidejunk ? CAMEL_MESSAGE_JUNK : 0) | (ml->hidedeleted ? CAMEL_MESSAGE_DELETED : 0)); + if (changes->uid_added->len == 0 && changes->uid_removed->len == 0 && changes->uid_changed->len < 100) { for (i = 0; i < changes->uid_changed->len; i++) { ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]); @@ -2579,6 +2589,7 @@ message_list_set_folder (MessageList *message_list, CamelFolder *folder, const c gconf = mail_config_get_gconf_client (); hide_deleted = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL); message_list->hidedeleted = hide_deleted && !(folder->folder_flags & CAMEL_FOLDER_IS_TRASH); + message_list->hidejunk = junk_folder && !(folder->folder_flags & CAMEL_FOLDER_IS_JUNK) && !(folder->folder_flags & CAMEL_FOLDER_IS_TRASH); hide_load_state (message_list); mail_regen_list (message_list, message_list->search, NULL, NULL); @@ -3038,6 +3049,7 @@ struct _regen_list_msg { CamelFolderChangeInfo *changes; gboolean dotree; /* we are building a tree */ gboolean hidedel; /* we want to/dont want to show deleted messages */ + gboolean hidejunk; /* we want to/dont want to show junk messages */ gboolean thread_subject; CamelFolderThread *tree; @@ -3076,17 +3088,36 @@ regen_list_regen (struct _mail_msg *mm) } else if (m->hidedel) { char *expr; - if (m->search) { - expr = alloca(strlen(m->search) + 64); - sprintf(expr, "(and (match-all (not (system-flag \"deleted\")))\n %s)", m->search); - } else - expr = "(match-all (not (system-flag \"deleted\")))"; + if (m->hidejunk) { + if (m->search) { + expr = alloca(strlen(m->search) + 92); + sprintf(expr, "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))\n %s)", m->search); + } else + expr = "(match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))"; + } else { + if (m->search) { + expr = alloca(strlen(m->search) + 64); + sprintf(expr, "(and (match-all (not (system-flag \"deleted\")))\n %s)", m->search); + } else + expr = "(match-all (not (system-flag \"deleted\")))"; + } searchuids = uids = camel_folder_search_by_expression (m->folder, expr, &mm->ex); } else { - if (m->search) - searchuids = uids = camel_folder_search_by_expression (m->folder, m->search, &mm->ex); - else - uids = camel_folder_get_uids (m->folder); + char *expr; + + if (m->hidejunk) { + if (m->search) { + expr = alloca(strlen(m->search) + 64); + sprintf(expr, "(and (match-all (not (system-flag \"junk\")))\n %s)", m->search); + } else + expr = "(match-all (not (system-flag \"junk\")))"; + searchuids = uids = camel_folder_search_by_expression (m->folder, expr, &mm->ex); + } else { + if (m->search) + searchuids = uids = camel_folder_search_by_expression (m->folder, m->search, &mm->ex); + else + uids = camel_folder_get_uids (m->folder); + } } if (camel_exception_is_set (&mm->ex)) @@ -3318,6 +3349,7 @@ mail_regen_list (MessageList *ml, const char *search, const char *hideexpr, Came m->changes = changes; m->dotree = ml->threaded; m->hidedel = ml->hidedeleted; + m->hidejunk = ml->hidejunk; m->thread_subject = gconf_client_get_bool (gconf, "/apps/evolution/mail/display/thread_subject", NULL); g_object_ref(ml); m->folder = ml->folder; diff --git a/mail/message-list.h b/mail/message-list.h index a5c88cf308..8052ed791b 100644 --- a/mail/message-list.h +++ b/mail/message-list.h @@ -113,6 +113,9 @@ struct _MessageList { /* do we automatically hide deleted messages? */ guint hidedeleted : 1; + + /* do we automatically hide junk messages? */ + guint hidejunk : 1; /* is the message-list object in a destroyed state? */ guint destroyed : 1; -- cgit v1.2.3