aboutsummaryrefslogblamecommitdiffstats
path: root/mail/e-mail-ui-session.c
blob: fd9a4a94f23ff0c9157b716afeb7e65bbbe1aa23 (plain) (tree)







































                                                                               












                                           
                                              












                                                
                                      















                                                               









                                          
                        
































                                                                               
                    





































































































                                                                                                    

                                                               









                                                                                                            


                                                                       
                                    


                                                             





                                                                                  
 
























                                                                                            












                                                                     
 





































































































































































































                                                                                        












                                                                          













































































                                                                      
                                               






















                                                                              

































































                                                                          
                                                                       





















                                                                     

















                                                                       



                                                    

























                                                                        





                                                                            




                                                         
                                              




                                                                         
                                                                  







                                                                             

                                                                                            













































                                                                      







                                                                    

















                                                                      
/*
 * e-mail-ui-session.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/>
 *
 *
 * Authors:
 *   Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 * Copyright (C) 2009 Intel Corporation
 *
 */

/* mail-session.c: handles the session information and resource manipulation */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <glib/gi18n.h>
#include <glib/gstdio.h>

#include <gtk/gtk.h>

#ifdef HAVE_CANBERRA
#include <canberra-gtk.h>
#endif

#include <libedataserver/e-flag.h>
#include <libedataserver/e-proxy.h>
#include <libebackend/e-extensible.h>
#include <libedataserverui/e-passwords.h>

#include "e-mail-account-store.h"

#include "e-util/e-util.h"
#include "libemail-utils/e-account-utils.h"
#include "libevolution-utils/e-alert-dialog.h"
#include "e-util/e-util-private.h"

#include "shell/e-shell.h"
#include "shell/e-shell-view.h"
#include "shell/e-shell-content.h"
#include "shell/e-shell-window.h"

#include "libemail-engine/e-mail-folder-utils.h"
#include "libemail-engine/e-mail-junk-filter.h"
#include "libemail-engine/e-mail-session.h"
#include "e-mail-ui-session.h"
#include "em-composer-utils.h"
#include "em-filter-context.h"
#include "em-vfolder-editor-context.h"
#include "em-filter-rule.h"
#include "em-utils.h"
#include "libemail-engine/mail-config.h"
#include "libemail-utils/mail-mt.h"
#include "libemail-engine/mail-ops.h"
#include "mail-send-recv.h"
#include "libemail-engine/mail-tools.h"

#define E_MAIL_UI_SESSION_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), E_TYPE_MAIL_UI_SESSION, EMailUISessionPrivate))

typedef struct _SourceContext SourceContext;

struct _EMailUISessionPrivate {
    FILE *filter_logfile;
    EMailAccountStore *account_store;
    EMailLabelListStore *label_store;

    EAccountList *account_list;
    gulong account_changed_handler_id;
};

enum {
    PROP_0,
    PROP_ACCOUNT_STORE,
    PROP_LABEL_STORE
};

enum {
    ACTIVITY_ADDED,
    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

G_DEFINE_TYPE_WITH_CODE (
    EMailUISession,
    e_mail_ui_session,
    E_TYPE_MAIL_SESSION,
    G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))

struct _SourceContext {
    EMailUISession *session;
    CamelService *service;
};

/* Support for CamelSession.alert_user() *************************************/

static gpointer user_message_dialog;
static GQueue user_message_queue = { NULL, NULL, 0 };

struct _user_message_msg {
    MailMsg base;

    CamelSessionAlertType type;
    gchar *prompt;
    GSList *button_captions;
    EFlag *done;

    gint result;
    guint ismain : 1;
};

static void user_message_exec (struct _user_message_msg *m,
                               GCancellable *cancellable,
                               GError **error);

static void
user_message_response_free (GtkDialog *dialog,
                            gint button)
{
    struct _user_message_msg *m = NULL;

    gtk_widget_destroy ((GtkWidget *) dialog);

    user_message_dialog = NULL;

    /* check for pendings */
    if (!g_queue_is_empty (&user_message_queue)) {
        GCancellable *cancellable;

        m = g_queue_pop_head (&user_message_queue);
        cancellable = m->base.cancellable;
        user_message_exec (m, cancellable, &m->base.error);
        mail_msg_unref (m);
    }
}

/* clicked, send back the reply */
static void
user_message_response (GtkDialog *dialog,
                       gint button,
                       struct _user_message_msg *m)
{
    /* if !m or !button_captions, then we've already replied */
    if (m && m->button_captions) {
        m->result = button;
        e_flag_set (m->done);
    }

    user_message_response_free (dialog, button);
}

static void
user_message_exec (struct _user_message_msg *m,
                   GCancellable *cancellable,
                   GError **error)
{
    gboolean info_only;
    GtkWindow *parent;
    EShell *shell;
    const gchar *error_type;
    gint index;
    GSList *iter;

    info_only = g_slist_length (m->button_captions) <= 1;

    if (!m->ismain && user_message_dialog != NULL && !info_only) {
        g_queue_push_tail (&user_message_queue, mail_msg_ref (m));
        return;
    }

    switch (m->type) {
        case CAMEL_SESSION_ALERT_INFO:
            error_type = "mail:session-message-info";
            break;
        case CAMEL_SESSION_ALERT_WARNING:
            error_type = "mail:session-message-warning";
            break;
        case CAMEL_SESSION_ALERT_ERROR:
            error_type = "mail:session-message-error";
            break;
        default:
            error_type = NULL;
            g_return_if_reached ();
    }

    shell = e_shell_get_default ();

    /* try to find "mail" view to place the informational alert to */
    if (info_only) {
        GtkWindow *active_window;
        EShellWindow *shell_window;
        EShellView *shell_view;
        EShellContent *shell_content = NULL;

        /* check currently active window first, ... */
        active_window = e_shell_get_active_window (shell);
        if (active_window && E_IS_SHELL_WINDOW (active_window)) {
            if (E_IS_SHELL_WINDOW (active_window)) {
                shell_window = E_SHELL_WINDOW (active_window);
                shell_view = e_shell_window_peek_shell_view (shell_window, "mail");
                if (shell_view)
                    shell_content = e_shell_view_get_shell_content (shell_view);
            }
        }

        if (!shell_content) {
            GList *list, *iter;

            list = gtk_application_get_windows (GTK_APPLICATION (shell));

            /* ...then iterate through all opened
             * windows and pick one which has it */
            for (iter = list; iter != NULL && !shell_content; iter = g_list_next (iter)) {
                if (E_IS_SHELL_WINDOW (iter->data)) {
                    shell_window = iter->data;
                    shell_view = e_shell_window_peek_shell_view (shell_window, "mail");
                    if (shell_view)
                        shell_content = e_shell_view_get_shell_content (shell_view);
                }
            }
        }

        /* When no shell-content found, which might not happen,
         * but just in case, process the information alert like
         * usual, through an EAlertDialog machinery. */
        if (shell_content) {
            e_alert_submit (
                E_ALERT_SINK (shell_content),
                error_type, m->prompt, NULL);
            return;
        } else if (!m->ismain && user_message_dialog != NULL) {
            g_queue_push_tail (&user_message_queue, mail_msg_ref (m));
            return;
        }
    }

    /* Pull in the active window from the shell to get a parent window */
    parent = e_shell_get_active_window (shell);
    user_message_dialog = e_alert_dialog_new_for_args (
        parent, error_type, m->prompt, NULL);
    g_object_set (user_message_dialog, "resizable", TRUE, NULL);

    if (m->button_captions) {
        GtkWidget *action_area;
        GList *children, *child;

        /* remove all default buttons and keep only those requested */
        action_area = gtk_dialog_get_action_area (GTK_DIALOG (user_message_dialog));

        children = gtk_container_get_children (GTK_CONTAINER (action_area));
        for (child = children; child != NULL; child = child->next) {
            gtk_container_remove (GTK_CONTAINER (action_area), child->data);
        }

        g_list_free (children);
    }

    for (index = 0, iter = m->button_captions; iter; index++, iter = iter->next) {
        gtk_dialog_add_button (GTK_DIALOG (user_message_dialog), iter->data, index);
    }

    /* XXX This is a case where we need to be able to construct
     *     custom EAlerts without a predefined XML definition. */
    if (m->ismain) {
        gint response;

        response = gtk_dialog_run (user_message_dialog);
        user_message_response (
            user_message_dialog, response, m);
    } else {
        gpointer user_data = m;

        if (g_slist_length (m->button_captions) <= 1)
            user_data = NULL;

        g_signal_connect (
            user_message_dialog, "response",
            G_CALLBACK (user_message_response), user_data);
        gtk_widget_show (user_message_dialog);
    }
}

static void
user_message_free (struct _user_message_msg *m)
{
    g_free (m->prompt);
    g_slist_free_full (m->button_captions, g_free);
    e_flag_free (m->done);
}

static MailMsgInfo user_message_info = {
    sizeof (struct _user_message_msg),
    (MailMsgDescFunc) NULL,
    (MailMsgExecFunc) user_message_exec,
    (MailMsgDoneFunc) NULL,
    (MailMsgFreeFunc) user_message_free
};

/* Support for CamelSession.get_filter_driver () *****************************/

static CamelFolder *
get_folder (CamelFilterDriver *d,
            const gchar *uri,
            gpointer user_data,
            GError **error)
{
    EMailSession *session = E_MAIL_SESSION (user_data);

    /* FIXME Not passing a GCancellable here. */
    /* FIXME Need a camel_filter_driver_get_session(). */
    return e_mail_session_uri_to_folder_sync (
        session, uri, 0, NULL, error);
}

static gboolean
session_play_sound_cb (const gchar *filename)
{
#ifdef HAVE_CANBERRA
    if (filename != NULL && *filename != '\0')
        ca_context_play (
            ca_gtk_context_get (), 0,
            CA_PROP_MEDIA_FILENAME, filename,
            NULL);
    else
#endif
        gdk_beep ();

    return FALSE;
}

static void
session_play_sound (CamelFilterDriver *driver,
                    const gchar *filename,
                    gpointer user_data)
{
    g_idle_add_full (
        G_PRIORITY_DEFAULT_IDLE,
        (GSourceFunc) session_play_sound_cb,
        g_strdup (filename), (GDestroyNotify) g_free);
}

static void
session_system_beep (CamelFilterDriver *driver,
                     gpointer user_data)
{
    g_idle_add ((GSourceFunc) session_play_sound_cb, NULL);
}

static CamelFilterDriver *
main_get_filter_driver (CamelSession *session,
                        const gchar *type,
                        GError **error)
{
    EMailSession *ms = E_MAIL_SESSION (session);
    CamelFilterDriver *driver;
    EFilterRule *rule = NULL;
    const gchar *config_dir;
    gchar *user, *system;
    GSettings *settings;
    ERuleContext *fc;
    EMailUISessionPrivate *priv;

    priv = E_MAIL_UI_SESSION_GET_PRIVATE (session);

    settings = g_settings_new ("org.gnome.evolution.mail");

    config_dir = mail_session_get_config_dir ();
    user = g_build_filename (config_dir, "filters.xml", NULL);
    system = g_build_filename (EVOLUTION_PRIVDATADIR, "filtertypes.xml", NULL);
    fc = (ERuleContext *) em_filter_context_new (ms);
    e_rule_context_load (fc, system, user);
    g_free (system);
    g_free (user);

    driver = camel_filter_driver_new (session);
    camel_filter_driver_set_folder_func (driver, get_folder, session);

    if (g_settings_get_boolean (settings, "filters-log-actions")) {
        if (priv->filter_logfile == NULL) {
            gchar *filename;

            filename = g_settings_get_string (settings, "filters-log-file");
            if (filename) {
                priv->filter_logfile = g_fopen (filename, "a+");
                g_free (filename);
            }
        }

        if (priv->filter_logfile)
            camel_filter_driver_set_logfile (driver, priv->filter_logfile);
    }

    camel_filter_driver_set_shell_func (driver, mail_execute_shell_command, NULL);
    camel_filter_driver_set_play_sound_func (driver, session_play_sound, NULL);
    camel_filter_driver_set_system_beep_func (driver, session_system_beep, NULL);

    if ((!strcmp (type, E_FILTER_SOURCE_INCOMING) ||
        !strcmp (type, E_FILTER_SOURCE_JUNKTEST))
        && camel_session_get_check_junk (session)) {

        /* implicit junk check as 1st rule */
        camel_filter_driver_add_rule (
            driver, "Junk check", "(junk-test)",
            "(begin (set-system-flag \"junk\"))");
    }

    if (strcmp (type, E_FILTER_SOURCE_JUNKTEST) != 0) {
        GString *fsearch, *faction;

        fsearch = g_string_new ("");
        faction = g_string_new ("");

        if (!strcmp (type, E_FILTER_SOURCE_DEMAND))
            type = E_FILTER_SOURCE_INCOMING;

        /* add the user-defined rules next */
        while ((rule = e_rule_context_next_rule (fc, rule, type))) {
            g_string_truncate (fsearch, 0);
            g_string_truncate (faction, 0);

            /* skip disabled rules */
            if (!rule->enabled)
                continue;

            e_filter_rule_build_code (rule, fsearch);
            em_filter_rule_build_action (
                EM_FILTER_RULE (rule), faction);
            camel_filter_driver_add_rule (
                driver, rule->name,
                fsearch->str, faction->str);
        }

        g_string_free (fsearch, TRUE);
        g_string_free (faction, TRUE);
    }

    g_object_unref (fc);

    g_object_unref (settings);

    return driver;
}

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 void
mail_ui_session_dispose (GObject *object)
{
    EMailUISessionPrivate *priv;

    priv = E_MAIL_UI_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->label_store != NULL) {
        g_object_unref (priv->label_store);
        priv->label_store = NULL;
    }

    if (priv->account_list != NULL) {
        g_signal_handler_disconnect (
            priv->account_list,
            priv->account_changed_handler_id);
        g_object_unref (priv->account_list);
        priv->account_list = NULL;
    }

    /* Chain up to parent's dispose() method. */
    G_OBJECT_CLASS (e_mail_ui_session_parent_class)->dispose (object);
}

static void
mail_ui_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_ui_session_initialize_stores_idle (gpointer user_data)
{
    EMailUISession *session = user_data;
    EMailAccountStore *account_store;
    EAccount *account;

    g_return_val_if_fail (session != NULL, FALSE);

    account_store = e_mail_ui_session_get_account_store (session);

    /* 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 (
            account_store, service);
    }

    return FALSE;
}

static void
mail_ui_session_constructed (GObject *object)
{
    EMailUISessionPrivate *priv;
    EMFolderTreeModel *folder_tree_model;
    EMailSession *session;
    EMailUISession *uisession;
    EAccountList *account_list;
    gulong handler_id;

    session = E_MAIL_SESSION (object);
    uisession = E_MAIL_UI_SESSION (object);
    uisession->priv = priv = E_MAIL_UI_SESSION_GET_PRIVATE (object);

    priv->account_store = e_mail_account_store_new (session);

    account_list = e_get_account_list ();
    uisession->priv->account_list = g_object_ref (account_list);

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

    /* Chain up to parent's constructed() method. */
    G_OBJECT_CLASS (e_mail_ui_session_parent_class)->constructed (object);

    em_folder_tree_model_set_session (folder_tree_model, session);

    g_idle_add (mail_ui_session_initialize_stores_idle, object);

    handler_id = g_signal_connect (
        account_list, "account-changed",
        G_CALLBACK (mail_ui_session_account_changed_cb), session);
    priv->account_changed_handler_id = handler_id;

}

static gint
mail_ui_session_alert_user (CamelSession *session,
                            CamelSessionAlertType type,
                            const gchar *prompt,
                            GSList *button_captions)
{
    struct _user_message_msg *m;
    GCancellable *cancellable;
    gint result = -1;
    GSList *iter;

    m = mail_msg_new (&user_message_info);
    m->ismain = mail_in_main_thread ();
    m->type = type;
    m->prompt = g_strdup (prompt);
    m->done = e_flag_new ();
    m->button_captions = g_slist_copy (button_captions);

    for (iter = m->button_captions; iter; iter = iter->next)
        iter->data = g_strdup (iter->data);

    if (g_slist_length (button_captions) > 1)
        mail_msg_ref (m);

    cancellable = m->base.cancellable;

    if (m->ismain)
        user_message_exec (m, cancellable, &m->base.error);
    else
        mail_msg_main_loop_push (m);

    if (g_slist_length (button_captions) > 1) {
        e_flag_wait (m->done);
        result = m->result;
        mail_msg_unref (m);
    } else if (m->ismain)
        mail_msg_unref (m);

    return result;
}

static CamelFilterDriver *
mail_ui_session_get_filter_driver (CamelSession *session,
                                const gchar *type,
                                GError **error)
{
    return (CamelFilterDriver *) mail_call_main (
        MAIL_CALL_p_ppp, (MailMainFunc) main_get_filter_driver,
        session, type, error);
}

static void
mail_ui_session_set_property (GObject *object,
                           guint property_id,
                           const GValue *value,
                           GParamSpec *pspec)
{
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
mail_ui_session_get_property (GObject *object,
                           guint property_id,
                           GValue *value,
                           GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_ACCOUNT_STORE:
            g_value_set_object (
                value,
                e_mail_ui_session_get_account_store (
                E_MAIL_UI_SESSION (object)));
            return;

        case PROP_LABEL_STORE:
            g_value_set_object (
                value,
                e_mail_ui_session_get_label_store (
                E_MAIL_UI_SESSION (object)));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static gboolean
mail_ui_session_add_service_cb (SourceContext *context)
{
    EMailAccountStore *store;

    store = e_mail_ui_session_get_account_store (context->session);
    e_mail_account_store_add_service (store, context->service);

    return FALSE;
}

static CamelService *
mail_ui_session_add_service (CamelSession *session,
                             const gchar *uid,
                             const gchar *protocol,
                             CamelProviderType type,
                             GError **error)
{
    CamelService *service;

    /* Chain up to parent's constructed() method. */
    service = CAMEL_SESSION_CLASS (e_mail_ui_session_parent_class)->
        add_service (session, uid, protocol, type, error);

    /* 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_ui_session_add_service_cb,
            context, (GDestroyNotify) source_context_free);
    }

    return service;
}

static EMVFolderContext *
mail_ui_session_create_vfolder_context (EMailSession *session)
{
    return (EMVFolderContext *) em_vfolder_editor_context_new (session);
}

static void
e_mail_ui_session_class_init (EMailUISessionClass *class)
{
    GObjectClass *object_class;
    CamelSessionClass *session_class;
    EMailSessionClass *emailsession_class;

    g_type_class_add_private (class, sizeof (EMailUISessionPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->set_property = mail_ui_session_set_property;
    object_class->get_property = mail_ui_session_get_property;
    object_class->dispose = mail_ui_session_dispose;
    object_class->constructed = mail_ui_session_constructed;

    session_class = CAMEL_SESSION_CLASS (class);
    session_class->alert_user = mail_ui_session_alert_user;
    session_class->get_filter_driver = mail_ui_session_get_filter_driver;
    session_class->add_service = mail_ui_session_add_service;

    emailsession_class = E_MAIL_SESSION_CLASS (class);
    emailsession_class->create_vfolder_context = mail_ui_session_create_vfolder_context;

    g_object_class_install_property (
        object_class,
        PROP_LABEL_STORE,
        g_param_spec_object (
            "label-store",
            "Label Store",
            "Mail label store",
            E_TYPE_MAIL_LABEL_LIST_STORE,
            G_PARAM_READABLE |
            G_PARAM_STATIC_STRINGS));

    signals[ACTIVITY_ADDED] = g_signal_new (
        "activity-added",
        G_OBJECT_CLASS_TYPE (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (EMailUISessionClass, activity_added),
        NULL, NULL,
        g_cclosure_marshal_VOID__OBJECT,
        G_TYPE_NONE, 1,
        E_TYPE_ACTIVITY);
}

static void
e_mail_ui_session_init (EMailUISession *session)
{
    session->priv = E_MAIL_UI_SESSION_GET_PRIVATE (session);
    session->priv->label_store = e_mail_label_list_store_new ();
}

EMailSession *
e_mail_ui_session_new (void)
{
    const gchar *user_data_dir;
    const gchar *user_cache_dir;

    user_data_dir = mail_session_get_data_dir ();
    user_cache_dir = mail_session_get_cache_dir ();

    return g_object_new (
        E_TYPE_MAIL_UI_SESSION,
        "user-data-dir", user_data_dir,
        "user-cache-dir", user_cache_dir,
        NULL);
}

EMailAccountStore *
e_mail_ui_session_get_account_store (EMailUISession *session)
{
    g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session), NULL);

    return session->priv->account_store;
}

void
e_mail_ui_session_add_activity (EMailUISession *session,
                                EActivity *activity)
{
    g_return_if_fail (E_IS_MAIL_UI_SESSION (session));
    g_return_if_fail (E_IS_ACTIVITY (activity));

    g_signal_emit (session, signals[ACTIVITY_ADDED], 0, activity);
}

EMailLabelListStore *
e_mail_ui_session_get_label_store (EMailUISession *session)
{
    g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session), NULL);

    return session->priv->label_store;
}