Improve GSimpleActionGroup test coverage
[platform/upstream/glib.git] / gio / gnetworkmonitorbase.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright 2011 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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gnetworkmonitorbase.h"
24 #include "ginetaddress.h"
25 #include "ginetaddressmask.h"
26 #include "ginetsocketaddress.h"
27 #include "ginitable.h"
28 #include "gioerror.h"
29 #include "giomodule-priv.h"
30 #include "gnetworkmonitor.h"
31 #include "gsocketaddressenumerator.h"
32 #include "gsocketconnectable.h"
33 #include "gtask.h"
34 #include "glibintl.h"
35
36 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
37 static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
38
39 enum
40 {
41   PROP_0,
42
43   PROP_NETWORK_AVAILABLE
44 };
45
46 struct _GNetworkMonitorBasePrivate
47 {
48   GPtrArray    *networks;
49   gboolean      have_ipv4_default_route;
50   gboolean      have_ipv6_default_route;
51   gboolean      is_available;
52
53   GMainContext *context;
54   GSource      *network_changed_source;
55   gboolean      initializing;
56 };
57
58 static guint network_changed_signal = 0;
59
60 static void queue_network_changed (GNetworkMonitorBase *monitor);
61
62 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
63                          G_ADD_PRIVATE (GNetworkMonitorBase)
64                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
65                                                 g_network_monitor_base_initable_iface_init)
66                          G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
67                                                 g_network_monitor_base_iface_init)
68                          _g_io_modules_ensure_extension_points_registered ();
69                          g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
70                                                          g_define_type_id,
71                                                          "base",
72                                                          0))
73
74 static void
75 g_network_monitor_base_init (GNetworkMonitorBase *monitor)
76 {
77   monitor->priv = g_network_monitor_base_get_instance_private (monitor);
78   monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
79   monitor->priv->context = g_main_context_get_thread_default ();
80   if (monitor->priv->context)
81     g_main_context_ref (monitor->priv->context);
82
83   monitor->priv->initializing = TRUE;
84   queue_network_changed (monitor);
85 }
86
87 static void
88 g_network_monitor_base_constructed (GObject *object)
89 {
90   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
91
92   if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
93     {
94       GInetAddressMask *mask;
95
96       /* We're the dumb base class, not a smarter subclass. So just
97        * assume that the network is available.
98        */
99       mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
100       g_network_monitor_base_add_network (monitor, mask);
101       g_object_unref (mask);
102
103       mask = g_inet_address_mask_new_from_string ("::/0", NULL);
104       g_network_monitor_base_add_network (monitor, mask);
105       g_object_unref (mask);
106     }
107 }
108
109 static void
110 g_network_monitor_base_get_property (GObject    *object,
111                                      guint       prop_id,
112                                      GValue     *value,
113                                      GParamSpec *pspec)
114 {
115   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
116
117   switch (prop_id)
118     {
119       case PROP_NETWORK_AVAILABLE:
120         g_value_set_boolean (value, monitor->priv->is_available);
121         break;
122
123       default:
124         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
125     }
126
127 }
128
129 static void
130 g_network_monitor_base_finalize (GObject *object)
131 {
132   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
133
134   g_ptr_array_free (monitor->priv->networks, TRUE);
135   if (monitor->priv->network_changed_source)
136     {
137       g_source_destroy (monitor->priv->network_changed_source);
138       g_source_unref (monitor->priv->network_changed_source);
139     }
140   if (monitor->priv->context)
141     g_main_context_unref (monitor->priv->context);
142
143   G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
144 }
145
146 static void
147 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
148 {
149   GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
150
151   gobject_class->constructed  = g_network_monitor_base_constructed;
152   gobject_class->get_property = g_network_monitor_base_get_property;
153   gobject_class->finalize     = g_network_monitor_base_finalize;
154
155   g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
156 }
157
158 static gboolean
159 g_network_monitor_base_can_reach_sockaddr (GNetworkMonitorBase *base,
160                                            GSocketAddress *sockaddr)
161 {
162   GInetAddress *iaddr;
163   int i;
164
165   if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
166     return FALSE;
167
168   iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
169   for (i = 0; i < base->priv->networks->len; i++)
170     {
171       if (g_inet_address_mask_matches (base->priv->networks->pdata[i], iaddr))
172         return TRUE;
173     }
174
175   return FALSE;
176 }
177
178 static gboolean
179 g_network_monitor_base_can_reach (GNetworkMonitor      *monitor,
180                                   GSocketConnectable   *connectable,
181                                   GCancellable         *cancellable,
182                                   GError              **error)
183 {
184   GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (monitor);
185   GSocketAddressEnumerator *enumerator;
186   GSocketAddress *addr;
187
188   if (base->priv->networks->len == 0)
189     {
190       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
191                            _("Network unreachable"));
192       return FALSE;
193     }
194
195   enumerator = g_socket_connectable_proxy_enumerate (connectable);
196   addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
197   if (!addr)
198     {
199       /* Either the user cancelled, or DNS resolution failed */
200       g_object_unref (enumerator);
201       return FALSE;
202     }
203
204   if (base->priv->have_ipv4_default_route &&
205       base->priv->have_ipv6_default_route)
206     {
207       g_object_unref (enumerator);
208       g_object_unref (addr);
209       return TRUE;
210     }
211
212   while (addr)
213     {
214       if (g_network_monitor_base_can_reach_sockaddr (base, addr))
215         {
216           g_object_unref (addr);
217           g_object_unref (enumerator);
218           return TRUE;
219         }
220
221       g_object_unref (addr);
222       addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
223     }
224   g_object_unref (enumerator);
225
226   if (error && !*error)
227     {
228       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
229                            _("Host unreachable"));
230     }
231   return FALSE;
232 }
233
234 static void
235 can_reach_async_got_address (GObject      *object,
236                              GAsyncResult *result,
237                              gpointer      user_data)
238 {
239   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (object);
240   GTask *task = user_data;
241   GNetworkMonitorBase *base = g_task_get_source_object (task);
242   GSocketAddress *addr;
243   GError *error = NULL;
244
245   addr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
246   if (!addr)
247     {
248       if (error)
249         {
250           /* Either the user cancelled, or DNS resolution failed */
251           g_task_return_error (task, error);
252           g_object_unref (task);
253           return;
254         }
255       else
256         {
257           /* Resolved all addresses, none matched */
258           g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
259                                    _("Host unreachable"));
260           g_object_unref (task);
261           return;
262         }
263     }
264
265   if (g_network_monitor_base_can_reach_sockaddr (base, addr))
266     {
267       g_object_unref (addr);
268       g_task_return_boolean (task, TRUE);
269       g_object_unref (task);
270       return;
271     }
272   g_object_unref (addr);
273
274   g_socket_address_enumerator_next_async (enumerator,
275                                           g_task_get_cancellable (task),
276                                           can_reach_async_got_address, task);
277 }
278
279 static void
280 g_network_monitor_base_can_reach_async (GNetworkMonitor     *monitor,
281                                         GSocketConnectable  *connectable,
282                                         GCancellable        *cancellable,
283                                         GAsyncReadyCallback  callback,
284                                         gpointer             user_data)
285 {
286   GTask *task;
287   GSocketAddressEnumerator *enumerator;
288
289   task = g_task_new (monitor, cancellable, callback, user_data);
290
291   if (G_NETWORK_MONITOR_BASE (monitor)->priv->networks->len == 0)
292     {
293       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
294                                _("Network unreachable"));
295       g_object_unref (task);
296       return;
297     }
298
299   enumerator = g_socket_connectable_proxy_enumerate (connectable);
300   g_socket_address_enumerator_next_async (enumerator, cancellable,
301                                           can_reach_async_got_address, task);
302   g_object_unref (enumerator);
303 }
304
305 static gboolean
306 g_network_monitor_base_can_reach_finish (GNetworkMonitor  *monitor,
307                                          GAsyncResult     *result,
308                                          GError          **error)
309 {
310   g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE);
311
312   return g_task_propagate_boolean (G_TASK (result), error);
313 }
314
315 static void
316 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
317 {
318   monitor_iface->can_reach = g_network_monitor_base_can_reach;
319   monitor_iface->can_reach_async = g_network_monitor_base_can_reach_async;
320   monitor_iface->can_reach_finish = g_network_monitor_base_can_reach_finish;
321
322   network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
323 }
324
325 static gboolean
326 g_network_monitor_base_initable_init (GInitable     *initable,
327                                       GCancellable  *cancellable,
328                                       GError       **error)
329 {
330   return TRUE;
331 }
332
333 static void
334 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
335 {
336   iface->init = g_network_monitor_base_initable_init;
337 }
338
339 static gboolean
340 emit_network_changed (gpointer user_data)
341 {
342   GNetworkMonitorBase *monitor = user_data;
343   gboolean is_available;
344
345   g_object_ref (monitor);
346
347   if (monitor->priv->initializing)
348     monitor->priv->initializing = FALSE;
349   else
350     {
351       is_available = (monitor->priv->have_ipv4_default_route ||
352                       monitor->priv->have_ipv6_default_route);
353       if (monitor->priv->is_available != is_available)
354         {
355           monitor->priv->is_available = is_available;
356           g_object_notify (G_OBJECT (monitor), "network-available");
357         }
358
359       g_signal_emit (monitor, network_changed_signal, 0, is_available);
360     }
361
362   g_source_unref (monitor->priv->network_changed_source);
363   monitor->priv->network_changed_source = NULL;
364
365   g_object_unref (monitor);
366   return FALSE;
367 }
368
369 static void
370 queue_network_changed (GNetworkMonitorBase *monitor)
371 {
372   if (!monitor->priv->network_changed_source)
373     {
374       GSource *source;
375
376       source = g_idle_source_new ();
377       /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
378        * network-change-related notifications coming in at
379        * G_PRIORITY_DEFAULT will get coalesced into one signal
380        * emission.
381        */
382       g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
383       g_source_set_callback (source, emit_network_changed, monitor, NULL);
384       g_source_attach (source, monitor->priv->context);
385       monitor->priv->network_changed_source = source;
386     }
387
388   /* Normally we wait to update is_available until we emit the signal,
389    * to keep things consistent. But when we're first creating the
390    * object, we want it to be correct right away.
391    */
392   if (monitor->priv->initializing)
393     {
394       monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
395                                      monitor->priv->have_ipv6_default_route);
396     }
397 }
398
399 /**
400  * g_network_monitor_base_add_network:
401  * @monitor: the #GNetworkMonitorBase
402  * @network: a #GInetAddressMask
403  *
404  * Adds @network to @monitor's list of available networks.
405  *
406  * Since: 2.32
407  */
408 void
409 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
410                                     GInetAddressMask    *network)
411 {
412   int i;
413
414   for (i = 0; i < monitor->priv->networks->len; i++)
415     {
416       if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
417         return;
418     }
419
420   g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
421   if (g_inet_address_mask_get_length (network) == 0)
422     {
423       switch (g_inet_address_mask_get_family (network))
424         {
425         case G_SOCKET_FAMILY_IPV4:
426           monitor->priv->have_ipv4_default_route = TRUE;
427           break;
428         case G_SOCKET_FAMILY_IPV6:
429           monitor->priv->have_ipv6_default_route = TRUE;
430           break;
431         default:
432           break;
433         }
434     }
435
436   /* Don't emit network-changed when multicast-link-local routing
437    * changes. This rather arbitrary decision is mostly because it
438    * seems to change quite often...
439    */
440   if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
441     return;
442
443   queue_network_changed (monitor);
444 }
445
446 /**
447  * g_network_monitor_base_remove_network:
448  * @monitor: the #GNetworkMonitorBase
449  * @network: a #GInetAddressMask
450  *
451  * Removes @network from @monitor's list of available networks.
452  *
453  * Since: 2.32
454  */
455 void
456 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
457                                        GInetAddressMask    *network)
458 {
459   int i;
460
461   for (i = 0; i < monitor->priv->networks->len; i++)
462     {
463       if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
464         {
465           g_ptr_array_remove_index_fast (monitor->priv->networks, i);
466
467           if (g_inet_address_mask_get_length (network) == 0)
468             {
469               switch (g_inet_address_mask_get_family (network))
470                 {
471                 case G_SOCKET_FAMILY_IPV4:
472                   monitor->priv->have_ipv4_default_route = FALSE;
473                   break;
474                 case G_SOCKET_FAMILY_IPV6:
475                   monitor->priv->have_ipv6_default_route = FALSE;
476                   break;
477                 default:
478                   break;
479                 }
480             }
481
482           queue_network_changed (monitor);
483           return;
484         }
485     }
486 }
487
488 /**
489  * g_network_monitor_base_set_networks:
490  * @monitor: the #GNetworkMonitorBase
491  * @networks: (array length=length): an array of #GInetAddressMask
492  * @length: length of @networks
493  *
494  * Drops @monitor's current list of available networks and replaces
495  * it with @networks.
496  */
497 void
498 g_network_monitor_base_set_networks (GNetworkMonitorBase  *monitor,
499                                      GInetAddressMask    **networks,
500                                      gint                  length)
501 {
502   int i;
503
504   g_ptr_array_set_size (monitor->priv->networks, 0);
505   monitor->priv->have_ipv4_default_route = FALSE;
506   monitor->priv->have_ipv6_default_route = FALSE;
507
508   for (i = 0; i < length; i++)
509     g_network_monitor_base_add_network (monitor, networks[i]);
510 }