1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2010-2012 Richard Hughes <richard@hughsie.com>
4  * Copyright (C) 2012 Thomas Bechtold <thomasbechtold@jpberlin.de>
5  * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <config.h>
23 #include <glib/gi18n.h>
24 #include <stdlib.h>
25 
26 #include "shell/cc-object-storage.h"
27 
28 #include "cc-network-panel.h"
29 #include "cc-network-resources.h"
30 
31 #include <NetworkManager.h>
32 
33 #include "net-device-bluetooth.h"
34 #include "net-device-ethernet.h"
35 #include "net-device-mobile.h"
36 #include "net-device-wifi.h"
37 #include "net-proxy.h"
38 #include "net-vpn.h"
39 
40 #include "panel-common.h"
41 
42 #include "network-dialogs.h"
43 #include "connection-editor/net-connection-editor.h"
44 
45 #include <libmm-glib.h>
46 
47 typedef enum {
48         OPERATION_NULL,
49         OPERATION_SHOW_DEVICE,
50         OPERATION_CONNECT_MOBILE
51 } CmdlineOperation;
52 
53 struct _CcNetworkPanel
54 {
55         CcPanel           parent;
56 
57         GPtrArray        *bluetooth_devices;
58         GPtrArray        *ethernet_devices;
59         GPtrArray        *mobile_devices;
60         GPtrArray        *vpns;
61         GHashTable       *nm_device_to_device;
62 
63         NMClient         *client;
64         MMManager        *modem_manager;
65         gboolean          updating_device;
66 
67         /* widgets */
68         GtkWidget        *box_bluetooth;
69         GtkWidget        *box_proxy;
70         GtkWidget        *box_vpn;
71         GtkWidget        *box_wired;
72         GtkWidget        *container_bluetooth;
73         GtkWidget        *empty_listbox;
74 
75         /* wireless dialog stuff */
76         CmdlineOperation  arg_operation;
77         gchar            *arg_device;
78         gchar            *arg_access_point;
79         gboolean          operation_done;
80 };
81 
82 enum {
83         PROP_0,
84         PROP_PARAMETERS
85 };
86 
87 static void handle_argv (CcNetworkPanel *self);
88 
CC_PANEL_REGISTER(CcNetworkPanel,cc_network_panel)89 CC_PANEL_REGISTER (CcNetworkPanel, cc_network_panel)
90 
91 static void
92 cc_network_panel_get_property (GObject    *object,
93                                guint       property_id,
94                                GValue     *value,
95                                GParamSpec *pspec)
96 {
97         switch (property_id) {
98         default:
99                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
100         }
101 }
102 
103 static CmdlineOperation
cmdline_operation_from_string(const gchar * string)104 cmdline_operation_from_string (const gchar *string)
105 {
106         if (g_strcmp0 (string, "connect-3g") == 0)
107                 return OPERATION_CONNECT_MOBILE;
108         if (g_strcmp0 (string, "show-device") == 0)
109                 return OPERATION_SHOW_DEVICE;
110 
111         g_warning ("Invalid additional argument %s", string);
112         return OPERATION_NULL;
113 }
114 
115 static void
reset_command_line_args(CcNetworkPanel * self)116 reset_command_line_args (CcNetworkPanel *self)
117 {
118 	self->arg_operation = OPERATION_NULL;
119 	g_clear_pointer (&self->arg_device, g_free);
120 	g_clear_pointer (&self->arg_access_point, g_free);
121 }
122 
123 static gboolean
verify_argv(CcNetworkPanel * self,const char ** args)124 verify_argv (CcNetworkPanel *self,
125 	     const char    **args)
126 {
127 	switch (self->arg_operation) {
128 	case OPERATION_CONNECT_MOBILE:
129 	case OPERATION_SHOW_DEVICE:
130 		if (self->arg_device == NULL) {
131 			g_warning ("Operation %s requires an object path", args[0]);
132 		        return FALSE;
133                 }
134 	default:
135 		return TRUE;
136 	}
137 }
138 
139 static GPtrArray *
variant_av_to_string_array(GVariant * array)140 variant_av_to_string_array (GVariant *array)
141 {
142         GVariantIter iter;
143         GVariant *v;
144         GPtrArray *strv;
145         gsize count;
146         count = g_variant_iter_init (&iter, array);
147         strv = g_ptr_array_sized_new (count + 1);
148         while (g_variant_iter_next (&iter, "v", &v)) {
149                 g_ptr_array_add (strv, (gpointer)g_variant_get_string (v, NULL));
150                 g_variant_unref (v);
151         }
152         g_ptr_array_add (strv, NULL); /* NULL-terminate the strv data array */
153         return strv;
154 }
155 
156 static void
cc_network_panel_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)157 cc_network_panel_set_property (GObject      *object,
158                                guint         property_id,
159                                const GValue *value,
160                                GParamSpec   *pspec)
161 {
162         CcNetworkPanel *self = CC_NETWORK_PANEL (object);
163 
164         switch (property_id) {
165         case PROP_PARAMETERS: {
166                 GVariant *parameters;
167 
168                 reset_command_line_args (self);
169 
170                 parameters = g_value_get_variant (value);
171                 if (parameters) {
172                         g_autoptr(GPtrArray) array = NULL;
173                         const gchar **args;
174                         array = variant_av_to_string_array (parameters);
175                         args = (const gchar **) array->pdata;
176 
177                         g_debug ("Invoked with operation %s", args[0]);
178 
179                         if (args[0])
180                                 self->arg_operation = cmdline_operation_from_string (args[0]);
181                         if (args[0] && args[1])
182                                 self->arg_device = g_strdup (args[1]);
183                         if (args[0] && args[1] && args[2])
184                                 self->arg_access_point = g_strdup (args[2]);
185 
186                         if (verify_argv (self, (const char **) args) == FALSE) {
187                                 reset_command_line_args (self);
188                                 return;
189                         }
190                         g_debug ("Calling handle_argv() after setting property");
191                         handle_argv (self);
192                 }
193                 break;
194         }
195         default:
196                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
197         }
198 }
199 
200 static void
cc_network_panel_dispose(GObject * object)201 cc_network_panel_dispose (GObject *object)
202 {
203         CcNetworkPanel *self = CC_NETWORK_PANEL (object);
204 
205         g_clear_object (&self->client);
206         g_clear_object (&self->modem_manager);
207 
208         g_clear_pointer (&self->bluetooth_devices, g_ptr_array_unref);
209         g_clear_pointer (&self->ethernet_devices, g_ptr_array_unref);
210         g_clear_pointer (&self->mobile_devices, g_ptr_array_unref);
211         g_clear_pointer (&self->vpns, g_ptr_array_unref);
212         g_clear_pointer (&self->nm_device_to_device, g_hash_table_destroy);
213 
214         G_OBJECT_CLASS (cc_network_panel_parent_class)->dispose (object);
215 }
216 
217 static void
cc_network_panel_finalize(GObject * object)218 cc_network_panel_finalize (GObject *object)
219 {
220         CcNetworkPanel *self = CC_NETWORK_PANEL (object);
221 
222         reset_command_line_args (self);
223 
224         G_OBJECT_CLASS (cc_network_panel_parent_class)->finalize (object);
225 }
226 
227 static const char *
cc_network_panel_get_help_uri(CcPanel * self)228 cc_network_panel_get_help_uri (CcPanel *self)
229 {
230 	return "help:gnome-help/net";
231 }
232 
233 static void
panel_refresh_device_titles(CcNetworkPanel * self)234 panel_refresh_device_titles (CcNetworkPanel *self)
235 {
236         g_autoptr(GPtrArray) ndarray = NULL;
237         g_autoptr(GPtrArray) nmdarray = NULL;
238         GtkWidget **devices;
239         NMDevice **nm_devices;
240         g_auto(GStrv) titles = NULL;
241         guint i, num_devices;
242 
243         ndarray = g_ptr_array_new ();
244         nmdarray = g_ptr_array_new ();
245         for (i = 0; i < self->bluetooth_devices->len; i++) {
246                 NetDeviceBluetooth *device = g_ptr_array_index (self->bluetooth_devices, i);
247                 g_ptr_array_add (ndarray, device);
248                 g_ptr_array_add (nmdarray, net_device_bluetooth_get_device (device));
249         }
250         for (i = 0; i < self->ethernet_devices->len; i++) {
251                 NetDeviceEthernet *device = g_ptr_array_index (self->ethernet_devices, i);
252                 g_ptr_array_add (ndarray, device);
253                 g_ptr_array_add (nmdarray, net_device_ethernet_get_device (device));
254         }
255         for (i = 0; i < self->mobile_devices->len; i++) {
256                 NetDeviceMobile *device = g_ptr_array_index (self->mobile_devices, i);
257                 g_ptr_array_add (ndarray, device);
258                 g_ptr_array_add (nmdarray, net_device_mobile_get_device (device));
259         }
260 
261         if (ndarray->len == 0)
262                 return;
263 
264         devices = (GtkWidget **)ndarray->pdata;
265         nm_devices = (NMDevice **)nmdarray->pdata;
266         num_devices = ndarray->len;
267 
268         titles = nm_device_disambiguate_names (nm_devices, num_devices);
269         for (i = 0; i < num_devices; i++) {
270                 if (NM_IS_DEVICE_BT (nm_devices[i]))
271                         net_device_bluetooth_set_title (NET_DEVICE_BLUETOOTH (devices[i]), nm_device_bt_get_name (NM_DEVICE_BT (nm_devices[i])));
272                 else if (NET_IS_DEVICE_ETHERNET (devices[i]))
273                         net_device_ethernet_set_title (NET_DEVICE_ETHERNET (devices[i]), titles[i]);
274                 else if (NET_IS_DEVICE_MOBILE (devices[i]))
275                         net_device_mobile_set_title (NET_DEVICE_MOBILE (devices[i]), titles[i]);
276         }
277 }
278 
279 static gboolean
handle_argv_for_device(CcNetworkPanel * self,NMDevice * device)280 handle_argv_for_device (CcNetworkPanel *self,
281 			NMDevice       *device)
282 {
283         GtkWidget *toplevel = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
284 
285         if (self->arg_operation == OPERATION_NULL)
286                 return TRUE;
287 
288         if (g_strcmp0 (nm_object_get_path (NM_OBJECT (device)), self->arg_device) == 0) {
289                 if (self->arg_operation == OPERATION_CONNECT_MOBILE) {
290                         cc_network_panel_connect_to_3g_network (toplevel, self->client, device);
291 
292                         reset_command_line_args (self); /* done */
293                         return TRUE;
294                 } else if (self->arg_operation == OPERATION_SHOW_DEVICE) {
295                         reset_command_line_args (self); /* done */
296                         return TRUE;
297                 }
298         }
299 
300         return FALSE;
301 }
302 
303 static gboolean
handle_argv_for_connection(CcNetworkPanel * self,NMConnection * connection)304 handle_argv_for_connection (CcNetworkPanel *self,
305                             NMConnection   *connection)
306 {
307         if (self->arg_operation == OPERATION_NULL)
308                 return TRUE;
309         if (self->arg_operation != OPERATION_SHOW_DEVICE)
310                 return FALSE;
311 
312         if (g_strcmp0 (nm_connection_get_path (connection), self->arg_device) == 0) {
313                 reset_command_line_args (self);
314                 return TRUE;
315         }
316 
317         return FALSE;
318 }
319 
320 
321 static void
handle_argv(CcNetworkPanel * self)322 handle_argv (CcNetworkPanel *self)
323 {
324         gint i;
325 
326         if (self->arg_operation == OPERATION_NULL)
327                 return;
328 
329         for (i = 0; i < self->bluetooth_devices->len; i++) {
330                 NetDeviceBluetooth *device = g_ptr_array_index (self->bluetooth_devices, i);
331                 if (handle_argv_for_device (self, net_device_bluetooth_get_device (device)))
332                         return;
333         }
334         for (i = 0; i < self->ethernet_devices->len; i++) {
335                 NetDeviceEthernet *device = g_ptr_array_index (self->ethernet_devices, i);
336                 if (handle_argv_for_device (self, net_device_ethernet_get_device (device)))
337                         return;
338         }
339         for (i = 0; i < self->mobile_devices->len; i++) {
340                 NetDeviceMobile *device = g_ptr_array_index (self->mobile_devices, i);
341                 if (handle_argv_for_device (self, net_device_mobile_get_device (device)))
342                         return;
343         }
344         for (i = 0; i < self->vpns->len; i++) {
345                 NetVpn *vpn = g_ptr_array_index (self->vpns, i);
346                 if (handle_argv_for_connection (self, net_vpn_get_connection (vpn)))
347                         return;
348         }
349 
350         g_debug ("Could not handle argv operation, no matching device yet?");
351 }
352 
353 /* HACK: this function is basically a workaround. We don't have a single
354  * listbox in the VPN section, thus we need to track the separators and the
355  * stub row manually.
356  */
357 static void
update_vpn_section(CcNetworkPanel * self)358 update_vpn_section (CcNetworkPanel *self)
359 {
360         guint i, n_vpns;
361 
362         for (i = 0, n_vpns = 0; i < self->vpns->len; i++) {
363                 NetVpn *vpn = g_ptr_array_index (self->vpns, i);
364 
365                 net_vpn_set_show_separator (vpn, n_vpns > 0);
366                 n_vpns++;
367         }
368 
369         gtk_widget_set_visible (self->empty_listbox, n_vpns == 0);
370 }
371 
372 static void
update_bluetooth_section(CcNetworkPanel * self)373 update_bluetooth_section (CcNetworkPanel *self)
374 {
375         guint i;
376 
377         for (i = 0; i < self->bluetooth_devices->len; i++) {
378                 NetDeviceBluetooth *device = g_ptr_array_index (self->bluetooth_devices, i);
379                 net_device_bluetooth_set_show_separator (device, i > 0);
380         }
381 
382         gtk_widget_set_visible (self->container_bluetooth, self->bluetooth_devices->len > 0);
383 }
384 
385 static gboolean
wwan_panel_supports_modem(GDBusObject * object)386 wwan_panel_supports_modem (GDBusObject *object)
387 {
388         MMObject *mm_object;
389         MMModem *modem;
390         MMModemCapability capability, supported_capabilities;
391 
392         g_assert (G_IS_DBUS_OBJECT (object));
393 
394         supported_capabilities = MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE;
395 #if MM_CHECK_VERSION (1,14,0)
396         supported_capabilities |= MM_MODEM_CAPABILITY_5GNR;
397 #endif
398 
399         mm_object = MM_OBJECT (object);
400         modem = mm_object_get_modem (mm_object);
401         capability = mm_modem_get_current_capabilities (modem);
402 
403         return capability & supported_capabilities;
404 }
405 
406 static void
panel_add_device(CcNetworkPanel * self,NMDevice * device)407 panel_add_device (CcNetworkPanel *self, NMDevice *device)
408 {
409         NMDeviceType type;
410         NetDeviceEthernet *device_ethernet;
411         NetDeviceMobile *device_mobile;
412         NetDeviceBluetooth *device_bluetooth;
413         g_autoptr(GDBusObject) modem_object = NULL;
414 
415         /* does already exist */
416         if (g_hash_table_lookup (self->nm_device_to_device, device) != NULL)
417                 return;
418 
419         type = nm_device_get_device_type (device);
420 
421         g_debug ("device %s type %i path %s",
422                  nm_device_get_udi (device), type, nm_object_get_path (NM_OBJECT (device)));
423 
424         /* map the NMDeviceType to the GType, or ignore */
425         switch (type) {
426         case NM_DEVICE_TYPE_ETHERNET:
427         case NM_DEVICE_TYPE_INFINIBAND:
428                 device_ethernet = net_device_ethernet_new (self->client, device);
429                 gtk_widget_show (GTK_WIDGET (device_ethernet));
430                 gtk_container_add (GTK_CONTAINER (self->box_wired), GTK_WIDGET (device_ethernet));
431                 g_ptr_array_add (self->ethernet_devices, device_ethernet);
432                 g_hash_table_insert (self->nm_device_to_device, device, device_ethernet);
433                 break;
434         case NM_DEVICE_TYPE_MODEM:
435                 if (g_str_has_prefix (nm_device_get_udi (device), "/org/freedesktop/ModemManager1/Modem/")) {
436                         if (self->modem_manager == NULL) {
437                                 g_warning ("Cannot grab information for modem at %s: No ModemManager support",
438                                            nm_device_get_udi (device));
439                                 return;
440                         }
441 
442                         modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (self->modem_manager),
443                                                                          nm_device_get_udi (device));
444                         if (modem_object == NULL) {
445                                 g_warning ("Cannot grab information for modem at %s: Not found",
446                                            nm_device_get_udi (device));
447                                 return;
448                         }
449 
450                         /* This will be handled by cellular panel */
451                         if (wwan_panel_supports_modem (modem_object))
452                                 return;
453                 }
454 
455                 device_mobile = net_device_mobile_new (self->client, device, modem_object);
456                 gtk_widget_show (GTK_WIDGET (device_mobile));
457                 gtk_container_add (GTK_CONTAINER (self->box_wired), GTK_WIDGET (device_mobile));
458                 g_ptr_array_add (self->mobile_devices, device_mobile);
459                 g_hash_table_insert (self->nm_device_to_device, device, device_mobile);
460                 break;
461         case NM_DEVICE_TYPE_BT:
462                 device_bluetooth = net_device_bluetooth_new (self->client, device);
463                 gtk_widget_show (GTK_WIDGET (device_bluetooth));
464                 gtk_container_add (GTK_CONTAINER (self->box_bluetooth), GTK_WIDGET (device_bluetooth));
465                 g_ptr_array_add (self->bluetooth_devices, device_bluetooth);
466                 g_hash_table_insert (self->nm_device_to_device, device, device_bluetooth);
467 
468                 /* Update the device_bluetooth section if we're adding a bluetooth
469                  * device. This is a temporary solution though, for these will
470                  * be handled by the future Mobile Broadband panel */
471                 update_bluetooth_section (self);
472                 break;
473 
474         /* For Wi-Fi and VPN we handle connections separately; we correctly manage
475          * them, but not here.
476          */
477         case NM_DEVICE_TYPE_WIFI:
478         case NM_DEVICE_TYPE_TUN:
479         /* And the rest we simply cannot deal with currently. */
480         default:
481                 return;
482         }
483 }
484 
485 static void
panel_remove_device(CcNetworkPanel * self,NMDevice * device)486 panel_remove_device (CcNetworkPanel *self, NMDevice *device)
487 {
488         GtkWidget *net_device;
489 
490         net_device = g_hash_table_lookup (self->nm_device_to_device, device);
491         if (net_device == NULL)
492                 return;
493 
494         g_ptr_array_remove (self->bluetooth_devices, net_device);
495         g_ptr_array_remove (self->ethernet_devices, net_device);
496         g_ptr_array_remove (self->mobile_devices, net_device);
497         g_hash_table_remove (self->nm_device_to_device, device);
498 
499         gtk_widget_destroy (net_device);
500 
501         /* update vpn widgets */
502         update_vpn_section (self);
503 
504         /* update device_bluetooth widgets */
505         update_bluetooth_section (self);
506 }
507 
508 static void
connection_state_changed(CcNetworkPanel * self)509 connection_state_changed (CcNetworkPanel *self)
510 {
511 }
512 
513 static void
active_connections_changed(CcNetworkPanel * self)514 active_connections_changed (CcNetworkPanel *self)
515 {
516         const GPtrArray *connections;
517         int i, j;
518 
519         g_debug ("Active connections changed:");
520         connections = nm_client_get_active_connections (self->client);
521         for (i = 0; connections && (i < connections->len); i++) {
522                 NMActiveConnection *connection;
523                 const GPtrArray *devices;
524 
525                 connection = g_ptr_array_index (connections, i);
526                 g_debug ("    %s", nm_object_get_path (NM_OBJECT (connection)));
527                 devices = nm_active_connection_get_devices (connection);
528                 for (j = 0; devices && j < devices->len; j++)
529                         g_debug ("           %s", nm_device_get_udi (g_ptr_array_index (devices, j)));
530                 if (NM_IS_VPN_CONNECTION (connection))
531                         g_debug ("           VPN base connection: %s", nm_active_connection_get_specific_object_path (connection));
532 
533                 if (g_object_get_data (G_OBJECT (connection), "has-state-changed-handler") == NULL) {
534                         g_signal_connect_object (connection, "notify::state",
535                                                  G_CALLBACK (connection_state_changed), self, G_CONNECT_SWAPPED);
536                         g_object_set_data (G_OBJECT (connection), "has-state-changed-handler", GINT_TO_POINTER (TRUE));
537                 }
538         }
539 }
540 
541 static void
device_managed_cb(CcNetworkPanel * self,GParamSpec * pspec,NMDevice * device)542 device_managed_cb (CcNetworkPanel *self, GParamSpec *pspec, NMDevice *device)
543 {
544         if (!nm_device_get_managed (device))
545                 return;
546 
547         panel_add_device (self, device);
548         panel_refresh_device_titles (self);
549 }
550 
551 static void
device_added_cb(CcNetworkPanel * self,NMDevice * device)552 device_added_cb (CcNetworkPanel *self, NMDevice *device)
553 {
554         g_debug ("New device added");
555 
556         if (nm_device_get_managed (device))
557                 device_managed_cb (self, NULL, device);
558         else
559                 g_signal_connect_object (device, "notify::managed", G_CALLBACK (device_managed_cb), self, G_CONNECT_SWAPPED);
560 }
561 
562 static void
device_removed_cb(CcNetworkPanel * self,NMDevice * device)563 device_removed_cb (CcNetworkPanel *self, NMDevice *device)
564 {
565         g_debug ("Device removed");
566         panel_remove_device (self, device);
567         panel_refresh_device_titles (self);
568 
569         g_signal_handlers_disconnect_by_func (device,
570                                               G_CALLBACK (device_managed_cb),
571                                               self);
572 }
573 
574 static void
manager_running(CcNetworkPanel * self)575 manager_running (CcNetworkPanel *self)
576 {
577         const GPtrArray *devices;
578         int i;
579 
580         /* clear all devices we added */
581         if (!nm_client_get_nm_running (self->client)) {
582                 g_debug ("NM disappeared");
583                 goto out;
584         }
585 
586         g_debug ("coldplugging devices");
587         devices = nm_client_get_devices (self->client);
588         if (devices == NULL) {
589                 g_debug ("No devices to add");
590                 return;
591         }
592         for (i = 0; i < devices->len; i++) {
593                 NMDevice *device = g_ptr_array_index (devices, i);
594                 device_added_cb (self, device);
595         }
596 out:
597         panel_refresh_device_titles (self);
598 
599         g_debug ("Calling handle_argv() after cold-plugging devices");
600         handle_argv (self);
601 }
602 
603 static void
panel_add_vpn_device(CcNetworkPanel * self,NMConnection * connection)604 panel_add_vpn_device (CcNetworkPanel *self, NMConnection *connection)
605 {
606         NetVpn *net_vpn;
607         guint i;
608 
609         /* does already exist */
610         for (i = 0; i < self->vpns->len; i++) {
611                 net_vpn = g_ptr_array_index (self->vpns, i);
612                 if (net_vpn_get_connection (net_vpn) == connection)
613                         return;
614         }
615 
616         net_vpn = net_vpn_new (self->client, connection);
617         gtk_widget_show (GTK_WIDGET (net_vpn));
618         gtk_container_add (GTK_CONTAINER (self->box_vpn), GTK_WIDGET (net_vpn));
619 
620         /* store in the devices array */
621         g_ptr_array_add (self->vpns, net_vpn);
622 
623         /* update vpn widgets */
624         update_vpn_section (self);
625 }
626 
627 static void
add_connection(CcNetworkPanel * self,NMConnection * connection)628 add_connection (CcNetworkPanel *self, NMConnection *connection)
629 {
630         NMSettingConnection *s_con;
631         const gchar *type, *iface;
632 
633         s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection,
634                                                                   NM_TYPE_SETTING_CONNECTION));
635         type = nm_setting_connection_get_connection_type (s_con);
636         iface = nm_connection_get_interface_name (connection);
637         if (g_strcmp0 (type, "vpn") != 0 && iface == NULL)
638                 return;
639 
640         /* Don't add the libvirtd bridge to the UI */
641         if (g_strcmp0 (nm_setting_connection_get_interface_name (s_con), "virbr0") == 0)
642                 return;
643 
644         g_debug ("add %s/%s remote connection: %s",
645                  type, g_type_name_from_instance ((GTypeInstance*)connection),
646                  nm_connection_get_path (connection));
647         if (!iface)
648                 panel_add_vpn_device (self, connection);
649 }
650 
651 static void
client_connection_removed_cb(CcNetworkPanel * self,NMConnection * connection)652 client_connection_removed_cb (CcNetworkPanel *self, NMConnection *connection)
653 {
654         guint i;
655 
656         for (i = 0; i < self->vpns->len; i++) {
657                 NetVpn *vpn = g_ptr_array_index (self->vpns, i);
658                 if (net_vpn_get_connection (vpn) == connection) {
659                         g_ptr_array_remove (self->vpns, vpn);
660                         gtk_widget_destroy (GTK_WIDGET (vpn));
661                         update_vpn_section (self);
662                         return;
663                 }
664         }
665 }
666 
667 static void
panel_check_network_manager_version(CcNetworkPanel * self)668 panel_check_network_manager_version (CcNetworkPanel *self)
669 {
670         const gchar *version;
671 
672         /* parse running version */
673         version = nm_client_get_version (self->client);
674         if (version == NULL) {
675                 GtkWidget *box;
676                 GtkWidget *label;
677                 g_autofree gchar *markup = NULL;
678 
679                 gtk_container_remove (GTK_CONTAINER (self), gtk_bin_get_child (GTK_BIN (self)));
680 
681                 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
682                 gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
683                 gtk_widget_set_vexpand (box, TRUE);
684                 gtk_container_add (GTK_CONTAINER (self), box);
685 
686                 label = gtk_label_new (_("Oops, something has gone wrong. Please contact your software vendor."));
687                 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
688                 gtk_widget_set_valign (label, GTK_ALIGN_END);
689                 gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
690 
691                 markup = g_strdup_printf ("<small><tt>%s</tt></small>",
692                                           _("NetworkManager needs to be running."));
693                 label = gtk_label_new (NULL);
694                 gtk_label_set_markup (GTK_LABEL (label), markup);
695                 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
696                 gtk_widget_set_valign (label, GTK_ALIGN_START);
697                 gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
698 
699                 gtk_widget_show_all (box);
700         } else {
701                 manager_running (self);
702         }
703 }
704 
705 static void
create_connection_cb(GtkWidget * button,CcNetworkPanel * self)706 create_connection_cb (GtkWidget      *button,
707                       CcNetworkPanel *self)
708 {
709         NetConnectionEditor *editor;
710 
711         editor = net_connection_editor_new (NULL, NULL, NULL, self->client);
712         gtk_window_set_transient_for (GTK_WINDOW (editor), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));
713         gtk_window_present (GTK_WINDOW (editor));
714 }
715 
716 static void
on_toplevel_map(GtkWidget * widget,CcNetworkPanel * self)717 on_toplevel_map (GtkWidget      *widget,
718                  CcNetworkPanel *self)
719 {
720         /* is the user compiling against a new version, but not running
721          * the daemon? */
722         panel_check_network_manager_version (self);
723 }
724 
725 
726 static void
cc_network_panel_class_init(CcNetworkPanelClass * klass)727 cc_network_panel_class_init (CcNetworkPanelClass *klass)
728 {
729         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
730         GObjectClass *object_class = G_OBJECT_CLASS (klass);
731 	CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
732 
733 	panel_class->get_help_uri = cc_network_panel_get_help_uri;
734 
735         object_class->get_property = cc_network_panel_get_property;
736         object_class->set_property = cc_network_panel_set_property;
737         object_class->dispose = cc_network_panel_dispose;
738         object_class->finalize = cc_network_panel_finalize;
739 
740         g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
741 
742         gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/cc-network-panel.ui");
743 
744         gtk_widget_class_bind_template_child (widget_class, CcNetworkPanel, box_bluetooth);
745         gtk_widget_class_bind_template_child (widget_class, CcNetworkPanel, box_proxy);
746         gtk_widget_class_bind_template_child (widget_class, CcNetworkPanel, box_vpn);
747         gtk_widget_class_bind_template_child (widget_class, CcNetworkPanel, box_wired);
748         gtk_widget_class_bind_template_child (widget_class, CcNetworkPanel, container_bluetooth);
749         gtk_widget_class_bind_template_child (widget_class, CcNetworkPanel, empty_listbox);
750 
751         gtk_widget_class_bind_template_callback (widget_class, create_connection_cb);
752 }
753 
754 static void
cc_network_panel_init(CcNetworkPanel * self)755 cc_network_panel_init (CcNetworkPanel *self)
756 {
757         NetProxy *proxy;
758         g_autoptr(GError) error = NULL;
759         GtkWidget *toplevel;
760         g_autoptr(GDBusConnection) system_bus = NULL;
761         const GPtrArray *connections;
762         guint i;
763 
764         g_resources_register (cc_network_get_resource ());
765 
766         gtk_widget_init_template (GTK_WIDGET (self));
767 
768         self->bluetooth_devices = g_ptr_array_new ();
769         self->ethernet_devices = g_ptr_array_new ();
770         self->mobile_devices = g_ptr_array_new ();
771         self->vpns = g_ptr_array_new ();
772         self->nm_device_to_device = g_hash_table_new (g_direct_hash, g_direct_equal);
773 
774         /* add the virtual proxy device */
775         proxy = net_proxy_new ();
776         gtk_widget_show (GTK_WIDGET (proxy));
777         gtk_container_add (GTK_CONTAINER (self->box_proxy), GTK_WIDGET (proxy));
778 
779         /* Create and store a NMClient instance if it doesn't exist yet */
780         if (!cc_object_storage_has_object (CC_OBJECT_NMCLIENT)) {
781                 g_autoptr(NMClient) client = nm_client_new (NULL, NULL);
782                 cc_object_storage_add_object (CC_OBJECT_NMCLIENT, client);
783         }
784 
785         /* use NetworkManager client */
786         self->client = cc_object_storage_get_object (CC_OBJECT_NMCLIENT);
787 
788         g_signal_connect_object (self->client, "notify::nm-running" ,
789                                  G_CALLBACK (manager_running), self, G_CONNECT_SWAPPED);
790         g_signal_connect_object (self->client, "notify::active-connections",
791                                  G_CALLBACK (active_connections_changed), self, G_CONNECT_SWAPPED);
792         g_signal_connect_object (self->client, "device-added",
793                                  G_CALLBACK (device_added_cb), self, G_CONNECT_SWAPPED);
794         g_signal_connect_object (self->client, "device-removed",
795                                  G_CALLBACK (device_removed_cb), self, G_CONNECT_SWAPPED);
796 
797         /* Setup ModemManager client */
798         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
799         if (system_bus == NULL) {
800                 g_warning ("Error connecting to system D-Bus: %s",
801                            error->message);
802         } else {
803                 self->modem_manager = mm_manager_new_sync (system_bus,
804                                                             G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
805                                                             NULL,
806                                                             &error);
807                 if (self->modem_manager == NULL)
808                         g_warning ("Error connecting to ModemManager: %s",
809                                    error->message);
810         }
811 
812         /* add remote settings such as VPN settings as virtual devices */
813         g_signal_connect_object (self->client, NM_CLIENT_CONNECTION_ADDED,
814                                  G_CALLBACK (add_connection), self, G_CONNECT_SWAPPED);
815         g_signal_connect_object (self->client, NM_CLIENT_CONNECTION_REMOVED,
816                                  G_CALLBACK (client_connection_removed_cb), self, G_CONNECT_SWAPPED);
817 
818         toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
819         g_signal_connect_after (toplevel, "map", G_CALLBACK (on_toplevel_map), self);
820 
821         /* Cold-plug existing connections */
822         connections = nm_client_get_connections (self->client);
823         if (connections) {
824                 for (i = 0; i < connections->len; i++)
825                         add_connection (self, connections->pdata[i]);
826         }
827 
828         g_debug ("Calling handle_argv() after cold-plugging connections");
829         handle_argv (self);
830 }
831