[evolution-ews] Bug #699349 - Listen for server change notifications



commit 16ed93ca16c7e773229e351a290383d61f4367e7
Author: Fabiano Fidêncio <fidencio redhat com>
Date:   Tue Oct 29 17:02:20 2013 +0100

    Bug #699349 - Listen for server change notifications

 src/addressbook/e-book-backend-ews.c |  420 ++++++++++++++----
 src/calendar/e-cal-backend-ews.c     |  306 +++++++++++---
 src/camel/camel-ews-provider.c       |    2 +
 src/camel/camel-ews-store.c          |  605 +++++++++++++++++++++++++-
 src/camel/camel-ews-utils.c          |   14 +-
 src/server/Makefile.am               |    6 +
 src/server/camel-ews-settings.c      |   68 +++
 src/server/camel-ews-settings.h      |    5 +
 src/server/e-ews-connection-utils.c  |  244 ++++++++++
 src/server/e-ews-connection-utils.h  |   48 ++
 src/server/e-ews-connection.c        |  804 +++++++++++++++++++++++-----------
 src/server/e-ews-connection.h        |   70 +++
 src/server/e-ews-message.c           |    5 +-
 src/server/e-ews-message.h           |    3 +-
 src/server/e-ews-notification.c      |  531 ++++++++++++++++++++++
 src/server/e-ews-notification.h      |   68 +++
 src/server/e-ews-oof-settings.c      |    6 +-
 src/server/e-soap-message.c          |   11 +-
 src/server/e-soap-message.h          |    3 +-
 src/server/e-soap-response.c         |   12 +-
 src/server/e-soap-response.h         |    6 +-
 21 files changed, 2792 insertions(+), 445 deletions(-)
---
diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c
index a3c5a53..451b391 100644
--- a/src/addressbook/e-book-backend-ews.c
+++ b/src/addressbook/e-book-backend-ews.c
@@ -90,6 +90,9 @@ struct _EBookBackendEwsPrivate {
        SyncDelta *dlock;
 
        GCancellable *cancellable;
+
+       gboolean listen_notifications;
+       gchar *subscription_id;
 };
 
 /* using this for backward compatibility with E_DATA_BOOK_MODE */
@@ -115,6 +118,8 @@ enum {
 /* Forward Declarations */
 static void    e_book_backend_ews_authenticator_init
                                (ESourceAuthenticatorInterface *interface);
+static gpointer ews_update_items_thread (gpointer data);
+
 
 G_DEFINE_TYPE_WITH_CODE (
        EBookBackendEws,
@@ -2854,7 +2859,7 @@ exit:
        return contact;
 }
 
-static void
+static gboolean
 ebews_store_distribution_list_items (EBookBackendEws *ebews,
                                      const EwsId *id,
                                      const gchar *d_name,
@@ -2863,23 +2868,26 @@ ebews_store_distribution_list_items (EBookBackendEws *ebews,
 {
        EContact *contact;
        EVCardAttribute *attr;
+       gboolean ret;
 
-       g_return_if_fail (ebews->priv->summary != NULL);
+       g_return_val_if_fail (ebews->priv->summary != NULL, FALSE);
 
        contact = ebews_get_dl_info (ebews, id, d_name, members, error);
        if (contact == NULL)
-               return;
+               return FALSE;
 
        attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
        e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST");
 
-       e_book_backend_sqlitedb_new_contact (ebews->priv->summary, ebews->priv->folder_id, contact, TRUE, 
error);
-       e_book_backend_notify_update (E_BOOK_BACKEND (ebews), contact);
+       ret = e_book_backend_sqlitedb_new_contact (ebews->priv->summary, ebews->priv->folder_id, contact, 
TRUE, error);
+       if (ret)
+               e_book_backend_notify_update (E_BOOK_BACKEND (ebews), contact);
 
        g_object_unref (contact);
+       return ret;
 }
 
-static void
+static gboolean
 ebews_vcards_append_dl (EBookBackendEws *ebews,
                        const EwsId *id,
                         const gchar *d_name,
@@ -2893,7 +2901,7 @@ ebews_vcards_append_dl (EBookBackendEws *ebews,
 
        contact = ebews_get_dl_info (ebews, id, d_name, members, error);
        if (contact == NULL)
-               return;
+               return FALSE;
 
        attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
        e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST");
@@ -2903,6 +2911,8 @@ ebews_vcards_append_dl (EBookBackendEws *ebews,
 
        g_free (vcard_string);
        g_object_unref (contact);
+
+       return TRUE;
 }
 
 static gboolean
@@ -2918,9 +2928,10 @@ ebews_fetch_items (EBookBackendEws *ebews,
        GSList *l;
        GSList *contact_item_ids = NULL, *dl_ids = NULL;
        GSList *new_items = NULL;
+       gboolean ret = FALSE;
 
        if (!book_backend_ews_ensure_connected (ebews, cancellable, error)) {
-               return FALSE;
+               return ret;
        }
 
        priv = ebews->priv;
@@ -2944,14 +2955,12 @@ ebews_fetch_items (EBookBackendEws *ebews,
 
        /* TODO fetch attachments */
        if (contact_item_ids)
-               e_ews_connection_get_items_sync (
+               if (!e_ews_connection_get_items_sync (
                        cnc, EWS_PRIORITY_MEDIUM,
                        contact_item_ids, "Default", CONTACT_ITEM_PROPS,
                        FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL,
-                       cancellable, error);
-
-       if (*error)
-               goto cleanup;
+                       cancellable, error))
+                       goto cleanup;
 
        if (new_items) {
                if (store_to_cache)
@@ -2963,13 +2972,12 @@ ebews_fetch_items (EBookBackendEws *ebews,
 
        /* Get the display names of the distribution lists */
        if (dl_ids)
-               e_ews_connection_get_items_sync (
+               if (!e_ews_connection_get_items_sync (
                        cnc, EWS_PRIORITY_MEDIUM,
                        dl_ids, "Default", NULL,
                        FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL,
-                       cancellable, error);
-       if (*error)
-               goto cleanup;
+                       cancellable, error))
+                       goto cleanup;
 
        for (l = new_items; l != NULL; l = g_slist_next (l)) {
                EEwsItem *item = (EEwsItem *) l->data;
@@ -2986,26 +2994,21 @@ ebews_fetch_items (EBookBackendEws *ebews,
                mb = g_new0 (EwsMailbox, 1);
                mb->item_id = (EwsId *) id;
 
-               /* expand dl */
-               if (*error)
-                       goto cleanup;
-
                d_name = e_ews_item_get_subject (item);
-               e_ews_connection_expand_dl_sync (
+               if (!e_ews_connection_expand_dl_sync (
                        cnc, EWS_PRIORITY_MEDIUM, mb, &members,
-                       &includes_last, cancellable, error);
-               if (*error)
+                       &includes_last, cancellable, error))
                        goto cleanup;
 
                if (store_to_cache)
-                       ebews_store_distribution_list_items (ebews, id, d_name, members, error);
+                       ret = ebews_store_distribution_list_items (ebews, id, d_name, members, error);
                else
-                       ebews_vcards_append_dl (ebews, id, d_name, members, vcards, error);
+                       ret = ebews_vcards_append_dl (ebews, id, d_name, members, vcards, error);
 
                g_free (mb);
                g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free);
 
-               if (*error)
+               if (!ret)
                        goto cleanup;
        }
 
@@ -3047,8 +3050,7 @@ ebews_start_sync (gpointer data)
        EBookBackendEws *ebews;
        EBookBackendEwsPrivate *priv;
        GList *list, *link;
-       gchar *sync_state, *status_message = NULL;
-       gboolean includes_last_item;
+       gchar *status_message = NULL;
        GCancellable *cancellable;
        GError *error = NULL;
 
@@ -3070,64 +3072,7 @@ ebews_start_sync (gpointer data)
        g_list_free_full (list, g_object_unref);
        g_free (status_message);
 
-       sync_state = e_book_backend_sqlitedb_get_sync_data (priv->summary, priv->folder_id, NULL);
-       do
-       {
-               GSList *items_created = NULL, *items_updated = NULL;
-               GSList *items_deleted = NULL;
-               gchar *old_sync_state = sync_state;
-
-               sync_state = NULL;
-
-               e_ews_connection_sync_folder_items_sync (
-                       priv->cnc, EWS_PRIORITY_MEDIUM,
-                       old_sync_state, priv->folder_id,
-                       "IdOnly", NULL, EWS_MAX_FETCH_COUNT,
-                       &sync_state,
-                       &includes_last_item,
-                       &items_created, &items_updated,
-                       &items_deleted, cancellable, &error);
-
-               g_free (old_sync_state);
-
-               if (g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) 
{
-                       g_clear_error (&error);
-                       e_book_backend_sqlitedb_set_sync_data (priv->summary, priv->folder_id, NULL, &error);
-                       ebews_forget_all_contacts (ebews);
-
-                       e_ews_connection_sync_folder_items_sync (priv->cnc, EWS_PRIORITY_MEDIUM, NULL, 
priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT,
-                               &sync_state, &includes_last_item, &items_created, &items_updated, 
&items_deleted,
-                               cancellable, &error);
-               }
-
-               if (error)
-                       break;
-
-               if (items_deleted)
-                       ebews_sync_deleted_items (ebews, items_deleted, &error);
-
-               if (items_created)
-                       ebews_fetch_items (ebews, items_created, TRUE, NULL, cancellable, &error);
-
-               if (error) {
-                       g_slist_free_full (items_updated, g_object_unref);
-
-                       break;
-               }
-
-               if (items_updated)
-                       ebews_fetch_items (ebews, items_updated, TRUE, NULL, cancellable, &error);
-
-               if (error)
-                       break;
-
-               e_book_backend_sqlitedb_set_sync_data (priv->summary, priv->folder_id, sync_state, &error);
-       } while (!error && !includes_last_item);
-
-       if (!error)
-               e_book_backend_sqlitedb_set_is_populated (priv->summary, priv->folder_id, TRUE, &error);
-
-       g_free (sync_state);
+       ews_update_items_thread (g_object_ref (ebews));
 
        /* hide progress message when done */
        list = e_book_backend_list_views (E_BOOK_BACKEND (ebews));
@@ -3606,6 +3551,245 @@ e_book_backend_ews_get_backend_property (EBookBackend *backend,
                get_backend_property (backend, prop_name);
 }
 
+static gpointer
+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)
+                       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);
+               g_slist_free (folders);
+       } else {
+               if (ebews->priv->subscription_id == NULL)
+                       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;
+       }
+
+ exit:
+       PRIV_UNLOCK (ebews->priv);
+       g_object_unref (ebews);
+       return NULL;
+}
+
+static void
+ebews_listen_notifications_cb (EBookBackendEws *ebews,
+                              GParamSpec *spec,
+                              CamelEwsSettings *ews_settings)
+{
+       GThread *thread;
+
+       PRIV_LOCK (ebews->priv);
+       if (ebews->priv->cnc == NULL) {
+               PRIV_UNLOCK (ebews->priv);
+               return;
+       }
+
+       if (!e_ews_connection_satisfies_server_version (ebews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) {
+               PRIV_UNLOCK (ebews->priv);
+               return;
+       }
+
+       ebews->priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
+       PRIV_UNLOCK (ebews->priv);
+
+       thread = g_thread_new (NULL, handle_notifications_thread, g_object_ref (ebews));
+       g_thread_unref (thread);
+}
+
+static gpointer
+ews_update_items_thread (gpointer data)
+{
+       EBookBackendEws *ebews = data;
+       EBookBackendEwsPrivate *priv;
+       gchar *sync_state;
+       GError *error = NULL;
+       gboolean includes_last_item;
+
+       priv = ebews->priv;
+
+       sync_state = e_book_backend_sqlitedb_get_sync_data (priv->summary, priv->folder_id, NULL);
+       do {
+               GSList *items_created = NULL;
+               GSList *items_updated = NULL;
+               GSList *items_deleted = NULL;
+               gchar *old_sync_state = sync_state;
+
+               sync_state = NULL;
+               includes_last_item = TRUE;
+
+               e_ews_connection_sync_folder_items_sync (
+                               priv->cnc,
+                               EWS_PRIORITY_MEDIUM,
+                               old_sync_state,
+                               priv->folder_id,
+                               "IdOnly",
+                               NULL,
+                               EWS_MAX_FETCH_COUNT,
+                               &sync_state,
+                               &includes_last_item,
+                               &items_created,
+                               &items_updated,
+                               &items_deleted,
+                               priv->cancellable,
+                               &error);
+
+               g_free (old_sync_state);
+
+               if (error != NULL) {
+                       if (g_error_matches (error, EWS_CONNECTION_ERROR, 
EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
+                               g_clear_error (&error);
+                               e_book_backend_sqlitedb_set_sync_data (priv->summary, priv->folder_id, NULL, 
&error);
+                               if (error != NULL)
+                                       break;
+
+                               ebews_forget_all_contacts (ebews);
+
+                               if (!e_ews_connection_sync_folder_items_sync (
+                                               priv->cnc,
+                                               EWS_PRIORITY_MEDIUM,
+                                               NULL,
+                                               priv->folder_id,
+                                               "IdOnly",
+                                               NULL,
+                                               EWS_MAX_FETCH_COUNT,
+                                               &sync_state,
+                                               &includes_last_item,
+                                               &items_created,
+                                               &items_updated,
+                                               &items_deleted,
+                                               priv->cancellable,
+                                               &error))
+                                       break;
+                       } else {
+                                       break;
+                       }
+               }
+
+               if (items_deleted) {
+                       ebews_sync_deleted_items (
+                                       ebews,
+                                       items_deleted, /* freed inside the function */
+                                       &error);
+                       if (error != NULL) {
+                               g_slist_free_full (items_created, g_object_unref);
+                               g_slist_free_full (items_updated, g_object_unref);
+                               break;
+                       }
+               }
+
+               if (items_created) {
+                       ebews_fetch_items (
+                                       ebews,
+                                       items_created, /* freed insite the function */
+                                       TRUE,
+                                       NULL,
+                                       priv->cancellable,
+                                       &error);
+                       if (error != NULL) {
+                               g_slist_free_full (items_updated, g_object_unref);
+                               break;
+                       };
+               }
+
+               if (items_updated) {
+                       ebews_fetch_items (
+                                       ebews,
+                                       items_updated, /* freed inside the function */
+                                       TRUE,
+                                       NULL,
+                                       priv->cancellable,
+                                       &error);
+                       if (error != NULL)
+                               break;
+               }
+
+               e_book_backend_sqlitedb_set_sync_data (priv->summary, priv->folder_id, sync_state, &error);
+               if (error != NULL)
+                       break;
+       } while (!includes_last_item);
+
+       if (error == NULL)
+               e_book_backend_sqlitedb_set_is_populated (priv->summary, priv->folder_id, TRUE, &error);
+
+       if (error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       }
+
+       g_free (sync_state);
+       g_object_unref (ebews);
+
+       return NULL;
+}
+
+static void
+ebews_server_notification_cb (EBookBackendEws *ebews,
+                             GSList *events,
+                             EEwsConnection *cnc)
+{
+       GSList *l;
+       gboolean update_folder = FALSE;
+
+       g_return_if_fail (ebews != NULL);
+       g_return_if_fail (ebews->priv != NULL);
+
+       for (l = events; l != NULL; l = l->next) {
+               EEwsNotificationEvent *event = l->data;
+
+               switch (event->type) {
+                       case E_EWS_NOTIFICATION_EVENT_CREATED:
+                       case E_EWS_NOTIFICATION_EVENT_DELETED:
+                       case E_EWS_NOTIFICATION_EVENT_MODIFIED:
+                               PRIV_LOCK (ebews->priv);
+                               if (g_strcmp0 (event->folder_id, ebews->priv->folder_id) == 0)
+                                       update_folder = TRUE;
+                               PRIV_UNLOCK (ebews->priv);
+                               break;
+                       case E_EWS_NOTIFICATION_EVENT_MOVED:
+                       case E_EWS_NOTIFICATION_EVENT_COPIED:
+                               PRIV_LOCK (ebews->priv);
+                               if (g_strcmp0 (event->folder_id, ebews->priv->folder_id) == 0 ||
+                                   g_strcmp0 (event->old_folder_id, ebews->priv->folder_id) == 0)
+                                       update_folder = TRUE;
+                               PRIV_UNLOCK (ebews->priv);
+                               break;
+                       default:
+                               return;
+               }
+       }
+
+       if (update_folder) {
+               GThread *thread;
+
+               thread = g_thread_new (NULL, ews_update_items_thread, g_object_ref (ebews));
+               g_thread_unref (thread);
+       }
+}
+
 static void
 e_book_backend_ews_open (EBookBackend *backend,
                          EDataBook *book,
@@ -3613,32 +3797,58 @@ e_book_backend_ews_open (EBookBackend *backend,
                          GCancellable *cancellable,
                          gboolean only_if_exists)
 {
-       EBookBackendEws *cbews;
+       CamelEwsSettings *ews_settings;
+       EBookBackendEws *ebews;
+       EBookBackendEwsPrivate * priv;
        ESource *source;
        GError *error = NULL;
 
-       cbews = E_BOOK_BACKEND_EWS (backend);
+       if (e_book_backend_is_opened (backend))
+               return;
+
+       ebews = E_BOOK_BACKEND_EWS (backend);
+       priv = ebews->priv;
 
        source = e_backend_get_source (E_BACKEND (backend));
        e_book_backend_ews_load_source (backend, source, only_if_exists, &error);
+       ews_settings = book_backend_ews_get_collection_settings (ebews);
 
        if (error == NULL) {
                gboolean need_to_authenticate;
 
-               PRIV_LOCK (cbews->priv);
-               need_to_authenticate = cbews->priv->cnc == NULL && e_backend_get_online (E_BACKEND (backend));
-               PRIV_UNLOCK (cbews->priv);
+               PRIV_LOCK (priv);
+               need_to_authenticate = priv->cnc == NULL && e_backend_get_online (E_BACKEND (backend));
+               PRIV_UNLOCK (priv);
 
                if (need_to_authenticate) {
                        e_backend_authenticate_sync (
-                               E_BACKEND (backend),
-                               E_SOURCE_AUTHENTICATOR (backend),
-                               cancellable, &error);
+                                       E_BACKEND (backend),
+                                       E_SOURCE_AUTHENTICATOR (backend),
+                                       cancellable, &error);
                }
        }
 
+       if (error == NULL) {
+               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 (
+                       priv->cnc,
+                       "server-notification",
+                       G_CALLBACK (ebews_server_notification_cb),
+                       ebews);
+
+       g_signal_connect_swapped (
+                       ews_settings,
+                       "notify::listen-notifications",
+                       G_CALLBACK (ebews_listen_notifications_cb),
+                       ebews);
 }
 
 /**
@@ -3712,10 +3922,14 @@ e_book_backend_ews_dispose (GObject *object)
 {
        EBookBackendEws *bews;
        EBookBackendEwsPrivate *priv;
+       CamelEwsSettings *ews_settings;
 
        bews = E_BOOK_BACKEND_EWS (object);
        priv = bews->priv;
 
+       ews_settings = book_backend_ews_get_collection_settings (bews);
+       g_signal_handlers_disconnect_by_func (ews_settings, ebews_listen_notifications_cb, bews);
+
        if (priv->cancellable)
                g_cancellable_cancel (priv->cancellable);
 
@@ -3740,10 +3954,22 @@ e_book_backend_ews_dispose (GObject *object)
        }
 
        if (priv->cnc) {
-               g_object_unref (priv->cnc);
-               priv->cnc = NULL;
+               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 (
+                                       priv->cnc,
+                                       priv->subscription_id,
+                                       NULL,
+                                       NULL);
+               }
+
+               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 ff4b28f..8dc2547 100644
--- a/src/calendar/e-cal-backend-ews.c
+++ b/src/calendar/e-cal-backend-ews.c
@@ -83,6 +83,9 @@ struct _ECalBackendEwsPrivate {
        GHashTable *item_id_hash;
 
        GCancellable *cancellable;
+
+       gboolean listen_notifications;
+       gchar *subscription_id;
 };
 
 #define PRIV_LOCK(p)   (g_rec_mutex_lock (&(p)->rec_mutex))
@@ -113,6 +116,8 @@ struct _ECalBackendEwsPrivate {
 static void ews_cal_component_get_item_id (ECalComponent *comp, gchar **itemid, gchar **changekey);
 static gboolean ews_start_sync (gpointer data);
 static icaltimezone * e_cal_get_timezone_from_ical_component (ECalBackend *backend, icalcomponent *comp);
+static gpointer ews_start_sync_thread (gpointer data);
+
 
 /* Forward Declarations */
 static void    e_cal_backend_ews_authenticator_init
@@ -599,6 +604,115 @@ add_comps_to_item_id_hash (ECalBackendEws *cbews)
        g_slist_free (comps);
 }
 
+static gpointer
+handle_notifications_thread (gpointer data)
+{
+       ECalBackendEws *cbews = data;
+
+       PRIV_LOCK (cbews->priv);
+
+       if (cbews->priv->cnc == NULL)
+               goto exit;
+
+       if (cbews->priv->listen_notifications) {
+               GSList *folders = NULL;
+
+               if (cbews->priv->subscription_id != NULL)
+                       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);
+               g_slist_free (folders);
+       } else {
+               if (cbews->priv->subscription_id == NULL)
+                       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;
+       }
+
+ exit:
+       PRIV_UNLOCK (cbews->priv);
+       g_object_unref (cbews);
+       return NULL;
+}
+
+static void
+cbews_listen_notifications_cb (ECalBackendEws *cbews,
+                              GParamSpec *spec,
+                              CamelEwsSettings *ews_settings)
+{
+       GThread *thread;
+
+       PRIV_LOCK (cbews->priv);
+       if (cbews->priv->cnc == NULL) {
+               PRIV_UNLOCK (cbews->priv);
+               return;
+       }
+
+       if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) {
+               PRIV_UNLOCK (cbews->priv);
+               return;
+       }
+
+       cbews->priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
+       PRIV_UNLOCK (cbews->priv);
+
+       thread = g_thread_new (NULL, handle_notifications_thread, g_object_ref (cbews));
+       g_thread_unref (thread);
+}
+
+static void
+cbews_server_notification_cb (ECalBackendEws *cbews,
+                             GSList *events,
+                             EEwsConnection *cnc)
+{
+       GSList *l;
+       gboolean update_folder = FALSE;
+
+       g_return_if_fail (cbews != NULL);
+       g_return_if_fail (cbews->priv != NULL);
+
+       for (l = events; l != NULL; l = l->next) {
+               EEwsNotificationEvent *event = l->data;
+
+               switch (event->type) {
+                       case E_EWS_NOTIFICATION_EVENT_CREATED:
+                       case E_EWS_NOTIFICATION_EVENT_DELETED:
+                       case E_EWS_NOTIFICATION_EVENT_MODIFIED:
+                               PRIV_LOCK (cbews->priv);
+                               if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0)
+                                       update_folder = TRUE;
+                               PRIV_UNLOCK (cbews->priv);
+                               break;
+                       case E_EWS_NOTIFICATION_EVENT_MOVED:
+                       case E_EWS_NOTIFICATION_EVENT_COPIED:
+                               PRIV_LOCK (cbews->priv);
+                               if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0 ||
+                                   g_strcmp0 (event->old_folder_id, cbews->priv->folder_id) == 0)
+                                       update_folder = TRUE;
+                               PRIV_UNLOCK (cbews->priv);
+                               break;
+                       default:
+                               return;
+               }
+       }
+
+       if (update_folder)
+               ews_start_sync (cbews);
+}
+
 static void
 e_cal_backend_ews_open (ECalBackend *backend,
                         EDataCal *cal,
@@ -606,18 +720,24 @@ e_cal_backend_ews_open (ECalBackend *backend,
                         GCancellable *cancellable,
                         gboolean only_if_exists)
 {
+       CamelEwsSettings *ews_settings;
        ECalBackendEws *cbews;
        ECalBackendEwsPrivate *priv;
        ESource *source;
        const gchar *cache_dir;
        gboolean need_to_authenticate;
+       gboolean ret = TRUE;
        GError *error = NULL;
 
+       if (e_cal_backend_is_opened (backend))
+               return;
+
        cbews = (ECalBackendEws *) backend;
        priv = cbews->priv;
 
        cache_dir = e_cal_backend_get_cache_dir (backend);
        source = e_backend_get_source (E_BACKEND (cbews));
+       ews_settings = cal_backend_ews_get_collection_settings (cbews);
 
        PRIV_LOCK (priv);
 
@@ -649,16 +769,33 @@ e_cal_backend_ews_open (ECalBackend *backend,
        PRIV_UNLOCK (priv);
 
        if (need_to_authenticate)
-               e_backend_authenticate_sync (
-                       E_BACKEND (backend),
-                       E_SOURCE_AUTHENTICATOR (backend),
-                       cancellable, &error);
+               ret = e_backend_authenticate_sync (
+                               E_BACKEND (backend),
+                               E_SOURCE_AUTHENTICATOR (backend),
+                               cancellable, &error);
 
-       if (!error)
+       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);
+       }
 
        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);
 }
 
 static void
@@ -813,16 +950,19 @@ e_cal_backend_ews_get_object_list (ECalBackend *backend,
        }
 }
 
-static void
+static gboolean
 ews_cal_delete_comp (ECalBackendEws *cbews,
                      ECalComponent *comp,
                      const gchar *item_id)
 {
        ECalBackendEwsPrivate *priv = cbews->priv;
        ECalComponentId *uid;
+       gboolean ret;
 
        uid = e_cal_component_get_id (comp);
-       e_cal_backend_store_remove_component (priv->store, uid->uid, uid->rid);
+       ret = e_cal_backend_store_remove_component (priv->store, uid->uid, uid->rid);
+       if (!ret)
+               goto exit;
 
        /* TODO test with recurrence handling */
        e_cal_backend_notify_component_removed (E_CAL_BACKEND (cbews), uid, comp, NULL);
@@ -831,7 +971,9 @@ ews_cal_delete_comp (ECalBackendEws *cbews,
        g_hash_table_remove (priv->item_id_hash, item_id);
        PRIV_UNLOCK (priv);
 
+exit:
        e_cal_component_free_id (uid);
+       return ret;
 }
 
 static void
@@ -3700,13 +3842,14 @@ ews_refreshing_dec (ECalBackendEws *cbews)
        PRIV_UNLOCK (cbews->priv);
 }
 
-static void
+static gboolean
 ews_cal_sync_get_items_sync (ECalBackendEws *cbews,
                              const GSList *item_ids,
                              const gchar *default_props,
                              const gchar *additional_props)
 {
        ECalBackendEwsPrivate *priv;
+       gboolean ret = FALSE;
        GSList *items = NULL, *l;
        GError *error = NULL;
 
@@ -3726,11 +3869,11 @@ ews_cal_sync_get_items_sync (ECalBackendEws *cbews,
                priv->cancellable,
                &error);
 
-       if (error) {
+       if (error != NULL) {
                g_debug ("%s: Unable to get items: %s", G_STRFUNC, error->message);
                g_clear_error (&error);
 
-               return;
+               goto exit;
        }
 
        /* fetch modified occurrences */
@@ -3743,7 +3886,7 @@ ews_cal_sync_get_items_sync (ECalBackendEws *cbews,
 
                modified_occurrences = e_ews_item_get_modified_occurrences (item);
                if (modified_occurrences) {
-                       ews_cal_sync_get_items_sync (
+                       ret = ews_cal_sync_get_items_sync (
                                cbews, modified_occurrences,
                                "IdOnly",
                                "item:Attachments"
@@ -3756,6 +3899,9 @@ ews_cal_sync_get_items_sync (ECalBackendEws *cbews,
                                " calendar:ModifiedOccurrences"
                                " calendar:RequiredAttendees"
                                " calendar:OptionalAttendees");
+
+                       if (!ret)
+                               goto exit;
                }
        }
 
@@ -3770,15 +3916,16 @@ ews_cal_sync_get_items_sync (ECalBackendEws *cbews,
                        add_item_to_cache (cbews, item);
                        ews_get_attachments (cbews, item);
                }
-
-               g_object_unref (item);
        }
        e_cal_backend_store_thaw_changes (priv->store);
+       ret = TRUE;
 
-       g_slist_free (items);
+exit:
+       g_slist_free_full (items, g_object_unref);
+       return ret;
 }
 
-static void
+static gboolean
 cal_backend_ews_process_folder_items (ECalBackendEws *cbews,
                                       const gchar *sync_state,
                                       GSList *items_created,
@@ -3788,13 +3935,14 @@ cal_backend_ews_process_folder_items (ECalBackendEws *cbews,
        ECalBackendEwsPrivate *priv;
        GSList *l[2], *m, *cal_item_ids = NULL, *task_memo_item_ids = NULL;
        gint i;
+       gboolean ret = FALSE;
 
        priv = cbews->priv;
 
        l[0] = items_created;
        l[1] = items_updated;
 
-       for (i = 0; i < 2; i++) {
+       for (i = 0; i < 2; i++) {
                for (; l[i] != NULL; l[i] = g_slist_next (l[i])) {
                        EEwsItem *item = (EEwsItem *) l[i]->data;
                        EEwsItemType type = e_ews_item_get_item_type (item);
@@ -3817,8 +3965,10 @@ cal_backend_ews_process_folder_items (ECalBackendEws *cbews,
                comp = g_hash_table_lookup (priv->item_id_hash, item_id);
                PRIV_UNLOCK (priv);
 
-               if (comp)
-                       ews_cal_delete_comp (cbews, comp, item_id);
+               if (comp) {
+                       if (!ews_cal_delete_comp (cbews, comp, item_id))
+                               goto exit;
+               }
        }
        e_cal_backend_store_thaw_changes (priv->store);
 
@@ -3846,9 +3996,12 @@ cal_backend_ews_process_folder_items (ECalBackendEws *cbews,
                        "AllProperties",
                        NULL);
        }
+       ret = TRUE;
 
+exit:
        g_slist_free (cal_item_ids);
        g_slist_free (task_memo_item_ids);
+       return ret;
 }
 
 static void
@@ -3885,6 +4038,7 @@ ews_start_sync_thread (gpointer data)
        GSList *items_updated = NULL;
        GSList *items_deleted = NULL;
        gboolean includes_last_item;
+       gboolean ret;
        gchar *old_sync_state = NULL;
        gchar *new_sync_state = NULL;
        GError *error = NULL;
@@ -3896,10 +4050,13 @@ ews_start_sync_thread (gpointer data)
        do {
                includes_last_item = TRUE;
 
-               e_ews_connection_sync_folder_items_sync (
-                       priv->cnc, EWS_PRIORITY_MEDIUM,
-                       old_sync_state, priv->folder_id,
-                       "IdOnly", "item:ItemClass",
+               ret = e_ews_connection_sync_folder_items_sync (
+                       priv->cnc,
+                       EWS_PRIORITY_MEDIUM,
+                       old_sync_state,
+                       priv->folder_id,
+                       "IdOnly",
+                       "item:ItemClass",
                        EWS_MAX_FETCH_COUNT,
                        &new_sync_state,
                        &includes_last_item,
@@ -3909,48 +4066,63 @@ ews_start_sync_thread (gpointer data)
                        priv->cancellable,
                        &error);
 
-               if (g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) 
{
-                       g_clear_error (&error);
-                       e_cal_backend_store_put_key_value (priv->store, SYNC_KEY, NULL);
-                       cbews_forget_all_components (cbews);
+               g_free (old_sync_state);
+               old_sync_state = NULL;
 
-                       e_ews_connection_sync_folder_items_sync (priv->cnc, EWS_PRIORITY_MEDIUM, NULL, 
priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT,
-                               &new_sync_state, &includes_last_item, &items_created, &items_updated, 
&items_deleted,
-                               priv->cancellable, &error);
-               }
+               if (!ret) {
+                       if (g_error_matches (error, EWS_CONNECTION_ERROR, 
EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
+                               g_clear_error (&error);
+                               e_cal_backend_store_put_key_value (priv->store, SYNC_KEY, NULL);
+                               cbews_forget_all_components (cbews);
 
-               if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
-                   g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_CANCELLED)) {
-                       break;
+                               if (!e_ews_connection_sync_folder_items_sync (
+                                                       priv->cnc,
+                                                       EWS_PRIORITY_MEDIUM,
+                                                       NULL,
+                                                       priv->folder_id,
+                                                       "IdOnly",
+                                                       NULL,
+                                                       EWS_MAX_FETCH_COUNT,
+                                                       &new_sync_state,
+                                                       &includes_last_item,
+                                                       &items_created,
+                                                       &items_updated,
+                                                       &items_deleted,
+                                                       priv->cancellable,
+                                                       &error)) {
+                                       if (!g_error_matches (
+                                                       error,
+                                                       EWS_CONNECTION_ERROR,
+                                                       EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) {
+                                               e_cal_backend_set_writable (E_CAL_BACKEND (cbews), TRUE);
+                                               break;
+                                       }
+                               }
+                       } else {
+                               break;
+                       }
                }
 
-               if (!g_error_matches (error, EWS_CONNECTION_ERROR, 
EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED))
-                       e_cal_backend_set_writable (E_CAL_BACKEND (cbews), TRUE);
+               ret = cal_backend_ews_process_folder_items (
+                               cbews,
+                               new_sync_state,
+                               items_created,
+                               items_updated,
+                               items_deleted);
 
-               if (error == NULL) {
-                       cal_backend_ews_process_folder_items (
-                               cbews, new_sync_state,
-                               items_created, items_updated, items_deleted);
+               if (!ret)
+                       break;
 
-                       g_slist_free_full (items_created, g_object_unref);
-                       g_slist_free_full (items_updated, g_object_unref);
-                       g_slist_free_full (items_deleted, g_free);
-                       items_created = NULL;
-                       items_updated = NULL;
-                       items_deleted = NULL;
-               } else {
-                       g_warn_if_fail (items_created == NULL);
-                       g_warn_if_fail (items_updated == NULL);
-                       g_warn_if_fail (items_deleted == NULL);
+               g_slist_free_full (items_created, g_object_unref);
+               g_slist_free_full (items_updated, g_object_unref);
+               g_slist_free_full (items_deleted, g_free);
+               items_created = NULL;
+               items_updated = NULL;
+               items_deleted = NULL;
 
-                       g_warning ("%s: %s", G_STRFUNC, error->message);
-                       g_error_free (error);
-                       break;
-               }
+               e_cal_backend_store_put_key_value (priv->store, SYNC_KEY, new_sync_state);
 
-               g_free (old_sync_state);
                old_sync_state = new_sync_state;
-               e_cal_backend_store_put_key_value (priv->store, SYNC_KEY, new_sync_state);
                new_sync_state = NULL;
        } while (!includes_last_item);
 
@@ -3960,6 +4132,11 @@ ews_start_sync_thread (gpointer data)
        g_slist_free_full (items_updated, g_object_unref);
        g_slist_free_full (items_deleted, g_free);
 
+       if (error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+               g_clear_error (&error);
+       }
+
        g_free (new_sync_state);
        g_free (old_sync_state);
 
@@ -4399,6 +4576,7 @@ e_cal_backend_ews_dispose (GObject *object)
 {
        ECalBackendEws *cbews;
        ECalBackendEwsPrivate *priv;
+       CamelEwsSettings *ews_settings;
 
        g_return_if_fail (object != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND_EWS (object));
@@ -4406,6 +4584,9 @@ e_cal_backend_ews_dispose (GObject *object)
        cbews = E_CAL_BACKEND_EWS (object);
        priv = cbews->priv;
 
+       ews_settings = cal_backend_ews_get_collection_settings (cbews);
+       g_signal_handlers_disconnect_by_func (ews_settings, cbews_listen_notifications_cb, cbews);
+
        if (priv->refresh_timeout) {
                g_source_remove (priv->refresh_timeout);
                priv->refresh_timeout = 0;
@@ -4418,10 +4599,25 @@ 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 (
+                                       priv->cnc,
+                                       priv->subscription_id,
+                                       NULL,
+                                       NULL);
+
+                       priv->listen_notifications = FALSE;
+               }
+
                g_object_unref (priv->cnc);
                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-provider.c b/src/camel/camel-ews-provider.c
index 47085a8..e9449d2 100644
--- a/src/camel/camel-ews-provider.c
+++ b/src/camel/camel-ews-provider.c
@@ -47,6 +47,8 @@ static CamelProviderConfEntry ews_conf_entries[] = {
          N_("Checking for new mail") },
        { CAMEL_PROVIDER_CONF_CHECKBOX, "check-all", NULL,
          N_("C_heck for new messages in all folders"), "1" },
+       { CAMEL_PROVIDER_CONF_CHECKBOX, "listen-notifications", NULL,
+         N_("_Listen for server change notifications"), "1" },
        { CAMEL_PROVIDER_CONF_SECTION_END },
 
        { CAMEL_PROVIDER_CONF_SECTION_START, "general", NULL, N_("Options") },
diff --git a/src/camel/camel-ews-store.c b/src/camel/camel-ews-store.c
index 233bf0d..bedfca2 100644
--- a/src/camel/camel-ews-store.c
+++ b/src/camel/camel-ews-store.c
@@ -65,6 +65,9 @@
 
 #define FINFO_REFRESH_INTERVAL 60
 
+#define UPDATE_LOCK(x) (g_rec_mutex_lock(&(x)->priv->update_lock))
+#define UPDATE_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->update_lock))
+
 struct _CamelEwsStorePrivate {
        time_t last_refresh_time;
        GMutex get_finfo_lock;
@@ -72,6 +75,15 @@ struct _CamelEwsStorePrivate {
        GMutex connection_lock;
        gboolean has_ooo_set;
        CamelEwsStoreOooAlertState ooo_alert_state;
+
+       gboolean listen_notifications;
+       guint update_folder_id;
+       guint update_folder_list_id;
+       gchar *subscription_id;
+       GCancellable *updates_cancellable;
+       GSList *update_folder_names;
+       GRecMutex update_lock;
+
        GSList *public_folders; /* EEwsFolder * objects */
 };
 
@@ -578,12 +590,9 @@ ews_update_folder_hierarchy (CamelEwsStore *ews_store,
        camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", sync_state);
        camel_ews_store_summary_save (ews_store->summary, NULL);
 
-       g_slist_foreach (folders_created, (GFunc) g_object_unref, NULL);
-       g_slist_foreach (folders_updated, (GFunc) g_object_unref, NULL);
-       g_slist_foreach (folders_deleted, (GFunc) g_free, NULL);
-       g_slist_free (folders_created);
-       g_slist_free (folders_deleted);
-       g_slist_free (folders_updated);
+       g_slist_free_full (folders_created, g_object_unref);
+       g_slist_free_full (folders_updated, g_object_unref);
+       g_slist_free_full (folders_deleted, g_free);
        g_free (sync_state);
 }
 
@@ -593,7 +602,6 @@ ews_update_has_ooo_set (CamelSession *session,
                        gpointer user_data,
                        GError **error)
 {
-
        CamelEwsStore *ews_store = user_data;
        EEwsOofSettings *oof_settings;
        EEwsOofState oof_state;
@@ -624,6 +632,481 @@ ews_update_has_ooo_set (CamelSession *session,
        camel_operation_pop_message (cancellable);
 }
 
+struct ScheduleUpdateData
+{
+       GCancellable *cancellable;
+       CamelEwsStore *ews_store;
+       guint expected_id;
+};
+
+static void
+free_schedule_update_data (gpointer ptr)
+{
+       struct ScheduleUpdateData *sud = ptr;
+
+       if (sud == NULL)
+               return;
+
+       g_clear_object (&sud->cancellable);
+       g_clear_object (&sud->ews_store);
+
+       g_free (sud);
+}
+
+static gpointer
+camel_ews_folder_list_update_thread (gpointer user_data)
+{
+       struct ScheduleUpdateData *sud = user_data;
+       CamelEwsStore *ews_store = sud->ews_store;
+       GSList *created = NULL;
+       GSList *updated = NULL;
+       GSList *deleted = NULL;
+       gchar *old_sync_state = NULL;
+       gchar *new_sync_state;
+       gboolean includes_last;
+
+       if (g_cancellable_is_cancelled (sud->cancellable))
+               goto exit;
+
+       old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL);
+       if (!e_ews_connection_sync_folder_hierarchy_sync (
+                       ews_store->priv->connection,
+                       EWS_PRIORITY_LOW,
+                       old_sync_state,
+                       &new_sync_state,
+                       &includes_last,
+                       &created,
+                       &updated,
+                       &deleted,
+                       sud->cancellable,
+                       NULL))
+               goto exit;
+
+       if (g_cancellable_is_cancelled (sud->cancellable)) {
+               g_slist_free_full (created, g_object_unref);
+               g_slist_free_full (updated, g_object_unref);
+               g_slist_free_full (deleted, g_free);
+               g_free (new_sync_state);
+
+               goto exit;
+       }
+
+       if (created != NULL || updated != NULL || deleted != NULL) {
+               ews_update_folder_hierarchy (
+                               ews_store,
+                               new_sync_state, /* freed in the function */
+                               includes_last,
+                               created, /* freed in the function */
+                               deleted, /* freed in the function */
+                               updated, /* freed in the function */
+                               NULL);
+       } else {
+               g_slist_free_full (created, g_object_unref);
+               g_slist_free_full (updated, g_object_unref);
+               g_slist_free_full (deleted, g_free);
+               g_free (new_sync_state);
+       }
+
+exit:
+       g_free (old_sync_state);
+       free_schedule_update_data (sud);
+       return NULL;
+}
+
+static gpointer
+camel_ews_folder_update_thread (gpointer user_data)
+{
+       struct ScheduleUpdateData *sud = user_data;
+       CamelEwsStore *ews_store = sud->ews_store;
+       GSList *l;
+
+       g_return_val_if_fail (sud != NULL, NULL);
+
+       for (l = ews_store->priv->update_folder_names; l != NULL && !g_cancellable_is_cancelled 
(sud->cancellable); l = l->next) {
+               const gchar *folder_name = l->data;
+               CamelFolder *folder;
+               GError *error = NULL;
+
+               folder = camel_store_get_folder_sync (CAMEL_STORE (ews_store), folder_name, 0, 
sud->cancellable, NULL);
+               if (folder == NULL)
+                       continue;
+
+               camel_folder_refresh_info_sync (folder, sud->cancellable, &error);
+               g_object_unref (folder);
+
+               if (error != NULL) {
+                       g_warning ("%s: %s\n", G_STRFUNC, error->message);
+                       g_clear_error (&error);
+                       break;
+               }
+       }
+
+       g_slist_free_full (ews_store->priv->update_folder_names, g_free);
+       ews_store->priv->update_folder_names = NULL;
+       free_schedule_update_data (sud);
+       return NULL;
+}
+
+static void
+run_update_thread (CamelEwsStore *ews_store,
+                  gboolean folder_list,
+                  GCancellable *cancellable)
+{
+       GThread *thread;
+       struct ScheduleUpdateData *sud;
+
+       g_return_if_fail (ews_store != NULL);
+       g_return_if_fail (cancellable != NULL);
+
+       sud = g_new0 (struct ScheduleUpdateData, 1);
+       sud->ews_store = g_object_ref (ews_store);
+       sud->cancellable = g_object_ref (cancellable);
+
+       thread = g_thread_new (
+               NULL,
+               folder_list ? camel_ews_folder_list_update_thread : camel_ews_folder_update_thread,
+               sud);
+       g_thread_unref (thread);
+}
+
+static gboolean
+folder_update_cb (gpointer user_data)
+{
+       struct ScheduleUpdateData *sud = user_data;
+
+       g_return_val_if_fail (sud != NULL, FALSE);
+
+       if (g_cancellable_is_cancelled (sud->cancellable))
+               return FALSE;
+
+       g_return_val_if_fail (sud->ews_store != NULL, FALSE);
+       g_return_val_if_fail (sud->ews_store->priv != NULL, FALSE);
+
+       UPDATE_LOCK (sud->ews_store);
+       if (sud->expected_id != sud->ews_store->priv->update_folder_id)
+               goto exit;
+
+       sud->ews_store->priv->update_folder_id = 0;
+
+       if (!g_cancellable_is_cancelled (sud->cancellable))
+               run_update_thread (sud->ews_store, FALSE, sud->cancellable);
+
+exit:
+       UPDATE_UNLOCK (sud->ews_store);
+       return FALSE;
+}
+
+static void
+get_folder_names_to_update (gpointer key,
+                           gpointer value,
+                           gpointer user_data)
+{
+       CamelEwsStore *ews_store = user_data;
+       const gchar *folder_id = key;
+       gchar *folder_name;
+
+       folder_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, folder_id, NULL);
+       if (folder_name != NULL)
+               ews_store->priv->update_folder_names = g_slist_prepend (ews_store->priv->update_folder_names, 
folder_name);
+}
+
+static void
+schedule_folder_update (CamelEwsStore *ews_store,
+                       GHashTable *folder_ids)
+{
+       struct ScheduleUpdateData *sud;
+       CamelSettings *settings;
+
+       g_return_if_fail (ews_store != NULL);
+       g_return_if_fail (ews_store->priv != NULL);
+
+       UPDATE_LOCK (ews_store);
+       g_hash_table_foreach (folder_ids, get_folder_names_to_update, ews_store);
+
+       if (ews_store->priv->update_folder_names == NULL)
+               goto exit;
+
+       sud = g_new0 (struct ScheduleUpdateData, 1);
+       sud->ews_store = g_object_ref (ews_store);
+       sud->cancellable = g_object_ref (ews_store->priv->updates_cancellable);
+
+       if (ews_store->priv->update_folder_id > 0)
+               g_source_remove (ews_store->priv->update_folder_id);
+
+       settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store));
+
+       ews_store->priv->update_folder_id = g_timeout_add_seconds_full (
+                                                               G_PRIORITY_LOW,
+                                                               1,
+                                                               folder_update_cb,
+                                                               sud,
+                                                               free_schedule_update_data);
+       sud->expected_id = ews_store->priv->update_folder_id;
+
+       g_object_unref (settings);
+
+exit:
+       UPDATE_UNLOCK (ews_store);
+}
+
+static gboolean
+folder_list_update_cb (gpointer user_data)
+{
+       struct ScheduleUpdateData *sud = user_data;
+
+       g_return_val_if_fail (sud != NULL, FALSE);
+
+       if (g_cancellable_is_cancelled (sud->cancellable))
+               return FALSE;
+
+       g_return_val_if_fail (sud->ews_store != NULL, FALSE);
+       g_return_val_if_fail (sud->ews_store->priv != NULL, FALSE);
+
+       if (sud->expected_id != sud->ews_store->priv->update_folder_list_id)
+               goto exit;
+
+       sud->ews_store->priv->update_folder_list_id = 0;
+
+       if (!g_cancellable_is_cancelled (sud->cancellable))
+               run_update_thread (sud->ews_store, TRUE, sud->cancellable);
+
+exit:
+       return FALSE;
+}
+
+static void
+schedule_folder_list_update (CamelEwsStore *ews_store)
+{
+       struct ScheduleUpdateData *sud;
+       CamelSettings *settings;
+
+       g_return_if_fail (ews_store != NULL);
+       g_return_if_fail (ews_store->priv != NULL);
+
+       UPDATE_LOCK (ews_store);
+       if (!ews_store->priv->updates_cancellable)
+               goto exit;
+
+       sud = g_new0 (struct ScheduleUpdateData, 1);
+       sud->ews_store = g_object_ref (ews_store);
+       sud->cancellable = g_object_ref (ews_store->priv->updates_cancellable);
+
+       if (ews_store->priv->update_folder_list_id > 0)
+               g_source_remove (ews_store->priv->update_folder_list_id);
+
+       settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store));
+
+       ews_store->priv->update_folder_list_id = g_timeout_add_seconds_full (
+                                                               G_PRIORITY_LOW,
+                                                               1,
+                                                               folder_list_update_cb,
+                                                               sud,
+                                                               free_schedule_update_data);
+       sud->expected_id = ews_store->priv->update_folder_list_id;
+
+       g_object_unref (settings);
+
+exit:
+       UPDATE_UNLOCK (ews_store);
+}
+
+static void
+camel_ews_store_server_notification_cb (CamelEwsStore *ews_store,
+                                       GSList *events,
+                                       EEwsConnection *cnc)
+{
+       GSList *l;
+       gboolean update_folder = FALSE;
+       gboolean update_folder_list = FALSE;
+       GHashTable *folder_ids;
+
+       g_return_if_fail (ews_store != NULL);
+       g_return_if_fail (ews_store->priv != NULL);
+
+       folder_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+       for (l = events; l != NULL; l = l->next) {
+               EEwsNotificationEvent *event = l->data;
+
+               switch (event->type) {
+                       case E_EWS_NOTIFICATION_EVENT_CREATED:
+                       case E_EWS_NOTIFICATION_EVENT_DELETED:
+                       case E_EWS_NOTIFICATION_EVENT_MODIFIED:
+                               UPDATE_LOCK (ews_store);
+                               if (event->is_item) {
+                                       update_folder = TRUE;
+                                       if (!g_hash_table_lookup (folder_ids, event->folder_id))
+                                               g_hash_table_insert (
+                                                       folder_ids, g_strdup (event->folder_id), 
GINT_TO_POINTER (1));
+                               } else {
+                                       update_folder_list = TRUE;
+                               }
+                               UPDATE_UNLOCK (ews_store);
+                               break;
+                       case E_EWS_NOTIFICATION_EVENT_MOVED:
+                       case E_EWS_NOTIFICATION_EVENT_COPIED:
+                               UPDATE_LOCK (ews_store);
+                               if (event->is_item) {
+                                       update_folder = TRUE;
+                                       if (!g_hash_table_lookup (folder_ids, event->old_folder_id))
+                                               g_hash_table_insert (
+                                                       folder_ids, g_strdup (event->old_folder_id), 
GINT_TO_POINTER (1));
+
+                                       if (!g_hash_table_lookup (folder_ids, event->folder_id))
+                                               g_hash_table_insert (
+                                                       folder_ids, g_strdup (event->folder_id), 
GINT_TO_POINTER (1));
+                               } else {
+                                       update_folder_list = TRUE;
+                               }
+                               UPDATE_UNLOCK (ews_store);
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       if (update_folder)
+               schedule_folder_update (ews_store, folder_ids);
+       if (update_folder_list)
+               schedule_folder_list_update (ews_store);
+
+       g_hash_table_destroy (folder_ids);
+}
+
+struct HandleNotificationsData {
+       CamelEwsStore *ews_store;
+       GSList *folders;
+};
+
+static void
+handle_notifications_data_free (struct HandleNotificationsData *hnd)
+{
+       if (hnd == NULL)
+               return;
+
+       if (hnd->ews_store)
+               g_object_unref (hnd->ews_store);
+
+       g_slist_free_full (hnd->folders, g_free);
+       g_free (hnd);
+}
+
+static gpointer
+start_notifications_thread (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) {
+               e_ews_connection_enable_notifications_sync (
+                               ews_store->priv->connection,
+                               hnd->folders,
+                               NULL,
+                               &ews_store->priv->subscription_id,
+                               NULL,
+                               NULL);
+       } else {
+               if (ews_store->priv->subscription_id == NULL)
+                       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;
+       }
+
+ exit:
+       handle_notifications_data_free (hnd);
+       return NULL;
+}
+
+static gpointer
+restart_notifications_thread (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;
+
+       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;
+       }
+
+       e_ews_connection_enable_notifications_sync (
+                       ews_store->priv->connection,
+                       hnd->folders,
+                       NULL,
+                       &ews_store->priv->subscription_id,
+                       NULL,
+                       NULL);
+
+ exit:
+       handle_notifications_data_free (hnd);
+       return NULL;
+}
+
+static void
+handle_notifications (CamelEwsStore *ews_store,
+                     CamelEwsSettings *ews_settings,
+                     gboolean restart)
+{
+       GThread *thread;
+       struct HandleNotificationsData *hnd;
+
+       if (ews_store->priv->connection == NULL)
+               return;
+
+       if (!e_ews_connection_satisfies_server_version (ews_store->priv->connection, E_EWS_EXCHANGE_2010_SP1))
+               return;
+
+       hnd = g_new0 (struct HandleNotificationsData, 1);
+       hnd->ews_store = g_object_ref (ews_store);
+
+       if (!camel_ews_settings_get_check_all (ews_settings)) {
+               gchar *inbox;
+
+               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);
+       }
+
+       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);
+       g_thread_unref (thread);
+}
+
+static void
+camel_ews_store_listen_notifications_cb (CamelEwsStore *ews_store,
+                                        GParamSpec *spec,
+                                        CamelEwsSettings *ews_settings)
+{
+       handle_notifications (ews_store, ews_settings, FALSE);
+}
+
+static void
+camel_ews_store_check_all_cb (CamelEwsStore *ews_store,
+                             GParamSpec *spec,
+                             CamelEwsSettings *ews_settings)
+{
+       handle_notifications (ews_store, ews_settings, TRUE);
+}
+
 static gboolean
 ews_connect_sync (CamelService *service,
                   GCancellable *cancellable,
@@ -631,12 +1114,15 @@ ews_connect_sync (CamelService *service,
 {
        EEwsConnection *connection;
        CamelEwsStore *ews_store;
+       CamelEwsStorePrivate *priv;
+       CamelEwsSettings *ews_settings;
        CamelSession *session;
        CamelSettings *settings;
        gchar *auth_mech;
        gboolean success;
 
        ews_store = CAMEL_EWS_STORE (service);
+       priv = ews_store->priv;
 
        if (camel_service_get_connection_status (service) == CAMEL_SERVICE_DISCONNECTED)
                return FALSE;
@@ -649,6 +1135,7 @@ ews_connect_sync (CamelService *service,
 
        session = camel_service_ref_session (service);
        settings = camel_service_ref_settings (service);
+       ews_settings = CAMEL_EWS_SETTINGS (settings);
 
        /* Try running an operation that requires authentication
         * to make sure we have valid credentials available. */
@@ -658,7 +1145,6 @@ ews_connect_sync (CamelService *service,
                           auth_mech ? auth_mech : "NTLM", cancellable, error);
 
        g_free (auth_mech);
-       g_object_unref (settings);
 
        if (success) {
                CamelEwsStoreOooAlertState state;
@@ -671,15 +1157,64 @@ ews_connect_sync (CamelService *service,
                                g_object_ref (ews_store),
                                g_object_unref);
 
+               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)
+                       camel_ews_store_listen_notifications_cb (ews_store, NULL, ews_settings);
+
                camel_offline_store_set_online_sync (
                        CAMEL_OFFLINE_STORE (ews_store),
                        TRUE, cancellable, NULL);
+
+               g_signal_connect_swapped (
+                       priv->connection,
+                       "server-notification",
+                       G_CALLBACK (camel_ews_store_server_notification_cb),
+                       ews_store);
        }
 
+       g_signal_connect_swapped (
+               ews_settings,
+               "notify::listen-notifications",
+               G_CALLBACK (camel_ews_store_listen_notifications_cb),
+               ews_store);
+
+       g_signal_connect_swapped (
+               ews_settings,
+               "notify::check-all",
+               G_CALLBACK (camel_ews_store_check_all_cb),
+               ews_store);
+
        g_object_unref (session);
+       g_object_unref (settings);
+
        return success;
 }
 
+static void
+stop_pending_updates (CamelEwsStore *ews_store)
+{
+       CamelEwsStorePrivate *priv;
+
+       g_return_if_fail (ews_store != NULL);
+       g_return_if_fail (ews_store->priv != NULL);
+
+       priv = ews_store->priv;
+
+       UPDATE_LOCK (ews_store);
+       if (priv->updates_cancellable) {
+               g_cancellable_cancel (priv->updates_cancellable);
+               g_object_unref (priv->updates_cancellable);
+               priv->updates_cancellable = NULL;
+       }
+
+       g_slist_free_full (priv->update_folder_names, g_free);
+       priv->update_folder_names = NULL;
+       UPDATE_UNLOCK (ews_store);
+}
+
 static gboolean
 ews_disconnect_sync (CamelService *service,
                      gboolean clean,
@@ -703,8 +1238,25 @@ ews_disconnect_sync (CamelService *service,
                 *       the first place. */
                settings = camel_service_ref_settings (service);
                g_signal_handlers_disconnect_by_data (settings, service);
+               g_signal_handlers_disconnect_by_func (
+                       ews_store->priv->connection, camel_ews_store_server_notification_cb, ews_store);
+
                g_object_unref (settings);
 
+               if (ews_store->priv->listen_notifications) {
+                       stop_pending_updates (ews_store);
+                       if (ews_store->priv->subscription_id != NULL) {
+                               e_ews_connection_disable_notifications_sync (
+                                               ews_store->priv->connection,
+                                               ews_store->priv->subscription_id,
+                                               cancellable,
+                                               error);
+
+                               g_free (ews_store->priv->subscription_id);
+                               ews_store->priv->subscription_id = NULL;
+                       }
+               }
+
                e_ews_connection_set_password (
                        ews_store->priv->connection, NULL);
                g_object_unref (ews_store->priv->connection);
@@ -2896,9 +3448,15 @@ static void
 ews_store_dispose (GObject *object)
 {
        CamelEwsStore *ews_store;
+       CamelEwsSettings *ews_settings;
 
        ews_store = CAMEL_EWS_STORE (object);
 
+       ews_settings = CAMEL_EWS_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (ews_store)));
+       g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_listen_notifications_cb, 
ews_store);
+       g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_check_all_cb, ews_store);
+       g_object_unref (ews_settings);
+
        if (ews_store->summary != NULL) {
                camel_ews_store_summary_save (ews_store->summary, NULL);
                g_object_unref (ews_store->summary);
@@ -2906,8 +3464,29 @@ ews_store_dispose (GObject *object)
        }
 
        if (ews_store->priv->connection != NULL) {
-               g_object_unref (ews_store->priv->connection);
-               ews_store->priv->connection = NULL;
+               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) {
+                       stop_pending_updates (ews_store);
+                       e_ews_connection_disable_notifications_sync (
+                               ews_store->priv->connection,
+                               ews_store->priv->subscription_id,
+                               NULL,
+                               NULL);
+               }
+
+               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;
        }
 
        if (ews_store->priv->public_folders) {
@@ -2929,6 +3508,7 @@ ews_store_finalize (GObject *object)
        g_free (ews_store->storage_path);
        g_mutex_clear (&ews_store->priv->get_finfo_lock);
        g_mutex_clear (&ews_store->priv->connection_lock);
+       g_rec_mutex_clear (&ews_store->priv->update_lock);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (camel_ews_store_parent_class)->finalize (object);
@@ -3009,6 +3589,11 @@ camel_ews_store_init (CamelEwsStore *ews_store)
                CAMEL_EWS_STORE_GET_PRIVATE (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->update_folder_id = 0;
+       ews_store->priv->update_folder_list_id = 0;
        g_mutex_init (&ews_store->priv->get_finfo_lock);
        g_mutex_init (&ews_store->priv->connection_lock);
+       g_rec_mutex_init (&ews_store->priv->update_lock);
 }
diff --git a/src/camel/camel-ews-utils.c b/src/camel/camel-ews-utils.c
index 9457b03..66e19ad 100644
--- a/src/camel/camel-ews-utils.c
+++ b/src/camel/camel-ews-utils.c
@@ -114,14 +114,16 @@ sync_deleted_folders (CamelEwsStore *store,
                if (ftype == E_EWS_FOLDER_TYPE_MAILBOX) {
                        fi = camel_ews_utils_build_folder_info (store, fid);
 
-                       camel_ews_store_summary_remove_folder (ews_summary, fid, &error);
-
-                       if ((fi->flags & CAMEL_FOLDER_SUBSCRIBED) != 0) {
-                               camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (store), fi);
-                               camel_store_folder_deleted (CAMEL_STORE (store), fi);
+                       if (!camel_ews_store_summary_remove_folder (ews_summary, fid, &error)) {
+                               if (error != NULL) {
+                                       g_warning ("%s: %s", G_STRFUNC, error->message);
+                                       g_clear_error (&error);
+                               }
+                               continue;
                        }
 
-                       g_clear_error (&error);
+                       camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (store), fi);
+                       camel_store_folder_deleted (CAMEL_STORE (store), fi);
                }
        }
 }
diff --git a/src/server/Makefile.am b/src/server/Makefile.am
index c895be7..82ae923 100644
--- a/src/server/Makefile.am
+++ b/src/server/Makefile.am
@@ -51,6 +51,8 @@ libeews_1_2_la_SOURCES = \
        $(KERBEROS_FILES) \
        e-ews-connection.c \
        e-ews-connection.h \
+       e-ews-connection-utils.c \
+       e-ews-connection-utils.g \
        e-ews-debug.c \
        e-ews-debug.h \
        e-ews-enumtypes.c \
@@ -58,6 +60,8 @@ libeews_1_2_la_SOURCES = \
        e-ews-item.c \
        e-ews-item-change.c \
        e-ews-message.c \
+       e-ews-notification.c \
+       e-ews-notification.h \
        e-ews-oof-settings.c \
        e-soap-message.c \
        e-soap-message.h \
@@ -83,6 +87,7 @@ libeewsincludedir = $(privincludedir)/ews
 libeewsinclude_HEADERS = \
        ews-errors.h \
        e-ews-connection.h \
+       e-ews-connection-utils.h \
        e-ews-debug.h \
        e-ews-enums.h \
        e-ews-enumtypes.h \
@@ -90,6 +95,7 @@ libeewsinclude_HEADERS = \
        e-ews-item.h \
        e-ews-item-change.h \
        e-ews-message.h \
+       e-ews-notification.h \
        e-ews-oof-settings.h \
        $(NULL)
 
diff --git a/src/server/camel-ews-settings.c b/src/server/camel-ews-settings.c
index 4db2656..bd6fa9b 100644
--- a/src/server/camel-ews-settings.c
+++ b/src/server/camel-ews-settings.c
@@ -31,6 +31,7 @@
 struct _CamelEwsSettingsPrivate {
        GMutex property_lock;
        gboolean check_all;
+       gboolean listen_notifications;
        gboolean filter_junk;
        gboolean filter_junk_inbox;
        gboolean oab_offline;
@@ -49,6 +50,7 @@ enum {
        PROP_0,
        PROP_AUTH_MECHANISM,
        PROP_CHECK_ALL,
+       PROP_LISTEN_NOTIFICATIONS,
        PROP_EMAIL,
        PROP_FILTER_JUNK,
        PROP_FILTER_JUNK_INBOX,
@@ -93,6 +95,12 @@ ews_settings_set_property (GObject *object,
                                g_value_get_boolean (value));
                        return;
 
+               case PROP_LISTEN_NOTIFICATIONS:
+                       camel_ews_settings_set_listen_notifications (
+                               CAMEL_EWS_SETTINGS (object),
+                               g_value_get_boolean (value));
+                       return;
+
                case PROP_EMAIL:
                        camel_ews_settings_set_email (
                                CAMEL_EWS_SETTINGS (object),
@@ -214,6 +222,13 @@ ews_settings_get_property (GObject *object,
                                CAMEL_EWS_SETTINGS (object)));
                        return;
 
+               case PROP_LISTEN_NOTIFICATIONS:
+                       g_value_set_boolean (
+                               value,
+                               camel_ews_settings_get_listen_notifications (
+                               CAMEL_EWS_SETTINGS (object)));
+                       return;
+
                case PROP_EMAIL:
                        g_value_take_string (
                                value,
@@ -382,6 +397,18 @@ camel_ews_settings_class_init (CamelEwsSettingsClass *class)
 
        g_object_class_install_property (
                object_class,
+               PROP_LISTEN_NOTIFICATIONS,
+               g_param_spec_boolean (
+                       "listen-notifications",
+                       "Listen Notifications",
+                       "Whether to listen for server notification",
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
                PROP_EMAIL,
                g_param_spec_string (
                        "email",
@@ -598,6 +625,47 @@ camel_ews_settings_set_check_all (CamelEwsSettings *settings,
 }
 
 /**
+ * camel_ews_settings_get_listen_notifications:
+ * @settings: a #CamelEwsSettings
+ *
+ * Returns whether to listen for server notifications.
+ *
+ * Returns: whether to listen for server notifications
+ *
+ * Since: 3.12
+ **/
+gboolean
+camel_ews_settings_get_listen_notifications (CamelEwsSettings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), FALSE);
+
+       return settings->priv->listen_notifications;
+}
+
+/**
+ * camel_ews_settings_set_listen_notifications:
+ * @settings: a #CamelEwsSettings
+ * @listen_notifications: whether to listen for server notifications
+ *
+ * Sets whether to listen for server notifications.
+ *
+ * Since: 3.12
+ **/
+void
+camel_ews_settings_set_listen_notifications (CamelEwsSettings *settings,
+                                            gboolean listen_notifications)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings));
+
+       if ((settings->priv->listen_notifications ? 1 : 0) == (listen_notifications ? 1 : 0))
+               return;
+
+       settings->priv->listen_notifications = listen_notifications;
+
+       g_object_notify (G_OBJECT (settings), "listen-notifications");
+}
+
+/**
  * camel_ews_settings_get_folders_initialized:
  * @settings: a #CamelEwsSettings
  *
diff --git a/src/server/camel-ews-settings.h b/src/server/camel-ews-settings.h
index c06e869..acb1751 100644
--- a/src/server/camel-ews-settings.h
+++ b/src/server/camel-ews-settings.h
@@ -61,6 +61,11 @@ gboolean     camel_ews_settings_get_check_all
 void           camel_ews_settings_set_check_all
                                                (CamelEwsSettings *settings,
                                                 gboolean check_all);
+gboolean       camel_ews_settings_get_listen_notifications
+                                               (CamelEwsSettings *settings);
+void           camel_ews_settings_set_listen_notifications
+                                               (CamelEwsSettings *settings,
+                                                gboolean listen_notifications);
 const gchar *  camel_ews_settings_get_email    (CamelEwsSettings *settings);
 gchar *                camel_ews_settings_dup_email    (CamelEwsSettings *settings);
 void           camel_ews_settings_set_email    (CamelEwsSettings *settings,
diff --git a/src/server/e-ews-connection-utils.c b/src/server/e-ews-connection-utils.c
new file mode 100644
index 0000000..e01a316
--- /dev/null
+++ b/src/server/e-ews-connection-utils.c
@@ -0,0 +1,244 @@
+/*
+ * e-ews-connection-utils.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/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "e-ews-connection-utils.h"
+
+#define EWS_GSSAPI_SOUP_SESSION "ews-gssapi-soup-session"
+#define EWS_GSSAPI_SASL                "ews-gssapi-sasl"
+#define EWS_GSSAPI_CONNECTION  "ews-gssapi-connection"
+
+static gchar *
+ews_connection_utils_gssapi_challenge (CamelSasl *sasl,
+                                      const gchar *what,
+                                      gboolean is_base64,
+                                      GError **error)
+{
+       GByteArray *ain, *aout = NULL;
+       gchar *response = NULL;
+       GError *local_error = NULL;
+
+       g_return_val_if_fail (sasl != NULL, NULL);
+
+       ain = g_byte_array_new ();
+
+       if (what && *what) {
+               if (is_base64) {
+                       guchar *bytes;
+                       gsize len = 0;
+
+                       bytes = g_base64_decode (what, &len);
+                       if (bytes) {
+                               g_byte_array_append (ain, bytes, len);
+                               g_free (bytes);
+                       }
+               } else {
+                       g_byte_array_append (ain, (const guchar *) what, strlen (what));
+               }
+       }
+
+       aout = camel_sasl_challenge_sync (sasl, ain, NULL, &local_error);
+
+       if (local_error) {
+               g_propagate_error (error, local_error);
+       } else if (aout && aout->len) {
+               response = g_base64_encode (aout->data, aout->len);
+       } else {
+               response = g_strdup ("");
+       }
+
+       g_byte_array_unref (ain);
+
+       if (aout)
+               g_byte_array_unref (aout);
+
+       return response;
+}
+
+static void
+ews_connection_utils_authenticate_gssapi_cb (SoupMessage *message,
+                                            gpointer user_data)
+{
+       EEwsConnection *connection = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_CONNECTION);
+       SoupSession *session = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_SOUP_SESSION);
+       CamelSasl *sasl = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_SASL);
+       const gchar *auths_lst;
+       gchar **auths;
+       gint ii;
+
+       g_return_if_fail (E_IS_EWS_CONNECTION (connection));
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (CAMEL_IS_SASL (sasl));
+
+       if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code))
+               return;
+
+       auths_lst = soup_message_headers_get_list (message->response_headers, "WWW-Authenticate");
+       if (!auths_lst)
+               return;
+
+       auths = g_strsplit (auths_lst, ",", -1);
+       for (ii = 0; auths && auths[ii]; ii++) {
+               if (g_ascii_strncasecmp (auths[ii], "Negotiate", 9) == 0) {
+                       GError *error = NULL;
+                       const gchar *chlg = auths[ii] + 9;
+                       gchar *response;
+
+                       if (*chlg)
+                               chlg++;
+                       if (!*chlg)
+                               chlg = NULL;
+
+                       response = ews_connection_utils_gssapi_challenge (
+                               sasl, chlg ? chlg : "\r\n", chlg != NULL, &error);
+
+                       if (response && *response) {
+                               gchar *sasl_response = g_strconcat ("Negotiate ", response, NULL);
+
+                               soup_message_headers_remove (message->request_headers, "Authorization");
+                               soup_message_headers_append (message->request_headers, "Authorization", 
sasl_response);
+                               soup_session_requeue_message (session, message);
+
+                               g_free (sasl_response);
+                       } else if (error) {
+                               /* cannot use SOUP_STATUS_UNAUTHORIZED, because it may hide an error message,
+                                  which is a local error of Kerberos/GSSAPI call */
+                               soup_message_set_status_full (message, SOUP_STATUS_BAD_REQUEST, 
error->message);
+                       }
+
+                       g_free (response);
+                       break;
+               }
+       }
+
+       g_strfreev (auths);
+}
+
+void
+e_ews_connection_utils_setup_msg_gssapi_auth (EEwsConnection *connection,
+                                             SoupSession *session,
+                                             SoupMessage *message)
+{
+       CamelSasl *gssapi_sasl;
+       CamelEwsSettings *ews_settings;
+       CamelNetworkSettings *network_settings;
+       SoupURI *soup_uri;
+       const gchar *host, *user;
+
+       if (!camel_sasl_gssapi_is_available ())
+               return;
+
+       g_return_if_fail (E_IS_EWS_CONNECTION (connection));
+       g_return_if_fail (SOUP_IS_MESSAGE (message));
+
+       ews_settings = e_ews_connection_ref_settings (connection);
+       network_settings = CAMEL_NETWORK_SETTINGS (ews_settings);
+       gssapi_sasl = g_object_new (
+               camel_sasl_gssapi_get_type (),
+               "mechanism", "GSSAPI",
+               "service-name", "HTTP",
+               NULL);
+
+       soup_uri = soup_message_get_uri (message);
+       host = soup_uri_get_host (soup_uri);
+       user = soup_uri_get_user (soup_uri);
+       if (!host || !*host)
+               host = camel_network_settings_get_host (network_settings);
+       if (!user || !*user)
+               user = camel_network_settings_get_user (network_settings);
+
+       camel_sasl_gssapi_override_host_and_user (CAMEL_SASL_GSSAPI (gssapi_sasl), host, user);
+
+       /* this might not be a cyclic ref dependency, as long as the message
+          is properly served through the session and freed */
+       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_SOUP_SESSION,
+               g_object_ref (session), g_object_unref);
+       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_CONNECTION,
+               g_object_ref (connection), e_ews_connection_utils_unref_in_thread);
+       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_SASL,
+               gssapi_sasl, g_object_unref);
+
+       soup_message_add_header_handler (message, "got_body", "WWW-Authenticate",
+               G_CALLBACK (ews_connection_utils_authenticate_gssapi_cb), NULL);
+
+       g_object_unref (ews_settings);
+}
+
+static gpointer
+ews_unref_in_thread_func (gpointer data)
+{
+       g_object_unref (G_OBJECT (data));
+
+       return NULL;
+}
+
+void
+e_ews_connection_utils_unref_in_thread (gpointer object)
+{
+       GThread *thread;
+
+       g_return_if_fail (G_IS_OBJECT (object));
+
+       thread = g_thread_new (NULL, ews_unref_in_thread_func, object);
+       g_thread_unref (thread);
+}
+
+gboolean
+e_ews_connection_utils_auth_mech_to_use_ntlm (GBinding *binding,
+                                             const GValue *source_value,
+                                             GValue *target_value,
+                                             gpointer user_data)
+{
+       const gchar *auth_mechanism;
+       gboolean use_ntlm;
+
+       /* Use NTLM unless the auth mechanism is "PLAIN" or "GSSAPI". */
+       auth_mechanism = g_value_get_string (source_value);
+       use_ntlm = g_strcmp0 (auth_mechanism, "PLAIN") != 0 &&
+                  g_strcmp0 (auth_mechanism, "GSSAPI") != 0;
+       g_value_set_boolean (target_value, use_ntlm);
+
+       return TRUE;
+}
+
+/* Do not call this directly, use E_EWS_CONNECTION_UTILS_CHECK_ELEMENT macro instead. */
+gboolean
+e_ews_connection_utils_check_element (const gchar *function_name,
+                                     const gchar *element_name,
+                                     const gchar *expected_name)
+{
+       g_return_val_if_fail (function_name != NULL, FALSE);
+       g_return_val_if_fail (element_name != NULL, FALSE);
+       g_return_val_if_fail (expected_name != NULL, FALSE);
+
+       if (!g_str_equal (element_name, expected_name)) {
+               g_warning (
+                       "%s: Expected <%s> but got <%s>",
+                       function_name, expected_name, element_name);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
diff --git a/src/server/e-ews-connection-utils.h b/src/server/e-ews-connection-utils.h
new file mode 100644
index 0000000..7cc2022
--- /dev/null
+++ b/src/server/e-ews-connection-utils.h
@@ -0,0 +1,48 @@
+/*
+ * e-ews-connection-utils.h
+ *
+ * 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/>
+ *
+ */
+
+#ifndef E_EWS_CONNECTION_UTILS_H
+#define E_EWS_CONNECTION_UTILS_H
+
+#include <glib.h>
+#include <server/e-ews-connection.h>
+
+G_BEGIN_DECLS
+
+#define E_EWS_CONNECTION_UTILS_CHECK_ELEMENT(element_name, expected_name) \
+       (e_ews_connection_utils_check_element (G_STRFUNC, (element_name), (expected_name)))
+
+void           e_ews_connection_utils_setup_msg_gssapi_auth
+                                                       (EEwsConnection *connection,
+                                                        SoupSession *session,
+                                                        SoupMessage *message);
+void           e_ews_connection_utils_unref_in_thread  (gpointer object);
+gboolean       e_ews_connection_utils_auth_mech_to_use_ntlm
+                                                       (GBinding *binding,
+                                                        const GValue *source_value,
+                                                        GValue *target_value,
+                                                        gpointer user_data);
+gboolean       e_ews_connection_utils_check_element    (const gchar *function_name,
+                                                        const gchar *element_name,
+                                                        const gchar *expected_name);
+
+G_END_DECLS
+
+#endif /* E_EWS_CONNECTION_UTILS_H */
+
+
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index fee3d5c..be02aca 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -42,9 +41,11 @@
 #include <libxml/tree.h>
 
 #include "e-ews-connection.h"
+#include "e-ews-connection-utils.h"
 #include "e-ews-message.h"
 #include "e-ews-item-change.h"
 #include "e-ews-debug.h"
+#include "e-ews-notification.h"
 
 #define d(x) x
 
@@ -58,9 +59,6 @@
 #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 CHECK_ELEMENT(element_name, expected_name) \
-       (check_element (G_STRFUNC, (element_name), (expected_name)))
-
 struct _EwsNode;
 static GMutex connecting;
 static GHashTable *loaded_connections_permissions = NULL;
@@ -82,6 +80,7 @@ struct _EEwsConnectionPrivate {
        GMainLoop *soup_loop;
        GMainContext *soup_context;
        GProxyResolver *proxy_resolver;
+       EEwsNotification *notification;
 
        CamelEwsSettings *settings;
        GMutex property_lock;
@@ -108,8 +107,26 @@ enum {
        PROP_SETTINGS
 };
 
+enum {
+       SERVER_NOTIFICATION,
+       LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL];
+
+static const gchar *default_events_names[] = {
+       "CopiedEvent",
+       "CreatedEvent",
+       "DeletedEvent",
+       "ModifiedEvent",
+       "MovedEvent",
+       "StatusEvent",
+       NULL};
+
 typedef struct _EwsNode EwsNode;
 typedef struct _EwsAsyncData EwsAsyncData;
+typedef struct _EwsEventsAsyncData EwsEventsAsyncData;
+typedef struct _EwsNotificationThreadData EwsNotificationThreadData;
 
 struct _EwsAsyncData {
        GSList *items_created;
@@ -127,6 +144,10 @@ struct _EwsAsyncData {
        EEwsConnection *cnc;
 };
 
+struct _EwsEventsAsyncData {
+       gchar *subscription_id;
+};
+
 struct _EwsNode {
        ESoapMessage *msg;
        EEwsConnection *cnc;
@@ -139,6 +160,11 @@ struct _EwsNode {
        gulong cancel_handler_id;
 };
 
+struct _EwsNotificationThreadData {
+       EEwsConnection *cnc;
+       gchar *subscription_id;
+};
+
 /* Forward Declarations */
 static void    e_ews_connection_authenticator_init
                                (ESourceAuthenticatorInterface *interface);
@@ -166,68 +192,32 @@ ews_connection_error_quark (void)
        return quark;
 }
 
-static gboolean
-ews_auth_mech_to_use_ntlm (GBinding *binding,
-                           const GValue *source_value,
-                           GValue *target_value,
-                           gpointer user_data)
-{
-       const gchar *auth_mechanism;
-       gboolean use_ntlm;
-
-       /* Use NTLM unless the auth mechanism is "PLAIN" or "GSSAPI". */
-       auth_mechanism = g_value_get_string (source_value);
-       use_ntlm = g_strcmp0 (auth_mechanism, "PLAIN") != 0 &&
-                  g_strcmp0 (auth_mechanism, "GSSAPI") != 0;
-       g_value_set_boolean (target_value, use_ntlm);
-
-       return TRUE;
-}
-
-static gpointer
-ews_unref_in_thread_func (gpointer data)
-{
-       g_object_unref (G_OBJECT (data));
-
-       return NULL;
-}
-
-void
-e_ews_connection_utils_unref_in_thread (gpointer object)
+static void
+async_data_free (EwsAsyncData *async_data)
 {
-       GThread *thread;
-
-       g_return_if_fail (G_IS_OBJECT (object));
-
-       thread = g_thread_new (NULL, ews_unref_in_thread_func, object);
-       g_thread_unref (thread);
+       g_free (async_data);
 }
 
 static void
-async_data_free (EwsAsyncData *async_data)
+events_async_data_free (EwsEventsAsyncData *async_data)
 {
        g_free (async_data);
 }
 
-static gboolean
-check_element (const gchar *function_name,
-               const gchar *element_name,
-               const gchar *expected_name)
+EEwsNotificationEvent *
+e_ews_notification_event_new (void)
 {
-       /* Do not call this directory, use CHECK_ELEMENT macro instead. */
-
-       g_return_val_if_fail (function_name != NULL, FALSE);
-       g_return_val_if_fail (element_name != NULL, FALSE);
-       g_return_val_if_fail (expected_name != NULL, FALSE);
+       return g_new0 (EEwsNotificationEvent, 1);
+}
 
-       if (!g_str_equal (element_name, expected_name)) {
-               g_warning (
-                       "%s: Expected <%s> but got <%s>",
-                       function_name, expected_name, element_name);
-               return FALSE;
+void
+e_ews_notification_event_free (EEwsNotificationEvent *event)
+{
+       if (event != NULL) {
+               g_free (event->folder_id);
+               g_free (event->old_folder_id);
+               g_free (event);
        }
-
-       return TRUE;
 }
 
 static EwsNode *
@@ -264,7 +254,6 @@ comp_func (gconstpointer a,
 {
        EwsNode *node1 = (EwsNode *) a;
        EwsNode *node2 = (EwsNode *) b;
-
        if (node1->pri > node2->pri)
                return -1;
        else if (node1->pri < node2->pri)
@@ -273,164 +262,6 @@ comp_func (gconstpointer a,
                return 0;
 }
 
-static gchar *
-ews_connection_gssapi_challenge (CamelSasl *sasl,
-                                const gchar *what,
-                                gboolean is_base64,
-                                GError **error)
-{
-       GByteArray *ain, *aout = NULL;
-       gchar *response = NULL;
-       GError *local_error = NULL;
-
-       g_return_val_if_fail (sasl != NULL, NULL);
-
-       ain = g_byte_array_new ();
-
-       if (what && *what) {
-               if (is_base64) {
-                       guchar *bytes;
-                       gsize len = 0;
-
-                       bytes = g_base64_decode (what, &len);
-                       if (bytes) {
-                               g_byte_array_append (ain, bytes, len);
-                               g_free (bytes);
-                       }
-               } else {
-                       g_byte_array_append (ain, (const guchar *) what, strlen (what));
-               }
-       }
-
-       aout = camel_sasl_challenge_sync (sasl, ain, NULL, &local_error);
-
-       if (local_error) {
-               g_propagate_error (error, local_error);
-       } else if (aout && aout->len) {
-               response = g_base64_encode (aout->data, aout->len);
-       } else {
-               response = g_strdup ("");
-       }
-
-       g_byte_array_unref (ain);
-
-       if (aout)
-               g_byte_array_unref (aout);
-
-       return response;
-}
-
-#define EWS_GSSAPI_SOUP_SESSION "ews-gssapi-soup-session"
-#define EWS_GSSAPI_SASL                "ews-gssapi-sasl"
-#define EWS_GSSAPI_CONNECTION  "ews-gssapi-connection"
-
-static void
-ews_connection_authenticate_gssapi_cb (SoupMessage *message,
-                                      gpointer user_data)
-{
-       EEwsConnection *connection = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_CONNECTION);
-       SoupSession *session = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_SOUP_SESSION);
-       CamelSasl *sasl = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_SASL);
-       const gchar *auths_lst;
-       gchar **auths;
-       gint ii;
-
-       g_return_if_fail (E_IS_EWS_CONNECTION (connection));
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (CAMEL_IS_SASL (sasl));
-
-       if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code))
-               return;
-
-       auths_lst = soup_message_headers_get_list (message->response_headers, "WWW-Authenticate");
-       if (!auths_lst)
-               return;
-
-       auths = g_strsplit (auths_lst, ",", -1);
-       for (ii = 0; auths && auths[ii]; ii++) {
-               if (g_ascii_strncasecmp (auths[ii], "Negotiate", 9) == 0) {
-                       GError *error = NULL;
-                       const gchar *chlg = auths[ii] + 9;
-                       gchar *response;
-
-                       if (*chlg)
-                               chlg++;
-                       if (!*chlg)
-                               chlg = NULL;
-
-                       response = ews_connection_gssapi_challenge (sasl, chlg ? chlg : "\r\n", chlg != NULL, 
&error);
-
-                       if (response && *response) {
-                               gchar *sasl_response = g_strconcat ("Negotiate ", response, NULL);
-
-                               soup_message_headers_remove (message->request_headers, "Authorization");
-                               soup_message_headers_append (message->request_headers, "Authorization", 
sasl_response);
-                               soup_session_requeue_message (session, message);
-
-                               g_free (sasl_response);
-                       } else if (error) {
-                               /* cannot use SOUP_STATUS_UNAUTHORIZED, because it may hide an error message,
-                                  which is a local error of Kerberos/GSSAPI call */
-                               soup_message_set_status_full (message, SOUP_STATUS_BAD_REQUEST, 
error->message);
-                       }
-
-                       g_free (response);
-                       break;
-               }
-       }
-
-       g_strfreev (auths);
-}
-
-static void
-ews_connection_setup_msg_gssapi_auth (EEwsConnection *connection,
-                                     SoupSession *session,
-                                     SoupMessage *message)
-{
-       CamelSasl *gssapi_sasl;
-       CamelEwsSettings *ews_settings;
-       CamelNetworkSettings *network_settings;
-       SoupURI *soup_uri;
-       const gchar *host, *user;
-
-       if (!camel_sasl_gssapi_is_available ())
-               return;
-
-       g_return_if_fail (E_IS_EWS_CONNECTION (connection));
-       g_return_if_fail (SOUP_IS_MESSAGE (message));
-
-       ews_settings = e_ews_connection_ref_settings (connection);
-       network_settings = CAMEL_NETWORK_SETTINGS (ews_settings);
-       gssapi_sasl = g_object_new (camel_sasl_gssapi_get_type (),
-                       "mechanism", "GSSAPI",
-                       "service-name", "HTTP",
-                       NULL);
-
-       soup_uri = soup_message_get_uri (message);
-       host = soup_uri_get_host (soup_uri);
-       user = soup_uri_get_user (soup_uri);
-       if (!host || !*host)
-               host = camel_network_settings_get_host (network_settings);
-       if (!user || !*user)
-               user = camel_network_settings_get_user (network_settings);
-
-       camel_sasl_gssapi_override_host_and_user (CAMEL_SASL_GSSAPI (gssapi_sasl), host, user);
-
-       /* this might not be a cyclic ref dependency, as long as the message
-          is properly served through the session and freed */
-       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_SOUP_SESSION,
-               g_object_ref (session), g_object_unref);
-       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_CONNECTION,
-               g_object_ref (connection), e_ews_connection_utils_unref_in_thread);
-       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_SASL,
-               gssapi_sasl, g_object_unref);
-
-       soup_message_add_header_handler (message, "got_body", "WWW-Authenticate",
-               G_CALLBACK (ews_connection_authenticate_gssapi_cb), NULL);
-
-       g_object_unref (ews_settings);
-}
-
 typedef enum _EwsScheduleOp {
        EWS_SCHEDULE_OP_QUEUE_MESSAGE,
        EWS_SCHEDULE_OP_CANCEL,
@@ -610,7 +441,7 @@ ews_next_request (gpointer _cnc)
 
                g_object_get (G_OBJECT (ews_settings), "auth-mechanism", &auth_mech, NULL);
                if (g_strcmp0 (auth_mech, "GSSAPI") == 0)
-                       ews_connection_setup_msg_gssapi_auth (cnc, cnc->priv->soup_session, msg);
+                       e_ews_connection_utils_setup_msg_gssapi_auth (cnc, cnc->priv->soup_session, msg);
 
                g_object_unref (ews_settings);
                g_free (auth_mech);
@@ -920,7 +751,7 @@ sync_hierarchy_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "SyncFolderHierarchyResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SyncFolderHierarchyResponseMessage"))
                        sync_xxx_response_cb (
                                subparam, async_data,
                                (ItemParser) e_ews_folder_new_from_soap_parameter,
@@ -964,7 +795,7 @@ sync_folder_items_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "SyncFolderItemsResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SyncFolderItemsResponseMessage"))
                        sync_xxx_response_cb (
                                subparam, async_data,
                                (ItemParser) e_ews_item_new_from_soap_parameter,
@@ -1036,7 +867,7 @@ get_folder_response_cb (ESoapResponse *response,
                                g_simple_async_result_take_error (simple, error);
                                return;
                        }
-               } else if (CHECK_ELEMENT (name, "GetFolderResponseMessage"))
+               } else if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage"))
                        ews_handle_folders_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -1111,7 +942,7 @@ find_folder_items_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "FindItemResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "FindItemResponseMessage"))
                        ews_handle_root_folder_param_items (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -1273,7 +1104,7 @@ resolve_names_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "ResolveNamesResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "ResolveNamesResponseMessage"))
                        ews_handle_resolution_set_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -1347,7 +1178,7 @@ expand_dl_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "ExpandDLResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "ExpandDLResponseMessage"))
                        ews_handle_dl_expansion_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -1432,7 +1263,7 @@ create_folder_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "CreateFolderResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "CreateFolderResponseMessage"))
                        ews_handle_create_folders_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -1614,7 +1445,6 @@ ews_connection_try_password_sync (ESourceAuthenticator *authenticator,
 
        if (local_error == NULL) {
                result = E_SOURCE_AUTHENTICATION_ACCEPTED;
-
        } else {
                gboolean auth_failed;
 
@@ -1682,6 +1512,15 @@ e_ews_connection_class_init (EEwsConnectionClass *class)
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT_ONLY |
                        G_PARAM_STATIC_STRINGS));
+
+       signals[SERVER_NOTIFICATION] = g_signal_new (
+               "server-notification",
+               G_OBJECT_CLASS_TYPE (object_class),
+               G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__POINTER,
+               G_TYPE_NONE, 1,
+               G_TYPE_POINTER);
 }
 
 static void
@@ -2139,7 +1978,7 @@ e_ews_connection_new_full (const gchar *uri,
                settings, "auth-mechanism",
                cnc->priv->soup_session, "use-ntlm",
                G_BINDING_SYNC_CREATE,
-               ews_auth_mech_to_use_ntlm,
+               e_ews_connection_utils_auth_mech_to_use_ntlm,
                NULL,
                NULL, (GDestroyNotify) NULL);
 
@@ -3658,7 +3497,8 @@ e_ews_connection_sync_folder_items (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
        e_soap_message_start_element (msg, "ItemShape", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props);
 
@@ -3862,7 +3702,8 @@ e_ews_connection_find_folder_items (EEwsConnection *cnc,
                        "Shallow",
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
        e_soap_message_start_element (msg, "ItemShape", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props);
 
@@ -3993,7 +3834,8 @@ e_ews_connection_sync_folder_hierarchy (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
        e_soap_message_start_element (msg, "FolderShape", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "AllProperties");
        e_soap_message_end_element (msg);
@@ -4266,7 +4108,8 @@ e_ews_connection_get_items (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        if (progress_fn && progress_data)
                e_soap_message_set_progress_fn (msg, progress_fn, progress_data);
@@ -4527,7 +4370,8 @@ e_ews_connection_delete_items (EEwsConnection *cnc,
                        ews_delete_type_to_str (delete_type),
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        if (send_cancels)
                e_soap_message_add_attribute (
@@ -4590,7 +4434,8 @@ e_ews_connection_delete_item (EEwsConnection *cnc,
                        ews_delete_type_to_str (delete_type),
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        if (send_cancels)
                e_soap_message_add_attribute (
@@ -4832,7 +4677,8 @@ e_ews_connection_update_items (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        if (conflict_res)
                e_soap_message_add_attribute (
@@ -5000,7 +4846,8 @@ e_ews_connection_create_items (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        if (msg_disposition)
                e_soap_message_add_attribute (
@@ -5162,7 +5009,8 @@ e_ews_connection_resolve_names (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_add_attribute (msg, "SearchScope", get_search_scope_str (scope), NULL, NULL);
 
@@ -5448,7 +5296,8 @@ e_ews_connection_expand_dl (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "Mailbox", "messages", NULL);
 
@@ -5600,7 +5449,8 @@ e_ews_connection_update_folder (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "FolderChanges", "messages", NULL);
 
@@ -5731,7 +5581,8 @@ e_ews_connection_move_folder (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "ToFolderId", "messages", NULL);
        if (to_folder)
@@ -5842,6 +5693,7 @@ e_ews_connection_get_folder (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
+                       TRUE,
                        TRUE);
 
        e_soap_message_start_element (msg, "FolderShape", "messages", NULL);
@@ -5962,7 +5814,8 @@ e_ews_connection_create_folder (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "ParentFolderId", "messages", NULL);
 
@@ -6120,7 +5973,8 @@ e_ews_connection_move_items (EEwsConnection *cnc,
                                NULL,
                                cnc->priv->version,
                                E_EWS_EXCHANGE_2007_SP1,
-                               FALSE);
+                               FALSE,
+                               TRUE);
        else
                msg = e_ews_message_new_with_header (
                                cnc->priv->uri,
@@ -6130,7 +5984,8 @@ e_ews_connection_move_items (EEwsConnection *cnc,
                                NULL,
                                cnc->priv->version,
                                E_EWS_EXCHANGE_2007_SP1,
-                               FALSE);
+                               FALSE,
+                               TRUE);
 
        e_soap_message_start_element (msg, "ToFolderId", "messages", NULL);
        e_soap_message_start_element (msg, "FolderId", NULL, NULL);
@@ -6301,7 +6156,8 @@ e_ews_connection_delete_folder (EEwsConnection *cnc,
                        delete_type,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "FolderIds", "messages", NULL);
 
@@ -6463,7 +6319,8 @@ e_ews_connection_empty_folder (EEwsConnection *cnc,
                        delete_type,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2010,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_add_attribute (msg, "DeleteSubFolders", delete_subfolders ? "true" : "false", NULL, 
NULL);
 
@@ -6618,7 +6475,7 @@ create_attachments_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "CreateAttachmentResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "CreateAttachmentResponseMessage"))
                        ews_handle_create_attachments_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -6729,7 +6586,8 @@ e_ews_connection_create_attachments (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "ParentItemId", "messages", NULL);
        e_soap_message_add_attribute (msg, "Id", parent->id, NULL, NULL);
@@ -6882,7 +6740,7 @@ delete_attachments_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "DeleteAttachmentResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "DeleteAttachmentResponseMessage"))
                        ews_handle_root_item_id_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -6912,7 +6770,8 @@ e_ews_connection_delete_attachments (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        /* start interation over all items to get the attachemnts */
        e_soap_message_start_element (msg, "AttachmentIds", "messages", NULL);
@@ -7065,7 +6924,7 @@ get_attachments_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "GetAttachmentResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetAttachmentResponseMessage"))
                        ews_handle_attachments_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -7100,7 +6959,8 @@ e_ews_connection_get_attachments (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        /* not sure why I need it, need to check */
        if (progress_fn && progress_data)
@@ -7375,7 +7235,8 @@ e_ews_connection_get_free_busy (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        free_busy_cb (msg, free_busy_user_data);
 
@@ -7630,7 +7491,7 @@ get_delegate_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "DelegateUserResponseMessageType"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "DelegateUserResponseMessageType"))
                        ews_handle_delegate_user_param (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -7660,7 +7521,8 @@ e_ews_connection_get_delegate (EEwsConnection *cnc,
                        include_permissions ? "true" : "false",
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "Mailbox", "messages", NULL);
 
@@ -7837,7 +7699,8 @@ e_ews_connection_add_delegate (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "Mailbox", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : 
cnc->priv->email);
@@ -7967,7 +7830,8 @@ e_ews_connection_remove_delegate (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "Mailbox", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : 
cnc->priv->email);
@@ -8077,7 +7941,8 @@ e_ews_connection_update_delegate (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "Mailbox", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : 
cnc->priv->email);
@@ -8224,7 +8089,7 @@ get_folder_permissions_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "GetFolderResponseMessage")) {
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) {
                        ESoapParameter *node;
 
                        node = e_soap_parameter_get_first_child_by_name (subparam, "Folders");
@@ -8270,7 +8135,8 @@ e_ews_connection_get_folder_permissions (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "FolderShape", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "IdOnly");
@@ -8388,7 +8254,8 @@ e_ews_connection_set_folder_permissions (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "FolderChanges", "messages", NULL);
        e_ews_message_start_item_change (
@@ -8635,7 +8502,8 @@ e_ews_connection_get_password_expiration (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2010_SP2,
-                       FALSE);
+                       FALSE,
+                       TRUE);
        e_ews_message_write_string_parameter (msg, "MailboxSmtpAddress", NULL, mail_id ? mail_id : 
cnc->priv->email);
        e_ews_message_write_footer (msg);
 
@@ -8756,7 +8624,7 @@ get_folder_info_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "GetFolderResponseMessage")) {
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) {
                        ESoapParameter *node;
 
                        node = e_soap_parameter_get_first_child_by_name (subparam, "Folders");
@@ -8798,7 +8666,8 @@ e_ews_connection_get_folder_info (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
 
        e_soap_message_start_element (msg, "FolderShape", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "Default");
@@ -8963,7 +8832,7 @@ find_folder_response_cb (ESoapResponse *response,
                        return;
                }
 
-               if (CHECK_ELEMENT (name, "FindFolderResponseMessage"))
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "FindFolderResponseMessage"))
                        ews_handle_root_folder_param_folders (subparam, async_data);
 
                subparam = e_soap_parameter_get_next_child (subparam);
@@ -8992,7 +8861,8 @@ e_ews_connection_find_folder (EEwsConnection *cnc,
                        "Shallow",
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
-                       FALSE);
+                       FALSE,
+                       TRUE);
        e_soap_message_start_element (msg, "FolderShape", "messages", NULL);
        e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "Default");
        e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL);
@@ -9173,6 +9043,7 @@ e_ews_connection_query_auth_methods (EEwsConnection *cnc,
                        NULL,
                        cnc->priv->version,
                        E_EWS_EXCHANGE_2007_SP1,
+                       TRUE,
                        TRUE);
 
        e_soap_message_start_element (msg, "FolderShape", "messages", NULL);
@@ -9258,3 +9129,414 @@ e_ews_connection_query_auth_methods_sync (EEwsConnection *cnc,
 
        return success;
 }
+
+static void
+subscribe_folder_response_cb (ESoapResponse *response,
+                             GSimpleAsyncResult *simple)
+{
+       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;
+
+                       node = e_soap_parameter_get_first_child_by_name (subparam, "SubscriptionId");
+                       async_data->subscription_id = e_soap_parameter_get_string_value (node);
+               }
+
+               subparam = e_soap_parameter_get_next_child (subparam);
+       }
+}
+
+void
+e_ews_connection_subscribe_folder (EEwsConnection *cnc,
+                                  gint pri,
+                                  GSList *folders,
+                                  GSList *events,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+       ESoapMessage *msg;
+       GSimpleAsyncResult *simple;
+       EwsEventsAsyncData *async_data;
+       GSList *l;
+
+       g_return_if_fail (cnc != NULL);
+       g_return_if_fail (cnc->priv != 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);
+
+       /*
+        * 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;
+       }
+
+       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);
+
+       e_soap_message_start_element (msg, "StreamingSubscriptionRequest", "messages", NULL);
+
+       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 */
+       }
+
+       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]);
+               }
+       }
+
+       ret = e_ews_connection_subscribe_folder_sync (
+               cnc,
+               EWS_PRIORITY_MEDIUM,
+               folders,
+               events != NULL ? events : default_events,
+               subscription_id,
+               cancellable,
+               error);
+
+       if (!ret)
+               goto exit;
+
+       ent = g_new0 (EwsNotificationThreadData, 1);
+       ent->cnc = cnc;
+       ent->subscription_id = g_strdup (*subscription_id);
+
+       cnc->priv->notification = e_ews_notification_new (cnc);
+
+       thread = g_thread_new (NULL, e_ews_connection_notification_thread, ent);
+       g_thread_unref (thread);
+
+exit:
+       g_slist_free (default_events);
+       return ret;
+}
+
+gboolean
+e_ews_connection_disable_notifications_sync (EEwsConnection *cnc,
+                                            const gchar *subscription_id,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+       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);
+
+       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 (!ret) {
+               EwsNotificationThreadData *ent;
+               GThread *thread;
+
+               ent = g_new0 (EwsNotificationThreadData, 1);
+               ent->cnc = cnc;
+               ent->subscription_id = g_strdup (subscription_id);
+
+               thread = g_thread_new (NULL, e_ews_connection_notification_thread, ent);
+               g_thread_unref (thread);
+
+               goto exit;
+       }
+
+       g_clear_object (&cnc->priv->notification);
+
+exit:
+       return ret;
+}
diff --git a/src/server/e-ews-connection.h b/src/server/e-ews-connection.h
index 288c824..51dfefd 100644
--- a/src/server/e-ews-connection.h
+++ b/src/server/e-ews-connection.h
@@ -205,6 +205,26 @@ typedef struct {
        gsize len;
 } EwsPhotoAttachmentInfo;
 
+typedef enum {
+       E_EWS_NOTIFICATION_EVENT_COPIED = 0,
+       E_EWS_NOTIFICATION_EVENT_CREATED,
+       E_EWS_NOTIFICATION_EVENT_DELETED,
+       E_EWS_NOTIFICATION_EVENT_MODIFIED,
+       E_EWS_NOTIFICATION_EVENT_MOVED,
+       E_EWS_NOTIFICATION_EVENT_STATUS,
+} EEwsNotificationEventType;
+
+typedef struct {
+       EEwsNotificationEventType type;
+       gboolean is_item;
+       gchar *folder_id;
+       gchar *old_folder_id;
+} EEwsNotificationEvent;
+
+EEwsNotificationEvent *
+               e_ews_notification_event_new    (void);
+void           e_ews_notification_event_free   (EEwsNotificationEvent *event);
+
 void           ews_oal_free                    (EwsOAL *oal);
 void           ews_oal_details_free            (EwsOALDetails *details);
 
@@ -1077,6 +1097,56 @@ 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
+                                               (EEwsConnection *cnc,
+                                                GSList *folders,
+                                                GSList *events,
+                                                gchar **subscription_id,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_ews_connection_disable_notifications_sync
+                                               (EEwsConnection *cnc,
+                                                const gchar *subscription_id,
+                                                GCancellable *cancellable,
+                                                GError **error);
 
 G_END_DECLS
 
diff --git a/src/server/e-ews-message.c b/src/server/e-ews-message.c
index 7d353b2..207c63e 100644
--- a/src/server/e-ews-message.c
+++ b/src/server/e-ews-message.c
@@ -37,14 +37,15 @@ e_ews_message_new_with_header (const gchar *uri,
                                const gchar *attribute_value,
                               EEwsServerVersion server_version,
                                EEwsServerVersion minimum_version,
-                              gboolean force_minimum_version)
+                              gboolean force_minimum_version,
+                              gboolean standard_handlers)
 {
        ESoapMessage *msg;
        const gchar *server_ver = "Exchange2007";
        EEwsServerVersion version;
 
        msg = e_soap_message_new (
-               SOUP_METHOD_POST, uri, FALSE, NULL, NULL, NULL);
+               SOUP_METHOD_POST, uri, FALSE, NULL, NULL, NULL, standard_handlers);
        if (msg == NULL) {
                g_warning (G_STRLOC ": Could not build SOAP message");
                return NULL;
diff --git a/src/server/e-ews-message.h b/src/server/e-ews-message.h
index 28884a6..43bbbde 100644
--- a/src/server/e-ews-message.h
+++ b/src/server/e-ews-message.h
@@ -45,7 +45,8 @@ ESoapMessage *        e_ews_message_new_with_header   (const gchar *uri,
                                                 const gchar *attribute_value,
                                                 EEwsServerVersion server_version,
                                                 EEwsServerVersion minimum_version,
-                                                gboolean force_minimum_version);
+                                                gboolean force_minimum_version,
+                                                gboolean standard_handlers);
 void           e_ews_message_write_string_parameter
                                                (ESoapMessage *msg,
                                                 const gchar *name,
diff --git a/src/server/e-ews-notification.c b/src/server/e-ews-notification.c
new file mode 100644
index 0000000..e399480
--- /dev/null
+++ b/src/server/e-ews-notification.c
@@ -0,0 +1,531 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "e-ews-connection-utils.h"
+#include "e-ews-debug.h"
+#include "e-ews-notification.h"
+
+#define E_EWS_NOTIFICATION_GET_PRIVATE(obj)\
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_EWS_NOTIFICATION, EEwsNotificationPrivate))
+
+G_DEFINE_TYPE (EEwsNotification, e_ews_notification, G_TYPE_OBJECT)
+
+struct _EEwsNotificationPrivate {
+       SoupSession *soup_session;
+       EEwsConnection *connection; /* not referred */
+       ESoapMessage *msg;
+       GByteArray *chunk;
+};
+
+enum {
+       PROP_0,
+       PROP_CONNECTION
+};
+
+static const gchar *default_events_names[] = {
+       "CopiedEvent",
+       "CreatedEvent",
+       "DeletedEvent",
+       "ModifiedEvent",
+       "MovedEvent",
+       "StatusEvent",
+       NULL};
+
+static void
+ews_notification_authenticate (SoupSession *session,
+                              SoupMessage *message,
+                              SoupAuth *auth,
+                              gboolean retrying,
+                              gpointer data)
+{
+       EEwsNotification *notification = data;
+       EEwsConnection *connection;
+       CamelNetworkSettings *network_settings;
+       gchar *user, *password;
+
+       g_return_if_fail (notification != NULL);
+       g_return_if_fail (notification->priv->connection != NULL);
+
+       connection = notification->priv->connection;
+
+       if (retrying)
+               e_ews_connection_set_password (connection, NULL);
+
+       network_settings = CAMEL_NETWORK_SETTINGS (e_ews_connection_ref_settings (connection));
+       user = camel_network_settings_dup_user (network_settings);
+
+       password = e_ews_connection_dup_password (connection);
+
+       if (password != NULL)
+               soup_auth_authenticate (auth, user, password);
+
+       g_free (password);
+       g_free (user);
+       g_object_unref (network_settings);
+
+}
+
+EEwsNotification *
+e_ews_notification_new (EEwsConnection *connection)
+{
+       EEwsNotification *notification;
+       CamelEwsSettings *ews_settings;
+
+       g_return_val_if_fail (E_IS_EWS_CONNECTION (connection), NULL);
+
+       notification = g_object_new (
+               E_TYPE_EWS_NOTIFICATION,
+               "connection", connection, NULL);
+
+       ews_settings = e_ews_connection_ref_settings (connection);
+
+       g_object_bind_property_full (
+               ews_settings, "auth-mechanism",
+               notification->priv->soup_session, "use-ntlm",
+               G_BINDING_SYNC_CREATE,
+               e_ews_connection_utils_auth_mech_to_use_ntlm,
+               NULL,
+               NULL, (GDestroyNotify) NULL);
+
+       g_object_unref (ews_settings);
+
+       return notification;
+}
+
+static void
+e_ews_notification_set_connection (EEwsNotification *notification,
+                                  EEwsConnection *connection)
+{
+       g_return_if_fail (E_IS_EWS_NOTIFICATION (notification));
+       g_return_if_fail (E_IS_EWS_CONNECTION (connection));
+       g_return_if_fail (notification->priv->connection == NULL);
+
+       notification->priv->connection = connection;
+       g_object_weak_ref (
+               G_OBJECT (notification->priv->connection),
+               (GWeakNotify) g_nullify_pointer,
+               &notification->priv->connection);
+}
+
+static void
+ews_notification_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CONNECTION:
+                       e_ews_notification_set_connection (
+                               E_EWS_NOTIFICATION (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       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,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CONNECTION:
+                       g_value_take_object (
+                               value,
+                               e_ews_notification_get_connection (
+                               E_EWS_NOTIFICATION (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ews_notification_dispose (GObject *object)
+{
+       EEwsNotificationPrivate *priv;
+
+       priv = E_EWS_NOTIFICATION_GET_PRIVATE (object);
+
+       if (priv->soup_session) {
+               g_signal_handlers_disconnect_by_func (
+                       priv->soup_session,
+                       ews_notification_authenticate, object);
+       }
+
+       if (priv->connection) {
+               g_object_weak_unref (
+                       G_OBJECT (priv->connection),
+                       (GWeakNotify) g_nullify_pointer,
+                       &priv->connection);
+               priv->connection = NULL;
+       }
+}
+
+static void
+e_ews_notification_class_init (EEwsNotificationClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EEwsNotificationPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = ews_notification_set_property;
+       object_class->get_property = ews_notification_get_property;
+       object_class->dispose = ews_notification_dispose;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CONNECTION,
+               g_param_spec_object (
+                       "connection",
+                       "Connection",
+                       "Connection",
+                       E_TYPE_EWS_CONNECTION,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_ews_notification_init (EEwsNotification *notification)
+{
+       gint log_level;
+
+       notification->priv = E_EWS_NOTIFICATION_GET_PRIVATE (notification);
+
+       notification->priv->soup_session = soup_session_sync_new_with_options (
+               SOUP_SESSION_USE_NTLM, TRUE,
+               NULL);
+
+       log_level = e_ews_debug_get_log_level ();
+       if (log_level >= 2) {
+               SoupLogger *logger;
+               logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+
+               soup_session_add_feature (notification->priv->soup_session, SOUP_SESSION_FEATURE (logger));
+               g_object_unref (logger);
+       }
+
+       g_signal_connect (notification->priv->soup_session, "authenticate", G_CALLBACK 
(ews_notification_authenticate), notification);
+}
+
+static EEwsNotificationEvent *
+get_folder_event_info (ESoapParameter *param,
+                      EEwsNotificationEventType event_type)
+{
+       ESoapParameter *subparam;
+       EEwsNotificationEvent *event;
+
+       event = e_ews_notification_event_new ();
+
+       event->type = event_type;
+       event->is_item = FALSE;
+
+       subparam = e_soap_parameter_get_first_child_by_name (param, "FolderId");
+       event->folder_id = e_soap_parameter_get_property (subparam, "Id");
+
+       subparam = e_soap_parameter_get_first_child_by_name (param, "OldFolderId");
+       if (subparam != NULL) {
+               event->old_folder_id = e_soap_parameter_get_property (subparam, "Id");
+       }
+
+       return event;
+}
+
+static EEwsNotificationEvent *
+get_item_event_info (ESoapParameter *param,
+                    EEwsNotificationEventType event_type)
+{
+       ESoapParameter *subparam;
+       EEwsNotificationEvent *event;
+
+       event = e_ews_notification_event_new ();
+
+       event->type = event_type;
+       event->is_item = TRUE;
+
+       subparam = e_soap_parameter_get_first_child_by_name (param, "ParentFolderId");
+       event->folder_id = e_soap_parameter_get_property (subparam, "Id");
+
+       subparam = e_soap_parameter_get_first_child_by_name (param, "OldParentFolderId");
+       if (subparam != NULL) {
+               event->old_folder_id = e_soap_parameter_get_property (subparam, "Id");
+       }
+
+       return event;
+}
+
+static EEwsNotificationEvent *
+get_event_info (ESoapParameter *param,
+               EEwsNotificationEventType event_type)
+{
+       ESoapParameter *subparam;
+
+       subparam = e_soap_parameter_get_first_child_by_name (param, "ItemId");
+       return (subparam != NULL) ? get_item_event_info (param, event_type) : get_folder_event_info (param, 
event_type);
+}
+
+static void
+ews_notification_handle_events_param (ESoapParameter *node,
+                                     GSList **events)
+{
+       ESoapParameter *param;
+       EEwsNotificationEvent *event;
+       guint event_type;
+
+       param = e_soap_parameter_get_first_child_by_name (node, "SubscriptionId");
+
+       for (param = e_soap_parameter_get_next_child (param); param != NULL; param = 
e_soap_parameter_get_next_child (param)) {
+               for (event_type = 0; default_events_names[event_type] != NULL; event_type++) {
+                       if (g_strcmp0 ((const gchar *) param->name, default_events_names[event_type]) == 0)
+                               break;
+
+                       continue;
+               }
+
+               if (default_events_names[event_type] == NULL)
+                       continue;
+
+               if (event_type != E_EWS_NOTIFICATION_EVENT_STATUS) {
+                       event = get_event_info (param, event_type);
+                       *events = g_slist_prepend (*events, event);
+               }
+       }
+
+       *events = g_slist_reverse (*events);
+}
+
+static void
+ews_notification_fire_events_from_response (EEwsNotification *notification,
+                                           ESoapResponse *response)
+{
+       ESoapParameter *param, *subparam;
+       GSList *events = NULL;
+       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_warning (G_STRLOC ": %s\n", error->message);
+               g_error_free (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_warning (G_STRLOC ": %s\n", error->message);
+                       g_error_free (error);
+                       g_slist_free_full (events, (GDestroyNotify) e_ews_notification_event_free);
+                       return;
+               }
+
+               if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetStreamingEventsResponseMessage")) {
+                       ESoapParameter *node, *node2;
+
+                       node = e_soap_parameter_get_first_child_by_name (subparam, "Notifications");
+                       if (node) {
+                               node2 = e_soap_parameter_get_first_child_by_name (node, "Notification");
+                               if (node2)
+                                       ews_notification_handle_events_param (node2, &events);
+                       }
+               }
+
+               subparam = e_soap_parameter_get_next_child (subparam);
+       }
+
+       if (events != NULL) {
+               g_signal_emit_by_name (notification->priv->connection, "server-notification", events);
+               g_slist_free_full (events, (GDestroyNotify) e_ews_notification_event_free);
+       }
+}
+
+static void
+ews_notification_soup_got_chunk (SoupMessage *msg,
+                                SoupBuffer *chunk,
+                                gpointer user_data)
+{
+       EEwsNotification *notification = user_data;
+       const gchar *chunk_str;
+       gsize chunk_len;
+       gboolean keep_parsing = TRUE;
+
+       /*
+        * Here we receive, in chunks, "well-formed" messages that contain:
+        * <Envelope>...</Envelope><Envelope>...</Envelope><Envelope>....
+        *
+        * We need to treat chunks which don't end with </Envelope> (it can be
+        * also in the middle of the chunk, or even cut into two pieces by chunk
+        * division -- one part already read, the other just arriving)
+        *
+        * We are parsing those chunks in the following way:
+        * 1. Append newly arrived chunk->data to notification->priv->chunk->data
+        * 2. Search for </Envelope> in notification->priv->chunk->data
+        * 3.1 </Envelope> is not found: Do nothing. Waiting for the next chunk
+        * 3.2 </Envelope> is found: Get the pair <Envelope>...</Envelope> and handle it
+        * 4. Update the notification->priv->chunk->{data,len}, removing the pair used in 3.2
+        * 5. Repeat from 2, until that 3.1 happens
+        */
+       if (notification->priv->chunk == NULL)
+               notification->priv->chunk = g_byte_array_new ();
+
+       notification->priv->chunk =
+               g_byte_array_append (notification->priv->chunk, (guint8 *) chunk->data, chunk->length);
+
+       chunk_str = (gchar *) notification->priv->chunk->data;
+       chunk_len = notification->priv->chunk->len;
+
+       do {
+               ESoapResponse *response;
+               const gchar *end;
+               gsize len;
+
+               end = g_strstr_len (chunk_str, chunk_len, "</Envelope>");
+
+               if (end == NULL)
+                       break;
+
+               len = end + strlen ("</Envelope>") - chunk_str;
+
+               response = e_soap_response_new_from_string (chunk_str, len);
+               if (response == NULL)
+                       break;
+
+               ews_notification_fire_events_from_response (notification, response);
+               g_object_unref (response);
+
+               notification->priv->chunk = g_byte_array_remove_range (notification->priv->chunk, 0, len);
+
+               chunk_str = (gchar *) notification->priv->chunk->data;
+               chunk_len = notification->priv->chunk->len;
+
+               if (chunk_len > 0) {
+                       g_byte_array_free (notification->priv->chunk, TRUE);
+                       notification->priv->chunk = NULL;
+                       keep_parsing = FALSE;
+               }
+       } while (keep_parsing);
+}
+
+/*
+ * e_ews_notification_get_events_sync
+ *
+ * @param notification:
+ * @param pri
+ * @param subscription_id
+ * @param cancellable
+ * @param user_data
+ */
+guint
+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;
+
+       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 (
+               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,
+               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_soap_message_end_element (msg); /* SubscriptionIds */
+
+       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 (
+               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);
+
+       return ret;
+}
+
+void
+e_ews_notification_cancel_get_events (EEwsNotification *notification)
+{
+       soup_session_cancel_message (
+               notification->priv->soup_session,
+               SOUP_MESSAGE (notification->priv->msg),
+               SOUP_STATUS_CANCELLED);
+}
diff --git a/src/server/e-ews-notification.h b/src/server/e-ews-notification.h
new file mode 100644
index 0000000..86107d5
--- /dev/null
+++ b/src/server/e-ews-notification.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef E_EWS_NOTIFICATION_H
+#define E_EWS_NOTIFICATION_H
+
+#include <glib-object.h>
+#include "e-ews-connection.h"
+
+#define E_TYPE_EWS_NOTIFICATION \
+       (e_ews_notification_get_type ())
+#define E_EWS_NOTIFICATION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_EWS_NOTIFICATION, EEwsNotification))
+#define E_EWS_NOTIFICATION_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST \
+        ((klass), E_TYPE_EWS_NOTIFICATION, EEwsNotificationClass))
+#define E_IS_EWS_NOTIFICATION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_EWS_NOTIFICATION))
+#define E_IS_EWS_NOTIFICATION_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((klass), E_TYPE_EWS_NOTIFICATION))
+
+G_BEGIN_DECLS
+
+typedef struct _EEwsNotification EEwsNotification;
+typedef struct _EEwsNotificationClass EEwsNotificationClass;
+typedef struct _EEwsNotificationPrivate EEwsNotificationPrivate;
+
+struct _EEwsNotification {
+       GObject parent;
+       EEwsNotificationPrivate *priv;
+};
+
+struct _EEwsNotificationClass {
+       GObjectClass parent_class;
+};
+
+GType          e_ews_notification_get_type     (void);
+EEwsNotification *
+               e_ews_notification_new          (EEwsConnection *connection);
+
+guint          e_ews_notification_get_events_sync
+                                               (EEwsNotification *notification,
+                                                gint pri,
+                                                const gchar *subscription_id);
+void           e_ews_notification_cancel_get_events
+                                               (EEwsNotification *notification);
+
+
+G_END_DECLS
+
+#endif /* E_EWS_NOTIFICATION_H */
diff --git a/src/server/e-ews-oof-settings.c b/src/server/e-ews-oof-settings.c
index 34805c7..2e26531 100644
--- a/src/server/e-ews-oof-settings.c
+++ b/src/server/e-ews-oof-settings.c
@@ -483,7 +483,8 @@ ews_oof_settings_initable_init_async (GAsyncInitable *initable,
                NULL,
                version,
                E_EWS_EXCHANGE_2007_SP1,
-               FALSE);
+               FALSE,
+               TRUE);
 
        e_soap_message_start_element (message, "Mailbox", NULL, NULL);
        e_ews_message_write_string_parameter (
@@ -999,7 +1000,8 @@ e_ews_oof_settings_submit (EEwsOofSettings *settings,
                NULL,
                version,
                E_EWS_EXCHANGE_2007_SP1,
-               FALSE);
+               FALSE,
+               TRUE);
 
        /* <Mailbox> */
 
diff --git a/src/server/e-soap-message.c b/src/server/e-soap-message.c
index c737b1b..a96a464 100644
--- a/src/server/e-soap-message.c
+++ b/src/server/e-soap-message.c
@@ -331,7 +331,8 @@ e_soap_message_new (const gchar *method,
                     gboolean standalone,
                     const gchar *xml_encoding,
                     const gchar *env_prefix,
-                    const gchar *env_uri)
+                    const gchar *env_uri,
+                   gboolean standard_handlers)
 {
        ESoapMessage *msg;
        SoupURI *uri;
@@ -358,9 +359,11 @@ e_soap_message_new (const gchar *method,
                                FALSE);
        }
 
-       g_signal_connect (msg, "got-headers", G_CALLBACK (soap_got_headers), NULL);
-       g_signal_connect (msg, "got-chunk", G_CALLBACK (soap_got_chunk), NULL);
-       g_signal_connect (msg, "restarted", G_CALLBACK (soap_restarted), NULL);
+       if (standard_handlers) {
+               g_signal_connect (msg, "got-headers", G_CALLBACK (soap_got_headers), NULL);
+               g_signal_connect (msg, "got-chunk", G_CALLBACK (soap_got_chunk), NULL);
+               g_signal_connect (msg, "restarted", G_CALLBACK (soap_restarted), NULL);
+       }
 
        return msg;
 }
diff --git a/src/server/e-soap-message.h b/src/server/e-soap-message.h
index b2e0fa1..459bca7 100644
--- a/src/server/e-soap-message.h
+++ b/src/server/e-soap-message.h
@@ -51,7 +51,8 @@ ESoapMessage *        e_soap_message_new              (const gchar *method,
                                                 gboolean standalone,
                                                 const gchar *xml_encoding,
                                                 const gchar *env_prefix,
-                                                const gchar *env_uri);
+                                                const gchar *env_uri,
+                                                gboolean standard_handlers);
 ESoapMessage * e_soap_message_new_from_uri     (const gchar *method,
                                                 SoupURI *uri,
                                                 gboolean standalone,
diff --git a/src/server/e-soap-response.c b/src/server/e-soap-response.c
index 3549962..939e283 100644
--- a/src/server/e-soap-response.c
+++ b/src/server/e-soap-response.c
@@ -96,6 +96,7 @@ e_soap_response_new (void)
 /**
  * e_soap_response_new_from_string:
  * @xmlstr: the XML string to parse.
+ * @xmlstr_len: XML string's length or -1 for a null-terminated string
  *
  * Create a new #ESoapResponse object from the XML string contained
  * in @xmlstr.
@@ -104,14 +105,15 @@ e_soap_response_new (void)
  * error).
  */
 ESoapResponse *
-e_soap_response_new_from_string (const gchar *xmlstr)
+e_soap_response_new_from_string (const gchar *xmlstr,
+                                gint xmlstr_length)
 {
        ESoapResponse *response;
 
        g_return_val_if_fail (xmlstr != NULL, NULL);
 
        response = g_object_new (E_TYPE_SOAP_RESPONSE, NULL);
-       if (!e_soap_response_from_string (response, xmlstr)) {
+       if (!e_soap_response_from_string (response, xmlstr, xmlstr_length)) {
                g_object_unref (response);
                return NULL;
        }
@@ -169,6 +171,7 @@ parse_parameters (ESoapResponse *response,
  * e_soap_response_from_string:
  * @response: the #ESoapResponse object.
  * @xmlstr: XML string to parse.
+ * @xmlstr_len: XML string's length or -1 for a null-terminated string
  *
  * Parses the string contained in @xmlstr and sets all properties from
  * it in the @response object.
@@ -177,7 +180,8 @@ parse_parameters (ESoapResponse *response,
  */
 gboolean
 e_soap_response_from_string (ESoapResponse *response,
-                             const gchar *xmlstr)
+                             const gchar *xmlstr,
+                            gint xmlstr_length)
 {
        xmlDocPtr xmldoc;
 
@@ -185,7 +189,7 @@ e_soap_response_from_string (ESoapResponse *response,
        g_return_val_if_fail (xmlstr != NULL, FALSE);
 
        /* parse the string */
-       xmldoc = xmlParseMemory (xmlstr, strlen (xmlstr));
+       xmldoc = xmlParseMemory (xmlstr, xmlstr_length == -1 ? strlen (xmlstr) : xmlstr_length);
        if (!xmldoc)
                return FALSE;
 
diff --git a/src/server/e-soap-response.h b/src/server/e-soap-response.h
index a9ddc0d..1184379 100644
--- a/src/server/e-soap-response.h
+++ b/src/server/e-soap-response.h
@@ -45,10 +45,12 @@ struct _ESoapResponseClass {
 
 GType          e_soap_response_get_type        (void) G_GNUC_CONST;
 ESoapResponse *        e_soap_response_new             (void);
-ESoapResponse *        e_soap_response_new_from_string (const gchar *xmlstr);
+ESoapResponse *        e_soap_response_new_from_string (const gchar *xmlstr,
+                                                gint xmlstr_length);
 ESoapResponse *        e_soap_response_new_from_xmldoc (xmlDoc *xmldoc);
 gboolean       e_soap_response_from_string     (ESoapResponse *response,
-                                                const gchar *xmlstr);
+                                                const gchar *xmlstr,
+                                                gint xmlstr_length);
 gboolean       e_soap_response_from_xmldoc     (ESoapResponse *response,
                                                 xmlDoc *xmldoc);
 const gchar *  e_soap_response_get_method_name (ESoapResponse *response);


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