[evolution-data-server] Make network connection change listener actually working



commit b64b51e0e969fbf355d3a2bc1c69f4123c33078f
Author: Milan Crha <mcrha redhat com>
Date:   Thu Feb 21 15:22:08 2013 +0100

    Make network connection change listener actually working
    
    With this it's properly recognized when network connection changes,
    like when user connects to VPN, or disconnects from it, and the backend's
    online state is properly updated (if is provides connection address).

 libebackend/e-backend.c |  116 +++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 97 insertions(+), 19 deletions(-)
---
diff --git a/libebackend/e-backend.c b/libebackend/e-backend.c
index 6a291e5..c22fc29 100644
--- a/libebackend/e-backend.c
+++ b/libebackend/e-backend.c
@@ -62,6 +62,7 @@ struct _EBackendPrivate {
 
        GNetworkMonitor *network_monitor;
        gulong network_changed_handler_id;
+       guint network_changed_timeout_id;
 
        GMutex network_monitor_cancellable_lock;
        GCancellable *network_monitor_cancellable;
@@ -90,6 +91,27 @@ async_context_free (AsyncContext *async_context)
        g_slice_free (AsyncContext, async_context);
 }
 
+struct UpdateOnlineData
+{
+       EBackend *backend;
+       gboolean is_online;
+};
+
+static gpointer
+set_backend_online_thread (gpointer user_data)
+{
+       struct UpdateOnlineData *uod = user_data;
+
+       g_return_val_if_fail (uod != NULL, NULL);
+
+       e_backend_set_online (uod->backend, uod->is_online);
+
+       g_object_unref (uod->backend);
+       g_slice_free (struct UpdateOnlineData, uod);
+
+       return NULL;
+}
+
 static void
 backend_network_monitor_can_reach_cb (GObject *source_object,
                                       GAsyncResult *result,
@@ -97,6 +119,8 @@ backend_network_monitor_can_reach_cb (GObject *source_object,
 {
        EBackend *backend = E_BACKEND (user_data);
        gboolean host_is_reachable;
+       struct UpdateOnlineData *uod;
+       GThread *thread;
        GError *error = NULL;
 
        host_is_reachable = g_network_monitor_can_reach_finish (
@@ -107,26 +131,22 @@ backend_network_monitor_can_reach_cb (GObject *source_object,
                (host_is_reachable && (error == NULL)) ||
                (!host_is_reachable && (error != NULL)));
 
-       if (host_is_reachable) {
-               e_backend_set_online (backend, TRUE);
-
-       } else if (G_IS_IO_ERROR (error, G_IO_ERROR_CANCELLED)) {
-               /* Ignore cancellations. */
-
-       } else if (G_IS_IO_ERROR (error, G_IO_ERROR_HOST_UNREACHABLE)) {
-               e_backend_set_online (backend, FALSE);
-
-       } else if (G_IS_RESOLVER_ERROR (error, G_RESOLVER_ERROR_NOT_FOUND)) {
-               e_backend_set_online (backend, FALSE);
-
-       } else {
-               g_warning ("%s: %s", G_STRFUNC, error->message);
+       if (G_IS_IO_ERROR (error, G_IO_ERROR_CANCELLED) ||
+           (host_is_reachable ? 1 : 0) == (e_backend_get_online (backend) ? 1 : 0)) {
+               g_clear_error (&error);
+               g_object_unref (backend);
+               return;
        }
 
-       if (error != NULL)
-               g_error_free (error);
+       g_clear_error (&error);
+
+       uod = g_slice_new (struct UpdateOnlineData);
+       uod->backend = backend;
+       uod->is_online = host_is_reachable;
 
-       g_object_unref (backend);
+       /* do this in a separate thread, not the main thread */
+       thread = g_thread_new (NULL, set_backend_online_thread, uod);
+       g_thread_unref (thread);
 }
 
 static void
@@ -148,6 +168,36 @@ backend_update_online_state (EBackend *backend)
                cancellable = NULL;
        }
 
+       if (connectable && G_IS_NETWORK_ADDRESS (connectable)) {
+               GNetworkAddress *network_address = G_NETWORK_ADDRESS (connectable);
+
+               /* create copy of the backend's connectable, because once the connectable
+                  reaches its destination it caches the value and doesn't retry after
+                  network changes.
+
+                  This should be eventually replaced with default implementation
+                  of EBackend::get_destination_address() doing basically the same
+                  what currently does the backend silently on construction, thus
+                  it'll get also current values from the ESource, not stale from
+                  construct time.
+               */
+               connectable = g_network_address_new (
+                       g_network_address_get_hostname (network_address),
+                       g_network_address_get_port (network_address));
+
+               g_object_unref (network_address);
+       }
+
+       if (!connectable) {
+               gchar *host = NULL;
+               guint16 port = 0;
+
+               if (e_backend_get_destination_address (backend, &host, &port) && host)
+                       connectable = g_network_address_new (host, port);
+
+               g_free (host);
+       }
+
        if (connectable == NULL) {
                e_backend_set_online (backend, TRUE);
        } else {
@@ -168,12 +218,30 @@ backend_update_online_state (EBackend *backend)
                g_object_unref (connectable);
 }
 
+static gboolean
+backend_network_changed_timeout_cb (gpointer user_data)
+{
+       EBackend *backend = user_data;
+
+       if (!g_source_is_destroyed (g_main_current_source ())) {
+               backend->priv->network_changed_timeout_id = 0;
+
+               backend_update_online_state (backend);
+       }
+
+       return FALSE;
+}
+
 static void
 backend_network_changed_cb (GNetworkMonitor *network_monitor,
                             gboolean network_available,
                             EBackend *backend)
 {
-       backend_update_online_state (backend);
+       if (backend->priv->network_changed_timeout_id)
+               g_source_remove (backend->priv->network_changed_timeout_id);
+
+       /* wait few seconds, a network change can fire this event multiple times */
+       backend->priv->network_changed_timeout_id = g_timeout_add_seconds (3, 
backend_network_changed_timeout_cb, backend);
 }
 
 static void
@@ -264,6 +332,11 @@ backend_dispose (GObject *object)
                priv->network_changed_handler_id = 0;
        }
 
+       if (priv->network_changed_timeout_id) {
+               g_source_remove (priv->network_changed_timeout_id);
+               priv->network_changed_timeout_id = 0;
+       }
+
        g_clear_object (&priv->source);
        g_clear_object (&priv->prompter);
        g_clear_object (&priv->connectable);
@@ -284,6 +357,11 @@ backend_finalize (GObject *object)
        g_mutex_clear (&priv->property_lock);
        g_mutex_clear (&priv->network_monitor_cancellable_lock);
 
+       if (priv->network_changed_timeout_id) {
+               g_source_remove (priv->network_changed_timeout_id);
+               priv->network_changed_timeout_id = 0;
+       }
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_backend_parent_class)->finalize (object);
 }
@@ -986,7 +1064,7 @@ e_backend_is_destination_reachable (EBackend *backend,
                        GNetworkMonitor *network_monitor;
                        GSocketConnectable *connectable;
 
-                       network_monitor = g_network_monitor_get_default ();
+                       network_monitor = backend->priv->network_monitor;
 
                        connectable = g_network_address_new (host, port);
                        if (connectable) {


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