424 lines
9.8 KiB
C
424 lines
9.8 KiB
C
/*
|
|
* 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);
|
|
}
|
|
}
|