[evolution-data-server] Introduce an ENetworkMonitor



commit 84bd48b7a91da19fe8d3e92d381d147f2ac02f83
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jun 1 00:14:49 2016 +0200

    Introduce an ENetworkMonitor
    
    It's a GNetworkMonitor implementation, a proxy for a real instance,
    which allows to change in runtime which implementation of the GNetworkMonitor
    will be used, together with a special option to report an always-online
    state. This instance can be used by any part, thus the changes in it
    influence not only Mail, but also Contacts, Calendars, Memo Lists and
    Task Lists. It wasn't easily possible with the raw GNetworkMonitor.
    
    The added GSettings option is:
       org.gnome.evolution-data-server network-monitor-gio-name
    and the ENetworkMonitor uses it transparently.

 camel/camel-network-service.c                      |  103 +++-
 camel/camel-session.c                              |  103 ++++-
 camel/camel-session.h                              |    6 +
 configure.ac                                       |    4 +-
 .../org.gnome.evolution-data-server.gschema.xml.in |    5 +
 docs/reference/camel/camel-sections.txt            |    2 +
 docs/reference/eds/eds-sections.txt                |   20 +
 libebackend/e-backend.c                            |    2 +-
 libedataserver/Makefile.am                         |    2 +
 libedataserver/e-network-monitor.c                 |  584 ++++++++++++++++++++
 libedataserver/e-network-monitor.h                 |   89 +++
 libedataserver/libedataserver.h                    |    1 +
 12 files changed, 904 insertions(+), 17 deletions(-)
---
diff --git a/camel/camel-network-service.c b/camel/camel-network-service.c
index 19f499a..28b9d11 100644
--- a/camel/camel-network-service.c
+++ b/camel/camel-network-service.c
@@ -49,6 +49,9 @@ struct _CamelNetworkServicePrivate {
        gboolean host_reachable;
        gboolean host_reachable_set;
 
+       GWeakRef session_weakref;
+       gulong session_notify_network_monitor_handler_id;
+
        GNetworkMonitor *network_monitor;
        gulong network_changed_handler_id;
 
@@ -467,11 +470,63 @@ network_service_network_changed_cb (GNetworkMonitor *network_monitor,
        network_service_update_host_reachable (service);
 }
 
+static void
+network_service_session_notify_network_monitor_cb (GObject *object,
+                                                  GParamSpec *param,
+                                                  gpointer user_data)
+{
+       CamelSession *session;
+       CamelNetworkService *service = user_data;
+       CamelNetworkServicePrivate *priv;
+       GNetworkMonitor *network_monitor;
+       gboolean update_host_reachable = FALSE;
+
+       g_return_if_fail (CAMEL_IS_SESSION (object));
+       g_return_if_fail (CAMEL_IS_NETWORK_SERVICE (service));
+
+       priv = CAMEL_NETWORK_SERVICE_GET_PRIVATE (service);
+       g_return_if_fail (priv != NULL);
+
+       g_object_ref (service);
+
+       session = CAMEL_SESSION (object);
+
+       network_monitor = camel_session_ref_network_monitor (session);
+
+       g_mutex_lock (&priv->property_lock);
+
+       if (network_monitor != priv->network_monitor) {
+               if (priv->network_monitor) {
+                       g_signal_handler_disconnect (
+                               priv->network_monitor,
+                               priv->network_changed_handler_id);
+                       g_object_unref (priv->network_monitor);
+               }
+
+               priv->network_monitor = g_object_ref (network_monitor);
+
+               priv->network_changed_handler_id = g_signal_connect (
+                       priv->network_monitor, "network-changed",
+                       G_CALLBACK (network_service_network_changed_cb), service);
+
+               update_host_reachable = TRUE;
+       }
+
+       g_mutex_unlock (&priv->property_lock);
+
+       g_clear_object (&network_monitor);
+
+       if (update_host_reachable)
+               network_service_update_host_reachable (service);
+
+       g_object_unref (service);
+}
+
 static CamelNetworkServicePrivate *
 network_service_private_new (CamelNetworkService *service)
 {
        CamelNetworkServicePrivate *priv;
-       GNetworkMonitor *network_monitor;
+       CamelSession *session;
        gulong handler_id;
 
        priv = g_slice_new0 (CamelNetworkServicePrivate);
@@ -482,13 +537,26 @@ network_service_private_new (CamelNetworkService *service)
 
        /* Configure network monitoring. */
 
-       network_monitor = g_network_monitor_get_default ();
-       priv->network_monitor = g_object_ref (network_monitor);
+       session = camel_service_ref_session (CAMEL_SERVICE (service));
+       if (session) {
+               priv->network_monitor = camel_session_ref_network_monitor (session);
+
+               priv->session_notify_network_monitor_handler_id =
+                       g_signal_connect (session, "notify::network-monitor",
+                               G_CALLBACK (network_service_session_notify_network_monitor_cb), service);
+
+               g_weak_ref_init (&priv->session_weakref, session);
 
-       handler_id = g_signal_connect (
-               priv->network_monitor, "network-changed",
-               G_CALLBACK (network_service_network_changed_cb), service);
-       priv->network_changed_handler_id = handler_id;
+               g_object_unref (session);
+       } else
+               g_weak_ref_init (&priv->session_weakref, NULL);
+
+       if (priv->network_monitor) {
+               handler_id = g_signal_connect (
+                       priv->network_monitor, "network-changed",
+                       G_CALLBACK (network_service_network_changed_cb), service);
+               priv->network_changed_handler_id = handler_id;
+       }
 
        return priv;
 }
@@ -496,13 +564,28 @@ network_service_private_new (CamelNetworkService *service)
 static void
 network_service_private_free (CamelNetworkServicePrivate *priv)
 {
-       g_signal_handler_disconnect (
-               priv->network_monitor,
-               priv->network_changed_handler_id);
+       if (priv->network_changed_handler_id) {
+               g_signal_handler_disconnect (
+                       priv->network_monitor,
+                       priv->network_changed_handler_id);
+       }
+
+       if (priv->session_notify_network_monitor_handler_id) {
+               CamelSession *session;
+
+               session = g_weak_ref_get (&priv->session_weakref);
+               if (session) {
+                       g_signal_handler_disconnect (
+                               session,
+                               priv->session_notify_network_monitor_handler_id);
+                       g_object_unref (session);
+               }
+       }
 
        g_clear_object (&priv->connectable);
        g_clear_object (&priv->network_monitor);
        g_clear_object (&priv->network_monitor_cancellable);
+       g_weak_ref_clear (&priv->session_weakref);
 
        if (priv->update_host_reachable != NULL) {
                g_source_destroy (priv->update_host_reachable);
diff --git a/camel/camel-session.c b/camel/camel-session.c
index c740f20..10437dd 100644
--- a/camel/camel-session.c
+++ b/camel/camel-session.c
@@ -70,6 +70,9 @@ struct _CamelSessionPrivate {
 
        GMainContext *main_context;
 
+       GMutex property_lock;
+       GNetworkMonitor *network_monitor;
+
        guint online : 1;
 };
 
@@ -102,6 +105,7 @@ enum {
        PROP_0,
        PROP_JUNK_FILTER,
        PROP_MAIN_CONTEXT,
+       PROP_NETWORK_MONITOR,
        PROP_ONLINE,
        PROP_USER_DATA_DIR,
        PROP_USER_CACHE_DIR
@@ -284,6 +288,12 @@ session_set_property (GObject *object,
                                g_value_get_object (value));
                        return;
 
+               case PROP_NETWORK_MONITOR:
+                       camel_session_set_network_monitor (
+                               CAMEL_SESSION (object),
+                               g_value_get_object (value));
+                       return;
+
                case PROP_ONLINE:
                        camel_session_set_online (
                                CAMEL_SESSION (object),
@@ -325,6 +335,12 @@ session_get_property (GObject *object,
                                CAMEL_SESSION (object)));
                        return;
 
+               case PROP_NETWORK_MONITOR:
+                       g_value_take_object (
+                               value, camel_session_ref_network_monitor (
+                               CAMEL_SESSION (object)));
+                       return;
+
                case PROP_ONLINE:
                        g_value_set_boolean (
                                value, camel_session_get_online (
@@ -356,10 +372,8 @@ session_dispose (GObject *object)
 
        g_hash_table_remove_all (priv->services);
 
-       if (priv->junk_filter != NULL) {
-               g_object_unref (priv->junk_filter);
-               priv->junk_filter = NULL;
-       }
+       g_clear_object (&priv->junk_filter);
+       g_clear_object (&priv->network_monitor);
 
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (camel_session_parent_class)->dispose (object);
@@ -381,6 +395,7 @@ session_finalize (GObject *object)
                g_main_context_unref (priv->main_context);
 
        g_mutex_clear (&priv->services_lock);
+       g_mutex_clear (&priv->property_lock);
 
        if (priv->junk_headers) {
                g_hash_table_remove_all (priv->junk_headers);
@@ -629,6 +644,17 @@ camel_session_class_init (CamelSessionClass *class)
 
        g_object_class_install_property (
                object_class,
+               PROP_NETWORK_MONITOR,
+               g_param_spec_object (
+                       "network-monitor",
+                       "Network Monitor",
+                       NULL,
+                       G_TYPE_NETWORK_MONITOR,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
                PROP_ONLINE,
                g_param_spec_boolean (
                        "online",
@@ -720,6 +746,7 @@ camel_session_init (CamelSession *session)
 
        session->priv->services = services;
        g_mutex_init (&session->priv->services_lock);
+       g_mutex_init (&session->priv->property_lock);
        session->priv->junk_headers = NULL;
 
        session->priv->main_context = g_main_context_ref_thread_default ();
@@ -781,6 +808,74 @@ camel_session_get_user_cache_dir (CamelSession *session)
 }
 
 /**
+ * camel_session_set_network_monitor:
+ * @session: a #CamelSession
+ * @network_monitor: (nullable): a #GNetworkMonitor or %NULL
+ *
+ * Sets a network monitor instance for the @session. This can be used
+ * to override which #GNetworkMonitor should be used to check network
+ * availability and whether a server is reachable.
+ *
+ * Since: 3.22
+ **/
+void
+camel_session_set_network_monitor (CamelSession *session,
+                                  GNetworkMonitor *network_monitor)
+{
+       gboolean changed = FALSE;
+
+       g_return_if_fail (CAMEL_IS_SESSION (session));
+       if (network_monitor)
+               g_return_if_fail (G_IS_NETWORK_MONITOR (network_monitor));
+
+       g_mutex_lock (&session->priv->property_lock);
+
+       if (network_monitor != session->priv->network_monitor) {
+               g_clear_object (&session->priv->network_monitor);
+               session->priv->network_monitor = network_monitor ? g_object_ref (network_monitor) : NULL;
+
+               changed = TRUE;
+       }
+
+       g_mutex_unlock (&session->priv->property_lock);
+
+       if (changed)
+               g_object_notify (G_OBJECT (session), "network-monitor");
+}
+
+/**
+ * camel_session_ref_network_monitor:
+ * @session: a #CamelSession
+ *
+ * References a #GNetworkMonitor instance, which had been previously set
+ * by camel_session_set_network_monitor(). If none is set, then the default
+ * #GNetworkMonitor is returned, as provided by g_network_monitor_get_default().
+ * The returned pointer is referenced for thread safety, unref it with
+ * g_object_unref() when no longer needed.
+ *
+ * Returns: (transfer full): A referenced #GNetworkMonitor instance to use
+ *   for network availability tests.
+ *
+ * Since:3.22
+ **/
+GNetworkMonitor *
+camel_session_ref_network_monitor (CamelSession *session)
+{
+       GNetworkMonitor *network_monitor;
+
+       g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
+
+       g_mutex_lock (&session->priv->property_lock);
+
+       network_monitor = g_object_ref (session->priv->network_monitor ?
+               session->priv->network_monitor : g_network_monitor_get_default ());
+
+       g_mutex_unlock (&session->priv->property_lock);
+
+       return network_monitor;
+}
+
+/**
  * camel_session_add_service:
  * @session: a #CamelSession
  * @uid: a unique identifier string
diff --git a/camel/camel-session.h b/camel/camel-session.h
index ce9b045..e837e0f 100644
--- a/camel/camel-session.h
+++ b/camel/camel-session.h
@@ -152,6 +152,12 @@ GMainContext *     camel_session_ref_main_context  (CamelSession *session);
 const gchar *  camel_session_get_user_data_dir (CamelSession *session);
 const gchar *  camel_session_get_user_cache_dir
                                                (CamelSession *session);
+void           camel_session_set_network_monitor
+                                               (CamelSession *session,
+                                                GNetworkMonitor *network_monitor);
+GNetworkMonitor *
+               camel_session_ref_network_monitor
+                                               (CamelSession *session);
 CamelService * camel_session_add_service       (CamelSession *session,
                                                 const gchar *uid,
                                                 const gchar *protocol,
diff --git a/configure.ac b/configure.ac
index cbf7aeb..53e1d1d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,8 +35,8 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 dnl Required Package Versions
 
 dnl Keep these two definitions in agreement.
-m4_define([glib_minimum_version], [2.40])
-m4_define([glib_encoded_version], [GLIB_VERSION_2_40])
+m4_define([glib_minimum_version], [2.46])
+m4_define([glib_encoded_version], [GLIB_VERSION_2_46])
 
 dnl Keep these two definitions in agreement.
 m4_define([gdk_minimum_version], [3.10])
diff --git a/data/org.gnome.evolution-data-server.gschema.xml.in 
b/data/org.gnome.evolution-data-server.gschema.xml.in
index 54e55a6..05ae319 100644
--- a/data/org.gnome.evolution-data-server.gschema.xml.in
+++ b/data/org.gnome.evolution-data-server.gschema.xml.in
@@ -14,5 +14,10 @@
       <_summary>Whether to load photos of signers/encrypters</_summary>
       <_description>When set to 'true', tries to load also photo of the signers/encrypters, if available in 
the key/certificate.</_description>
     </key>
+    <key name="network-monitor-gio-name" type="s">
+      <default>''</default>
+      <_summary>GIO name of the GNetworkMonitor to use for an ENetworkMonitor instance</_summary>
+      <_description>When set to an unknown value, then the default GNetworkMonitor is used in the 
background. A special value 'always-online' is used for no network monitoring.</_description>
+    </key>
   </schema>
 </schemalist>
diff --git a/docs/reference/camel/camel-sections.txt b/docs/reference/camel/camel-sections.txt
index 9c614e1..c11afe4 100644
--- a/docs/reference/camel/camel-sections.txt
+++ b/docs/reference/camel/camel-sections.txt
@@ -1965,6 +1965,8 @@ CamelSessionAlertType
 camel_session_ref_main_context
 camel_session_get_user_data_dir
 camel_session_get_user_cache_dir
+camel_session_set_network_monitor
+camel_session_ref_network_monitor
 camel_session_add_service
 camel_session_remove_service
 camel_session_ref_service
diff --git a/docs/reference/eds/eds-sections.txt b/docs/reference/eds/eds-sections.txt
index 8d9425e..13c71cc 100644
--- a/docs/reference/eds/eds-sections.txt
+++ b/docs/reference/eds/eds-sections.txt
@@ -2646,6 +2646,26 @@ e_name_western_free
 </SECTION>
 
 <SECTION>
+<FILE>e-network-monitor</FILE>
+<TITLE>ENetworkMonitor</TITLE>
+ENetworkMonitor
+ENetworkMonitorClass
+e_network_monitor_get_default
+e_network_monitor_list_gio_names
+e_network_monitor_dup_gio_name
+e_network_monitor_set_gio_name
+<SUBSECTION Standard>
+ENetworkMonitorPrivate
+E_IS_NETWORK_MONITOR
+E_IS_NETWORK_MONITOR_CLASS
+E_NETWORK_MONITOR
+E_NETWORK_MONITOR_CLASS
+E_NETWORK_MONITOR_GET_CLASS
+E_TYPE_NETWORK_MONITOR
+e_network_monitor_get_type
+</SECTION>
+
+<SECTION>
 <FILE>e-oauth2-support</FILE>
 <TITLE>EOAuth2Support</TITLE>
 EOAuth2SupportInterface
diff --git a/libebackend/e-backend.c b/libebackend/e-backend.c
index 1369e10..ab76092 100644
--- a/libebackend/e-backend.c
+++ b/libebackend/e-backend.c
@@ -734,7 +734,7 @@ e_backend_init (EBackend *backend)
 
        /* Configure network monitoring. */
 
-       network_monitor = g_network_monitor_get_default ();
+       network_monitor = e_network_monitor_get_default ();
        backend->priv->network_monitor = g_object_ref (network_monitor);
        backend->priv->online = g_network_monitor_get_network_available (network_monitor);
 
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index a71ea8f..1561d80 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -64,6 +64,7 @@ libedataserver_1_2_la_SOURCES = \
        e-list-iterator.c \
        e-memory.c \
        e-module.c \
+       e-network-monitor.c \
        e-operation-pool.c \
        e-proxy.c \
        e-secret-store.c \
@@ -158,6 +159,7 @@ libedataserverinclude_HEADERS = \
        e-list-iterator.h \
        e-memory.h \
        e-module.h \
+       e-network-monitor.h \
        e-operation-pool.h \
        e-proxy.h \
        e-secret-store.h \
diff --git a/libedataserver/e-network-monitor.c b/libedataserver/e-network-monitor.c
new file mode 100644
index 0000000..c80d352
--- /dev/null
+++ b/libedataserver/e-network-monitor.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+
+#include "e-network-monitor.h"
+
+struct _ENetworkMonitorPrivate {
+       GMutex property_lock;
+       gchar *gio_name;
+       GNetworkMonitor *gio_monitor;
+       gulong network_available_notify_id;
+       gulong network_metered_notify_id;
+       gulong network_connectivity_notify_id;
+       gulong network_changed_id;
+       GSource *network_changed_source;
+};
+
+enum {
+       PROP_0,
+       PROP_GIO_NAME,
+       PROP_CONNECTIVITY,
+       PROP_NETWORK_METERED,
+       PROP_NETWORK_AVAILABLE
+};
+
+static guint network_changed_signal = 0;
+
+static void e_network_monitor_initable_iface_init (GInitableIface *iface);
+static void e_network_monitor_gio_iface_init (GNetworkMonitorInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ENetworkMonitor, e_network_monitor, G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, e_network_monitor_initable_iface_init)
+       G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR, e_network_monitor_gio_iface_init))
+
+static GNetworkConnectivity
+e_network_monitor_get_connectivity (ENetworkMonitor *network_monitor)
+{
+       GNetworkConnectivity connectivity;
+
+       g_return_val_if_fail (E_IS_NETWORK_MONITOR (network_monitor), G_NETWORK_CONNECTIVITY_LOCAL);
+
+       g_mutex_lock (&network_monitor->priv->property_lock);
+
+       if (network_monitor->priv->gio_monitor)
+               connectivity = g_network_monitor_get_connectivity (network_monitor->priv->gio_monitor);
+       else
+               connectivity = G_NETWORK_CONNECTIVITY_FULL;
+
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+
+       return connectivity;
+}
+
+static gboolean
+e_network_monitor_get_network_available (ENetworkMonitor *network_monitor)
+{
+       gboolean network_available;
+
+       g_return_val_if_fail (E_IS_NETWORK_MONITOR (network_monitor), FALSE);
+
+       g_mutex_lock (&network_monitor->priv->property_lock);
+
+       if (network_monitor->priv->gio_monitor)
+               network_available = g_network_monitor_get_network_available 
(network_monitor->priv->gio_monitor);
+       else
+               network_available = TRUE;
+
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+
+       return network_available;
+}
+
+static gboolean
+e_network_monitor_get_network_metered (ENetworkMonitor *network_monitor)
+{
+       gboolean network_metered;
+
+       g_return_val_if_fail (E_IS_NETWORK_MONITOR (network_monitor), FALSE);
+
+       g_mutex_lock (&network_monitor->priv->property_lock);
+
+       if (network_monitor->priv->gio_monitor)
+               network_metered = g_network_monitor_get_network_metered (network_monitor->priv->gio_monitor);
+       else
+               network_metered = FALSE;
+
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+
+       return network_metered;
+}
+
+static gboolean
+e_network_monitor_emit_network_changed_idle_cb (gpointer user_data)
+{
+       ENetworkMonitor *network_monitor = user_data;
+       gboolean is_available;
+
+       g_return_val_if_fail (E_IS_NETWORK_MONITOR (network_monitor), FALSE);
+
+       g_object_ref (network_monitor);
+
+       is_available = e_network_monitor_get_network_available (network_monitor);
+       g_signal_emit (network_monitor, network_changed_signal, 0, is_available);
+
+       g_source_unref (network_monitor->priv->network_changed_source);
+       network_monitor->priv->network_changed_source = NULL;
+
+       g_object_unref (network_monitor);
+
+       return FALSE;
+}
+
+static void
+e_network_monitor_schedule_network_changed_emit (ENetworkMonitor *network_monitor)
+{
+       g_mutex_lock (&network_monitor->priv->property_lock);
+
+       if (!network_monitor->priv->network_changed_source) {
+               network_monitor->priv->network_changed_source = g_idle_source_new ();
+               /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
+                * network-change-related notifications coming in at
+                * G_PRIORITY_DEFAULT will get coalesced into one signal
+                * emission.
+                */
+               g_source_set_priority (network_monitor->priv->network_changed_source, G_PRIORITY_HIGH_IDLE);
+               g_source_set_callback (network_monitor->priv->network_changed_source,
+                       e_network_monitor_emit_network_changed_idle_cb, network_monitor, NULL);
+               g_source_attach (network_monitor->priv->network_changed_source, NULL);
+       }
+
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+}
+
+static void
+e_network_monitor_notify_cb (GNetworkMonitor *gio_monitor,
+                            GParamSpec *param,
+                            ENetworkMonitor *network_monitor)
+{
+       g_return_if_fail (G_IS_NETWORK_MONITOR (gio_monitor));
+       g_return_if_fail (param && param->name);
+       g_return_if_fail (E_IS_NETWORK_MONITOR (network_monitor));
+
+       g_object_notify (G_OBJECT (network_monitor), param->name);
+}
+
+static void
+e_network_monitor_network_changed_cb (GNetworkMonitor *gio_monitor,
+                                     gboolean is_available,
+                                     ENetworkMonitor *network_monitor)
+{
+       g_return_if_fail (G_IS_NETWORK_MONITOR (gio_monitor));
+       g_return_if_fail (E_IS_NETWORK_MONITOR (network_monitor));
+
+       e_network_monitor_schedule_network_changed_emit (network_monitor);
+}
+
+static void
+e_network_monitor_disconnect_gio_monitor_locked (ENetworkMonitor *network_monitor)
+{
+       if (!network_monitor->priv->gio_monitor)
+               return;
+
+       #define disconnect_signal(x) G_STMT_START { \
+               if (network_monitor->priv->x) { \
+                       g_signal_handler_disconnect (network_monitor->priv->gio_monitor, 
network_monitor->priv->x); \
+                       network_monitor->priv->x = 0; \
+               } \
+               } G_STMT_END
+
+       disconnect_signal (network_available_notify_id);
+       disconnect_signal (network_metered_notify_id);
+       disconnect_signal (network_connectivity_notify_id);
+       disconnect_signal (network_changed_id);
+
+       #undef disconnect_signal
+}
+
+static void
+e_network_monitor_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_GIO_NAME:
+                       e_network_monitor_set_gio_name (
+                               E_NETWORK_MONITOR (object),
+                               g_value_get_string (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_network_monitor_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_GIO_NAME:
+                       g_value_take_string (
+                               value,
+                               e_network_monitor_dup_gio_name (
+                               E_NETWORK_MONITOR (object)));
+                       return;
+
+               case PROP_CONNECTIVITY:
+                       g_value_set_enum (
+                               value,
+                               e_network_monitor_get_connectivity (
+                               E_NETWORK_MONITOR (object)));
+                       return;
+
+               case PROP_NETWORK_METERED:
+                       g_value_set_boolean (
+                               value,
+                               e_network_monitor_get_network_metered (
+                               E_NETWORK_MONITOR (object)));
+                       return;
+
+               case PROP_NETWORK_AVAILABLE:
+                       g_value_set_boolean (
+                               value,
+                               e_network_monitor_get_network_available (
+                               E_NETWORK_MONITOR (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_network_monitor_constructed (GObject *object)
+{
+       GSettings *settings;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_network_monitor_parent_class)->constructed (object);
+
+       settings = g_settings_new ("org.gnome.evolution-data-server");
+       g_settings_bind (
+               settings, "network-monitor-gio-name",
+               object, "gio-name",
+               G_SETTINGS_BIND_DEFAULT);
+       g_object_unref (settings);
+}
+
+static void
+e_network_monitor_finalize (GObject *object)
+{
+       ENetworkMonitor *network_monitor = E_NETWORK_MONITOR (object);
+
+       if (network_monitor->priv->network_changed_source) {
+               g_source_destroy (network_monitor->priv->network_changed_source);
+               g_source_unref (network_monitor->priv->network_changed_source);
+               network_monitor->priv->network_changed_source = NULL;
+       }
+
+       g_mutex_lock (&network_monitor->priv->property_lock);
+       e_network_monitor_disconnect_gio_monitor_locked (network_monitor);
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+
+       g_mutex_clear (&network_monitor->priv->property_lock);
+       g_clear_object (&network_monitor->priv->gio_monitor);
+       g_free (network_monitor->priv->gio_name);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_network_monitor_parent_class)->finalize (object);
+}
+
+static void
+e_network_monitor_class_init (ENetworkMonitorClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (ENetworkMonitorPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = e_network_monitor_set_property;
+       object_class->get_property = e_network_monitor_get_property;
+       object_class->constructed = e_network_monitor_constructed;
+       object_class->finalize = e_network_monitor_finalize;
+
+       /**
+        * ENetworkMonitor:gio-name:
+        *
+        * The GIO name of the underlying #GNetworkMonitor to use.
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_GIO_NAME,
+               g_param_spec_string (
+                       "gio-name",
+                       "GIO name",
+                       NULL,
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_override_property (object_class, PROP_NETWORK_AVAILABLE, "network-available");
+       g_object_class_override_property (object_class, PROP_NETWORK_METERED, "network-metered");
+       g_object_class_override_property (object_class, PROP_CONNECTIVITY, "connectivity");
+}
+
+static void
+e_network_monitor_init (ENetworkMonitor *network_monitor)
+{
+       network_monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (network_monitor, E_TYPE_NETWORK_MONITOR, 
ENetworkMonitorPrivate);
+
+       g_mutex_init (&network_monitor->priv->property_lock);
+}
+
+static gboolean
+e_network_monitor_initable_init (GInitable *initable,
+                                GCancellable *cancellable,
+                                GError **error)
+{
+       return TRUE;
+}
+
+static void
+e_network_monitor_initable_iface_init (GInitableIface *iface)
+{
+       iface->init = e_network_monitor_initable_init;
+}
+
+static gboolean
+e_network_monitor_can_reach (GNetworkMonitor *monitor,
+                            GSocketConnectable *connectable,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       ENetworkMonitor *network_monitor;
+       GNetworkMonitor *use_gio_monitor = NULL;
+       gboolean can_reach;
+
+       g_return_val_if_fail (E_IS_NETWORK_MONITOR (monitor), FALSE);
+
+       network_monitor = E_NETWORK_MONITOR (monitor);
+
+       g_mutex_lock (&network_monitor->priv->property_lock);
+
+       if (network_monitor->priv->gio_monitor)
+               use_gio_monitor = g_object_ref (network_monitor->priv->gio_monitor);
+
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+
+       if (use_gio_monitor)
+               can_reach = g_network_monitor_can_reach (use_gio_monitor, connectable, cancellable, error);
+       else
+               can_reach = TRUE;
+
+       g_clear_object (&use_gio_monitor);
+
+       return can_reach;
+}
+
+static void
+e_network_monitor_gio_iface_init (GNetworkMonitorInterface *iface)
+{
+       iface->can_reach = e_network_monitor_can_reach;
+
+       if (!network_changed_signal)
+               network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
+}
+
+static GNetworkMonitor *
+e_network_monitor_create_instance_for_gio_name (const gchar *gio_name)
+{
+       GIOExtensionPoint *pnt;
+       GList *extensions, *link;
+
+       if (!gio_name || !*gio_name)
+               return NULL;
+
+       /* To initialize the GIO extension point for the GNetworkMonitor */
+       g_network_monitor_get_default ();
+
+       pnt = g_io_extension_point_lookup (G_NETWORK_MONITOR_EXTENSION_POINT_NAME);
+       if (!pnt)
+               return NULL;
+
+       extensions = g_io_extension_point_get_extensions (pnt);
+
+       for (link = extensions; link; link = g_list_next (link)) {
+               GIOExtension *ext = link->data;
+
+               if (g_strcmp0 (g_io_extension_get_name (ext), gio_name) == 0)
+                       return g_initable_new (g_io_extension_get_type (ext), NULL, NULL, NULL);
+       }
+
+       return NULL;
+}
+
+/**
+ * e_network_monitor_get_default:
+ *
+ * Gets the default #ENetworkMonitor. The caller should not unref the returned instance.
+ * The #ENetworkMonitor implements the #GNetworkMonitor iterface.
+ *
+ * Returns: (transfer none): The default #ENetworkMonitor instance.
+ *
+ * Since: 3.22
+ **/
+GNetworkMonitor *
+e_network_monitor_get_default (void)
+{
+       static GNetworkMonitor *network_monitor = NULL;
+       G_LOCK_DEFINE_STATIC (network_monitor);
+
+       G_LOCK (network_monitor);
+       if (!network_monitor)
+               network_monitor = g_initable_new (E_TYPE_NETWORK_MONITOR, NULL, NULL, NULL);
+       G_UNLOCK (network_monitor);
+
+       return network_monitor;
+}
+
+/**
+ * e_network_monitor_list_gio_names:
+ * @network_monitor: an #ENetworkMonitor
+ *
+ * Get a list of available GIO names for the #GNetworkMonitor implementations.
+ * The strings can be used in e_network_monitor_set_gio_name().
+ *
+ * Returns: (transfer full) (element-type utf8): A newly allocated #GSList,
+ *   with newly allocated strings, the GIO names. The #GSList should be freed
+ *   with g_slist_free_full (gio_names, g_free); when no longer needed.
+ *
+ * Since: 3.22
+ **/
+GSList *
+e_network_monitor_list_gio_names (ENetworkMonitor *network_monitor)
+{
+       GIOExtensionPoint *pnt;
+       GList *extensions, *link;
+       GSList *gio_names = NULL;
+
+       g_return_val_if_fail (E_IS_NETWORK_MONITOR (network_monitor), NULL);
+
+       /* To initialize the GIO extension point for the GNetworkMonitor */
+       g_network_monitor_get_default ();
+
+       pnt = g_io_extension_point_lookup (G_NETWORK_MONITOR_EXTENSION_POINT_NAME);
+       if (!pnt)
+               return NULL;
+
+       extensions = g_io_extension_point_get_extensions (pnt);
+
+       for (link = extensions; link; link = g_list_next (link)) {
+               GIOExtension *ext = link->data;
+
+               gio_names = g_slist_prepend (gio_names, g_strdup (g_io_extension_get_name (ext)));
+       }
+
+       return g_slist_reverse (gio_names);
+}
+
+/**
+ * e_network_monitor_dup_gio_name:
+ * @network_monitor: an #ENetworkMonitor
+ *
+ * Get currently set GIO name for the network availability checks.
+ * See e_network_monitor_set_gio_name() for more details.
+ *
+ * Returns: (transfer full): A newly allocated string, a GIO name
+ *   of the underlying GNetworkMonitor which is set to be used.
+ *   The returned string should be freed with g_free(), when
+ *   no longer needed.
+ *
+ * Since: 3.22
+ **/
+gchar *
+e_network_monitor_dup_gio_name (ENetworkMonitor *network_monitor)
+{
+       gchar *gio_name;
+
+       g_return_val_if_fail (E_IS_NETWORK_MONITOR (network_monitor), NULL);
+
+       g_mutex_lock (&network_monitor->priv->property_lock);
+       gio_name = g_strdup (network_monitor->priv->gio_name);
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+
+       return gio_name;
+}
+
+/**
+ * e_network_monitor_set_gio_name:
+ * @network_monitor: an #ENetworkMonitor
+ * @gio_name: (nullable): a GIO name of a #GNetworkMonitor implementation to use, or %NULL
+ *
+ * Set a @gio_name of the #GNetworkMonitor implementation to use, can be %NULL.
+ * Use e_network_monitor_list_gio_names() for a list of available
+ * implementations. A special value, %E_NETWORK_MONITOR_ALWAYS_ONLINE_NAME, can
+ * be used to report the network as always reachable. When an unknown GIO
+ * name is used the default #GNetworkMonitor implementation, as returned
+ * by the g_network_monitor_get_default(), will be used.
+ *
+ * Since: 3.22
+ **/
+void
+e_network_monitor_set_gio_name (ENetworkMonitor *network_monitor,
+                               const gchar *gio_name)
+{
+       GObject *object;
+
+       g_return_if_fail (E_IS_NETWORK_MONITOR (network_monitor));
+
+       g_mutex_lock (&network_monitor->priv->property_lock);
+
+       if (g_strcmp0 (gio_name, network_monitor->priv->gio_name) == 0) {
+               g_mutex_unlock (&network_monitor->priv->property_lock);
+               return;
+       }
+
+       g_free (network_monitor->priv->gio_name);
+       network_monitor->priv->gio_name = g_strdup (gio_name);
+
+       if (network_monitor->priv->gio_monitor)
+               e_network_monitor_disconnect_gio_monitor_locked (network_monitor);
+
+       g_clear_object (&network_monitor->priv->gio_monitor);
+
+       if (g_strcmp0 (network_monitor->priv->gio_name, E_NETWORK_MONITOR_ALWAYS_ONLINE_NAME) != 0) {
+               GNetworkMonitor *gio_monitor;
+
+               gio_monitor = e_network_monitor_create_instance_for_gio_name 
(network_monitor->priv->gio_name);
+               if (!gio_monitor)
+                       gio_monitor = g_object_ref (g_network_monitor_get_default ());
+
+               network_monitor->priv->gio_monitor = gio_monitor;
+
+               if (gio_monitor) {
+                       network_monitor->priv->network_available_notify_id =
+                               g_signal_connect (network_monitor->priv->gio_monitor, 
"notify::network-available",
+                                       G_CALLBACK (e_network_monitor_notify_cb), network_monitor);
+
+                       network_monitor->priv->network_metered_notify_id =
+                               g_signal_connect (network_monitor->priv->gio_monitor, 
"notify::network-metered",
+                                       G_CALLBACK (e_network_monitor_notify_cb), network_monitor);
+
+                       network_monitor->priv->network_connectivity_notify_id =
+                               g_signal_connect (network_monitor->priv->gio_monitor, "notify::connectivity",
+                                       G_CALLBACK (e_network_monitor_notify_cb), network_monitor);
+
+                       network_monitor->priv->network_changed_id =
+                               g_signal_connect (network_monitor->priv->gio_monitor, "network-changed",
+                                       G_CALLBACK (e_network_monitor_network_changed_cb), network_monitor);
+               }
+       }
+
+       g_mutex_unlock (&network_monitor->priv->property_lock);
+
+       object = G_OBJECT (network_monitor);
+
+       g_object_freeze_notify (object);
+       g_object_notify (object, "gio-name");
+       g_object_notify (object, "network-available");
+       g_object_notify (object, "network-metered");
+       g_object_notify (object, "connectivity");
+       g_object_thaw_notify (object);
+
+       e_network_monitor_schedule_network_changed_emit (network_monitor);
+}
diff --git a/libedataserver/e-network-monitor.h b/libedataserver/e-network-monitor.h
new file mode 100644
index 0000000..1d64100
--- /dev/null
+++ b/libedataserver/e-network-monitor.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_NETWORK_MONITOR_H
+#define E_NETWORK_MONITOR_H
+
+#include <gio/gio.h>
+
+/* Standard GObject macros */
+#define E_TYPE_NETWORK_MONITOR \
+       (e_network_monitor_get_type ())
+#define E_NETWORK_MONITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_NETWORK_MONITOR, ENetworkMonitor))
+#define E_NETWORK_MONITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_NETWORK_MONITOR, ENetworkMonitorClass))
+#define E_IS_NETWORK_MONITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_NETWORK_MONITOR))
+#define E_IS_NETWORK_MONITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_NETWORK_MONITOR))
+#define E_NETWORK_MONITOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_NETWORK_MONITOR, ENetworkMonitorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ENetworkMonitor ENetworkMonitor;
+typedef struct _ENetworkMonitorClass ENetworkMonitorClass;
+typedef struct _ENetworkMonitorPrivate ENetworkMonitorPrivate;
+
+/**
+ * @E_NETWORK_MONITOR_ALWAYS_ONLINE_NAME:
+ *
+ * A special name, which can be used as a GIO name in the call
+ * to e_network_monitor_set_gio_name(), which is used to report
+ * the the network as always reachable.
+ *
+ * Since: 3.22
+ **/
+#define E_NETWORK_MONITOR_ALWAYS_ONLINE_NAME "always-online"
+
+/**
+ * ENetworkMonitor:
+ *
+ * Contains only private data that should be read and manipulated using
+ * the functions below. Implements #GNetworkMonitorInterface.
+ *
+ * Since: 3.22
+ **/
+struct _ENetworkMonitor {
+       GObject parent;
+       ENetworkMonitorPrivate *priv;
+};
+
+struct _ENetworkMonitorClass {
+       GObjectClass parent_class;
+};
+
+GType          e_network_monitor_get_type              (void) G_GNUC_CONST;
+GNetworkMonitor *
+               e_network_monitor_get_default           (void);
+GSList *       e_network_monitor_list_gio_names        (ENetworkMonitor *network_monitor);
+gchar *                e_network_monitor_dup_gio_name          (ENetworkMonitor *network_monitor);
+void           e_network_monitor_set_gio_name          (ENetworkMonitor *network_monitor,
+                                                        const gchar *gio_name);
+
+G_END_DECLS
+
+#endif /* E_NETWORK_MONITOR_H */
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index 4c6e053..060cbde 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -37,6 +37,7 @@
 #include <libedataserver/e-list.h>
 #include <libedataserver/e-memory.h>
 #include <libedataserver/e-module.h>
+#include <libedataserver/e-network-monitor.h>
 #include <libedataserver/e-operation-pool.h>
 #include <libedataserver/e-proxy.h>
 #include <libedataserver/e-secret-store.h>


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