aboutsummaryrefslogtreecommitdiffstats
path: root/mail/e-mail-session.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/e-mail-session.c')
-rw-r--r--mail/e-mail-session.c563
1 files changed, 551 insertions, 12 deletions
diff --git a/mail/e-mail-session.c b/mail/e-mail-session.c
index 03f4961206..6d04c2c23d 100644
--- a/mail/e-mail-session.c
+++ b/mail/e-mail-session.c
@@ -52,9 +52,9 @@
#include "e-util/e-alert-dialog.h"
#include "e-util/e-util-private.h"
+#include "e-mail-account-store.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"
#include "em-filter-context.h"
@@ -71,13 +71,26 @@
((obj), E_TYPE_MAIL_SESSION, EMailSessionPrivate))
typedef struct _AsyncContext AsyncContext;
+typedef struct _SourceContext SourceContext;
struct _EMailSessionPrivate {
+ EMailAccountStore *account_store;
MailFolderCache *folder_cache;
+ EAccountList *account_list;
+ gulong account_added_handler_id;
+ gulong account_changed_handler_id;
+
+ CamelStore *local_store;
+ CamelStore *vfolder_store;
+
FILE *filter_logfile;
GHashTable *junk_filters;
EProxy *proxy;
+
+ /* Local folder cache. */
+ GPtrArray *local_folders;
+ GPtrArray *local_folder_uris;
};
struct _AsyncContext {
@@ -90,10 +103,27 @@ struct _AsyncContext {
CamelFolder *folder;
};
+struct _SourceContext {
+ EMailSession *session;
+ CamelService *service;
+};
+
enum {
PROP_0,
+ PROP_ACCOUNT_STORE,
PROP_FOLDER_CACHE,
- PROP_JUNK_FILTER_NAME
+ PROP_JUNK_FILTER_NAME,
+ PROP_LOCAL_STORE,
+ PROP_VFOLDER_STORE
+};
+
+static const gchar *local_folder_names[E_MAIL_NUM_LOCAL_FOLDERS] = {
+ N_("Inbox"), /* E_MAIL_LOCAL_FOLDER_INBOX */
+ N_("Drafts"), /* E_MAIL_LOCAL_FOLDER_DRAFTS */
+ N_("Outbox"), /* E_MAIL_LOCAL_FOLDER_OUTBOX */
+ N_("Sent"), /* E_MAIL_LOCAL_FOLDER_SENT */
+ N_("Templates"), /* E_MAIL_LOCAL_FOLDER_TEMPLATES */
+ "Inbox" /* E_MAIL_LOCAL_FOLDER_LOCAL_INBOX */
};
static gchar *mail_data_dir;
@@ -463,6 +493,18 @@ async_context_free (AsyncContext *context)
g_slice_free (AsyncContext, context);
}
+static void
+source_context_free (SourceContext *context)
+{
+ if (context->session != NULL)
+ g_object_unref (context->session);
+
+ if (context->service != NULL)
+ g_object_unref (context->service);
+
+ g_slice_free (SourceContext, context);
+}
+
static gchar *
mail_session_make_key (CamelService *service,
const gchar *item)
@@ -553,6 +595,270 @@ mail_session_set_junk_filter_name (EMailSession *session,
}
static void
+mail_session_add_by_account (EMailSession *session,
+ EAccount *account)
+{
+ CamelService *service = NULL;
+ CamelProvider *provider;
+ CamelURL *url;
+ gboolean transport_only;
+ GError *error = NULL;
+
+ /* check whether it's transport-only accounts */
+ transport_only =
+ (account->source == NULL) ||
+ (account->source->url == NULL) ||
+ (*account->source->url == '\0');
+ if (transport_only)
+ goto handle_transport;
+
+ /* Load the service, but don't connect. Check its provider,
+ * and if this belongs in the folder tree model, add it. */
+
+ url = camel_url_new (account->source->url, NULL);
+ if (url != NULL) {
+ provider = camel_provider_get (url->protocol, NULL);
+ camel_url_free (url);
+ } else {
+ provider = NULL;
+ }
+
+ if (provider == NULL) {
+ /* In case we do not have a provider here, we handle
+ * the special case of having multiple mail identities
+ * eg. a dummy account having just SMTP server defined */
+ goto handle_transport;
+ }
+
+ service = camel_session_add_service (
+ CAMEL_SESSION (session),
+ account->uid, provider->protocol,
+ CAMEL_PROVIDER_STORE, &error);
+
+ if (error != NULL) {
+ g_warning (
+ "Failed to add service: %s: %s",
+ account->name, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ camel_service_set_display_name (service, account->name);
+
+handle_transport:
+
+ /* While we're at it, add the account's transport (if it has one)
+ * to the CamelSession. The transport's UID is a kludge for now.
+ * We take the EAccount's UID and tack on "-transport". */
+
+ if (account->transport) {
+ GError *transport_error = NULL;
+
+ url = camel_url_new (
+ account->transport->url,
+ &transport_error);
+
+ if (url != NULL) {
+ provider = camel_provider_get (
+ url->protocol, &transport_error);
+ camel_url_free (url);
+ } else
+ provider = NULL;
+
+ if (provider != NULL) {
+ gchar *transport_uid;
+
+ transport_uid = g_strconcat (
+ account->uid, "-transport", NULL);
+
+ camel_session_add_service (
+ CAMEL_SESSION (session),
+ transport_uid, provider->protocol,
+ CAMEL_PROVIDER_TRANSPORT, &transport_error);
+
+ g_free (transport_uid);
+ }
+
+ if (transport_error) {
+ g_warning (
+ "%s: Failed to add transport service: %s",
+ G_STRFUNC, transport_error->message);
+ g_error_free (transport_error);
+ }
+ }
+}
+
+static void
+mail_session_account_added_cb (EAccountList *account_list,
+ EAccount *account,
+ EMailSession *session)
+{
+ mail_session_add_by_account (session, account);
+}
+
+static void
+mail_session_account_changed_cb (EAccountList *account_list,
+ EAccount *account,
+ EMailSession *session)
+{
+ EMFolderTreeModel *folder_tree_model;
+ CamelService *service;
+
+ service = camel_session_get_service (
+ CAMEL_SESSION (session), account->uid);
+
+ if (!CAMEL_IS_STORE (service))
+ return;
+
+ /* Update the display name of the corresponding CamelStore.
+ * EMailAccountStore listens for "notify" signals from each
+ * service so it will detect this and update the model.
+ *
+ * XXX If EAccount defined GObject properties we could just
+ * bind EAccount:name to CamelService:display-name and
+ * be done with it. Oh well.
+ */
+
+ camel_service_set_display_name (service, account->name);
+
+ /* Remove the store from the folder tree model and, if the
+ * account is still enabled, re-add it. Easier than trying
+ * to update the model with the store in place.
+ *
+ * em_folder_tree_model_add_store() already knows which types
+ * of stores to disregard, so we don't have to deal with that
+ * here. */
+
+ folder_tree_model = em_folder_tree_model_get_default ();
+
+ em_folder_tree_model_remove_store (
+ folder_tree_model, CAMEL_STORE (service));
+
+ if (account->enabled)
+ em_folder_tree_model_add_store (
+ folder_tree_model, CAMEL_STORE (service));
+}
+
+static gboolean
+mail_session_add_service_cb (SourceContext *context)
+{
+ EMailAccountStore *store;
+
+ store = e_mail_session_get_account_store (context->session);
+ e_mail_account_store_add_service (store, context->service);
+
+ return FALSE;
+}
+
+static void
+mail_session_add_local_store (EMailSession *session)
+{
+ CamelLocalSettings *local_settings;
+ CamelSession *camel_session;
+ CamelSettings *settings;
+ CamelService *service;
+ const gchar *data_dir;
+ gchar *path;
+ gint ii;
+ GError *error = NULL;
+
+ camel_session = CAMEL_SESSION (session);
+
+ service = camel_session_add_service (
+ camel_session, E_MAIL_SESSION_LOCAL_UID,
+ "maildir", CAMEL_PROVIDER_STORE, &error);
+
+ /* XXX One could argue this is a fatal error
+ * since we depend on it in so many places. */
+ if (error != NULL) {
+ g_critical ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+ camel_service_set_display_name (service, _("On This Computer"));
+
+ settings = camel_service_get_settings (service);
+ local_settings = CAMEL_LOCAL_SETTINGS (settings);
+ data_dir = camel_session_get_user_data_dir (camel_session);
+
+ path = g_build_filename (data_dir, E_MAIL_SESSION_LOCAL_UID, NULL);
+ camel_local_settings_set_path (local_settings, path);
+ g_free (path);
+
+ /* Shouldn't need to worry about other mail applications
+ * altering files in our local mail store. */
+ g_object_set (service, "need-summary-check", FALSE, NULL);
+
+ /* Populate the local folder cache. */
+ for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
+ CamelFolder *folder;
+ gchar *folder_uri;
+ const gchar *display_name;
+ GError *error = NULL;
+
+ display_name = local_folder_names[ii];
+
+ /* XXX This blocks but should be fast. */
+ if (ii == E_MAIL_LOCAL_FOLDER_LOCAL_INBOX)
+ folder = camel_store_get_inbox_folder_sync (
+ CAMEL_STORE (service), NULL, &error);
+ else
+ folder = camel_store_get_folder_sync (
+ CAMEL_STORE (service), display_name,
+ CAMEL_STORE_FOLDER_CREATE, NULL, &error);
+
+ folder_uri = e_mail_folder_uri_build (
+ CAMEL_STORE (service), display_name);
+
+ /* The arrays take ownership of the items added. */
+ g_ptr_array_add (session->priv->local_folders, folder);
+ g_ptr_array_add (session->priv->local_folder_uris, folder_uri);
+
+ if (error != NULL) {
+ g_critical ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+ }
+
+ session->priv->local_store = g_object_ref (service);
+}
+
+static void
+mail_session_add_vfolder_store (EMailSession *session)
+{
+ CamelSession *camel_session;
+ CamelService *service;
+ GError *error = NULL;
+
+ camel_session = CAMEL_SESSION (session);
+
+ service = camel_session_add_service (
+ camel_session, E_MAIL_SESSION_VFOLDER_UID,
+ "vfolder", CAMEL_PROVIDER_STORE, &error);
+
+ if (error != NULL) {
+ g_critical ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+ camel_service_set_display_name (service, _("Search Folders"));
+ em_utils_connect_service_sync (service, NULL, NULL);
+
+ /* XXX There's more configuration to do in vfolder_load_storage()
+ * but it requires an EMailBackend, which we don't have access
+ * to from here, so it has to be called from elsewhere. Kinda
+ * thinking about reworking that... */
+
+ session->priv->vfolder_store = g_object_ref (service);
+}
+
+static void
mail_session_set_property (GObject *object,
guint property_id,
const GValue *value,
@@ -576,6 +882,13 @@ mail_session_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
+ case PROP_ACCOUNT_STORE:
+ g_value_set_object (
+ value,
+ e_mail_session_get_account_store (
+ E_MAIL_SESSION (object)));
+ return;
+
case PROP_FOLDER_CACHE:
g_value_set_object (
value,
@@ -589,6 +902,20 @@ mail_session_get_property (GObject *object,
mail_session_get_junk_filter_name (
E_MAIL_SESSION (object)));
return;
+
+ case PROP_LOCAL_STORE:
+ g_value_set_object (
+ value,
+ e_mail_session_get_local_store (
+ E_MAIL_SESSION (object)));
+ return;
+
+ case PROP_VFOLDER_STORE:
+ g_value_set_object (
+ value,
+ e_mail_session_get_vfolder_store (
+ E_MAIL_SESSION (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -601,11 +928,41 @@ mail_session_dispose (GObject *object)
priv = E_MAIL_SESSION_GET_PRIVATE (object);
+ if (priv->account_store != NULL) {
+ e_mail_account_store_clear (priv->account_store);
+ g_object_unref (priv->account_store);
+ priv->account_store = NULL;
+ }
+
if (priv->folder_cache != NULL) {
g_object_unref (priv->folder_cache);
priv->folder_cache = NULL;
}
+ if (priv->account_list != NULL) {
+ g_signal_handler_disconnect (
+ priv->account_list,
+ priv->account_added_handler_id);
+ g_signal_handler_disconnect (
+ priv->account_list,
+ priv->account_changed_handler_id);
+ g_object_unref (priv->account_list);
+ priv->account_list = NULL;
+ }
+
+ if (priv->local_store != NULL) {
+ g_object_unref (priv->local_store);
+ priv->local_store = NULL;
+ }
+
+ if (priv->vfolder_store != NULL) {
+ g_object_unref (priv->vfolder_store);
+ priv->vfolder_store = NULL;
+ }
+
+ g_ptr_array_set_size (priv->local_folders, 0);
+ g_ptr_array_set_size (priv->local_folder_uris, 0);
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_mail_session_parent_class)->dispose (object);
}
@@ -620,6 +977,9 @@ mail_session_finalize (GObject *object)
g_hash_table_destroy (priv->junk_filters);
g_object_unref (priv->proxy);
+ g_ptr_array_free (priv->local_folders, TRUE);
+ g_ptr_array_free (priv->local_folder_uris, TRUE);
+
g_free (mail_data_dir);
g_free (mail_config_dir);
@@ -642,17 +1002,87 @@ mail_session_notify (GObject *object,
static void
mail_session_constructed (GObject *object)
{
- EMailSessionPrivate *priv;
+ EMFolderTreeModel *folder_tree_model;
+ EMailSession *session;
EExtensible *extensible;
GType extension_type;
- GList *list, *iter;
+ GList *list, *link;
GSettings *settings;
+ EAccountList *account_list;
+ EIterator *iter;
+ EAccount *account;
+ gulong handler_id;
- priv = E_MAIL_SESSION_GET_PRIVATE (object);
+ session = E_MAIL_SESSION (object);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_mail_session_parent_class)->constructed (object);
+ account_list = e_get_account_list ();
+ session->priv->account_list = g_object_ref (account_list);
+
+ session->priv->account_store = e_mail_account_store_new (session);
+
+ /* This must be created after the account store. */
+ session->priv->folder_cache = mail_folder_cache_new (session);
+
+ /* XXX Make sure the folder tree model is created before we
+ * add built-in CamelStores so it gets signals from the
+ * EMailAccountStore.
+ *
+ * XXX This is creating a circular reference. Perhaps the
+ * model should only hold a weak pointer to EMailSession?
+ *
+ * FIXME EMailSession should just own the default instance.
+ */
+ folder_tree_model = em_folder_tree_model_get_default ();
+ em_folder_tree_model_set_session (folder_tree_model, session);
+
+ /* Add built-in CamelStores. */
+
+ mail_session_add_local_store (session);
+ mail_session_add_vfolder_store (session);
+
+ /* Load user-defined mail accounts. */
+
+ iter = e_list_get_iterator (E_LIST (account_list));
+
+ while (e_iterator_is_valid (iter)) {
+ /* XXX EIterator misuses const. */
+ account = (EAccount *) e_iterator_get (iter);
+
+ if (account->enabled)
+ mail_session_add_by_account (session, account);
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ /* Initialize which account is default. */
+
+ account = e_get_default_account ();
+ if (account != NULL) {
+ CamelService *service;
+
+ service = camel_session_get_service (
+ CAMEL_SESSION (session), account->uid);
+ e_mail_account_store_set_default_service (
+ session->priv->account_store, service);
+ }
+
+ /* Listen for account list updates. */
+
+ handler_id = g_signal_connect (
+ account_list, "account-added",
+ G_CALLBACK (mail_session_account_added_cb), session);
+ session->priv->account_added_handler_id = handler_id;
+
+ handler_id = g_signal_connect (
+ account_list, "account-changed",
+ G_CALLBACK (mail_session_account_changed_cb), session);
+ session->priv->account_changed_handler_id = handler_id;
+
extensible = E_EXTENSIBLE (object);
e_extensible_load_extensions (extensible);
@@ -661,11 +1091,11 @@ mail_session_constructed (GObject *object)
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)) {
+ for (link = list; link != NULL; link = g_list_next (link)) {
EMailJunkFilter *junk_filter;
EMailJunkFilterClass *class;
- junk_filter = E_MAIL_JUNK_FILTER (iter->data);
+ junk_filter = E_MAIL_JUNK_FILTER (link->data);
class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
if (!CAMEL_IS_JUNK_FILTER (junk_filter)) {
@@ -693,17 +1123,21 @@ mail_session_constructed (GObject *object)
/* No need to reference the EMailJunkFilter since
* EMailSession owns the reference to it already. */
g_hash_table_insert (
- priv->junk_filters,
+ session->priv->junk_filters,
(gpointer) class->filter_name,
junk_filter);
}
g_list_free (list);
- /* Bind the "junk-default-plugin" GSettings key to our "junk-filter-name" property. */
+ /* Bind the "junk-default-plugin" GSettings
+ * key to our "junk-filter-name" property. */
settings = g_settings_new ("org.gnome.evolution.mail");
- g_settings_bind (settings, "junk-default-plugin", object, "junk-filter-name", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (
+ settings, "junk-default-plugin",
+ object, "junk-filter-name",
+ G_SETTINGS_BIND_DEFAULT);
g_object_unref (settings);
}
@@ -765,6 +1199,22 @@ mail_session_add_service (CamelSession *session,
}
}
+ /* Inform the EMailAccountStore of the new CamelService
+ * from an idle callback so the service has a chance to
+ * fully initialize first. */
+ if (CAMEL_IS_STORE (service)) {
+ SourceContext *context;
+
+ context = g_slice_new0 (SourceContext);
+ context->session = g_object_ref (session);
+ context->service = g_object_ref (service);
+
+ g_idle_add_full (
+ G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc) mail_session_add_service_cb,
+ context, (GDestroyNotify) source_context_free);
+ }
+
return service;
}
@@ -1073,7 +1523,8 @@ mail_session_forward_to (CamelSession *session,
/* and send it */
info = camel_message_info_new (NULL);
- out_folder = e_mail_local_get_folder (E_MAIL_LOCAL_FOLDER_OUTBOX);
+ out_folder = e_mail_session_get_local_folder (
+ E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_OUTBOX);
camel_message_info_set_flags (
info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
@@ -1300,6 +1751,28 @@ e_mail_session_class_init (EMailSessionClass *class)
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_LOCAL_STORE,
+ g_param_spec_object (
+ "local-store",
+ "Local Store",
+ "Built-in local store",
+ CAMEL_TYPE_STORE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_VFOLDER_STORE,
+ g_param_spec_object (
+ "vfolder-store",
+ "Search Folder Store",
+ "Built-in search folder store",
+ CAMEL_TYPE_STORE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
}
static void
@@ -1308,11 +1781,17 @@ e_mail_session_init (EMailSession *session)
GSettings *settings;
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);
session->priv->proxy = e_proxy_new ();
+ session->priv->local_folders =
+ g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_object_unref);
+ session->priv->local_folder_uris =
+ g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_free);
+
/* Initialize the EAccount setup. */
e_account_writable (NULL, E_ACCOUNT_SOURCE_SAVE_PASSWD);
@@ -1348,6 +1827,14 @@ e_mail_session_new (void)
NULL);
}
+EMailAccountStore *
+e_mail_session_get_account_store (EMailSession *session)
+{
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ return session->priv->account_store;
+}
+
MailFolderCache *
e_mail_session_get_folder_cache (EMailSession *session)
{
@@ -1356,6 +1843,58 @@ e_mail_session_get_folder_cache (EMailSession *session)
return session->priv->folder_cache;
}
+CamelStore *
+e_mail_session_get_local_store (EMailSession *session)
+{
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ return session->priv->local_store;
+}
+
+CamelStore *
+e_mail_session_get_vfolder_store (EMailSession *session)
+{
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ return session->priv->vfolder_store;
+}
+
+CamelFolder *
+e_mail_session_get_local_folder (EMailSession *session,
+ EMailLocalFolder type)
+{
+ GPtrArray *local_folders;
+ CamelFolder *folder;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ local_folders = session->priv->local_folders;
+ g_return_val_if_fail (type < local_folders->len, NULL);
+
+ folder = g_ptr_array_index (local_folders, type);
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+ return folder;
+}
+
+const gchar *
+e_mail_session_get_local_folder_uri (EMailSession *session,
+ EMailLocalFolder type)
+{
+ GPtrArray *local_folder_uris;
+ const gchar *folder_uri;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ local_folder_uris = session->priv->local_folder_uris;
+ g_return_val_if_fail (type < local_folder_uris->len, NULL);
+
+ folder_uri = g_ptr_array_index (local_folder_uris, type);
+ g_return_val_if_fail (folder_uri != NULL, NULL);
+
+ return folder_uri;
+}
+
GList *
e_mail_session_get_available_junk_filters (EMailSession *session)
{