[evolution-ews] Bug #699349 - Listen for server change notifications
- From: Fabiano Fidêncio <ffidencio src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews] Bug #699349 - Listen for server change notifications
- Date: Tue, 12 Nov 2013 11:07:22 +0000 (UTC)
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,
+ ¬ification->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]