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 | |
44 | gboolean available; |
45 | gboolean metered; |
46 | GNetworkConnectivity connectivity; |
47 | }; |
48 | |
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, |
57 | g_define_type_id, |
58 | "portal" , |
59 | 40)) |
60 | |
61 | static void |
62 | g_network_monitor_portal_init (GNetworkMonitorPortal *nm) |
63 | { |
64 | nm->priv = g_network_monitor_portal_get_instance_private (self: nm); |
65 | } |
66 | |
67 | static void |
68 | g_network_monitor_portal_get_property (GObject *object, |
69 | guint prop_id, |
70 | GValue *value, |
71 | GParamSpec *pspec) |
72 | { |
73 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (object); |
74 | |
75 | switch (prop_id) |
76 | { |
77 | case PROP_NETWORK_AVAILABLE: |
78 | g_value_set_boolean (value, v_boolean: nm->priv->available); |
79 | break; |
80 | |
81 | case PROP_NETWORK_METERED: |
82 | g_value_set_boolean (value, v_boolean: nm->priv->metered); |
83 | break; |
84 | |
85 | case PROP_CONNECTIVITY: |
86 | g_value_set_enum (value, v_enum: nm->priv->connectivity); |
87 | break; |
88 | |
89 | default: |
90 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
91 | break; |
92 | } |
93 | } |
94 | |
95 | static gboolean |
96 | is_valid_connectivity (guint32 value) |
97 | { |
98 | GEnumValue *enum_value; |
99 | GEnumClass *enum_klass; |
100 | |
101 | enum_klass = g_type_class_ref (type: G_TYPE_NETWORK_CONNECTIVITY); |
102 | enum_value = g_enum_get_value (enum_class: enum_klass, value); |
103 | |
104 | g_type_class_unref (g_class: enum_klass); |
105 | |
106 | return enum_value != NULL; |
107 | } |
108 | |
109 | static void |
110 | got_available (GObject *source, |
111 | GAsyncResult *res, |
112 | gpointer data) |
113 | { |
114 | GDBusProxy *proxy = G_DBUS_PROXY (source); |
115 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data); |
116 | GError *error = NULL; |
117 | GVariant *ret; |
118 | gboolean available; |
119 | |
120 | ret = g_dbus_proxy_call_finish (proxy, res, &error); |
121 | if (ret == NULL) |
122 | { |
123 | if (!g_error_matches (error, domain: G_DBUS_ERROR, code: G_DBUS_ERROR_UNKNOWN_METHOD)) |
124 | { |
125 | g_warning ("%s" , error->message); |
126 | g_clear_error (err: &error); |
127 | return; |
128 | } |
129 | |
130 | g_clear_error (err: &error); |
131 | |
132 | /* Fall back to version 1 */ |
133 | ret = g_dbus_proxy_get_cached_property (nm->priv->proxy, "available" ); |
134 | if (ret == NULL) |
135 | { |
136 | g_warning ("Failed to get the '%s' property" , "available" ); |
137 | return; |
138 | } |
139 | |
140 | available = g_variant_get_boolean (value: ret); |
141 | g_variant_unref (value: ret); |
142 | } |
143 | else |
144 | { |
145 | g_variant_get (value: ret, format_string: "(b)" , &available); |
146 | g_variant_unref (value: ret); |
147 | } |
148 | |
149 | if (nm->priv->available != available) |
150 | { |
151 | nm->priv->available = available; |
152 | g_object_notify (G_OBJECT (nm), property_name: "network-available" ); |
153 | g_signal_emit_by_name (instance: nm, detailed_signal: "network-changed" , available); |
154 | } |
155 | } |
156 | |
157 | static void |
158 | got_metered (GObject *source, |
159 | GAsyncResult *res, |
160 | gpointer data) |
161 | { |
162 | GDBusProxy *proxy = G_DBUS_PROXY (source); |
163 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data); |
164 | GError *error = NULL; |
165 | GVariant *ret; |
166 | gboolean metered; |
167 | |
168 | ret = g_dbus_proxy_call_finish (proxy, res, &error); |
169 | if (ret == NULL) |
170 | { |
171 | if (!g_error_matches (error, domain: G_DBUS_ERROR, code: G_DBUS_ERROR_UNKNOWN_METHOD)) |
172 | { |
173 | g_warning ("%s" , error->message); |
174 | g_clear_error (err: &error); |
175 | return; |
176 | } |
177 | |
178 | g_clear_error (err: &error); |
179 | |
180 | /* Fall back to version 1 */ |
181 | ret = g_dbus_proxy_get_cached_property (nm->priv->proxy, "metered" ); |
182 | if (ret == NULL) |
183 | { |
184 | g_warning ("Failed to get the '%s' property" , "metered" ); |
185 | return; |
186 | } |
187 | |
188 | metered = g_variant_get_boolean (value: ret); |
189 | g_variant_unref (value: ret); |
190 | } |
191 | else |
192 | { |
193 | g_variant_get (value: ret, format_string: "(b)" , &metered); |
194 | g_variant_unref (value: ret); |
195 | } |
196 | |
197 | if (nm->priv->metered != metered) |
198 | { |
199 | nm->priv->metered = metered; |
200 | g_object_notify (G_OBJECT (nm), property_name: "network-metered" ); |
201 | g_signal_emit_by_name (instance: nm, detailed_signal: "network-changed" , nm->priv->available); |
202 | } |
203 | } |
204 | |
205 | static void |
206 | got_connectivity (GObject *source, |
207 | GAsyncResult *res, |
208 | gpointer data) |
209 | { |
210 | GDBusProxy *proxy = G_DBUS_PROXY (source); |
211 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data); |
212 | GError *error = NULL; |
213 | GVariant *ret; |
214 | GNetworkConnectivity connectivity; |
215 | |
216 | ret = g_dbus_proxy_call_finish (proxy, res, &error); |
217 | if (ret == NULL) |
218 | { |
219 | if (!g_error_matches (error, domain: G_DBUS_ERROR, code: G_DBUS_ERROR_UNKNOWN_METHOD)) |
220 | { |
221 | g_warning ("%s" , error->message); |
222 | g_clear_error (err: &error); |
223 | return; |
224 | } |
225 | |
226 | g_clear_error (err: &error); |
227 | |
228 | /* Fall back to version 1 */ |
229 | ret = g_dbus_proxy_get_cached_property (nm->priv->proxy, "connectivity" ); |
230 | if (ret == NULL) |
231 | { |
232 | g_warning ("Failed to get the '%s' property" , "connectivity" ); |
233 | return; |
234 | } |
235 | |
236 | connectivity = g_variant_get_uint32 (value: ret); |
237 | g_variant_unref (value: ret); |
238 | } |
239 | else |
240 | { |
241 | g_variant_get (value: ret, format_string: "(u)" , &connectivity); |
242 | g_variant_unref (value: ret); |
243 | } |
244 | |
245 | if (nm->priv->connectivity != connectivity && |
246 | is_valid_connectivity (value: connectivity)) |
247 | { |
248 | nm->priv->connectivity = connectivity; |
249 | g_object_notify (G_OBJECT (nm), property_name: "connectivity" ); |
250 | g_signal_emit_by_name (instance: nm, detailed_signal: "network-changed" , nm->priv->available); |
251 | } |
252 | } |
253 | |
254 | static void |
255 | got_status (GObject *source, |
256 | GAsyncResult *res, |
257 | gpointer data) |
258 | { |
259 | GDBusProxy *proxy = G_DBUS_PROXY (source); |
260 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (data); |
261 | GError *error = NULL; |
262 | GVariant *ret; |
263 | GVariant *status; |
264 | gboolean available; |
265 | gboolean metered; |
266 | GNetworkConnectivity connectivity; |
267 | |
268 | ret = g_dbus_proxy_call_finish (proxy, res, &error); |
269 | if (ret == NULL) |
270 | { |
271 | if (g_error_matches (error, domain: G_DBUS_ERROR, code: G_DBUS_ERROR_UNKNOWN_METHOD)) |
272 | { |
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); |
277 | } |
278 | else |
279 | g_warning ("%s" , error->message); |
280 | |
281 | g_clear_error (err: &error); |
282 | return; |
283 | } |
284 | |
285 | g_variant_get (value: ret, format_string: "(@a{sv})" , &status); |
286 | g_variant_unref (value: ret); |
287 | |
288 | g_variant_lookup (dictionary: status, key: "available" , format_string: "b" , &available); |
289 | g_variant_lookup (dictionary: status, key: "metered" , format_string: "b" , &metered); |
290 | g_variant_lookup (dictionary: status, key: "connectivity" , format_string: "u" , &connectivity); |
291 | g_variant_unref (value: status); |
292 | |
293 | g_object_freeze_notify (G_OBJECT (nm)); |
294 | |
295 | if (nm->priv->available != available) |
296 | { |
297 | nm->priv->available = available; |
298 | g_object_notify (G_OBJECT (nm), property_name: "network-available" ); |
299 | } |
300 | |
301 | if (nm->priv->metered != metered) |
302 | { |
303 | nm->priv->metered = metered; |
304 | g_object_notify (G_OBJECT (nm), property_name: "network-metered" ); |
305 | } |
306 | |
307 | if (nm->priv->connectivity != connectivity && |
308 | is_valid_connectivity (value: connectivity)) |
309 | { |
310 | nm->priv->connectivity = connectivity; |
311 | g_object_notify (G_OBJECT (nm), property_name: "connectivity" ); |
312 | } |
313 | |
314 | g_object_thaw_notify (G_OBJECT (nm)); |
315 | |
316 | g_signal_emit_by_name (instance: nm, detailed_signal: "network-changed" , available); |
317 | } |
318 | |
319 | static void |
320 | update_properties (GDBusProxy *proxy, |
321 | GNetworkMonitorPortal *nm) |
322 | { |
323 | /* Try version 3 first */ |
324 | g_dbus_proxy_call (proxy, "GetStatus" , NULL, 0, -1, NULL, got_status, nm); |
325 | } |
326 | |
327 | static void |
328 | proxy_signal (GDBusProxy *proxy, |
329 | const char *sender, |
330 | const char *signal, |
331 | GVariant *parameters, |
332 | GNetworkMonitorPortal *nm) |
333 | { |
334 | if (!nm->priv->has_network) |
335 | return; |
336 | |
337 | if (strcmp (s1: signal, s2: "changed" ) != 0) |
338 | return; |
339 | |
340 | /* Version 1 updates "available" with the "changed" signal */ |
341 | if (g_variant_is_of_type (value: parameters, G_VARIANT_TYPE ("(b)" ))) |
342 | { |
343 | gboolean available; |
344 | |
345 | g_variant_get (value: parameters, format_string: "(b)" , &available); |
346 | if (nm->priv->available != available) |
347 | { |
348 | nm->priv->available = available; |
349 | g_object_notify (G_OBJECT (nm), property_name: "available" ); |
350 | } |
351 | g_signal_emit_by_name (instance: nm, detailed_signal: "network-changed" , available); |
352 | } |
353 | else |
354 | { |
355 | update_properties (proxy, nm); |
356 | } |
357 | } |
358 | |
359 | static void |
360 | proxy_properties_changed (GDBusProxy *proxy, |
361 | GVariant *changed, |
362 | GVariant *invalidated, |
363 | GNetworkMonitorPortal *nm) |
364 | { |
365 | gboolean should_emit_changed = FALSE; |
366 | GVariant *ret; |
367 | |
368 | if (!nm->priv->has_network) |
369 | return; |
370 | |
371 | ret = g_dbus_proxy_get_cached_property (proxy, "connectivity" ); |
372 | if (ret) |
373 | { |
374 | GNetworkConnectivity connectivity = g_variant_get_uint32 (value: ret); |
375 | if (nm->priv->connectivity != connectivity && |
376 | is_valid_connectivity (value: connectivity)) |
377 | { |
378 | nm->priv->connectivity = connectivity; |
379 | g_object_notify (G_OBJECT (nm), property_name: "connectivity" ); |
380 | should_emit_changed = TRUE; |
381 | } |
382 | g_variant_unref (value: ret); |
383 | } |
384 | |
385 | ret = g_dbus_proxy_get_cached_property (proxy, "metered" ); |
386 | if (ret) |
387 | { |
388 | gboolean metered = g_variant_get_boolean (value: ret); |
389 | if (nm->priv->metered != metered) |
390 | { |
391 | nm->priv->metered = metered; |
392 | g_object_notify (G_OBJECT (nm), property_name: "network-metered" ); |
393 | should_emit_changed = TRUE; |
394 | } |
395 | g_variant_unref (value: ret); |
396 | } |
397 | |
398 | ret = g_dbus_proxy_get_cached_property (proxy, "available" ); |
399 | if (ret) |
400 | { |
401 | gboolean available = g_variant_get_boolean (value: ret); |
402 | if (nm->priv->available != available) |
403 | { |
404 | nm->priv->available = available; |
405 | g_object_notify (G_OBJECT (nm), property_name: "network-available" ); |
406 | should_emit_changed = TRUE; |
407 | } |
408 | g_variant_unref (value: ret); |
409 | } |
410 | |
411 | if (should_emit_changed) |
412 | g_signal_emit_by_name (instance: nm, detailed_signal: "network-changed" , nm->priv->available); |
413 | } |
414 | |
415 | static gboolean |
416 | g_network_monitor_portal_initable_init (GInitable *initable, |
417 | GCancellable *cancellable, |
418 | GError **error) |
419 | { |
420 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (initable); |
421 | GDBusProxy *proxy; |
422 | gchar *name_owner = NULL; |
423 | |
424 | nm->priv->available = FALSE; |
425 | nm->priv->metered = FALSE; |
426 | nm->priv->connectivity = G_NETWORK_CONNECTIVITY_LOCAL; |
427 | |
428 | if (!glib_should_use_portal ()) |
429 | { |
430 | g_set_error (err: error, domain: G_IO_ERROR, code: G_IO_ERROR_FAILED, format: "Not using portals" ); |
431 | return FALSE; |
432 | } |
433 | |
434 | proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, |
435 | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, |
436 | NULL, |
437 | "org.freedesktop.portal.Desktop" , |
438 | "/org/freedesktop/portal/desktop" , |
439 | "org.freedesktop.portal.NetworkMonitor" , |
440 | cancellable, |
441 | error); |
442 | if (!proxy) |
443 | return FALSE; |
444 | |
445 | name_owner = g_dbus_proxy_get_name_owner (proxy); |
446 | |
447 | if (!name_owner) |
448 | { |
449 | g_object_unref (object: proxy); |
450 | g_set_error (err: error, |
451 | domain: G_DBUS_ERROR, |
452 | code: G_DBUS_ERROR_NAME_HAS_NO_OWNER, |
453 | format: "Desktop portal not found" ); |
454 | return FALSE; |
455 | } |
456 | |
457 | g_free (mem: name_owner); |
458 | |
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); |
461 | |
462 | nm->priv->proxy = proxy; |
463 | nm->priv->has_network = glib_network_available_in_sandbox (); |
464 | |
465 | if (!initable_parent_iface->init (initable, cancellable, error)) |
466 | return FALSE; |
467 | |
468 | if (nm->priv->has_network) |
469 | update_properties (proxy, nm); |
470 | |
471 | return TRUE; |
472 | } |
473 | |
474 | static void |
475 | g_network_monitor_portal_finalize (GObject *object) |
476 | { |
477 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (object); |
478 | |
479 | g_clear_object (&nm->priv->proxy); |
480 | |
481 | G_OBJECT_CLASS (g_network_monitor_portal_parent_class)->finalize (object); |
482 | } |
483 | |
484 | static void |
485 | g_network_monitor_portal_class_init (GNetworkMonitorPortalClass *class) |
486 | { |
487 | GObjectClass *gobject_class = G_OBJECT_CLASS (class); |
488 | |
489 | gobject_class->finalize = g_network_monitor_portal_finalize; |
490 | gobject_class->get_property = g_network_monitor_portal_get_property; |
491 | |
492 | g_object_class_override_property (oclass: gobject_class, property_id: PROP_NETWORK_AVAILABLE, name: "network-available" ); |
493 | g_object_class_override_property (oclass: gobject_class, property_id: PROP_NETWORK_METERED, name: "network-metered" ); |
494 | g_object_class_override_property (oclass: gobject_class, property_id: PROP_CONNECTIVITY, name: "connectivity" ); |
495 | } |
496 | |
497 | static gboolean |
498 | g_network_monitor_portal_can_reach (GNetworkMonitor *monitor, |
499 | GSocketConnectable *connectable, |
500 | GCancellable *cancellable, |
501 | GError **error) |
502 | { |
503 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (monitor); |
504 | GVariant *ret; |
505 | GNetworkAddress *address; |
506 | gboolean reachable = FALSE; |
507 | |
508 | if (!G_IS_NETWORK_ADDRESS (connectable)) |
509 | { |
510 | g_set_error (err: error, domain: G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED, |
511 | format: "Can't handle this kind of GSocketConnectable (%s)" , |
512 | G_OBJECT_TYPE_NAME (connectable)); |
513 | return FALSE; |
514 | } |
515 | |
516 | address = G_NETWORK_ADDRESS (connectable); |
517 | |
518 | ret = g_dbus_proxy_call_sync (nm->priv->proxy, |
519 | "CanReach" , |
520 | g_variant_new (format_string: "(su)" , |
521 | g_network_address_get_hostname (address), |
522 | g_network_address_get_port (address)), |
523 | G_DBUS_CALL_FLAGS_NONE, |
524 | -1, |
525 | cancellable, |
526 | error); |
527 | |
528 | if (ret) |
529 | { |
530 | g_variant_get (value: ret, format_string: "(b)" , &reachable); |
531 | g_variant_unref (value: ret); |
532 | } |
533 | |
534 | return reachable; |
535 | } |
536 | |
537 | static void |
538 | can_reach_done (GObject *source, |
539 | GAsyncResult *result, |
540 | gpointer data) |
541 | { |
542 | GTask *task = data; |
543 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (g_task_get_source_object (task)); |
544 | GError *error = NULL; |
545 | GVariant *ret; |
546 | gboolean reachable; |
547 | |
548 | ret = g_dbus_proxy_call_finish (nm->priv->proxy, result, &error); |
549 | if (ret == NULL) |
550 | { |
551 | g_task_return_error (task, error); |
552 | g_object_unref (object: task); |
553 | return; |
554 | } |
555 | |
556 | g_variant_get (value: ret, format_string: "(b)" , &reachable); |
557 | g_variant_unref (value: ret); |
558 | |
559 | if (reachable) |
560 | g_task_return_boolean (task, TRUE); |
561 | else |
562 | g_task_return_new_error (task, |
563 | G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE, |
564 | "Can't reach host" ); |
565 | |
566 | g_object_unref (object: task); |
567 | } |
568 | |
569 | static void |
570 | g_network_monitor_portal_can_reach_async (GNetworkMonitor *monitor, |
571 | GSocketConnectable *connectable, |
572 | GCancellable *cancellable, |
573 | GAsyncReadyCallback callback, |
574 | gpointer data) |
575 | { |
576 | GNetworkMonitorPortal *nm = G_NETWORK_MONITOR_PORTAL (monitor); |
577 | GTask *task; |
578 | GNetworkAddress *address; |
579 | |
580 | task = g_task_new (monitor, cancellable, callback, data); |
581 | |
582 | if (!G_IS_NETWORK_ADDRESS (connectable)) |
583 | { |
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 (object: task); |
588 | return; |
589 | } |
590 | |
591 | address = G_NETWORK_ADDRESS (connectable); |
592 | |
593 | g_dbus_proxy_call (nm->priv->proxy, |
594 | "CanReach" , |
595 | g_variant_new (format_string: "(su)" , |
596 | g_network_address_get_hostname (address), |
597 | g_network_address_get_port (address)), |
598 | G_DBUS_CALL_FLAGS_NONE, |
599 | -1, |
600 | cancellable, |
601 | can_reach_done, |
602 | task); |
603 | } |
604 | |
605 | static gboolean |
606 | g_network_monitor_portal_can_reach_finish (GNetworkMonitor *monitor, |
607 | GAsyncResult *result, |
608 | GError **error) |
609 | { |
610 | return g_task_propagate_boolean (G_TASK (result), error); |
611 | } |
612 | |
613 | static void |
614 | g_network_monitor_portal_iface_init (GNetworkMonitorInterface *monitor_iface) |
615 | { |
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; |
619 | } |
620 | |
621 | static void |
622 | g_network_monitor_portal_initable_iface_init (GInitableIface *iface) |
623 | { |
624 | initable_parent_iface = g_type_interface_peek_parent (g_iface: iface); |
625 | |
626 | iface->init = g_network_monitor_portal_initable_init; |
627 | } |
628 | |