1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright 2016 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 #include "gnetworkmonitorportal.h"
22 #include "ginitable.h"
23 #include "giomodule-priv.h"
25 #include "gportalsupport.h"
27 static GInitableIface *initable_parent_iface;
28 static void g_network_monitor_portal_iface_init (GNetworkMonitorInterface *iface);
29 static void g_network_monitor_portal_initable_iface_init (GInitableIface *iface);
34 PROP_NETWORK_AVAILABLE,
39 struct _GNetworkMonitorPortalPrivate
46 GNetworkConnectivity connectivity;
49 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorPortal, g_network_monitor_portal, G_TYPE_NETWORK_MONITOR_BASE,
50 G_ADD_PRIVATE (GNetworkMonitorPortal)
51 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
52 g_network_monitor_portal_iface_init)
53 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
54 g_network_monitor_portal_initable_iface_init)
55 _g_io_modules_ensure_extension_points_registered ();
56 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
62 g_network_monitor_portal_init (GNetworkMonitorPortal *nm)
64 nm->priv = g_network_monitor_portal_get_instance_private (nm);
68 g_network_monitor_portal_get_property (GObject *object,
73 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (object);
77 case PROP_NETWORK_AVAILABLE:
78 g_value_set_boolean (value, nm->priv->available);
81 case PROP_NETWORK_METERED:
82 g_value_set_boolean (value, nm->priv->metered);
85 case PROP_CONNECTIVITY:
86 g_value_set_enum (value, nm->priv->connectivity);
90 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96 is_valid_connectivity (guint32 value)
98 GEnumValue *enum_value;
99 GEnumClass *enum_klass;
101 enum_klass = g_type_class_ref (G_TYPE_NETWORK_CONNECTIVITY);
102 enum_value = g_enum_get_value (enum_klass, value);
104 g_type_class_unref (enum_klass);
106 return enum_value != NULL;
110 got_available (GObject *source,
114 GDBusProxy *proxy = G_DBUS_PROXY (source);
115 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data);
116 GError *error = NULL;
120 ret = g_dbus_proxy_call_finish (proxy, res, &error);
123 if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
125 g_warning ("%s", error->message);
126 g_clear_error (&error);
130 g_clear_error (&error);
132 /* Fall back to version 1 */
133 ret = g_dbus_proxy_get_cached_property (nm->priv->proxy, "available");
136 g_warning ("Failed to get the '%s' property", "available");
140 available = g_variant_get_boolean (ret);
141 g_variant_unref (ret);
145 g_variant_get (ret, "(b)", &available);
146 g_variant_unref (ret);
149 if (nm->priv->available != available)
151 nm->priv->available = available;
152 g_object_notify (G_OBJECT (nm), "network-available");
153 g_signal_emit_by_name (nm, "network-changed", available);
158 got_metered (GObject *source,
162 GDBusProxy *proxy = G_DBUS_PROXY (source);
163 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data);
164 GError *error = NULL;
168 ret = g_dbus_proxy_call_finish (proxy, res, &error);
171 if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
173 g_warning ("%s", error->message);
174 g_clear_error (&error);
178 g_clear_error (&error);
180 /* Fall back to version 1 */
181 ret = g_dbus_proxy_get_cached_property (nm->priv->proxy, "metered");
184 g_warning ("Failed to get the '%s' property", "metered");
188 metered = g_variant_get_boolean (ret);
189 g_variant_unref (ret);
193 g_variant_get (ret, "(b)", &metered);
194 g_variant_unref (ret);
197 if (nm->priv->metered != metered)
199 nm->priv->metered = metered;
200 g_object_notify (G_OBJECT (nm), "network-metered");
201 g_signal_emit_by_name (nm, "network-changed", nm->priv->available);
206 got_connectivity (GObject *source,
210 GDBusProxy *proxy = G_DBUS_PROXY (source);
211 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data);
212 GError *error = NULL;
214 GNetworkConnectivity connectivity;
216 ret = g_dbus_proxy_call_finish (proxy, res, &error);
219 if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
221 g_warning ("%s", error->message);
222 g_clear_error (&error);
226 g_clear_error (&error);
228 /* Fall back to version 1 */
229 ret = g_dbus_proxy_get_cached_property (nm->priv->proxy, "connectivity");
232 g_warning ("Failed to get the '%s' property", "connectivity");
236 connectivity = g_variant_get_uint32 (ret);
237 g_variant_unref (ret);
241 g_variant_get (ret, "(u)", &connectivity);
242 g_variant_unref (ret);
245 if (nm->priv->connectivity != connectivity &&
246 is_valid_connectivity (connectivity))
248 nm->priv->connectivity = connectivity;
249 g_object_notify (G_OBJECT (nm), "connectivity");
250 g_signal_emit_by_name (nm, "network-changed", nm->priv->available);
255 got_status (GObject *source,
259 GDBusProxy *proxy = G_DBUS_PROXY (source);
260 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data);
261 GError *error = NULL;
266 GNetworkConnectivity connectivity;
268 ret = g_dbus_proxy_call_finish (proxy, res, &error);
271 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
273 /* Fall back to version 2 */
274 g_dbus_proxy_call (proxy, "GetConnectivity", NULL, 0, -1, NULL, got_connectivity, nm);
275 g_dbus_proxy_call (proxy, "GetMetered", NULL, 0, -1, NULL, got_metered, nm);
276 g_dbus_proxy_call (proxy, "GetAvailable", NULL, 0, -1, NULL, got_available, nm);
279 g_warning ("%s", error->message);
281 g_clear_error (&error);
285 g_variant_get (ret, "(@a{sv})", &status);
286 g_variant_unref (ret);
288 g_variant_lookup (status, "available", "b", &available);
289 g_variant_lookup (status, "metered", "b", &metered);
290 g_variant_lookup (status, "connectivity", "u", &connectivity);
291 g_variant_unref (status);
293 g_object_freeze_notify (G_OBJECT (nm));
295 if (nm->priv->available != available)
297 nm->priv->available = available;
298 g_object_notify (G_OBJECT (nm), "network-available");
301 if (nm->priv->metered != metered)
303 nm->priv->metered = metered;
304 g_object_notify (G_OBJECT (nm), "network-metered");
307 if (nm->priv->connectivity != connectivity &&
308 is_valid_connectivity (connectivity))
310 nm->priv->connectivity = connectivity;
311 g_object_notify (G_OBJECT (nm), "connectivity");
314 g_object_thaw_notify (G_OBJECT (nm));
316 g_signal_emit_by_name (nm, "network-changed", available);
320 update_properties (GDBusProxy *proxy,
321 GNetworkMonitorPortal *nm)
323 /* Try version 3 first */
324 g_dbus_proxy_call (proxy, "GetStatus", NULL, 0, -1, NULL, got_status, nm);
328 proxy_signal (GDBusProxy *proxy,
331 GVariant *parameters,
332 GNetworkMonitorPortal *nm)
334 if (!nm->priv->has_network)
337 if (strcmp (signal, "changed") != 0)
340 /* Version 1 updates "available" with the "changed" signal */
341 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(b)")))
345 g_variant_get (parameters, "(b)", &available);
346 if (nm->priv->available != available)
348 nm->priv->available = available;
349 g_object_notify (G_OBJECT (nm), "available");
351 g_signal_emit_by_name (nm, "network-changed", available);
355 update_properties (proxy, nm);
360 proxy_properties_changed (GDBusProxy *proxy,
362 GVariant *invalidated,
363 GNetworkMonitorPortal *nm)
365 gboolean should_emit_changed = FALSE;
368 if (!nm->priv->has_network)
371 ret = g_dbus_proxy_get_cached_property (proxy, "connectivity");
374 GNetworkConnectivity connectivity = g_variant_get_uint32 (ret);
375 if (nm->priv->connectivity != connectivity &&
376 is_valid_connectivity (connectivity))
378 nm->priv->connectivity = connectivity;
379 g_object_notify (G_OBJECT (nm), "connectivity");
380 should_emit_changed = TRUE;
382 g_variant_unref (ret);
385 ret = g_dbus_proxy_get_cached_property (proxy, "metered");
388 gboolean metered = g_variant_get_boolean (ret);
389 if (nm->priv->metered != metered)
391 nm->priv->metered = metered;
392 g_object_notify (G_OBJECT (nm), "network-metered");
393 should_emit_changed = TRUE;
395 g_variant_unref (ret);
398 ret = g_dbus_proxy_get_cached_property (proxy, "available");
401 gboolean available = g_variant_get_boolean (ret);
402 if (nm->priv->available != available)
404 nm->priv->available = available;
405 g_object_notify (G_OBJECT (nm), "network-available");
406 should_emit_changed = TRUE;
408 g_variant_unref (ret);
411 if (should_emit_changed)
412 g_signal_emit_by_name (nm, "network-changed", nm->priv->available);
416 g_network_monitor_portal_initable_init (GInitable *initable,
417 GCancellable *cancellable,
420 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (initable);
422 gchar *name_owner = NULL;
424 nm->priv->available = FALSE;
425 nm->priv->metered = FALSE;
426 nm->priv->connectivity = G_NETWORK_CONNECTIVITY_LOCAL;
428 if (!glib_should_use_portal ())
430 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not using portals");
434 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
435 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
437 "org.freedesktop.portal.Desktop",
438 "/org/freedesktop/portal/desktop",
439 "org.freedesktop.portal.NetworkMonitor",
445 name_owner = g_dbus_proxy_get_name_owner (proxy);
449 g_object_unref (proxy);
452 G_DBUS_ERROR_NAME_HAS_NO_OWNER,
453 "Desktop portal not found");
459 g_signal_connect (proxy, "g-signal", G_CALLBACK (proxy_signal), nm);
460 g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (proxy_properties_changed), nm);
462 nm->priv->proxy = proxy;
463 nm->priv->has_network = glib_network_available_in_sandbox ();
465 if (!initable_parent_iface->init (initable, cancellable, error))
468 if (nm->priv->has_network)
469 update_properties (proxy, nm);
475 g_network_monitor_portal_finalize (GObject *object)
477 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (object);
479 g_clear_object (&nm->priv->proxy);
481 G_OBJECT_CLASS (g_network_monitor_portal_parent_class)->finalize (object);
485 g_network_monitor_portal_class_init (GNetworkMonitorPortalClass *class)
487 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
489 gobject_class->finalize = g_network_monitor_portal_finalize;
490 gobject_class->get_property = g_network_monitor_portal_get_property;
492 g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
493 g_object_class_override_property (gobject_class, PROP_NETWORK_METERED, "network-metered");
494 g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
498 g_network_monitor_portal_can_reach (GNetworkMonitor *monitor,
499 GSocketConnectable *connectable,
500 GCancellable *cancellable,
503 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (monitor);
505 GNetworkAddress *address;
506 gboolean reachable = FALSE;
508 if (!G_IS_NETWORK_ADDRESS (connectable))
510 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
511 "Can't handle this kind of GSocketConnectable (%s)",
512 G_OBJECT_TYPE_NAME (connectable));
516 address = G_NETWORK_ADDRESS (connectable);
518 ret = g_dbus_proxy_call_sync (nm->priv->proxy,
520 g_variant_new ("(su)",
521 g_network_address_get_hostname (address),
522 g_network_address_get_port (address)),
523 G_DBUS_CALL_FLAGS_NONE,
530 g_variant_get (ret, "(b)", &reachable);
531 g_variant_unref (ret);
538 can_reach_done (GObject *source,
539 GAsyncResult *result,
543 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (g_task_get_source_object (task));
544 GError *error = NULL;
548 ret = g_dbus_proxy_call_finish (nm->priv->proxy, result, &error);
551 g_task_return_error (task, error);
552 g_object_unref (task);
556 g_variant_get (ret, "(b)", &reachable);
557 g_variant_unref (ret);
560 g_task_return_boolean (task, TRUE);
562 g_task_return_new_error (task,
563 G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
566 g_object_unref (task);
570 g_network_monitor_portal_can_reach_async (GNetworkMonitor *monitor,
571 GSocketConnectable *connectable,
572 GCancellable *cancellable,
573 GAsyncReadyCallback callback,
576 GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (monitor);
578 GNetworkAddress *address;
580 task = g_task_new (monitor, cancellable, callback, data);
582 if (!G_IS_NETWORK_ADDRESS (connectable))
584 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
585 "Can't handle this kind of GSocketConnectable (%s)",
586 G_OBJECT_TYPE_NAME (connectable));
587 g_object_unref (task);
591 address = G_NETWORK_ADDRESS (connectable);
593 g_dbus_proxy_call (nm->priv->proxy,
595 g_variant_new ("(su)",
596 g_network_address_get_hostname (address),
597 g_network_address_get_port (address)),
598 G_DBUS_CALL_FLAGS_NONE,
606 g_network_monitor_portal_can_reach_finish (GNetworkMonitor *monitor,
607 GAsyncResult *result,
610 return g_task_propagate_boolean (G_TASK (result), error);
614 g_network_monitor_portal_iface_init (GNetworkMonitorInterface *monitor_iface)
616 monitor_iface->can_reach = g_network_monitor_portal_can_reach;
617 monitor_iface->can_reach_async = g_network_monitor_portal_can_reach_async;
618 monitor_iface->can_reach_finish = g_network_monitor_portal_can_reach_finish;
622 g_network_monitor_portal_initable_iface_init (GInitableIface *iface)
624 initable_parent_iface = g_type_interface_peek_parent (iface);
626 iface->init = g_network_monitor_portal_initable_init;