/*
* e-mail-backend.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see
*
* Authors:
* Jonathon Jongsma
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
* Copyright (C) 2009 Intel Corporation
*
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include "e-mail-backend.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define E_MAIL_BACKEND_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_MAIL_BACKEND, EMailBackendPrivate))
#define E_MAIL_BACKEND_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_MAIL_BACKEND, EMailBackendPrivate))
#define QUIT_POLL_INTERVAL 1 /* seconds */
struct _EMailBackendPrivate {
EMailSession *session;
GHashTable *jobs;
};
enum {
PROP_0,
PROP_SESSION
};
/* FIXME Kill this thing. It's a horrible hack. */
extern gint camel_application_is_exiting;
G_DEFINE_ABSTRACT_TYPE (
EMailBackend,
e_mail_backend,
E_TYPE_SHELL_BACKEND)
static const gchar *
mail_shell_backend_get_data_dir (EShellBackend *backend)
{
return mail_session_get_data_dir ();
}
static const gchar *
mail_shell_backend_get_config_dir (EShellBackend *backend)
{
return mail_session_get_config_dir ();
}
static gchar *
mail_backend_uri_to_evname (const gchar *uri,
const gchar *prefix)
{
const gchar *data_dir;
gchar *basename;
gchar *filename;
gchar *safe;
/* Converts a folder URI to a GalView filename. */
data_dir = mail_session_get_data_dir ();
safe = g_strdup (uri);
e_filename_make_safe (safe);
basename = g_strdup_printf ("%s%s.xml", prefix, safe);
filename = g_build_filename (data_dir, basename, NULL);
g_free (basename);
g_free (safe);
return filename;
}
/* Callback for various asynchronous CamelStore operations where
* the EActivity's reference count is used as a counting semaphore. */
static void
mail_backend_store_operation_done_cb (CamelStore *store,
GAsyncResult *result,
EActivity *activity)
{
/* FIXME Not checking result for error. To fix this, we need
* separate callbacks to call different finish functions
* and then submit an EAlert on error. */
g_object_unref (activity);
}
static void
mail_backend_prepare_for_offline_cb (EShell *shell,
EActivity *activity,
EMailBackend *backend)
{
GtkWindow *window;
EMailSession *session;
GList *list, *link;
gboolean synchronize = FALSE;
window = e_shell_get_active_window (shell);
session = e_mail_backend_get_session (backend);
if (e_shell_get_network_available (shell) &&
e_shell_backend_is_started (E_SHELL_BACKEND (backend)))
synchronize = em_utils_prompt_user (
window, NULL, "mail:ask-quick-offline", NULL);
if (!synchronize) {
mail_cancel_all ();
camel_session_set_network_available (
CAMEL_SESSION (session), FALSE);
}
list = camel_session_list_services (CAMEL_SESSION (session));
for (link = list; link != NULL; link = g_list_next (link)) {
CamelService *service;
service = CAMEL_SERVICE (link->data);
if (!CAMEL_IS_STORE (service))
continue;
/* FIXME Not passing a GCancellable. */
e_mail_store_go_offline (
CAMEL_STORE (service), G_PRIORITY_DEFAULT,
NULL, (GAsyncReadyCallback)
mail_backend_store_operation_done_cb,
g_object_ref (activity));
}
g_list_free (list);
}
static void
mail_backend_prepare_for_online_cb (EShell *shell,
EActivity *activity,
EMailBackend *backend)
{
EMailSession *session;
GList *list, *link;
session = e_mail_backend_get_session (backend);
camel_session_set_online (CAMEL_SESSION (session), TRUE);
list = camel_session_list_services (CAMEL_SESSION (session));
for (link = list; link != NULL; link = g_list_next (link)) {
CamelService *service;
EAccount *account;
const gchar *uid;
service = CAMEL_SERVICE (link->data);
if (!CAMEL_IS_STORE (service))
continue;
uid = camel_service_get_uid (service);
account = e_get_account_by_uid (uid);
if (account != NULL && !account->enabled)
continue;
/* FIXME Not passing a GCancellable. */
e_mail_store_go_online (
CAMEL_STORE (service), G_PRIORITY_DEFAULT,
NULL, (GAsyncReadyCallback)
mail_backend_store_operation_done_cb,
g_object_ref (activity));
}
g_list_free (link);
}
/* Helper for mail_backend_prepare_for_quit_cb() */
static void
mail_backend_delete_junk (CamelService *service,
EMailBackend *backend)
{
CamelFolder *folder;
GPtrArray *uids;
guint32 flags;
guint32 mask;
guint ii;
/* FIXME camel_store_get_junk_folder_sync() may block. */
folder = camel_store_get_junk_folder_sync (
CAMEL_STORE (service), NULL, NULL);
if (folder == NULL)
return;
uids = camel_folder_get_uids (folder);
flags = mask = CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN;
camel_folder_freeze (folder);
for (ii = 0; ii < uids->len; ii++) {
const gchar *uid = uids->pdata[ii];
camel_folder_set_message_flags (folder, uid, flags, mask);
}
camel_folder_thaw (folder);
camel_folder_free_uids (folder, uids);
}
/* Helper for mail_backend_prepare_for_quit_cb() */
static gboolean
mail_backend_poll_to_quit (EActivity *activity)
{
return mail_msg_active ();
}
/* Helper for mail_backend_prepare_for_quit_cb() */
static void
mail_backend_ready_to_quit (EActivity *activity)
{
emu_free_mail_cache ();
/* Do this last. It may terminate the process. */
g_object_unref (activity);
}
static void
mail_backend_prepare_for_quit_cb (EShell *shell,
EActivity *activity,
EMailBackend *backend)
{
EAccountList *account_list;
EMailSession *session;
GList *list, *link;
gboolean delete_junk;
gboolean empty_trash;
session = e_mail_backend_get_session (backend);
delete_junk = e_mail_backend_delete_junk_policy_decision (backend);
empty_trash = e_mail_backend_empty_trash_policy_decision (backend);
camel_application_is_exiting = TRUE;
account_list = e_get_account_list ();
e_account_list_prune_proxies (account_list);
mail_vfolder_shutdown ();
/* Cancel all pending activities. */
mail_cancel_all ();
list = camel_session_list_services (CAMEL_SESSION (session));
if (delete_junk) {
for (link = list; link != NULL; link = g_list_next (link)) {
CamelService *service;
service = CAMEL_SERVICE (link->data);
if (!CAMEL_IS_STORE (service))
continue;
mail_backend_delete_junk (service, backend);
}
}
for (link = list; link != NULL; link = g_list_next (link)) {
CamelService *service;
service = CAMEL_SERVICE (link->data);
if (!CAMEL_IS_STORE (service))
continue;
/* FIXME Not passing a GCancellable. */
/* FIXME This operation should be queued. */
camel_store_synchronize (
CAMEL_STORE (service),
empty_trash, G_PRIORITY_DEFAULT,
NULL, (GAsyncReadyCallback)
mail_backend_store_operation_done_cb,
g_object_ref (activity));
}
g_list_free (list);
/* Now we poll until all activities are actually cancelled or finished.
* Reffing the activity delays quitting; the reference count
* acts like a counting semaphore. */
if (mail_msg_active ())
g_timeout_add_seconds_full (
G_PRIORITY_DEFAULT, QUIT_POLL_INTERVAL,
(GSourceFunc) mail_backend_poll_to_quit,
g_object_ref (activity),
(GDestroyNotify) mail_backend_ready_to_quit);
else
mail_backend_ready_to_quit (g_object_ref (activity));
}
static void
mail_backend_quit_requested_cb (EShell *shell,
EShellQuitReason reason,
EShellBackend *mail_shell_backend)
{
EMailBackend *backend;
EMailSession *session;
CamelFolder *folder;
GtkWindow *window;
gint response;
window = e_shell_get_active_window (shell);
/* We can quit immediately if offline. */
if (!e_shell_get_online (shell))
return;
/* Or if another Evolution process asked us to. */
if (reason == E_SHELL_QUIT_REMOTE_REQUEST)
return;
/* In express mode, don't raise mail request in non mail window. */
if (e_shell_get_express_mode (shell) &&
strcmp(e_shell_window_get_active_view((EShellWindow *)window), "mail") != 0)
return;
if (!e_shell_backend_is_started (mail_shell_backend))
return;
/* Check Outbox for any unsent messages. */
backend = E_MAIL_BACKEND (mail_shell_backend);
session = e_mail_backend_get_session (backend);
folder = e_mail_session_get_local_folder (
session, E_MAIL_LOCAL_FOLDER_OUTBOX);
if (folder == NULL)
return;
if (camel_folder_summary_get_visible_count (folder->summary) == 0)
return;
response = e_alert_run_dialog_for_args (
window, "mail:exit-unsaved", NULL);
if (response == GTK_RESPONSE_YES)
return;
e_shell_cancel_quit (shell);
}
static void
mail_backend_folder_deleted_cb (MailFolderCache *folder_cache,
CamelStore *store,
const gchar *folder_name,
EMailBackend *backend)
{
CamelStoreClass *class;
EAccountList *account_list;
EIterator *iterator;
EMailSession *session;
EAlertSink *alert_sink;
const gchar *local_drafts_folder_uri;
const gchar *local_sent_folder_uri;
gboolean write_config = FALSE;
gchar *uri;
/* Check whether the deleted folder was a designated Drafts or
* Sent folder for any mail account, and if so revert the setting
* to the equivalent local folder, which is always present. */
class = CAMEL_STORE_GET_CLASS (store);
g_return_if_fail (class->compare_folder_name != NULL);
session = e_mail_backend_get_session (backend);
alert_sink = e_mail_backend_get_alert_sink (backend);
local_drafts_folder_uri =
e_mail_session_get_local_folder_uri (
session, E_MAIL_LOCAL_FOLDER_DRAFTS);
local_sent_folder_uri =
e_mail_session_get_local_folder_uri (
session, E_MAIL_LOCAL_FOLDER_SENT);
uri = e_mail_folder_uri_build (store, folder_name);
account_list = e_get_account_list ();
iterator = e_list_get_iterator (E_LIST (account_list));
while (e_iterator_is_valid (iterator)) {
EAccount *account;
/* XXX EIterator misuses const. */
account = (EAccount *) e_iterator_get (iterator);
if (account->sent_folder_uri != NULL) {
gboolean match;
match = class->compare_folder_name (
account->sent_folder_uri, uri);
if (match) {
g_free (account->sent_folder_uri);
account->sent_folder_uri =
g_strdup (local_sent_folder_uri);
write_config = TRUE;
}
}
if (account->drafts_folder_uri != NULL) {
gboolean match;
match = class->compare_folder_name (
account->drafts_folder_uri, uri);
if (match) {
g_free (account->drafts_folder_uri);
account->drafts_folder_uri =
g_strdup (local_drafts_folder_uri);
write_config = TRUE;
}
}
e_iterator_next (iterator);
}
g_object_unref (iterator);
g_free (uri);
if (write_config)
mail_config_write ();
/* This does something completely different.
* XXX Make it a separate signal handler? */
mail_filter_delete_folder (store, folder_name, alert_sink);
}
static void
mail_backend_folder_renamed_cb (MailFolderCache *folder_cache,
CamelStore *store,
const gchar *old_folder_name,
const gchar *new_folder_name,
EMailBackend *backend)
{
CamelStoreClass *class;
EAccountList *account_list;
EIterator *iterator;
gboolean write_config = FALSE;
gchar *old_uri;
gchar *new_uri;
gint ii;
const gchar *cachenames[] = {
"views/current_view-",
"views/custom_view-"
};
class = CAMEL_STORE_GET_CLASS (store);
g_return_if_fail (class->compare_folder_name != NULL);
old_uri = e_mail_folder_uri_build (store, old_folder_name);
new_uri = e_mail_folder_uri_build (store, new_folder_name);
account_list = e_get_account_list ();
iterator = e_list_get_iterator (E_LIST (account_list));
while (e_iterator_is_valid (iterator)) {
EAccount *account;
/* XXX EIterator misuses const. */
account = (EAccount *) e_iterator_get (iterator);
if (account->sent_folder_uri != NULL) {
gboolean match;
match = class->compare_folder_name (
account->sent_folder_uri, old_uri);
if (match) {
g_free (account->sent_folder_uri);
account->sent_folder_uri = g_strdup (new_uri);
write_config = TRUE;
}
}
if (account->drafts_folder_uri != NULL) {
gboolean match;
match = class->compare_folder_name (
account->drafts_folder_uri, old_uri);
if (match) {
g_free (account->drafts_folder_uri);
account->drafts_folder_uri = g_strdup (new_uri);
write_config = TRUE;
}
}
e_iterator_next (iterator);
}
g_object_unref (iterator);
if (write_config)
mail_config_write ();
/* Rename GalView files. */
for (ii = 0; ii < G_N_ELEMENTS (cachenames); ii++) {
gchar *oldname;
gchar *newname;
oldname = mail_backend_uri_to_evname (old_uri, cachenames[ii]);
newname = mail_backend_uri_to_evname (new_uri, cachenames[ii]);
/* Ignore errors; doesn't matter. */
g_rename (oldname, newname);
g_free (oldname);
g_free (newname);
}
g_free (old_uri);
g_free (new_uri);
/* This does something completely different.
* XXX Make it a separate signal handler? */
mail_filter_rename_folder (
store, old_folder_name, new_folder_name);
}
static void
mail_backend_folder_changed_cb (MailFolderCache *folder_cache,
CamelStore *store,
const gchar *folder_name,
gint new_messages,
const gchar *msg_uid,
const gchar *msg_sender,
const gchar *msg_subject,
EMailBackend *mail_backend)
{
EMEvent *event = em_event_peek ();
EMEventTargetFolder *target;
EMFolderTreeModel *model;
gchar *folder_uri;
gint folder_type;
CamelFolderInfoFlags flags = 0;
folder_uri = e_mail_folder_uri_build (store, folder_name);
if (folder_uri != NULL) {
CamelFolder *folder = NULL;
if (mail_folder_cache_get_folder_from_uri (
folder_cache, folder_uri, &folder))
if (folder != NULL &&
!mail_folder_cache_get_folder_info_flags (
folder_cache, folder, &flags))
g_return_if_reached ();
if (folder != NULL)
g_object_unref (folder);
}
g_free (folder_uri);
target = em_event_target_new_folder (
event, store, folder_name, new_messages,
msg_uid, msg_sender, msg_subject);
folder_type = (flags & CAMEL_FOLDER_TYPE_MASK);
target->is_inbox = (folder_type == CAMEL_FOLDER_TYPE_INBOX);
model = em_folder_tree_model_get_default ();
target->display_name = em_folder_tree_model_get_folder_name (
model, store, folder_name);
if (target->new > 0) {
EShell *shell;
EShellBackend *shell_backend;
shell_backend = E_SHELL_BACKEND (mail_backend);
shell = e_shell_backend_get_shell (shell_backend);
e_shell_event (shell, "mail-icon", (gpointer) "mail-unread");
}
/**
* @Event: folder.changed
* @Title: Folder changed
* @Target: EMEventTargetFolder
*
* folder.changed is emitted whenever a folder changes. There is no
* detail on how the folder has changed.
*
* UPDATE: We tell the number of new UIDs added rather than the new
* mails received.
*/
e_event_emit (
(EEvent *) event, "folder.changed",
(EEventTarget *) target);
}
static void
mail_backend_job_started_cb (CamelSession *session,
GCancellable *cancellable,
EShellBackend *shell_backend)
{
EMailBackendPrivate *priv;
EActivity *activity;
priv = E_MAIL_BACKEND_GET_PRIVATE (shell_backend);
/* Make sure this operation shows up in the user interface.
* This message should get overridden, if not it's a bug in
* whatever CamelService submitted this. */
camel_operation_push_message (
cancellable, _("Unknown background operation"));
activity = e_activity_new ();
e_activity_set_cancellable (activity, cancellable);
e_shell_backend_add_activity (shell_backend, activity);
/* The hash table takes ownership of the activity. */
g_hash_table_insert (priv->jobs, cancellable, activity);
}
static void
mail_backend_job_finished_cb (CamelSession *session,
GCancellable *cancellable,
const GError *error,
EShellBackend *shell_backend)
{
EMailBackendPrivate *priv;
EShellBackendClass *class;
EActivity *activity;
const gchar *description;
priv = E_MAIL_BACKEND_GET_PRIVATE (shell_backend);
class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
/* Pop the generic "background operation" message. */
camel_operation_pop_message (cancellable);
activity = g_hash_table_lookup (priv->jobs, cancellable);
description = e_activity_get_text (activity);
if (e_activity_handle_cancellation (activity, error)) {
/* nothing to do */
} else if (error != NULL) {
EShell *shell;
GtkApplication *application;
GList *list, *iter;
shell = e_shell_backend_get_shell (shell_backend);
application = GTK_APPLICATION (shell);
list = gtk_application_get_windows (application);
/* Submit the error to an appropriate EAlertSink. */
for (iter = list; iter != NULL; iter = g_list_next (iter)) {
EShellView *shell_view;
EShellContent *shell_content;
if (!E_IS_SHELL_WINDOW (iter->data))
continue;
shell_view = e_shell_window_peek_shell_view (
E_SHELL_WINDOW (iter->data), class->name);
if (!E_IS_SHELL_VIEW (shell_view))
continue;
shell_content =
e_shell_view_get_shell_content (shell_view);
if (description != NULL && *description != '\0')
e_alert_submit (
E_ALERT_SINK (shell_content),
"mail:async-error", description,
error->message, NULL);
else
e_alert_submit (
E_ALERT_SINK (shell_content),
"mail:async-error-nodescribe",
error->message, NULL);
break;
}
}
g_hash_table_remove (priv->jobs, cancellable);
}
static void
mail_backend_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_SESSION:
g_value_set_object (
value,
e_mail_backend_get_session (
E_MAIL_BACKEND (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
mail_backend_dispose (GObject *object)
{
EMailBackendPrivate *priv;
priv = E_MAIL_BACKEND_GET_PRIVATE (object);
if (priv->session != NULL) {
g_signal_handlers_disconnect_matched (
priv->session, G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, object);
camel_session_remove_services (
CAMEL_SESSION (priv->session));
g_object_unref (priv->session);
priv->session = NULL;
}
/* There should be no unfinished jobs left. */
g_warn_if_fail (g_hash_table_size (priv->jobs) == 0);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_mail_backend_parent_class)->dispose (object);
}
static void
mail_backend_finalize (GObject *object)
{
EMailBackendPrivate *priv;
priv = E_MAIL_BACKEND_GET_PRIVATE (object);
g_hash_table_destroy (priv->jobs);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_mail_backend_parent_class)->finalize (object);
camel_shutdown ();
}
static void
mail_backend_add_store (EMailSession *session,
CamelStore *store,
EMailBackend *backend)
{
EMFolderTreeModel *model;
model = em_folder_tree_model_get_default ();
em_folder_tree_model_add_store (model, store);
}
static void
mail_backend_remove_store (EMailSession *session,
CamelStore *store,
EMailBackend *backend)
{
EMFolderTreeModel *model;
model = em_folder_tree_model_get_default ();
em_folder_tree_model_remove_store (model, store);
}
#define SET_ACITIVITY(cancellable, activity) \
g_object_set_data (G_OBJECT (cancellable), "e-activity", activity)
#define GET_ACITIVITY(cancellable) \
g_object_get_data (G_OBJECT (cancellable), "e-activity")
static void
mail_mt_create_activity (GCancellable *cancellable)
{
EActivity *activity;
activity = e_activity_new ();
e_activity_set_percent (activity, 0.0);
e_activity_set_cancellable (activity, cancellable);
SET_ACITIVITY (cancellable, activity);
}
static void
mail_mt_submit_activity (GCancellable *cancellable)
{
EShell *shell;
EShellBackend *shell_backend;
EActivity *activity;
shell = e_shell_get_default ();
shell_backend = e_shell_get_backend_by_name (
shell, "mail");
activity = GET_ACITIVITY (cancellable);
if (activity)
e_shell_backend_add_activity (shell_backend, activity);
}
static void
mail_mt_free_activity (GCancellable *cancellable)
{
EActivity *activity = GET_ACITIVITY (cancellable);
if (activity)
g_object_unref (activity);
}
static void
mail_mt_complete_acitivity (GCancellable *cancellable)
{
EActivity *activity = GET_ACITIVITY (cancellable);
if (activity)
e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
}
static void
mail_mt_cancel_activity (GCancellable *cancellable)
{
EActivity *activity = GET_ACITIVITY (cancellable);
if (activity)
e_activity_set_state (activity, E_ACTIVITY_CANCELLED);
}
static void
mail_mt_alert_error (GCancellable *cancellable,
const gchar *what,
const gchar *message)
{
EShell *shell;
EShellView *shell_view;
EShellWindow *shell_window = NULL;
EShellContent *shell_content;
GList *list, *iter;
GtkApplication *application;
shell = e_shell_get_default ();
application = GTK_APPLICATION (shell);
list = gtk_application_get_windows (application);
/* Find the most recently used EShellWindow. */
for (iter = list; iter != NULL; iter = g_list_next (iter)) {
if (E_IS_SHELL_WINDOW (iter->data)) {
shell_window = E_SHELL_WINDOW (iter->data);
break;
}
}
/* If we can't find an EShellWindow then... well, screw it. */
if (shell_window == NULL)
return;
shell_view = e_shell_window_get_shell_view (
shell_window, "mail");
shell_content = e_shell_view_get_shell_content (shell_view);
if (what) {
e_alert_submit (
E_ALERT_SINK (shell_content),
"mail:async-error", what,
message, NULL);
} else
e_alert_submit (
E_ALERT_SINK (shell_content),
"mail:async-error-nodescribe",
message, NULL);
}
static EAlertSink *
mail_mt_get_alert_sink ()
{
EShell *shell;
EShellBackend *shell_backend;
shell = e_shell_get_default ();
shell_backend = e_shell_get_backend_by_name (
shell, "mail");
return e_mail_backend_get_alert_sink (E_MAIL_BACKEND (shell_backend));
}
static void
mail_backend_constructed (GObject *object)
{
EMailBackendPrivate *priv;
EShell *shell;
EShellBackend *shell_backend;
MailFolderCache *folder_cache;
priv = E_MAIL_BACKEND_GET_PRIVATE (object);
shell_backend = E_SHELL_BACKEND (object);
shell = e_shell_backend_get_shell (shell_backend);
if (camel_init (e_get_user_data_dir (), TRUE) != 0)
exit (0);
camel_provider_init ();
priv->session = e_mail_ui_session_new ();
g_signal_connect (
priv->session, "flush-outbox",
G_CALLBACK (mail_send), priv->session);
g_object_bind_property (
shell, "online",
priv->session, "online",
G_BINDING_SYNC_CREATE);
/* Propagate "activity-added" signals from
* the mail session to the shell backend. */
g_signal_connect_swapped (
priv->session, "activity-added",
G_CALLBACK (e_shell_backend_add_activity),
shell_backend);
g_signal_connect (
priv->session, "job-started",
G_CALLBACK (mail_backend_job_started_cb),
shell_backend);
g_signal_connect (
priv->session, "job-finished",
G_CALLBACK (mail_backend_job_finished_cb),
shell_backend);
/* FIXME This is an evil hack that needs to die.
* Give EAccountComboBox a CamelSession property. */
e_account_combo_box_set_session (CAMEL_SESSION (priv->session));
g_signal_connect (
priv->session, "store-added",
G_CALLBACK (mail_backend_add_store),
shell_backend);
g_signal_connect (
priv->session, "store-removed",
G_CALLBACK (mail_backend_remove_store),
shell_backend);
g_signal_connect (
shell, "prepare-for-offline",
G_CALLBACK (mail_backend_prepare_for_offline_cb),
shell_backend);
g_signal_connect (
shell, "prepare-for-online",
G_CALLBACK (mail_backend_prepare_for_online_cb),
shell_backend);
g_signal_connect (
shell, "prepare-for-quit",
G_CALLBACK (mail_backend_prepare_for_quit_cb),
shell_backend);
g_signal_connect (
shell, "quit-requested",
G_CALLBACK (mail_backend_quit_requested_cb),
shell_backend);
folder_cache = e_mail_session_get_folder_cache (priv->session);
g_signal_connect (
folder_cache, "folder-deleted",
G_CALLBACK (mail_backend_folder_deleted_cb),
shell_backend);
g_signal_connect (
folder_cache, "folder-renamed",
G_CALLBACK (mail_backend_folder_renamed_cb),
shell_backend);
g_signal_connect (
folder_cache, "folder-changed",
G_CALLBACK (mail_backend_folder_changed_cb), shell_backend);
mail_config_init (priv->session);
mail_msg_init ();
mail_msg_register_activities (
mail_mt_create_activity,
mail_mt_submit_activity,
mail_mt_free_activity,
mail_mt_complete_acitivity,
mail_mt_cancel_activity,
mail_mt_alert_error,
mail_mt_get_alert_sink);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_mail_backend_parent_class)->constructed (object);
}
static void
e_mail_backend_class_init (EMailBackendClass *class)
{
GObjectClass *object_class;
EShellBackendClass *shell_backend_class;
g_type_class_add_private (class, sizeof (EMailBackendPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->get_property = mail_backend_get_property;
object_class->dispose = mail_backend_dispose;
object_class->finalize = mail_backend_finalize;
object_class->constructed = mail_backend_constructed;
shell_backend_class = E_SHELL_BACKEND_CLASS (class);
shell_backend_class->migrate = e_mail_migrate;
shell_backend_class->get_data_dir = mail_shell_backend_get_data_dir;
shell_backend_class->get_config_dir = mail_shell_backend_get_config_dir;
g_object_class_install_property (
object_class,
PROP_SESSION,
g_param_spec_object (
"session",
NULL,
NULL,
E_TYPE_MAIL_SESSION,
G_PARAM_READABLE));
}
static void
e_mail_backend_init (EMailBackend *backend)
{
backend->priv = E_MAIL_BACKEND_GET_PRIVATE (backend);
backend->priv->jobs = g_hash_table_new_full (
(GHashFunc) g_direct_hash,
(GEqualFunc) g_direct_equal,
(GDestroyNotify) NULL,
(GDestroyNotify) g_object_unref);
}
EMailSession *
e_mail_backend_get_session (EMailBackend *backend)
{
g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
return backend->priv->session;
}
EAlertSink *
e_mail_backend_get_alert_sink (EMailBackend *backend)
{
EShell *shell;
EShellView *shell_view;
EShellBackend *shell_backend;
EShellContent *shell_content;
EShellWindow *shell_window = NULL;
EShellBackendClass *class;
GtkApplication *application;
GList *list, *link;
/* XXX This is meant to be a convenient but temporary hack.
* It digs through the list of available EShellWindows
* to find a suitable EAlertSink. */
g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL);
shell_backend = E_SHELL_BACKEND (backend);
shell = e_shell_backend_get_shell (shell_backend);
application = GTK_APPLICATION (shell);
list = gtk_application_get_windows (application);
/* Find the most recently used EShellWindow. */
for (link = list; link != NULL; link = g_list_next (link)) {
if (E_IS_SHELL_WINDOW (link->data)) {
shell_window = E_SHELL_WINDOW (link->data);
break;
}
}
g_return_val_if_fail (shell_window != NULL, NULL);
class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
shell_view = e_shell_window_get_shell_view (shell_window, class->name);
shell_content = e_shell_view_get_shell_content (shell_view);
return E_ALERT_SINK (shell_content);
}
gboolean
e_mail_backend_delete_junk_policy_decision (EMailBackend *backend)
{
EMailBackendClass *class;
g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), FALSE);
class = E_MAIL_BACKEND_GET_CLASS (backend);
if (class->delete_junk_policy_decision == NULL)
return FALSE;
return class->delete_junk_policy_decision (backend);
}
gboolean
e_mail_backend_empty_trash_policy_decision (EMailBackend *backend)
{
EMailBackendClass *class;
g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), FALSE);
class = E_MAIL_BACKEND_GET_CLASS (backend);
if (class->empty_trash_policy_decision == NULL)
return FALSE;
return class->empty_trash_policy_decision (backend);
}