xdgmime: plug a small leak
[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 "glibintl.h"
34
35 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
36 static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
37
38 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
39                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
40                                                 g_network_monitor_base_initable_iface_init)
41                          G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
42                                                 g_network_monitor_base_iface_init)
43                          _g_io_modules_ensure_extension_points_registered ();
44                          g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
45                                                          g_define_type_id,
46                                                          "base",
47                                                          0))
48
49 enum
50 {
51   PROP_0,
52
53   PROP_NETWORK_AVAILABLE
54 };
55
56 struct _GNetworkMonitorBasePrivate
57 {
58   GPtrArray    *networks;
59   gboolean      have_ipv4_default_route;
60   gboolean      have_ipv6_default_route;
61   gboolean      is_available;
62
63   GMainContext *context;
64   GSource      *network_changed_source;
65   gboolean      initializing;
66 };
67
68 static guint network_changed_signal = 0;
69
70 static void queue_network_changed (GNetworkMonitorBase *monitor);
71
72 static void
73 g_network_monitor_base_init (GNetworkMonitorBase *monitor)
74 {
75   monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
76                                                G_TYPE_NETWORK_MONITOR_BASE,
77                                                GNetworkMonitorBasePrivate);
78
79   monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
80   monitor->priv->context = g_main_context_get_thread_default ();
81   if (monitor->priv->context)
82     g_main_context_ref (monitor->priv->context);
83
84   monitor->priv->initializing = TRUE;
85   queue_network_changed (monitor);
86 }
87
88 static void
89 g_network_monitor_base_constructed (GObject *object)
90 {
91   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
92
93   if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
94     {
95       GInetAddressMask *mask;
96
97       /* We're the dumb base class, not a smarter subclass. So just
98        * assume that the network is available.
99        */
100       mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
101       g_network_monitor_base_add_network (monitor, mask);
102       g_object_unref (mask);
103
104       mask = g_inet_address_mask_new_from_string ("::/0", NULL);
105       g_network_monitor_base_add_network (monitor, mask);
106       g_object_unref (mask);
107     }
108 }
109
110 static void
111 g_network_monitor_base_get_property (GObject    *object,
112                                      guint       prop_id,
113                                      GValue     *value,
114                                      GParamSpec *pspec)
115 {
116   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
117
118   switch (prop_id)
119     {
120       case PROP_NETWORK_AVAILABLE:
121         g_value_set_boolean (value, monitor->priv->is_available);
122         break;
123
124       default:
125         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
126     }
127
128 }
129
130 static void
131 g_network_monitor_base_finalize (GObject *object)
132 {
133   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
134
135   g_ptr_array_free (monitor->priv->networks, TRUE);
136   if (monitor->priv->network_changed_source)
137     {
138       g_source_destroy (monitor->priv->network_changed_source);
139       g_source_unref (monitor->priv->network_changed_source);
140     }
141   if (monitor->priv->context)
142     g_main_context_unref (monitor->priv->context);
143
144   G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
145 }
146
147 static void
148 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
149 {
150   GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
151
152   g_type_class_add_private (monitor_class, sizeof (GNetworkMonitorBasePrivate));
153
154   gobject_class->constructed  = g_network_monitor_base_constructed;
155   gobject_class->get_property = g_network_monitor_base_get_property;
156   gobject_class->finalize     = g_network_monitor_base_finalize;
157
158   g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
159 }
160
161 static gboolean
162 g_network_monitor_base_can_reach (GNetworkMonitor      *monitor,
163                                   GSocketConnectable   *connectable,
164                                   GCancellable         *cancellable,
165                                   GError              **error)
166 {
167   GNetworkMonitorBasePrivate *priv = G_NETWORK_MONITOR_BASE (monitor)->priv;
168   GSocketAddressEnumerator *enumerator;
169   GSocketAddress *addr;
170
171   if (priv->have_ipv4_default_route &&
172       priv->have_ipv6_default_route)
173     return TRUE;
174
175   if (priv->networks->len == 0)
176     {
177       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
178                            _("Network unreachable"));
179       return FALSE;
180     }
181
182   enumerator = g_socket_connectable_proxy_enumerate (connectable);
183   addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
184   if (!addr)
185     {
186       /* Either the user cancelled, or DNS resolution failed */
187       g_object_unref (enumerator);
188       return FALSE;
189     }
190
191   while (addr)
192     {
193       if (G_IS_INET_SOCKET_ADDRESS (addr))
194         {
195           GInetAddress *iaddr;
196           int i;
197
198           iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
199           for (i = 0; i < priv->networks->len; i++)
200             {
201               if (g_inet_address_mask_matches (priv->networks->pdata[i], iaddr))
202                 {
203                   g_object_unref (addr);
204                   g_object_unref (enumerator);
205                   return TRUE;
206                 }
207             }
208         }
209
210       g_object_unref (addr);
211       addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
212     }
213   g_object_unref (enumerator);
214
215   if (error && !*error)
216     {
217       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
218                            _("Host unreachable"));
219     }
220   return FALSE;
221 }
222
223 static void
224 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
225 {
226   monitor_iface->can_reach = g_network_monitor_base_can_reach;
227
228   network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
229 }
230
231 static gboolean
232 g_network_monitor_base_initable_init (GInitable     *initable,
233                                       GCancellable  *cancellable,
234                                       GError       **error)
235 {
236   return TRUE;
237 }
238
239 static void
240 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
241 {
242   iface->init = g_network_monitor_base_initable_init;
243 }
244
245 static gboolean
246 emit_network_changed (gpointer user_data)
247 {
248   GNetworkMonitorBase *monitor = user_data;
249   gboolean is_available;
250
251   g_object_ref (monitor);
252
253   if (monitor->priv->initializing)
254     monitor->priv->initializing = FALSE;
255   else
256     {
257       is_available = (monitor->priv->have_ipv4_default_route ||
258                       monitor->priv->have_ipv6_default_route);
259       if (monitor->priv->is_available != is_available)
260         {
261           monitor->priv->is_available = is_available;
262           g_object_notify (G_OBJECT (monitor), "network-available");
263         }
264
265       g_signal_emit (monitor, network_changed_signal, 0, is_available);
266     }
267
268   g_source_unref (monitor->priv->network_changed_source);
269   monitor->priv->network_changed_source = NULL;
270
271   g_object_unref (monitor);
272   return FALSE;
273 }
274
275 static void
276 queue_network_changed (GNetworkMonitorBase *monitor)
277 {
278   if (!monitor->priv->network_changed_source)
279     {
280       GSource *source;
281
282       source = g_idle_source_new ();
283       /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
284        * network-change-related notifications coming in at
285        * G_PRIORITY_DEFAULT will get coalesced into one signal
286        * emission.
287        */
288       g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
289       g_source_set_callback (source, emit_network_changed, monitor, NULL);
290       g_source_attach (source, monitor->priv->context);
291       monitor->priv->network_changed_source = source;
292     }
293
294   /* Normally we wait to update is_available until we emit the signal,
295    * to keep things consistent. But when we're first creating the
296    * object, we want it to be correct right away.
297    */
298   if (monitor->priv->initializing)
299     {
300       monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
301                                      monitor->priv->have_ipv6_default_route);
302     }
303 }
304
305 /**
306  * g_network_monitor_base_add_network:
307  * @monitor: the #GNetworkMonitorBase
308  * @network: a #GInetAddressMask
309  *
310  * Adds @network to @monitor's list of available networks.
311  *
312  * Since: 2.32
313  */
314 void
315 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
316                                     GInetAddressMask    *network)
317 {
318   int i;
319
320   for (i = 0; i < monitor->priv->networks->len; i++)
321     {
322       if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
323         return;
324     }
325
326   g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
327   if (g_inet_address_mask_get_length (network) == 0)
328     {
329       switch (g_inet_address_mask_get_family (network))
330         {
331         case G_SOCKET_FAMILY_IPV4:
332           monitor->priv->have_ipv4_default_route = TRUE;
333           break;
334         case G_SOCKET_FAMILY_IPV6:
335           monitor->priv->have_ipv6_default_route = TRUE;
336           break;
337         default:
338           break;
339         }
340     }
341
342   /* Don't emit network-changed when multicast-link-local routing
343    * changes. This rather arbitrary decision is mostly because it
344    * seems to change quite often...
345    */
346   if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
347     return;
348
349   queue_network_changed (monitor);
350 }
351
352 /**
353  * g_network_monitor_base_remove_network:
354  * @monitor: the #GNetworkMonitorBase
355  * @network: a #GInetAddressMask
356  *
357  * Removes @network from @monitor's list of available networks.
358  *
359  * Since: 2.32
360  */
361 void
362 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
363                                        GInetAddressMask    *network)
364 {
365   int i;
366
367   for (i = 0; i < monitor->priv->networks->len; i++)
368     {
369       if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
370         {
371           g_ptr_array_remove_index_fast (monitor->priv->networks, i);
372
373           if (g_inet_address_mask_get_length (network) == 0)
374             {
375               switch (g_inet_address_mask_get_family (network))
376                 {
377                 case G_SOCKET_FAMILY_IPV4:
378                   monitor->priv->have_ipv4_default_route = FALSE;
379                   break;
380                 case G_SOCKET_FAMILY_IPV6:
381                   monitor->priv->have_ipv6_default_route = FALSE;
382                   break;
383                 default:
384                   break;
385                 }
386             }
387
388           queue_network_changed (monitor);
389           return;
390         }
391     }
392 }
393
394 /**
395  * g_network_monitor_base_set_networks:
396  * @monitor: the #GNetworkMonitorBase
397  * @networks: (array length=length): an array of #GInetAddressMask
398  * @length: length of @networks
399  *
400  * Drops @monitor's current list of available networks and replaces
401  * it with @networks.
402  */
403 void
404 g_network_monitor_base_set_networks (GNetworkMonitorBase  *monitor,
405                                      GInetAddressMask    **networks,
406                                      gint                  length)
407 {
408   int i;
409
410   g_ptr_array_set_size (monitor->priv->networks, 0);
411   monitor->priv->have_ipv4_default_route = FALSE;
412   monitor->priv->have_ipv6_default_route = FALSE;
413
414   for (i = 0; i < length; i++)
415     g_network_monitor_base_add_network (monitor, networks[i]);
416 }