[evolution-ews/gnome-3-36] EEwsNotification: Change how subscription to server change notifications work
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews/gnome-3-36] EEwsNotification: Change how subscription to server change notifications work
- Date: Thu, 21 May 2020 14:06:02 +0000 (UTC)
commit bbd1f8e84dfb30aa74a9e535aa5feb46cf4439ed
Author: Milan Crha <mcrha redhat com>
Date: Thu May 21 16:02:32 2020 +0200
EEwsNotification: Change how subscription to server change notifications work
* the subscription ID is independent of the connection, it can survive
disconnects, thus it should try to unsubscribe, if any existed
* the connection (EEwsConnection object) is shared between backends
within one project and each of the backends can subscribe for change
notifications, which can eventually mean a flood of server requests
shortly after the backends connect to the server; this change adds
a short delay before the notification is started, which gives time
to the backends to register with their folders
This is partly related to a downstream bug:
https://bugzilla.redhat.com/show_bug.cgi?id=1823240
src/addressbook/e-book-backend-ews.c | 30 ++++++
src/calendar/e-cal-backend-ews.c | 31 ++++++
src/camel/camel-ews-store.c | 32 +++++++
src/server/e-ews-connection.c | 177 +++++++++++++++++++++++++++++++++--
src/server/e-ews-connection.h | 5 +
src/server/e-ews-notification.c | 39 +++++++-
src/server/e-ews-notification.h | 3 +-
7 files changed, 306 insertions(+), 11 deletions(-)
---
diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c
index 7f91dc81..8497933e 100644
--- a/src/addressbook/e-book-backend-ews.c
+++ b/src/addressbook/e-book-backend-ews.c
@@ -96,6 +96,11 @@ struct _EBookBackendEwsPrivate {
guint subscription_key;
+ /* The subscription ID is not tight to the actual connection, it survives
+ disconnects, thus remember it and pass it back to the new connection,
+ thus it can eventually unsubscribe from it. */
+ gchar *last_subscription_id;
+
/* used for storing attachments */
gchar *attachments_dir;
};
@@ -3221,6 +3226,25 @@ ebb_ews_check_is_gal (EBookBackendEws *bbews)
return is_gal;
}
+static void
+ebb_ews_subscription_id_changed_cb (EEwsConnection *cnc,
+ const gchar *subscription_id,
+ gpointer user_data)
+{
+ EBookBackendEws *bbews = user_data;
+
+ g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews));
+
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
+
+ if (g_strcmp0 (bbews->priv->last_subscription_id, subscription_id) != 0) {
+ g_free (bbews->priv->last_subscription_id);
+ bbews->priv->last_subscription_id = g_strdup (subscription_id);
+ }
+
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
+}
+
static gboolean
ebb_ews_connect_sync (EBookMetaBackend *meta_backend,
const ENamedParameters *credentials,
@@ -3306,6 +3330,11 @@ ebb_ews_connect_sync (EBookMetaBackend *meta_backend,
folders = g_slist_prepend (folders, bbews->priv->folder_id);
+ e_ews_connection_set_last_subscription_id (bbews->priv->cnc,
bbews->priv->last_subscription_id);
+
+ g_signal_connect_object (bbews->priv->cnc, "subscription-id-changed",
+ G_CALLBACK (ebb_ews_subscription_id_changed_cb), bbews, 0);
+
e_ews_connection_enable_notifications_sync (bbews->priv->cnc,
folders, &bbews->priv->subscription_key);
@@ -4049,6 +4078,7 @@ e_book_backend_ews_finalize (GObject *object)
g_free (bbews->priv->folder_id);
g_free (bbews->priv->attachments_dir);
+ g_free (bbews->priv->last_subscription_id);
g_rec_mutex_clear (&bbews->priv->cnc_lock);
diff --git a/src/calendar/e-cal-backend-ews.c b/src/calendar/e-cal-backend-ews.c
index dfd02fa4..88bbe38a 100644
--- a/src/calendar/e-cal-backend-ews.c
+++ b/src/calendar/e-cal-backend-ews.c
@@ -70,6 +70,12 @@ struct _ECalBackendEwsPrivate {
gchar *folder_id;
guint subscription_key;
+
+ /* The subscription ID is not tight to the actual connection, it survives
+ disconnects, thus remember it and pass it back to the new connection,
+ thus it can eventually unsubscribe from it. */
+ gchar *last_subscription_id;
+
gboolean is_freebusy_calendar;
gchar *attachments_dir;
@@ -1643,6 +1649,25 @@ ecb_ews_can_send_invitations (ECalBackendEws *cbews,
return ecb_ews_organizer_is_user (cbews, comp);
}
+static void
+ecb_ews_subscription_id_changed_cb (EEwsConnection *cnc,
+ const gchar *subscription_id,
+ gpointer user_data)
+{
+ ECalBackendEws *cbews = user_data;
+
+ g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews));
+
+ g_rec_mutex_lock (&cbews->priv->cnc_lock);
+
+ if (g_strcmp0 (cbews->priv->last_subscription_id, subscription_id) != 0) {
+ g_free (cbews->priv->last_subscription_id);
+ cbews->priv->last_subscription_id = g_strdup (subscription_id);
+ }
+
+ g_rec_mutex_unlock (&cbews->priv->cnc_lock);
+}
+
static gboolean
ecb_ews_connect_sync (ECalMetaBackend *meta_backend,
const ENamedParameters *credentials,
@@ -1705,6 +1730,11 @@ ecb_ews_connect_sync (ECalMetaBackend *meta_backend,
folders = g_slist_prepend (folders, cbews->priv->folder_id);
+ e_ews_connection_set_last_subscription_id (cbews->priv->cnc,
cbews->priv->last_subscription_id);
+
+ g_signal_connect_object (cbews->priv->cnc, "subscription-id-changed",
+ G_CALLBACK (ecb_ews_subscription_id_changed_cb), cbews, 0);
+
e_ews_connection_enable_notifications_sync (cbews->priv->cnc,
folders, &cbews->priv->subscription_key);
@@ -4332,6 +4362,7 @@ ecb_ews_finalize (GObject *object)
g_free (cbews->priv->folder_id);
g_free (cbews->priv->attachments_dir);
+ g_free (cbews->priv->last_subscription_id);
g_rec_mutex_clear (&cbews->priv->cnc_lock);
diff --git a/src/camel/camel-ews-store.c b/src/camel/camel-ews-store.c
index 7dd9e085..acc6384a 100644
--- a/src/camel/camel-ews-store.c
+++ b/src/camel/camel-ews-store.c
@@ -77,6 +77,12 @@ struct _CamelEwsStorePrivate {
gboolean listen_notifications;
guint subscription_key;
+
+ /* The subscription ID is not tight to the actual connection, it survives
+ disconnects, thus remember it and pass it back to the new connection,
+ thus it can eventually unsubscribe from it. */
+ gchar *last_subscription_id;
+
guint update_folder_id;
guint update_folder_list_id;
GCancellable *updates_cancellable;
@@ -1427,6 +1433,25 @@ camel_ews_store_check_all_cb (CamelEwsStore *ews_store,
camel_ews_store_handle_notifications (ews_store, ews_settings);
}
+static void
+ews_camel_subscription_id_changed_cb (EEwsConnection *cnc,
+ const gchar *subscription_id,
+ gpointer user_data)
+{
+ CamelEwsStore *ews_store = user_data;
+
+ g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store));
+
+ g_mutex_lock (&ews_store->priv->connection_lock);
+
+ if (g_strcmp0 (ews_store->priv->last_subscription_id, subscription_id) != 0) {
+ g_free (ews_store->priv->last_subscription_id);
+ ews_store->priv->last_subscription_id = g_strdup (subscription_id);
+ }
+
+ g_mutex_unlock (&ews_store->priv->connection_lock);
+}
+
static gboolean
ews_connect_sync (CamelService *service,
GCancellable *cancellable,
@@ -1506,6 +1531,12 @@ ews_connect_sync (CamelService *service,
"server-notification",
G_CALLBACK (camel_ews_store_server_notification_cb),
ews_store);
+
+ e_ews_connection_set_last_subscription_id (connection,
ews_store->priv->last_subscription_id);
+
+ g_signal_connect_object (connection, "subscription-id-changed",
+ G_CALLBACK (ews_camel_subscription_id_changed_cb), ews_store, 0);
+
g_clear_object (&connection);
}
}
@@ -4000,6 +4031,7 @@ ews_store_finalize (GObject *object)
ews_store = CAMEL_EWS_STORE (object);
g_free (ews_store->storage_path);
+ g_free (ews_store->priv->last_subscription_id);
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);
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index 127b436f..55660237 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -82,6 +82,7 @@ struct _EEwsConnectionPrivate {
GMainContext *soup_context;
GProxyResolver *proxy_resolver;
EEwsNotification *notification;
+ guint notification_delay_id;
CamelEwsSettings *settings;
guint concurrent_connections;
@@ -102,6 +103,10 @@ struct _EEwsConnectionPrivate {
GHashTable *subscriptions;
GSList *subscribed_folders;
+ /* The subscription ID is not tight to the actual connection, it survives
+ disconnects, thus remember it and unsubscribe from it, before adding
+ a new subscription. */
+ gchar *last_subscription_id;
EEwsServerVersion version;
gboolean backoff_enabled;
@@ -126,7 +131,8 @@ enum {
enum {
SERVER_NOTIFICATION,
PASSWORD_WILL_EXPIRE,
- LAST_SIGNAL,
+ SUBSCRIPTION_ID_CHANGED,
+ LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
@@ -2052,6 +2058,11 @@ ews_connection_dispose (GObject *object)
g_mutex_unlock (&connecting);
NOTIFICATION_LOCK (cnc);
+ if (cnc->priv->notification_delay_id) {
+ g_source_remove (cnc->priv->notification_delay_id);
+ cnc->priv->notification_delay_id = 0;
+ }
+
if (cnc->priv->notification) {
e_ews_notification_stop_listening_sync (cnc->priv->notification);
g_clear_object (&cnc->priv->notification);
@@ -2110,6 +2121,7 @@ ews_connection_finalize (GObject *object)
g_free (priv->hash_key);
g_free (priv->impersonate_user);
g_free (priv->ssl_certificate_pem);
+ g_free (priv->last_subscription_id);
g_clear_object (&priv->bearer_auth);
@@ -2214,6 +2226,15 @@ e_ews_connection_class_init (EEwsConnectionClass *class)
G_TYPE_NONE, 2,
G_TYPE_INT,
G_TYPE_STRING);
+
+ signals[SUBSCRIPTION_ID_CHANGED] = g_signal_new (
+ "subscription-id-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
}
static void
@@ -3015,6 +3036,38 @@ e_ews_connection_set_disconnected_flag (EEwsConnection *cnc,
cnc->priv->disconnected_flag = disconnected_flag;
}
+gchar *
+e_ews_connection_dup_last_subscription_id (EEwsConnection *cnc)
+{
+ gchar *res;
+
+ g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL);
+
+ g_mutex_lock (&cnc->priv->property_lock);
+
+ res = g_strdup (cnc->priv->last_subscription_id);
+
+ g_mutex_unlock (&cnc->priv->property_lock);
+
+ return res;
+}
+
+void
+e_ews_connection_set_last_subscription_id (EEwsConnection *cnc,
+ const gchar *subscription_id)
+{
+ g_return_if_fail (E_IS_EWS_CONNECTION (cnc));
+
+ g_mutex_lock (&cnc->priv->property_lock);
+
+ if (g_strcmp0 (subscription_id, cnc->priv->last_subscription_id) != 0) {
+ g_free (cnc->priv->last_subscription_id);
+ cnc->priv->last_subscription_id = g_strdup (subscription_id);
+ }
+
+ g_mutex_unlock (&cnc->priv->property_lock);
+}
+
static xmlDoc *
e_ews_autodiscover_ws_xml (const gchar *email_address)
{
@@ -10494,6 +10547,97 @@ ews_connection_build_subscribed_folders_list (gpointer key,
}
}
+static void
+ews_connection_subscription_id_changed_cb (EEwsNotification *notification,
+ const gchar *subscription_id,
+ gpointer user_data)
+{
+ EEwsConnection *cnc = user_data;
+
+ g_return_if_fail (E_IS_EWS_CONNECTION (cnc));
+
+ NOTIFICATION_LOCK (cnc);
+
+ if (cnc->priv->notification == notification)
+ g_signal_emit (cnc, signals[SUBSCRIPTION_ID_CHANGED], 0, subscription_id, NULL);
+
+ NOTIFICATION_UNLOCK (cnc);
+}
+
+static gpointer
+ews_connection_notification_start_thread (gpointer user_data)
+{
+ GWeakRef *weakref = user_data;
+ EEwsConnection *cnc;
+
+ g_return_val_if_fail (weakref != NULL, NULL);
+
+ cnc = g_weak_ref_get (weakref);
+
+ if (cnc && !e_ews_connection_get_disconnected_flag (cnc)) {
+ gchar *last_subscription_id = e_ews_connection_dup_last_subscription_id (cnc);
+
+ NOTIFICATION_LOCK (cnc);
+
+ if (cnc->priv->subscribed_folders) {
+ g_warn_if_fail (cnc->priv->notification == NULL);
+ g_clear_object (&cnc->priv->notification);
+
+ cnc->priv->notification = e_ews_notification_new (cnc, last_subscription_id);
+
+ /* The 'notification' assumes ownership of the 'last_subscription_id' */
+ last_subscription_id = NULL;
+
+ g_signal_connect_object (cnc->priv->notification, "subscription-id-changed",
+ G_CALLBACK (ews_connection_subscription_id_changed_cb), cnc, 0);
+
+ e_ews_notification_start_listening_sync (cnc->priv->notification,
cnc->priv->subscribed_folders);
+ }
+
+ NOTIFICATION_UNLOCK (cnc);
+
+ g_free (last_subscription_id);
+ }
+
+ g_clear_object (&cnc);
+ e_weak_ref_free (weakref);
+
+ return NULL;
+}
+
+static gboolean
+ews_connection_notification_delay_cb (gpointer user_data)
+{
+ GWeakRef *weakref = user_data;
+ EEwsConnection *cnc;
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ g_return_val_if_fail (weakref != NULL, FALSE);
+
+ cnc = g_weak_ref_get (weakref);
+
+ if (cnc) {
+ NOTIFICATION_LOCK (cnc);
+
+ if (cnc->priv->notification_delay_id == g_source_get_id (g_main_current_source ())) {
+ cnc->priv->notification_delay_id = 0;
+
+ if (cnc->priv->subscribed_folders) {
+ g_thread_unref (g_thread_new (NULL, ews_connection_notification_start_thread,
+ e_weak_ref_new (cnc)));
+ }
+ }
+
+ NOTIFICATION_UNLOCK (cnc);
+
+ g_object_unref (cnc);
+ }
+
+ return FALSE;
+}
+
/*
* 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.
@@ -10521,7 +10665,7 @@ e_ews_connection_enable_notifications_sync (EEwsConnection *cnc,
GSList *folders,
guint *subscription_key)
{
- GSList *new_folders = NULL, *l;
+ GSList *new_folders = NULL, *l, *flink;
gint subscriptions_size;
g_return_if_fail (cnc != NULL);
@@ -10535,10 +10679,25 @@ e_ews_connection_enable_notifications_sync (EEwsConnection *cnc,
if (subscriptions_size == G_MAXUINT - 1)
goto exit;
- if (subscriptions_size > 0) {
- e_ews_notification_stop_listening_sync (cnc->priv->notification);
+ for (flink = folders; flink; flink = g_slist_next (flink)) {
+ for (l = cnc->priv->subscribed_folders; l; l = g_slist_next (l)) {
+ if (g_strcmp0 (l->data, flink->data) == 0)
+ break;
+ }
- g_clear_object (&cnc->priv->notification);
+ if (!l)
+ break;
+ }
+
+ /* All requested folders are already subscribed */
+ if (!flink && cnc->priv->notification)
+ goto exit;
+
+ if (subscriptions_size > 0) {
+ if (cnc->priv->notification) {
+ e_ews_notification_stop_listening_sync (cnc->priv->notification);
+ g_clear_object (&cnc->priv->notification);
+ }
g_slist_free_full (cnc->priv->subscribed_folders, g_free);
cnc->priv->subscribed_folders = NULL;
@@ -10558,9 +10717,11 @@ e_ews_connection_enable_notifications_sync (EEwsConnection *cnc,
g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc);
- cnc->priv->notification = e_ews_notification_new (cnc);
+ if (cnc->priv->notification_delay_id)
+ g_source_remove (cnc->priv->notification_delay_id);
- e_ews_notification_start_listening_sync (cnc->priv->notification, cnc->priv->subscribed_folders);
+ cnc->priv->notification_delay_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 5,
+ ews_connection_notification_delay_cb, e_weak_ref_new (cnc), (GDestroyNotify) e_weak_ref_free);
exit:
*subscription_key = notification_key;
@@ -10591,7 +10752,7 @@ e_ews_connection_disable_notifications_sync (EEwsConnection *cnc,
cnc->priv->subscribed_folders = NULL;
g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc);
- if (cnc->priv->subscribed_folders != NULL) {
+ if (cnc->priv->subscribed_folders != NULL && !e_ews_connection_get_disconnected_flag (cnc)) {
e_ews_notification_start_listening_sync (cnc->priv->notification,
cnc->priv->subscribed_folders);
} else {
g_clear_object (&cnc->priv->notification);
diff --git a/src/server/e-ews-connection.h b/src/server/e-ews-connection.h
index 37f9592f..f2643836 100644
--- a/src/server/e-ews-connection.h
+++ b/src/server/e-ews-connection.h
@@ -477,6 +477,11 @@ gboolean e_ews_connection_get_disconnected_flag
void e_ews_connection_set_disconnected_flag
(EEwsConnection *cnc,
gboolean disconnected_flag);
+gchar * e_ews_connection_dup_last_subscription_id
+ (EEwsConnection *cnc);
+void e_ews_connection_set_last_subscription_id
+ (EEwsConnection *cnc,
+ const gchar *subscription_id);
EEwsConnection *e_ews_connection_find (const gchar *uri,
const gchar *username);
GSList * e_ews_connection_list_existing (void); /* EEwsConnection * */
diff --git a/src/server/e-ews-notification.c b/src/server/e-ews-notification.c
index c808245b..c4b84a49 100644
--- a/src/server/e-ews-notification.c
+++ b/src/server/e-ews-notification.c
@@ -36,6 +36,7 @@ struct _EEwsNotificationPrivate {
GWeakRef connection_wk;
GByteArray *chunk;
GCancellable *cancellable;
+ gchar *last_subscription_id; /* guarded by the caller, because it can be set only after construct */
};
enum {
@@ -43,6 +44,13 @@ enum {
PROP_CONNECTION
};
+enum {
+ SUBSCRIPTION_ID_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
static const gchar *default_events_names[] = {
"CopiedEvent",
"CreatedEvent",
@@ -91,13 +99,20 @@ ews_notification_authenticate (SoupSession *session,
}
EEwsNotification *
-e_ews_notification_new (EEwsConnection *connection)
+e_ews_notification_new (EEwsConnection *connection,
+ gchar *last_subscription_id)
{
+ EEwsNotification *notif;
+
g_return_val_if_fail (E_IS_EWS_CONNECTION (connection), NULL);
- return g_object_new (
+ notif = g_object_new (
E_TYPE_EWS_NOTIFICATION,
"connection", connection, NULL);
+
+ notif->priv->last_subscription_id = last_subscription_id;
+
+ return notif;
}
static void
@@ -203,6 +218,7 @@ ews_notification_finalize (GObject *object)
notif = E_EWS_NOTIFICATION (object);
g_weak_ref_clear (¬if->priv->connection_wk);
+ g_free (notif->priv->last_subscription_id);
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_ews_notification_parent_class)->finalize (object);
@@ -233,6 +249,15 @@ e_ews_notification_class_init (EEwsNotificationClass *class)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+
+ signals[SUBSCRIPTION_ID_CHANGED] = g_signal_new (
+ "subscription-id-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
}
static void
@@ -416,6 +441,9 @@ e_ews_notification_subscribe_folder_sync (EEwsNotification *notification,
}
g_object_unref (response);
+
+ g_signal_emit (notification, signals[SUBSCRIPTION_ID_CHANGED], 0, *subscription_id, NULL);
+
return TRUE;
}
@@ -504,6 +532,8 @@ e_ews_notification_unsubscribe_folder_sync (EEwsNotification *notification,
return FALSE;
}
+ g_signal_emit (notification, signals[SUBSCRIPTION_ID_CHANGED], 0, NULL, NULL);
+
return TRUE;
}
@@ -898,6 +928,11 @@ e_ews_notification_get_events_thread (gpointer user_data)
g_return_val_if_fail (td->notification != NULL, NULL);
g_return_val_if_fail (td->folders != NULL, NULL);
+ if (td->notification->priv->last_subscription_id) {
+ e_ews_notification_unsubscribe_folder_sync (td->notification,
td->notification->priv->last_subscription_id);
+ g_clear_pointer (&td->notification->priv->last_subscription_id, g_free);
+ }
+
if (!e_ews_notification_subscribe_folder_sync (td->notification, td->folders, &subscription_id,
td->cancellable))
goto exit;
diff --git a/src/server/e-ews-notification.h b/src/server/e-ews-notification.h
index 2ecbc651..e790b8f0 100644
--- a/src/server/e-ews-notification.h
+++ b/src/server/e-ews-notification.h
@@ -53,7 +53,8 @@ struct _EEwsNotificationClass {
GType e_ews_notification_get_type (void);
EEwsNotification *
- e_ews_notification_new (EEwsConnection *connection);
+ e_ews_notification_new (EEwsConnection *connection,
+ gchar *last_subscription_id); /* assumes ownership of it */
void e_ews_notification_start_listening_sync
(EEwsNotification *notification,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]