aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/Makefile.am2
-rw-r--r--modules/bogofilter/Makefile.am57
-rw-r--r--modules/bogofilter/evolution-bogofilter.c524
-rw-r--r--modules/bogofilter/evolution-bogofilter.schemas.in20
-rw-r--r--modules/mail/Makefile.am2
-rw-r--r--modules/mail/e-mail-junk-hook.c344
-rw-r--r--modules/mail/e-mail-junk-hook.h66
-rw-r--r--modules/mail/em-mailer-prefs.c164
-rw-r--r--modules/mail/em-mailer-prefs.h6
-rw-r--r--modules/mail/evolution-module-mail.c2
-rw-r--r--modules/spamassassin/Makefile.am56
-rw-r--r--modules/spamassassin/evolution-spamassassin.c1177
-rw-r--r--modules/spamassassin/evolution-spamassassin.schemas.in33
13 files changed, 1888 insertions, 565 deletions
diff --git a/modules/Makefile.am b/modules/Makefile.am
index 9a32c72ebc..dd363d27a3 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -24,6 +24,7 @@ endif
SUBDIRS = \
addressbook \
+ bogofilter \
calendar \
mail \
composer-autosave \
@@ -31,6 +32,7 @@ SUBDIRS = \
offline-alert \
plugin-lib \
plugin-manager \
+ spamassassin \
startup-wizard \
$(MONO_DIR) \
$(PYTHON_DIR) \
diff --git a/modules/bogofilter/Makefile.am b/modules/bogofilter/Makefile.am
new file mode 100644
index 0000000000..7803267836
--- /dev/null
+++ b/modules/bogofilter/Makefile.am
@@ -0,0 +1,57 @@
+module_LTLIBRARIES = libevolution-module-bogofilter.la
+
+libevolution_module_bogofilter_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"evolution-bogofilter\" \
+ -DWELCOME_MESSAGE=\""$(privdatadir)/default/C/mail/local/Inbox"\" \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(EVOLUTION_MAIL_CFLAGS)
+
+libevolution_module_bogofilter_la_SOURCES = \
+ evolution-bogofilter.c
+
+libevolution_module_bogofilter_la_LIBADD = \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/mail/libevolution-mail.la \
+ $(GNOME_PLATFORM_LIBS) \
+ $(EVOLUTION_MAIL_LIBS)
+
+libevolution_module_bogofilter_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED)
+
+schemadir = $(GCONF_SCHEMA_FILE_DIR)
+schema_in_files = evolution-bogofilter.schemas.in
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+@INTLTOOL_SCHEMAS_RULE@
+
+if GCONF_SCHEMAS_INSTALL
+
+if OS_WIN32
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schema_DATA) ; do \
+ (echo set GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE); \
+ echo $(GCONFTOOL) --makefile-install-rule $$p) >_temp.bat; \
+ cmd /c _temp.bat; \
+ rm _temp.bat; \
+ done \
+ fi
+else
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schema_DATA) ; do \
+ GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) \
+ $(GCONFTOOL) --makefile-install-rule $$p; \
+ done \
+ fi
+endif
+
+endif
+
+DISTCLEANFILES = $(schema_DATA)
+
+EXTRA_DIST = $(schema_in_files)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/bogofilter/evolution-bogofilter.c b/modules/bogofilter/evolution-bogofilter.c
new file mode 100644
index 0000000000..2cc7a64359
--- /dev/null
+++ b/modules/bogofilter/evolution-bogofilter.c
@@ -0,0 +1,524 @@
+/*
+ * evolution-bogofilter.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel.h>
+
+#include <e-util/gconf-bridge.h>
+#include <mail/e-mail-junk-filter.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BOGOFILTER \
+ (e_bogofilter_get_type ())
+#define E_BOGOFILTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_BOGOFILTER, EBogofilter))
+
+#ifndef BOGOFILTER_BINARY
+#define BOGOFILTER_BINARY "/usr/bin/bogofilter"
+#endif
+
+#define BOGOFILTER_EXIT_STATUS_SPAM 0
+#define BOGOFILTER_EXIT_STATUS_HAM 1
+#define BOGOFILTER_EXIT_STATUS_UNSURE 2
+#define BOGOFILTER_EXIT_STATUS_ERROR 3
+
+typedef struct _EBogofilter EBogofilter;
+typedef struct _EBogofilterClass EBogofilterClass;
+
+struct _EBogofilter {
+ EMailJunkFilter parent;
+ gboolean convert_to_unicode;
+};
+
+struct _EBogofilterClass {
+ EMailJunkFilterClass parent_class;
+};
+
+enum {
+ PROP_0,
+ PROP_CONVERT_TO_UNICODE
+};
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_bogofilter_get_type (void);
+static void e_bogofilter_interface_init (CamelJunkFilterInterface *interface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (
+ EBogofilter,
+ e_bogofilter,
+ E_TYPE_MAIL_JUNK_FILTER, 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (
+ CAMEL_TYPE_JUNK_FILTER,
+ e_bogofilter_interface_init))
+
+#ifdef G_OS_UNIX
+static void
+bogofilter_cancelled_cb (GCancellable *cancellable,
+ GPid *pid)
+{
+ /* XXX On UNIX-like systems we can safely assume a GPid is the
+ * process ID and use it to terminate the process via signal. */
+ kill (*pid, SIGTERM);
+}
+#endif
+
+static void
+bogofilter_exited_cb (GPid *pid,
+ gint status,
+ gpointer user_data)
+{
+ struct {
+ GMainLoop *loop;
+ gint exit_code;
+ } *source_data = user_data;
+
+ if (WIFEXITED (status))
+ source_data->exit_code = WEXITSTATUS (status);
+ else
+ source_data->exit_code = BOGOFILTER_EXIT_STATUS_ERROR;
+
+ g_main_loop_quit (source_data->loop);
+}
+
+static gint
+bogofilter_command (const gchar **argv,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelStream *stream;
+ GMainContext *context;
+ GSource *source;
+ GPid child_pid;
+ gssize bytes_written;
+ gint standard_input;
+ gulong handler_id = 0;
+ gboolean success;
+
+ struct {
+ GMainLoop *loop;
+ gint exit_code;
+ } source_data;
+
+ /* Spawn Bogofilter with an open stdin pipe. */
+ success = g_spawn_async_with_pipes (
+ NULL,
+ (gchar **) argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD |
+ G_SPAWN_STDOUT_TO_DEV_NULL,
+ NULL, NULL,
+ &child_pid,
+ &standard_input,
+ NULL,
+ NULL,
+ error);
+
+ if (!success) {
+ gchar *command_line;
+
+ command_line = g_strjoinv (" ", (gchar **) argv);
+ g_prefix_error (
+ error, _("Failed to spawn Bogofilter (%s): "),
+ command_line);
+ g_free (command_line);
+
+ return BOGOFILTER_EXIT_STATUS_ERROR;
+ }
+
+ /* Stream the CamelMimeMessage to Bogofilter. */
+ stream = camel_stream_fs_new_with_fd (standard_input);
+ bytes_written = camel_data_wrapper_write_to_stream_sync (
+ CAMEL_DATA_WRAPPER (message), stream, cancellable, error);
+ success = (bytes_written >= 0) &&
+ (camel_stream_close (stream, cancellable, error) == 0);
+ g_object_unref (stream);
+
+ if (!success) {
+ g_spawn_close_pid (child_pid);
+ g_prefix_error (
+ error, _("Failed to stream mail "
+ "message content to Bogofilter: "));
+ return BOGOFILTER_EXIT_STATUS_ERROR;
+ }
+
+ /* Wait for the Bogofilter process to terminate
+ * using GLib's main loop for better portability. */
+
+ context = g_main_context_new ();
+
+ source = g_child_watch_source_new (child_pid);
+ g_source_set_callback (
+ source, (GSourceFunc)
+ bogofilter_exited_cb,
+ &source_data, NULL);
+ g_source_attach (source, context);
+ g_source_unref (source);
+
+ source_data.loop = g_main_loop_new (context, TRUE);
+ source_data.exit_code = 0;
+
+#ifdef G_OS_UNIX
+ if (G_IS_CANCELLABLE (cancellable))
+ handler_id = g_cancellable_connect (
+ cancellable,
+ G_CALLBACK (bogofilter_cancelled_cb),
+ &child_pid, (GDestroyNotify) NULL);
+#endif
+
+ g_main_loop_run (source_data.loop);
+
+ if (handler_id > 0)
+ g_cancellable_disconnect (cancellable, handler_id);
+
+ g_main_loop_unref (source_data.loop);
+ source_data.loop = NULL;
+
+ g_main_context_unref (context);
+
+ /* Clean up. */
+
+ g_spawn_close_pid (child_pid);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ source_data.exit_code = BOGOFILTER_EXIT_STATUS_ERROR;
+
+ else if (source_data.exit_code == BOGOFILTER_EXIT_STATUS_ERROR)
+ g_set_error_literal (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("Bogofilter either crashed or "
+ "failed to process a mail message"));
+
+ return source_data.exit_code;
+}
+
+static void
+bogofilter_init_wordlist (EBogofilter *extension)
+{
+ CamelStream *stream;
+ CamelMimeParser *parser;
+ CamelMimeMessage *message;
+
+ /* Initialize the Bogofilter database with a welcome message. */
+
+ parser = camel_mime_parser_new ();
+ message = camel_mime_message_new ();
+
+ stream = camel_stream_fs_new_with_name (
+ WELCOME_MESSAGE, O_RDONLY, 0, NULL);
+ camel_mime_parser_init_with_stream (parser, stream, NULL);
+ camel_mime_parser_scan_from (parser, FALSE);
+ g_object_unref (stream);
+
+ camel_mime_part_construct_from_parser_sync (
+ CAMEL_MIME_PART (message), parser, NULL, NULL);
+
+ camel_junk_filter_learn_not_junk (
+ CAMEL_JUNK_FILTER (extension), message, NULL, NULL);
+
+ g_object_unref (message);
+ g_object_unref (parser);
+}
+
+static gboolean
+bogofilter_get_convert_to_unicode (EBogofilter *extension)
+{
+ return extension->convert_to_unicode;
+}
+
+static void
+bogofilter_set_convert_to_unicode (EBogofilter *extension,
+ gboolean convert_to_unicode)
+{
+ extension->convert_to_unicode = convert_to_unicode;
+
+ g_object_notify (G_OBJECT (extension), "convert-to-unicode");
+}
+
+static void
+bogofilter_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CONVERT_TO_UNICODE:
+ bogofilter_set_convert_to_unicode (
+ E_BOGOFILTER (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+bogofilter_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CONVERT_TO_UNICODE:
+ g_value_set_boolean (
+ value, bogofilter_get_convert_to_unicode (
+ E_BOGOFILTER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static gboolean
+bogofilter_available (EMailJunkFilter *junk_filter)
+{
+ return g_file_test (BOGOFILTER_BINARY, G_FILE_TEST_IS_EXECUTABLE);
+}
+
+static GtkWidget *
+bogofilter_new_config_widget (EMailJunkFilter *junk_filter)
+{
+ GtkWidget *box;
+ GtkWidget *widget;
+ gchar *markup;
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+ markup = g_markup_printf_escaped (
+ "<b>%s</b>", _("Bogofilter Options"));
+ widget = gtk_label_new (markup);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+ g_free (markup);
+
+ widget = gtk_check_button_new_with_mnemonic (
+ _("Convert message text to _Unicode"));
+ gtk_widget_set_margin_left (widget, 12);
+ gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ g_object_bind_property (
+ junk_filter, "convert-to-unicode",
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ return box;
+}
+
+static gboolean
+bogofilter_classify (CamelJunkFilter *junk_filter,
+ CamelMimeMessage *message,
+ CamelJunkStatus *status,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBogofilter *extension = E_BOGOFILTER (junk_filter);
+ static gboolean wordlist_initialized = FALSE;
+ gint exit_code;
+
+ const gchar *argv[] = {
+ BOGOFILTER_BINARY,
+ NULL, /* leave room for unicode option */
+ NULL
+ };
+
+ if (bogofilter_get_convert_to_unicode (extension))
+ argv[1] = "--unicode=yes";
+
+retry:
+ exit_code = bogofilter_command (argv, message, cancellable, error);
+
+ switch (exit_code) {
+ case BOGOFILTER_EXIT_STATUS_SPAM:
+ *status = CAMEL_JUNK_STATUS_MESSAGE_IS_JUNK;
+ break;
+
+ case BOGOFILTER_EXIT_STATUS_HAM:
+ *status = CAMEL_JUNK_STATUS_MESSAGE_IS_NOT_JUNK;
+ break;
+
+ case BOGOFILTER_EXIT_STATUS_UNSURE:
+ *status = CAMEL_JUNK_STATUS_INCONCLUSIVE;
+ break;
+
+ case BOGOFILTER_EXIT_STATUS_ERROR:
+ if (!wordlist_initialized) {
+ wordlist_initialized = TRUE;
+ bogofilter_init_wordlist (extension);
+ goto retry;
+ }
+ break;
+
+ default:
+ g_warning (
+ "Bogofilter: Unexpected exit code (%d) "
+ "while classifying message", exit_code);
+ break;
+ }
+
+ /* Check that the return value and GError agree. */
+ if (exit_code != BOGOFILTER_EXIT_STATUS_ERROR)
+ g_warn_if_fail (error == NULL || *error == NULL);
+ else
+ g_warn_if_fail (error == NULL || *error != NULL);
+
+ return (exit_code != BOGOFILTER_EXIT_STATUS_ERROR);
+}
+
+static gboolean
+bogofilter_learn_junk (CamelJunkFilter *junk_filter,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBogofilter *extension = E_BOGOFILTER (junk_filter);
+ gint exit_code;
+
+ const gchar *argv[] = {
+ BOGOFILTER_BINARY,
+ "--register-spam",
+ NULL, /* leave room for unicode option */
+ NULL
+ };
+
+ if (bogofilter_get_convert_to_unicode (extension))
+ argv[2] = "--unicode=yes";
+
+ exit_code = bogofilter_command (argv, message, cancellable, error);
+
+ if (exit_code != 0)
+ g_warning (
+ "Bogofilter: Unexpected exit code (%d) "
+ "while registering spam", exit_code);
+
+ /* Check that the return value and GError agree. */
+ if (exit_code != BOGOFILTER_EXIT_STATUS_ERROR)
+ g_warn_if_fail (error == NULL || *error == NULL);
+ else
+ g_warn_if_fail (error == NULL || *error != NULL);
+
+ return (exit_code != BOGOFILTER_EXIT_STATUS_ERROR);
+}
+
+static gboolean
+bogofilter_learn_not_junk (CamelJunkFilter *junk_filter,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBogofilter *extension = E_BOGOFILTER (junk_filter);
+ gint exit_code;
+
+ const gchar *argv[] = {
+ BOGOFILTER_BINARY,
+ "--register-ham",
+ NULL, /* leave room for unicode option */
+ NULL
+ };
+
+ if (bogofilter_get_convert_to_unicode (extension))
+ argv[2] = "--unicode=yes";
+
+ exit_code = bogofilter_command (argv, message, cancellable, error);
+
+ if (exit_code != 0)
+ g_warning (
+ "Bogofilter: Unexpected exit code (%d) "
+ "while registering ham", exit_code);
+
+ /* Check that the return value and GError agree. */
+ if (exit_code != BOGOFILTER_EXIT_STATUS_ERROR)
+ g_warn_if_fail (error == NULL || *error == NULL);
+ else
+ g_warn_if_fail (error == NULL || *error != NULL);
+
+ return (exit_code != BOGOFILTER_EXIT_STATUS_ERROR);
+}
+
+static void
+e_bogofilter_class_init (EBogofilterClass *class)
+{
+ GObjectClass *object_class;
+ EMailJunkFilterClass *junk_filter_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = bogofilter_set_property;
+ object_class->get_property = bogofilter_get_property;
+
+ junk_filter_class = E_MAIL_JUNK_FILTER_CLASS (class);
+ junk_filter_class->filter_name = "Bogofilter";
+ junk_filter_class->display_name = _("Bogofilter");
+ junk_filter_class->available = bogofilter_available;
+ junk_filter_class->new_config_widget = bogofilter_new_config_widget;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CONVERT_TO_UNICODE,
+ g_param_spec_boolean (
+ "convert-to-unicode",
+ "Convert to Unicode",
+ "Convert message text to Unicode",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_bogofilter_class_finalize (EBogofilterClass *class)
+{
+}
+
+static void
+e_bogofilter_interface_init (CamelJunkFilterInterface *interface)
+{
+ interface->classify = bogofilter_classify;
+ interface->learn_junk = bogofilter_learn_junk;
+ interface->learn_not_junk = bogofilter_learn_not_junk;
+}
+
+static void
+e_bogofilter_init (EBogofilter *extension)
+{
+ gconf_bridge_bind_property (
+ gconf_bridge_get (),
+ "/apps/evolution/mail/junk/bogofilter/unicode",
+ G_OBJECT (extension), "convert-to-unicode");
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_bogofilter_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/bogofilter/evolution-bogofilter.schemas.in b/modules/bogofilter/evolution-bogofilter.schemas.in
new file mode 100644
index 0000000000..e313eb9ffc
--- /dev/null
+++ b/modules/bogofilter/evolution-bogofilter.schemas.in
@@ -0,0 +1,20 @@
+<gconfschemafile>
+ <schemalist>
+
+ <schema>
+ <key>/schemas/apps/evolution/mail/junk/bogofilter/unicode</key>
+ <applyto>/apps/evolution/mail/junk/bogofilter/unicode</applyto>
+ <owner>evolution-bogofilter</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Convert mail messages to Unicode</short>
+ <long>
+ Convert message text to Unicode UTF-8 to unify spam/ham tokens
+ coming from different character sets.
+ </long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</gconfschemafile>
diff --git a/modules/mail/Makefile.am b/modules/mail/Makefile.am
index 884d050d78..0bea9ac1a2 100644
--- a/modules/mail/Makefile.am
+++ b/modules/mail/Makefile.am
@@ -27,8 +27,6 @@ libevolution_module_mail_la_SOURCES = \
e-mail-config-web-view.h \
e-mail-event-hook.c \
e-mail-event-hook.h \
- e-mail-junk-hook.c \
- e-mail-junk-hook.h \
e-mail-shell-backend.c \
e-mail-shell-backend.h \
e-mail-shell-content.c \
diff --git a/modules/mail/e-mail-junk-hook.c b/modules/mail/e-mail-junk-hook.c
deleted file mode 100644
index 4ccc404e8d..0000000000
--- a/modules/mail/e-mail-junk-hook.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * e-mail-junk-hook.c
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "e-mail-junk-hook.h"
-
-#include <glib/gi18n.h>
-
-#include "e-util/e-alert-dialog.h"
-#include "shell/e-shell.h"
-
-#include "mail/em-junk.h"
-#include "mail/em-utils.h"
-#include "mail/e-mail-backend.h"
-#include "mail/e-mail-session.h"
-
-struct _EMailJunkHookPrivate {
- EMJunkInterface interface;
-};
-
-struct ErrorData {
- const gchar *error_message;
- GError *error;
-};
-
-static gpointer parent_class;
-static GType mail_junk_hook_type;
-
-static gboolean
-mail_junk_hook_idle_cb (struct ErrorData *data)
-{
- EShell *shell;
- EShellBackend *shell_backend;
-
- shell = e_shell_get_default ();
- shell_backend = e_shell_get_backend_by_name (shell, "mail");
-
- e_mail_backend_submit_alert (
- E_MAIL_BACKEND (shell_backend),
- data->error_message, data->error->message, NULL);
-
- g_error_free (data->error);
- g_slice_free (struct ErrorData, data);
-
- return FALSE;
-}
-
-static void
-mail_junk_hook_error (const gchar *error_message,
- GError *error)
-{
- struct ErrorData *data;
-
- g_return_if_fail (error != NULL);
-
- data = g_slice_new (struct ErrorData);
- data->error_message = error_message;
- data->error = error;
-
- g_idle_add ((GSourceFunc) mail_junk_hook_idle_cb, data);
-}
-
-static const gchar *
-mail_junk_hook_get_name (CamelJunkPlugin *junk_plugin)
-{
- EMJunkInterface *interface;
-
- interface = (EMJunkInterface *) junk_plugin;
-
- if (!interface->hook->plugin->enabled) {
- /* Translators: "None" for a junk hook name,
- * when the junk plugin is not enabled. */
- return C_("mail-junk-hook", "None");
- }
-
- return interface->hook->plugin->name;
-}
-
-static void
-mail_junk_hook_plugin_init (CamelJunkPlugin *junk_plugin)
-{
- EMJunkInterface *interface;
- EPluginClass *class;
-
- interface = (EMJunkInterface *) junk_plugin;
-
- class = E_PLUGIN_GET_CLASS (interface->hook->plugin);
- g_return_if_fail (class->enable != NULL);
-
- class->enable (interface->hook->plugin, 1);
-}
-
-static gboolean
-mail_junk_hook_check_junk (CamelJunkPlugin *junk_plugin,
- CamelMimeMessage *mime_message)
-{
- EMJunkTarget target = { mime_message, NULL };
- EMJunkInterface *interface;
- gpointer result;
-
- interface = (EMJunkInterface *) junk_plugin;
-
- if (!interface->hook->plugin->enabled)
- return FALSE;
-
- result = e_plugin_invoke (
- interface->hook->plugin,
- interface->check_junk, &target);
-
- if (target.error != NULL)
- mail_junk_hook_error ("mail:junk-check-error", target.error);
-
- return (result != NULL);
-}
-
-static void
-mail_junk_hook_report_junk (CamelJunkPlugin *junk_plugin,
- CamelMimeMessage *mime_message)
-{
- EMJunkTarget target = { mime_message, NULL };
- EMJunkInterface *interface;
-
- interface = (EMJunkInterface *) junk_plugin;
-
- if (!interface->hook->plugin->enabled)
- return;
-
- e_plugin_invoke (
- interface->hook->plugin,
- interface->report_junk, &target);
-
- if (target.error != NULL)
- mail_junk_hook_error ("mail:junk-report-error", target.error);
-}
-
-static void
-mail_junk_hook_report_notjunk (CamelJunkPlugin *junk_plugin,
- CamelMimeMessage *mime_message)
-{
- EMJunkTarget target = { mime_message, NULL };
- EMJunkInterface *interface;
-
- interface = (EMJunkInterface *) junk_plugin;
-
- if (!interface->hook->plugin->enabled)
- return;
-
- e_plugin_invoke (
- interface->hook->plugin,
- interface->report_notjunk, &target);
-
- if (target.error != NULL)
- mail_junk_hook_error (
- "mail:junk-not-report-error", target.error);
-}
-
-static void
-mail_junk_hook_commit_reports (CamelJunkPlugin *junk_plugin)
-{
- EMJunkInterface *interface;
-
- interface = (EMJunkInterface *) junk_plugin;
-
- if (!interface->hook->plugin->enabled)
- return;
-
- e_plugin_invoke (
- interface->hook->plugin,
- interface->commit_reports, NULL);
-}
-
-static void
-mail_junk_hook_finalize (GObject *object)
-{
- EMailJunkHookPrivate *priv;
-
- priv = E_MAIL_JUNK_HOOK (object)->priv;
-
- g_free (priv->interface.check_junk);
- g_free (priv->interface.report_junk);
- g_free (priv->interface.report_notjunk);
- g_free (priv->interface.commit_reports);
- g_free (priv->interface.validate_binary);
- g_free (priv->interface.plugin_name);
-
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static gint
-mail_junk_hook_construct (EPluginHook *hook,
- EPlugin *plugin,
- xmlNodePtr node)
-{
- EMailJunkHookPrivate *priv;
- EShell *shell;
- EShellBackend *shell_backend;
- EMailBackend *backend;
- EMailSession *session;
- gchar *property;
-
- priv = E_MAIL_JUNK_HOOK (hook)->priv;
-
- /* Chain up to parent's construct() method. */
- if (E_PLUGIN_HOOK_CLASS (parent_class)->construct (hook, plugin, node) == -1)
- return -1;
-
- if (!plugin->enabled)
- return -1;
-
- node = xmlFirstElementChild (node);
-
- if (node == NULL)
- return -1;
-
- if (g_strcmp0 ((gchar *) node->name, "interface") != 0)
- return -1;
-
- property = e_plugin_xml_prop (node, "check_junk");
- priv->interface.check_junk = property;
-
- property = e_plugin_xml_prop (node, "report_junk");
- priv->interface.report_junk = property;
-
- property = e_plugin_xml_prop (node, "report_non_junk");
- priv->interface.report_notjunk = property;
-
- property = e_plugin_xml_prop (node, "commit_reports");
- priv->interface.commit_reports = property;
-
- property = e_plugin_xml_prop (node, "validate_binary");
- priv->interface.validate_binary = property;
-
- property = e_plugin_xml_prop (node, "name");
- priv->interface.plugin_name = property;
-
- if (priv->interface.check_junk == NULL)
- return -1;
-
- if (priv->interface.report_junk == NULL)
- return -1;
-
- if (priv->interface.report_notjunk == NULL)
- return -1;
-
- if (priv->interface.commit_reports == NULL)
- return -1;
-
- shell = e_shell_get_default ();
- shell_backend = e_shell_get_backend_by_name (shell, "mail");
-
- backend = E_MAIL_BACKEND (shell_backend);
- session = e_mail_backend_get_session (backend);
-
- mail_session_add_junk_plugin (
- session, priv->interface.plugin_name,
- &priv->interface.camel);
-
- return 0;
-}
-
-static void
-mail_junk_hook_class_init (EMailJunkHookClass *class)
-{
- GObjectClass *object_class;
- EPluginHookClass *plugin_hook_class;
-
- parent_class = g_type_class_peek_parent (class);
- g_type_class_add_private (class, sizeof (EMailJunkHookPrivate));
-
- object_class = G_OBJECT_CLASS (class);
- object_class->finalize = mail_junk_hook_finalize;
-
- plugin_hook_class = E_PLUGIN_HOOK_CLASS (class);
- plugin_hook_class->construct = mail_junk_hook_construct;
- plugin_hook_class->id = "org.gnome.evolution.mail.junk:1.0";
-}
-
-static void
-mail_junk_hook_init (EMailJunkHook *mail_junk_hook)
-{
- EMJunkInterface *interface;
-
- mail_junk_hook->priv = G_TYPE_INSTANCE_GET_PRIVATE (
- mail_junk_hook, E_TYPE_MAIL_JUNK_HOOK, EMailJunkHookPrivate);
-
- interface = &mail_junk_hook->priv->interface;
- interface->camel.get_name = mail_junk_hook_get_name;
- interface->camel.api_version = 1;
- interface->camel.check_junk = mail_junk_hook_check_junk;
- interface->camel.report_junk = mail_junk_hook_report_junk;
- interface->camel.report_notjunk = mail_junk_hook_report_notjunk;
- interface->camel.commit_reports = mail_junk_hook_commit_reports;
- interface->camel.init = mail_junk_hook_plugin_init;
- interface->hook = E_PLUGIN_HOOK (mail_junk_hook);
-}
-
-GType
-e_mail_junk_hook_get_type (void)
-{
- return mail_junk_hook_type;
-}
-
-void
-e_mail_junk_hook_register_type (GTypeModule *type_module)
-{
- const GTypeInfo type_info = {
- sizeof (EMailJunkHookClass),
- (GBaseInitFunc) NULL,
- (GBaseFinalizeFunc) NULL,
- (GClassInitFunc) mail_junk_hook_class_init,
- (GClassFinalizeFunc) NULL,
- NULL, /* class_data */
- sizeof (EMailJunkHook),
- 0, /* n_preallocs */
- (GInstanceInitFunc) mail_junk_hook_init,
- NULL /* value_table */
- };
-
- mail_junk_hook_type = g_type_module_register_type (
- type_module, E_TYPE_PLUGIN_HOOK,
- "EMailJunkHook", &type_info, 0);
-}
diff --git a/modules/mail/e-mail-junk-hook.h b/modules/mail/e-mail-junk-hook.h
deleted file mode 100644
index f5882e66b3..0000000000
--- a/modules/mail/e-mail-junk-hook.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * e-mail-junk-hook.h
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) version 3.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with the program; if not, see <http://www.gnu.org/licenses/>
- *
- *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
- *
- */
-
-#ifndef E_MAIL_JUNK_HOOK_H
-#define E_MAIL_JUNK_HOOK_H
-
-#include <e-util/e-plugin.h>
-
-/* Standard GObject macros */
-#define E_TYPE_MAIL_JUNK_HOOK \
- (e_mail_junk_hook_get_type ())
-#define E_MAIL_JUNK_HOOK(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), E_TYPE_MAIL_JUNK_HOOK, EMailJunkHook))
-#define E_MAIL_JUNK_HOOK_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_CAST \
- ((cls), E_TYPE_MAIL_JUNK_HOOK, EMailJunkHookClass))
-#define E_IS_MAIL_JUNK_HOOK(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), E_TYPE_MAIL_JUNK_HOOK))
-#define E_IS_MAIL_JUNK_HOOK_CLASS(cls) \
- (G_TYPE_CHECK_CLASS_TYPE \
- ((cls), E_TYPE_MAIL_JUNK_HOOK))
-#define E_MAIL_JUNK_HOOK_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), E_TYPE_MAIL_JUNK_HOOK, EMailJunkHookClass))
-
-G_BEGIN_DECLS
-
-typedef struct _EMailJunkHook EMailJunkHook;
-typedef struct _EMailJunkHookClass EMailJunkHookClass;
-typedef struct _EMailJunkHookPrivate EMailJunkHookPrivate;
-
-struct _EMailJunkHook {
- EPluginHook parent;
- EMailJunkHookPrivate *priv;
-};
-
-struct _EMailJunkHookClass {
- EPluginHookClass parent_class;
-};
-
-GType e_mail_junk_hook_get_type (void);
-void e_mail_junk_hook_register_type (GTypeModule *type_module);
-
-G_END_DECLS
-
-#endif /* E_MAIL_JUNK_HOOK_H */
diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c
index 8e2688e615..bbf65e36b5 100644
--- a/modules/mail/em-mailer-prefs.c
+++ b/modules/mail/em-mailer-prefs.c
@@ -37,18 +37,20 @@
#include "libedataserverui/e-cell-renderer-color.h"
-#include "e-util/e-util.h"
-#include "e-util/e-datetime-format.h"
-#include "e-util/e-util-private.h"
-#include "widgets/misc/e-charset-combo-box.h"
-#include "shell/e-shell-utils.h"
-
-#include "e-mail-backend.h"
-#include "e-mail-label-manager.h"
-#include "e-mail-reader-utils.h"
-#include "em-folder-selection-button.h"
-#include "em-junk.h"
-#include "em-config.h"
+#include <e-util/e-util.h>
+#include <e-util/e-datetime-format.h>
+#include <e-util/e-util-private.h>
+
+#include <misc/e-charset-combo-box.h>
+#include <misc/e-port-entry.h>
+#include <shell/e-shell-utils.h>
+
+#include <mail/e-mail-backend.h>
+#include <mail/e-mail-junk-options.h>
+#include <mail/e-mail-label-manager.h>
+#include <mail/e-mail-reader-utils.h>
+#include <mail/em-folder-selection-button.h>
+#include <mail/em-config.h>
enum {
HEADER_LIST_NAME_COLUMN, /* displayable name of the header (may be a translation) */
@@ -102,7 +104,6 @@ em_mailer_prefs_finalize (GObject *object)
{
EMMailerPrefs *prefs = (EMMailerPrefs *) object;
- g_object_unref (prefs->session);
g_object_unref (prefs->builder);
if (prefs->labels_change_notify_id) {
@@ -676,131 +677,8 @@ emmp_free (EConfig *ec, GSList *items, gpointer data)
}
static void
-junk_plugin_changed (GtkWidget *combo, EMMailerPrefs *prefs)
-{
- gchar *def_plugin;
- const GList *plugins = mail_session_get_junk_plugins (prefs->session);
- GtkTreeIter iter;
-
- g_return_if_fail (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter));
-
- def_plugin = NULL;
- gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)), &iter, 0, &def_plugin, -1);
-
- gconf_client_set_string (prefs->gconf, "/apps/evolution/mail/junk/default_plugin", def_plugin, NULL);
- while (plugins) {
- EMJunkInterface *iface = plugins->data;
-
- if (iface->plugin_name && def_plugin && !strcmp (iface->plugin_name, def_plugin)) {
- gboolean status;
-
- CAMEL_SESSION (prefs->session)->junk_plugin =
- CAMEL_JUNK_PLUGIN (&iface->camel);
- status = e_plugin_invoke (iface->hook->plugin, iface->validate_binary, NULL) != NULL;
- if ((gboolean) status == TRUE) {
- gchar *text, *html;
- gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-info", GTK_ICON_SIZE_MENU);
- text = g_strdup_printf (_("%s plugin is available and the binary is installed."), iface->plugin_name);
- html = g_strdup_printf ("<i>%s</i>", text);
- gtk_label_set_markup (prefs->plugin_status, html);
- g_free (html);
- g_free (text);
- } else {
- gchar *text, *html;
- gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-warning", GTK_ICON_SIZE_MENU);
- text = g_strdup_printf (_("%s plugin is not available. Please check whether the package is installed."), iface->plugin_name);
- html = g_strdup_printf ("<i>%s</i>", text);
- gtk_label_set_markup (prefs->plugin_status, html);
- g_free (html);
- g_free (text);
- }
- break;
- }
- plugins = plugins->next;
- }
-
- g_free (def_plugin);
-}
-
-static void
-junk_plugin_setup (GtkComboBox *combo_box, EMMailerPrefs *prefs)
-{
- GtkListStore *store;
- GtkCellRenderer *cell;
- gint index = 0;
- gboolean def_set = FALSE;
- const GList *plugins = mail_session_get_junk_plugins (prefs->session);
- gchar *pdefault = gconf_client_get_string (prefs->gconf, "/apps/evolution/mail/junk/default_plugin", NULL);
-
- store = gtk_list_store_new (1, G_TYPE_STRING);
- gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
-
- cell = gtk_cell_renderer_text_new ();
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
- gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
- "text", 0,
- NULL);
-
- if (!plugins || !g_list_length ((GList *) plugins)) {
- GtkTreeIter iter;
-
- gtk_list_store_append (store, &iter);
- gtk_list_store_set (
- store, &iter, 0, _("No junk plugin available"), -1);
- gtk_combo_box_set_active (combo_box, 0);
- gtk_widget_set_sensitive (GTK_WIDGET (combo_box), FALSE);
- gtk_widget_hide (GTK_WIDGET (prefs->plugin_image));
- gtk_widget_hide (GTK_WIDGET (prefs->plugin_status));
- gtk_image_set_from_stock (prefs->plugin_image, NULL, 0);
- g_free (pdefault);
-
- return;
- }
-
- while (plugins) {
- EMJunkInterface *iface = plugins->data;
- GtkTreeIter iter;
-
- gtk_list_store_append (store, &iter);
- gtk_list_store_set (store, &iter, 0, iface->plugin_name, -1);
- if (!def_set && pdefault && iface->plugin_name && !strcmp (pdefault, iface->plugin_name)) {
- gboolean status;
-
- def_set = TRUE;
- gtk_combo_box_set_active (combo_box, index);
- status = e_plugin_invoke (iface->hook->plugin, iface->validate_binary, NULL) != NULL;
- if (status) {
- gchar *text, *html;
- gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-info", GTK_ICON_SIZE_MENU);
- /* May be a better text */
- text = g_strdup_printf (_("%s plugin is available and the binary is installed."), iface->plugin_name);
- html = g_strdup_printf ("<i>%s</i>", text);
- gtk_label_set_markup (prefs->plugin_status, html);
- g_free (html);
- g_free (text);
- } else {
- gchar *text, *html;
- gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-warning", GTK_ICON_SIZE_MENU);
- /* May be a better text */
- text = g_strdup_printf (_("%s plugin is not available. Please check whether the package is installed."), iface->plugin_name);
- html = g_strdup_printf ("<i>%s</i>", text);
- gtk_label_set_markup (prefs->plugin_status, html);
- g_free (html);
- g_free (text);
- }
- }
- plugins = plugins->next;
- index++;
- }
-
- g_signal_connect (
- combo_box, "changed",
- G_CALLBACK (junk_plugin_changed), prefs);
- g_free (pdefault);
-}
-
-static void
em_mailer_prefs_construct (EMMailerPrefs *prefs,
+ EMailSession *session,
EShell *shell)
{
GSList *header_config_list, *header_add_list, *p;
@@ -823,6 +701,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
/* Make sure our custom widget classes are registered with
* GType before we load the GtkBuilder definition file. */
+ E_TYPE_MAIL_JUNK_OPTIONS;
EM_TYPE_FOLDER_SELECTION_BUTTON;
prefs->builder = gtk_builder_new ();
@@ -1182,10 +1061,8 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
G_BINDING_SYNC_CREATE);
emmp_empty_junk_init (prefs, GTK_COMBO_BOX (widget));
- prefs->default_junk_plugin = GTK_COMBO_BOX (e_builder_get_widget (prefs->builder, "default_junk_plugin"));
- prefs->plugin_status = GTK_LABEL (e_builder_get_widget (prefs->builder, "plugin_status"));
- prefs->plugin_image = GTK_IMAGE (e_builder_get_widget (prefs->builder, "plugin_image"));
- junk_plugin_setup (prefs->default_junk_plugin, prefs);
+ widget = e_builder_get_widget (prefs->builder, "junk-module-options");
+ e_mail_junk_options_set_session (E_MAIL_JUNK_OPTIONS (widget), session);
prefs->junk_header_check = (GtkToggleButton *)e_builder_get_widget (prefs->builder, "junk_header_check");
prefs->junk_header_tree = (GtkTreeView *)e_builder_get_widget (prefs->builder, "junk_header_tree");
@@ -1238,11 +1115,8 @@ em_mailer_prefs_new (EPreferencesWindow *window)
new = g_object_new (EM_TYPE_MAILER_PREFS, NULL);
- /* FIXME This should be a constructor property. */
- new->session = g_object_ref (session);
-
/* FIXME Kill this function. */
- em_mailer_prefs_construct (new, shell);
+ em_mailer_prefs_construct (new, session, shell);
return GTK_WIDGET (new);
}
diff --git a/modules/mail/em-mailer-prefs.h b/modules/mail/em-mailer-prefs.h
index d28fd5f5e4..35ebec3bf7 100644
--- a/modules/mail/em-mailer-prefs.h
+++ b/modules/mail/em-mailer-prefs.h
@@ -26,7 +26,6 @@
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>
#include <shell/e-shell.h>
-#include <mail/e-mail-session.h>
#include <widgets/misc/e-preferences-window.h>
/* Standard GObject macros */
@@ -56,8 +55,6 @@ typedef struct _EMMailerPrefsClass EMMailerPrefsClass;
struct _EMMailerPrefs {
GtkVBox parent_object;
- EMailSession *session;
-
GtkBuilder *builder;
GConfClient *gconf;
@@ -95,9 +92,6 @@ struct _EMMailerPrefs {
GtkToggleButton *sa_local_tests_only;
GtkToggleButton *sa_use_daemon;
- GtkComboBox *default_junk_plugin;
- GtkLabel *plugin_status;
- GtkImage *plugin_image;
GtkToggleButton *junk_header_check;
GtkTreeView *junk_header_tree;
diff --git a/modules/mail/evolution-module-mail.c b/modules/mail/evolution-module-mail.c
index ddb9d8996b..bda0db5d95 100644
--- a/modules/mail/evolution-module-mail.c
+++ b/modules/mail/evolution-module-mail.c
@@ -27,7 +27,6 @@
#include "e-mail-config-hook.h"
#include "e-mail-event-hook.h"
-#include "e-mail-junk-hook.h"
#include "e-mail-shell-backend.h"
#include "e-mail-shell-content.h"
@@ -52,7 +51,6 @@ e_module_load (GTypeModule *type_module)
e_mail_config_hook_register_type (type_module);
e_mail_event_hook_register_type (type_module);
- e_mail_junk_hook_register_type (type_module);
e_mail_shell_backend_register_type (type_module);
e_mail_shell_content_register_type (type_module);
diff --git a/modules/spamassassin/Makefile.am b/modules/spamassassin/Makefile.am
new file mode 100644
index 0000000000..8bce85214b
--- /dev/null
+++ b/modules/spamassassin/Makefile.am
@@ -0,0 +1,56 @@
+module_LTLIBRARIES = libevolution-module-spamassassin.la
+
+libevolution_module_spamassassin_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"evolution-spamassassin\" \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(EVOLUTION_MAIL_CFLAGS)
+
+libevolution_module_spamassassin_la_SOURCES = \
+ evolution-spamassassin.c
+
+libevolution_module_spamassassin_la_LIBADD = \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/mail/libevolution-mail.la \
+ $(GNOME_PLATFORM_LIBS) \
+ $(EVOLUTION_MAIL_LIBS)
+
+libevolution_module_spamassassin_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED)
+
+schemadir = $(GCONF_SCHEMA_FILE_DIR)
+schema_in_files = evolution-spamassassin.schemas.in
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+@INTLTOOL_SCHEMAS_RULE@
+
+if GCONF_SCHEMAS_INSTALL
+
+if OS_WIN32
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schema_DATA) ; do \
+ (echo set GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE); \
+ echo $(GCONFTOOL) --makefile-install-rule $$p) >_temp.bat; \
+ cmd /c _temp.bat; \
+ rm _temp.bat; \
+ done \
+ fi
+else
+install-data-local:
+ if test -z "$(DESTDIR)" ; then \
+ for p in $(schema_DATA) ; do \
+ GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) \
+ $(GCONFTOOL) --makefile-install-rule $$p; \
+ done \
+ fi
+endif
+
+endif
+
+DISTCLEANFILES = $(schema_DATA)
+
+EXTRA_DIST = $(schema_in_files)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/spamassassin/evolution-spamassassin.c b/modules/spamassassin/evolution-spamassassin.c
new file mode 100644
index 0000000000..6107694713
--- /dev/null
+++ b/modules/spamassassin/evolution-spamassassin.c
@@ -0,0 +1,1177 @@
+/*
+ * evolution-spamassassin.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel.h>
+
+#include <e-util/e-mktemp.h>
+#include <e-util/gconf-bridge.h>
+#include <mail/e-mail-junk-filter.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SPAM_ASSASSIN \
+ (e_spam_assassin_get_type ())
+#define E_SPAM_ASSASSIN(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SPAM_ASSASSIN, ESpamAssassin))
+
+#ifndef SPAMASSASSIN_BINARY
+#define SPAMASSASSIN_BINARY "/usr/bin/spamassassin"
+#endif
+
+#ifndef SA_LEARN_BINARY
+#define SA_LEARN_BINARY "/usr/bin/sa-learn"
+#endif
+
+#ifndef SPAMC_BINARY
+#define SPAMC_BINARY "/usr/bin/spamc"
+#endif
+
+#ifndef SPAMD_BINARY
+#define SPAMD_BINARY "/usr/bin/spamd"
+#endif
+
+/* For starting our own daemon. */
+#define DAEMON_MAX_RETRIES 100
+#define DAEMON_RETRY_DELAY 0.05 /* seconds */
+
+#define SPAM_ASSASSIN_EXIT_STATUS_SUCCESS 0
+#define SPAM_ASSASSIN_EXIT_STATUS_ERROR -1
+
+typedef struct _ESpamAssassin ESpamAssassin;
+typedef struct _ESpamAssassinClass ESpamAssassinClass;
+
+struct _ESpamAssassin {
+ EMailJunkFilter parent;
+
+ GMutex *socket_path_mutex;
+
+ gchar *pid_file;
+ gchar *socket_path;
+ gchar *spamc_binary;
+ gchar *spamd_binary;
+ gint version;
+
+ gboolean local_only;
+ gboolean use_daemon;
+ gboolean version_set;
+
+ /* spamc/spamd state */
+ gboolean spamd_tested;
+ gboolean spamd_using_allow_tell;
+ gboolean system_spamd_available;
+ gboolean use_spamc;
+};
+
+struct _ESpamAssassinClass {
+ EMailJunkFilterClass parent_class;
+};
+
+enum {
+ PROP_0,
+ PROP_LOCAL_ONLY,
+ PROP_SPAMC_BINARY,
+ PROP_SPAMD_BINARY,
+ PROP_SOCKET_PATH,
+ PROP_USE_DAEMON
+};
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_spam_assassin_get_type (void);
+static void e_spam_assassin_interface_init (CamelJunkFilterInterface *interface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (
+ ESpamAssassin,
+ e_spam_assassin,
+ E_TYPE_MAIL_JUNK_FILTER, 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (
+ CAMEL_TYPE_JUNK_FILTER,
+ e_spam_assassin_interface_init))
+
+#ifdef G_OS_UNIX
+static void
+spam_assassin_cancelled_cb (GCancellable *cancellable,
+ GPid *pid)
+{
+ /* XXX On UNIX-like systems we can safely assume a GPid is the
+ * process ID and use it to terminate the process via signal. */
+ kill (*pid, SIGTERM);
+}
+#endif
+
+static void
+spam_assassin_exited_cb (GPid *pid,
+ gint status,
+ gpointer user_data)
+{
+ struct {
+ GMainLoop *loop;
+ gint exit_code;
+ } *source_data = user_data;
+
+ if (WIFEXITED (status))
+ source_data->exit_code = WEXITSTATUS (status);
+ else
+ source_data->exit_code = SPAM_ASSASSIN_EXIT_STATUS_ERROR;
+
+ g_main_loop_quit (source_data->loop);
+}
+
+static gint
+spam_assassin_command_full (const gchar **argv,
+ CamelMimeMessage *message,
+ const gchar *input_data,
+ GByteArray *output_buffer,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GMainContext *context;
+ GSpawnFlags flags;
+ GSource *source;
+ GPid child_pid;
+ gint standard_input;
+ gint standard_output;
+ gulong handler_id = 0;
+ gboolean success;
+
+ struct {
+ GMainLoop *loop;
+ gint exit_code;
+ } source_data;
+
+ flags = G_SPAWN_DO_NOT_REAP_CHILD;
+ if (output_buffer == NULL)
+ flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
+
+ /* Spawn SpamAssassin with an open stdin pipe. */
+ success = g_spawn_async_with_pipes (
+ NULL,
+ (gchar **) argv,
+ NULL,
+ flags,
+ NULL, NULL,
+ &child_pid,
+ &standard_input,
+ (output_buffer != NULL) ? &standard_output : NULL,
+ NULL,
+ error);
+
+ if (!success) {
+ gchar *command_line;
+
+ command_line = g_strjoinv (" ", (gchar **) argv);
+ g_prefix_error (
+ error, _("Failed to spawn SpamAssassin (%s): "),
+ command_line);
+ g_free (command_line);
+
+ return SPAM_ASSASSIN_EXIT_STATUS_ERROR;
+ }
+
+ if (message != NULL) {
+ CamelStream *stream;
+ gssize bytes_written;
+
+ /* Stream the CamelMimeMessage to SpamAssassin. */
+ stream = camel_stream_fs_new_with_fd (standard_input);
+ bytes_written = camel_data_wrapper_write_to_stream_sync (
+ CAMEL_DATA_WRAPPER (message),
+ stream, cancellable, error);
+ success = (bytes_written >= 0) &&
+ (camel_stream_close (stream, cancellable, error) == 0);
+ g_object_unref (stream);
+
+ if (!success) {
+ g_spawn_close_pid (child_pid);
+ g_prefix_error (
+ error, _("Failed to stream mail "
+ "message content to SpamAssassin: "));
+ return SPAM_ASSASSIN_EXIT_STATUS_ERROR;
+ }
+
+ } else if (input_data != NULL) {
+ gssize bytes_written;
+
+ /* Write raw data directly to SpamAssassin. */
+ bytes_written = camel_write (
+ standard_input, input_data,
+ strlen (input_data), cancellable, error);
+ success = (bytes_written >= 0);
+
+ close (standard_input);
+
+ if (!success) {
+ g_spawn_close_pid (child_pid);
+ g_prefix_error (
+ error, _("Failed to write '%s' "
+ "to SpamAssassin: "), input_data);
+ return SPAM_ASSASSIN_EXIT_STATUS_ERROR;
+ }
+ }
+
+ if (output_buffer != NULL) {
+ CamelStream *input_stream;
+ CamelStream *output_stream;
+ gssize bytes_written;
+
+ input_stream = camel_stream_fs_new_with_fd (standard_output);
+
+ output_stream = camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (
+ CAMEL_STREAM_MEM (output_stream), output_buffer);
+
+ bytes_written = camel_stream_write_to_stream (
+ input_stream, output_stream, cancellable, error);
+ g_byte_array_append (output_buffer, (guint8 *) "", 1);
+ success = (bytes_written >= 0);
+
+ g_object_unref (input_stream);
+ g_object_unref (output_stream);
+
+ if (!success) {
+ g_spawn_close_pid (child_pid);
+ g_prefix_error (
+ error, _("Failed to read "
+ "output from SpamAssassin: "));
+ return SPAM_ASSASSIN_EXIT_STATUS_ERROR;
+ }
+ }
+
+ /* Wait for the SpamAssassin process to terminate
+ * using GLib's main loop for better portability. */
+
+ context = g_main_context_new ();
+
+ source = g_child_watch_source_new (child_pid);
+ g_source_set_callback (
+ source, (GSourceFunc)
+ spam_assassin_exited_cb,
+ &source_data, NULL);
+ g_source_attach (source, context);
+ g_source_unref (source);
+
+ source_data.loop = g_main_loop_new (context, TRUE);
+ source_data.exit_code = 0;
+
+#ifdef G_OS_UNIX
+ if (G_IS_CANCELLABLE (cancellable))
+ handler_id = g_cancellable_connect (
+ cancellable,
+ G_CALLBACK (spam_assassin_cancelled_cb),
+ &child_pid, (GDestroyNotify) NULL);
+#endif
+
+ g_main_loop_run (source_data.loop);
+
+ if (handler_id > 0)
+ g_cancellable_disconnect (cancellable, handler_id);
+
+ g_main_loop_unref (source_data.loop);
+ source_data.loop = NULL;
+
+ g_main_context_unref (context);
+
+ /* Clean up. */
+
+ g_spawn_close_pid (child_pid);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ source_data.exit_code = SPAM_ASSASSIN_EXIT_STATUS_ERROR;
+
+ else if (source_data.exit_code == SPAM_ASSASSIN_EXIT_STATUS_ERROR)
+ g_set_error_literal (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("SpamAssassin either crashed or "
+ "failed to process a mail message"));
+
+ return source_data.exit_code;
+}
+
+static gint
+spam_assassin_command (const gchar **argv,
+ CamelMimeMessage *message,
+ const gchar *input_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return spam_assassin_command_full (
+ argv, message, input_data, NULL, cancellable, error);
+}
+
+static gboolean
+spam_assassin_get_local_only (ESpamAssassin *extension)
+{
+ return extension->local_only;
+}
+
+static void
+spam_assassin_set_local_only (ESpamAssassin *extension,
+ gboolean local_only)
+{
+ extension->local_only = local_only;
+
+ g_object_notify (G_OBJECT (extension), "local-only");
+}
+
+static const gchar *
+spam_assassin_get_spamc_binary (ESpamAssassin *extension)
+{
+ return extension->spamc_binary;
+}
+
+static void
+spam_assassin_set_spamc_binary (ESpamAssassin *extension,
+ const gchar *spamc_binary)
+{
+ g_free (extension->spamc_binary);
+ extension->spamc_binary = g_strdup (spamc_binary);
+
+ g_object_notify (G_OBJECT (extension), "spamc-binary");
+}
+
+static const gchar *
+spam_assassin_get_spamd_binary (ESpamAssassin *extension)
+{
+ return extension->spamd_binary;
+}
+
+static void
+spam_assassin_set_spamd_binary (ESpamAssassin *extension,
+ const gchar *spamd_binary)
+{
+ g_free (extension->spamd_binary);
+ extension->spamd_binary = g_strdup (spamd_binary);
+
+ g_object_notify (G_OBJECT (extension), "spamd-binary");
+}
+
+static const gchar *
+spam_assassin_get_socket_path (ESpamAssassin *extension)
+{
+ return extension->socket_path;
+}
+
+static void
+spam_assassin_set_socket_path (ESpamAssassin *extension,
+ const gchar *socket_path)
+{
+ g_free (extension->socket_path);
+ extension->socket_path = g_strdup (socket_path);
+
+ g_object_notify (G_OBJECT (extension), "socket-path");
+}
+
+static gboolean
+spam_assassin_get_use_daemon (ESpamAssassin *extension)
+{
+ return extension->use_daemon;
+}
+
+static void
+spam_assassin_set_use_daemon (ESpamAssassin *extension,
+ gboolean use_daemon)
+{
+ extension->use_daemon = use_daemon;
+
+ g_object_notify (G_OBJECT (extension), "use-daemon");
+}
+
+static gboolean
+spam_assassin_get_version (ESpamAssassin *extension,
+ gint *spam_assassin_version,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GByteArray *output_buffer;
+ gint exit_code;
+ guint ii;
+
+ const gchar *argv[] = {
+ SA_LEARN_BINARY,
+ "--version",
+ NULL
+ };
+
+ if (extension->version_set) {
+ if (spam_assassin_version != NULL)
+ *spam_assassin_version = extension->version;
+ return TRUE;
+ }
+
+ output_buffer = g_byte_array_new ();
+
+ exit_code = spam_assassin_command_full (
+ argv, NULL, NULL, output_buffer, cancellable, error);
+
+ if (exit_code != 0) {
+ g_byte_array_free (output_buffer, TRUE);
+ return FALSE;
+ }
+
+ for (ii = 0; ii < output_buffer->len; ii++) {
+ if (g_ascii_isdigit (output_buffer->data[ii])) {
+ guint8 ch = output_buffer->data[ii];
+ extension->version = (ch - '0');
+ extension->version_set = TRUE;
+ break;
+ }
+ }
+
+ if (spam_assassin_version != NULL)
+ *spam_assassin_version = extension->version;
+
+ g_byte_array_free (output_buffer, TRUE);
+
+ return TRUE;
+}
+
+static void
+spam_assassin_test_spamd_allow_tell (ESpamAssassin *extension)
+{
+ gint exit_code;
+ GError *error = NULL;
+
+ const gchar *argv[] = {
+ SPAMC_BINARY,
+ "--learntype=forget",
+ NULL
+ };
+
+ /* Check if spamd is running with --allow-tell. */
+
+ exit_code = spam_assassin_command (argv, NULL, "\n", NULL, &error);
+ extension->spamd_using_allow_tell = (exit_code == 0);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+spam_assassin_test_spamd_running (ESpamAssassin *extension,
+ gboolean system_spamd)
+{
+ const gchar *argv[5];
+ gint exit_code;
+ gint ii = 0;
+ GError *error = NULL;
+
+ g_mutex_lock (extension->socket_path_mutex);
+
+ argv[ii++] = extension->spamc_binary;
+ argv[ii++] = "--no-safe-fallback";
+ if (!system_spamd) {
+ argv[ii++] = "--socket";
+ argv[ii++] = extension->socket_path;
+ }
+ argv[ii] = NULL;
+
+ g_assert (ii < G_N_ELEMENTS (argv));
+
+ exit_code = spam_assassin_command (
+ argv, NULL, "From test@127.0.0.1", NULL, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_mutex_unlock (extension->socket_path_mutex);
+
+ return (exit_code == 0);
+}
+
+static gboolean
+spam_assassin_start_our_own_daemon (ESpamAssassin *extension)
+{
+ const gchar *argv[8];
+ gchar *pid_file;
+ gchar *socket_path;
+ gboolean started = FALSE;
+ gint exit_code;
+ gint ii = 0;
+ GError *error = NULL;
+
+ g_mutex_lock (extension->socket_path_mutex);
+
+ pid_file = e_mktemp ("spamd-pid-file-XXXXXX");
+ socket_path = e_mktemp ("spamd-socket-path-XXXXXX");
+
+ argv[ii++] = extension->spamd_binary;
+ argv[ii++] = "--socketpath";
+ argv[ii++] = socket_path;
+
+ if (spam_assassin_get_local_only (extension))
+ argv[ii++] = "--local";
+
+ argv[ii++] = "--max-children=1";
+ argv[ii++] = "--pidfile";
+ argv[ii++] = pid_file;
+ argv[ii] = NULL;
+
+ g_assert (ii < G_N_ELEMENTS (argv));
+
+ exit_code = spam_assassin_command (argv, NULL, NULL, NULL, &error);
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ goto exit;
+ }
+
+ if (exit_code == 0) {
+ /* Wait for the socket path to appear. */
+ for (ii = 0; ii < DAEMON_MAX_RETRIES; ii++) {
+ if (g_file_test (socket_path, G_FILE_TEST_EXISTS)) {
+ started = TRUE;
+ break;
+ }
+ g_usleep (DAEMON_RETRY_DELAY * G_USEC_PER_SEC);
+ }
+ }
+
+ /* Set these directly to avoid emitting "notify" signals. */
+ if (started) {
+ g_free (extension->pid_file);
+ extension->pid_file = pid_file;
+ pid_file = NULL;
+
+ g_free (extension->socket_path);
+ extension->socket_path = socket_path;
+ socket_path = NULL;
+ }
+
+exit:
+ g_free (pid_file);
+ g_free (socket_path);
+
+ g_mutex_unlock (extension->socket_path_mutex);
+
+ return started;
+}
+
+static void
+spam_assassin_kill_our_own_daemon (ESpamAssassin *extension)
+{
+ gint pid;
+ gchar *contents = NULL;
+ GError *error = NULL;
+
+ g_mutex_lock (extension->socket_path_mutex);
+
+ g_free (extension->socket_path);
+ extension->socket_path = NULL;
+
+ g_mutex_unlock (extension->socket_path_mutex);
+
+ if (extension->pid_file == NULL)
+ return;
+
+ g_file_get_contents (extension->pid_file, &contents, NULL, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (contents == NULL);
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (contents != NULL);
+
+ pid = atoi (contents);
+ g_free (contents);
+
+ if (pid > 0 && kill (pid, SIGTERM) == 0)
+ waitpid (pid, NULL, 0);
+}
+
+static void
+spam_assassin_test_spamd (ESpamAssassin *extension)
+{
+ const gchar *spamd_binary;
+ gboolean try_system_spamd;
+
+ /* XXX SpamAssassin could really benefit from a D-Bus interface
+ * these days. These tests are just needlessly painful for
+ * clients trying to talk to an already-running spamd. */
+
+ extension->use_spamc = FALSE;
+ spamd_binary = extension->spamd_binary;
+ try_system_spamd = (g_strcmp0 (spamd_binary, SPAMD_BINARY) == 0);
+
+ if (extension->local_only && try_system_spamd) {
+ gint exit_code;
+
+ /* Run a shell command to check for a running
+ * spamd process with a -L/--local option or a
+ * -p/--port option. */
+
+ const gchar *argv[] = {
+ "/bin/sh",
+ "-c",
+ "ps ax | grep -v grep | "
+ "grep -E 'spamd.*(\\-L|\\-\\-local)' | "
+ "grep -E -v '\\ \\-p\\ |\\ \\-\\-port\\ '",
+ NULL
+ };
+
+ exit_code = spam_assassin_command (
+ argv, NULL, NULL, NULL, NULL);
+ try_system_spamd = (exit_code == 0);
+ }
+
+ /* Try to use the system spamd first. */
+ if (try_system_spamd) {
+ if (spam_assassin_test_spamd_running (extension, TRUE)) {
+ extension->use_spamc = TRUE;
+ extension->system_spamd_available = TRUE;
+ }
+ }
+
+ /* If there's no system spamd running, try
+ * to use one with a user specified socket. */
+ if (!extension->use_spamc && extension->socket_path != NULL) {
+ if (spam_assassin_test_spamd_running (extension, FALSE)) {
+ extension->use_spamc = TRUE;
+ extension->system_spamd_available = FALSE;
+ }
+ }
+
+ /* Still unsuccessful? Try to start our own spamd. */
+ if (!extension->use_spamc) {
+ extension->use_spamc =
+ spam_assassin_start_our_own_daemon (extension) &&
+ spam_assassin_test_spamd_running (extension, FALSE);
+ }
+}
+
+static void
+spam_assassin_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_LOCAL_ONLY:
+ spam_assassin_set_local_only (
+ E_SPAM_ASSASSIN (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_SPAMC_BINARY:
+ spam_assassin_set_spamc_binary (
+ E_SPAM_ASSASSIN (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_SPAMD_BINARY:
+ spam_assassin_set_spamd_binary (
+ E_SPAM_ASSASSIN (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_SOCKET_PATH:
+ spam_assassin_set_socket_path (
+ E_SPAM_ASSASSIN (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_USE_DAEMON:
+ spam_assassin_set_use_daemon (
+ E_SPAM_ASSASSIN (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spam_assassin_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_LOCAL_ONLY:
+ g_value_set_boolean (
+ value, spam_assassin_get_local_only (
+ E_SPAM_ASSASSIN (object)));
+ return;
+
+ case PROP_SPAMC_BINARY:
+ g_value_set_string (
+ value, spam_assassin_get_spamc_binary (
+ E_SPAM_ASSASSIN (object)));
+ return;
+
+ case PROP_SPAMD_BINARY:
+ g_value_set_string (
+ value, spam_assassin_get_spamd_binary (
+ E_SPAM_ASSASSIN (object)));
+ return;
+
+ case PROP_SOCKET_PATH:
+ g_value_set_string (
+ value, spam_assassin_get_socket_path (
+ E_SPAM_ASSASSIN (object)));
+ return;
+
+ case PROP_USE_DAEMON:
+ g_value_set_boolean (
+ value, spam_assassin_get_use_daemon (
+ E_SPAM_ASSASSIN (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+spam_assassin_finalize (GObject *object)
+{
+ ESpamAssassin *extension = E_SPAM_ASSASSIN (object);
+
+ /* If we started our own daemon, kill it. */
+ spam_assassin_kill_our_own_daemon (extension);
+
+ g_mutex_free (extension->socket_path_mutex);
+
+ g_free (extension->pid_file);
+ g_free (extension->socket_path);
+ g_free (extension->spamc_binary);
+ g_free (extension->spamd_binary);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_spam_assassin_parent_class)->finalize (object);
+}
+
+static gboolean
+spam_assassin_available (EMailJunkFilter *junk_filter)
+{
+ ESpamAssassin *extension = E_SPAM_ASSASSIN (junk_filter);
+ gboolean available;
+ GError *error = NULL;
+
+ available = spam_assassin_get_version (extension, NULL, NULL, &error);
+
+ /* XXX These tests block like crazy so maybe this isn't the best
+ * place to be doing this, but the first available() call is
+ * done at startup before the UI is shown. So hopefully the
+ * delay will not be noticeable. */
+ if (available && extension->use_daemon && !extension->spamd_tested) {
+ extension->spamd_tested = TRUE;
+ spam_assassin_test_spamd (extension);
+ spam_assassin_test_spamd_allow_tell (extension);
+ }
+
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ return available;
+}
+
+static GtkWidget *
+spam_assassin_new_config_widget (EMailJunkFilter *junk_filter)
+{
+ GtkWidget *box;
+ GtkWidget *widget;
+ GtkWidget *container;
+ gchar *markup;
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+ markup = g_markup_printf_escaped (
+ "<b>%s</b>", _("SpamAssassin Options"));
+ widget = gtk_label_new (markup);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+ g_free (markup);
+
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_check_button_new_with_mnemonic (
+ _("I_nclude remote tests"));
+ gtk_widget_set_margin_left (widget, 12);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ g_object_bind_property (
+ junk_filter, "local-only",
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_INVERT_BOOLEAN);
+
+ markup = g_markup_printf_escaped (
+ "<small>%s</small>",
+ _("This will make SpamAssassin more reliable, but slower."));
+ widget = gtk_label_new (markup);
+ gtk_widget_set_margin_left (widget, 36);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+ g_free (markup);
+
+ return box;
+}
+
+static gboolean
+spam_assassin_classify (CamelJunkFilter *junk_filter,
+ CamelMimeMessage *message,
+ CamelJunkStatus *status,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESpamAssassin *extension = E_SPAM_ASSASSIN (junk_filter);
+ const gchar *argv[7];
+ gboolean using_spamc;
+ gint exit_code;
+ gint ii = 0;
+
+ g_mutex_lock (extension->socket_path_mutex);
+
+ using_spamc = (extension->use_spamc && extension->use_daemon);
+
+ if (using_spamc) {
+ argv[ii++] = extension->spamc_binary;
+ argv[ii++] = "--check";
+ argv[ii++] = "--timeout=60";
+ if (!extension->system_spamd_available) {
+ argv[ii++] = "--socket";
+ argv[ii++] = extension->socket_path;
+ }
+ } else {
+ argv[ii++] = SPAMASSASSIN_BINARY;
+ argv[ii++] = "--exit-code";
+ if (extension->local_only)
+ argv[ii++] = "--local";
+ }
+ argv[ii] = NULL;
+
+ g_assert (ii < G_N_ELEMENTS (argv));
+
+ exit_code = spam_assassin_command (
+ argv, message, NULL, cancellable, error);
+
+ /* For either program, exit code 0 means the message is ham. */
+ if (exit_code == 0)
+ *status = CAMEL_JUNK_STATUS_MESSAGE_IS_NOT_JUNK;
+
+ /* spamassassin(1) only specifies zero and non-zero exit codes. */
+ else if (!using_spamc)
+ *status = CAMEL_JUNK_STATUS_MESSAGE_IS_JUNK;
+
+ /* Whereas spamc(1) explicitly states exit code 1 means spam. */
+ else if (exit_code == 1)
+ *status = CAMEL_JUNK_STATUS_MESSAGE_IS_JUNK;
+
+ /* Consider any other spamc(1) exit code to be inconclusive
+ * since it most likely failed to process the message. */
+ else
+ *status = CAMEL_JUNK_STATUS_INCONCLUSIVE;
+
+ /* Check that the return value and GError agree. */
+ if (exit_code != SPAM_ASSASSIN_EXIT_STATUS_ERROR)
+ g_warn_if_fail (error == NULL || *error == NULL);
+ else
+ g_warn_if_fail (error == NULL || *error != NULL);
+
+ g_mutex_unlock (extension->socket_path_mutex);
+
+ return (exit_code != SPAM_ASSASSIN_EXIT_STATUS_ERROR);
+}
+
+static gboolean
+spam_assassin_learn_junk (CamelJunkFilter *junk_filter,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESpamAssassin *extension = E_SPAM_ASSASSIN (junk_filter);
+ const gchar *argv[5];
+ gint exit_code;
+ gint ii = 0;
+
+ if (extension->spamd_using_allow_tell) {
+ argv[ii++] = extension->spamc_binary;
+ argv[ii++] = "--learntype=spam";
+ } else {
+ argv[ii++] = SA_LEARN_BINARY;
+ argv[ii++] = "--spam";
+ if (extension->version >= 3)
+ argv[ii++] = "--no-sync";
+ else
+ argv[ii++] = "--no-rebuild";
+ if (extension->local_only)
+ argv[ii++] = "--local";
+ }
+ argv[ii] = NULL;
+
+ g_assert (ii < G_N_ELEMENTS (argv));
+
+ exit_code = spam_assassin_command (
+ argv, message, NULL, cancellable, error);
+
+ /* Check that the return value and GError agree. */
+ if (exit_code == SPAM_ASSASSIN_EXIT_STATUS_SUCCESS)
+ g_warn_if_fail (error == NULL || *error == NULL);
+ else
+ g_warn_if_fail (error == NULL || *error != NULL);
+
+ return (exit_code == SPAM_ASSASSIN_EXIT_STATUS_SUCCESS);
+}
+
+static gboolean
+spam_assassin_learn_not_junk (CamelJunkFilter *junk_filter,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESpamAssassin *extension = E_SPAM_ASSASSIN (junk_filter);
+ const gchar *argv[5];
+ gint exit_code;
+ gint ii = 0;
+
+ if (extension->spamd_using_allow_tell) {
+ argv[ii++] = extension->spamc_binary;
+ argv[ii++] = "--learntype=ham";
+ } else {
+ argv[ii++] = SA_LEARN_BINARY;
+ argv[ii++] = "--ham";
+ if (extension->version >= 3)
+ argv[ii++] = "--no-sync";
+ else
+ argv[ii++] = "--no-rebuild";
+ if (extension->local_only)
+ argv[ii++] = "--local";
+ }
+ argv[ii] = NULL;
+
+ g_assert (ii < G_N_ELEMENTS (argv));
+
+ exit_code = spam_assassin_command (
+ argv, message, NULL, cancellable, error);
+
+ /* Check that the return value and GError agree. */
+ if (exit_code == SPAM_ASSASSIN_EXIT_STATUS_SUCCESS)
+ g_warn_if_fail (error == NULL || *error == NULL);
+ else
+ g_warn_if_fail (error == NULL || *error != NULL);
+
+ return (exit_code == SPAM_ASSASSIN_EXIT_STATUS_SUCCESS);
+}
+
+static gboolean
+spam_assassin_synchronize (CamelJunkFilter *junk_filter,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESpamAssassin *extension = E_SPAM_ASSASSIN (junk_filter);
+ const gchar *argv[4];
+ gint exit_code;
+ gint ii = 0;
+
+ /* If we're using a spamd that allows learning,
+ * there's no need to synchronize anything. */
+ if (extension->spamd_using_allow_tell)
+ return TRUE;
+
+ argv[ii++] = SA_LEARN_BINARY;
+ if (extension->version >= 3)
+ argv[ii++] = "--sync";
+ else
+ argv[ii++] = "--rebuild";
+ if (extension->local_only)
+ argv[ii++] = "--local";
+ argv[ii] = NULL;
+
+ g_assert (ii < G_N_ELEMENTS (argv));
+
+ exit_code = spam_assassin_command (
+ argv, NULL, NULL, cancellable, error);
+
+ /* Check that the return value and GError agree. */
+ if (exit_code == SPAM_ASSASSIN_EXIT_STATUS_SUCCESS)
+ g_warn_if_fail (error == NULL || *error == NULL);
+ else
+ g_warn_if_fail (error == NULL || *error != NULL);
+
+ return (exit_code == SPAM_ASSASSIN_EXIT_STATUS_SUCCESS);
+}
+
+static void
+e_spam_assassin_class_init (ESpamAssassinClass *class)
+{
+ GObjectClass *object_class;
+ EMailJunkFilterClass *junk_filter_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = spam_assassin_set_property;
+ object_class->get_property = spam_assassin_get_property;
+ object_class->finalize = spam_assassin_finalize;
+
+ junk_filter_class = E_MAIL_JUNK_FILTER_CLASS (class);
+ junk_filter_class->filter_name = "SpamAssassin";
+ junk_filter_class->display_name = _("SpamAssassin");
+ junk_filter_class->available = spam_assassin_available;
+ junk_filter_class->new_config_widget = spam_assassin_new_config_widget;
+
+ /* XXX Argh, the boolean sense of the GConf key is inverted from
+ * that of the checkbox widget. The checkbox wording is more
+ * natural, but GConfBridge doesn't support transform functions
+ * so the property has to match the sense of the GConf key. */
+ g_object_class_install_property (
+ object_class,
+ PROP_LOCAL_ONLY,
+ g_param_spec_boolean (
+ "local-only",
+ "Local Only",
+ "Do not use tests requiring DNS lookups",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SPAMC_BINARY,
+ g_param_spec_string (
+ "spamc-binary",
+ "spamc Binary",
+ "File path for the spamc binary",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SPAMD_BINARY,
+ g_param_spec_string (
+ "spamd-binary",
+ "spamd Binary",
+ "File path for the spamd binary",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SOCKET_PATH,
+ g_param_spec_string (
+ "socket-path",
+ "Socket Path",
+ "Socket path for a SpamAssassin daemon",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_USE_DAEMON,
+ g_param_spec_boolean (
+ "use-daemon",
+ "Use Daemon",
+ "Whether to use a SpamAssassin daemon",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_spam_assassin_class_finalize (ESpamAssassinClass *class)
+{
+}
+
+static void
+e_spam_assassin_interface_init (CamelJunkFilterInterface *interface)
+{
+ interface->classify = spam_assassin_classify;
+ interface->learn_junk = spam_assassin_learn_junk;
+ interface->learn_not_junk = spam_assassin_learn_not_junk;
+ interface->synchronize = spam_assassin_synchronize;
+}
+
+static void
+e_spam_assassin_init (ESpamAssassin *extension)
+{
+ extension->socket_path_mutex = g_mutex_new ();
+
+ /* XXX Once we move to GSettings these probably don't
+ * need to be properties anymore. GConfBridge is
+ * just easier to deal with than GConfClient. */
+
+ gconf_bridge_bind_property (
+ gconf_bridge_get (),
+ "/apps/evolution/mail/junk/sa/local_only",
+ G_OBJECT (extension), "local-only");
+
+ gconf_bridge_bind_property (
+ gconf_bridge_get (),
+ "/apps/evolution/mail/junk/sa/spamc_binary",
+ G_OBJECT (extension), "spamc-binary");
+
+ gconf_bridge_bind_property (
+ gconf_bridge_get (),
+ "/apps/evolution/mail/junk/sa/spamd_binary",
+ G_OBJECT (extension), "spamd-binary");
+
+ gconf_bridge_bind_property (
+ gconf_bridge_get (),
+ "/apps/evolution/mail/junk/sa/socket_path",
+ G_OBJECT (extension), "socket-path");
+
+ gconf_bridge_bind_property (
+ gconf_bridge_get (),
+ "/apps/evolution/mail/junk/sa/use_daemon",
+ G_OBJECT (extension), "use-daemon");
+
+ if (extension->spamc_binary == NULL)
+ extension->spamc_binary = g_strdup (SPAMC_BINARY);
+
+ if (extension->spamd_binary == NULL)
+ extension->spamd_binary = g_strdup (SPAMD_BINARY);
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_spam_assassin_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/spamassassin/evolution-spamassassin.schemas.in b/modules/spamassassin/evolution-spamassassin.schemas.in
new file mode 100644
index 0000000000..b344d1bffd
--- /dev/null
+++ b/modules/spamassassin/evolution-spamassassin.schemas.in
@@ -0,0 +1,33 @@
+<gconfschemafile>
+ <schemalist>
+
+ <schema>
+ <key>/schemas/apps/evolution/mail/junk/sa/local_only</key>
+ <applyto>/apps/evolution/mail/junk/sa/local_only</applyto>
+ <owner>evolution-spamassassin</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Use only local spam tests.</short>
+ <long>
+ Use only the local spam tests (no DNS).
+ </long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/evolution/mail/junk/sa/use_daemon</key>
+ <applyto>/apps/evolution/mail/junk/sa/use_daemon</applyto>
+ <owner>evolution-spamassassin</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Use SpamAssassin daemon and client</short>
+ <long>
+ Use SpamAssassin daemon and client (spamc/spamd).
+ </long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</gconfschemafile>