bce8a338a7c088cb0db65eadf87d3a310e25e8ac
[platform/upstream/glib.git] / gio / gnetworkmonitorportal.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright 2016 Red Hat, Inc.
4  *
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.
9  *
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.
14  *
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/>.
17  */
18
19 #include "config.h"
20
21 #include "gnetworkmonitorportal.h"
22 #include "ginitable.h"
23 #include "giomodule-priv.h"
24 #include "xdp-dbus.h"
25 #include "gportalsupport.h"
26
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);
30
31 enum
32 {
33   PROP_0,
34   PROP_NETWORK_AVAILABLE,
35   PROP_NETWORK_METERED,
36   PROP_CONNECTIVITY
37 };
38
39 struct _GNetworkMonitorPortalPrivate
40 {
41   GDBusProxy *proxy;
42   gboolean has_network;
43   int version;
44
45   gboolean available;
46   gboolean metered;
47   GNetworkConnectivity connectivity;
48 };
49
50 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorPortal, g_network_monitor_portal, G_TYPE_NETWORK_MONITOR_BASE,
51                          G_ADD_PRIVATE (GNetworkMonitorPortal)
52                          G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
53                                                 g_network_monitor_portal_iface_init)
54                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
55                                                 g_network_monitor_portal_initable_iface_init)
56                          _g_io_modules_ensure_extension_points_registered ();
57                          g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
58                                                          g_define_type_id,
59                                                          "portal",
60                                                          40))
61
62 static void
63 g_network_monitor_portal_init (GNetworkMonitorPortal *nm)
64 {
65   nm->priv = g_network_monitor_portal_get_instance_private (nm);
66 }
67
68 static void
69 g_network_monitor_portal_get_property (GObject    *object,
70                                        guint       prop_id,
71                                        GValue     *value,
72                                        GParamSpec *pspec)
73 {
74   GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (object);
75
76   switch (prop_id)
77     {
78     case PROP_NETWORK_AVAILABLE:
79       g_value_set_boolean (value, nm->priv->available);
80       break;
81
82     case PROP_NETWORK_METERED:
83       g_value_set_boolean (value, nm->priv->metered);
84       break;
85
86     case PROP_CONNECTIVITY:
87       g_value_set_enum (value, nm->priv->connectivity);
88       break;
89
90     default:
91       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
92       break;
93     }
94 }
95
96 static void
97 got_available (GObject *source,
98                GAsyncResult *res,
99                gpointer data)
100 {
101   GDBusProxy *proxy = G_DBUS_PROXY (source);
102   GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data);
103   GError *error = NULL;
104   GVariant *ret;
105   gboolean available;
106   
107   ret = g_dbus_proxy_call_finish (proxy, res, &error);
108   if (ret == NULL)
109     {
110       g_warning ("%s", error->message);
111       g_clear_error (&error);
112       return;
113     }
114
115   g_variant_get (ret, "(b)", &available);
116   g_variant_unref (ret);
117
118   if (nm->priv->available != available)
119     {
120       nm->priv->available = available;
121       g_object_notify (G_OBJECT (nm), "network-available");
122       g_signal_emit_by_name (nm, "network-changed", available);
123     }
124 }
125
126 static void
127 got_metered (GObject *source,
128              GAsyncResult *res,
129              gpointer data)
130 {
131   GDBusProxy *proxy = G_DBUS_PROXY (source);
132   GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data);
133   GError *error = NULL;
134   GVariant *ret;
135   gboolean metered;
136   
137   ret = g_dbus_proxy_call_finish (proxy, res, &error);
138   if (ret == NULL)
139     {
140       g_warning ("%s", error->message);
141       g_clear_error (&error);
142       return;
143     }
144
145   g_variant_get (ret, "(b)", &metered);
146   g_variant_unref (ret);
147
148   if (nm->priv->metered != metered)
149     {
150       nm->priv->metered = metered;
151       g_object_notify (G_OBJECT (nm), "network-metered");
152     }
153 }
154
155 static void
156 got_connectivity (GObject *source,
157                   GAsyncResult *res,
158                   gpointer data)
159 {
160   GDBusProxy *proxy = G_DBUS_PROXY (source);
161   GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data);
162   GError *error = NULL;
163   GVariant *ret;
164   GNetworkConnectivity connectivity;
165   
166   ret = g_dbus_proxy_call_finish (proxy, res, &error);
167   if (ret == NULL)
168     {
169       g_warning ("%s", error->message);
170       g_clear_error (&error);
171       return;
172     }
173
174   g_variant_get (ret, "(u)", &connectivity);
175   g_variant_unref (ret);
176
177   if (nm->priv->connectivity != connectivity)
178     {
179       nm->priv->connectivity = connectivity;
180       g_object_notify (G_OBJECT (nm), "connectivity");
181     }
182 }
183
184 static void
185 update_properties (GDBusProxy *proxy,
186                    GNetworkMonitorPortal *nm)
187 {
188   g_dbus_proxy_call (proxy, "GetConnectivity", NULL, 0, -1, NULL, got_connectivity, nm);
189   g_dbus_proxy_call (proxy, "GetMetered", NULL, 0, -1, NULL, got_metered, nm);
190   g_dbus_proxy_call (proxy, "GetAvailable", NULL, 0, -1, NULL, got_available, nm);
191 }
192
193 static void
194 proxy_signal (GDBusProxy *proxy,
195               const char *sender,
196               const char *signal,
197               GVariant *parameters,
198               GNetworkMonitorPortal *nm)
199 {
200   if (!nm->priv->has_network)
201     return;
202
203   if (nm->priv->version == 1)
204     {
205       gboolean available;
206
207       g_variant_get (parameters, "(b)", &available);
208       g_signal_emit_by_name (nm, "network-changed", available);
209     }
210   else if (nm->priv->version == 2)
211     {
212       update_properties (proxy, nm);
213     }
214 }
215
216 static void
217 proxy_properties_changed (GDBusProxy *proxy,
218                           GVariant *changed,
219                           GVariant *invalidated,
220                           GNetworkMonitorPortal *nm)
221 {
222   if (!nm->priv->has_network)
223     return;
224
225   if (nm->priv->version == 1)
226     {
227       GVariant *ret;
228
229       ret = g_dbus_proxy_get_cached_property (proxy, "connectivity");
230       if (ret)
231         {
232           GNetworkConnectivity connectivity = g_variant_get_uint32 (ret);
233           if (nm->priv->connectivity != connectivity)
234             {
235               nm->priv->connectivity = connectivity;
236               g_object_notify (G_OBJECT (nm), "connectivity");
237             }
238           g_variant_unref (ret);
239         }
240
241       ret = g_dbus_proxy_get_cached_property (proxy, "metered");
242       if (ret)
243         {
244           gboolean metered = g_variant_get_boolean (ret);
245           if (nm->priv->metered != metered)
246             {
247               nm->priv->metered = metered;
248               g_object_notify (G_OBJECT (nm), "network-metered");
249             }
250           g_variant_unref (ret);
251         }
252
253       ret = g_dbus_proxy_get_cached_property (proxy, "available");
254       if (ret)
255         {
256           gboolean available = g_variant_get_boolean (ret);
257           if (nm->priv->available != available)
258             {
259               nm->priv->available = available;
260               g_object_notify (G_OBJECT (nm), "network-available");
261               g_signal_emit_by_name (nm, "network-changed", available);
262             }
263           g_variant_unref (ret);
264         }
265     }
266 }
267                            
268 static gboolean
269 g_network_monitor_portal_initable_init (GInitable     *initable,
270                                         GCancellable  *cancellable,
271                                         GError       **error)
272 {
273   GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (initable);
274   GDBusProxy *proxy;
275   gchar *name_owner = NULL;
276   int version;
277   GVariant *ret;
278
279   nm->priv->available = FALSE;
280   nm->priv->metered = FALSE;
281   nm->priv->connectivity = G_NETWORK_CONNECTIVITY_LOCAL;
282
283   if (!glib_should_use_portal ())
284     {
285       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not using portals");
286       return FALSE;
287     }
288
289   proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
290                                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START
291                                          | G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
292                                          NULL,
293                                          "org.freedesktop.portal.Desktop",
294                                          "/org/freedesktop/portal/desktop",
295                                          "org.freedesktop.portal.NetworkMonitor",
296                                          cancellable,
297                                          error);
298   if (!proxy)
299     return FALSE;
300
301   name_owner = g_dbus_proxy_get_name_owner (proxy);
302
303   if (!name_owner)
304     {
305       g_object_unref (proxy);
306       g_set_error (error,
307                    G_DBUS_ERROR,
308                    G_DBUS_ERROR_NAME_HAS_NO_OWNER,
309                    "Desktop portal not found");
310       return FALSE;
311     }
312
313   g_free (name_owner);
314
315   ret = g_dbus_proxy_get_cached_property (proxy, "version");
316   g_variant_get (ret, "u", &version);
317   g_variant_unref (ret);
318
319   if (version != 1 && version != 2)
320     {
321       g_object_unref (proxy);
322       g_set_error (error,
323                    G_DBUS_ERROR,
324                    G_DBUS_ERROR_NAME_HAS_NO_OWNER,
325                    "NetworkMonitor portal unsupported version: %d", version);
326       return FALSE;
327     }
328
329   g_signal_connect (proxy, "g-signal", G_CALLBACK (proxy_signal), nm);
330   g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (proxy_properties_changed), nm);
331
332   nm->priv->proxy = proxy;
333   nm->priv->has_network = glib_network_available_in_sandbox ();
334   nm->priv->version = version;
335
336   if (!initable_parent_iface->init (initable, cancellable, error))
337     return FALSE;
338
339   if (nm->priv->has_network && nm->priv->version == 2)
340     update_properties (proxy, nm);
341
342   return TRUE;
343 }
344
345 static void
346 g_network_monitor_portal_finalize (GObject *object)
347 {
348   GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (object);
349
350   g_clear_object (&nm->priv->proxy);
351
352   G_OBJECT_CLASS (g_network_monitor_portal_parent_class)->finalize (object);
353 }
354
355 static void
356 g_network_monitor_portal_class_init (GNetworkMonitorPortalClass *class)
357 {
358   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
359
360   gobject_class->finalize  = g_network_monitor_portal_finalize;
361   gobject_class->get_property = g_network_monitor_portal_get_property;
362
363   g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
364   g_object_class_override_property (gobject_class, PROP_NETWORK_METERED, "network-metered");
365   g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
366 }
367
368 static void
369 g_network_monitor_portal_iface_init (GNetworkMonitorInterface *monitor_iface)
370 {
371 }
372
373 static void
374 g_network_monitor_portal_initable_iface_init (GInitableIface *iface)
375 {
376   initable_parent_iface = g_type_interface_peek_parent (iface);
377
378   iface->init = g_network_monitor_portal_initable_init;
379 }