From c238fbfd1525aa282673abdc435a7f9e4a7f7f3e Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Tue, 12 Jul 2011 07:06:12 -0400 Subject: Convert junk filtering EPlugins to EExtensions. We now have a proper junk mail filtering API. All junk filtering extensions must subclass EMailJunkFilter for user preferences and availability testing, and implement the CamelJunkFilter interface for the actual junk filtering and learning operations. The bogofilter module should be feature-equivalent to its former EPlugin. The spamassassin module is far more complex. It's nearly feature-equivalent to its former EPlugin, but I ditched the spamd respawning code since it seemed unnecessary for a mail client to have to deal with. If there's a huge outcry from users about it I'll reluctantly put it back, but I don't expect one. This gets us a step closer to killing off EConfig, and eventually the EPlugin framework itself. --- configure.ac | 12 +- mail/Makefile.am | 6 +- mail/e-mail-junk-filter.c | 82 + mail/e-mail-junk-filter.h | 74 + mail/e-mail-junk-options.c | 372 + mail/e-mail-junk-options.h | 67 + mail/e-mail-session.c | 273 +- mail/e-mail-session.h | 6 +- mail/em-junk.c | 35 - mail/em-junk.h | 65 - mail/evolution-mail.schemas.in | 28 - mail/mail-config.ui | 7319 +++++++++++--------- maint/evolution.xml | 5 +- modules/Makefile.am | 2 + modules/bogofilter/Makefile.am | 57 + modules/bogofilter/evolution-bogofilter.c | 524 ++ modules/bogofilter/evolution-bogofilter.schemas.in | 20 + modules/mail/Makefile.am | 2 - modules/mail/e-mail-junk-hook.c | 344 - modules/mail/e-mail-junk-hook.h | 66 - modules/mail/em-mailer-prefs.c | 164 +- modules/mail/em-mailer-prefs.h | 6 - modules/mail/evolution-module-mail.c | 2 - modules/spamassassin/Makefile.am | 56 + modules/spamassassin/evolution-spamassassin.c | 1177 ++++ .../spamassassin/evolution-spamassassin.schemas.in | 33 + plugins/bogo-junk-plugin/Makefile.am | 67 - plugins/bogo-junk-plugin/README | 35 - plugins/bogo-junk-plugin/bf-junk-filter.c | 420 -- .../bogo-junk-plugin/bogo-junk-plugin.schemas.in | 18 - .../org-gnome-bogo-junk-plugin.eplug.xml | 26 - plugins/sa-junk-plugin/Makefile.am | 31 - plugins/sa-junk-plugin/em-junk-filter.c | 966 --- .../org-gnome-sa-junk-plugin.eplug.xml | 24 - 34 files changed, 6590 insertions(+), 5794 deletions(-) create mode 100644 mail/e-mail-junk-filter.c create mode 100644 mail/e-mail-junk-filter.h create mode 100644 mail/e-mail-junk-options.c create mode 100644 mail/e-mail-junk-options.h delete mode 100644 mail/em-junk.c delete mode 100644 mail/em-junk.h create mode 100644 modules/bogofilter/Makefile.am create mode 100644 modules/bogofilter/evolution-bogofilter.c create mode 100644 modules/bogofilter/evolution-bogofilter.schemas.in delete mode 100644 modules/mail/e-mail-junk-hook.c delete mode 100644 modules/mail/e-mail-junk-hook.h create mode 100644 modules/spamassassin/Makefile.am create mode 100644 modules/spamassassin/evolution-spamassassin.c create mode 100644 modules/spamassassin/evolution-spamassassin.schemas.in delete mode 100644 plugins/bogo-junk-plugin/Makefile.am delete mode 100644 plugins/bogo-junk-plugin/README delete mode 100644 plugins/bogo-junk-plugin/bf-junk-filter.c delete mode 100644 plugins/bogo-junk-plugin/bogo-junk-plugin.schemas.in delete mode 100644 plugins/bogo-junk-plugin/org-gnome-bogo-junk-plugin.eplug.xml delete mode 100644 plugins/sa-junk-plugin/Makefile.am delete mode 100644 plugins/sa-junk-plugin/em-junk-filter.c delete mode 100644 plugins/sa-junk-plugin/org-gnome-sa-junk-plugin.eplug.xml diff --git a/configure.ac b/configure.ac index 474d20b449..8ba00a265e 100644 --- a/configure.ac +++ b/configure.ac @@ -198,8 +198,6 @@ case "$host" in os_win32=yes NO_UNDEFINED='-no-undefined' SOEXT='.dll' - SA_JUNK_PLUGIN='' - BF_JUNK_PLUGIN='' DL_LIB='' SOFTOKN3_LIB='' CHAMPLAIN_REQUIREMENT='' @@ -218,8 +216,6 @@ case "$host" in os_win32=no NO_UNDEFINED='-no-undefined' SOEXT='.so' - SA_JUNK_PLUGIN=sa-junk-plugin - BF_JUNK_PLUGIN=bogo-junk-plugin DL_LIB='-ldl' SOFTOKN3_LIB='-lsoftokn3' ;; @@ -1437,8 +1433,8 @@ AC_ARG_ENABLE([plugins], dnl Add any new plugins here plugins_base_always="calendar-file calendar-http itip-formatter default-source addressbook-file mark-all-read publish-calendar caldav imap-features google-account-setup webdav-account-setup" -plugins_base="$plugins_base_always $SA_JUNK_PLUGIN $BF_JUNK_PLUGIN" -dist_plugins_base="$plugins_base_always calendar-weather sa-junk-plugin bogo-junk-plugin" +plugins_base="$plugins_base_always" +dist_plugins_base="$plugins_base_always calendar-weather" plugins_standard_always="bbdb save-calendar mail-to-task mailing-list-actions prefer-plain mail-notification attachment-reminder backup-restore email-custom-header face templates vcard-inline dbx-import" @@ -1754,6 +1750,7 @@ mail/importers/Makefile maint/Makefile modules/Makefile modules/addressbook/Makefile +modules/bogofilter/Makefile modules/calendar/Makefile modules/mail/Makefile modules/composer-autosave/Makefile @@ -1766,6 +1763,7 @@ modules/plugin-lib/Makefile modules/plugin-manager/Makefile modules/plugin-mono/Makefile modules/plugin-python/Makefile +modules/spamassassin/Makefile modules/startup-wizard/Makefile modules/windows-sens/Makefile plugins/Makefile @@ -1774,7 +1772,6 @@ plugins/attachment-reminder/Makefile plugins/audio-inline/Makefile plugins/backup-restore/Makefile plugins/bbdb/Makefile -plugins/bogo-junk-plugin/Makefile plugins/caldav/Makefile plugins/calendar-file/Makefile plugins/calendar-http/Makefile @@ -1795,7 +1792,6 @@ plugins/mark-all-read/Makefile plugins/prefer-plain/Makefile plugins/pst-import/Makefile plugins/publish-calendar/Makefile -plugins/sa-junk-plugin/Makefile plugins/save-calendar/Makefile plugins/templates/Makefile plugins/tnef-attachments/Makefile diff --git a/mail/Makefile.am b/mail/Makefile.am index 520e6876cd..0d2864d59f 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -56,6 +56,8 @@ mailinclude_HEADERS = \ e-mail-enums.h \ e-mail-enumtypes.h \ e-mail-folder-utils.h \ + e-mail-junk-filter.h \ + e-mail-junk-options.h \ e-mail-label-action.h \ e-mail-label-dialog.h \ e-mail-label-list-store.h \ @@ -91,7 +93,6 @@ mailinclude_HEADERS = \ em-format-html-display.h \ em-format-html-print.h \ em-html-stream.h \ - em-junk.h \ em-search-context.h \ em-subscription-editor.h \ em-sync-stream.h \ @@ -128,6 +129,8 @@ libevolution_mail_la_SOURCES = \ e-mail-display.c \ e-mail-enumtypes.c \ e-mail-folder-utils.c \ + e-mail-junk-filter.c \ + e-mail-junk-options.c \ e-mail-label-action.c \ e-mail-label-dialog.c \ e-mail-label-list-store.c \ @@ -163,7 +166,6 @@ libevolution_mail_la_SOURCES = \ em-format-html-display.c \ em-format-html-print.c \ em-html-stream.c \ - em-junk.c \ em-search-context.c \ em-subscription-editor.c \ em-sync-stream.c \ diff --git a/mail/e-mail-junk-filter.c b/mail/e-mail-junk-filter.c new file mode 100644 index 0000000000..71128013ad --- /dev/null +++ b/mail/e-mail-junk-filter.c @@ -0,0 +1,82 @@ +/* + * e-mail-junk-filter.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 + * + */ + +#include "e-mail-junk-filter.h" + +#include + +G_DEFINE_ABSTRACT_TYPE ( + EMailJunkFilter, + e_mail_junk_filter, + E_TYPE_EXTENSION) + +static void +e_mail_junk_filter_class_init (EMailJunkFilterClass *class) +{ + EExtensionClass *extension_class; + + extension_class = E_EXTENSION_CLASS (class); + extension_class->extensible_type = E_TYPE_MAIL_SESSION; +} + +static void +e_mail_junk_filter_init (EMailJunkFilter *junk_filter) +{ +} + +gboolean +e_mail_junk_filter_available (EMailJunkFilter *junk_filter) +{ + EMailJunkFilterClass *class; + + g_return_val_if_fail (E_IS_MAIL_JUNK_FILTER (junk_filter), FALSE); + + class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter); + g_return_val_if_fail (class->available != NULL, FALSE); + + return class->available (junk_filter); +} + +GtkWidget * +e_mail_junk_filter_new_config_widget (EMailJunkFilter *junk_filter) +{ + EMailJunkFilterClass *class; + GtkWidget *widget = NULL; + + g_return_val_if_fail (E_IS_MAIL_JUNK_FILTER (junk_filter), NULL); + + class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter); + + if (class->new_config_widget != NULL) + widget = class->new_config_widget (junk_filter); + + return widget; +} + +gint +e_mail_junk_filter_compare (EMailJunkFilter *junk_filter_a, + EMailJunkFilter *junk_filter_b) +{ + EMailJunkFilterClass *class_a; + EMailJunkFilterClass *class_b; + + class_a = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter_a); + class_b = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter_b); + + return g_utf8_collate (class_a->display_name, class_b->display_name); +} diff --git a/mail/e-mail-junk-filter.h b/mail/e-mail-junk-filter.h new file mode 100644 index 0000000000..34d95e56c7 --- /dev/null +++ b/mail/e-mail-junk-filter.h @@ -0,0 +1,74 @@ +/* + * e-mail-junk-filter.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 + * + */ + +#ifndef E_MAIL_JUNK_FILTER_H +#define E_MAIL_JUNK_FILTER_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_MAIL_JUNK_FILTER \ + (e_mail_junk_filter_get_type ()) +#define E_MAIL_JUNK_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilter)) +#define E_MAIL_JUNK_FILTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilterClass)) +#define E_IS_MAIL_JUNK_FILTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_JUNK_FILTER)) +#define E_IS_MAIL_JUNK_FILTER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_JUNK_FILTER)) +#define E_MAIL_JUNK_FILTER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_JUNK_FILTER, EMailJunkFilterClass)) + +G_BEGIN_DECLS + +typedef struct _EMailJunkFilter EMailJunkFilter; +typedef struct _EMailJunkFilterClass EMailJunkFilterClass; +typedef struct _EMailJunkFilterPrivate EMailJunkFilterPrivate; + +struct _EMailJunkFilter { + EExtension parent; + EMailJunkFilterPrivate *priv; +}; + +struct _EMailJunkFilterClass { + EExtensionClass parent_class; + + const gchar *filter_name; + const gchar *display_name; + + gboolean (*available) (EMailJunkFilter *junk_filter); + GtkWidget * (*new_config_widget) (EMailJunkFilter *junk_filter); +}; + +GType e_mail_junk_filter_get_type (void) G_GNUC_CONST; +gboolean e_mail_junk_filter_available (EMailJunkFilter *junk_filter); +GtkWidget * e_mail_junk_filter_new_config_widget + (EMailJunkFilter *junk_filter); +gint e_mail_junk_filter_compare (EMailJunkFilter *junk_filter_a, + EMailJunkFilter *junk_filter_b); + +G_END_DECLS + +#endif /* E_MAIL_JUNK_FILTER_H */ diff --git a/mail/e-mail-junk-options.c b/mail/e-mail-junk-options.c new file mode 100644 index 0000000000..d57e1d6dc6 --- /dev/null +++ b/mail/e-mail-junk-options.c @@ -0,0 +1,372 @@ +/* + * e-mail-junk-options.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 + * + */ + +#include "e-mail-junk-options.h" + +#include +#include + +#include + +#define E_MAIL_JUNK_OPTIONS_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_JUNK_OPTIONS, EMailJunkOptionsPrivate)) + +G_DEFINE_TYPE ( + EMailJunkOptions, + e_mail_junk_options, + GTK_TYPE_GRID) + +struct _EMailJunkOptionsPrivate { + EMailSession *session; + + GtkWidget *label; /* not referenced */ + GtkWidget *combo_box; /* not referenced */ + GtkWidget *option_box; /* not referenced */ + GPtrArray *widgets; /* not referenced */ + + GBinding *active_id_binding; +}; + +enum { + PROP_0, + PROP_SESSION +}; + +enum { + COLUMN_FILTER_NAME, + COLUMN_DISPLAY_NAME +}; + +static void +mail_junk_options_combo_box_changed_cb (GtkComboBox *combo_box, + EMailJunkOptions *options) +{ + GPtrArray *array; + gint active; + guint ii; + + array = options->priv->widgets; + active = gtk_combo_box_get_active (combo_box); + + for (ii = 0; ii < array->len; ii++) { + GtkWidget *widget = GTK_WIDGET (array->pdata[ii]); + gtk_widget_set_visible (widget, ii == active); + } +} + +static void +mail_junk_options_rebuild (EMailJunkOptions *options) +{ + EMailSession *session; + GtkComboBox *combo_box; + GtkTreeModel *model; + GtkBox *option_box; + GList *list = NULL; + GList *link; + guint n_filters; + + session = e_mail_junk_options_get_session (options); + combo_box = GTK_COMBO_BOX (options->priv->combo_box); + option_box = GTK_BOX (options->priv->option_box); + + /* Remove the GtkComboBox:active-id binding so it doesn't + * affect EMailSession:junk-filter-name when we clear the + * combo box's list model. */ + if (options->priv->active_id_binding != NULL) { + g_object_unref (options->priv->active_id_binding); + options->priv->active_id_binding = NULL; + } + + model = gtk_combo_box_get_model (combo_box); + gtk_list_store_clear (GTK_LIST_STORE (model)); + + g_ptr_array_foreach ( + options->priv->widgets, + (GFunc) gtk_widget_destroy, NULL); + g_ptr_array_set_size (options->priv->widgets, 0); + + if (session != NULL) + list = e_mail_session_get_available_junk_filters (session); + + for (link = list; link != NULL; link = g_list_next (link)) { + EMailJunkFilter *junk_filter; + EMailJunkFilterClass *class; + GtkWidget *widget; + GtkTreeIter iter; + + junk_filter = E_MAIL_JUNK_FILTER (link->data); + class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter); + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + + gtk_list_store_set ( + GTK_LIST_STORE (model), &iter, + COLUMN_FILTER_NAME, class->filter_name, + COLUMN_DISPLAY_NAME, class->display_name, + -1); + + /* Create a configuration widget for this junk filter, + * or else just create an empty placeholder widget. */ + widget = e_mail_junk_filter_new_config_widget (junk_filter); + if (widget == NULL) + widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + g_ptr_array_add (options->priv->widgets, widget); + + /* Set extra padding to 12px, since only one child of + * 'option_box' is visible at a time, and we still want + * the extra padding if the first grid row is invisible. */ + gtk_box_pack_start (option_box, widget, FALSE, FALSE, 12); + } + + /* Synchronize the combo box with the active junk filter. */ + if (session != NULL) { + GBinding *binding; + + binding = g_object_bind_property ( + session, "junk-filter-name", + combo_box, "active-id", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + options->priv->active_id_binding = binding; + } + + /* Select the first combo box item if we need to. If there's + * no first item to select, this will silently do nothing. */ + if (gtk_combo_box_get_active (combo_box) < 0) + gtk_combo_box_set_active (combo_box, 0); + + /* Update visibility of widgets. */ + n_filters = g_list_length (list); + gtk_widget_set_visible (GTK_WIDGET (options), n_filters > 0); + gtk_widget_set_visible (options->priv->label, n_filters > 1); + gtk_widget_set_visible (options->priv->combo_box, n_filters > 1); + + g_list_free (list); +} + +static void +mail_junk_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SESSION: + e_mail_junk_options_set_session ( + E_MAIL_JUNK_OPTIONS (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_junk_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SESSION: + g_value_set_object ( + value, + e_mail_junk_options_get_session ( + E_MAIL_JUNK_OPTIONS (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_junk_options_dispose (GObject *object) +{ + EMailJunkOptionsPrivate *priv; + + priv = E_MAIL_JUNK_OPTIONS_GET_PRIVATE (object); + + if (priv->session != NULL) { + g_object_unref (priv->session); + priv->session = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_mail_junk_options_parent_class)->dispose (object); +} + +static void +mail_junk_options_finalize (GObject *object) +{ + EMailJunkOptionsPrivate *priv; + + priv = E_MAIL_JUNK_OPTIONS_GET_PRIVATE (object); + + g_ptr_array_free (priv->widgets, TRUE); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_junk_options_parent_class)->finalize (object); +} + +static void +mail_junk_options_constructed (GObject *object) +{ + EMailJunkOptionsPrivate *priv; + GtkCellRenderer *cell_renderer; + GtkCellLayout *cell_layout; + GtkListStore *list_store; + GtkWidget *widget; + + priv = E_MAIL_JUNK_OPTIONS_GET_PRIVATE (object); + + /* XXX The margins we're using here are tailored to its + * placement in the Junk tab of Mail Preferences. + * EMailJunkOptions is not really reusable as is. */ + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_junk_options_parent_class)->constructed (object); + + gtk_grid_set_column_spacing (GTK_GRID (object), 6); + + list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + + /* Label + combo box has a 12px left margin so it's + * aligned with the junk mail options above it. */ + widget = gtk_label_new (_("Junk filtering software:")); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); + gtk_widget_set_margin_left (widget, 12); + gtk_grid_attach (GTK_GRID (object), widget, 0, 0, 1, 1); + priv->label = widget; /* not referenced */ + gtk_widget_show (widget); + + widget = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store)); + gtk_combo_box_set_id_column ( + GTK_COMBO_BOX (widget), COLUMN_FILTER_NAME); + gtk_grid_attach (GTK_GRID (object), widget, 1, 0, 1, 1); + priv->combo_box = widget; /* not referenced */ + gtk_widget_show (widget); + + g_signal_connect ( + widget, "changed", + G_CALLBACK (mail_junk_options_combo_box_changed_cb), object); + + /* The config widgets that come from EMailJunkFilter have no + * left margin, since they usually include a bold header and + * interactive widgets with their own left margin. */ + widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_grid_attach (GTK_GRID (object), widget, 0, 1, 2, 1); + priv->option_box = widget; /* not referenced */ + gtk_widget_show (widget); + + cell_layout = GTK_CELL_LAYOUT (priv->combo_box); + cell_renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (cell_layout, cell_renderer, FALSE); + + gtk_cell_layout_add_attribute ( + cell_layout, cell_renderer, + "text", COLUMN_DISPLAY_NAME); + + g_object_unref (list_store); +} + +static void +mail_junk_options_map (GtkWidget *widget) +{ + /* Chain up to parent's map() method. */ + GTK_WIDGET_CLASS (e_mail_junk_options_parent_class)->map (widget); + + mail_junk_options_rebuild (E_MAIL_JUNK_OPTIONS (widget)); +} + +static void +e_mail_junk_options_class_init (EMailJunkOptionsClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + g_type_class_add_private (class, sizeof (EMailJunkOptionsPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_junk_options_set_property; + object_class->get_property = mail_junk_options_get_property; + object_class->dispose = mail_junk_options_dispose; + object_class->finalize = mail_junk_options_finalize; + object_class->constructed = mail_junk_options_constructed; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->map = mail_junk_options_map; + + g_object_class_install_property ( + object_class, + PROP_SESSION, + g_param_spec_object ( + "session", + NULL, + NULL, + E_TYPE_MAIL_SESSION, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_mail_junk_options_init (EMailJunkOptions *options) +{ + options->priv = E_MAIL_JUNK_OPTIONS_GET_PRIVATE (options); + + options->priv->widgets = g_ptr_array_new (); +} + +GtkWidget * +e_mail_junk_options_new (EMailSession *session) +{ + g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL); + + return g_object_new ( + E_TYPE_MAIL_JUNK_OPTIONS, "session", session, NULL); +} + +EMailSession * +e_mail_junk_options_get_session (EMailJunkOptions *options) +{ + g_return_val_if_fail (E_IS_MAIL_JUNK_OPTIONS (options), NULL); + + return options->priv->session; +} + +void +e_mail_junk_options_set_session (EMailJunkOptions *options, + EMailSession *session) +{ + g_return_if_fail (E_IS_MAIL_JUNK_OPTIONS (options)); + + if (session != NULL) { + g_return_if_fail (E_IS_MAIL_SESSION (session)); + g_object_ref (session); + } + + if (options->priv->session != NULL) + g_object_unref (options->priv->session); + + options->priv->session = session; + + g_object_notify (G_OBJECT (options), "session"); + + mail_junk_options_rebuild (options); +} diff --git a/mail/e-mail-junk-options.h b/mail/e-mail-junk-options.h new file mode 100644 index 0000000000..5e2c99faf7 --- /dev/null +++ b/mail/e-mail-junk-options.h @@ -0,0 +1,67 @@ +/* + * e-mail-junk-options.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 + * + */ + +#ifndef E_MAIL_JUNK_OPTIONS_H +#define E_MAIL_JUNK_OPTIONS_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_MAIL_JUNK_OPTIONS \ + (e_mail_junk_options_get_type ()) +#define E_MAIL_JUNK_OPTIONS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_JUNK_OPTIONS, EMailJunkOptions)) +#define E_MAIL_JUNK_OPTIONS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_JUNK_OPTIONS, EMailJunkOptionsClass)) +#define E_IS_MAIL_JUNK_OPTIONS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_JUNK_OPTIONS)) +#define E_IS_MAIL_JUNK_OPTIONS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_JUNK_OPTIONS)) +#define E_MAIL_JUNK_OPTIONS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_JUNK_OPTIONS, EMailJunkOptionsClass)) + +G_BEGIN_DECLS + +typedef struct _EMailJunkOptions EMailJunkOptions; +typedef struct _EMailJunkOptionsClass EMailJunkOptionsClass; +typedef struct _EMailJunkOptionsPrivate EMailJunkOptionsPrivate; + +struct _EMailJunkOptions { + GtkGrid parent; + EMailJunkOptionsPrivate *priv; +}; + +struct _EMailJunkOptionsClass { + GtkGridClass parent_class; +}; + +GType e_mail_junk_options_get_type (void); +GtkWidget * e_mail_junk_options_new (EMailSession *session); +EMailSession * e_mail_junk_options_get_session (EMailJunkOptions *options); +void e_mail_junk_options_set_session (EMailJunkOptions *options, + EMailSession *session); + +G_END_DECLS + +#endif /* E_MAIL_JUNK_OPTIONS_H */ diff --git a/mail/e-mail-session.c b/mail/e-mail-session.c index 44bbd8d0aa..8a3072447a 100644 --- a/mail/e-mail-session.c +++ b/mail/e-mail-session.c @@ -50,9 +50,12 @@ #include "e-util/e-util.h" #include "e-util/e-account-utils.h" #include "e-util/e-alert-dialog.h" +#include "e-util/e-extensible.h" #include "e-util/e-util-private.h" +#include "e-util/gconf-bridge.h" #include "e-mail-folder-utils.h" +#include "e-mail-junk-filter.h" #include "e-mail-local.h" #include "e-mail-session.h" #include "em-composer-utils.h" @@ -78,7 +81,7 @@ struct _EMailSessionPrivate { MailFolderCache *folder_cache; FILE *filter_logfile; - GList *junk_plugins; + GHashTable *junk_filters; }; struct _AsyncContext { @@ -93,7 +96,8 @@ struct _AsyncContext { enum { PROP_0, - PROP_FOLDER_CACHE + PROP_FOLDER_CACHE, + PROP_JUNK_FILTER_NAME }; static gchar *mail_data_dir; @@ -103,10 +107,11 @@ static gchar *mail_config_dir; static MailMsgInfo ms_thread_info_dummy = { sizeof (MailMsg) }; #endif -G_DEFINE_TYPE ( +G_DEFINE_TYPE_WITH_CODE ( EMailSession, e_mail_session, - CAMEL_TYPE_SESSION) + CAMEL_TYPE_SESSION, + G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)) /* Support for CamelSession.alert_user() *************************************/ @@ -546,6 +551,84 @@ mail_session_check_junk_notify (GConfClient *gconf, } } +static const gchar * +mail_session_get_junk_filter_name (EMailSession *session) +{ + CamelJunkFilter *junk_filter; + GHashTableIter iter; + gpointer key, value; + + /* XXX This property can be removed once Evolution moves to + * GSettings and can use transform functions when binding + * properties to settings. That's why this is private. */ + + g_hash_table_iter_init (&iter, session->priv->junk_filters); + junk_filter = camel_session_get_junk_filter (CAMEL_SESSION (session)); + + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (junk_filter == CAMEL_JUNK_FILTER (value)) + return (const gchar *) key; + } + + if (junk_filter != NULL) + g_warning ( + "Camel is using a junk filter " + "unknown to Evolution of type %s", + G_OBJECT_TYPE_NAME (junk_filter)); + + return ""; /* GConfBridge doesn't like NULL strings */ +} + +static void +mail_session_set_junk_filter_name (EMailSession *session, + const gchar *junk_filter_name) +{ + CamelJunkFilter *junk_filter = NULL; + + /* XXX This property can be removed once Evolution moves to + * GSettings and can use transform functions when binding + * properties to settings. That's why this is private. */ + + /* An empty string is equivalent to a NULL string. */ + if (junk_filter_name != NULL && *junk_filter_name == '\0') + junk_filter_name = NULL; + + if (junk_filter_name != NULL) { + junk_filter = g_hash_table_lookup ( + session->priv->junk_filters, junk_filter_name); + if (junk_filter != NULL) { + if (!e_mail_junk_filter_available ( + E_MAIL_JUNK_FILTER (junk_filter))) + junk_filter = NULL; + } else { + g_warning ( + "Unrecognized junk filter name " + "'%s' in GConf", junk_filter_name); + } + } + + camel_session_set_junk_filter (CAMEL_SESSION (session), junk_filter); + + /* XXX We emit the "notify" signal in mail_session_notify(). */ +} + +static void +mail_session_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_JUNK_FILTER_NAME: + mail_session_set_junk_filter_name ( + E_MAIL_SESSION (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + static void mail_session_get_property (GObject *object, guint property_id, @@ -559,6 +642,13 @@ mail_session_get_property (GObject *object, e_mail_session_get_folder_cache ( E_MAIL_SESSION (object))); return; + + case PROP_JUNK_FILTER_NAME: + g_value_set_string ( + value, + mail_session_get_junk_filter_name ( + E_MAIL_SESSION (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -583,8 +673,13 @@ mail_session_dispose (GObject *object) static void mail_session_finalize (GObject *object) { + EMailSessionPrivate *priv; GConfClient *client; + priv = E_MAIL_SESSION_GET_PRIVATE (object); + + g_hash_table_destroy (priv->junk_filters); + client = gconf_client_get_default (); if (session_check_junk_notify_id != 0) { @@ -606,6 +701,87 @@ mail_session_finalize (GObject *object) G_OBJECT_CLASS (e_mail_session_parent_class)->finalize (object); } +static void +mail_session_notify (GObject *object, + GParamSpec *pspec) +{ + /* GObject does not implement this method; do not chain up. */ + + /* XXX Delete this once Evolution moves to GSettings and + * we're able to get rid of PROP_JUNK_FILTER_NAME. */ + if (g_strcmp0 (pspec->name, "junk-filter") == 0) + g_object_notify (object, "junk-filter-name"); +} + +static void +mail_session_constructed (GObject *object) +{ + EMailSessionPrivate *priv; + EExtensible *extensible; + GType extension_type; + GList *list, *iter; + + priv = E_MAIL_SESSION_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_session_parent_class)->constructed (object); + + extensible = E_EXTENSIBLE (object); + e_extensible_load_extensions (extensible); + + /* Add junk filter extensions to an internal hash table. */ + + extension_type = E_TYPE_MAIL_JUNK_FILTER; + list = e_extensible_list_extensions (extensible, extension_type); + + for (iter = list; iter != NULL; iter = g_list_next (iter)) { + EMailJunkFilter *junk_filter; + EMailJunkFilterClass *class; + + junk_filter = E_MAIL_JUNK_FILTER (iter->data); + class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter); + + if (!CAMEL_IS_JUNK_FILTER (junk_filter)) { + g_warning ( + "Skipping %s: Does not implement " + "CamelJunkFilterInterface", + G_OBJECT_TYPE_NAME (junk_filter)); + continue; + } + + if (class->filter_name == NULL) { + g_warning ( + "Skipping %s: filter_name unset", + G_OBJECT_TYPE_NAME (junk_filter)); + continue; + } + + if (class->display_name == NULL) { + g_warning ( + "Skipping %s: display_name unset", + G_OBJECT_TYPE_NAME (junk_filter)); + continue; + } + + /* No need to reference the EMailJunkFilter since + * EMailSession owns the reference to it already. */ + g_hash_table_insert ( + priv->junk_filters, + (gpointer) class->filter_name, + junk_filter); + } + + g_list_free (list); + + /* Bind the "/apps/evolution/mail/junk/default_plugin" + * GConf key to our "junk-filter-name" property. */ + + gconf_bridge_bind_property ( + gconf_bridge_get (), + "/apps/evolution/mail/junk/default_plugin", + object, "junk-filter-name"); +} + static gchar * mail_session_get_password (CamelSession *session, CamelService *service, @@ -928,9 +1104,12 @@ e_mail_session_class_init (EMailSessionClass *class) g_type_class_add_private (class, sizeof (EMailSessionPrivate)); object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_session_set_property; object_class->get_property = mail_session_get_property; object_class->dispose = mail_session_dispose; object_class->finalize = mail_session_finalize; + object_class->notify = mail_session_notify; + object_class->constructed = mail_session_constructed; session_class = CAMEL_SESSION_CLASS (class); session_class->get_password = mail_session_get_password; @@ -948,7 +1127,22 @@ e_mail_session_class_init (EMailSessionClass *class) NULL, NULL, MAIL_TYPE_FOLDER_CACHE, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /* XXX This property can be removed once Evolution moves to + * GSettings and can use transform functions when binding + * properties to settings. */ + g_object_class_install_property ( + object_class, + PROP_JUNK_FILTER_NAME, + g_param_spec_string ( + "junk-filter-name", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } static void @@ -958,6 +1152,8 @@ e_mail_session_init (EMailSession *session) session->priv = E_MAIL_SESSION_GET_PRIVATE (session); session->priv->folder_cache = mail_folder_cache_new (); + session->priv->junk_filters = g_hash_table_new ( + (GHashFunc) g_str_hash, (GEqualFunc) g_str_equal); /* Initialize the EAccount setup. */ e_account_writable (NULL, E_ACCOUNT_SOURCE_SAVE_PASSWD); @@ -974,7 +1170,6 @@ e_mail_session_init (EMailSession *session) client, "/apps/evolution/mail/junk", (GConfClientNotifyFunc) mail_session_check_junk_notify, session, NULL, NULL); - CAMEL_SESSION (session)->junk_plugin = NULL; mail_config_reload_junk_headers (session); @@ -1003,6 +1198,36 @@ e_mail_session_get_folder_cache (EMailSession *session) return session->priv->folder_cache; } +GList * +e_mail_session_get_available_junk_filters (EMailSession *session) +{ + GList *list, *link; + GQueue trash = G_QUEUE_INIT; + + g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL); + + list = g_hash_table_get_values (session->priv->junk_filters); + + /* Discard unavailable junk filters. (e.g. Junk filter + * requires Bogofilter but Bogofilter is not installed, + * hence the junk filter is unavailable.) */ + + for (link = list; link != NULL; link = g_list_next (link)) { + EMailJunkFilter *junk_filter; + + junk_filter = E_MAIL_JUNK_FILTER (link->data); + if (!e_mail_junk_filter_available (junk_filter)) + g_queue_push_tail (&trash, link); + } + + while ((link = g_queue_pop_head (&trash)) != NULL) + list = g_list_delete_link (list, link); + + /* Sort the remaining junk filters by display name. */ + + return g_list_sort (list, (GCompareFunc) e_mail_junk_filter_compare); +} + static void mail_session_get_inbox_thread (GSimpleAsyncResult *simple, EMailSession *session, @@ -1327,42 +1552,6 @@ mail_session_flush_filter_log (EMailSession *session) fflush (session->priv->filter_logfile); } -void -mail_session_add_junk_plugin (EMailSession *session, - const gchar *plugin_name, - CamelJunkPlugin *junk_plugin) -{ - GConfClient *client; - gchar *def_plugin; - const gchar *key; - - g_return_if_fail (E_IS_MAIL_SESSION (session)); - - client = gconf_client_get_default (); - key = "/apps/evolution/mail/junk/default_plugin"; - def_plugin = gconf_client_get_string (client, key, NULL); - g_object_unref (client); - - session->priv->junk_plugins = g_list_append ( - session->priv->junk_plugins, junk_plugin); - if (def_plugin && plugin_name) { - if (!strcmp (def_plugin, plugin_name)) { - CAMEL_SESSION (session)->junk_plugin = junk_plugin; - camel_junk_plugin_init (junk_plugin); - } - } - - g_free (def_plugin); -} - -const GList * -mail_session_get_junk_plugins (EMailSession *session) -{ - g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL); - - return session->priv->junk_plugins; -} - const gchar * mail_session_get_data_dir (void) { diff --git a/mail/e-mail-session.h b/mail/e-mail-session.h index 5cc6b8976e..653404ca7a 100644 --- a/mail/e-mail-session.h +++ b/mail/e-mail-session.h @@ -66,6 +66,8 @@ GType e_mail_session_get_type (void); EMailSession * e_mail_session_new (void); MailFolderCache * e_mail_session_get_folder_cache (EMailSession *session); +GList * e_mail_session_get_available_junk_filters + (EMailSession *session); CamelFolder * e_mail_session_get_inbox_sync (EMailSession *session, const gchar *service_uid, GCancellable *cancellable, @@ -113,10 +115,6 @@ CamelFolder * e_mail_session_uri_to_folder_finish /*** Legacy API ***/ void mail_session_flush_filter_log (EMailSession *session); -void mail_session_add_junk_plugin (EMailSession *session, - const gchar *plugin_name, - CamelJunkPlugin *junk_plugin); -const GList * mail_session_get_junk_plugins (EMailSession *session); const gchar * mail_session_get_data_dir (void); const gchar * mail_session_get_config_dir (void); diff --git a/mail/em-junk.c b/mail/em-junk.c deleted file mode 100644 index 1026e4d76a..0000000000 --- a/mail/em-junk.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * em-junk.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 - * - * - * Authors: - * Vivek Jain - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "em-junk.h" - -GQuark -em_junk_error_quark (void) -{ - return g_quark_from_static_string ("em-junk-error-quark"); -} diff --git a/mail/em-junk.h b/mail/em-junk.h deleted file mode 100644 index 978f5ece7d..0000000000 --- a/mail/em-junk.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * em-junk.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 - * - * - * Authors: - * Vivek Jain - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef EM_JUNK_H -#define EM_JUNK_H - -#include -#include - -#define EM_JUNK_ERROR (em_junk_error_quark ()) - -G_BEGIN_DECLS - -typedef struct _EMJunkTarget EMJunkTarget; -typedef struct _EMJunkInterface EMJunkInterface; - -typedef void (*EMJunkHookFunc) (EPlugin *plugin, EMJunkTarget *data); - -struct _EMJunkTarget { - CamelMimeMessage *m; - GError *error; -}; - -struct _EMJunkInterface { - CamelJunkPlugin camel; - - /* The hook forwards calls from Camel to the EPlugin. */ - EPluginHook *hook; - - /* These are symbol names in the EPlugin. */ - gchar *check_junk; - gchar *report_junk; - gchar *report_notjunk; - gchar *commit_reports; - gchar *validate_binary; - - gchar *plugin_name; -}; - -GQuark em_junk_error_quark (void); - -G_END_DECLS - -#endif /* EM_JUNK_H */ diff --git a/mail/evolution-mail.schemas.in b/mail/evolution-mail.schemas.in index cb697b5aff..3261788016 100644 --- a/mail/evolution-mail.schemas.in +++ b/mail/evolution-mail.schemas.in @@ -1433,34 +1433,6 @@ - - /schemas/apps/evolution/mail/junk/sa/local_only - /apps/evolution/mail/junk/sa/local_only - evolution-mail - bool - true - - Use only local spam tests. - - Use only the local spam tests (no DNS). - - - - - - /schemas/apps/evolution/mail/junk/sa/use_daemon - /apps/evolution/mail/junk/sa/use_daemon - evolution-mail - bool - true - - Use SpamAssassin daemon and client - - Use SpamAssassin daemon and client (spamc/spamd). - - - - /schemas/apps/evolution/mail/junk/lookup_addressbook /apps/evolution/mail/junk/lookup_addressbook diff --git a/mail/mail-config.ui b/mail/mail-config.ui index d2e5d8dd34..213f397660 100644 --- a/mail/mail-config.ui +++ b/mail/mail-config.ui @@ -1,798 +1,463 @@ - + - - - - 1.5 - 10 - 1 - 1 - - - 30000 - 1 - 10 - - - 5 - 1 - 100 - 1 - 10 - - - 65535 - 1 - 10 - - - 65535 - 1 - 10 - - - - - - - - - a - - - b - - - - - - - - - - - a - - - b - - - - - - - - - - - Attachment - - - Inline (Outlook style) - - - Quoted - - - Do not quote - - - - - - - - - - - Attachment - - - Inline - - - Quoted - - - - - - - - - - - Default - - - SHA1 - - - SHA256 - - - SHA384 - - - SHA512 - - - - - True + + + 400 + False 12 - vertical - 12 - - + Set custom junk header + False + dialog + + True + False vertical - 6 - - + 12 + + True - 0 - Account Information - - - + False + end + + + gtk-cancel + True + True + False + False + True + True + + + False + False + 0 + + + + + gtk-ok + True + True + False + False + True + True + + + False + False + 1 + + False - False + True + end 0 - + True - 12 - - - True - 6 - 2 - - - True - vertical - 6 - - - True - 0 - Type the name by which you would like to refer to this account. -For example: "Work" or "Personal" - True - - - False - 0 - - - - - True - 12 - - - True - _Name: - True - right - management_name - - - False - False - 0 - - - - - True - True - - - - 1 - - - - - False - 1 - - - - - - + False + All new emails with header that matches given content will be automatically filtered as junk + True + False + True 1 - - - False - False - 0 - - - - - True - vertical - 6 - + True - 0 - Required Information - - - - - - False - False - 0 - - - - - True - 12 + False + 2 + 2 + 12 + 6 - + True - 2 - 2 - 12 - 6 - - - True - True - - - - - - - - 1 - 2 - 1 - 2 - - - - - - True - 0 - Email _Address: - True - identity_address - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - Full Nam_e: - True - identity_full_name - - - GTK_FILL - - - - - - True - True - - - - - - - - 1 - 2 - - - + False + 0 + Header name + junk-header-name + + + GTK_SHRINK + + + + + True + False + 0 + Header content + junk-header-content + + + 1 + 2 + GTK_SHRINK + + + + + True + True + + + 1 + 2 + + + + + True + True + + 1 + 2 + 1 + 2 + - 1 + False + True + 2 - - False - False - 1 - - - + + junk-header-cancel + junk-header-ok + + + + False + normal + + True + False vertical - 6 - - + 12 + + True - 0 - Optional Information - - - + False + end + + + True + True + True + False + False + + + True + False + 0 + 0 + + + True + False + 2 + + + True + False + gtk-add + + + False + False + 0 + + + + + True + False + _Add Signature + True + + + False + False + 1 + + + + + + + + + False + False + 0 + + + + + gtk-cancel + True + True + True + False + False + True + + + False + False + 1 + + False - False + True + end 0 - + True - 12 + False - + True - 4 - 2 - 12 - 6 - - - True - 0 - Signat_ure: - True - signature_dropdown - - - 3 - 4 - GTK_FILL - - - + False + 12 + 6 - + True + False 6 - + True + False + 0 + gtk-dialog-info + 6 - False - False + True + True 0 - - Add Ne_w Signature... + True - True - True - True - + False + 0 + The output of this script will be used as your +signature. The name you specify will be used +for display purposes only. + True False False - end 1 - 1 - 2 - 3 - 4 - GTK_FILL - GTK_FILL - - - - - True - True - - - - - - - - 1 - 2 - 2 - 3 - - - - - - True - 0 - Or_ganization: - True - identity_organization - - - 2 - 3 - GTK_FILL - - - - - - True - True - - - - - - - - 1 - 2 - 1 - 2 - - - - - - True - 0 - Re_ply-To: - True - center - identity_reply_to - - - 1 - 2 - GTK_FILL - + False + False + 0 - - _Make this my default account + True - True - False - True - True + False + 2 + 2 + 6 + 6 + + + True + False + 0 + _Name: + True + center + entry_add_script_name + + + GTK_FILL + + + + + + True + False + 0 + _Script: + True + center + filechooserbutton_add_script + + + 1 + 2 + GTK_FILL + + + + + + True + True + + + 1 + 2 + + + + + + True + False + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + - 2 - GTK_FILL - GTK_FILL + True + True + 1 + + False + False + 0 + + False + True 1 - - False - False - 2 - + + button_add_script_add + button_add_script_cancel + - + + 10 + 1.5 + 1 + 1 + + + 30000 + 1 + 10 + + + 1 + 100 + 5 + 1 + 10 + + + 65535 + 1 + 10 + + + 65535 + 1 + 10 + + True - 12 - vertical - 12 + True - + True - 2 - 3 - 12 - 6 - - - True - 0 - Server _Type: - True - right - source_type_dropdown - - - GTK_FILL - - - - - - True - 0 - 0 - Description: - center - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - 0 - description - True - - - 1 - 3 - 1 - 2 - GTK_FILL - - - + False + 12 + 6 - + True - - - 1 - 3 - GTK_FILL - - - - - - False - False - 0 - - - - - True - - - False - False - 1 - - - - - True - vertical - 6 - - - True - 0 - Configuration - - - - - - False - False - 0 - - - - - True - 12 + False + 6 - + True - 3 - 4 - 12 - 6 - - - True - 1 - _Server: - True - source_host - - - 0 - 1 - 0 - GTK_FILL - - - - - - True - 1 - _Port: - True - source_port - - - 0 - 1 - 2 - GTK_FILL - - - - - - True - 1 - User _Name: - True - source_user - - - 1 - GTK_FILL - - - - - - True - True - - - - 1 - 0 - - - - - True - True - - - 3 - 0 - - - - - - - True - True - - - - 1 - 4 - 1 - 2 - - - - - - True - 1 - _Path: - True - - - 2 - 3 - GTK_FILL - - - - - - True - Mailbox location - - - 1 - 2 - 2 - 3 - - - + False + 0 + Default Behavior + + + + + False + False + 0 + - - - 1 - - - - - False - False - 2 - - - - - True - vertical - 6 - - - True - 0 - Security - source_auth_dropdown - - - - - - False - False - 0 - - - - - True - 12 - + True - vertical - 6 + False + 12 - + True - 12 + False + 8 - + + Format messages in _HTML True - _Use Secure Connection: + True + False + False True - center - source_use_ssl + True False @@ -801,15 +466,14 @@ For example: "Work" or "Personal" - + + Automatically insert _emoticon images True - use_ssl_model - - - - 0 - - + True + False + False + True + True False @@ -817,79 +481,78 @@ For example: "Work" or "Personal" 1 - - - False - False - 0 - - - - - True - 6 - + + Always request rea_d receipt True - gtk-dialog-warning + True + False + False + True + True False False - 0 + 2 - + + Encode file names in an Outlook/GMail way True - 0 - SSL is not supported in this build of Evolution - center - - - + True + False + False + True + True False False - 1 + 3 - - - 1 - + + + True + False + 6 + + + True + False + 1 + C_haracter set: + True + center + + + False + False + 0 + + + + + + + + False + False + 4 + + + + + False + False + 1 + - - 1 - - - - - False - False - 3 - - - - - True - vertical - 6 - - - True - 0 - _Authentication Type - True - source_auth_dropdown - - - - False False @@ -897,351 +560,488 @@ For example: "Work" or "Personal" - + True - 12 + False + 6 - + True - vertical - 6 + False + 0 + Replies and Forwards + + + + + + False + False + 0 + + + + + True + False + 12 - + True - 6 + False + 6 + 2 + 6 + 6 - + True + False + 1 + _Reply style: + True - False - False - 0 + GTK_FILL + - - Ch_eck for Supported Types + + True + False + 1 + _Forward style: + True + + + 1 + 2 + GTK_FILL + + + + + + Start _typing at the bottom on replying True True - True + False + False True + True - False - False - 1 + 2 + 2 + 3 + + + + + + _Keep signature above the original message on replying + True + True + False + False + True + True + + + 2 + 3 + 4 + + + + + + Ignore Reply-To: for mailing lists + True + True + False + False + True + True + + + 2 + 4 + 5 + + + + + + Group Reply goes only to mailing list, if possible + True + True + False + False + True + True + + + 2 + 5 + 6 + + + + + + True + False + 0 + 0 + + + True + False + model3 + + + + 0 + + + + + + + 1 + 2 + + + + + True + False + 0 + 0 + + + True + False + model4 + + + + 0 + + + + + + + 1 + 2 + 1 + 2 - - 0 - - - - - Re_member password - True - True - False - True - True - - - False - False - 1 - + + False + False + 1 + + False + False 1 - - False - False - 4 - - - - - True - 12 - vertical - 12 - - - True - 2 - 3 - 12 - 6 - - - True - 0 - 0 - Server _Type: - True - right - transport_type_dropdown - - - GTK_FILL - - - - - - True - 0 - 0 - Description: - right - - - 1 - 2 - GTK_FILL - - - - - True - - - 1 - 3 - GTK_FILL - - - - - - True - 0 - 0 - description - True - - - 1 - 3 - 1 - 2 - GTK_FILL - - - - - - False - False - 0 - - - + + True + False + General + True + center - False - False - 1 + False - + True - vertical - 6 + False + 12 + 12 - + True - 0 - Server Configuration - - - + False + 6 + + + True + False + 0 + Sig_natures + True + + + + + + False + False + 0 + + + + + True + False + 12 + + + + + + True + True + 1 + + - False - False + True + True 0 - + True - 12 + False + 6 - + True - 2 - 4 - 12 - 6 - - - True - 1 - _Server: - True - transport_host - - - GTK_FILL - - - - - - True - _Port: - True - right - transport_port - - - 2 - GTK_FILL - - - - - - True - True - - - - 1 - 2 - - - - - - True - True - - - 3 - - - - + False + 0 + Preview + + + + + + False + False + 0 + + + + + True + False + 12 - - Ser_ver requires authentication + True True - False - True - True + in + + + - - 1 - 2 - 1 - 2 - + + True + True + 1 + + True + True 1 - False - False - 2 + 1 + + + + + True + False + Signatures + True + center + + + 1 + False - + True - vertical - 6 + False + 12 + 12 - + True - 0 - Security - - - - - - False - False - 0 - - - - - True - 12 + False + 6 - + True - 2 - 6 + False + 0 + _Languages + True + listSpellCheckLanguage + + + + + + False + False + 0 + + + + + True + False + 12 - + True - 6 + False + 2 + 2 + 6 + 6 - + True - gtk-dialog-warning + False + 0 + gtk-dialog-info - False - False - 0 + 1 + 2 + + - + True + False 0 - SSL is not supported in this build of Evolution - center - - - + The list of languages here reflects only the languages for which you have a dictionary installed. + True - False - False - 1 + 1 + 2 + 1 + 2 + + + + + + True + True + in + + + True + True + False + + + Languages Table + + + + + + + + + + 2 - - 1 - 2 - + + + True + True + 1 + + + + + True + True + 0 + + + + + True + False + 6 + + + True + False + 0 + Options + + + + + + False + False + 0 + + + + + True + False + 12 - + True - 12 + False + 6 - + + Check spelling while I _type True - _Use Secure Connection: + True + False + False True - center - transport_use_ssl + True False @@ -1250,14 +1050,39 @@ For example: "Work" or "Personal" - + True - use_ssl_model + False + 6 - - - 0 - + + True + False + Color for _misspelled words: + True + center + colorButtonSpellCheckColor + + + False + False + 0 + + + + + True + True + True + False + Pick a color + #000000000000 + + + False + False + 1 + @@ -1269,32 +1094,50 @@ For example: "Work" or "Personal" + + False + False + 1 + + False + False 1 - False - False - 3 + 2 + + + + + True + False + Spell Checking + True + center + + + 2 + False - + True - vertical - 6 + False + 12 + 12 - + True + False 0 - Authentication - - - + To help avoid email accidents and embarrassments, ask for confirmation before taking the following checkmarked actions: + True False @@ -1303,653 +1146,309 @@ For example: "Work" or "Personal" - + True + False 12 - + True - 3 - 2 - 12 - 6 + False + 6 - + + Sending a message with an _empty subject line True - 1 - T_ype: + True + False + False True - center - transport_auth_dropdown + True - GTK_FILL - + False + False + 0 - + + Sending a message with only _Bcc recipients defined True - 1 - User _Name: + True + False + False True - right - transport_user - - - 1 - 2 - GTK_FILL - - - - - - True - True - + True - 1 - 2 - 1 - 2 - + False + False + 1 - - Remember _password + + Sending a _private reply to a mailing list message True True False + False True True - 1 - 2 - 2 - 3 - - - - - True - 0 - 0 - - - True - 6 - - - True - - - False - False - 0 - - - - - Ch_eck for Supported Types - True - True - True - True - - - False - False - 1 - - - - - - - 1 - 2 - - - - - - - - - - 1 - - - - - False - False - 4 - - - - - True - 12 - vertical - 12 - - - True - vertical - 6 - - - True - 0 - Special Folders - - - - - - False - False - 0 - - - - - True - 0 - 0 - 12 - - - True - 5 - 2 - 12 - 6 - - - True - 0 - Drafts _Folder: - True - - - GTK_FILL - - - - - - True - 0 - Sent _Messages Folder: - True - - - 1 - 2 - GTK_FILL - + False + False + 2 - - _Trash Folder: + + Sending a reply to a large _number of recipients True True False + False True True - 2 - 3 - GTK_FILL - + False + False + 3 - - _Junk Folder: + + Allowing a _mailing list to redirect a private reply to the list True True False + False True True - 3 - 4 - GTK_FILL - - - - - - True - 0 - 0 - - - gtk-revert-to-saved - True - True - True - True - - - - - 1 - 2 - 4 - 5 - - - - - True - True - True - - - 1 - 2 - - - - - True - True - True - - - 1 - 2 - 1 - 2 - - - - - True - True - True - - - 1 - 2 - 2 - 3 + False + False + 4 - + + Sending a message with _recipients not entered as mail addresses True True - True + False + False + True + True - 1 - 2 - 3 - 4 + False + False + 5 - - - + True + True 1 - False - False - 0 + 3 + + + + + True + False + Confirmations + + + 3 + False + + + + + + + + + Default + + + SHA1 + + + SHA256 + + + SHA384 + + + SHA512 + + + + + + + + + + + a + + + b + + + + + + + + + + + a + + + b + + + + + + + + + + + Attachment + + + Inline (Outlook style) + + + Quoted + + + Do not quote + + + + + + + + + + + Attachment + + + Inline + + + Quoted + + + + + True + True - + True - vertical - 6 - - - True - 0 - Composing Messages - - - - - - False - False - 0 - - + False + 12 + 12 - + True - 12 + False + 6 - + True - 4 - 6 + False + 0 + Proxy Settings + + + + + + False + False + 0 + + + + + True + False + 12 - + True - 18 + False + 6 - + + _Use system defaults True True - + False + False + True + True + True + + False + False + 0 + - - - 3 - 4 - - - - - Always _blind carbon-copy (bcc) to: - True - True - False - True - True - - - 2 - 3 - - - - - True - 18 - + + _Direct connection to the Internet True True - + False + False + True + True + rdoSysSettings - - - - 1 - 2 - - - - - Alway_s carbon-copy (cc) to: - True - True - False - True - True - - - - - - - 1 - - - - - False - False - 1 - - - - - True - vertical - 6 - - - True - 0 - Message Receipts - - - - - - False - False - 0 - - - - - True - 12 - - - True - 12 - - - True - S_end message receipts: - True - receipt_policy_dropdown - - - False - False - 0 - - - - - True - - - 1 - - - - - - - 1 - - - - - False - False - 2 - - - - - True - 12 - vertical - 12 - - - True - vertical - 6 - - - True - 0 - General - - - - - - False - False - 0 - - - - - True - 12 - - - _Do not sign meeting requests (for Outlook compatibility) - True - True - False - True - True - - - - - 1 - - - - - False - False - 0 - - - - - True - vertical - 6 - - - True - 0 - Pretty Good Privacy (PGP/GPG) - - - - - - False - False - 0 - - - - - True - 12 - - - True - 6 - 2 - - - True - vertical - 6 - - - True - 12 - - - True - 0 - PGP/GPG _Key ID: - True - pgp_key - - - False - False - 0 - - - - - True - True - - - - 1 - - - - - False - False - 0 - - - - - True - 12 - - - True - 0 - Si_gning algorithm: - True - pgp_hash_algo - - - False - False - 0 - - - - - True - hash_algo_model - - - - 0 - - - - - False - False - 1 - - - - - 1 - + + False + False + 1 + - - Al_ways sign outgoing messages when using this account + + _Manual proxy configuration: True True False + False True True + rdoSysSettings False @@ -1958,507 +1457,335 @@ For example: "Work" or "Personal" - - Always encrypt to _myself when sending encrypted messages - True - True - False - True - True - True - - - False - False - 3 - - - - - Always _trust keys in my keyring when encrypting + True - True - False - True - True - - - False - False - 4 - - - - - - - - - 1 - - - - - False - False - 1 - - - - - True - vertical - 6 - - - True - 0 - Secure MIME (S/MIME) - - - - - - False - False - 0 - - - - - True - 12 - - - True - 7 - 3 - 12 - 6 - - - True - True - - - - 1 - 2 - 1 - 2 - - - - - - True - True - - - - 1 - 2 - 6 - 7 - - - - - - Also encrypt to sel_f when sending encrypted messages - True - True - False - True - True - - - 3 - 5 - 6 - GTK_FILL - - - - - - Encrypt out_going messages (by default) - True - True - False - True - True - - - 3 - 4 - 5 - GTK_FILL - - - - - - Digitally sign o_utgoing messages (by default) - True - True - False - True - True - - - 3 - GTK_FILL - - - - - - True - - - 3 - 3 - 4 - GTK_FILL - GTK_FILL - 6 - - - - - True - 0 - Encry_ption certificate: - True - smime_encrypt_key - - - 6 - 7 - GTK_FILL - - - - - - True - 0 - Sig_ning certificate: - True - smime_sign_key - - - 1 - 2 - GTK_FILL - - - - - - True - 6 - - - True - True - True + False + 24 - + True - 0 - 0 + False + 6 - + True - 2 + False + 4 + 4 + 6 + 6 - + True - gtk-open + False + 0 + H_TTP Proxy: + True + txtHttpHost - False - False - 0 + GTK_FILL + - + True - S_elect... + False + 0 + _Secure HTTP Proxy: True + txtHttpsHost - False - False - 1 + 1 + 2 + GTK_FILL + - - - - - - - False - False - 0 - - - - - True - True - True - - - True - 0 - 0 - - - True - 2 - + True - gtk-clear + False + 0 + No _Proxy for: + True + txtIgnoreHosts - False - False - 0 + 3 + 4 + GTK_FILL + - + True - Clea_r - True + True + â— - False - False - 1 + 1 + 2 + - - - - - - - False - False - 1 - - - - - 2 - 3 - 6 - 7 - GTK_FILL - GTK_FILL - - - - - True - 0 - Signing _algorithm: - True - smime_hash_algo - - - 2 - 3 - GTK_FILL - - - - - - True - 0 - 0 - - - True - hash_algo_model - - - - 0 - - - - - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - - True - 0 - 0 - - - True - 6 - - - True - True - True + + + True + True + â— + + + 1 + 2 + 1 + 2 + + + + + + True + False + 0 + Port: + + + 2 + 3 + GTK_FILL + + + + + + True + False + 0 + Port: + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + True + â— + adjustment4 + 1 + + + 3 + 4 + + + + + + True + True + â— + adjustment5 + 1 + + + 3 + 4 + 1 + 2 + + + + + + True + True + â— + + + 1 + 4 + 3 + 4 + + + + + + + + + + + + + + + + + + False + False + 0 + + - + + Use Authe_ntication True - 0 - 0 + True + False + False + True + True + + + False + False + 1 + + + + + True + False + 24 - + True - 2 + False + 2 + 2 + 6 + 3 - + True - gtk-open + False + 0 + Us_ername: + True + txtAuthUser - False - False - 0 + GTK_FILL + - + True - _Select... + False + 0 + Pass_word: True + txtAuthPwd - False - False - 1 + 1 + 2 + GTK_FILL + - - - - - - - False - False - 0 - - - - - True - True - True - - - True - 0 - 0 - - - True - 2 - + True - gtk-clear + True + â— - False - False - 0 + 1 + 2 + GTK_FILL - + True - Cle_ar - True + True + False + â— - False - False - 1 - + 1 + 2 + 1 + 2 + GTK_FILL + + + False + False + 2 + - - False - False - 1 - + + False + False + 3 + - - 2 - 3 - 1 - 3 - + + False + False + 1 + + False + False + 0 + + + + + True + False + + + + + + True + True 1 + + + + True + False + General + - False - False - 2 + False - - - - - - - - - - No encryption - never - - - TLS encryption - when-possible - - - SSL encryption - always - - - True True @@ -2466,17 +1793,18 @@ For example: "Work" or "Personal" True + False 12 - vertical 12 True - vertical + False 6 True + False 0 Start up @@ -2492,15 +1820,17 @@ For example: "Work" or "Personal" True + False 12 True - vertical + False 6 True + False 4 @@ -2508,6 +1838,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -2527,6 +1858,7 @@ For example: "Work" or "Personal" True + False 4 @@ -2534,6 +1866,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -2569,11 +1902,12 @@ For example: "Work" or "Personal" True - vertical + False 6 True + False 0 Message Display @@ -2589,11 +1923,12 @@ For example: "Work" or "Personal" True + False 12 True - vertical + False 6 @@ -2601,6 +1936,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -2613,10 +1949,12 @@ For example: "Work" or "Personal" True + False 24 True + False 2 2 6 @@ -2624,6 +1962,7 @@ For example: "Work" or "Personal" True + False 1 S_tandard Font: True @@ -2640,8 +1979,9 @@ For example: "Work" or "Personal" True True True + False Select HTML fixed width font - + 1 @@ -2657,8 +1997,9 @@ For example: "Work" or "Personal" True True True + False Select HTML variable width font - + 1 @@ -2670,6 +2011,7 @@ For example: "Work" or "Personal" True + False 1 Fix_ed Width Font: True @@ -2687,12 +2029,15 @@ For example: "Work" or "Personal" + True + True 1 True + False 6 @@ -2700,6 +2045,7 @@ For example: "Work" or "Personal" True True False + False True True True @@ -2715,6 +2061,7 @@ For example: "Work" or "Personal" True True True + False Pick a color #000000000000 @@ -2727,6 +2074,7 @@ For example: "Work" or "Personal" True + False color center @@ -2738,16 +2086,20 @@ For example: "Work" or "Personal" + True + True 2 True + False 6 True + False Default character e_ncoding: True center @@ -2763,6 +2115,8 @@ For example: "Work" or "Personal" + True + True 3 @@ -2772,10 +2126,13 @@ For example: "Work" or "Personal" True True False + False True True + True + True 4 @@ -2785,10 +2142,13 @@ For example: "Work" or "Personal" True True False + False True True + True + True 5 @@ -2796,6 +2156,8 @@ For example: "Work" or "Personal" + True + True 1 @@ -2809,11 +2171,12 @@ For example: "Work" or "Personal" True - vertical + False 6 True + False 0 Delete Mail @@ -2829,15 +2192,17 @@ For example: "Work" or "Personal" True + False 12 True - vertical + False 6 True + False 4 @@ -2845,6 +2210,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -2857,6 +2223,7 @@ For example: "Work" or "Personal" True + False model1 @@ -2884,6 +2251,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -2897,6 +2265,8 @@ For example: "Work" or "Personal" + True + True 1 @@ -2915,6 +2285,7 @@ For example: "Work" or "Personal" True + False General True center @@ -2926,17 +2297,18 @@ For example: "Work" or "Personal" True + False 12 - vertical 12 True - vertical + False 6 True + False 0 General @@ -2952,11 +2324,12 @@ For example: "Work" or "Personal" True + False 12 True - vertical + False 6 @@ -2964,6 +2337,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -2979,6 +2353,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -3007,11 +2382,12 @@ For example: "Work" or "Personal" True - vertical + False 6 True + False 0 Loading Images @@ -3027,11 +2403,12 @@ For example: "Work" or "Personal" True + False 12 True - vertical + False 6 @@ -3039,6 +2416,7 @@ For example: "Work" or "Personal" True True False + False True True True @@ -3055,6 +2433,7 @@ For example: "Work" or "Personal" True True False + False True True True @@ -3072,6 +2451,7 @@ For example: "Work" or "Personal" True True False + False True True True @@ -3087,9 +2467,9 @@ For example: "Work" or "Personal" - 1 False False + 1 @@ -3108,6 +2488,7 @@ For example: "Work" or "Personal" True + False HTML Messages True center @@ -3120,12 +2501,13 @@ For example: "Work" or "Personal" True + False 12 - vertical 6 True + False 0 Labels @@ -3141,12 +2523,15 @@ For example: "Work" or "Personal" True + False 12 + True + True 1 @@ -3158,6 +2543,7 @@ For example: "Work" or "Personal" True + False Labels True center @@ -3170,17 +2556,18 @@ For example: "Work" or "Personal" True + False 12 - vertical 12 True - vertical + False 6 True + False 0 Sender Photograph @@ -3196,11 +2583,12 @@ For example: "Work" or "Personal" True + False 12 True - vertical + False 6 @@ -3208,6 +2596,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -3223,6 +2612,7 @@ For example: "Work" or "Personal" True True False + False True True @@ -3251,11 +2641,12 @@ For example: "Work" or "Personal" True - vertical + False 6 True + False 0 Displayed Message Headers True @@ -3273,21 +2664,23 @@ For example: "Work" or "Personal" True + False 12 True + False 12 True - vertical + False 12 True True - + â— False @@ -3299,8 +2692,6 @@ For example: "Work" or "Personal" True True - automatic - automatic in @@ -3312,24 +2703,29 @@ For example: "Work" or "Personal" Mail Headers Table + + + + True + True 1 - 0 True True + 0 True - vertical + False 6 @@ -3338,6 +2734,7 @@ For example: "Work" or "Personal" False True True + False True @@ -3352,6 +2749,7 @@ For example: "Work" or "Personal" True True True + False True @@ -3371,6 +2769,8 @@ For example: "Work" or "Personal" + True + True 1 @@ -3384,11 +2784,12 @@ For example: "Work" or "Personal" True - vertical + False 6 True + False 0 Date/Time Format True @@ -3406,10 +2807,12 @@ For example: "Work" or "Personal" True + False 12 True + False 3 @@ -3424,9 +2827,9 @@ For example: "Work" or "Personal" - 1 False False + 1 @@ -3445,6 +2848,7 @@ For example: "Work" or "Personal" True + False Headers True @@ -3454,236 +2858,272 @@ For example: "Work" or "Personal" - + True - 12 - vertical - 12 + False + 0 + 0 - + True - vertical - 6 + False + 12 + immediate + 12 - + True - 0 - General - - - - - - False - False - 0 - - - - - True - 12 + False + 6 - + True - 9 - 3 + False + 0 + General + + + + + + False + False + 0 + + + + + True + False + 12 - + True - 6 + False + 8 + 3 - + + Check incoming _messages for junk True - _Default junk plugin: + True + False + False True - default_junk_plugin + 0 + True - False - False - 6 - 0 + GTK_FILL + + 4 - + True + False + 4 + + + Delete junk messages on e_xit + True + True + False + False + True + 0 + True + + + False + False + 0 + + + + + True + False + model2 + + + + 0 + + + + + False + False + 1 + + - False - False - 1 + 1 + 2 + GTK_FILL + 4 - - - 7 - 8 - GTK_FILL - - - - - Check incoming _messages for junk - True - True - False - True - True - - - GTK_FILL - - 4 - - - - - True - 3 - + + Check cu_stom headers for junk True - gtk-info + True + False + False + True + 0 + True - False - False - 0 + 2 + 3 + GTK_FILL + + 4 - + True - True + False + 6 + + + True + True + in + + + True + True + + + + + + + + True + True + 0 + + + + + True + False + 6 + start + + + gtk-add + True + True + True + True + False + True + + + False + False + 0 + + + + + gtk-remove + True + True + True + True + False + True + + + False + False + 1 + + + + + False + True + 1 + + - False - False - 1 + 3 + 4 + 22 - - - 8 - 9 - GTK_FILL - 15 - - - - - True - 4 - - Delete junk messages on e_xit + + Do not mar_k messages as junk if sender is in my address book True True False + False True + 0 True - False - False - 0 - - - - - True - model2 - - - - 0 - - - - - False - False - 1 + 4 + 5 + GTK_FILL + + 4 - - - 1 - 2 - GTK_FILL - 4 - - - - - Check cu_stom headers for junk - True - True - False - True - True - - - 2 - 3 - GTK_FILL - - 4 - - - - - True - 6 - + + _Lookup in local address book only True True - automatic - automatic - in - - - True - True - - + False + False + True + 0 + True - 0 + 5 + 6 + GTK_FILL + + 25 - + True - vertical + False 6 - start - - gtk-add + True - True - True - True - True + False + gtk-info False - False + True 0 - - gtk-remove + True - True - True - True - True + False + Option is ignored if a match for custom junk headers is found. False @@ -3693,96 +3133,56 @@ For example: "Work" or "Personal" - False - 1 - - - - - 3 - 4 - 22 - - - - - Do not mar_k messages as junk if sender is in my address book - True - True - False - True - True - - - 4 - 5 - GTK_FILL - - 4 - - - - - _Lookup in local address book only - True - True - False - True - True - - - 5 - 6 - GTK_FILL - - 25 - - - - - True - 6 - - - True - gtk-info - - - False - 0 + 6 + 7 + GTK_FILL - + True - Option is ignored if a match for custom junk headers is found. + False + 12 + 6 + + + + + + + + + + + + + + + - False - False - 1 + 7 + 8 + GTK_FILL - - 6 - 7 - GTK_FILL - + + True + True + 1 + - 1 + False + False + 0 - - False - False - 0 - @@ -3792,6 +3192,7 @@ For example: "Work" or "Personal" True + False Junk True @@ -3801,142 +3202,243 @@ For example: "Work" or "Personal" - + + + + + + + + + + No encryption + never + + + TLS encryption + when-possible + + + SSL encryption + always + + + + True - True + False + 12 + 12 - + True - 12 - vertical + False 6 - + True - vertical - 6 - - - True - 0 - Default Behavior - - - - - - False - False - 0 - - + False + 0 + Special Folders + + + + + + False + False + 0 + + + + + True + False + 0 + 0 + 12 - + True - 12 + False + 5 + 2 + 12 + 6 - + True - vertical - 8 - - - Format messages in _HTML - True - True - False - True - True - - - False - False - 0 - - - - - Automatically insert _emoticon images - True - True - False - True - True - - - False - False - 1 - - - - - Always request rea_d receipt - True - True - False - True - True - - - False - False - 2 - - - - - Encode file names in an Outlook/GMail way - True - True - False - True - True - - - False - False - 3 - - + False + 0 + Drafts _Folder: + True + + + GTK_FILL + + + + + + True + False + 0 + Sent _Messages Folder: + True + + + 1 + 2 + GTK_FILL + + + + + + _Trash Folder: + True + True + False + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + _Junk Folder: + True + True + False + False + True + True + + + 3 + 4 + GTK_FILL + + + + + + True + False + 0 + 0 - + + gtk-revert-to-saved True - 6 - - - True - 1 - C_haracter set: - True - center - - - False - False - 0 - - - - - + True + True + False + True - - 4 - False - False - + + 1 + 2 + 4 + 5 + + + + + True + True + True + False + + + 1 + 2 + + + + + True + True + True + False + + + 1 + 2 + 1 + 2 + + + + + True + True + True + False + + + 1 + 2 + 2 + 3 + + + + + True + True + True + False + + + 1 + 2 + 3 + 4 + + + + - - 1 - False - False - + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + 6 + + + True + False + 0 + Composing Messages + + + + False False @@ -3944,293 +3446,151 @@ For example: "Work" or "Personal" - + True - vertical - 6 - - - True - 0 - Replies and Forwards - - - - - - 0 - False - False - - + False + 12 - + True - 12 + False + 4 + 6 - + True - 6 - 2 - 6 - 6 - - - True - 1 - _Reply style: - True - - - GTK_FILL - - - - - - True - 1 - _Forward style: - True - - - 1 - 2 - GTK_FILL - - - + False + 18 - - Start _typing at the bottom on replying + True True - False - True - True + â— - - 2 - 2 - 3 - - - - - - _Keep signature above the original message on replying - True - True - False - True - True - - - 2 - 3 - 4 - - - - - - Ignore Reply-To: for mailing lists - True - True - False - True - True - - - 2 - 4 - 5 - - + + + 3 + 4 + + + + + Always _blind carbon-copy (bcc) to: + True + True + False + False + True + True + + + 2 + 3 + + + + + True + False + 18 - - Group Reply goes only to mailing list, if possible + True True - False - True - True - - - 2 - 5 - 6 - - - - - - True - 0 - 0 - - - True - model3 - - - - 0 - - - - - - - 1 - 2 - - - - - True - 0 - 0 - - - True - model4 - - - - 0 - - - - + â— - - 1 - 2 - 1 - 2 - + + 1 + 2 + + + + + Alway_s carbon-copy (cc) to: + True + True + False + False + True + True + - - False - False - 1 - - False - False + True + True 1 - - - - True - General - True - center - - False + False + False + 1 - + True - 12 - vertical - 12 + False + 6 - + True - vertical - 6 - - - True - 0 - Sig_natures - True - - - - - - False - False - 0 - - - - - True - 12 - - - - - - 1 - True - True - - + False + 0 + Message Receipts + + + + False + False 0 - True - True - + True - vertical - 6 - - - True - 0 - Preview - - - - - - False - False - 0 - - + False + 12 - + True - 12 + False + 12 - + True - True - automatic - automatic - in - - - + False + S_end message receipts: + True + receipt_policy_dropdown + + + False + False + 0 + + + + + True + False + + True + True + 1 + - - 1 - True - True - @@ -4241,183 +3601,82 @@ For example: "Work" or "Personal" - 1 - - - - - True - Signatures - True - center - - - 1 - False + False + False + 2 + + + True + False + 12 + 12 - + True - 12 - vertical - 12 + False + 6 - + True - vertical - 6 - - - True - 0 - _Languages - True - listSpellCheckLanguage - - - - - - False - False - 0 - - - - - True - 12 - - - True - 2 - 2 - 6 - 6 - - - True - 0 - gtk-dialog-info - - - 1 - 2 - - - - - - - True - 0 - The list of languages here reflects only the languages for which you have a dictionary installed. - True - - - 1 - 2 - 1 - 2 - - - - - - True - True - automatic - automatic - in - - - True - True - False - - - Languages Table - - - - - - - 2 - - - - - - - 1 - True - True - - + False + 0 + Account Information + + + + False + False 0 - True - True - + True - vertical - 6 - - - True - 0 - Options - - - - - - False - False - 0 - - + False + 12 - + True - 12 + False + 6 + 2 - + True - vertical + False 6 - - Check spelling while I _type + True - True - False - True - True + False + 0 + Type the name by which you would like to refer to this account. +For example: "Work" or "Personal" + True False - False + True 0 - + True - 6 + False + 12 - + True - Color for _misspelled words: + False + _Name: True - center - colorButtonSpellCheckColor + right + management_name False @@ -4426,71 +3685,56 @@ For example: "Work" or "Personal" - + True True - True - Pick a color - #000000000000 + â— - False - False + True + True 1 False - False + True 1 - - 1 - False - False - - False - False + True + True 1 - 2 - - - - - True - Spell Checking - True - center - - - 2 - False + False + False + 0 - + True - 12 - vertical - 12 + False + 6 - + True + False 0 - To help avoid email accidents and embarrassments, ask for confirmation before taking the following checkmarked actions: - True + Required Information + + + False @@ -4499,861 +3743,2028 @@ For example: "Work" or "Personal" - + True + False 12 - + True - vertical - 6 + False + 2 + 2 + 12 + 6 - - Sending a message with an _empty subject line + True True - False - True - True + â— + + + + - False - False - 0 + 1 + 2 + 1 + 2 + - - Sending a message with only _Bcc recipients defined + True - True - False - True - True - - - False - False - 1 - - - - - Sending a _private reply to a mailing list message - True - True - False - True - True - - - False - False - 2 - - - - - Sending a reply to a large _number of recipients - True - True - False + False + 0 + Email _Address: True - True + identity_address - False - False - 3 + 1 + 2 + GTK_FILL + - - Allowing a _mailing list to redirect a private reply to the list + True - True - False + False + 0 + Full Nam_e: True - True + identity_full_name - False - False - 4 + GTK_FILL + - - Sending a message with _recipients not entered as mail addresses + True True - False - True - True + â— + + + + - False - False - 5 + 1 + 2 + + True + True 1 - 3 - - - - - True - Confirmations - - - 3 - False + False + False + 1 - - - normal - - + + True - vertical - 12 + False + 6 - + + True + False + 0 + Optional Information + + + + + + False + False + 0 + + + + True - vertical + False + 12 - + True - 12 - vertical - 6 + False + 4 + 2 + 12 + 6 - + + True + False + 0 + Signat_ure: + True + signature_dropdown + + + 3 + 4 + GTK_FILL + + + + + True + False 6 - + True - 0 - gtk-dialog-info - 6 + False + False + False 0 - + + Add Ne_w Signature... True - 0 - The output of this script will be used as your -signature. The name you specify will be used -for display purposes only. - True + True + True + False + True + False False + end 1 - False - False - 0 + 1 + 2 + 3 + 4 + GTK_FILL + GTK_FILL - + True - 2 - 2 - 6 - 6 - - - True - 0 - _Name: - True - center - entry_add_script_name - - - GTK_FILL - - - - - - True - 0 - _Script: - True - center - filechooserbutton_add_script - - - 1 - 2 - GTK_FILL - - - - - - True - True - - - 1 - 2 - - - - - - True - - - - 1 - 2 - 1 - 2 - GTK_FILL - - - + True + â— + + + + - 1 + 1 + 2 + 2 + 3 + - - - False - False - 0 - - - - - 1 - - - - - True - end - - - True - True - True - False - + True - 0 - 0 - - - True - 2 - - - True - gtk-add - - - False - False - 0 - - - - - True - _Add Signature - True - - - False - False - 1 - - - - + False + 0 + Or_ganization: + True + identity_organization + + 2 + 3 + GTK_FILL + + + + + + True + True + â— + + + + + + + 1 + 2 + 1 + 2 + + + + + + True + False + 0 + Re_ply-To: + True + center + identity_reply_to + + + 1 + 2 + GTK_FILL + + + + + + _Make this my default account + True + True + False + False + True + True + + + 2 + GTK_FILL + GTK_FILL + - - False - False - 0 - - - - - gtk-cancel - True - True - True - False - True - - - False - False - 1 - - False - end - 0 + True + True + 1 + + False + False + 2 + - - button_add_script_add - button_add_script_cancel - - + True - True + False + 12 + 12 - + True - 12 - vertical - 12 + False + 6 - + True - vertical - 6 + False + 0 + General + + + + + + False + False + 0 + + + + + True + False + 12 - + + _Do not sign meeting requests (for Outlook compatibility) True - 0 - Proxy Settings - - - + True + False + False + True + True - - 0 - False - False - + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + 6 + + + True + False + 0 + Pretty Good Privacy (PGP/GPG) + + + + + + False + False + 0 + + + + + True + False + 12 - + True - 12 + False + 6 + 2 - + True - vertical + False 6 - - _Use system defaults + True - True - False - True - True - True + False + 12 + + + True + False + 0 + PGP/GPG _Key ID: + True + pgp_key + + + False + False + 0 + + + + + True + True + â— + + + True + True + 1 + + - 0 False False + 0 - - _Direct connection to the Internet + + True + False + 12 + + + True + False + 0 + Si_gning algorithm: + True + pgp_hash_algo + + + False + False + 0 + + + + + True + False + hash_algo_model + + + + 0 + + + + + False + False + 1 + + + + + True + True + 1 + + + + + Al_ways sign outgoing messages when using this account True True False + False True True - rdoSysSettings - 1 False False + 2 - - _Manual proxy configuration: + + Always encrypt to _myself when sending encrypted messages True True False + False True + True True - rdoSysSettings - 2 False False + 3 - + + Always _trust keys in my keyring when encrypting True - 24 + True + False + False + True + True + + + False + False + 4 + + + + + + + + + True + True + 1 + + + + + False + False + 1 + + + + + True + False + 6 + + + True + False + 0 + Secure MIME (S/MIME) + + + + + + False + False + 0 + + + + + True + False + 12 + + + True + False + 7 + 3 + 12 + 6 + + + True + True + â— + + + 1 + 2 + 1 + 2 + + + + + + True + True + â— + + + 1 + 2 + 6 + 7 + + + + + + Also encrypt to sel_f when sending encrypted messages + True + True + False + False + True + True + + + 3 + 5 + 6 + GTK_FILL + + + + + + Encrypt out_going messages (by default) + True + True + False + False + True + True + + + 3 + 4 + 5 + GTK_FILL + + + + + + Digitally sign o_utgoing messages (by default) + True + True + False + False + True + True + + + 3 + GTK_FILL + + + + + + True + False + + + 3 + 3 + 4 + GTK_FILL + GTK_FILL + 6 + + + + + True + False + 0 + Encry_ption certificate: + True + smime_encrypt_key + + + 6 + 7 + GTK_FILL + + + + + + True + False + 0 + Sig_ning certificate: + True + smime_sign_key + + + 1 + 2 + GTK_FILL + + + + + + True + False + 6 + + + True + True + True + False - + True - vertical - 6 + False + 0 + 0 - + True - 4 - 4 - 6 - 6 + False + 2 - + True - 0 - H_TTP Proxy: - True - txtHttpHost + False + gtk-open - GTK_FILL - + False + False + 0 - + True - 0 - _Secure HTTP Proxy: + False + S_elect... True - txtHttpsHost - 1 - 2 - GTK_FILL - + False + False + 1 + + + + + + + False + False + 0 + + + + + True + True + True + False + + + True + False + 0 + 0 + + + True + False + 2 - + True - 0 - No _Proxy for: - True - txtIgnoreHosts + False + gtk-clear - 3 - 4 - GTK_FILL - + False + False + 0 - + True - True - + False + Clea_r + True - 1 - 2 - + False + False + 1 + + + + + + + False + False + 1 + + + + + 2 + 3 + 6 + 7 + GTK_FILL + GTK_FILL + + + + + True + False + 0 + Signing _algorithm: + True + smime_hash_algo + + + 2 + 3 + GTK_FILL + + + + + + True + False + 0 + 0 + + + True + False + hash_algo_model + + + + 0 + + + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + False + 0 + 0 + + + True + False + 6 + + + True + True + True + False + + + True + False + 0 + 0 - + True - True - + False + 2 + + + True + False + gtk-open + + + False + False + 0 + + + + + True + False + _Select... + True + + + False + False + 1 + + - - 1 - 2 - 1 - 2 - - + + + + + False + False + 0 + + + + + True + True + True + False + + + True + False + 0 + 0 - + True - 0 - Port: - - - 2 - 3 - GTK_FILL - - - - - - True - 0 - Port: - - - 2 - 3 - 1 - 2 - GTK_FILL - - - - - - True - True - - adjustment4 - 1 - - - 3 - 4 - - - - - - True - True - - adjustment5 - 1 - - - 3 - 4 - 1 - 2 - - - - - - True - True - - - - 1 - 4 - 3 - 4 - - - - - - - - - - - - - - - - - - False - False - 0 - - - - - Use Authe_ntication - True - True - False - True - True - - - False - False - 1 - - - - - True - 24 - - - True - 2 - 2 - 6 - 3 - - - True - 0 - Us_ername: - True - txtAuthUser - - - GTK_FILL - - - - - - True - 0 - Pass_word: - True - txtAuthPwd - - - 1 - 2 - GTK_FILL - - - - - - True - True - - - - 1 - 2 - GTK_FILL - - - - - True - True - False - - - - 1 - 2 - 1 - 2 - GTK_FILL - - + False + 2 + + + True + False + gtk-clear + + + False + False + 0 + + + + + True + False + Cle_ar + True + + + False + False + 1 + + - - False - False - 2 - + + False + False + 1 + + + + + + + 2 + 3 + 1 + 3 + + + + + + + True + True + 1 + + + + + False + False + 2 + + + + + True + False + 12 + 12 + + + True + False + 2 + 3 + 12 + 6 + + + True + False + 0 + Server _Type: + True + right + source_type_dropdown + + + GTK_FILL + + + + + + True + False + 0 + 0 + Description: + center + + + 1 + 2 + GTK_FILL + + + + + + True + False + 0 + 0 + description + True + + + 1 + 3 + 1 + 2 + GTK_FILL + + + + + + True + False + + + 1 + 3 + GTK_FILL + + + + + + False + False + 0 + + + + + True + False + + + False + False + 1 + + + + + True + False + 6 + + + True + False + 0 + Configuration + + + + + + False + False + 0 + + + + + True + False + 12 + + + True + False + 3 + 4 + 12 + 6 + + + True + False + 1 + _Server: + True + source_host + + + GTK_FILL + + + + + + True + False + 1 + _Port: + True + source_port + + + 2 + GTK_FILL + + + + + + True + False + 1 + User _Name: + True + source_user + + + 1 + GTK_FILL + + + + + + True + True + â— + + + 1 + + + + + True + False + True + + + False + + + + + 3 + + + + + + + True + True + â— + + + 1 + 4 + 1 + 2 + + + + + + True + False + 1 + _Path: + True + + + 2 + 3 + GTK_FILL + + + + + + True + False + Mailbox location + + + 1 + 2 + 2 + 3 + + + + + + + + + + + + + + True + True + 1 + + + + + False + False + 2 + + + + + True + False + 6 + + + True + False + 0 + Security + source_auth_dropdown + + + + + + False + False + 0 + + + + + True + False + 12 + + + True + False + 6 + + + True + False + 12 + + + True + False + _Use Secure Connection: + True + center + source_use_ssl + + + False + False + 0 + + + + + True + False + use_ssl_model + + + + 0 + + + + + False + False + 1 + + + + + False + False + 0 + + + + + True + False + 6 + + + True + False + gtk-dialog-warning + + + False + False + 0 + + + + + True + False + 0 + SSL is not supported in this build of Evolution + center + + + + + + False + False + 1 + + + + + True + True + 1 + + + + + + + True + True + 1 + + + + + False + False + 3 + + + + + True + False + 6 + + + True + False + 0 + _Authentication Type + True + source_auth_dropdown + + + + + + False + False + 0 + + + + + True + False + 12 + + + True + False + 6 + + + True + False + 6 + + + True + False + + + False + False + 0 + + + + + Ch_eck for Supported Types + True + True + True + False + True + + + False + False + 1 + + + + + True + True + 0 + + + + + Re_member password + True + True + False + False + True + True + + + False + False + 1 + + + + + + + True + True + 1 + + + + + False + False + 4 + + + + + True + False + 12 + 12 + + + True + False + 2 + 3 + 12 + 6 + + + True + False + 0 + 0 + Server _Type: + True + right + transport_type_dropdown + + + GTK_FILL + + + + + + True + False + 0 + 0 + Description: + right + + + 1 + 2 + GTK_FILL + + + + + True + False + + + 1 + 3 + GTK_FILL + + + + + + True + False + 0 + 0 + description + True + + + 1 + 3 + 1 + 2 + GTK_FILL + + + + + + False + False + 0 + + + + + True + False + + + False + False + 1 + + + + + True + False + 6 + + + True + False + 0 + Server Configuration + + + + + + False + False + 0 + + + + + True + False + 12 + + + True + False + 2 + 4 + 12 + 6 + + + True + False + 1 + _Server: + True + transport_host + + + GTK_FILL + + + + + + True + False + _Port: + True + right + transport_port + + + 2 + GTK_FILL + + + + + + True + True + â— + + + 1 + 2 + + + + + + True + False + True + + + False + + + + + 3 + + + + + + + Ser_ver requires authentication + True + True + False + False + True + True + + + 1 + 2 + 1 + 2 + + + + + + + + + + + + + + + + True + True + 1 + + + + + False + False + 2 + + + + + True + False + 6 + + + True + False + 0 + Security + + + + + + False + False + 0 + + + + + True + False + 12 + + + True + False + 2 + 6 + + + True + False + 6 + + + True + False + gtk-dialog-warning + + + False + False + 0 + + + + + True + False + 0 + SSL is not supported in this build of Evolution + center + + + + + + False + False + 1 + + + + + 1 + 2 + + + + + True + False + 12 + + + True + False + _Use Secure Connection: + True + center + transport_use_ssl + + + False + False + 0 + + + + + True + False + use_ssl_model + + + + 0 + False False - 3 + 1 - - False - False - 1 - - - - - False - False - 0 - - - - - True - - + True + True 1 - - - - True - General - - False + False + False + 3 - - - dialog - Set custom junk header - 12 - 400 - FALSE - - + + True - vertical - 12 - 12 + False + 6 - + True - True - All new emails with header that matches given content will be automatically filtered as junk + False + 0 + Authentication + + + + + False + False + 0 + - - - True - 2 - 2 - 12 - 6 - - - True - Header name - junk-header-name - 0 - - - GTK_SHRINK - - - - - True - Header content - junk-header-content - 0 - - - 1 - 2 - GTK_SHRINK - - - - - True - True - - - 1 - 2 - - - - - True - True - - - 1 - 2> - 1 - 2 - - - - - - + + True - end - - - gtk-cancel - True - True - True - True - - - False - False - 0 - - + False + 12 - - gtk-ok + True - True - True - True + False + 3 + 2 + 12 + 6 + + + True + False + 1 + T_ype: + True + center + transport_auth_dropdown + + + GTK_FILL + + + + + + True + False + 1 + User _Name: + True + right + transport_user + + + 1 + 2 + GTK_FILL + + + + + + True + True + â— + + + 1 + 2 + 1 + 2 + + + + + + Remember _password + True + True + False + False + True + True + + + 1 + 2 + 2 + 3 + + + + + True + False + 0 + 0 + + + True + False + 6 + + + True + False + + + False + False + 0 + + + + + Ch_eck for Supported Types + True + True + True + False + True + + + False + False + 1 + + + + + + + 1 + 2 + + + + + - - False - False - 1 - - False - end - 0 + True + True + 1 + + False + False + 4 + - - junk-header-cancel - junk-header-ok - + + + + + + @@ -5362,10 +5773,4 @@ for display purposes only. - - - - - - diff --git a/maint/evolution.xml b/maint/evolution.xml index 8f48229dd1..c072734164 100644 --- a/maint/evolution.xml +++ b/maint/evolution.xml @@ -17,12 +17,14 @@ + + - + @@ -39,6 +41,7 @@ + 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 + * + */ + +#include +#include +#include +#include + +#include + +#include +#include + +/* 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 ( + "%s", _("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 @@ + + + + + /schemas/apps/evolution/mail/junk/bogofilter/unicode + /apps/evolution/mail/junk/bogofilter/unicode + evolution-bogofilter + bool + true + + Convert mail messages to Unicode + + Convert message text to Unicode UTF-8 to unify spam/ham tokens + coming from different character sets. + + + + + + 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 - * - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "e-mail-junk-hook.h" - -#include - -#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 - * - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef E_MAIL_JUNK_HOOK_H -#define E_MAIL_JUNK_HOOK_H - -#include - -/* 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 +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include 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) { @@ -675,132 +676,9 @@ emmp_free (EConfig *ec, GSList *items, gpointer data) g_slist_free (items); } -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 ("%s", 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 ("%s", 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 ("%s", 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 ("%s", 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 #include #include -#include #include /* 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 + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* 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 ( + "%s", _("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 ( + "%s", + _("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 @@ + + + + + /schemas/apps/evolution/mail/junk/sa/local_only + /apps/evolution/mail/junk/sa/local_only + evolution-spamassassin + bool + true + + Use only local spam tests. + + Use only the local spam tests (no DNS). + + + + + + /schemas/apps/evolution/mail/junk/sa/use_daemon + /apps/evolution/mail/junk/sa/use_daemon + evolution-spamassassin + bool + true + + Use SpamAssassin daemon and client + + Use SpamAssassin daemon and client (spamc/spamd). + + + + + + diff --git a/plugins/bogo-junk-plugin/Makefile.am b/plugins/bogo-junk-plugin/Makefile.am deleted file mode 100644 index c4ee4a65ac..0000000000 --- a/plugins/bogo-junk-plugin/Makefile.am +++ /dev/null @@ -1,67 +0,0 @@ -@EVO_PLUGIN_RULE@ - -plugin_DATA = org-gnome-bogo-junk-plugin.eplug - -plugin_LTLIBRARIES = liborg-gnome-bogo-junk-plugin.la - -liborg_gnome_bogo_junk_plugin_la_CPPFLAGS = \ - $(AM_CPPFLAGS) \ - -I$(top_srcdir) \ - -DWELCOME_MESSAGE=\""$(privdatadir)/default/C/mail/local/Inbox"\" \ - $(GNOME_PLATFORM_CFLAGS) \ - $(EVOLUTION_MAIL_CFLAGS) - -liborg_gnome_bogo_junk_plugin_la_SOURCES = bf-junk-filter.c - -liborg_gnome_bogo_junk_plugin_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) - -liborg_gnome_bogo_junk_plugin_la_LIBADD = \ - $(top_builddir)/mail/libevolution-mail.la \ - $(top_builddir)/e-util/libeutil.la \ - $(top_builddir)/shell/libeshell.la \ - $(EVOLUTION_MAIL_LIBS) \ - $(GNOME_PLATFORM_LIBS) - -schemadir = $(GCONF_SCHEMA_FILE_DIR) -schema_in_files = bogo-junk-plugin.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 - - -BUILT_SOURCES = $(plugin_DATA) $(error_DATA) - -CLEANFILES = $(BUILT_SOURCES) - -DISTCLEANFILES = $(schema_DATA) - -EXTRA_DIST = \ - org-gnome-bogo-junk-plugin.eplug.xml \ - $(schema_in_files) - - --include $(top_srcdir)/git.mk diff --git a/plugins/bogo-junk-plugin/README b/plugins/bogo-junk-plugin/README deleted file mode 100644 index a14bcf6dda..0000000000 --- a/plugins/bogo-junk-plugin/README +++ /dev/null @@ -1,35 +0,0 @@ -Bogofilter plugin for Evolution - -This plugin implements junk filtering for the Evolution mailer, -provided by the bogofilter utility. Bogofilter (http://www.bogofilter.org) -if a fast and nimble mail filter using a so-called Bayesian technique to -classify junk and non-junk email. - -CAVEATS: - -For Evolution versions before 2.5.2, the definition file for the stock -junk filter plugin, 'org-gnome-sa-junk-plugin.eplug', must be removed -from the plugin directory to avoid conflict with any alternate junk plugin. -Simply disabling the SA plugin in the configuration won't help. -This is due to a flaw in the loading code for this hook type -(see GNOME bug #313096). - -To be able to classify emails as spam, bogofilter needs to have some -messages in its ham (non-spam) wordlist. This presents something of a -chicken-and-egg problem for Evolution, because it can feed messages -to the junk filter for learning as non-junk only after these messages have been -classified as junk and moved into a junk folder (GNOME bug #322105). -Thus, if you haven't got a pre-existing bogofilter database, you may need -to feed it some messages known to be non-junk, using its -command line utility: - -bogofilter -n < saved-ham-message - -Alternatively, you may use Spam Trainer, which is a GUI tool that supports -drag-and-drop from Evolution: - -http://spamtrainer.sourceforge.net/ - -Set it up to use bogofilter commands for training: - Ham command: bogofilter -n < %f - Spam command: bogofilter -s < %f diff --git a/plugins/bogo-junk-plugin/bf-junk-filter.c b/plugins/bogo-junk-plugin/bf-junk-filter.c deleted file mode 100644 index 0d6726fd03..0000000000 --- a/plugins/bogo-junk-plugin/bf-junk-filter.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * 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 - * - * - * Authors: - * Mikhail Zabaluev - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright 2005 Mikhail Zabaluev - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#define G_LOG_DOMAIN "bf-junk-filter" - -#ifndef G_OS_WIN32 -# include -#else -# include -#endif - -#include -#include -#include -#include "mail/em-config.h" -#include -#include -#include "shell/e-shell.h" - -#ifndef BOGOFILTER_BINARY -#define BOGOFILTER_BINARY "/usr/bin/bogofilter" -#endif - -#define BOGOFILTER_ERROR 3 - -#define EM_JUNK_BF_GCONF_DIR "/apps/evolution/mail/junk/bogofilter" - -#define d(x) (camel_debug("junk")?(x):0) - -static gboolean is_installed = FALSE; - -static gchar em_junk_bf_binary[] = BOGOFILTER_BINARY; - -static const gchar em_junk_bf_gconf_dir[] = EM_JUNK_BF_GCONF_DIR; -GtkWidget * org_gnome_bogo_convert_unicode (struct _EPlugin *epl, struct _EConfigHookItemFactoryData *data); - -/* plugin fonction prototypes */ -gboolean em_junk_bf_check_junk (EPlugin *ep, EMJunkTarget *target); -gpointer em_junk_bf_validate_binary (EPlugin *ep, EMJunkTarget *target); -void em_junk_bf_report_junk (EPlugin *ep, EMJunkTarget *target); -void em_junk_bf_report_non_junk (EPlugin *ep, EMJunkTarget *target); -void em_junk_bf_commit_reports (EPlugin *ep, EMJunkTarget *target); - -static gint -pipe_to_bogofilter (CamelMimeMessage *msg, - const gchar **argv, - GCancellable *cancellable, - GError **error); - -/* eplugin stuff */ -gint e_plugin_lib_enable (EPlugin *ep, gint enable); - -#define EM_JUNK_BF_GCONF_DIR_LENGTH (G_N_ELEMENTS (em_junk_bf_gconf_dir) - 1) - -static gboolean em_junk_bf_unicode = TRUE; - -static void -init_db (void) -{ - CamelStream *stream = camel_stream_fs_new_with_name (WELCOME_MESSAGE, O_RDONLY, 0, NULL); - CamelMimeParser *parser = camel_mime_parser_new (); - CamelMimeMessage *msg = camel_mime_message_new (); - const gchar *argv[] = { - em_junk_bf_binary, - "-n", - NULL, - 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 ( - (CamelMimePart *) msg, parser, NULL, NULL); - g_object_unref (parser); - - d(fprintf (stderr, "Initing the bogofilter DB with Welcome message\n")); - - if (em_junk_bf_unicode) { - argv[2] = "--unicode=yes"; - } - - pipe_to_bogofilter (msg, argv, NULL, NULL); - g_object_unref (msg); - -} - -static gint -pipe_to_bogofilter (CamelMimeMessage *msg, - const gchar **argv, - GCancellable *cancellable, - GError **error) -{ - GPid child_pid; - gint bf_in; - CamelStream *stream; - GError *err = NULL; - gint status; - gint waitres; - gint res; - static gboolean only_once = FALSE; - -retry: - if (camel_debug_start ("junk")) { - gint i; - - printf ("pipe_to_bogofilter "); - for (i = 0; argv[i]; i++) - printf ("%s ", argv[i]); - printf ("\n"); - camel_debug_end (); - } - - if (!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, - &bf_in, - NULL, - NULL, - &err)) - { - g_warning ("error occurred while spawning %s: %s", argv[0], err->message); - - if (g_error_matches (err, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT)) { - if (is_installed) - g_set_error (error, EM_JUNK_ERROR, err->code, _("Bogofilter is not available. Please install it first.")); - is_installed = FALSE; - } else { - /* For Translators: The first %s stands for the executable full path with a file name, the second is the error message itself. */ - g_set_error (error, EM_JUNK_ERROR, err->code, _("Error occurred while spawning %s: %s."), argv[0], err->message); - } - - g_error_free (err); - - return BOGOFILTER_ERROR; - } - - stream = camel_stream_fs_new_with_fd (bf_in); - camel_data_wrapper_write_to_stream_sync ( - CAMEL_DATA_WRAPPER (msg), stream, cancellable, NULL); - camel_stream_flush (stream, cancellable, NULL); - camel_stream_close (stream, cancellable, NULL); - g_object_unref (stream); - -#ifndef G_OS_WIN32 - waitres = waitpid (child_pid, &status, 0); - if (waitres < 0 && errno == EINTR) { - /* child process is hanging... */ - g_warning ("wait for bogofilter child process interrupted, terminating"); - kill (child_pid, SIGTERM); - sleep (1); - waitres = waitpid (child_pid, &status, WNOHANG); - if (waitres == 0) { - /* ...still hanging, set phasers to KILL */ - g_warning ("bogofilter child process does not respond, killing"); - kill (child_pid, SIGKILL); - sleep (1); - waitres = waitpid (child_pid, &status, WNOHANG); - g_set_error (error, EM_JUNK_ERROR, -2, _("Bogofilter child process does not respond, killing...")); - } else - g_set_error (error, EM_JUNK_ERROR, -3, _("Wait for Bogofilter child process interrupted, terminating...")); - } - - if (waitres >= 0 && WIFEXITED (status)) { - res = WEXITSTATUS (status); - } else { - res = BOGOFILTER_ERROR; - } -#else - WaitForSingleObject (child_pid, INFINITE); - GetExitCodeProcess (child_pid, &res); -#endif - - g_spawn_close_pid (child_pid); - - if (res < 0 || res > 2) { - if (!only_once) { - /* Create wordlist.db */ - only_once = TRUE; - init_db (); - - goto retry; - } - g_set_error (error, EM_JUNK_ERROR, res, _("Pipe to Bogofilter failed, error code: %d."), res); - - } - - return res; -} - -static void -em_junk_bf_setting_notify (GConfClient *gconf, - guint cnxn_id, - GConfEntry *entry, - void *data) -{ - const gchar *key; - GConfValue *value; - - value = gconf_entry_get_value (entry); - if (value == NULL) { - return; - } - - key = gconf_entry_get_key (entry); - g_return_if_fail (key != NULL); - - g_return_if_fail (!strncmp (key, em_junk_bf_gconf_dir, EM_JUNK_BF_GCONF_DIR_LENGTH)); - key += EM_JUNK_BF_GCONF_DIR_LENGTH; - - g_return_if_fail (*key == '/'); - ++key; - - if (strcmp (key, "unicode") == 0) { - em_junk_bf_unicode = gconf_value_get_bool (value); - } -} - -gboolean -em_junk_bf_check_junk (EPlugin *ep, EMJunkTarget *target) -{ - CamelMimeMessage *msg = target->m; - gint rv; - - const gchar *argv[] = { - em_junk_bf_binary, - NULL, - NULL - }; - - if (!is_installed) - return FALSE; - - d(fprintf (stderr, "em_junk_bf_check_junk\n")); - - if (em_junk_bf_unicode) { - argv[1] = "--unicode=yes"; - } - - rv = pipe_to_bogofilter (msg, argv, NULL, &target->error); - - d(fprintf (stderr, "em_junk_bf_check_junk rv = %d\n", rv)); - - return (rv == 0); -} - -void -em_junk_bf_report_junk (EPlugin *ep, EMJunkTarget *target) -{ - CamelMimeMessage *msg = target->m; - - const gchar *argv[] = { - em_junk_bf_binary, - "-s", - NULL, - NULL - }; - - if (!is_installed) - return; - - d(fprintf (stderr, "em_junk_bf_report_junk\n")); - - if (em_junk_bf_unicode) { - argv[2] = "--unicode=yes"; - } - - pipe_to_bogofilter (msg, argv, NULL, &target->error); -} - -void -em_junk_bf_report_non_junk (EPlugin *ep, EMJunkTarget *target) -{ - CamelMimeMessage *msg = target->m; - - const gchar *argv[] = { - em_junk_bf_binary, - "-n", - NULL, - NULL - }; - - if (!is_installed) - return; - - d(fprintf (stderr, "em_junk_bf_report_non_junk\n")); - - if (em_junk_bf_unicode) { - argv[2] = "--unicode=yes"; - } - - pipe_to_bogofilter (msg, argv, NULL, &target->error); -} - -void -em_junk_bf_commit_reports (EPlugin *ep, EMJunkTarget *target) -{ - if (!is_installed) - return; -} - -gpointer -em_junk_bf_validate_binary (EPlugin *ep, EMJunkTarget *target) -{ - gpointer res = g_file_test (em_junk_bf_binary, G_FILE_TEST_EXISTS) ? (gpointer) "1" : NULL; - - if (res != NULL) - is_installed = TRUE; - - return res; -} - -gint -e_plugin_lib_enable (EPlugin *ep, gint enable) -{ - static gboolean first = TRUE; - GConfClient *gconf; - - is_installed = enable != 0; - - if (!first) - return 0; - - first = FALSE; - gconf = gconf_client_get_default (); - - gconf_client_add_dir (gconf, - em_junk_bf_gconf_dir, - GCONF_CLIENT_PRELOAD_ONELEVEL, - NULL); - - gconf_client_notify_add (gconf, - em_junk_bf_gconf_dir, - em_junk_bf_setting_notify, - NULL, NULL, NULL); - - em_junk_bf_unicode = gconf_client_get_bool (gconf, - EM_JUNK_BF_GCONF_DIR "/unicode", NULL); - - g_object_unref (gconf); - - return 0; -} - -static void -convert_unicode_cb (GtkWidget *widget, gpointer data) -{ - - gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); - GConfClient *gconf = gconf_client_get_default (); - - gconf_client_set_bool (gconf, data, active, NULL); - - g_object_unref (gconf); -} - -GtkWidget * -org_gnome_bogo_convert_unicode (struct _EPlugin *epl, struct _EConfigHookItemFactoryData *data) -{ - EShell *shell; - GtkWidget *check; - guint n_rows; - - g_object_get (data->parent, "n-rows", &n_rows, NULL); - - if (data->old) - return data->old; - - check = gtk_check_button_new_with_mnemonic (_("Convert message text to _Unicode")); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), em_junk_bf_unicode); - g_signal_connect (GTK_TOGGLE_BUTTON (check), "toggled", G_CALLBACK (convert_unicode_cb), (gpointer) "/apps/evolution/mail/junk/bogofilter/unicode"); - gtk_table_attach ( - GTK_TABLE (data->parent), check, - 0, 1, n_rows, n_rows+1, 0, 0, 0, 0); - - shell = e_shell_get_default (); - if (e_shell_get_express_mode (shell)) - gtk_widget_hide (check); - else - gtk_widget_show (check); - - return check; -} - diff --git a/plugins/bogo-junk-plugin/bogo-junk-plugin.schemas.in b/plugins/bogo-junk-plugin/bogo-junk-plugin.schemas.in deleted file mode 100644 index d4bfbb5b70..0000000000 --- a/plugins/bogo-junk-plugin/bogo-junk-plugin.schemas.in +++ /dev/null @@ -1,18 +0,0 @@ - - - - /schemas/apps/evolution/mail/junk/bogofilter/unicode - /apps/evolution/mail/junk/bogofilter/unicode - bf-eplugin - bool - true - - Convert mail messages to Unicode - - Convert message text to Unicode UTF-8 to unify spam/ham tokens - coming from different character sets. - - - - - diff --git a/plugins/bogo-junk-plugin/org-gnome-bogo-junk-plugin.eplug.xml b/plugins/bogo-junk-plugin/org-gnome-bogo-junk-plugin.eplug.xml deleted file mode 100644 index 1317b988d9..0000000000 --- a/plugins/bogo-junk-plugin/org-gnome-bogo-junk-plugin.eplug.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - <_description>Filter junk messages using Bogofilter. - - - - - - - - - - - - - - diff --git a/plugins/sa-junk-plugin/Makefile.am b/plugins/sa-junk-plugin/Makefile.am deleted file mode 100644 index 0452fcd6ec..0000000000 --- a/plugins/sa-junk-plugin/Makefile.am +++ /dev/null @@ -1,31 +0,0 @@ -@EVO_PLUGIN_RULE@ - -plugin_DATA = org-gnome-sa-junk-plugin.eplug - -plugin_LTLIBRARIES = liborg-gnome-sa-junk-plugin.la - -liborg_gnome_sa_junk_plugin_la_CPPFLAGS = \ - $(AM_CPPFLAGS) \ - -I$(top_srcdir) \ - -I$(top_srcdir)/widgets \ - $(GNOME_PLATFORM_CFLAGS) \ - $(EVOLUTION_MAIL_CFLAGS) - -liborg_gnome_sa_junk_plugin_la_SOURCES = em-junk-filter.c - -liborg_gnome_sa_junk_plugin_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) - -liborg_gnome_sa_junk_plugin_la_LIBADD = \ - $(top_builddir)/mail/libevolution-mail.la \ - $(top_builddir)/e-util/libeutil.la \ - $(top_builddir)/shell/libeshell.la \ - $(EVOLUTION_MAIL_LIBS) \ - $(GNOME_PLATFORM_LIBS) - -BUILT_SOURCES = $(plugin_DATA) $(error_DATA) - -CLEANFILES = $(BUILT_SOURCES) - -EXTRA_DIST = org-gnome-sa-junk-plugin.eplug.xml - --include $(top_srcdir)/git.mk diff --git a/plugins/sa-junk-plugin/em-junk-filter.c b/plugins/sa-junk-plugin/em-junk-filter.c deleted file mode 100644 index 635e1192c4..0000000000 --- a/plugins/sa-junk-plugin/em-junk-filter.c +++ /dev/null @@ -1,966 +0,0 @@ -/* - * 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 - * - * - * Authors: - * Radek Doulik - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include "mail/em-config.h" -#include "shell/e-shell.h" - -#include - -#define d(x) (camel_debug("junk")?(x):0) - -G_LOCK_DEFINE_STATIC (init); -G_LOCK_DEFINE_STATIC (report); -G_LOCK_DEFINE_STATIC (socket_path); -G_LOCK_DEFINE_STATIC (spamd_restart); - -gint e_plugin_lib_enable (EPlugin *ep, gint enable); -gboolean em_junk_sa_check_junk (EPlugin *ep, EMJunkTarget *target); -void em_junk_sa_report_junk (EPlugin *ep, EMJunkTarget *target); -void em_junk_sa_report_non_junk (EPlugin *ep, EMJunkTarget *target); -void em_junk_sa_commit_reports (EPlugin *ep); -gpointer em_junk_sa_validate_binary (EPlugin *ep); -GtkWidget *org_gnome_sa_use_remote_tests (struct _EPlugin *epl, struct _EConfigHookItemFactoryData *data); - -static void em_junk_sa_init (void); -static void em_junk_sa_finalize (void); -static void em_junk_sa_kill_spamd (void); - -static gboolean em_junk_sa_tested = FALSE; -static gboolean em_junk_sa_spamd_tested = FALSE; -static gboolean em_junk_sa_use_spamc = FALSE; -static gboolean em_junk_sa_available = FALSE; -static gboolean em_junk_sa_system_spamd_available = FALSE; -static gboolean em_junk_sa_new_daemon_started = FALSE; -static gboolean em_junk_sa_checked_spamassassin_version = FALSE; -static guint em_junk_sa_spamassassin_version = 0; -static gchar *em_junk_sa_socket_path = NULL; -static gchar *em_junk_sa_spamd_pidfile = NULL; -static const gchar *em_junk_sa_spamc_binary = NULL; -static GConfClient *em_junk_sa_gconf = NULL; - -/* volatile so not cached between threads */ -static volatile gboolean em_junk_sa_local_only; -static volatile gboolean em_junk_sa_use_daemon; -static gchar * em_junk_sa_preferred_socket_path; - -static const gchar *em_junk_sa_spamc_binaries [4] = {"spamc", "/usr/bin/spamc", "/usr/sbin/spamc", NULL}; -static const gchar *em_junk_sa_spamd_binaries [4] = {"spamd", "/usr/bin/spamd", "/usr/sbin/spamd", NULL}; - -#define SPAMD_RESTARTS_SIZE 8 -static time_t em_junk_sa_spamd_restarts[SPAMD_RESTARTS_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; -static gint em_junk_sa_spamd_restarts_count = 0; - -/* Variables to indicate whether spamd is running with --allow-tell */ -static gint no_allow_tell; -static gboolean em_junk_sa_allow_tell_tested = FALSE; -static gboolean is_installed = FALSE; - -gchar *em_junk_sa_spamc_gconf_binary = NULL; -gchar *em_junk_sa_spamd_gconf_binary = NULL; - -static gint -pipe_to_sa_full (CamelMimeMessage *msg, - const gchar *in, - const gchar **argv, - gint rv_err, - gint wait_for_termination, - GByteArray *output_buffer, - GCancellable *cancellable, - GError **error) -{ - gint result, status, errnosav, fds[2], out_fds[2]; - CamelStream *stream; - gchar *program; - pid_t pid; - - if (camel_debug_start ("junk")) { - gint i; - - printf ("pipe_to_sa "); - for (i = 0; argv[i]; i++) - printf ("%s ", argv[i]); - printf ("\n"); - camel_debug_end (); - } - - program = g_find_program_in_path (argv[0]); - if (program == NULL) { - d(printf ("program not found, returning %d\n", rv_err)); - g_set_error (error, EM_JUNK_ERROR, rv_err, _("SpamAssassin not found, code: %d"), rv_err); - return rv_err; - } - g_free (program); - - if (pipe (fds) == -1) { - errnosav = errno; - d(printf ("failed to create a pipe (for use with SpamAssassin: %s\n", g_strerror (errno))); - g_set_error (error, EM_JUNK_ERROR, errnosav, _("Failed to create pipe: %s"), g_strerror (errnosav)); - errno = errnosav; - return rv_err; - } - - if (output_buffer && pipe (out_fds) == -1) { - errnosav = errno; - d(printf ("failed to create a pipe (for use with SpamAssassin: %s\n", g_strerror (errno))); - g_set_error (error, EM_JUNK_ERROR, errnosav, _("Failed to create pipe: %s"), g_strerror (errnosav)); - close (fds[0]); - close (fds[1]); - errno = errnosav; - return rv_err; - } - - if (!(pid = fork ())) { - /* child process */ - gint maxfd, fd, nullfd; - - nullfd = open ("/dev/null", O_WRONLY); - - if (dup2 (fds[0], STDIN_FILENO) == -1 || - dup2 (nullfd, STDERR_FILENO) == -1 || - (output_buffer == NULL && dup2 (nullfd, STDOUT_FILENO) == -1) || - (output_buffer != NULL && dup2 (out_fds[1], STDOUT_FILENO) == -1)) - _exit (rv_err & 0377); - close (fds[0]); - if (output_buffer) - close (out_fds[1]); - - setsid (); - - maxfd = sysconf (_SC_OPEN_MAX); - for (fd = 3; fd < maxfd; fd++) - fcntl (fd, F_SETFD, FD_CLOEXEC); - - execvp (argv[0], (gchar * const *) argv); - _exit (rv_err & 0377); - } else if (pid < 0) { - errnosav = errno; - close (fds[0]); - close (fds[1]); - if (output_buffer) { - close (out_fds[0]); - close (out_fds[1]); - } - if (errnosav != 0 && errnosav != -1) - g_set_error (error, EM_JUNK_ERROR, errnosav, _("Error after fork: %s"), g_strerror (errnosav)); - errno = errnosav; - return rv_err; - } - - /* parent process */ - close (fds[0]); - if (output_buffer) - close (out_fds[1]); - - if (msg) { - stream = camel_stream_fs_new_with_fd (fds[1]); - - camel_data_wrapper_write_to_stream_sync ( - CAMEL_DATA_WRAPPER (msg), stream, cancellable, NULL); - camel_stream_flush (stream, cancellable, NULL); - camel_stream_close (stream, cancellable, NULL); - g_object_unref (stream); - } else if (in) { - camel_write (fds[1], in, strlen (in), cancellable, NULL); - close (fds[1]); - } - - if (output_buffer) { - CamelStream *memstream; - - stream = camel_stream_fs_new_with_fd (out_fds[0]); - - memstream = camel_stream_mem_new (); - camel_stream_mem_set_byte_array ( - CAMEL_STREAM_MEM (memstream), output_buffer); - - camel_stream_write_to_stream ( - stream, memstream, cancellable, NULL); - g_object_unref (stream); - g_byte_array_append (output_buffer, (guchar *)"", 1); - - d(printf ("child process output: %s len: %d\n", output_buffer->data, output_buffer->len)); - } - - if (wait_for_termination) { - gint res; - - d(printf ("wait for child %d termination\n", pid)); - result = waitpid (pid, &status, 0); - - d(printf ("child %d terminated with result %d status %d exited %d exitstatus %d\n", pid, result, status, WIFEXITED (status), WEXITSTATUS (status))); - - 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); - g_set_error (error, EM_JUNK_ERROR, -2, _("SpamAssassin child process does not respond, killing...")); - } else - g_set_error (error, EM_JUNK_ERROR, -3, _("Wait for SpamAssassin child process interrupted, terminating...")); - } - - if (result != -1 && WIFEXITED (status)) - res = WEXITSTATUS (status); - else - res = rv_err; - - if (res >= 64) - g_set_error (error, EM_JUNK_ERROR, res, _("Pipe to SpamAssassin failed, error code: %d"), res); - - return res; - } else - return 0; -} - -static gint -pipe_to_sa (CamelMimeMessage *msg, - const gchar *in, - const gchar **argv, - GCancellable *cancellable, - GError **error) -{ - return pipe_to_sa_full (msg, in, argv, -1, 1, NULL, cancellable, error); -} - -static gchar * -em_junk_sa_get_socket_path () -{ - if (em_junk_sa_preferred_socket_path) - return em_junk_sa_preferred_socket_path; - else - return em_junk_sa_socket_path; -} - -static gboolean -em_junk_sa_test_spamd_running (const gchar *binary, gboolean system) -{ - const gchar *argv[5]; - gint i = 0; - gboolean rv; - - G_LOCK (socket_path); - - d(fprintf (stderr, "test if spamd is running (system %d) or using socket path %s\n", system, em_junk_sa_get_socket_path ())); - - argv[i++] = binary; - argv[i++] = "-x"; - - if (!system) { - argv[i++] = "-U"; - argv[i++] = em_junk_sa_get_socket_path (); - } - - argv[i] = NULL; - - rv = pipe_to_sa (NULL, "From test@127.0.0.1", argv, NULL, NULL) == 0; - - d(fprintf (stderr, "result: %d (%s)\n", rv, rv ? "success" : "failed")); - - G_UNLOCK (socket_path); - - return rv; -} - -/* - One time test to see if spamd is running with --allow-tell. The call - to spamc should return 0 if it is. (Thanks to Karsten Bräckelmann - for the idea). -*/ -static void -em_junk_sa_test_allow_tell (void) -{ - const gchar *argv[4] = { - "spamc", - "-L", - "forget", - NULL - }; - - no_allow_tell = pipe_to_sa (NULL, "\n" , argv, NULL, NULL); - em_junk_sa_allow_tell_tested = TRUE; -} - -static void -em_junk_sa_test_spamassassin (void) -{ - const gchar *argv[3] = { - "spamassassin", - "--version", - NULL, - }; - - if (pipe_to_sa (NULL, NULL, argv, NULL, NULL) != 0) - em_junk_sa_available = FALSE; - else - em_junk_sa_available = TRUE; - - em_junk_sa_tested = TRUE; -} - -#define MAX_SPAMD_PORTS 1 - -static gboolean -em_junk_sa_run_spamd (const gchar *binary) -{ - const gchar *argv[8]; - gint i; - gboolean rv = FALSE; - - G_LOCK (socket_path); - - d(fprintf (stderr, "looks like spamd is not running\n")); - - i = 0; - argv[i++] = binary; - argv[i++] = "--socketpath"; - argv[i++] = em_junk_sa_get_socket_path (); - - if (em_junk_sa_local_only) - argv[i++] = "--local"; - - /* See bug #268852*/ - argv[i++] = "--max-children=1"; - /*argv[i++] = "--daemonize";*/ - argv[i++] = "--pidfile"; - argv[i++] = em_junk_sa_spamd_pidfile; - argv[i] = NULL; - - d(fprintf (stderr, "trying to run %s with socket path %s\n", binary, em_junk_sa_get_socket_path ())); - - if (!pipe_to_sa_full (NULL, NULL, argv, -1, 0, NULL, NULL, NULL)) { - struct timespec time_req; - struct stat stat_buf; - - d(fprintf (stderr, "success\n")); - d(fprintf (stderr, "waiting for spamd to come up\n")); - - time_req.tv_sec = 0; - time_req.tv_nsec = 50000000; - - for (i = 0; i < 100; i++) { - if (stat (em_junk_sa_get_socket_path (), &stat_buf) == 0) { - d(fprintf (stderr, "socket created\n")); - break; - } - nanosleep (&time_req, NULL); - } - d(fprintf (stderr, "waiting is over (after %dms)\n", 50*i)); - - rv = TRUE; - } - - G_UNLOCK (socket_path); - - return rv; -} - -static void -em_junk_sa_start_own_daemon () -{ - gint b; - - em_junk_sa_new_daemon_started = FALSE; - - em_junk_sa_socket_path = e_mktemp ("spamd-socket-path-XXXXXX"); - em_junk_sa_spamd_pidfile = e_mktemp ("spamd-pid-file-XXXXXX"); - - for (b = 0; em_junk_sa_spamd_binaries[b]; b++) { - em_junk_sa_use_spamc = em_junk_sa_run_spamd (em_junk_sa_spamd_binaries[b]); - if (em_junk_sa_use_spamc) { - em_junk_sa_new_daemon_started = TRUE; - break; - } - } -} - -static void -em_junk_sa_find_spamc () -{ - if (em_junk_sa_use_spamc && em_junk_sa_new_daemon_started) { - gint b; - - em_junk_sa_use_spamc = FALSE; - for (b = 0; em_junk_sa_spamc_binaries[b]; b++) { - em_junk_sa_spamc_binary = em_junk_sa_spamc_binaries[b]; - if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, FALSE)) { - em_junk_sa_use_spamc = TRUE; - break; - } - } - } -} - -static void -em_junk_sa_test_spamd (void) -{ - const gchar *argv[4]; - gint i, b; - gboolean try_system_spamd = TRUE; - - if (em_junk_sa_spamc_gconf_binary) { - em_junk_sa_spamc_binaries[0] = em_junk_sa_spamc_gconf_binary; - em_junk_sa_spamc_binaries[1] = NULL; - } - - if (em_junk_sa_spamd_gconf_binary) { - em_junk_sa_spamd_binaries[0] = em_junk_sa_spamd_gconf_binary; - em_junk_sa_spamd_binaries[1] = NULL; - try_system_spamd = FALSE; - } - - em_junk_sa_use_spamc = FALSE; - - if (em_junk_sa_local_only && try_system_spamd) { - i = 0; - argv [i++] = "/bin/sh"; - argv [i++] = "-c"; - argv [i++] = "ps ax|grep -v grep|grep -E 'spamd.*(\\-L|\\-\\-local)'|grep -E -v '\\ \\-p\\ |\\ \\-\\-port\\ '"; - argv[i] = NULL; - - if (pipe_to_sa (NULL, NULL, argv, NULL, NULL) != 0) { - try_system_spamd = FALSE; - d(fprintf (stderr, "there's no system spamd with -L/--local parameter running\n")); - } - } - - /* try to use sytem spamd first */ - if (try_system_spamd) { - for (b = 0; em_junk_sa_spamc_binaries[b]; b++) { - em_junk_sa_spamc_binary = em_junk_sa_spamc_binaries[b]; - if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, TRUE)) { - em_junk_sa_use_spamc = TRUE; - em_junk_sa_system_spamd_available = TRUE; - break; - } - } - } - - /* if there's no system spamd running, try to use user one with user specified socket */ - if (!em_junk_sa_use_spamc && em_junk_sa_preferred_socket_path) { - for (b = 0; em_junk_sa_spamc_binaries[b]; b++) { - em_junk_sa_spamc_binary = em_junk_sa_spamc_binaries[b]; - if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, FALSE)) { - em_junk_sa_use_spamc = TRUE; - em_junk_sa_system_spamd_available = FALSE; - break; - } - } - } - - /* unsuccessful? try to run one ourselfs */ - if (!em_junk_sa_use_spamc) - em_junk_sa_start_own_daemon (); - - /* new daemon started => let find spamc binary */ - em_junk_sa_find_spamc (); - - d(fprintf (stderr, "use spamd: %s\n", em_junk_sa_use_spamc ? "yes" : "no")); - - em_junk_sa_spamd_tested = TRUE; -} - -static gboolean -em_junk_sa_is_available (GError **error) -{ - G_LOCK (init); - - if (!em_junk_sa_tested) - em_junk_sa_test_spamassassin (); - - if (em_junk_sa_available && !em_junk_sa_spamd_tested && em_junk_sa_use_daemon) - em_junk_sa_test_spamd (); - - if (!em_junk_sa_available && error) { - if (is_installed) - g_set_error (error, EM_JUNK_ERROR, 1, _("SpamAssassin is not available. Please install it first.")); - - is_installed = FALSE; - } - - /* While we're at it, see if spamd is running with --allow-tell */ - if (!em_junk_sa_allow_tell_tested) - em_junk_sa_test_allow_tell (); - - G_UNLOCK (init); - - return em_junk_sa_available; -} - -static gboolean -em_junk_sa_check_respawn_too_fast () -{ - time_t time_now = time (NULL); - gboolean rv; - - G_LOCK (spamd_restart); - - if (em_junk_sa_spamd_restarts_count >= SPAMD_RESTARTS_SIZE) { - /* all restarts in last 5 minutes */ - rv = (time_now - em_junk_sa_spamd_restarts[em_junk_sa_spamd_restarts_count % SPAMD_RESTARTS_SIZE] < 5*60); - } else - rv = FALSE; - - em_junk_sa_spamd_restarts[em_junk_sa_spamd_restarts_count % SPAMD_RESTARTS_SIZE] = time_now; - em_junk_sa_spamd_restarts_count++; - - G_UNLOCK (spamd_restart); - - d(printf ("em_junk_sa_check_respawn_too_fast: %d\n", rv)); - - return rv; -} - -static gboolean -em_junk_sa_respawn_spamd () -{ - d(printf ("em_junk_sa_respawn_spamd\n")); - if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, em_junk_sa_system_spamd_available)) { - /* false alert */ - d(printf ("false alert, spamd still running\n")); - - return FALSE; - } - - d(printf ("going to kill old spamd and start new one\n")); - em_junk_sa_kill_spamd (); - - if (em_junk_sa_check_respawn_too_fast ()) { - g_warning ("respawning of spamd too fast => fallback to use SpamAssassin directly"); - - em_junk_sa_use_spamc = em_junk_sa_use_daemon = FALSE; - return FALSE; - } - - em_junk_sa_start_own_daemon (); - em_junk_sa_find_spamc (); - - d(printf ("%s\n", em_junk_sa_use_spamc ? "success" : "failed")); - - return em_junk_sa_use_spamc; -} - -gboolean -em_junk_sa_check_junk (EPlugin *ep, EMJunkTarget *target) -{ - GByteArray *out = NULL; - const gchar *argv[7]; - gchar *to_free = NULL; - gint i = 0, socket_i; - gboolean rv; - CamelMimeMessage *msg = target->m; - - if (!is_installed) - return FALSE; - - d(fprintf (stderr, "em_junk_sa_check_junk\n")); - - if (!em_junk_sa_is_available (&target->error)) { - return FALSE; - } - - if (em_junk_sa_use_spamc && em_junk_sa_use_daemon) { - out = g_byte_array_new (); - argv[i++] = em_junk_sa_spamc_binary; - argv[i++] = "-c"; - argv[i++] = "-t"; - argv[i++] = "60"; - if (!em_junk_sa_system_spamd_available) { - argv[i++] = "-U"; - - G_LOCK (socket_path); - socket_i = i; - argv[i++] = to_free = g_strdup (em_junk_sa_get_socket_path ()); - G_UNLOCK (socket_path); - } - } else { - argv [i++] = "spamassassin"; - argv [i++] = "--exit-code"; - if (em_junk_sa_local_only) - argv [i++] = "--local"; - } - - argv[i] = NULL; - - rv = pipe_to_sa_full (msg, NULL, argv, 0, 1, out, NULL, &target->error) != 0; - - if (!rv && out && out->data && !strcmp ((const gchar *)out->data, "0/0\n")) { - /* an error occurred */ - if (em_junk_sa_respawn_spamd ()) { - g_byte_array_set_size (out, 0); - - G_LOCK (socket_path); - g_free (to_free); - argv[socket_i] = to_free = g_strdup (em_junk_sa_get_socket_path ()); - G_UNLOCK (socket_path); - - rv = pipe_to_sa_full (msg, NULL, argv, 0, 1, out, NULL, &target->error) != 0; - } else if (!em_junk_sa_use_spamc) - /* in case respawning were too fast we fallback to spamassassin */ - rv = em_junk_sa_check_junk (ep, target); - } - - g_free (to_free); - - d(fprintf (stderr, "em_junk_sa_check_junk rv = %d\n", rv)); - - if (out) - g_byte_array_free (out, TRUE); - - return rv; -} - -static guint -get_spamassassin_version () -{ - GByteArray *out = NULL; - gint i; - - const gchar *argv[3] = { - "sa-learn", - "--version", - NULL - }; - - if (!em_junk_sa_checked_spamassassin_version) { - out = g_byte_array_new (); - - if (pipe_to_sa_full (NULL, NULL, argv, -1, 1, out, NULL, NULL) != 0) { - if (out) - g_byte_array_free (out, TRUE); - return em_junk_sa_spamassassin_version; - } - - if (out->len > 0) { - for (i = 0; i < out->len; i++) { - if (g_ascii_isdigit (out->data[i])) { - em_junk_sa_spamassassin_version = (out->data[i] - '0'); - em_junk_sa_checked_spamassassin_version = TRUE; - break; - } - } - } - - if (out) - g_byte_array_free (out, TRUE); - } - - return em_junk_sa_spamassassin_version; -} - -void -em_junk_sa_report_junk (EPlugin *ep, EMJunkTarget *target) -{ - const gchar *sync_op = - (get_spamassassin_version () >= 3) - ? "--no-sync": "--no-rebuild"; - const gchar *argv[6] = { - "sa-learn", - sync_op, - "--spam", - "--single", - NULL, - NULL - }; - /* Call setup for spamc */ - const gchar *argv2[4] = { - "spamc", - "-L", - "spam", - NULL - }; - CamelMimeMessage *msg = target->m; - - if (!is_installed) - return; - - d(fprintf (stderr, "em_junk_sa_report_junk\n")); - if (em_junk_sa_is_available (&target->error)) { - if (no_allow_tell && em_junk_sa_local_only) - argv[4] = "--local"; - - G_LOCK (report); - pipe_to_sa (msg, NULL, - (no_allow_tell ? argv : argv2), - NULL, &target->error); - G_UNLOCK (report); - } -} - -void -em_junk_sa_report_non_junk (EPlugin *ep, EMJunkTarget *target) -{ - const gchar *sync_op = - (get_spamassassin_version () >= 3) - ? "--no-sync": "--no-rebuild"; - const gchar *argv[6] = { - "sa-learn", - sync_op, - "--ham", - "--single", - NULL, - NULL - }; - /* Setup for spamc */ - const gchar *argv2[4] = { - "spamc", - "-L", - "ham", - NULL - }; - CamelMimeMessage *msg = target->m; - - if (!is_installed) - return; - - d(fprintf (stderr, "em_junk_sa_report_notjunk\n")); - - if (em_junk_sa_is_available (&target->error)) { - - if (no_allow_tell && em_junk_sa_local_only) - argv[4] = "--local"; - G_LOCK (report); - pipe_to_sa (msg, NULL, - (no_allow_tell ? argv : argv2), - NULL, &target->error); - G_UNLOCK (report); - } -} - -void -em_junk_sa_commit_reports (EPlugin *ep) -{ - const gchar *sync_op = - (get_spamassassin_version () >= 3) ? "--sync": "--rebuild"; - const gchar *argv[4] = { - "sa-learn", - sync_op, - NULL, - NULL - }; - - /* Only meaningful if we're using sa-learn */ - if (!no_allow_tell || !is_installed) - return; - - d(fprintf (stderr, "em_junk_sa_commit_reports\n")); - - if (em_junk_sa_is_available (NULL)) { - if (em_junk_sa_local_only) - argv[2] = "--local"; - - G_LOCK (report); - pipe_to_sa (NULL, NULL, argv, NULL, NULL); - G_UNLOCK (report); - } -} - -gpointer -em_junk_sa_validate_binary (EPlugin *ep) -{ - gpointer res = em_junk_sa_is_available (NULL) ? (gpointer) "1" : NULL; - - if (res != NULL) - is_installed = TRUE; - - return res; -} - -static void -em_junk_sa_setting_notify (GConfClient *gconf, guint cnxn_id, GConfEntry *entry, gpointer data) -{ - GConfValue *value; - gchar *tkey; - - g_return_if_fail (gconf_entry_get_key (entry) != NULL); - - if (!(value = gconf_entry_get_value (entry))) - return; - - tkey = strrchr (entry->key, '/'); - g_return_if_fail (tkey != NULL); - - if (!strcmp(tkey, "local_only")) - em_junk_sa_local_only = gconf_value_get_bool (value); - else if (!strcmp(tkey, "use_daemon")) - em_junk_sa_use_daemon = gconf_value_get_bool (value); - else if (!strcmp(tkey, "socket_path")) { - G_LOCK (socket_path); - g_free (em_junk_sa_preferred_socket_path); - em_junk_sa_preferred_socket_path = g_strdup (gconf_value_get_string (value)); - G_UNLOCK (socket_path); - } -} - -gint -e_plugin_lib_enable (EPlugin *ep, gint enable) -{ - is_installed = enable != 0; - - if (is_installed) - em_junk_sa_tested = FALSE; - - em_junk_sa_init (); - - return 0; -} - -static void -em_junk_sa_init (void) -{ - G_LOCK (init); - - if (!em_junk_sa_gconf) { - em_junk_sa_gconf = gconf_client_get_default (); - gconf_client_add_dir (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); - - em_junk_sa_local_only = gconf_client_get_bool (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/local_only", NULL); - em_junk_sa_use_daemon = gconf_client_get_bool (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/use_daemon", NULL); - - G_LOCK (socket_path); - g_free (em_junk_sa_preferred_socket_path); - em_junk_sa_preferred_socket_path = gconf_client_get_string (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/socket_path", NULL); - G_UNLOCK (socket_path); - - gconf_client_notify_add(em_junk_sa_gconf, "/apps/evolution/mail/junk/sa", - (GConfClientNotifyFunc) em_junk_sa_setting_notify, - NULL, NULL, NULL); - - em_junk_sa_spamc_gconf_binary = gconf_client_get_string (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/spamc_binary", NULL); - em_junk_sa_spamd_gconf_binary = gconf_client_get_string (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/spamd_binary", NULL); - - atexit (em_junk_sa_finalize); - } - - G_UNLOCK (init); -} - -static void -em_junk_sa_kill_spamd (void) -{ - G_LOCK (socket_path); - g_free (em_junk_sa_preferred_socket_path); - em_junk_sa_preferred_socket_path = NULL; - G_UNLOCK (socket_path); - - if (em_junk_sa_new_daemon_started) { - gint fd = open (em_junk_sa_spamd_pidfile, O_RDONLY); - - if (fd != -1) { - gchar pid_str[16]; - gint bytes; - - bytes = read (fd, pid_str, 15); - if (bytes > 0) { - gint pid; - - pid_str[bytes] = 0; - pid = atoi (pid_str); - - if (pid > 0) { - kill (pid, SIGTERM); - d(fprintf (stderr, "em_junk_sa_finalize send SIGTERM to daemon with pid %d\n", pid)); - waitpid (pid, NULL, 0); - } - } - - close (fd); - } - } -} - -static void -em_junk_sa_finalize (void) -{ - d(fprintf (stderr, "em_junk_sa_finalize\n")); - - g_object_unref (em_junk_sa_gconf); - em_junk_sa_kill_spamd (); -} - -static void -use_remote_tests_cb (GtkWidget *widget, gpointer data) -{ - gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); - gconf_client_set_bool (em_junk_sa_gconf, data, !active, NULL); -} - -GtkWidget * -org_gnome_sa_use_remote_tests (struct _EPlugin *epl, struct _EConfigHookItemFactoryData *data) -{ - EShell *shell; - GtkWidget *check, *vbox, *label; - gchar *text = g_strdup_printf (" %s", _("This will make SpamAssassin more reliable, but slower")); - guint n_rows; - - g_object_get (data->parent, "n-rows", &n_rows, NULL); - - if (data->old) - return data->old; - - check = gtk_check_button_new_with_mnemonic (_("I_nclude remote tests")); - label = gtk_label_new (NULL); - gtk_label_set_markup (GTK_LABEL (label), text); - g_free (text); - vbox = gtk_vbox_new (FALSE, 2); - gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (check), FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (label), FALSE, FALSE, 0); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), !em_junk_sa_local_only); - g_signal_connect (GTK_TOGGLE_BUTTON (check), "toggled", G_CALLBACK (use_remote_tests_cb), (gpointer) "/apps/evolution/mail/junk/sa/local_only"); - gtk_table_attach ( - GTK_TABLE (data->parent), vbox, - 0, 1, n_rows, n_rows+1, 0, 0, 0, 0); - - shell = e_shell_get_default (); - if (e_shell_get_express_mode (shell)) - gtk_widget_hide (vbox); - else - gtk_widget_show_all (vbox); - - return vbox; -} - diff --git a/plugins/sa-junk-plugin/org-gnome-sa-junk-plugin.eplug.xml b/plugins/sa-junk-plugin/org-gnome-sa-junk-plugin.eplug.xml deleted file mode 100644 index 83ed170c74..0000000000 --- a/plugins/sa-junk-plugin/org-gnome-sa-junk-plugin.eplug.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - <_description>Filter junk messages using SpamAssassin. - - - - - - - - - - - - - -- cgit v1.2.3