CamelNetworkService: Add a read-only "host-reachable" property.
authorMatthew Barnes <mbarnes@redhat.com>
Thu, 21 Feb 2013 17:44:18 +0000 (12:44 -0500)
committerMatthew Barnes <mbarnes@redhat.com>
Thu, 21 Feb 2013 20:17:37 +0000 (15:17 -0500)
This will be updated automatically as network conditions change.

New functions:

    camel_network_service_get_host_reachable()

camel/camel-network-service.c
camel/camel-network-service.h
docs/reference/camel/camel-sections.txt

index 282a38e..ea1b915 100644 (file)
 #define CAMEL_NETWORK_SERVICE_GET_PRIVATE(obj) \
        (g_object_get_data (G_OBJECT (obj), PRIVATE_KEY))
 
+#define G_IS_IO_ERROR(error, code) \
+       (g_error_matches ((error), G_IO_ERROR, (code)))
+
+#define G_IS_RESOLVER_ERROR(error, code) \
+       (g_error_matches ((error), G_RESOLVER_ERROR, (code)))
+
 typedef struct _CamelNetworkServicePrivate CamelNetworkServicePrivate;
 
 struct _CamelNetworkServicePrivate {
        GMutex property_lock;
        GSocketConnectable *connectable;
+       gboolean host_reachable;
+
+       GNetworkMonitor *network_monitor;
+       gulong network_changed_handler_id;
+
+       GCancellable *network_monitor_cancellable;
+       GMutex network_monitor_cancellable_lock;
+
+       GSource *update_host_reachable;
+       GMutex update_host_reachable_lock;
 };
 
 /* Forward Declarations */
@@ -49,14 +65,181 @@ G_DEFINE_INTERFACE (
        camel_network_service,
        CAMEL_TYPE_SERVICE)
 
+static void
+network_service_set_host_reachable (CamelNetworkService *service,
+                                    gboolean host_reachable)
+{
+       CamelNetworkServicePrivate *priv;
+
+       priv = CAMEL_NETWORK_SERVICE_GET_PRIVATE (service);
+       g_return_if_fail (priv != NULL);
+
+       g_mutex_lock (&priv->property_lock);
+
+       if (host_reachable == priv->host_reachable) {
+               g_mutex_unlock (&priv->property_lock);
+               return;
+       }
+
+       priv->host_reachable = host_reachable;
+
+       g_mutex_unlock (&priv->property_lock);
+
+       g_object_notify (G_OBJECT (service), "host-reachable");
+}
+
+static void
+network_service_can_reach_cb (GObject *source_object,
+                              GAsyncResult *result,
+                              gpointer user_data)
+{
+       CamelNetworkService *service;
+       GError *error = NULL;
+
+       service = CAMEL_NETWORK_SERVICE (user_data);
+
+       g_network_monitor_can_reach_finish (
+               G_NETWORK_MONITOR (source_object), result, &error);
+
+       if (error == NULL) {
+               network_service_set_host_reachable (service, 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)) {
+               network_service_set_host_reachable (service, FALSE);
+
+       } else if (G_IS_RESOLVER_ERROR (error, G_RESOLVER_ERROR_NOT_FOUND)) {
+               network_service_set_host_reachable (service, FALSE);
+
+       } else {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+       }
+
+       if (error != NULL)
+               g_error_free (error);
+
+       g_object_unref (service);
+}
+
+static gboolean
+network_service_update_host_reachable_idle_cb (gpointer user_data)
+{
+       CamelNetworkService *service;
+       CamelNetworkServicePrivate *priv;
+       GSocketConnectable *connectable;
+       GCancellable *cancellable;
+
+       service = CAMEL_NETWORK_SERVICE (user_data);
+       priv = CAMEL_NETWORK_SERVICE_GET_PRIVATE (service);
+       g_return_val_if_fail (priv != NULL, FALSE);
+
+       g_mutex_lock (&priv->update_host_reachable_lock);
+       g_source_unref (priv->update_host_reachable);
+       priv->update_host_reachable = NULL;
+       g_mutex_unlock (&priv->update_host_reachable_lock);
+
+       connectable = camel_network_service_ref_connectable (service);
+
+       g_mutex_lock (&priv->network_monitor_cancellable_lock);
+
+       cancellable = priv->network_monitor_cancellable;
+       priv->network_monitor_cancellable = NULL;
+
+       if (cancellable != NULL) {
+               g_cancellable_cancel (cancellable);
+               g_object_unref (cancellable);
+               cancellable = NULL;
+       }
+
+       if (connectable == NULL) {
+               network_service_set_host_reachable (service, FALSE);
+       } else {
+               cancellable = g_cancellable_new ();
+
+               g_network_monitor_can_reach_async (
+                       priv->network_monitor,
+                       connectable, cancellable,
+                       network_service_can_reach_cb,
+                       g_object_ref (service));
+       }
+
+       priv->network_monitor_cancellable = cancellable;
+
+       g_mutex_unlock (&priv->network_monitor_cancellable_lock);
+
+       if (connectable != NULL)
+               g_object_unref (connectable);
+
+       return FALSE;
+}
+
+static void
+network_service_update_host_reachable (CamelNetworkService *service)
+{
+       CamelNetworkServicePrivate *priv;
+       CamelSession *session;
+
+       priv = CAMEL_NETWORK_SERVICE_GET_PRIVATE (service);
+
+       session = camel_service_ref_session (CAMEL_SERVICE (service));
+
+       g_mutex_lock (&priv->update_host_reachable_lock);
+
+       if (priv->update_host_reachable == NULL) {
+               GMainContext *main_context;
+               GSource *idle_source;
+
+               main_context = camel_session_get_main_context (session);
+
+               idle_source = g_idle_source_new ();
+               g_source_set_priority (idle_source, G_PRIORITY_LOW);
+               g_source_set_callback (
+                       idle_source,
+                       network_service_update_host_reachable_idle_cb,
+                       g_object_ref (service),
+                       (GDestroyNotify) g_object_unref);
+               g_source_attach (idle_source, main_context);
+               priv->update_host_reachable = g_source_ref (idle_source);
+               g_source_unref (idle_source);
+       }
+
+       g_mutex_unlock (&priv->update_host_reachable_lock);
+
+       g_object_unref (session);
+}
+
+static void
+network_service_network_changed_cb (GNetworkMonitor *network_monitor,
+                                    gboolean network_available,
+                                    CamelNetworkService *service)
+{
+       network_service_update_host_reachable (service);
+}
+
 static CamelNetworkServicePrivate *
 network_service_private_new (CamelNetworkService *service)
 {
        CamelNetworkServicePrivate *priv;
+       GNetworkMonitor *network_monitor;
+       gulong handler_id;
 
        priv = g_slice_new0 (CamelNetworkServicePrivate);
 
        g_mutex_init (&priv->property_lock);
+       g_mutex_init (&priv->network_monitor_cancellable_lock);
+       g_mutex_init (&priv->update_host_reachable_lock);
+
+       /* Configure network monitoring. */
+
+       network_monitor = g_network_monitor_get_default ();
+       priv->network_monitor = g_object_ref (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;
 }
@@ -64,9 +247,22 @@ 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);
+
        g_clear_object (&priv->connectable);
+       g_clear_object (&priv->network_monitor);
+       g_clear_object (&priv->network_monitor_cancellable);
+
+       if (priv->update_host_reachable != NULL) {
+               g_source_destroy (priv->update_host_reachable);
+               g_source_unref (priv->update_host_reachable);
+       }
 
        g_mutex_clear (&priv->property_lock);
+       g_mutex_clear (&priv->network_monitor_cancellable_lock);
+       g_mutex_clear (&priv->update_host_reachable_lock);
 
        g_slice_free (CamelNetworkServicePrivate, priv);
 }
@@ -205,6 +401,16 @@ camel_network_service_default_init (CamelNetworkServiceInterface *interface)
                        G_TYPE_SOCKET_CONNECTABLE,
                        G_PARAM_READWRITE |
                        G_PARAM_STATIC_STRINGS));
+
+       g_object_interface_install_property (
+               interface,
+               g_param_spec_boolean (
+                       "host-reachable",
+                       "Host Reachable",
+                       "Whether the host is reachable",
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
 }
 
 void
@@ -362,10 +568,37 @@ camel_network_service_set_connectable (CamelNetworkService *service,
 
        g_mutex_unlock (&priv->property_lock);
 
+       network_service_update_host_reachable (service);
+
        g_object_notify (G_OBJECT (service), "connectable");
 }
 
 /**
+ * camel_network_service_get_host_reachable:
+ * @service: a #CamelNetworkService
+ *
+ * Returns %TRUE if @service believes that the host pointed to by
+ * #CamelNetworkService:connectable can be reached.  This property
+ * is updated automatically as network conditions change.
+ *
+ * Returns: whether the host is reachable
+ *
+ * Since: 3.8
+ **/
+gboolean
+camel_network_service_get_host_reachable (CamelNetworkService *service)
+{
+       CamelNetworkServicePrivate *priv;
+
+       g_return_val_if_fail (CAMEL_IS_NETWORK_SERVICE (service), FALSE);
+
+       priv = CAMEL_NETWORK_SERVICE_GET_PRIVATE (service);
+       g_return_val_if_fail (priv != NULL, FALSE);
+
+       return priv->host_reachable;
+}
+
+/**
  * camel_network_service_connect_sync:
  * @service: a #CamelNetworkService
  * @cancellable: optional #GCancellable object, or %NULL
index 75f388b..cf0a39c 100644 (file)
@@ -90,6 +90,8 @@ GSocketConnectable *
 void           camel_network_service_set_connectable
                                        (CamelNetworkService *service,
                                         GSocketConnectable *connectable);
+gboolean       camel_network_service_get_host_reachable
+                                       (CamelNetworkService *service);
 CamelStream *  camel_network_service_connect_sync
                                        (CamelNetworkService *service,
                                         GCancellable *cancellable,
index af93f2d..cccb546 100644 (file)
@@ -1806,6 +1806,7 @@ camel_network_service_get_service_name
 camel_network_service_get_default_port
 camel_network_service_ref_connectable
 camel_network_service_set_connectable
+camel_network_service_get_host_reachable
 camel_network_service_connect_sync
 <SUBSECTION Standard>
 CAMEL_NETWORK_SERVICE