diff options
author | Radek Doulik <rodo@src.gnome.org> | 2003-11-13 05:13:05 +0800 |
---|---|---|
committer | Radek Doulik <rodo@src.gnome.org> | 2003-11-13 05:13:05 +0800 |
commit | 8e1251fa17b522d0539a8fcfb7463ba8cef1b31a (patch) | |
tree | 9be2f3a8a184446361a8c72879947f7a6e7d8d75 | |
parent | 8187001a14295b4b64cc3e973fb1ab1e551133f2 (diff) | |
download | gsoc2013-evolution-8e1251fa17b522d0539a8fcfb7463ba8cef1b31a.tar.gz gsoc2013-evolution-8e1251fa17b522d0539a8fcfb7463ba8cef1b31a.tar.zst gsoc2013-evolution-8e1251fa17b522d0539a8fcfb7463ba8cef1b31a.zip |
merged spam filtering branch
svn path=/trunk/; revision=23302
43 files changed, 1056 insertions, 93 deletions
diff --git a/art/Makefile.am b/art/Makefile.am index bd96fd0759..e17e7bba91 100644 --- a/art/Makefile.am +++ b/art/Makefile.am @@ -45,6 +45,8 @@ images_DATA = \ evolution-today.png \ evolution-trash-mini.png \ evolution-trash.png \ + evolution-junk.png \ + evolution-junk-mini.png \ executive-summary-bg.png \ executive-summary-curve.png \ info-bulb.png \ diff --git a/art/evolution-junk-mini.png b/art/evolution-junk-mini.png Binary files differnew file mode 100644 index 0000000000..64d488ca1c --- /dev/null +++ b/art/evolution-junk-mini.png diff --git a/art/evolution-junk.png b/art/evolution-junk.png Binary files differnew file mode 100644 index 0000000000..5fefbefc56 --- /dev/null +++ b/art/evolution-junk.png diff --git a/camel/Makefile.am b/camel/Makefile.am index a1d00a1323..eee87b84db 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -47,6 +47,7 @@ libcamel_la_SOURCES = \ camel-http-stream.c \ camel-index.c \ camel-internet-address.c \ + camel-junk-plugin.c \ camel-lock.c \ camel-lock-client.c \ camel-medium.c \ @@ -148,6 +149,7 @@ libcamelinclude_HEADERS = \ camel-index.h \ camel-internet-address.h \ camel-i18n.h \ + camel-junk-plugin.h \ camel-lock.h \ camel-lock-client.h \ camel-medium.h \ diff --git a/camel/camel-digest-store.c b/camel/camel-digest-store.c index e3870e3672..833593ac9f 100644 --- a/camel/camel-digest-store.c +++ b/camel/camel-digest-store.c @@ -40,6 +40,8 @@ static void digest_delete_folder (CamelStore *store, const char *folder_name, Ca static void digest_rename_folder (CamelStore *store, const char *old, const char *new, CamelException *ex); static void digest_init_trash (CamelStore *store); static CamelFolder *digest_get_trash (CamelStore *store, CamelException *ex); +static void digest_init_junk (CamelStore *store); +static CamelFolder *digest_get_junk (CamelStore *store, CamelException *ex); static CamelFolderInfo *digest_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex); @@ -92,6 +94,8 @@ camel_digest_store_class_init (CamelDigestStoreClass *klass) store_class->init_trash = digest_init_trash; store_class->get_trash = digest_get_trash; + store_class->init_junk = digest_init_junk; + store_class->get_junk = digest_get_junk; } static void @@ -99,8 +103,8 @@ camel_digest_store_init (CamelDigestStore *obj) { CamelStore *store = (CamelStore *) obj; - /* we dont want a vtrash on this one */ - store->flags &= ~(CAMEL_STORE_VTRASH); + /* we dont want a vtrash and vjunk on this one */ + store->flags &= ~(CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK); } static void @@ -167,6 +171,19 @@ digest_get_trash (CamelStore *store, CamelException *ex) return NULL; } +static void +digest_init_junk (CamelStore *store) +{ + /* no-op */ + ; +} + +static CamelFolder * +digest_get_junk (CamelStore *store, CamelException *ex) +{ + return NULL; +} + static CamelFolderInfo * digest_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex) { diff --git a/camel/camel-filter-driver.c b/camel/camel-filter-driver.c index ff6a8a88d2..bd3478e64e 100644 --- a/camel/camel-filter-driver.c +++ b/camel/camel-filter-driver.c @@ -809,7 +809,6 @@ pipe_message (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilte return NULL; } - static ESExpResult * do_shell (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFilterDriver *driver) { diff --git a/camel/camel-filter-search.c b/camel/camel-filter-search.c index 8af3392e71..5897239164 100644 --- a/camel/camel-filter-search.c +++ b/camel/camel-filter-search.c @@ -91,6 +91,7 @@ static ESExpResult *get_current_date (struct _ESExp *f, int argc, struct _ESExpR static ESExpResult *header_source (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms); static ESExpResult *get_size (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms); static ESExpResult *pipe_message (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms); +static ESExpResult *junk_test (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms); /* builtin functions */ static struct { @@ -119,6 +120,7 @@ static struct { { "header-source", (ESExpFunc *) header_source, 0 }, { "get-size", (ESExpFunc *) get_size, 0 }, { "pipe-message", (ESExpFunc *) pipe_message, 0 }, + { "junk-test", (ESExpFunc *) junk_test, 0 }, }; @@ -609,6 +611,23 @@ pipe_message (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMess return r; } +static ESExpResult * +junk_test (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms) +{ + ESExpResult *r; + gboolean retval = FALSE; + + if (fms->session->junk_plugin != NULL) { + retval = camel_junk_plugin_check_junk (fms->session->junk_plugin, camel_filter_search_get_message (fms, f)); + + fprintf (stderr, "junk filter => %s\n", retval ? "*JUNK*" : "clean"); + } + r = e_sexp_result_new (f, ESEXP_RES_BOOL); + r->value.number = retval; + + return r; +} + /** * camel_filter_search_match: diff --git a/camel/camel-folder-summary.c b/camel/camel-folder-summary.c index 3ad9eb31ad..05b3f3a056 100644 --- a/camel/camel-folder-summary.c +++ b/camel/camel-folder-summary.c @@ -2503,6 +2503,7 @@ struct flag_names_t { { "flagged", CAMEL_MESSAGE_FLAGGED }, { "seen", CAMEL_MESSAGE_SEEN }, { "attachments", CAMEL_MESSAGE_ATTACHMENTS }, + { "junk", CAMEL_MESSAGE_JUNK }, { NULL, 0 } }; diff --git a/camel/camel-folder-summary.h b/camel/camel-folder-summary.h index 48713b1a3c..e57be75ae1 100644 --- a/camel/camel-folder-summary.h +++ b/camel/camel-folder-summary.h @@ -64,6 +64,7 @@ enum _CamelMessageFlags { CAMEL_MESSAGE_SEEN = 1<<4, CAMEL_MESSAGE_ATTACHMENTS = 1<<5, CAMEL_MESSAGE_ANSWERED_ALL = 1<<6, + CAMEL_MESSAGE_JUNK = 1<<7, /* following flags are for the folder, and are not really permanent flags */ CAMEL_MESSAGE_FOLDER_FLAGGED = 1<<16, /* for use by the folder implementation */ diff --git a/camel/camel-folder.c b/camel/camel-folder.c index f682ee25ca..d8fe518f71 100644 --- a/camel/camel-folder.c +++ b/camel/camel-folder.c @@ -549,7 +549,7 @@ get_unread_message_count(CamelFolder *folder) CamelMessageInfo *info = camel_folder_summary_index(folder->summary, i); if (info) { - if (!(info->flags & CAMEL_MESSAGE_SEEN)) + if (!(info->flags & CAMEL_MESSAGE_SEEN) && (!(info->flags & CAMEL_MESSAGE_JUNK) || (folder->folder_flags & CAMEL_FOLDER_IS_JUNK))) unread++; camel_folder_summary_info_free(folder->summary, info); } diff --git a/camel/camel-folder.h b/camel/camel-folder.h index f1fe9bfb63..83df665147 100644 --- a/camel/camel-folder.h +++ b/camel/camel-folder.h @@ -103,6 +103,7 @@ struct _CamelFolder #define CAMEL_FOLDER_FILTER_RECENT (1<<2) #define CAMEL_FOLDER_HAS_BEEN_DELETED (1<<3) #define CAMEL_FOLDER_IS_TRASH (1<<4) +#define CAMEL_FOLDER_IS_JUNK (1<<5) typedef struct { CamelObjectClass parent_class; diff --git a/camel/camel-junk-plugin.c b/camel/camel-junk-plugin.c new file mode 100644 index 0000000000..a203b45c3e --- /dev/null +++ b/camel/camel-junk-plugin.c @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <stdio.h> +#include <glib.h> +#include <camel/camel-junk-plugin.h> +#include <camel/camel-mime-message.h> + +#define d(x) x + +const char * +camel_junk_plugin_get_name (CamelJunkPlugin *csp) +{ + g_return_val_if_fail (csp->get_name != NULL, NULL); + + d(fprintf (stderr, "camel_junk_plugin_get_namen");) + + return (*csp->get_name) (); +} + +int +camel_junk_plugin_check_junk (CamelJunkPlugin *csp, CamelMimeMessage *message) +{ + g_return_val_if_fail (csp->check_junk != NULL, FALSE); + + d(fprintf (stderr, "camel_junk_plugin_check_junk\n");) + + return (*csp->check_junk) (message); +} + +void +camel_junk_plugin_report_junk (CamelJunkPlugin *csp, CamelMimeMessage *message) +{ + d(fprintf (stderr, "camel_junk_plugin_report_junk\n");) + + if (csp->report_junk) + (*csp->report_junk) (message); +} + +void +camel_junk_plugin_report_notjunk (CamelJunkPlugin *csp, CamelMimeMessage *message) +{ + d(fprintf (stderr, "camel_junk_plugin_report_notjunk\n");) + + if (csp->report_notjunk) + (*csp->report_notjunk) (message); +} + +void +camel_junk_plugin_commit_reports (CamelJunkPlugin *csp) +{ + d(fprintf (stderr, "camel_junk_plugin_commit_reports\n");) + + if (csp->commit_reports) + (*csp->commit_reports) (); +} diff --git a/camel/camel-junk-plugin.h b/camel/camel-junk-plugin.h new file mode 100644 index 0000000000..79d1bf6331 --- /dev/null +++ b/camel/camel-junk-plugin.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _CAMEL_JUNK_PLUGIN_H +#define _CAMEL_JUNK_PLUGIN_H + +#define CAMEL_JUNK_PLUGIN(x) ((CamelJunkPlugin *) x) + +typedef struct _CamelJunkPlugin CamelJunkPlugin; +struct _CamelMimeMessage; + +struct _CamelJunkPlugin +{ + /* junk filter human readable name, translated */ + const char * (*get_name) (void); + + /* should be set to 1 */ + int api_version; + + /* when called, it should return TRUE if message is identified as junk, + FALSE otherwise */ + int (*check_junk) (struct _CamelMimeMessage *message); + + /* called when user identified a message to be junk */ + void (*report_junk) (struct _CamelMimeMessage *message); + + /* called when user identified a message not to be junk */ + void (*report_notjunk) (struct _CamelMimeMessage *message); + + /* called after one or more junk/ham(s) reported */ + void (*commit_reports) (void); +}; + +const char * camel_junk_plugin_get_name (CamelJunkPlugin *csp); +int camel_junk_plugin_check_junk (CamelJunkPlugin *csp, struct _CamelMimeMessage *message); +void camel_junk_plugin_report_junk (CamelJunkPlugin *csp, struct _CamelMimeMessage *message); +void camel_junk_plugin_report_notjunk (CamelJunkPlugin *csp, struct _CamelMimeMessage *message); +void camel_junk_plugin_commit_reports (CamelJunkPlugin *csp); + +#endif diff --git a/camel/camel-session.h b/camel/camel-session.h index b9416ddc8d..43abc614a8 100644 --- a/camel/camel-session.h +++ b/camel/camel-session.h @@ -35,6 +35,7 @@ extern "C" { #include <camel/camel-object.h> #include <camel/camel-provider.h> +#include <camel/camel-junk-plugin.h> #include <e-util/e-msgport.h> @@ -59,6 +60,8 @@ struct _CamelSession char *storage_path; GHashTable *providers, *modules; gboolean online; + + CamelJunkPlugin *junk_plugin; }; #ifdef ENABLE_THREADS diff --git a/camel/camel-store.c b/camel/camel-store.c index 96d840d2d3..ee51def91a 100644 --- a/camel/camel-store.c +++ b/camel/camel-store.c @@ -53,6 +53,9 @@ static CamelFolder *get_inbox (CamelStore *store, CamelException *ex); static void init_trash (CamelStore *store); static CamelFolder *get_trash (CamelStore *store, CamelException *ex); +static void init_junk (CamelStore *store); +static CamelFolder *get_junk (CamelStore *store, CamelException *ex); + static CamelFolderInfo *create_folder (CamelStore *store, const char *parent_name, const char *folder_name, @@ -95,6 +98,8 @@ camel_store_class_init (CamelStoreClass *camel_store_class) camel_store_class->get_inbox = get_inbox; camel_store_class->init_trash = init_trash; camel_store_class->get_trash = get_trash; + camel_store_class->init_junk = init_junk; + camel_store_class->get_junk = get_junk; camel_store_class->create_folder = create_folder; camel_store_class->delete_folder = delete_folder; camel_store_class->rename_folder = rename_folder; @@ -132,8 +137,8 @@ camel_store_init (void *o) } else store->folders = NULL; - /* set vtrash on by default */ - store->flags = CAMEL_STORE_VTRASH; + /* set vtrash and vjunk on by default */ + store->flags = CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK; store->dir_sep = '/'; @@ -239,9 +244,11 @@ camel_store_get_folder (CamelStore *store, const char *folder_name, guint32 flag if (!folder) { folder = CS_CLASS (store)->get_folder (store, folder_name, flags, ex); if (folder) { - /* Add the folder to the vTrash folder if this store implements it */ + /* Add the folder to the vTrash/vJunk folder if this store implements it */ if (store->vtrash) camel_vee_folder_add_folder (CAMEL_VEE_FOLDER (store->vtrash), folder); + if (store->vjunk) + camel_vee_folder_add_folder (CAMEL_VEE_FOLDER (store->vjunk), folder); if (store->folders) camel_object_bag_add(store->folders, folder_name, folder); @@ -319,12 +326,14 @@ camel_store_delete_folder (CamelStore *store, const char *folder_name, CamelExce /* NB: Note similarity of this code to unsubscribe_folder */ - /* if we deleted a folder, force it out of the cache, and also out of the vtrash if setup */ + /* if we deleted a folder, force it out of the cache, and also out of the vtrash/vjunk if setup */ if (store->folders) { folder = camel_object_bag_get(store->folders, folder_name); if (folder) { if (store->vtrash) camel_vee_folder_remove_folder((CamelVeeFolder *)store->vtrash, folder); + if (store->vjunk) + camel_vee_folder_remove_folder((CamelVeeFolder *)store->vjunk, folder); camel_folder_delete (folder); } } @@ -480,26 +489,44 @@ trash_finalize (CamelObject *trash, gpointer event_data, gpointer user_data) } static void -init_trash (CamelStore *store) +junk_finalize (CamelObject *junk, gpointer event_data, gpointer user_data) { - if ((store->flags & CAMEL_STORE_VTRASH) == 0) - return; + CamelStore *store = CAMEL_STORE (user_data); + + store->vjunk = NULL; +} - store->vtrash = camel_vtrash_folder_new (store, CAMEL_VTRASH_NAME); +/* FIXME: derive vjunk folder object from vee_folder */ +#include "camel-vee-store.h" +static CamelFolder * +camel_vjunk_folder_new (CamelStore *parent_store, const char *name) +{ + CamelFolder *vjunk; - if (store->vtrash) { + vjunk = (CamelFolder *)camel_object_new (camel_vee_folder_get_type ()); + vjunk->folder_flags |= CAMEL_FOLDER_IS_JUNK; + camel_vee_folder_construct (CAMEL_VEE_FOLDER (vjunk), parent_store, name, + CAMEL_STORE_FOLDER_PRIVATE | CAMEL_STORE_FOLDER_CREATE | CAMEL_STORE_VEE_FOLDER_AUTO); + camel_vee_folder_set_expression((CamelVeeFolder *)vjunk, "(match-all (system-flag \"Junk\"))"); + + return vjunk; +} + +static void +init_trash_or_junk (CamelStore *store, CamelFolder *folder, void (*finalize) (CamelObject *o, gpointer event_data, gpointer user_data)) +{ + if (folder) { /* FIXME: this should probably use the object bag or another one ? ... */ - /* attach to the finalise event of the vtrash */ - camel_object_hook_event (store->vtrash, "finalize", - trash_finalize, store); + /* attach to the finalise event of the vtrash/vjunk */ + camel_object_hook_event (folder, "finalize", finalize, store); - /* add all the pre-opened folders to the vtrash */ + /* add all the pre-opened folders to the vtrash/vjunk */ if (store->folders) { GPtrArray *folders = camel_object_bag_list(store->folders); int i; for (i=0;i<folders->len;i++) { - camel_vee_folder_add_folder (CAMEL_VEE_FOLDER (store->vtrash), (CamelFolder *)folders->pdata[i]); + camel_vee_folder_add_folder (CAMEL_VEE_FOLDER (folder), (CamelFolder *)folders->pdata[i]); camel_object_unref(folders->pdata[i]); } g_ptr_array_free(folders, TRUE); @@ -507,6 +534,25 @@ init_trash (CamelStore *store) } } +static void +init_trash (CamelStore *store) +{ + if ((store->flags & CAMEL_STORE_VTRASH) == 0) + return; + + store->vtrash = camel_vtrash_folder_new (store, CAMEL_VTRASH_NAME); + init_trash_or_junk (store, store->vtrash, trash_finalize); +} + +static void +init_junk (CamelStore *store) +{ + if ((store->flags & CAMEL_STORE_VJUNK) == 0) + return; + + store->vjunk = camel_vjunk_folder_new (store, CAMEL_VJUNK_NAME); + init_trash_or_junk (store, store->vjunk, junk_finalize); +} static CamelFolder * get_trash (CamelStore *store, CamelException *ex) @@ -528,6 +574,26 @@ get_trash (CamelStore *store, CamelException *ex) } } +static CamelFolder * +get_junk (CamelStore *store, CamelException *ex) +{ + if (store->vjunk) { + camel_object_ref (CAMEL_OBJECT (store->vjunk)); + return store->vjunk; + } else { + CS_CLASS (store)->init_junk (store); + if (store->vjunk) { + /* We don't ref here because we don't want the + store to own a ref on the junk folder */ + /*camel_object_ref (CAMEL_OBJECT (store->vjunk));*/ + return store->vjunk; + } else { + w(g_warning ("This store does not support vJunk.")); + return NULL; + } + } +} + /** * camel_store_get_trash: * @store: a CamelStore @@ -551,6 +617,29 @@ camel_store_get_trash (CamelStore *store, CamelException *ex) return folder; } +/** + * camel_store_get_junk: + * @store: a CamelStore + * @ex: a CamelException + * + * Return value: the folder in the store into which junk is + * delivered, or %NULL if no such folder exists. + **/ +CamelFolder * +camel_store_get_junk (CamelStore *store, CamelException *ex) +{ + CamelFolder *folder; + + if ((store->flags & CAMEL_STORE_VJUNK) == 0) + return NULL; + + CAMEL_STORE_LOCK(store, folder_lock); + folder = CS_CLASS (store)->get_junk (store, ex); + CAMEL_STORE_UNLOCK(store, folder_lock); + + return folder; +} + static void store_sync (CamelStore *store, CamelException *ex) { @@ -1015,12 +1104,14 @@ camel_store_unsubscribe_folder (CamelStore *store, /* NB: Note similarity of this code to delete_folder */ - /* if we deleted a folder, force it out of the cache, and also out of the vtrash if setup */ + /* if we deleted a folder, force it out of the cache, and also out of the vtrash/vjunk if setup */ if (store->folders) { folder = camel_object_bag_get(store->folders, folder_name); if (folder) { if (store->vtrash) camel_vee_folder_remove_folder((CamelVeeFolder *)store->vtrash, folder); + if (store->vjunk) + camel_vee_folder_remove_folder((CamelVeeFolder *)store->vjunk, folder); camel_folder_delete (folder); } } diff --git a/camel/camel-store.h b/camel/camel-store.h index 4a0fb416f0..2b65751aac 100644 --- a/camel/camel-store.h +++ b/camel/camel-store.h @@ -83,6 +83,7 @@ typedef struct _CamelRenameInfo { #define CAMEL_STORE_SUBSCRIPTIONS (1 << 0) #define CAMEL_STORE_VTRASH (1 << 1) #define CAMEL_STORE_FILTER_INBOX (1 << 2) +#define CAMEL_STORE_VJUNK (1 << 3) struct _CamelStore { @@ -90,6 +91,7 @@ struct _CamelStore struct _CamelStorePrivate *priv; CamelFolder *vtrash; + CamelFolder *vjunk; CamelObjectBag *folders; @@ -127,6 +129,9 @@ typedef struct { void (*init_trash) (CamelStore *store); CamelFolder * (*get_trash) (CamelStore *store, CamelException *ex); + void (*init_junk) (CamelStore *store); + CamelFolder * (*get_junk) (CamelStore *store, + CamelException *ex); CamelFolderInfo *(*create_folder) (CamelStore *store, const char *parent_name, @@ -175,6 +180,8 @@ CamelFolder * camel_store_get_inbox (CamelStore *store, CamelException *ex); CamelFolder * camel_store_get_trash (CamelStore *store, CamelException *ex); +CamelFolder * camel_store_get_junk (CamelStore *store, + CamelException *ex); CamelFolderInfo *camel_store_create_folder (CamelStore *store, const char *parent_name, diff --git a/camel/camel-vee-store.c b/camel/camel-vee-store.c index 3ad39237e0..b13522b8de 100644 --- a/camel/camel-vee-store.c +++ b/camel/camel-vee-store.c @@ -37,6 +37,8 @@ static void vee_delete_folder(CamelStore *store, const char *folder_name, CamelE static void vee_rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex); static void vee_init_trash (CamelStore *store); static CamelFolder *vee_get_trash (CamelStore *store, CamelException *ex); +static void vee_init_junk (CamelStore *store); +static CamelFolder *vee_get_junk (CamelStore *store, CamelException *ex); static CamelFolderInfo *vee_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex); @@ -80,6 +82,8 @@ camel_vee_store_class_init (CamelVeeStoreClass *klass) store_class->init_trash = vee_init_trash; store_class->get_trash = vee_get_trash; + store_class->init_junk = vee_init_junk; + store_class->get_junk = vee_get_junk; } static void @@ -87,8 +91,8 @@ camel_vee_store_init (CamelVeeStore *obj) { CamelStore *store = (CamelStore *)obj; - /* we dont want a vtrash on this one */ - store->flags &= ~(CAMEL_STORE_VTRASH); + /* we dont want a vtrash/vjunk on this one */ + store->flags &= ~(CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK); } static void @@ -183,6 +187,19 @@ vee_get_trash (CamelStore *store, CamelException *ex) return NULL; } +static void +vee_init_junk (CamelStore *store) +{ + /* no-op */ + ; +} + +static CamelFolder * +vee_get_junk (CamelStore *store, CamelException *ex) +{ + return NULL; +} + static CamelFolderInfo * vee_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex) { @@ -268,6 +285,8 @@ vee_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex if (store->vtrash) camel_vee_folder_remove_folder((CamelVeeFolder *)store->vtrash, folder); + if (store->vjunk) + camel_vee_folder_remove_folder((CamelVeeFolder *)store->vjunk, folder); if ((((CamelVeeFolder *)folder)->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0) { /* what about now-empty parents? ignore? */ diff --git a/camel/camel-vtrash-folder.h b/camel/camel-vtrash-folder.h index f0441c4c82..089c969e71 100644 --- a/camel/camel-vtrash-folder.h +++ b/camel/camel-vtrash-folder.h @@ -33,6 +33,7 @@ extern "C" { #include <camel/camel-vee-folder.h> #define CAMEL_VTRASH_NAME "Trash" +#define CAMEL_VJUNK_NAME "Junk" #define CAMEL_VTRASH_FOLDER(obj) CAMEL_CHECK_CAST (obj, camel_vtrash_folder_get_type (), CamelVTrashFolder) #define CAMEL_VTRASH_FOLDER_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_vtrash_folder_get_type (), CamelVTrashFolderClass) diff --git a/configure.in b/configure.in index 63d7c8182a..7b64db1d98 100644 --- a/configure.in +++ b/configure.in @@ -1094,7 +1094,7 @@ AC_DEFUN(EVO_SET_COMPILE_FLAGS, [ dnl --- Required version numbers GAL_REQUIRED=2.1.0 -GTKHTML_REQUIRED=3.1.1 +GTKHTML_REQUIRED=3.1.0 AC_SUBST(GAL_REQUIRED) AC_SUBST(GTKHTML_REQUIRED) @@ -1423,6 +1423,7 @@ default_user/local/Calendar/Makefile default_user/local/Contacts/Makefile default_user/local/Drafts/Makefile default_user/local/Inbox/Makefile +default_user/local/Junk/Makefile default_user/local/Outbox/Makefile default_user/local/Sent/Makefile default_user/local/Tasks/Makefile diff --git a/default_user/local/Makefile.am b/default_user/local/Makefile.am index a1baef5052..71fc42640a 100644 --- a/default_user/local/Makefile.am +++ b/default_user/local/Makefile.am @@ -3,6 +3,7 @@ SUBDIRS = \ Contacts \ Drafts \ Inbox \ + Junk \ Outbox \ Sent \ Tasks \ diff --git a/filter/filtertypes.xml b/filter/filtertypes.xml index 012ba62274..440cd6dddd 100644 --- a/filter/filtertypes.xml +++ b/filter/filtertypes.xml @@ -671,6 +671,9 @@ <option value="Seen"> <title>Read</title> </option> + <option value="Spam"> + <title>Spam</title> + </option> </input> </part> <part name="unset-status"> @@ -694,6 +697,9 @@ <option value="Seen"> <title>Read</title> </option> + <option value="Spam"> + <title>Spam</title> + </option> </input> </part> <part name="beep"> diff --git a/filter/libfilter-i18n.h b/filter/libfilter-i18n.h index ea7ff03b0e..410ea7945f 100644 --- a/filter/libfilter-i18n.h +++ b/filter/libfilter-i18n.h @@ -32,6 +32,7 @@ char *s = N_("Set Status"); char *s = N_("Shell Command"); char *s = N_("Size (kB)"); char *s = N_("Source Account"); +char *s = N_("Spam"); char *s = N_("Specific header"); char *s = N_("Status"); char *s = N_("Stop Processing"); diff --git a/mail/Makefile.am b/mail/Makefile.am index 0d9a101462..417ee0be24 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -109,6 +109,10 @@ libevolution_mail_la_SOURCES = \ em-sync-stream.h \ em-icon-stream.c \ em-icon-stream.h \ + em-junk-filter.c \ + em-junk-filter.h \ + em-junk-plugin.c \ + em-junk-plugin.h \ em-html-stream.c \ em-html-stream.h \ folder-browser-factory.c \ diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c index 7dcdc1d9e8..e6274abe37 100644 --- a/mail/em-folder-tree.c +++ b/mail/em-folder-tree.c @@ -144,7 +144,8 @@ enum { FOLDER_ICON_NORMAL, FOLDER_ICON_INBOX, FOLDER_ICON_OUTBOX, - FOLDER_ICON_TRASH + FOLDER_ICON_TRASH, + FOLDER_ICON_JUNK }; static GdkPixbuf *folder_icons[4]; @@ -163,6 +164,7 @@ render_pixbuf (GtkTreeViewColumn *column, GtkCellRenderer *renderer, folder_icons[1] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/inbox-mini.png"); folder_icons[2] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/outbox-mini.png"); folder_icons[3] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/evolution-trash-mini.png"); + folder_icons[4] = gdk_pixbuf_load_from_file (EVOLUTION_ICONSDIR "/evolution-junk-mini.png"); initialised = TRUE; } @@ -176,6 +178,8 @@ render_pixbuf (GtkTreeViewColumn *column, GtkCellRenderer *renderer, pixbuf = folder_icons[FOLDER_ICON_OUTBOX]; else if (!strcasecmp (name, "/Trash")) pixbuf = folder_icons[FOLDER_ICON_TRASH]; + else if (!strcasecmp (name, "/Junk")) + pixbuf = folder_icons[FOLDER_ICON_JUNK]; else pixbuf = folder_icons[FOLDER_ICON_NORMAL]; } diff --git a/mail/em-folder-view.c b/mail/em-folder-view.c index 0c52a49fe1..45fe46dbbb 100644 --- a/mail/em-folder-view.c +++ b/mail/em-folder-view.c @@ -524,6 +524,18 @@ emfv_popup_mark_unimportant(GtkWidget *w, EMFolderView *emfv) } static void +emfv_popup_mark_junk (GtkWidget *w, EMFolderView *emfv) +{ + mail_mark_junk (emfv->folder, emfv->list, TRUE); +} + +static void +emfv_popup_mark_nojunk (GtkWidget *w, EMFolderView *emfv) +{ + mail_mark_junk (emfv->folder, emfv->list, FALSE); +} + +static void emfv_popup_delete(GtkWidget *w, EMFolderView *emfv) { GPtrArray *uids; @@ -676,6 +688,8 @@ static EMPopupItem emfv_popup_menu[] = { { EM_POPUP_ITEM, "30.emfv.01", N_("Mark as _Unread"), G_CALLBACK(emfv_popup_mark_unread), NULL, "mail-new.xpm", EM_POPUP_SELECT_MARK_UNREAD }, { EM_POPUP_ITEM, "30.emfv.02", N_("Mark as _Important"), G_CALLBACK(emfv_popup_mark_important), NULL, "priority-high.xpm", EM_POPUP_SELECT_MARK_IMPORTANT }, { EM_POPUP_ITEM, "30.emfv.03", N_("_Mark as Unimportant"), G_CALLBACK(emfv_popup_mark_unimportant), NULL, NULL, EM_POPUP_SELECT_MARK_UNIMPORTANT }, + { EM_POPUP_ITEM, "30.emfv.04", N_("Mark as _Junk"), G_CALLBACK(emfv_popup_mark_junk), NULL, NULL, EM_POPUP_SELECT_MARK_JUNK }, + { EM_POPUP_ITEM, "30.emfv.05", N_("Mark as _Not Junk"), G_CALLBACK(emfv_popup_mark_nojunk), NULL, NULL, EM_POPUP_SELECT_MARK_NOJUNK }, { EM_POPUP_BAR, "40.emfv" }, { EM_POPUP_ITEM, "40.emfv.00", N_("_Delete"), G_CALLBACK(emfv_popup_delete), NULL, "evolution-trash-mini.png", EM_POPUP_SELECT_DELETE }, @@ -821,6 +835,8 @@ EMFV_MAP_CALLBACK(emfv_message_mark_read, emfv_popup_mark_read) EMFV_MAP_CALLBACK(emfv_message_mark_unread, emfv_popup_mark_unread) EMFV_MAP_CALLBACK(emfv_message_mark_important, emfv_popup_mark_important) EMFV_MAP_CALLBACK(emfv_message_mark_unimportant, emfv_popup_mark_unimportant) +EMFV_MAP_CALLBACK(emfv_message_mark_junk, emfv_popup_mark_junk) +EMFV_MAP_CALLBACK(emfv_message_mark_nojunk, emfv_popup_mark_nojunk) EMFV_MAP_CALLBACK(emfv_message_delete, emfv_popup_delete) EMFV_MAP_CALLBACK(emfv_message_undelete, emfv_popup_undelete) EMFV_MAP_CALLBACK(emfv_message_followup_flag, emfv_popup_flag_followup) @@ -1246,6 +1262,8 @@ static BonoboUIVerb emfv_message_verbs[] = { BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnRead", emfv_message_mark_unread), BONOBO_UI_UNSAFE_VERB ("MessageMarkAsImportant", emfv_message_mark_important), BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnimportant", emfv_message_mark_unimportant), + BONOBO_UI_UNSAFE_VERB ("MessageMarkAsJunk", emfv_message_mark_junk), + BONOBO_UI_UNSAFE_VERB ("MessageMarkAsNojunk", emfv_message_mark_nojunk), BONOBO_UI_UNSAFE_VERB ("MessageFollowUpFlag", emfv_message_followup_flag), BONOBO_UI_UNSAFE_VERB ("MessageMove", emfv_message_move), BONOBO_UI_UNSAFE_VERB ("MessageOpen", emfv_message_open), @@ -1348,6 +1366,8 @@ static const EMFolderViewEnable emfv_enable_map[] = { { "MessageMarkAsUnRead", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNREAD }, { "MessageMarkAsImportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_IMPORTANT }, { "MessageMarkAsUnimportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNIMPORTANT }, + { "MessageMarkAsJunk", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_JUNK }, + { "MessageMarkAsNojunk", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_NOJUNK }, { "MessageFollowUpFlag", EM_POPUP_SELECT_MANY }, { "MessageMove", EM_POPUP_SELECT_MANY }, { "MessageOpen", EM_POPUP_SELECT_MANY }, diff --git a/mail/em-junk-filter.c b/mail/em-junk-filter.c new file mode 100644 index 0000000000..04bbadc2ba --- /dev/null +++ b/mail/em-junk-filter.c @@ -0,0 +1,327 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <pthread.h> + +#include <camel/camel-data-wrapper.h> +#include <camel/camel-stream-fs.h> + +#include "em-junk-filter.h" + +#define LOCK(x) pthread_mutex_lock(&x) +#define UNLOCK(x) pthread_mutex_unlock(&x) + +static pthread_mutex_t em_junk_sa_test_lock = PTHREAD_MUTEX_INITIALIZER; + +static const char * em_junk_sa_get_name (void); +static gboolean em_junk_sa_check_junk (CamelMimeMessage *msg); +static void em_junk_sa_report_junk (CamelMimeMessage *msg); +static void em_junk_sa_report_notjunk (CamelMimeMessage *msg); +static void em_junk_sa_commit_reports (void); + +static EMJunkPlugin spam_assassin_plugin = +{ + { + em_junk_sa_get_name, + 1, + em_junk_sa_check_junk, + em_junk_sa_report_junk, + em_junk_sa_report_notjunk, + em_junk_sa_commit_reports, + }, + NULL, + NULL +}; + +static gboolean em_junk_sa_spamd_tested = FALSE; +static gboolean em_junk_sa_use_spamc = FALSE; +static gint em_junk_sa_spamd_port = -1; + +#define d(x) x + +static const char * +em_junk_sa_get_name (void) +{ + return _("Spamassassin (built-in)"); +} + +static int +pipe_to_sa (CamelMimeMessage *msg, gchar *in, int argc, gchar **argv) +{ + CamelStream *stream; + int result, status; + int in_fds[2]; + pid_t pid; + + if (argc < 1 || argv[0] == '\0') + return 0; + + if (pipe (in_fds) == -1) { + /* camel_exception_setv (fms->ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to create pipe to '%s': %s"), + argv[0]->value.string, g_strerror (errno)); */ + return -1; + } + + if (!(pid = fork ())) { + /* child process */ + int maxfd, fd; + + fd = open ("/dev/null", O_WRONLY); + + if (dup2 (in_fds[0], STDIN_FILENO) < 0 || + dup2 (fd, STDOUT_FILENO) < 0 || + dup2 (fd, STDERR_FILENO) < 0) + _exit (255); + + setsid (); + + maxfd = sysconf (_SC_OPEN_MAX); + if (maxfd > 0) { + for (fd = 0; fd < maxfd; fd++) { + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + close (fd); + } + } + + execvp (argv [0], argv); + + d(printf ("Could not execute %s: %s\n", argv [0], g_strerror (errno))); + _exit (255); + } else if (pid < 0) { + /* camel_exception_setv (fms->ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to create create child process '%s': %s"), + argv[0]->value.string, g_strerror (errno)); */ + return -1; + } + + /* parent process */ + close (in_fds[0]); + fcntl (in_fds[1], F_SETFL, O_NONBLOCK); + + if (msg) { + stream = camel_stream_fs_new_with_fd (in_fds[1]); + + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (msg), stream); + camel_stream_flush (stream); + camel_object_unref (CAMEL_OBJECT (stream)); + } else if (in) { + write (in_fds [1], in, strlen (in)); + close (in_fds [1]); + } + + result = waitpid (pid, &status, 0); + + if (result == -1 && errno == EINTR) { + /* child process is hanging... */ + kill (pid, SIGTERM); + sleep (1); + result = waitpid (pid, &status, WNOHANG); + if (result == 0) { + /* ...still hanging, set phasers to KILL */ + kill (pid, SIGKILL); + sleep (1); + result = waitpid (pid, &status, WNOHANG); + } + } + + if (result != -1 && WIFEXITED (status)) + return WEXITSTATUS (status); + else + return -1; +} + + +#define NPORTS 1 + +static int +em_junk_sa_test_spamd_running (gint port) +{ + static gchar *sac_args [3] = { + "/bin/sh", + "-c", + NULL + }; + int retval; + + d(fprintf (stderr, "test if spamd is running (port %d)\n", port);) + sac_args [2] = port > 0 ? g_strdup_printf ("spamc -x -p %d", port) : g_strdup_printf ("spamc -x"); + + retval = pipe_to_sa (NULL, "From test@127.0.0.1", 3, sac_args) == 0; + g_free (sac_args [2]); + + return retval; +} + +static void +em_junk_sa_test_spamd () +{ + gint i, port = 7830; + + em_junk_sa_use_spamc = FALSE; + + /* if (em_junk_sa_test_spamd_running (-1)) { + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = -1; + } else { */ + for (i = 0; i < NPORTS; i ++) { + if (em_junk_sa_test_spamd_running (port)) { + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = port; + break; + } + port ++; + } + /* } */ + + if (!em_junk_sa_use_spamc) { + static gchar *sad_args [3] = { + "/bin/sh", + "-c", + NULL + }; + gint i, port = 7830; + + d(fprintf (stderr, "looks like spamd is not running\n");) + + for (i = 0; i < NPORTS; i ++) { + d(fprintf (stderr, "trying to run spamd at port %d\n", port)); + + sad_args [2] = g_strdup_printf ("spamd --port %d --local --daemonize", port); + if (!pipe_to_sa (NULL, NULL, 3, sad_args)) { + g_free (sad_args [2]); + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = port; + d(fprintf (stderr, "success at port %d\n", port)); + break; + } + g_free (sad_args [2]); + port ++; + } + } + + d(fprintf (stderr, "use spamd %d at port %d\n", em_junk_sa_use_spamc, em_junk_sa_spamd_port);) + + em_junk_sa_spamd_tested = TRUE; +} + +static gboolean +em_junk_sa_check_junk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + NULL + }; + gint retval; + + d(fprintf (stderr, "em_junk_sa_check_junk\n")); + + LOCK (em_junk_sa_test_lock); + if (!em_junk_sa_spamd_tested) + em_junk_sa_test_spamd (); + UNLOCK (em_junk_sa_test_lock); + + args [2] = em_junk_sa_use_spamc + ? (em_junk_sa_spamd_port == -1 + ? g_strdup ("spamc -c") /* Exit with a non-zero exit code if the + tested message was junk */ + : g_strdup_printf ("spamc" + " -c" /* Exit with a non-zero exit code if the + tested message was junk */ + " -p %d", em_junk_sa_spamd_port)) + : g_strdup ("spamassassin" + " --exit-code" /* Exit with a non-zero exit code if the + tested message was junk */ + " --local"); /* Local tests only (no online tests) */ + + retval = pipe_to_sa (msg, NULL, 3, args); + + g_free (args [2]); + + return retval; +} + +static void +em_junk_sa_report_junk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --no-rebuild" /* do not rebuild db */ + " --spam" /* report junk */ + " --single" /* single message */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_report_junk\n");) + + pipe_to_sa (msg, NULL, 3, args) > 0; +} + +static void +em_junk_sa_report_notjunk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --no-rebuild" /* do not rebuild db */ + " --ham" /* report notjunk */ + " --single" /* single message */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_report_notjunk\n");) + + pipe_to_sa (msg, NULL, 3, args) > 0; +} + +static void +em_junk_sa_commit_reports (void) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --rebuild" /* do not rebuild db */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_commit_reports\n");) + + pipe_to_sa (NULL, NULL, 3, args) > 0; +} + +const EMJunkPlugin * +em_junk_filter_get_plugin (void) +{ + return &spam_assassin_plugin; +} diff --git a/mail/em-junk-filter.h b/mail/em-junk-filter.h new file mode 100644 index 0000000000..f62ca7bfad --- /dev/null +++ b/mail/em-junk-filter.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _EM_JUNK_FILTER_H +#define _EM_JUNK_FILTER_H + +#include "em-junk-plugin.h" + +const EMJunkPlugin * em_junk_filter_get_plugin (void); + +#endif diff --git a/mail/em-junk-plugin.c b/mail/em-junk-plugin.c new file mode 100644 index 0000000000..5ea2a40119 --- /dev/null +++ b/mail/em-junk-plugin.c @@ -0,0 +1,24 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "em-junk-plugin.h" + diff --git a/mail/em-junk-plugin.h b/mail/em-junk-plugin.h new file mode 100644 index 0000000000..0c7eacd165 --- /dev/null +++ b/mail/em-junk-plugin.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _EM_JUNK_PLUGIN_H +#define _EM_JUNK_PLUGIN_H + +#include <camel/camel-junk-plugin.h> +#include <gtk/gtkwidget.h> + +#define EM_JUNK_PLUGIN(x) ((EMJunkPlugin *) x) + +typedef struct _EMJunkPlugin EMJunkPlugin; + +struct _EMJunkPlugin +{ + CamelJunkPlugin csp; + + /* when called, it should insert own GUI configuration into supplied. + container. returns data pointer which is later passed to apply, + plugin has to call (*changed_cb) (); whenever configuration + is changed to notify settings dialog about a change. + if setup_config_ui is NULL, it means there are no options */ + + gpointer (*setup_config_ui) (GtkWidget *container, void (*changed_cb) ()); + void (*apply) (gpointer data); +}; + +#endif diff --git a/mail/em-popup.c b/mail/em-popup.c index 63159848bf..760aed48d5 100644 --- a/mail/em-popup.c +++ b/mail/em-popup.c @@ -459,6 +459,11 @@ em_popup_target_new_select(struct _CamelFolder *folder, const char *folder_uri, mask &= ~EM_POPUP_SELECT_MARK_UNIMPORTANT; else mask &= ~EM_POPUP_SELECT_MARK_IMPORTANT; + + if (info->flags & CAMEL_MESSAGE_JUNK) + mask &= ~EM_POPUP_SELECT_MARK_NOJUNK; + else + mask &= ~EM_POPUP_SELECT_MARK_JUNK; tmp = camel_tag_get (&info->user_tags, "follow-up"); if (tmp && *tmp) { diff --git a/mail/em-popup.h b/mail/em-popup.h index 0ad1d0015c..831d586efe 100644 --- a/mail/em-popup.h +++ b/mail/em-popup.h @@ -88,7 +88,9 @@ enum { EM_POPUP_SELECT_FLAG_COMPLETED = 1<<12, EM_POPUP_SELECT_FLAG_CLEAR = 1<<13, EM_POPUP_SELECT_ADD_SENDER = 1<<14, - EM_POPUP_SELECT_LAST = 1<<16 /* reserve 2 slots */ + EM_POPUP_SELECT_MARK_JUNK = 1<<15, + EM_POPUP_SELECT_MARK_NOJUNK = 1<<16, + EM_POPUP_SELECT_LAST = 1<<18 /* reserve 2 slots */ }; /* Flags that describe a TARGET_URI */ diff --git a/mail/mail-component.c b/mail/mail-component.c index f22cb656b3..ea20dae624 100644 --- a/mail/mail-component.c +++ b/mail/mail-component.c @@ -269,6 +269,12 @@ type_is_vtrash (const char *type) return !strcmp (type, "vtrash"); } +static inline gboolean +type_is_vjunk (const char *type) +{ + return !strcmp (type, "vjunk"); +} + static void storage_go_online (gpointer key, gpointer value, gpointer data) { @@ -410,6 +416,11 @@ create_view_callback (EStorageBrowser *browser, control = folder_browser_factory_new_control ("vtrash:file:/"); else control = folder_browser_factory_new_control (physical_uri); + } else if (type_is_vjunk (folder_type)) { + if (!strncasecmp (physical_uri, "file:", 5)) + control = folder_browser_factory_new_control ("vjunk:file:/"); + else + control = folder_browser_factory_new_control (physical_uri); } else return NULL; diff --git a/mail/mail-folder-cache.c b/mail/mail-folder-cache.c index 3d4eb96d53..a75ed046e9 100644 --- a/mail/mail-folder-cache.c +++ b/mail/mail-folder-cache.c @@ -240,9 +240,17 @@ real_flush_updates(void *o, void *event_data, void *data) g_warning ("No folder at %s ?!", up->path); } } else if (storage != NULL) { - char *type = (strncmp(up->uri, "vtrash:", 7)==0)?"vtrash":"mail"; - EFolder *new_folder = e_folder_new (up->name, type, NULL); + char *type; + EFolder *new_folder; + if (strncmp(up->uri, "vtrash:", 7)==0) { + type = "vtrash"; + } else if (strncmp(up->uri, "vjunk:", 6)==0) { + type = "vjunk"; + } else + type = "mail"; + + new_folder = e_folder_new (up->name, type, NULL); d(printf("Adding new folder: %s\n", up->path)); e_folder_set_physical_uri (new_folder, up->uri); @@ -461,7 +469,7 @@ folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) if (mfi->folder != folder) return; - if (!CAMEL_IS_VTRASH_FOLDER (folder) && folder != outbox_folder && folder != sent_folder && changes && changes->uid_added) + if (!CAMEL_IS_VTRASH_FOLDER (folder) && !CAMEL_IS_VJUNK_FOLDER (folder) && folder != outbox_folder && folder != sent_folder && changes && changes->uid_added) new = changes->uid_added->len; LOCK(info_lock); diff --git a/mail/mail-ops.c b/mail/mail-ops.c index 8d3711ff4e..b7aad2e55b 100644 --- a/mail/mail-ops.c +++ b/mail/mail-ops.c @@ -1063,9 +1063,9 @@ get_folderinfo_desc (struct _mail_msg *mm, int done) } static void -add_vtrash_info (CamelStore *store, CamelFolderInfo *info) +add_vtrash_or_vjunk_info (CamelStore *store, CamelFolderInfo *info, gchar *name, gchar *full_name, gchar *url_base, gboolean unread_count) { - CamelFolderInfo *fi, *vtrash, *parent; + CamelFolderInfo *fi, *vinfo, *parent; char *uri, *path; CamelURL *url; @@ -1073,14 +1073,14 @@ add_vtrash_info (CamelStore *store, CamelFolderInfo *info) parent = NULL; for (fi = info; fi; fi = fi->sibling) { - if (!strcmp (fi->name, CAMEL_VTRASH_NAME)) + if (!strcmp (fi->name, name)) break; parent = fi; } - /* create our vTrash URL */ + /* create our vTrash/vJunk URL */ url = camel_url_new (info->url, NULL); - path = g_strdup_printf ("/%s", CAMEL_VTRASH_NAME); + path = g_strdup_printf ("/%s", name); if (url->fragment) camel_url_set_fragment (url, path); else @@ -1090,32 +1090,45 @@ add_vtrash_info (CamelStore *store, CamelFolderInfo *info) camel_url_free (url); if (fi) { - /* We're going to replace the physical Trash folder with our vTrash folder */ - vtrash = fi; - g_free (vtrash->full_name); - g_free (vtrash->name); - g_free (vtrash->url); + /* We're going to replace the physical Trash/Junk folder with our vTrash/vJunk folder */ + vinfo = fi; + g_free (vinfo->full_name); + g_free (vinfo->name); + g_free (vinfo->url); } else { - /* There wasn't a Trash folder so create a new folder entry */ - vtrash = g_new0 (CamelFolderInfo, 1); + /* There wasn't a Trash/Junk folder so create a new folder entry */ + vinfo = g_new0 (CamelFolderInfo, 1); g_assert(parent != NULL); /* link it into the right spot */ - vtrash->sibling = parent->sibling; - parent->sibling = vtrash; + vinfo->sibling = parent->sibling; + parent->sibling = vinfo; } /* Fill in the new fields */ - vtrash->full_name = g_strdup (_("Trash")); - vtrash->name = g_strdup(vtrash->full_name); - vtrash->url = g_strdup_printf ("vtrash:%s", uri); - vtrash->unread_message_count = -1; - vtrash->path = g_strdup_printf("/%s", vtrash->name); + vinfo->full_name = g_strdup (full_name); + vinfo->name = g_strdup(vinfo->full_name); + vinfo->url = g_strdup_printf ("%s:%s", url_base, uri); + if (!unread_count) + vinfo->unread_message_count = -1; + vinfo->path = g_strdup_printf("/%s", vinfo->name); g_free (uri); } static void +add_vtrash_info (CamelStore *store, CamelFolderInfo *info) +{ + add_vtrash_or_vjunk_info (store, info, CAMEL_VTRASH_NAME, _("Trash"), "vtrash", FALSE); +} + +static void +add_vjunk_info (CamelStore *store, CamelFolderInfo *info) +{ + add_vtrash_or_vjunk_info (store, info, CAMEL_VJUNK_NAME, _("Junk"), "vjunk", TRUE); +} + +static void add_unmatched_info(CamelFolderInfo *fi) { for (; fi; fi = fi->sibling) { @@ -1142,6 +1155,8 @@ get_folderinfo_get (struct _mail_msg *mm) if (m->info) { if (m->info->url && (m->store->flags & CAMEL_STORE_VTRASH)) add_vtrash_info(m->store, m->info); + if (m->info->url && (m->store->flags & CAMEL_STORE_VJUNK)) + add_vjunk_info(m->store, m->info); if (CAMEL_IS_VEE_STORE(m->store)) add_unmatched_info(m->info); } @@ -2329,3 +2344,101 @@ mail_execute_shell_command (CamelFilterDriver *driver, int argc, char **argv, vo gnome_execute_async_fds (NULL, argc, argv, TRUE); } + +/* [Un]mark junk flag */ + +struct _mark_junk_mail_msg { + struct _mail_msg msg; + + CamelFolder *folder; + MessageList *list; + gboolean junk; +}; + +static char * +mark_junk_describe (struct _mail_msg *mm, int complete) +{ + return g_strdup (_("Changing junk status")); +} + +/* filter a folder, or a subset thereof, uses source_folder/source_uids */ +/* this is shared with fetch_mail */ +static void +mark_junk_mark (struct _mail_msg *mm) +{ + struct _mark_junk_mail_msg *m = (struct _mark_junk_mail_msg *) mm; + CamelJunkPlugin *csp = NULL; + GPtrArray *uids; + gboolean commit_reports = FALSE; + int i; + + if (m->folder == NULL) + return; + + uids = message_list_get_selected (m->list); + camel_folder_freeze (m->folder); + + for (i=0; i<uids->len; i++) { + guint32 flags; + + flags = camel_folder_get_message_flags (m->folder, uids->pdata[i]); + if (((flags & CAMEL_MESSAGE_JUNK) == CAMEL_MESSAGE_JUNK) != m->junk) { + CamelMimeMessage *msg = camel_folder_get_message (m->folder, uids->pdata[i], NULL); + + if (msg) { + csp = CAMEL_SERVICE (m->folder->parent_store)->session->junk_plugin; + if (m->junk) + camel_junk_plugin_report_junk (csp, msg); + else + camel_junk_plugin_report_notjunk (csp, msg); + + commit_reports = TRUE; + camel_object_unref (msg); + } + } + camel_folder_set_message_flags(m->folder, uids->pdata[i], + CAMEL_MESSAGE_JUNK | (m->junk ? CAMEL_MESSAGE_DELETED : 0), + m->junk ? CAMEL_MESSAGE_JUNK : 0); + } + + if (commit_reports) + camel_junk_plugin_commit_reports (csp); + + message_list_free_uids(m->list, uids); + camel_folder_thaw(m->folder); +} + +static void +mark_junk_marked (struct _mail_msg *mm) +{ +} + +static void +mark_junk_free (struct _mail_msg *mm) +{ + struct _mark_junk_mail_msg *m = (struct _mark_junk_mail_msg *)mm; + + if (m->folder) + camel_object_unref (m->folder); +} + +static struct _mail_msg_op mark_junk_op = { + mark_junk_describe, + mark_junk_mark, + mark_junk_marked, + mark_junk_free, +}; + +void +mail_mark_junk (CamelFolder *folder, MessageList *list, gboolean junk) +{ + struct _mark_junk_mail_msg *m; + + m = mail_msg_new (&mark_junk_op, NULL, sizeof (*m)); + m->folder = folder; + camel_object_ref (folder); + m->list = list; + m->junk = junk; + + e_thread_put (mail_thread_new, (EMsg *) m); +} diff --git a/mail/mail-ops.h b/mail/mail-ops.h index e15d7729c2..081e1a1425 100644 --- a/mail/mail-ops.h +++ b/mail/mail-ops.h @@ -36,6 +36,8 @@ extern "C" { #include "camel/camel-mime-message.h" #include "camel/camel-operation.h" +#include "message-list.h" + #include "evolution-storage.h" /*EvolutionStorage */ #include "e-util/e-msgport.h" #include "e-util/e-account.h" @@ -164,6 +166,8 @@ int mail_store_set_offline(CamelStore *store, gboolean offline, /* filter driver execute shell command async callback */ void mail_execute_shell_command (CamelFilterDriver *driver, int argc, char **argv, void *data); +void mail_mark_junk (CamelFolder *folder, MessageList *list, gboolean junk); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/mail/mail-session.c b/mail/mail-session.c index 58773513c4..0db6f38ba8 100644 --- a/mail/mail-session.c +++ b/mail/mail-session.c @@ -48,6 +48,7 @@ #include "mail-ops.h" #include "e-util/e-passwords.h" #include "e-util/e-msgport.h" +#include "em-junk-filter.h" #define d(x) @@ -626,6 +627,9 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException fsearch = g_string_new (""); faction = g_string_new (""); + + /* implicit junk check as 1st rule */ + camel_filter_driver_add_rule (driver, "Junk check", "(junk-test)", "(begin (set-system-flag \"junk\"))"); /* add the user-defined rules next */ while ((rule = rule_context_next_rule (fc, rule, type))) { @@ -634,7 +638,6 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException filter_rule_build_code (rule, fsearch); filter_filter_build_action ((FilterFilter *) rule, faction); - camel_filter_driver_add_rule (driver, rule->name, fsearch->str, faction->str); } @@ -755,6 +758,8 @@ mail_session_init (const char *base_directory) camel_dir = g_strdup_printf ("%s/mail", base_directory); camel_session_construct (session, camel_dir); + + session->junk_plugin = CAMEL_JUNK_PLUGIN (em_junk_filter_get_plugin ()); /* The shell will tell us to go online. */ camel_session_set_online ((CamelSession *) session, FALSE); diff --git a/mail/mail-tools.c b/mail/mail-tools.c index b85a18c12f..0538efd354 100644 --- a/mail/mail-tools.c +++ b/mail/mail-tools.c @@ -312,6 +312,8 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex) /* This hack is still needed for file:/ since it's its own EvolutionStorage type */ if (!strncmp (uri, "vtrash:", 7)) offset = 7; + else if (!strncmp (uri, "vjunk:", 6)) + offset = 6; url = camel_url_new (uri + offset, ex); if (!url) { @@ -333,9 +335,14 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex) name = ""; } - if (offset) - folder = camel_store_get_trash (store, ex); - else + if (offset) { + if (offset == 7) + folder = camel_store_get_trash (store, ex); + else if (offset == 6) + folder = camel_store_get_junk (store, ex); + else + g_assert (FALSE); + } else folder = camel_store_get_folder (store, name, flags, ex); camel_object_unref (store); } diff --git a/mail/mail-vfolder.c b/mail/mail-vfolder.c index f47fe4c660..2bb08a587e 100644 --- a/mail/mail-vfolder.c +++ b/mail/mail-vfolder.c @@ -345,7 +345,7 @@ mail_vfolder_add_uri(CamelStore *store, const char *uri, int remove) GCompareFunc uri_cmp = CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name; int is_ignore; - if (CAMEL_IS_VEE_STORE(store) || !strncmp(uri, "vtrash:", 7) || context == NULL) + if (CAMEL_IS_VEE_STORE(store) || !strncmp(uri, "vtrash:", 7) || !strncmp(uri, "vjunk:", 6) || context == NULL) return; g_assert(pthread_self() == mail_gui_thread); @@ -426,7 +426,7 @@ mail_vfolder_delete_uri(CamelStore *store, const char *uri) CamelVeeFolder *vf; GString *changed; - if (context == NULL || !strncmp(uri, "vtrash:", 7)) + if (context == NULL || !strncmp(uri, "vtrash:", 7) || !strncmp(uri, "vjunk:", 6)) return; d(printf ("Deleting uri to check: %s\n", uri)); @@ -492,7 +492,7 @@ mail_vfolder_rename_uri(CamelStore *store, const char *from, const char *to) d(printf("vfolder rename uri: %s to %s\n", from, to)); - if (context == NULL || !strncmp(from, "vtrash:", 7) || !strncmp(to, "vtrash:", 7)) + if (context == NULL || !strncmp(from, "vtrash:", 7) || !strncmp(to, "vtrash:", 7) || !strncmp(from, "vjunk:", 6) || !strncmp(to, "vjunk:", 6)) return; g_assert(pthread_self() == mail_gui_thread); diff --git a/mail/message-list.c b/mail/message-list.c index a16c40f265..430d2f4896 100644 --- a/mail/message-list.c +++ b/mail/message-list.c @@ -188,6 +188,9 @@ static struct { { NULL, NULL } }; +/* FIXME: junk prefs */ +static gboolean junk_folder = TRUE; + #ifdef SMART_ADDRESS_COMPARE static EMailAddress * e_mail_address_new (const char *address) @@ -2377,11 +2380,44 @@ build_flat_diff(MessageList *ml, CamelFolderChangeInfo *changes) static void +mail_folder_hide_by_flag (CamelFolder *folder, MessageList *ml, CamelFolderChangeInfo **changes, int flag) +{ + CamelFolderChangeInfo *newchanges, *oldchanges = *changes; + CamelMessageInfo *info; + int i; + + newchanges = camel_folder_change_info_new (); + + for (i = 0; i < oldchanges->uid_changed->len; i++) { + ETreePath node = g_hash_table_lookup (ml->uid_nodemap, oldchanges->uid_changed->pdata[i]); + + info = camel_folder_get_message_info (folder, oldchanges->uid_changed->pdata[i]); + if (node != NULL && info != NULL && (info->flags & flag) != 0) + camel_folder_change_info_remove_uid (newchanges, oldchanges->uid_changed->pdata[i]); + else if (node == NULL && info != NULL && (info->flags & flag) == 0) + camel_folder_change_info_add_uid (newchanges, oldchanges->uid_changed->pdata[i]); + else + camel_folder_change_info_change_uid (newchanges, oldchanges->uid_changed->pdata[i]); + camel_folder_free_message_info (folder, info); + } + + if (newchanges->uid_added->len > 0 || newchanges->uid_removed->len > 0) { + for (i = 0; i < oldchanges->uid_added->len; i++) + camel_folder_change_info_add_uid (newchanges, oldchanges->uid_added->pdata[i]); + for (i = 0; i < oldchanges->uid_removed->len; i++) + camel_folder_change_info_remove_uid (newchanges, oldchanges->uid_removed->pdata[i]); + camel_folder_change_info_free (oldchanges); + *changes = newchanges; + } else { + camel_folder_change_info_free (newchanges); + } +} + +static void main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) { MessageList *ml = MESSAGE_LIST (user_data); - CamelFolderChangeInfo *changes = (CamelFolderChangeInfo *)event_data, *newchanges; - CamelMessageInfo *info; + CamelFolderChangeInfo *changes = (CamelFolderChangeInfo *)event_data; CamelFolder *folder = (CamelFolder *)o; int i; @@ -2402,35 +2438,9 @@ main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) } /* check if the hidden state has changed, if so modify accordingly, then regenerate */ - if (ml->hidedeleted) { - newchanges = camel_folder_change_info_new (); - - for (i = 0; i < changes->uid_changed->len; i++) { - ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]); - - info = camel_folder_get_message_info (folder, changes->uid_changed->pdata[i]); - if (node != NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) != 0) { - camel_folder_change_info_remove_uid (newchanges, changes->uid_changed->pdata[i]); - } else if (node == NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) == 0) { - camel_folder_change_info_add_uid (newchanges, changes->uid_changed->pdata[i]); - } else { - camel_folder_change_info_change_uid (newchanges, changes->uid_changed->pdata[i]); - } - camel_folder_free_message_info (folder, info); - } - - if (newchanges->uid_added->len > 0 || newchanges->uid_removed->len > 0) { - for (i = 0; i < changes->uid_added->len; i++) - camel_folder_change_info_add_uid (newchanges, changes->uid_added->pdata[i]); - for (i = 0; i < changes->uid_removed->len; i++) - camel_folder_change_info_remove_uid (newchanges, changes->uid_removed->pdata[i]); - camel_folder_change_info_free (changes); - changes = newchanges; - } else { - camel_folder_change_info_free (newchanges); - } - } - + if (ml->hidejunk || ml->hidedeleted) + mail_folder_hide_by_flag (folder, ml, &changes, (ml->hidejunk ? CAMEL_MESSAGE_JUNK : 0) | (ml->hidedeleted ? CAMEL_MESSAGE_DELETED : 0)); + if (changes->uid_added->len == 0 && changes->uid_removed->len == 0 && changes->uid_changed->len < 100) { for (i = 0; i < changes->uid_changed->len; i++) { ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]); @@ -2579,6 +2589,7 @@ message_list_set_folder (MessageList *message_list, CamelFolder *folder, const c gconf = mail_config_get_gconf_client (); hide_deleted = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL); message_list->hidedeleted = hide_deleted && !(folder->folder_flags & CAMEL_FOLDER_IS_TRASH); + message_list->hidejunk = junk_folder && !(folder->folder_flags & CAMEL_FOLDER_IS_JUNK) && !(folder->folder_flags & CAMEL_FOLDER_IS_TRASH); hide_load_state (message_list); mail_regen_list (message_list, message_list->search, NULL, NULL); @@ -3038,6 +3049,7 @@ struct _regen_list_msg { CamelFolderChangeInfo *changes; gboolean dotree; /* we are building a tree */ gboolean hidedel; /* we want to/dont want to show deleted messages */ + gboolean hidejunk; /* we want to/dont want to show junk messages */ gboolean thread_subject; CamelFolderThread *tree; @@ -3076,17 +3088,36 @@ regen_list_regen (struct _mail_msg *mm) } else if (m->hidedel) { char *expr; - if (m->search) { - expr = alloca(strlen(m->search) + 64); - sprintf(expr, "(and (match-all (not (system-flag \"deleted\")))\n %s)", m->search); - } else - expr = "(match-all (not (system-flag \"deleted\")))"; + if (m->hidejunk) { + if (m->search) { + expr = alloca(strlen(m->search) + 92); + sprintf(expr, "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))\n %s)", m->search); + } else + expr = "(match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))"; + } else { + if (m->search) { + expr = alloca(strlen(m->search) + 64); + sprintf(expr, "(and (match-all (not (system-flag \"deleted\")))\n %s)", m->search); + } else + expr = "(match-all (not (system-flag \"deleted\")))"; + } searchuids = uids = camel_folder_search_by_expression (m->folder, expr, &mm->ex); } else { - if (m->search) - searchuids = uids = camel_folder_search_by_expression (m->folder, m->search, &mm->ex); - else - uids = camel_folder_get_uids (m->folder); + char *expr; + + if (m->hidejunk) { + if (m->search) { + expr = alloca(strlen(m->search) + 64); + sprintf(expr, "(and (match-all (not (system-flag \"junk\")))\n %s)", m->search); + } else + expr = "(match-all (not (system-flag \"junk\")))"; + searchuids = uids = camel_folder_search_by_expression (m->folder, expr, &mm->ex); + } else { + if (m->search) + searchuids = uids = camel_folder_search_by_expression (m->folder, m->search, &mm->ex); + else + uids = camel_folder_get_uids (m->folder); + } } if (camel_exception_is_set (&mm->ex)) @@ -3318,6 +3349,7 @@ mail_regen_list (MessageList *ml, const char *search, const char *hideexpr, Came m->changes = changes; m->dotree = ml->threaded; m->hidedel = ml->hidedeleted; + m->hidejunk = ml->hidejunk; m->thread_subject = gconf_client_get_bool (gconf, "/apps/evolution/mail/display/thread_subject", NULL); g_object_ref(ml); m->folder = ml->folder; diff --git a/mail/message-list.h b/mail/message-list.h index a5c88cf308..8052ed791b 100644 --- a/mail/message-list.h +++ b/mail/message-list.h @@ -113,6 +113,9 @@ struct _MessageList { /* do we automatically hide deleted messages? */ guint hidedeleted : 1; + + /* do we automatically hide junk messages? */ + guint hidejunk : 1; /* is the message-list object in a destroyed state? */ guint destroyed : 1; diff --git a/shell/e-local-storage.c b/shell/e-local-storage.c index 8ce9f87189..01009cb805 100644 --- a/shell/e-local-storage.c +++ b/shell/e-local-storage.c @@ -182,6 +182,7 @@ setup_stock_folders (ELocalStorage *local_storage) setup_folder_as_stock (local_storage, "/Sent", _("Sent"), NULL); setup_folder_as_stock (local_storage, "/Tasks", _("Tasks"), NULL); setup_folder_as_stock (local_storage, "/Trash", _("Trash"), NULL); + setup_folder_as_stock (local_storage, "/Spam", _("Spam"), NULL); } static gboolean diff --git a/shell/e-shortcuts.c b/shell/e-shortcuts.c index 697f50fe90..2672dc251d 100644 --- a/shell/e-shortcuts.c +++ b/shell/e-shortcuts.c @@ -27,6 +27,7 @@ <group title="Evolution shortcuts"> <item name="Inbox" type="mail">evolution:/local/Inbox</item> <item name="Trash" type="vtrash">evolution:/local/Trash</item> + <item name="Spam" type="vspam">evolution:/local/Spam</item> <item name="Calendar" type="calendar">evolution:/local/Calendar</item> </group> diff --git a/ui/evolution-mail-message.xml b/ui/evolution-mail-message.xml index 7992c36be2..1d0af52295 100644 --- a/ui/evolution-mail-message.xml +++ b/ui/evolution-mail-message.xml @@ -97,6 +97,13 @@ <cmd name="MessageMarkAsUnimportant" _tip="Mark the selected message(s) as unimportant"/> + <cmd name="MessageMarkAsJunk" + _tip="Mark the selected message(s) as junk" + pixtype="pixbuf"/> + + <cmd name="MessageMarkAsNoJunk" + _tip="Mark the selected message(s) as not being junk"/> + <cmd name="MessageMove" _tip="Move selected message(s) to another folder" accel="*Control**Shift*v" @@ -268,6 +275,8 @@ <menuitem name="MessageMarkAsUnRead" verb="" _label="Mark as U_nread"/> <menuitem name="MessageMarkAsImportant" verb="" _label="Mark as I_mportant"/> <menuitem name="MessageMarkAsUnimportant" verb="" _label="Mark as Unimp_ortant"/> + <menuitem name="MessageMarkAsJunk" verb="" _label="Mark as _Junk"/> + <menuitem name="MessageMarkAsNojunk" verb="" _label="Mark as _Not Junk"/> </placeholder> </submenu> </placeholder> |