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