[evolution-ews] Bug #712304 - Crash in the notification code



commit 613053aed29db4ed8d6ccb73545407fa14954aa2
Author: Fabiano Fidêncio <fidencio redhat com>
Date:   Tue Nov 19 00:40:15 2013 +0100

    Bug #712304 - Crash in the notification code

 src/addressbook/e-book-backend-ews.c |   60 ++--
 src/calendar/e-cal-backend-ews.c     |   68 +++---
 src/camel/camel-ews-store.c          |  161 ++++++------
 src/server/e-ews-connection.c        |  497 +++++++--------------------------
 src/server/e-ews-connection.h        |   53 +----
 src/server/e-ews-notification.c      |  376 +++++++++++++++++++++----
 src/server/e-ews-notification.h      |    8 +-
 7 files changed, 575 insertions(+), 648 deletions(-)
---
diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c
index 451b391..402a2ec 100644
--- a/src/addressbook/e-book-backend-ews.c
+++ b/src/addressbook/e-book-backend-ews.c
@@ -91,8 +91,8 @@ struct _EBookBackendEwsPrivate {
 
        GCancellable *cancellable;
 
+       guint subscription_key;
        gboolean listen_notifications;
-       gchar *subscription_id;
 };
 
 /* using this for backward compatibility with E_DATA_BOOK_MODE */
@@ -3557,39 +3557,35 @@ handle_notifications_thread (gpointer data)
        EBookBackendEws *ebews = data;
 
        PRIV_LOCK (ebews->priv);
-
        if (ebews->priv->cnc == NULL)
                goto exit;
 
        if (ebews->priv->listen_notifications) {
                GSList *folders = NULL;
 
-               if (ebews->priv->subscription_id != NULL)
+               if (ebews->priv->subscription_key != 0)
                        goto exit;
 
                folders = g_slist_prepend (folders, ebews->priv->folder_id);
+
                e_ews_connection_enable_notifications_sync (
                                ebews->priv->cnc,
                                folders,
-                               NULL,
-                               &ebews->priv->subscription_id,
-                               NULL,
-                               NULL);
+                               &ebews->priv->subscription_key);
+
                g_slist_free (folders);
        } else {
-               if (ebews->priv->subscription_id == NULL)
+               if (ebews->priv->subscription_key == 0)
                        goto exit;
 
                e_ews_connection_disable_notifications_sync (
                                ebews->priv->cnc,
-                               ebews->priv->subscription_id,
-                               NULL,
-                               NULL);
-               g_free (ebews->priv->subscription_id);
-               ebews->priv->subscription_id = NULL;
+                               ebews->priv->subscription_key);
+
+               ebews->priv->subscription_key = 0;
        }
 
- exit:
+exit:
        PRIV_UNLOCK (ebews->priv);
        g_object_unref (ebews);
        return NULL;
@@ -3829,26 +3825,28 @@ e_book_backend_ews_open (EBookBackend *backend,
        }
 
        if (error == NULL) {
+               PRIV_LOCK (priv);
                priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
 
                if (priv->listen_notifications)
                        ebews_listen_notifications_cb (ebews, NULL, ews_settings);
-       }
 
-       convert_error_to_edb_error (&error);
-       e_data_book_respond_open (book, opid, error);
-
-       g_signal_connect_swapped (
+               g_signal_connect_swapped (
                        priv->cnc,
                        "server-notification",
                        G_CALLBACK (ebews_server_notification_cb),
                        ebews);
+               PRIV_UNLOCK (priv);
+       }
+
+       convert_error_to_edb_error (&error);
+       e_data_book_respond_open (book, opid, error);
 
        g_signal_connect_swapped (
-                       ews_settings,
-                       "notify::listen-notifications",
-                       G_CALLBACK (ebews_listen_notifications_cb),
-                       ebews);
+               ews_settings,
+               "notify::listen-notifications",
+               G_CALLBACK (ebews_listen_notifications_cb),
+               ebews);
 }
 
 /**
@@ -3956,20 +3954,20 @@ e_book_backend_ews_dispose (GObject *object)
        if (priv->cnc) {
                g_signal_handlers_disconnect_by_func (priv->cnc, ebews_server_notification_cb, bews);
 
-               if (priv->listen_notifications && priv->subscription_id != NULL) {
-                       e_ews_connection_disable_notifications_sync (
+               if (priv->listen_notifications) {
+                       if (priv->subscription_key != 0) {
+                               e_ews_connection_disable_notifications_sync (
                                        priv->cnc,
-                                       priv->subscription_id,
-                                       NULL,
-                                       NULL);
+                                       priv->subscription_key);
+                               priv->subscription_key = 0;
+                       }
+
+                       priv->listen_notifications = FALSE;
                }
 
                g_clear_object (&priv->cnc);
        }
 
-       g_free (priv->subscription_id);
-       priv->subscription_id = NULL;
-
        g_free (priv->folder_id);
        priv->folder_id = NULL;
 
diff --git a/src/calendar/e-cal-backend-ews.c b/src/calendar/e-cal-backend-ews.c
index 39b53eb..32aab8d 100644
--- a/src/calendar/e-cal-backend-ews.c
+++ b/src/calendar/e-cal-backend-ews.c
@@ -84,8 +84,8 @@ struct _ECalBackendEwsPrivate {
 
        GCancellable *cancellable;
 
+       guint subscription_key;
        gboolean listen_notifications;
-       gchar *subscription_id;
 };
 
 #define PRIV_LOCK(p)   (g_rec_mutex_lock (&(p)->rec_mutex))
@@ -617,32 +617,29 @@ handle_notifications_thread (gpointer data)
        if (cbews->priv->listen_notifications) {
                GSList *folders = NULL;
 
-               if (cbews->priv->subscription_id != NULL)
+               if (cbews->priv->subscription_key != 0)
                        goto exit;
 
                folders = g_slist_prepend (folders, cbews->priv->folder_id);
+
                e_ews_connection_enable_notifications_sync (
                                cbews->priv->cnc,
                                folders,
-                               NULL,
-                               &cbews->priv->subscription_id,
-                               NULL,
-                               NULL);
+                               &cbews->priv->subscription_key);
+
                g_slist_free (folders);
        } else {
-               if (cbews->priv->subscription_id == NULL)
+               if (cbews->priv->subscription_key == 0)
                        goto exit;
 
                e_ews_connection_disable_notifications_sync (
                                cbews->priv->cnc,
-                               cbews->priv->subscription_id,
-                               NULL,
-                               NULL);
-               g_free (cbews->priv->subscription_id);
-               cbews->priv->subscription_id = NULL;
+                               cbews->priv->subscription_key);
+
+               cbews->priv->subscription_key = 0;
        }
 
- exit:
+exit:
        PRIV_UNLOCK (cbews->priv);
        g_object_unref (cbews);
        return NULL;
@@ -776,26 +773,31 @@ e_cal_backend_ews_open (ECalBackend *backend,
 
        if (ret) {
                e_cal_backend_set_writable (backend, TRUE);
-               priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
 
-               if (priv->listen_notifications)
-                       cbews_listen_notifications_cb (cbews, NULL, ews_settings);
+               PRIV_LOCK (priv);
+               if (priv->cnc != NULL) {
+                       priv->listen_notifications = camel_ews_settings_get_listen_notifications 
(ews_settings);
+
+                       if (priv->listen_notifications)
+                               cbews_listen_notifications_cb (cbews, NULL, ews_settings);
+
+                       g_signal_connect_swapped (
+                               priv->cnc,
+                               "server-notification",
+                               G_CALLBACK (cbews_server_notification_cb),
+                               cbews);
+               }
+               PRIV_UNLOCK (priv);
        }
 
        convert_error_to_edc_error (&error);
        e_data_cal_respond_open (cal, opid, error);
 
        g_signal_connect_swapped (
-                       priv->cnc,
-                       "server-notification",
-                       G_CALLBACK (cbews_server_notification_cb),
-                       cbews);
-
-       g_signal_connect_swapped (
-                       ews_settings,
-                       "notify::listen-notifications",
-                       G_CALLBACK (cbews_listen_notifications_cb),
-                       cbews);
+               ews_settings,
+               "notify::listen-notifications",
+               G_CALLBACK (cbews_listen_notifications_cb),
+               cbews);
 }
 
 static void
@@ -4604,12 +4606,13 @@ e_cal_backend_ews_dispose (GObject *object)
        if (priv->cnc) {
                g_signal_handlers_disconnect_by_func (priv->cnc, cbews_server_notification_cb, object);
 
-               if (priv->listen_notifications && priv->subscription_id != NULL) {
-                       e_ews_connection_disable_notifications_sync (
+               if (priv->listen_notifications) {
+                       if (priv->subscription_key != 0) {
+                               e_ews_connection_disable_notifications_sync (
                                        priv->cnc,
-                                       priv->subscription_id,
-                                       NULL,
-                                       NULL);
+                                       priv->subscription_key);
+                               priv->subscription_key = 0;
+                       }
 
                        priv->listen_notifications = FALSE;
                }
@@ -4618,9 +4621,6 @@ e_cal_backend_ews_dispose (GObject *object)
                priv->cnc = NULL;
        }
 
-       g_free (priv->subscription_id);
-       priv->subscription_id = NULL;
-
        G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->dispose (object);
 }
 
diff --git a/src/camel/camel-ews-store.c b/src/camel/camel-ews-store.c
index 9a4a384..a4ff009 100644
--- a/src/camel/camel-ews-store.c
+++ b/src/camel/camel-ews-store.c
@@ -77,9 +77,9 @@ struct _CamelEwsStorePrivate {
        CamelEwsStoreOooAlertState ooo_alert_state;
 
        gboolean listen_notifications;
+       guint subscription_key;
        guint update_folder_id;
        guint update_folder_list_id;
-       gchar *subscription_id;
        GCancellable *updates_cancellable;
        GSList *update_folder_names;
        GRecMutex update_lock;
@@ -94,6 +94,12 @@ static void camel_ews_store_initable_init (GInitableIface *interface);
 static void camel_ews_subscribable_init (CamelSubscribableInterface *interface);
 static GInitableIface *parent_initable_interface;
 
+static CamelFolderInfo *folder_info_from_store_summary (CamelEwsStore *store,
+                                                       const gchar *top,
+                                                       guint32 flags,
+                                                       GCancellable *cancellable,
+                                                       GError **error);
+
 enum {
        PROP_0,
        PROP_HAS_OOO_SET,
@@ -1001,70 +1007,51 @@ start_notifications_thread (gpointer data)
                goto exit;
 
        if (ews_store->priv->listen_notifications) {
+               if (ews_store->priv->subscription_key != 0)
+                       goto exit;
+
                e_ews_connection_enable_notifications_sync (
-                               ews_store->priv->connection,
-                               hnd->folders,
-                               NULL,
-                               &ews_store->priv->subscription_id,
-                               NULL,
-                               NULL);
+                       ews_store->priv->connection,
+                       hnd->folders,
+                       &ews_store->priv->subscription_key);
        } else {
-               if (ews_store->priv->subscription_id == NULL)
+               if (ews_store->priv->subscription_key == 0)
                        goto exit;
 
                e_ews_connection_disable_notifications_sync (
-                               ews_store->priv->connection,
-                               ews_store->priv->subscription_id,
-                               NULL,
-                               NULL);
-               g_free (ews_store->priv->subscription_id);
-               ews_store->priv->subscription_id = NULL;
+                       ews_store->priv->connection,
+                       ews_store->priv->subscription_key);
+
+               ews_store->priv->subscription_key = 0;
        }
 
- exit:
+exit:
        handle_notifications_data_free (hnd);
        return NULL;
 }
 
-static gpointer
-restart_notifications_thread (gpointer data)
+static void
+folder_ids_populate (CamelFolderInfo *folder_info,
+                    gpointer data)
 {
        struct HandleNotificationsData *hnd = data;
-       CamelEwsStore *ews_store = hnd->ews_store;
-
-       if (ews_store->priv->connection == NULL)
-               goto exit;
 
-       if (!ews_store->priv->listen_notifications)
-               goto exit;
+       while (folder_info != NULL) {
+               gchar *id;
 
-       if (ews_store->priv->subscription_id != NULL) {
-               e_ews_connection_disable_notifications_sync (
-                               ews_store->priv->connection,
-                               ews_store->priv->subscription_id,
-                               NULL,
-                               NULL);
-               g_free (ews_store->priv->subscription_id);
-               ews_store->priv->subscription_id = NULL;
-       }
+               id = camel_ews_store_summary_get_folder_id_from_name (hnd->ews_store->summary, 
folder_info->full_name);
+               hnd->folders = g_slist_prepend (hnd->folders, id);
 
-       e_ews_connection_enable_notifications_sync (
-                       ews_store->priv->connection,
-                       hnd->folders,
-                       NULL,
-                       &ews_store->priv->subscription_id,
-                       NULL,
-                       NULL);
+               if (folder_info->child != NULL)
+                       folder_ids_populate (folder_info->child, hnd);
 
- exit:
-       handle_notifications_data_free (hnd);
-       return NULL;
+               folder_info = folder_info->next;
+       }
 }
 
 static void
-handle_notifications (CamelEwsStore *ews_store,
-                     CamelEwsSettings *ews_settings,
-                     gboolean restart)
+camel_ews_store_handle_notifications (CamelEwsStore *ews_store,
+                                     CamelEwsSettings *ews_settings)
 {
        GThread *thread;
        struct HandleNotificationsData *hnd;
@@ -1084,10 +1071,22 @@ handle_notifications (CamelEwsStore *ews_store,
                inbox = camel_ews_store_summary_get_folder_id_from_folder_type (
                        ews_store->summary, CAMEL_FOLDER_TYPE_INBOX);
                hnd->folders = g_slist_prepend (hnd->folders, inbox);
+       } else {
+               CamelFolderInfo *fi;
+
+               fi = folder_info_from_store_summary (
+                       ews_store,
+                       NULL,
+                       CAMEL_STORE_FOLDER_INFO_RECURSIVE,
+                       NULL,
+                       NULL);
+
+               folder_ids_populate (fi, hnd);
+
+               camel_folder_info_free (fi);
        }
 
-       ews_store->priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
-       thread = g_thread_new (NULL, restart ? restart_notifications_thread : start_notifications_thread, 
hnd);
+       thread = g_thread_new (NULL, start_notifications_thread, hnd);
        g_thread_unref (thread);
 }
 
@@ -1096,7 +1095,12 @@ camel_ews_store_listen_notifications_cb (CamelEwsStore *ews_store,
                                         GParamSpec *spec,
                                         CamelEwsSettings *ews_settings)
 {
-       handle_notifications (ews_store, ews_settings, FALSE);
+       if (ews_store->priv->listen_notifications == camel_ews_settings_get_listen_notifications 
(ews_settings))
+               return;
+
+       ews_store->priv->listen_notifications = !ews_store->priv->listen_notifications;
+
+       camel_ews_store_handle_notifications (ews_store, ews_settings);
 }
 
 static void
@@ -1104,7 +1108,10 @@ camel_ews_store_check_all_cb (CamelEwsStore *ews_store,
                              GParamSpec *spec,
                              CamelEwsSettings *ews_settings)
 {
-       handle_notifications (ews_store, ews_settings, TRUE);
+       if (!ews_store->priv->listen_notifications)
+               return;
+
+       camel_ews_store_handle_notifications (ews_store, ews_settings);
 }
 
 static gboolean
@@ -1146,6 +1153,8 @@ ews_connect_sync (CamelService *service,
 
        g_free (auth_mech);
 
+       priv->listen_notifications = FALSE;
+
        if (success) {
                CamelEwsStoreOooAlertState state;
 
@@ -1160,8 +1169,7 @@ ews_connect_sync (CamelService *service,
                if (!priv->updates_cancellable)
                        priv->updates_cancellable = g_cancellable_new ();
 
-               priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
-               if (priv->listen_notifications)
+               if (camel_ews_settings_get_listen_notifications (ews_settings))
                        camel_ews_store_listen_notifications_cb (ews_store, NULL, ews_settings);
 
                camel_offline_store_set_online_sync (
@@ -1221,7 +1229,7 @@ ews_disconnect_sync (CamelService *service,
                      GCancellable *cancellable,
                      GError **error)
 {
-       CamelEwsStore *ews_store = (CamelEwsStore *) service;
+       CamelEwsStore *ews_store = CAMEL_EWS_STORE (service);
        CamelServiceClass *service_class;
 
        g_mutex_lock (&ews_store->priv->connection_lock);
@@ -1245,16 +1253,16 @@ ews_disconnect_sync (CamelService *service,
 
                if (ews_store->priv->listen_notifications) {
                        stop_pending_updates (ews_store);
-                       if (ews_store->priv->subscription_id != NULL) {
+
+                       if (ews_store->priv->subscription_key != 0) {
                                e_ews_connection_disable_notifications_sync (
-                                               ews_store->priv->connection,
-                                               ews_store->priv->subscription_id,
-                                               cancellable,
-                                               error);
+                                       ews_store->priv->connection,
+                                       ews_store->priv->subscription_key);
 
-                               g_free (ews_store->priv->subscription_id);
-                               ews_store->priv->subscription_id = NULL;
+                               ews_store->priv->subscription_key = 0;
                        }
+
+                       ews_store->priv->listen_notifications = FALSE;
                }
 
                e_ews_connection_set_password (
@@ -3464,32 +3472,28 @@ ews_store_dispose (GObject *object)
                g_signal_handlers_disconnect_by_func (
                        ews_store->priv->connection, camel_ews_store_server_notification_cb, ews_store);
 
-               if (ews_store->priv->listen_notifications && ews_store->priv->subscription_id != NULL) {
+               if (ews_store->priv->listen_notifications) {
                        stop_pending_updates (ews_store);
-                       e_ews_connection_disable_notifications_sync (
-                               ews_store->priv->connection,
-                               ews_store->priv->subscription_id,
-                               NULL,
-                               NULL);
+
+                       if (ews_store->priv->subscription_key != 0) {
+                               e_ews_connection_disable_notifications_sync (
+                                       ews_store->priv->connection,
+                                       ews_store->priv->subscription_key);
+
+                               ews_store->priv->subscription_key = 0;
+                       }
+
+                       ews_store->priv->listen_notifications = FALSE;
                }
 
                g_clear_object (&ews_store->priv->connection);
        }
 
-       if (ews_store->priv->subscription_id) {
-               g_free (ews_store->priv->subscription_id);
-               ews_store->priv->subscription_id = NULL;
-       }
-
-       if (ews_store->priv->update_folder_names) {
-               g_slist_free_full (ews_store->priv->update_folder_names, g_free);
-               ews_store->priv->update_folder_names = NULL;
-       }
+       g_slist_free_full (ews_store->priv->update_folder_names, g_free);
+       ews_store->priv->update_folder_names = NULL;
 
-       if (ews_store->priv->public_folders) {
-               g_slist_free_full (ews_store->priv->public_folders, g_object_unref);
-               ews_store->priv->public_folders = NULL;
-       }
+       g_slist_free_full (ews_store->priv->public_folders, g_object_unref);
+       ews_store->priv->public_folders = NULL;
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (camel_ews_store_parent_class)->dispose (object);
@@ -3588,6 +3592,7 @@ camel_ews_store_init (CamelEwsStore *ews_store)
        ews_store->priv->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10);
        ews_store->priv->updates_cancellable = NULL;
        ews_store->priv->update_folder_names = NULL;
+       ews_store->priv->subscription_key = 0;
        ews_store->priv->update_folder_id = 0;
        ews_store->priv->update_folder_list_id = 0;
        g_mutex_init (&ews_store->priv->get_finfo_lock);
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index 4020279..d549714 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -59,6 +59,9 @@
 #define QUEUE_LOCK(x) (g_rec_mutex_lock(&(x)->priv->queue_lock))
 #define QUEUE_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->queue_lock))
 
+#define NOTIFICATION_LOCK(x) (g_mutex_lock(&(x)->priv->notification_lock))
+#define NOTIFICATION_UNLOCK(x) (g_mutex_unlock(&(x)->priv->notification_lock))
+
 struct _EwsNode;
 static GMutex connecting;
 static GHashTable *loaded_connections_permissions = NULL;
@@ -96,6 +99,10 @@ struct _EEwsConnectionPrivate {
        GSList *jobs;
        GSList *active_job_queue;
        GRecMutex queue_lock;
+       GMutex notification_lock;
+
+       GHashTable *subscriptions;
+       GSList *subscribed_folders;
 
        EEwsServerVersion version;
 };
@@ -114,19 +121,11 @@ enum {
 
 static guint signals[LAST_SIGNAL];
 
-static const gchar *default_events_names[] = {
-       "CopiedEvent",
-       "CreatedEvent",
-       "DeletedEvent",
-       "ModifiedEvent",
-       "MovedEvent",
-       "StatusEvent",
-       NULL};
+static guint notification_key = 1;
 
 typedef struct _EwsNode EwsNode;
 typedef struct _EwsAsyncData EwsAsyncData;
 typedef struct _EwsEventsAsyncData EwsEventsAsyncData;
-typedef struct _EwsNotificationThreadData EwsNotificationThreadData;
 
 struct _EwsAsyncData {
        GSList *items_created;
@@ -144,10 +143,6 @@ struct _EwsAsyncData {
        EEwsConnection *cnc;
 };
 
-struct _EwsEventsAsyncData {
-       gchar *subscription_id;
-};
-
 struct _EwsNode {
        ESoapMessage *msg;
        EEwsConnection *cnc;
@@ -160,11 +155,6 @@ struct _EwsNode {
        gulong cancel_handler_id;
 };
 
-struct _EwsNotificationThreadData {
-       EEwsConnection *cnc;
-       gchar *subscription_id;
-};
-
 /* Forward Declarations */
 static void    e_ews_connection_authenticator_init
                                (ESourceAuthenticatorInterface *interface);
@@ -198,12 +188,6 @@ async_data_free (EwsAsyncData *async_data)
        g_free (async_data);
 }
 
-static void
-events_async_data_free (EwsEventsAsyncData *async_data)
-{
-       g_free (async_data);
-}
-
 EEwsNotificationEvent *
 e_ews_notification_event_new (void)
 {
@@ -1393,6 +1377,14 @@ ews_connection_dispose (GObject *object)
        g_slist_free (priv->active_job_queue);
        priv->active_job_queue = NULL;
 
+       g_slist_free_full (priv->subscribed_folders, g_free);
+       priv->subscribed_folders = NULL;
+
+       if (priv->subscriptions != NULL) {
+               g_hash_table_destroy (priv->subscriptions);
+               priv->subscriptions = NULL;
+       }
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_ews_connection_parent_class)->dispose (object);
 }
@@ -1412,6 +1404,7 @@ ews_connection_finalize (GObject *object)
 
        g_mutex_clear (&priv->property_lock);
        g_rec_mutex_clear (&priv->queue_lock);
+       g_mutex_clear (&priv->notification_lock);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_ews_connection_parent_class)->finalize (object);
@@ -1525,6 +1518,12 @@ e_ews_connection_class_init (EEwsConnectionClass *class)
 }
 
 static void
+e_ews_connection_folders_list_free (gpointer data)
+{
+       g_slist_free_full ((GSList *) data, g_free);
+}
+
+static void
 e_ews_connection_authenticator_init (ESourceAuthenticatorInterface *interface)
 {
        interface->try_password_sync = ews_connection_try_password_sync;
@@ -1641,8 +1640,13 @@ e_ews_connection_init (EEwsConnection *cnc)
                g_object_unref (logger);
        }
 
+       cnc->priv->subscriptions = g_hash_table_new_full (
+                       g_direct_hash, g_direct_equal,
+                       NULL, e_ews_connection_folders_list_free);
+
        g_mutex_init (&cnc->priv->property_lock);
        g_rec_mutex_init (&cnc->priv->queue_lock);
+       g_mutex_init (&cnc->priv->notification_lock);
 
        g_signal_connect (
                cnc->priv->soup_session, "authenticate",
@@ -9122,412 +9126,123 @@ e_ews_connection_query_auth_methods_sync (EEwsConnection *cnc,
 }
 
 static void
-subscribe_folder_response_cb (ESoapResponse *response,
-                             GSimpleAsyncResult *simple)
+ews_connection_build_subscribed_folders_list (gpointer key,
+                                             gpointer value,
+                                             gpointer user_data)
 {
-       EwsEventsAsyncData *async_data;
-       ESoapParameter *param;
-       ESoapParameter *subparam;
-       GError *error = NULL;
-
-       async_data = g_simple_async_result_get_op_res_gpointer (simple);
-
-       param = e_soap_response_get_first_parameter_by_name (
-               response, "ResponseMessages", &error);
-
-       /* Sanity check */
-       g_return_if_fail (
-               (param != NULL && error == NULL) ||
-               (param == NULL && error != NULL));
-
-       if (error != NULL) {
-               g_simple_async_result_take_error (simple, error);
-               return;
-       }
-
-       subparam = e_soap_parameter_get_first_child (param);
-
-       while (subparam != NULL) {
-               const gchar *name = (const gchar *) subparam->name;
-
-               if (!ews_get_response_status (subparam, &error)) {
-                       g_simple_async_result_take_error (simple, error);
-                       return;
-               }
-
-               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SubscribeResponseMessage")) {
-                       ESoapParameter *node;
+       GSList *folders = value, *l;
+       EEwsConnection *cnc = user_data;
 
-                       node = e_soap_parameter_get_first_child_by_name (subparam, "SubscriptionId");
-                       async_data->subscription_id = e_soap_parameter_get_string_value (node);
+       for (l = folders; l != NULL; l = l->next) {
+               if (g_slist_find_custom (cnc->priv->subscribed_folders, l->data, (GCompareFunc) g_strcmp0) == 
NULL) {
+                       cnc->priv->subscribed_folders =
+                               g_slist_prepend (cnc->priv->subscribed_folders, g_strdup (l->data));
                }
-
-               subparam = e_soap_parameter_get_next_child (subparam);
        }
 }
 
+/*
+ * Enables server notification on a folder (or a set of folders).
+ * The events we are listen for notifications are: Copied, Created, Deleted, Modified and Moved.
+ *
+ * As we have only one subscription per connection, for every enable_notifications_sync() call,
+ * we do:
+ * - Check if we already are subscribed
+ * - If we are already subscribed:
+ *  - Stop to send events regards to the already subscribed folders' list
+ *  - Unsubscribe the already subscribed folders' list
+ * - Add the user folders' lst to the hash table if subscribed folders
+ * - Create a new list of the folders to subscribe for, based in the hash table of subscribed folders
+ * - Subscribed to listen notifications for the folders
+ * - Start to send events regards to the newly subscribed folders' list
+ *
+ * Pair function for this one is e_ews_connection_disable_notifications_sync() and we do
+ * something really similar for every disable_notifications_sync() call.
+ *
+ * The notification is received to the caller with the "server-notification" signal.
+ * Note that the signal is used for each notification, without distinction on the
+ * enabled object.
+ */
 void
-e_ews_connection_subscribe_folder (EEwsConnection *cnc,
-                                  gint pri,
-                                  GSList *folders,
-                                  GSList *events,
-                                  GCancellable *cancellable,
-                                  GAsyncReadyCallback callback,
-                                  gpointer user_data)
+e_ews_connection_enable_notifications_sync (EEwsConnection *cnc,
+                                           GSList *folders,
+                                           guint *subscription_key)
 {
-       ESoapMessage *msg;
-       GSimpleAsyncResult *simple;
-       EwsEventsAsyncData *async_data;
-       GSList *l;
+       GSList *new_folders = NULL, *l;
+       gint subscriptions_size;
 
        g_return_if_fail (cnc != NULL);
        g_return_if_fail (cnc->priv != NULL);
+       g_return_if_fail (cnc->priv->version >= E_EWS_EXCHANGE_2010_SP1);
+       g_return_if_fail (folders != NULL);
 
-       simple = g_simple_async_result_new (
-               G_OBJECT (cnc), callback, user_data, e_ews_connection_subscribe_folder);
-       async_data = g_new0 (EwsEventsAsyncData, 1);
-       g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) 
events_async_data_free);
+       NOTIFICATION_LOCK (cnc);
+       subscriptions_size = g_hash_table_size (cnc->priv->subscriptions);
 
-       /*
-        * EWS server version earlier than 2010_SP1 doesn't have support to "StreamingSubcription".
-        * So, if the API is called with an older version of the server to subscribe, let's just fail 
silently.
-        */
-       if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2010_SP1)) {
-               g_simple_async_result_complete_in_idle (simple);
-               g_object_unref (simple);
-               return;
-       }
+       if (subscriptions_size == G_MAXUINT - 1)
+               goto exit;
 
-       msg = e_ews_message_new_with_header (
-               cnc->priv->uri,
-               cnc->priv->impersonate_user,
-               "Subscribe",
-               NULL,
-               NULL,
-               cnc->priv->version,
-               E_EWS_EXCHANGE_2010_SP1,
-               FALSE,
-               TRUE);
+       if (subscriptions_size > 0) {
+               e_ews_notification_stop_listening_sync (cnc->priv->notification);
 
-       e_soap_message_start_element (msg, "StreamingSubscriptionRequest", "messages", NULL);
+               g_clear_object (&cnc->priv->notification);
 
-       if (folders == NULL) {
-               e_soap_message_add_attribute (msg, "SubscribeToAllFolders", "true", NULL, NULL);
-       } else {
-               e_soap_message_start_element (msg, "FolderIds", NULL, NULL);
-               for (l = folders; l; l = l->next) {
-                       e_ews_message_write_string_parameter_with_attribute (
-                               msg,
-                               "FolderId",
-                               NULL,
-                               NULL,
-                               "Id",
-                               l->data);
-               }
-               e_soap_message_end_element (msg); /* FolderIds */
+               g_slist_free_full (cnc->priv->subscribed_folders, g_free);
+               cnc->priv->subscribed_folders = NULL;
        }
 
-       e_soap_message_start_element (msg, "EventTypes", NULL, NULL);
-       for (l = events; l; l = l->next)
-               e_ews_message_write_string_parameter_with_attribute (msg, "EventType", NULL, l->data, NULL, 
NULL);
-       e_soap_message_end_element (msg); /* EventTypes */
-
-       e_soap_message_end_element (msg); /* StreamingSubscriptionRequest */
-       e_ews_message_write_footer (msg); /* Complete the footer and print the request */
-
-       e_ews_connection_queue_request (cnc, msg, subscribe_folder_response_cb, pri, cancellable, simple);
-
-       g_object_unref (simple);
-}
-
-gboolean
-e_ews_connection_subscribe_folder_finish (EEwsConnection *cnc,
-                                         GAsyncResult *result,
-                                         gchar **subscription_id,
-                                         GError **error)
-{
-       GSimpleAsyncResult *simple;
-       EwsEventsAsyncData *async_data;
-
-       g_return_val_if_fail (cnc != NULL, FALSE);
-       g_return_val_if_fail (
-               g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_subscribe_folder), 
FALSE);
-
-       simple = G_SIMPLE_ASYNC_RESULT (result);
-       async_data = g_simple_async_result_get_op_res_gpointer (simple);
-
-       if (g_simple_async_result_propagate_error (simple, error))
-               return FALSE;
-
-       if (async_data->subscription_id == NULL)
-               return FALSE;
-
-       if (subscription_id)
-               *subscription_id = async_data->subscription_id;
-       else
-               g_free (async_data->subscription_id);
-
-       return TRUE;
-}
-
-gboolean
-e_ews_connection_subscribe_folder_sync (EEwsConnection *cnc,
-                                       gint pri,
-                                       GSList *folders,
-                                       GSList *events,
-                                       gchar **subscription_id,
-                                       GCancellable *cancellable,
-                                       GError **error)
-{
-       EAsyncClosure *closure;
-       GAsyncResult *result;
-       gboolean success;
-
-       g_return_val_if_fail (cnc != NULL, FALSE);
-       g_return_val_if_fail (*subscription_id == NULL, FALSE);
-
-       closure = e_async_closure_new ();
-
-       e_ews_connection_subscribe_folder (
-               cnc, pri, folders, events, cancellable, e_async_closure_callback, closure);
-
-       result = e_async_closure_wait (closure);
-
-       success = e_ews_connection_subscribe_folder_finish (cnc, result, subscription_id, error);
-
-       e_async_closure_free (closure);
-
-       return success;
-}
-
-static void
-unsubscribe_folder_response_cb (ESoapResponse *response,
-                               GSimpleAsyncResult *simple)
-{
-       ESoapParameter *param;
-       GError *error = NULL;
-
-       param = e_soap_response_get_first_parameter_by_name (
-               response, "ResponseMessages", &error);
-
-       /* Sanity check */
-       g_return_if_fail (
-               (param != NULL && error == NULL) ||
-               (param == NULL && error != NULL));
-
-       if (error != NULL)
-               g_simple_async_result_take_error (simple, error);
-}
-
-void
-e_ews_connection_unsubscribe_folder (EEwsConnection *cnc,
-                                    gint pri,
-                                    const gchar *subscription_id,
-                                    GCancellable *cancellable,
-                                    GAsyncReadyCallback callback,
-                                    gpointer user_data)
-{
-       ESoapMessage *msg;
-       GSimpleAsyncResult *simple;
-
-       g_return_if_fail (cnc != NULL);
-       g_return_if_fail (cnc->priv != NULL);
-       g_return_if_fail (subscription_id != NULL);
-
-       msg = e_ews_message_new_with_header (
-               cnc->priv->uri,
-               cnc->priv->impersonate_user,
-               "Unsubscribe",
-               NULL,
-               NULL,
-               cnc->priv->version,
-               E_EWS_EXCHANGE_2010_SP1,
-               FALSE,
-               TRUE);
-
-       e_ews_message_write_string_parameter_with_attribute (
-               msg, "SubscriptionId", "messages", subscription_id, NULL, NULL);
-
-       e_ews_message_write_footer (msg); /* Complete the footer and print the request */
-
-       simple = g_simple_async_result_new (
-               G_OBJECT (cnc), callback, user_data, e_ews_connection_unsubscribe_folder);
-
-       e_ews_connection_queue_request (cnc, msg, unsubscribe_folder_response_cb, pri, cancellable, simple);
-
-       g_object_unref (simple);
-}
-
-gboolean
-e_ews_connection_unsubscribe_folder_finish (EEwsConnection *cnc,
-                                           GAsyncResult *result,
-                                           GError **error)
-{
-       GSimpleAsyncResult *simple;
-
-       g_return_val_if_fail (cnc != NULL, FALSE);
-       g_return_val_if_fail (
-               g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_unsubscribe_folder), 
FALSE);
-
-       simple = G_SIMPLE_ASYNC_RESULT (result);
-
-       if (g_simple_async_result_propagate_error (simple, error))
-               return FALSE;
-
-       return TRUE;
-}
-
-gboolean
-e_ews_connection_unsubscribe_folder_sync (EEwsConnection *cnc,
-                                         gint pri,
-                                         const gchar *subscription_id,
-                                         GCancellable *cancellable,
-                                         GError **error)
-{
-       EAsyncClosure *closure;
-       GAsyncResult *result;
-       gboolean success;
-
-       g_return_val_if_fail (cnc != NULL, FALSE);
-
-       closure = e_async_closure_new ();
-
-       e_ews_connection_unsubscribe_folder (
-               cnc, pri, subscription_id, cancellable, e_async_closure_callback, closure);
-
-       result = e_async_closure_wait (closure);
-
-       success = e_ews_connection_unsubscribe_folder_finish (cnc, result, error);
-
-       e_async_closure_free (closure);
-
-       return success;
-}
-
-static gpointer
-e_ews_connection_notification_thread (gpointer user_data)
-{
-       EwsNotificationThreadData *ent = user_data;
-       EEwsConnection *cnc = ent->cnc;
-       guint ret;
-
-       g_return_val_if_fail (cnc != NULL, NULL);
-       g_return_val_if_fail (cnc->priv != NULL, NULL);
-
-       do {
-               ret = e_ews_notification_get_events_sync (
-                       cnc->priv->notification,
-                       EWS_PRIORITY_MEDIUM,
-                       ent->subscription_id);
-       } while (SOUP_STATUS_IS_SUCCESSFUL (ret));
-
-       g_free (ent->subscription_id);
-       g_free (ent);
-
-       return NULL;
-}
-
-/* Enables server notification on a folder, or on a whole store, if folders is NULL.
- * The events can be NULL to obtain the default notifications (Copied, Created,
- * Deleted and Moved).
- * Pair function for this is e_ews_connection_disable_notifications_sync().
- * The notification is received to the caller with the "server-notification" signal.
- * Note that the signal is used for each notification, without distinction on the
- * enabled object.
- */
-gboolean
-e_ews_connection_enable_notifications_sync (EEwsConnection *cnc,
-                                           GSList *folders,
-                                           GSList *events,
-                                           gchar **subscription_id,
-                                           GCancellable *cancellable,
-                                           GError **error)
-{
-       GSList *default_events = NULL;
-       GThread *thread;
-       gboolean ret = TRUE;
-       EwsNotificationThreadData *ent;
-
-       g_return_val_if_fail (cnc != NULL, FALSE);
-       g_return_val_if_fail (*subscription_id == NULL, FALSE);
-       g_return_val_if_fail (subscription_id != NULL, FALSE);
-       g_return_val_if_fail (cnc->priv->version >= E_EWS_EXCHANGE_2010_SP1, FALSE);
-
-       if (events == NULL) {
-               guint event_type;
-               for (event_type = 0; default_events_names[event_type] != NULL; event_type++) {
-                       if (g_strcmp0 (default_events_names[event_type], "StatusEvent") == 0)
-                               continue;
-                       default_events = g_slist_prepend (default_events, (gchar *) 
default_events_names[event_type]);
-               }
+       while (g_hash_table_contains (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key))) {
+               notification_key++;
+               if (notification_key == 0)
+                       notification_key++;
        }
 
-       ret = e_ews_connection_subscribe_folder_sync (
-               cnc,
-               EWS_PRIORITY_MEDIUM,
-               folders,
-               events != NULL ? events : default_events,
-               subscription_id,
-               cancellable,
-               error);
+       for (l = folders; l != NULL; l = l->next)
+               new_folders = g_slist_prepend (new_folders, g_strdup (l->data));
 
-       if (!ret)
-               goto exit;
+       g_hash_table_insert (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key), new_folders);
+       new_folders = NULL;
 
-       ent = g_new0 (EwsNotificationThreadData, 1);
-       ent->cnc = cnc;
-       ent->subscription_id = g_strdup (*subscription_id);
+       g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc);
 
        cnc->priv->notification = e_ews_notification_new (cnc);
 
-       thread = g_thread_new (NULL, e_ews_connection_notification_thread, ent);
-       g_thread_unref (thread);
+       e_ews_notification_start_listening_sync (cnc->priv->notification, cnc->priv->subscribed_folders);
 
 exit:
-       g_slist_free (default_events);
-       return ret;
+       *subscription_key = notification_key;
+       notification_key++;
+       if (notification_key == 0)
+               notification_key++;
+
+       NOTIFICATION_UNLOCK (cnc);
 }
 
-gboolean
+void
 e_ews_connection_disable_notifications_sync (EEwsConnection *cnc,
-                                            const gchar *subscription_id,
-                                            GCancellable *cancellable,
-                                            GError **error)
+                                            guint subscription_key)
 {
-       gboolean ret = TRUE;
-
-       g_return_val_if_fail (cnc != NULL, FALSE);
-       g_return_val_if_fail (cnc->priv != NULL, FALSE);
-       g_return_val_if_fail (subscription_id != NULL, FALSE);
+       g_return_if_fail (cnc != NULL);
+       g_return_if_fail (cnc->priv != NULL);
 
+       NOTIFICATION_LOCK (cnc);
        if (cnc->priv->notification == NULL)
                goto exit;
 
-       e_ews_notification_cancel_get_events (cnc->priv->notification);
-
-       ret = e_ews_connection_unsubscribe_folder_sync (
-               cnc,
-               EWS_PRIORITY_MEDIUM,
-               subscription_id,
-               cancellable,
-               error);
+       if (!g_hash_table_remove (cnc->priv->subscriptions, GINT_TO_POINTER (subscription_key)))
+               goto exit;
 
-       if (!ret) {
-               EwsNotificationThreadData *ent;
-               GThread *thread;
+       e_ews_notification_stop_listening_sync (cnc->priv->notification);
 
-               ent = g_new0 (EwsNotificationThreadData, 1);
-               ent->cnc = cnc;
-               ent->subscription_id = g_strdup (subscription_id);
+       g_clear_object (&cnc->priv->notification);
 
-               thread = g_thread_new (NULL, e_ews_connection_notification_thread, ent);
-               g_thread_unref (thread);
+       g_slist_free_full (cnc->priv->subscribed_folders, g_free);
+       cnc->priv->subscribed_folders = NULL;
 
-               goto exit;
-       }
-
-       g_clear_object (&cnc->priv->notification);
+       g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc);
+       if (cnc->priv->subscribed_folders != NULL)
+               e_ews_notification_start_listening_sync (cnc->priv->notification, 
cnc->priv->subscribed_folders);
 
 exit:
-       return ret;
+       NOTIFICATION_UNLOCK (cnc);
 }
diff --git a/src/server/e-ews-connection.h b/src/server/e-ews-connection.h
index 51dfefd..e0a3067 100644
--- a/src/server/e-ews-connection.h
+++ b/src/server/e-ews-connection.h
@@ -211,7 +211,7 @@ typedef enum {
        E_EWS_NOTIFICATION_EVENT_DELETED,
        E_EWS_NOTIFICATION_EVENT_MODIFIED,
        E_EWS_NOTIFICATION_EVENT_MOVED,
-       E_EWS_NOTIFICATION_EVENT_STATUS,
+       E_EWS_NOTIFICATION_EVENT_STATUS
 } EEwsNotificationEventType;
 
 typedef struct {
@@ -1097,56 +1097,13 @@ gboolean        e_ews_connection_query_auth_methods_sync
                                                 GSList **auth_methods,
                                                 GCancellable *cancellable,
                                                 GError **error);
-void           e_ews_connection_subscribe_folder
-                                               (EEwsConnection *cnc,
-                                                gint pri,
-                                                GSList *folders,
-                                                GSList *events,
-                                                GCancellable *cancellable,
-                                                GAsyncReadyCallback callback,
-                                                gpointer user_data);
-gboolean       e_ews_connection_subscribe_folder_finish
-                                               (EEwsConnection *cnc,
-                                                GAsyncResult *result,
-                                                gchar **subscription_id,
-                                                GError **error);
-gboolean       e_ews_connection_subscribe_folder_sync
-                                               (EEwsConnection *cnc,
-                                                gint pri,
-                                                GSList *folders,
-                                                GSList *events,
-                                                gchar **subscription_id,
-                                                GCancellable *cancellable,
-                                                GError **error);
-void           e_ews_connection_unsubscribe_folder
-                                               (EEwsConnection *cnc,
-                                                gint pri,
-                                                const gchar *subscription_id,
-                                                GCancellable *cancellable,
-                                                GAsyncReadyCallback callback,
-                                                gpointer user_data);
-gboolean       e_ews_connection_unsubscribe_folder_finish
-                                               (EEwsConnection *cnc,
-                                                GAsyncResult *result,
-                                                GError **error);
-gboolean       e_ews_connection_unsubscribe_folder_sync
-                                               (EEwsConnection *cnc,
-                                                gint pri,
-                                                const gchar *subscription_id,
-                                                GCancellable *cancellable,
-                                                GError **error);
-gboolean       e_ews_connection_enable_notifications_sync
+void           e_ews_connection_enable_notifications_sync
                                                (EEwsConnection *cnc,
                                                 GSList *folders,
-                                                GSList *events,
-                                                gchar **subscription_id,
-                                                GCancellable *cancellable,
-                                                GError **error);
-gboolean       e_ews_connection_disable_notifications_sync
+                                                guint *subscription_key);
+void           e_ews_connection_disable_notifications_sync
                                                (EEwsConnection *cnc,
-                                                const gchar *subscription_id,
-                                                GCancellable *cancellable,
-                                                GError **error);
+                                                guint subscription_key);
 
 G_END_DECLS
 
diff --git a/src/server/e-ews-notification.c b/src/server/e-ews-notification.c
index b424fb1..e79bad7 100644
--- a/src/server/e-ews-notification.c
+++ b/src/server/e-ews-notification.c
@@ -35,8 +35,8 @@ G_DEFINE_TYPE (EEwsNotification, e_ews_notification, G_TYPE_OBJECT)
 struct _EEwsNotificationPrivate {
        SoupSession *soup_session;
        EEwsConnection *connection; /* not referred */
-       ESoapMessage *msg;
        GByteArray *chunk;
+       GCancellable *cancellable;
 };
 
 enum {
@@ -51,7 +51,16 @@ static const gchar *default_events_names[] = {
        "ModifiedEvent",
        "MovedEvent",
        "StatusEvent",
-       NULL};
+       NULL
+};
+
+typedef struct _EEwsNotificationThreadData EEwsNotificationThreadData;
+
+struct _EEwsNotificationThreadData {
+       EEwsNotification *notification;
+       GCancellable *cancellable;
+       GSList *folders;
+};
 
 static void
 ews_notification_authenticate (SoupSession *session,
@@ -129,6 +138,15 @@ e_ews_notification_set_connection (EEwsNotification *notification,
                &notification->priv->connection);
 }
 
+static EEwsConnection *
+e_ews_notification_get_connection (const EEwsNotification *notification)
+{
+       g_return_val_if_fail (E_IS_EWS_NOTIFICATION (notification), NULL);
+       g_return_val_if_fail (notification->priv == NULL, NULL);
+
+       return notification->priv->connection;
+}
+
 static void
 ews_notification_set_property (GObject *object,
                               guint property_id,
@@ -146,15 +164,6 @@ ews_notification_set_property (GObject *object,
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
-static EEwsConnection *
-e_ews_notification_get_connection (const EEwsNotification *notification)
-{
-       g_return_val_if_fail (E_IS_EWS_NOTIFICATION (notification), NULL);
-       g_return_val_if_fail (notification->priv == NULL, NULL);
-
-       return notification->priv->connection;
-}
-
 static void
 ews_notification_get_property (GObject *object,
                               guint property_id,
@@ -180,19 +189,28 @@ ews_notification_dispose (GObject *object)
 
        priv = E_EWS_NOTIFICATION_GET_PRIVATE (object);
 
-       if (priv->soup_session) {
+       if (priv->cancellable != NULL)
+               g_cancellable_cancel (priv->cancellable);
+
+       if (priv->soup_session != NULL) {
                g_signal_handlers_disconnect_by_func (
                        priv->soup_session,
                        ews_notification_authenticate, object);
        }
 
-       if (priv->connection) {
+       if (priv->cancellable != NULL)
+               g_clear_object (&priv->cancellable);
+
+       if (priv->connection != NULL) {
                g_object_weak_unref (
                        G_OBJECT (priv->connection),
                        (GWeakNotify) g_nullify_pointer,
                        &priv->connection);
                priv->connection = NULL;
        }
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_ews_notification_parent_class)->dispose (object);
 }
 
 static void
@@ -243,6 +261,203 @@ e_ews_notification_init (EEwsNotification *notification)
        g_signal_connect (notification->priv->soup_session, "authenticate", G_CALLBACK 
(ews_notification_authenticate), notification);
 }
 
+static gboolean
+e_ews_notification_subscribe_folder_sync (EEwsNotification *notification,
+                                         GSList *folders,
+                                         gchar **subscription_id,
+                                         GCancellable *cancellable)
+{
+       CamelEwsSettings *ews_settings;
+       ESoapMessage *msg;
+       ESoapResponse *response;
+       ESoapParameter *param, *subparam;
+       GError *error = NULL;
+       GSList *l;
+       gchar *auth_mech = NULL;
+       guint event_type;
+       xmlDoc *doc;
+
+       g_return_val_if_fail (notification != NULL, FALSE);
+       g_return_val_if_fail (notification->priv != NULL, FALSE);
+
+       msg = e_ews_message_new_with_header (
+               e_ews_connection_get_uri (notification->priv->connection),
+               e_ews_connection_get_impersonate_user (notification->priv->connection),
+               "Subscribe",
+               NULL,
+               NULL,
+               e_ews_connection_get_server_version (notification->priv->connection),
+               E_EWS_EXCHANGE_2010_SP1,
+               FALSE,
+               FALSE);
+
+       e_soap_message_start_element (msg, "StreamingSubscriptionRequest", "messages", NULL);
+
+       e_soap_message_start_element (msg, "FolderIds", NULL, NULL);
+       for (l = folders; l; l = l->next) {
+               e_ews_message_write_string_parameter_with_attribute (
+                       msg,
+                       "FolderId",
+                       NULL,
+                       NULL,
+                       "Id",
+                       l->data);
+       }
+       e_soap_message_end_element (msg); /* FolderIds */
+
+       e_soap_message_start_element (msg, "EventTypes", NULL, NULL);
+       for (event_type = 0; default_events_names[event_type] != NULL; event_type++) {
+               if (g_strcmp0 (default_events_names[event_type], "StatusEvent") == 0)
+                       continue;
+
+               e_ews_message_write_string_parameter_with_attribute (
+                       msg,
+                       "EventType",
+                       NULL,
+                       default_events_names[event_type],
+                       NULL,
+                       NULL);
+       }
+       e_soap_message_end_element (msg); /* EventTypes */
+
+       e_soap_message_end_element (msg); /* StreamingSubscriptionRequest */
+       e_ews_message_write_footer (msg); /* Complete the footer and print the request */
+
+       soup_message_body_set_accumulate (SOUP_MESSAGE (msg)->response_body, TRUE);
+
+       ews_settings = e_ews_connection_ref_settings (notification->priv->connection);
+
+       g_object_get (G_OBJECT (ews_settings), "auth-mechanism", &auth_mech, NULL);
+       if (g_strcmp0 (auth_mech, "GSSAPI") == 0)
+               e_ews_connection_utils_setup_msg_gssapi_auth (
+                       notification->priv->connection,
+                       notification->priv->soup_session,
+                       SOUP_MESSAGE (msg));
+
+       g_object_unref (ews_settings);
+       g_free (auth_mech);
+
+       if (g_cancellable_is_cancelled (cancellable)) {
+               g_object_unref (msg);
+               return FALSE;
+       }
+
+       if (!SOUP_STATUS_IS_SUCCESSFUL (
+               soup_session_send_message (notification->priv->soup_session, SOUP_MESSAGE (msg)))) {
+               g_object_unref (msg);
+               return FALSE;
+       }
+
+       doc = xmlReadMemory (
+               SOUP_MESSAGE (msg)->response_body->data,
+               SOUP_MESSAGE (msg)->response_body->length,
+               "response.xml", NULL, 0);
+
+       response = e_soap_response_new_from_xmldoc (doc);
+       g_object_unref (msg);
+
+       param = e_soap_response_get_first_parameter_by_name (response, "ResponseMessages", &error);
+
+       /* Sanity check */
+       g_warn_if_fail ((param != NULL && error == NULL) || (param == NULL && error != NULL));
+
+       if (error != NULL) {
+               g_warning (G_STRLOC ": %s\n", error->message);
+               g_error_free (error);
+
+               g_object_unref (response);
+               return FALSE;
+       }
+
+       subparam = e_soap_parameter_get_first_child (param);
+
+       while (subparam != NULL) {
+               const gchar *name = (const gchar *) subparam->name;
+
+               if (!ews_get_response_status (subparam, &error)) {
+                       g_warning (G_STRLOC ": %s\n", error->message);
+                       g_error_free (error);
+
+                       g_object_unref (response);
+                       return FALSE;
+               }
+
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SubscribeResponseMessage")) {
+                       ESoapParameter *node;
+
+                       node = e_soap_parameter_get_first_child_by_name (subparam, "SubscriptionId");
+                       *subscription_id = e_soap_parameter_get_string_value (node);
+               }
+
+               subparam = e_soap_parameter_get_next_child (subparam);
+       }
+
+       g_object_unref (response);
+       return TRUE;
+}
+
+static gboolean
+e_ews_notification_unsubscribe_folder_sync (EEwsNotification *notification,
+                                           const gchar *subscription_id)
+{
+       ESoapMessage *msg;
+       ESoapResponse *response;
+       ESoapParameter *param;
+       GError *error = NULL;
+       xmlDoc *doc;
+
+       g_return_val_if_fail (notification != NULL, FALSE);
+       g_return_val_if_fail (notification->priv != NULL, FALSE);
+
+       msg = e_ews_message_new_with_header (
+               e_ews_connection_get_uri (notification->priv->connection),
+               e_ews_connection_get_impersonate_user (notification->priv->connection),
+               "Unsubscribe",
+               NULL,
+               NULL,
+               e_ews_connection_get_server_version (notification->priv->connection),
+               E_EWS_EXCHANGE_2010_SP1,
+               FALSE,
+               FALSE);
+
+       e_ews_message_write_string_parameter_with_attribute (
+               msg, "SubscriptionId", "messages", subscription_id, NULL, NULL);
+
+       e_ews_message_write_footer (msg); /* Complete the footer and print the request */
+
+       soup_message_body_set_accumulate (SOUP_MESSAGE (msg)->response_body, TRUE);
+
+       if (!SOUP_STATUS_IS_SUCCESSFUL (
+               soup_session_send_message (notification->priv->soup_session, SOUP_MESSAGE (msg)))) {
+               g_object_unref (msg);
+               return FALSE;
+       }
+
+       doc = xmlReadMemory (
+               SOUP_MESSAGE (msg)->response_body->data,
+               SOUP_MESSAGE (msg)->response_body->length,
+               "response.xml", NULL, 0);
+
+       response = e_soap_response_new_from_xmldoc (doc);
+       g_object_unref (msg);
+
+       param = e_soap_response_get_first_parameter_by_name (response, "ResponseMessages", &error);
+
+       /* Sanity check */
+       g_warn_if_fail ((param != NULL && error == NULL) || (param == NULL && error != NULL));
+
+       g_object_unref (response);
+
+       if (error != NULL) {
+               g_warning (G_STRLOC ": %s\n", error->message);
+               g_error_free (error);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
 static EEwsNotificationEvent *
 get_folder_event_info (ESoapParameter *param,
                       EEwsNotificationEventType event_type)
@@ -337,13 +552,10 @@ ews_notification_fire_events_from_response (EEwsNotification *notification,
        GSList *events = NULL;
        GError *error = NULL;
 
-       param = e_soap_response_get_first_parameter_by_name (
-               response, "ResponseMessages", &error);
+       param = e_soap_response_get_first_parameter_by_name (response, "ResponseMessages", &error);
 
        /* Sanity check */
-       g_return_if_fail (
-               (param != NULL && error == NULL) ||
-               (param == NULL && error != NULL));
+       g_warn_if_fail ((param != NULL && error == NULL) || (param == NULL && error != NULL));
 
        if (error != NULL) {
                g_warning (G_STRLOC ": %s\n", error->message);
@@ -442,7 +654,7 @@ ews_notification_soup_got_chunk (SoupMessage *msg,
                chunk_str = (gchar *) notification->priv->chunk->data;
                chunk_len = notification->priv->chunk->len;
 
-               if (chunk_len == 0) {
+               if (chunk_len == 0 || g_cancellable_is_cancelled (notification->priv->cancellable)) {
                        g_byte_array_free (notification->priv->chunk, TRUE);
                        notification->priv->chunk = NULL;
                        keep_parsing = FALSE;
@@ -450,82 +662,124 @@ ews_notification_soup_got_chunk (SoupMessage *msg,
        } while (keep_parsing);
 }
 
-/*
- * e_ews_notification_get_events_sync
- *
- * @param notification:
- * @param pri
- * @param subscription_id
- * @param cancellable
- * @param user_data
- */
-guint
+static gboolean
 e_ews_notification_get_events_sync (EEwsNotification *notification,
-                                   gint pri,
                                    const gchar *subscription_id)
 {
-       CamelEwsSettings *ews_settings;
        ESoapMessage *msg;
-       gchar *auth_mech = NULL;
-       guint ret;
+       gboolean ret;
+       gulong handler_id;
 
        g_return_val_if_fail (notification != NULL, SOUP_STATUS_CANCELLED);
        g_return_val_if_fail (notification->priv != NULL, SOUP_STATUS_CANCELLED);
        g_return_val_if_fail (notification->priv->connection != NULL, SOUP_STATUS_CANCELLED);
 
-       notification->priv->msg = e_ews_message_new_with_header (
+       msg = e_ews_message_new_with_header (
                e_ews_connection_get_uri (notification->priv->connection),
                e_ews_connection_get_impersonate_user (notification->priv->connection),
                "GetStreamingEvents",
                NULL,
                NULL,
                e_ews_connection_get_server_version (notification->priv->connection),
-               E_EWS_EXCHANGE_2010_SP2,
+               E_EWS_EXCHANGE_2010_SP1,
                FALSE,
                FALSE);
 
-       msg = notification->priv->msg;
-
        e_soap_message_start_element (msg, "SubscriptionIds", "messages", NULL);
-       e_ews_message_write_string_parameter_with_attribute (
-               msg, "SubscriptionId", NULL, subscription_id, NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (msg, "SubscriptionId", NULL, subscription_id, 
NULL, NULL);
        e_soap_message_end_element (msg); /* SubscriptionIds */
 
-       e_ews_message_write_string_parameter_with_attribute (
-               msg, "ConnectionTimeout", "messages", "10", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (msg, "ConnectionTimeout", "messages", "10", 
NULL, NULL);
 
        e_ews_message_write_footer (msg); /* Complete the footer and print the request */
 
        if (e_ews_debug_get_log_level () <= 2)
                soup_message_body_set_accumulate (SOUP_MESSAGE (msg)->response_body, FALSE);
 
-       ews_settings = e_ews_connection_ref_settings (notification->priv->connection);
-
-       g_object_get (G_OBJECT (ews_settings), "auth-mechanism", &auth_mech, NULL);
-       if (g_strcmp0 (auth_mech, "GSSAPI") == 0)
-               e_ews_connection_utils_setup_msg_gssapi_auth (
-                       notification->priv->connection,
-                       notification->priv->soup_session,
-                       SOUP_MESSAGE (msg));
-
-       g_object_unref (ews_settings);
-       g_free (auth_mech);
-
-       g_signal_connect (
+       handler_id = g_signal_connect (
                SOUP_MESSAGE (msg), "got-chunk",
                G_CALLBACK (ews_notification_soup_got_chunk), notification);
 
-       ret = soup_session_send_message (notification->priv->soup_session, SOUP_MESSAGE (msg));
-       g_clear_object (&msg);
+       ret = SOUP_STATUS_IS_SUCCESSFUL (
+               soup_session_send_message (notification->priv->soup_session, SOUP_MESSAGE (msg)));
+
+       g_signal_handler_disconnect (msg, handler_id);
+       g_object_unref (msg);
 
        return ret;
 }
 
+static gpointer
+e_ews_notification_get_events_thread (gpointer user_data)
+{
+       EEwsNotificationThreadData *td = user_data;
+       gchar *subscription_id = NULL;
+       gboolean ret;
+
+       g_return_val_if_fail (td != NULL, NULL);
+       g_return_val_if_fail (td->notification != NULL, NULL);
+       g_return_val_if_fail (td->folders != NULL, NULL);
+
+       if (!e_ews_notification_subscribe_folder_sync (td->notification, td->folders, &subscription_id, 
td->cancellable))
+               goto exit;
+
+       do {
+               if (g_cancellable_is_cancelled (td->cancellable))
+                       goto exit;
+
+               ret = e_ews_notification_get_events_sync (
+                               td->notification,
+                               subscription_id);
+       } while (ret);
+
+
+exit:
+       if (subscription_id != NULL) {
+               e_ews_notification_unsubscribe_folder_sync (td->notification, subscription_id);
+               g_free (subscription_id);
+       }
+
+       g_slist_free_full (td->folders, g_free);
+       g_object_unref (td->cancellable);
+       g_object_unref (td->notification);
+       g_free (td);
+
+       return NULL;
+}
+
+void
+e_ews_notification_start_listening_sync (EEwsNotification *notification,
+                                        GSList *folders)
+{
+       EEwsNotificationThreadData *td;
+       GSList *l;
+       GThread *thread;
+
+       g_return_if_fail (notification != NULL);
+       g_return_if_fail (notification->priv != NULL);
+       g_return_if_fail (folders != NULL);
+
+       if (notification->priv->cancellable != NULL)
+               e_ews_notification_stop_listening_sync (notification);
+
+       notification->priv->cancellable = g_cancellable_new ();
+
+       td = g_new0 (EEwsNotificationThreadData, 1);
+       td->notification = g_object_ref (notification);
+       td->cancellable = g_object_ref (notification->priv->cancellable);
+       for (l = folders; l != NULL; l = l->next)
+               td->folders = g_slist_prepend(td->folders, g_strdup (l->data));
+
+       thread = g_thread_new (NULL, e_ews_notification_get_events_thread, td);
+       g_thread_unref (thread);
+}
+
 void
-e_ews_notification_cancel_get_events (EEwsNotification *notification)
+e_ews_notification_stop_listening_sync (EEwsNotification *notification)
 {
-       soup_session_cancel_message (
-               notification->priv->soup_session,
-               SOUP_MESSAGE (notification->priv->msg),
-               SOUP_STATUS_CANCELLED);
+       g_return_if_fail (notification != NULL);
+       g_return_if_fail (notification->priv != NULL);
+
+       g_cancellable_cancel (notification->priv->cancellable);
+       g_clear_object (&notification->priv->cancellable);
 }
diff --git a/src/server/e-ews-notification.h b/src/server/e-ews-notification.h
index 86107d5..2ecbc65 100644
--- a/src/server/e-ews-notification.h
+++ b/src/server/e-ews-notification.h
@@ -55,14 +55,12 @@ GType               e_ews_notification_get_type     (void);
 EEwsNotification *
                e_ews_notification_new          (EEwsConnection *connection);
 
-guint          e_ews_notification_get_events_sync
+void           e_ews_notification_start_listening_sync
                                                (EEwsNotification *notification,
-                                                gint pri,
-                                                const gchar *subscription_id);
-void           e_ews_notification_cancel_get_events
+                                                GSList *folders);
+void           e_ews_notification_stop_listening_sync
                                                (EEwsNotification *notification);
 
-
 G_END_DECLS
 
 #endif /* E_EWS_NOTIFICATION_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]