diff options
Diffstat (limited to 'mail/e-mail-store.c')
-rw-r--r-- | mail/e-mail-store.c | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/mail/e-mail-store.c b/mail/e-mail-store.c new file mode 100644 index 0000000000..1fc3eb5b25 --- /dev/null +++ b/mail/e-mail-store.c @@ -0,0 +1,423 @@ +/* + * e-mail-store.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-mail-store.h" + +#include <glib/gi18n.h> +#include <camel/camel-service.h> +#include <camel/camel-session.h> +#include <camel/camel-url.h> +#include <libedataserver/e-account.h> +#include <libedataserver/e-account-list.h> + +#include "e-util/e-account-utils.h" + +#include "mail/e-mail-local.h" +#include "mail/em-folder-tree-model.h" +#include "mail/mail-folder-cache.h" +#include "mail/mail-mt.h" +#include "mail/mail-session.h" + +typedef struct _StoreInfo StoreInfo; + +typedef void (*AddStoreCallback) (CamelStore *store, + CamelFolderInfo *info, + StoreInfo *store_info); + +struct _StoreInfo { + gint ref_count; + + CamelStore *store; + gchar *display_name; + + /* Hold a reference to keep them alive. */ + CamelFolder *vtrash; + CamelFolder *vjunk; + + AddStoreCallback callback; + + guint removed : 1; +}; + +CamelStore *vfolder_store; /* XXX write a get() function for this */ +static GHashTable *store_table; + +static MailAsyncEvent *async_event; + +static StoreInfo * +store_info_new (CamelStore *store, + const gchar *display_name) +{ + CamelService *service; + StoreInfo *store_info; + + g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); + + service = CAMEL_SERVICE (store); + + store_info = g_slice_new0 (StoreInfo); + store_info->ref_count = 1; + + camel_object_ref (store); + store_info->store = store; + + if (display_name == NULL) + store_info->display_name = + camel_service_get_name (service, TRUE); + else + store_info->display_name = g_strdup (display_name); + + /* If these are vfolders then they need to be opened now, + * otherwise they won't keep track of all folders. */ + if (store->flags & CAMEL_STORE_VTRASH) + store_info->vtrash = camel_store_get_trash (store, NULL); + if (store->flags & CAMEL_STORE_VJUNK) + store_info->vjunk = camel_store_get_junk (store, NULL); + + return store_info; +} + +static StoreInfo * +store_info_ref (StoreInfo *store_info) +{ + g_return_val_if_fail (store_info != NULL, store_info); + g_return_val_if_fail (store_info->ref_count > 0, store_info); + + g_atomic_int_add (&store_info->ref_count, 1); + + return store_info; +} + +static void +store_info_unref (StoreInfo *store_info) +{ + g_return_if_fail (store_info != NULL); + g_return_if_fail (store_info->ref_count > 0); + + if (g_atomic_int_exchange_and_add (&store_info->ref_count, -1) > 1) + return; + + camel_object_unref (store_info->store); + g_free (store_info->display_name); + + if (store_info->vtrash != NULL) + camel_object_unref (store_info->vtrash); + + if (store_info->vjunk != NULL) + camel_object_unref (store_info->vjunk); + + g_slice_free (StoreInfo, store_info); +} + +static void +store_table_free (StoreInfo *store_info) +{ + store_info->removed = 1; + store_info_unref (store_info); +} + +static gboolean +mail_store_note_store_cb (CamelStore *store, + CamelFolderInfo *info, + gpointer user_data) +{ + StoreInfo *store_info = user_data; + + if (store_info->callback != NULL) + store_info->callback (store, info, store_info); + + if (!store_info->removed) { + /* This keeps message counters up-to-date. */ + if (store_info->vtrash != NULL) + mail_note_folder (store_info->vtrash); + if (store_info->vjunk != NULL) + mail_note_folder (store_info->vjunk); + } + + store_info_unref (store_info); + + return TRUE; +} + +static void +mail_store_add (CamelStore *store, + const gchar *display_name, + AddStoreCallback callback) +{ + EMFolderTreeModel *default_model; + StoreInfo *store_info; + + g_return_if_fail (store_table != NULL); + + default_model = em_folder_tree_model_get_default (); + + store_info = store_info_new (store, display_name); + store_info->callback = callback; + + g_hash_table_insert (store_table, store, store_info); + + em_folder_tree_model_add_store ( + default_model, store, store_info->display_name); + + mail_note_store ( + store, NULL, + mail_store_note_store_cb, + store_info_ref (store_info)); +} + +static void +mail_store_add_local_done_cb (CamelStore *store, + CamelFolderInfo *info, + StoreInfo *store_info) +{ + CamelFolder *folder; + gint ii; + + for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) { + folder = e_mail_local_get_folder (ii); + if (folder != NULL) + mail_note_folder (folder); + } +} + +static void +mail_store_add_local_cb (CamelStore *local_store, + const gchar *display_name) +{ + mail_store_add ( + local_store, display_name, + (AddStoreCallback) mail_store_add_local_done_cb); +} + +static void +mail_store_load_accounts (const gchar *data_dir) +{ + CamelStore *local_store; + EAccountList *account_list; + EIterator *iter; + + /* Set up the local store. */ + + e_mail_local_init (data_dir); + local_store = e_mail_local_get_store (); + + mail_async_event_emit ( + async_event, MAIL_ASYNC_GUI, + (MailAsyncFunc) mail_store_add_local_cb, + local_store, _("On This Computer"), NULL); + + /* Set up remote stores. */ + + account_list = e_get_account_list (); + + for (iter = e_list_get_iterator ((EList *) account_list); + e_iterator_is_valid (iter); e_iterator_next (iter)) { + + EAccountService *service; + EAccount *account; + const gchar *display_name; + const gchar *uri; + + account = (EAccount *) e_iterator_get (iter); + display_name = account->name; + service = account->source; + uri = service->url; + + if (!account->enabled) + continue; + + if (uri == NULL || *uri == '\0') + continue; + + /* HACK: mbox URI's are handled by the local store setup + * above. Any that come through as account sources + * are really movemail sources! */ + if (g_str_has_prefix (uri, "mbox:")) + continue; + + e_mail_store_add_by_uri (uri, display_name); + } + + g_object_unref (iter); +} + +void +e_mail_store_init (const gchar *data_dir) +{ + static gboolean initialized = FALSE; + + g_return_if_fail (!initialized); + g_return_if_fail (data_dir != NULL); + + /* Initialize global variables. */ + + store_table = g_hash_table_new_full ( + g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) store_table_free); + + async_event = mail_async_event_new (); + + mail_store_load_accounts (data_dir); + + initialized = TRUE; +} + +void +e_mail_store_add (CamelStore *store, + const gchar *display_name) +{ + g_return_if_fail (CAMEL_IS_STORE (store)); + g_return_if_fail (display_name != NULL); + + mail_store_add (store, display_name, NULL); +} + +CamelStore * +e_mail_store_add_by_uri (const gchar *uri, + const gchar *display_name) +{ + CamelService *service; + CamelProvider *provider; + CamelException ex; + + g_return_val_if_fail (uri != NULL, NULL); + g_return_val_if_fail (display_name != NULL, NULL); + + camel_exception_init (&ex); + + /* Load the service, but don't connect. Check its provider, + * and if this belongs in the folder tree model, add it. */ + + provider = camel_provider_get (uri, &ex); + if (provider == NULL) + goto fail; + + if (!(provider->flags & CAMEL_PROVIDER_IS_STORAGE)) + return NULL; + + service = camel_session_get_service ( + session, uri, CAMEL_PROVIDER_STORE, &ex); + if (service == NULL) + goto fail; + + e_mail_store_add (CAMEL_STORE (service), display_name); + + camel_object_unref (service); + + return CAMEL_STORE (service); + +fail: + /* FIXME: Show an error dialog. */ + g_warning ( + "Couldn't get service: %s: %s", uri, + camel_exception_get_description (&ex)); + camel_exception_clear (&ex); + + return NULL; +} + +/* Helper for e_mail_store_remove() */ +static void +mail_store_remove_cb (CamelStore *store) +{ + camel_service_disconnect (CAMEL_SERVICE (store), TRUE, NULL); + camel_object_unref (store); +} + +void +e_mail_store_remove (CamelStore *store) +{ + EMFolderTreeModel *default_model; + + g_return_if_fail (CAMEL_IS_STORE (store)); + g_return_if_fail (store_table != NULL); + g_return_if_fail (async_event != NULL); + + /* Because the store table holds a reference to each store used + * as a key in it, none of them will ever be gc'ed, meaning any + * call to camel_session_get_{service,store} with the same URL + * will always return the same object. So this works. */ + + if (g_hash_table_lookup (store_table, store) == NULL) + return; + + camel_object_ref (store); + + g_hash_table_remove (store_table, store); + mail_note_store_remove (store); + + default_model = em_folder_tree_model_get_default (); + em_folder_tree_model_remove_store (default_model, store); + + mail_async_event_emit ( + async_event, MAIL_ASYNC_THREAD, + (MailAsyncFunc) mail_store_remove_cb, + store, NULL, NULL); +} + +void +e_mail_store_remove_by_uri (const gchar *uri) +{ + CamelService *service; + CamelProvider *provider; + + g_return_if_fail (uri != NULL); + + provider = camel_provider_get (uri, NULL); + if (provider == NULL) + return; + + if (!(provider->flags & CAMEL_PROVIDER_IS_STORAGE)) + return; + + service = camel_session_get_service ( + session, uri, CAMEL_PROVIDER_STORE, NULL); + if (service == NULL) + return; + + e_mail_store_remove (CAMEL_STORE (service)); + + camel_object_unref (service); +} + +void +e_mail_store_foreach (GHFunc func, + gpointer user_data) +{ + GHashTableIter iter; + gpointer key, value; + + g_return_if_fail (func != NULL); + g_return_if_fail (store_table != NULL); + + g_hash_table_iter_init (&iter, store_table); + + while (g_hash_table_iter_next (&iter, &key, &value)) { + StoreInfo *store_info = value; + + /* Just being paranoid. */ + g_return_if_fail (CAMEL_IS_STORE (key)); + g_return_if_fail (store_info != NULL); + + func (key, store_info->display_name, user_data); + } +} |