/* * 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); }