GCC Code Coverage Report


Directory: ./
File: panels/network/net-device-wifi.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 528 0.0%
Functions: 0 57 0.0%
Branches: 0 225 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2011-2012 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
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, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "config.h"
23
24 #include <glib-object.h>
25 #include <glib/gi18n.h>
26
27 #include <netinet/ether.h>
28
29 #include <NetworkManager.h>
30 #include <polkit/polkit.h>
31
32 #include "cc-wifi-hotspot-dialog.h"
33 #include "cc-hostname.h"
34 #include "hostname-helper.h"
35 #include "network-dialogs.h"
36 #include "panel-common.h"
37 #include "cc-list-row.h"
38 #include "cc-qr-code-dialog.h"
39
40 #include "connection-editor/net-connection-editor.h"
41 #include "net-device-wifi.h"
42
43 #include "cc-wifi-connection-list.h"
44 #include "cc-wifi-connection-row.h"
45
46 #define PERIODIC_WIFI_SCAN_TIMEOUT 15
47
48 static void nm_device_wifi_refresh_ui (NetDeviceWifi *self);
49 static void show_wifi_list (NetDeviceWifi *self);
50 static void show_hotspot_ui (NetDeviceWifi *self);
51 static void nm_client_on_permission_change (NetDeviceWifi *self);
52
53
54 struct _NetDeviceWifi
55 {
56 AdwBin parent;
57
58 AdwWindowTitle *wifi_headerbar_title;
59 AdwPreferencesGroup *details_box;
60 GtkSwitch *device_off_switch;
61 GtkBox *header_box;
62 GtkPopover *header_button_popover;
63 GtkBox *hotspot_box;
64 CcListRow *hotspot_name_row;
65 CcListRow *hotspot_security_row;
66 CcListRow *hotspot_password_row;
67 GtkBox *listbox_box;
68 GtkStack *stack;
69 AdwPreferencesGroup *saved_networks_box;
70 CcWifiConnectionList *saved_networks_list;
71 AdwDialog *saved_networks_dialog;
72 AdwToastOverlay *saved_networks_toast_overlay;
73 AdwToast *saved_networks_undo_toast;
74 GPtrArray *saved_networks_forgotten_rows;
75
76 AdwSwitchRow *device_enable_row;
77 CcListRow *saved_network_row;
78 CcListRow *connect_hidden_row;
79 CcListRow *hotspot_row;
80
81 CcPanel *panel;
82 NMClient *client;
83 NMDevice *device;
84 gboolean updating_device;
85 gchar *selected_ssid_title;
86 gchar *selected_connection_id;
87 gchar *selected_ap_id;
88 CcWifiHotspotDialog *hotspot_dialog;
89
90 gint64 last_scan;
91 gboolean scanning;
92
93 guint monitor_scanning_id;
94 guint scan_id;
95 GCancellable *cancellable;
96 };
97
98 enum {
99 PROP_0,
100 PROP_SCANNING,
101 PROP_LAST,
102 };
103
104 G_DEFINE_TYPE (NetDeviceWifi, net_device_wifi, ADW_TYPE_BIN)
105
106 static void
107 disable_scan_timeout (NetDeviceWifi *self)
108 {
109 g_debug ("Disabling periodic Wi-Fi scan");
110 g_clear_handle_id (&self->monitor_scanning_id, g_source_remove);
111 g_clear_handle_id (&self->scan_id, g_source_remove);
112 }
113
114 static void
115 wireless_enabled_toggled (NetDeviceWifi *self)
116 {
117 gboolean enabled;
118
119 enabled = nm_client_wireless_get_enabled (self->client);
120
121 self->updating_device = TRUE;
122 g_object_set (self->device_enable_row, "active", enabled, NULL);
123 if (!enabled) {
124 disable_scan_timeout (self);
125 gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), FALSE);
126 } else {
127 nm_client_on_permission_change(self);
128 }
129 gtk_widget_set_sensitive (GTK_WIDGET (self->connect_hidden_row), enabled);
130 self->updating_device = FALSE;
131 }
132
133 static NMConnection *
134 find_connection_for_device (NetDeviceWifi *self,
135 NMDevice *device)
136 {
137 return net_device_get_find_connection (self->client, device);
138 }
139
140 static gboolean
141 connection_is_shared (NMConnection *c)
142 {
143 NMSettingIPConfig *s_ip4;
144
145 s_ip4 = nm_connection_get_setting_ip4_config (c);
146 if (g_strcmp0 (nm_setting_ip_config_get_method (s_ip4),
147 NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0) {
148 return FALSE;
149 }
150
151 return TRUE;
152 }
153
154 static gboolean
155 device_is_hotspot (NetDeviceWifi *self)
156 {
157 NMConnection *c;
158
159 if (nm_device_get_active_connection (self->device) == NULL)
160 return FALSE;
161
162 c = find_connection_for_device (self, self->device);
163 if (c == NULL)
164 return FALSE;
165
166 return connection_is_shared (c);
167 }
168
169 static GBytes *
170 device_get_hotspot_ssid (NetDeviceWifi *self,
171 NMDevice *device)
172 {
173 NMConnection *c;
174 NMSettingWireless *sw;
175
176 c = find_connection_for_device (self, device);
177 if (c == NULL)
178 return NULL;
179
180 sw = nm_connection_get_setting_wireless (c);
181 return nm_setting_wireless_get_ssid (sw);
182 }
183
184 static void
185 get_secrets_cb (GObject *source_object,
186 GAsyncResult *res,
187 gpointer data)
188 {
189 NetDeviceWifi *self = data;
190 g_autoptr(GVariant) secrets = NULL;
191 g_autoptr(GError) error = NULL;
192
193 secrets = nm_remote_connection_get_secrets_finish (NM_REMOTE_CONNECTION (source_object), res, &error);
194 if (!secrets) {
195 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
196 g_warning ("Could not get secrets: %s", error->message);
197 return;
198 }
199
200 nm_connection_update_secrets (NM_CONNECTION (source_object),
201 NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
202 secrets, NULL);
203
204 nm_device_wifi_refresh_ui (self);
205 }
206
207 static void
208 device_get_hotspot_security_details (NetDeviceWifi *self,
209 NMDevice *device,
210 gchar **secret,
211 gchar **security)
212 {
213 NMConnection *c;
214 NMSettingWirelessSecurity *sws;
215 const gchar *key_mgmt;
216 const gchar *tmp_secret;
217 const gchar *tmp_security;
218
219 c = find_connection_for_device (self, device);
220 if (c == NULL)
221 return;
222
223 sws = nm_connection_get_setting_wireless_security (c);
224 if (sws == NULL)
225 return;
226
227 tmp_secret = NULL;
228 tmp_security = C_("Wifi security", "None");
229
230 /* Key management values:
231 * "none" = WEP or no password protection
232 * "wpa-psk" = WPAv2 Ad-Hoc mode (eg IBSS RSN) and AP-mode WPA v1 and v2
233 */
234 key_mgmt = nm_setting_wireless_security_get_key_mgmt (sws);
235 if (strcmp (key_mgmt, "none") == 0) {
236 tmp_secret = nm_setting_wireless_security_get_wep_key (sws, 0);
237 tmp_security = _("WEP");
238 }
239 else if (strcmp (key_mgmt, "wpa-psk") == 0) {
240 tmp_secret = nm_setting_wireless_security_get_psk (sws);
241 tmp_security = _("WPA");
242 } else {
243 g_warning ("unhandled security key-mgmt: %s", key_mgmt);
244 }
245
246 /* If we don't have secrets, request them from NM and bail.
247 * We'll refresh the UI when secrets arrive.
248 */
249 if (tmp_secret == NULL) {
250 nm_remote_connection_get_secrets_async ((NMRemoteConnection*)c,
251 NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
252 self->cancellable,
253 get_secrets_cb,
254 self);
255 return;
256 }
257
258 if (secret)
259 *secret = g_strdup (tmp_secret);
260 if (security)
261 *security = g_strdup (tmp_security);
262 }
263
264 static void
265 nm_device_wifi_refresh_hotspot (NetDeviceWifi *self)
266 {
267 GBytes *ssid;
268 g_autofree gchar *hotspot_secret = NULL;
269 g_autofree gchar *hotspot_security = NULL;
270 g_autofree gchar *hotspot_ssid = NULL;
271
272 /* refresh hotspot ui */
273 ssid = device_get_hotspot_ssid (self, self->device);
274 if (ssid)
275 hotspot_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
276 device_get_hotspot_security_details (self,
277 self->device,
278 &hotspot_secret,
279 &hotspot_security);
280
281 g_debug ("Refreshing hotspot labels to name: '%s', security key: '%s', security: '%s'",
282 hotspot_ssid, hotspot_secret, hotspot_security);
283
284 cc_list_row_set_secondary_label (self->hotspot_name_row, hotspot_ssid);
285 gtk_widget_set_visible (GTK_WIDGET (self->hotspot_name_row), hotspot_ssid != NULL);
286
287 cc_list_row_set_secondary_label (self->hotspot_password_row, hotspot_secret);
288 gtk_widget_set_visible (GTK_WIDGET (self->hotspot_password_row), hotspot_secret != NULL);
289
290 cc_list_row_set_secondary_label (self->hotspot_security_row, hotspot_security);
291 gtk_widget_set_visible (GTK_WIDGET (self->hotspot_security_row), hotspot_security != NULL);
292 }
293
294 static void
295 set_scanning (NetDeviceWifi *self,
296 gboolean scanning,
297 gint64 last_scan)
298 {
299 gboolean scanning_changed = self->scanning != scanning;
300
301 self->scanning = scanning;
302 self->last_scan = last_scan;
303
304 if (scanning_changed)
305 g_object_notify (G_OBJECT (self), "scanning");
306 }
307
308 static gboolean
309 update_scanning (gpointer user_data)
310 {
311 NetDeviceWifi *self = user_data;
312 gint64 last_scan;
313
314 last_scan = nm_device_wifi_get_last_scan (NM_DEVICE_WIFI (self->device));
315
316 /* The last_scan property is updated after the device finished scanning,
317 * so notify about it and stop monitoring for changes.
318 */
319 if (self->last_scan != last_scan) {
320 set_scanning (self, FALSE, last_scan);
321 self->monitor_scanning_id = 0;
322 return G_SOURCE_REMOVE;
323 }
324
325 return G_SOURCE_CONTINUE;
326 }
327
328 static gboolean
329 request_scan (gpointer user_data)
330 {
331 NetDeviceWifi *self = user_data;
332
333 g_debug ("Periodic Wi-Fi scan requested");
334
335 set_scanning (self, TRUE,
336 nm_device_wifi_get_last_scan (NM_DEVICE_WIFI (self->device)));
337
338 if (self->monitor_scanning_id == 0) {
339 self->monitor_scanning_id = g_timeout_add (1500, update_scanning,
340 self);
341 }
342
343 nm_device_wifi_request_scan_async (NM_DEVICE_WIFI (self->device),
344 self->cancellable, NULL, NULL);
345
346 return G_SOURCE_CONTINUE;
347 }
348
349 static void
350 nm_device_wifi_refresh_ui (NetDeviceWifi *self)
351 {
352 g_autofree gchar *status = NULL;
353
354 if (device_is_hotspot (self)) {
355 nm_device_wifi_refresh_hotspot (self);
356 show_hotspot_ui (self);
357 disable_scan_timeout (self);
358 return;
359 }
360
361 if (self->scan_id == 0 &&
362 nm_client_wireless_get_enabled (self->client)) {
363 self->scan_id = g_timeout_add_seconds (PERIODIC_WIFI_SCAN_TIMEOUT,
364 request_scan, self);
365 request_scan (self);
366 }
367
368 /* keep this in sync with the signal handler setup in cc_network_panel_init */
369 wireless_enabled_toggled (self);
370
371 status = panel_device_status_to_localized_string (self->device, NULL);
372 adw_window_title_set_subtitle (self->wifi_headerbar_title, status);
373 /* update list of APs */
374 show_wifi_list (self);
375
376 gtk_widget_set_visible (GTK_WIDGET (self->saved_network_row), !cc_wifi_connection_list_is_empty (self->saved_networks_list));
377 }
378
379 static void
380 device_off_switch_changed_cb (NetDeviceWifi *self)
381 {
382 gboolean active;
383
384 if (self->updating_device)
385 return;
386
387 active = adw_switch_row_get_active (self->device_enable_row);
388 nm_client_dbus_set_property (self->client,
389 NM_DBUS_PATH,
390 NM_DBUS_INTERFACE,
391 "WirelessEnabled",
392 g_variant_new_boolean (active),
393 -1,
394 NULL, NULL, NULL);
395 if (!active)
396 disable_scan_timeout (self);
397 gtk_widget_set_sensitive (GTK_WIDGET (self->connect_hidden_row), active);
398 }
399
400 static void
401 connect_hidden (NetDeviceWifi *self)
402 {
403 GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self));
404 cc_network_panel_connect_to_hidden_network (GTK_WIDGET (native), self->client);
405 }
406
407 static void
408 connection_add_activate_cb (GObject *source_object,
409 GAsyncResult *res,
410 gpointer user_data)
411 {
412 NMActiveConnection *conn;
413 g_autoptr(GError) error = NULL;
414
415 conn = nm_client_add_and_activate_connection_finish (NM_CLIENT (source_object), res, &error);
416 if (!conn) {
417 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
418 nm_device_wifi_refresh_ui (user_data);
419 /* failed to activate */
420 g_warning ("Failed to add and activate connection '%d': %s",
421 error->code,
422 error->message);
423 }
424 return;
425 }
426 }
427
428 static void
429 connection_activate_cb (GObject *source_object,
430 GAsyncResult *res,
431 gpointer user_data)
432 {
433 g_autoptr(GError) error = NULL;
434
435 if (!nm_client_activate_connection_finish (NM_CLIENT (source_object), res, &error)) {
436 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
437 nm_device_wifi_refresh_ui (user_data);
438 /* failed to activate */
439 g_debug ("Failed to add and activate connection '%d': %s",
440 error->code,
441 error->message);
442 }
443 return;
444 }
445 }
446
447 static gboolean
448 is_8021x (NMDevice *device,
449 const char *ap_object_path)
450 {
451 NM80211ApSecurityFlags wpa_flags, rsn_flags;
452 NMAccessPoint *ap;
453
454 ap = nm_device_wifi_get_access_point_by_path (NM_DEVICE_WIFI (device),
455 ap_object_path);
456 if (!ap)
457 return FALSE;
458
459 rsn_flags = nm_access_point_get_rsn_flags (ap);
460 if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
461 return TRUE;
462
463 wpa_flags = nm_access_point_get_wpa_flags (ap);
464 if (wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
465 return TRUE;
466 return FALSE;
467 }
468
469 static void
470 wireless_try_to_connect (NetDeviceWifi *self,
471 GBytes *ssid,
472 const gchar *ap_object_path)
473 {
474 const gchar *ssid_target;
475
476 if (self->updating_device)
477 return;
478
479 if (ap_object_path == NULL || ap_object_path[0] == 0)
480 return;
481
482 ssid_target = nm_utils_escape_ssid ((gpointer) g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
483 g_debug ("try to connect to WIFI network %s [%s]",
484 ssid_target, ap_object_path);
485
486 /* activate the connection */
487 if (!is_8021x (self->device, ap_object_path)) {
488 g_autoptr(GPermission) permission = NULL;
489 gboolean allowed_to_share = FALSE;
490 g_autoptr(NMConnection) partial = NULL;
491
492 permission = polkit_permission_new_sync ("org.freedesktop.NetworkManager.settings.modify.system",
493 NULL, NULL, NULL);
494 if (permission)
495 allowed_to_share = g_permission_get_allowed (permission);
496
497 if (!allowed_to_share) {
498 NMSettingConnection *s_con;
499
500 s_con = (NMSettingConnection *)nm_setting_connection_new ();
501 nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
502 partial = nm_simple_connection_new ();
503 nm_connection_add_setting (partial, NM_SETTING (s_con));
504 }
505
506 g_debug ("no existing connection found for %s, creating and activating one", ssid_target);
507 nm_client_add_and_activate_connection_async (self->client,
508 partial,
509 self->device,
510 ap_object_path,
511 self->cancellable,
512 connection_add_activate_cb,
513 self);
514 } else {
515 g_autoptr(GVariantBuilder) builder = NULL;
516 GVariant *parameters;
517
518 g_debug ("no existing connection found for %s, creating", ssid_target);
519 builder = g_variant_builder_new (G_VARIANT_TYPE ("av"));
520 g_variant_builder_add (builder, "v", g_variant_new_string ("connect-8021x-wifi"));
521 g_variant_builder_add (builder, "v", g_variant_new_string (nm_object_get_path (NM_OBJECT (self->device))));
522 g_variant_builder_add (builder, "v", g_variant_new_string (ap_object_path));
523 parameters = g_variant_new ("av", builder);
524
525 g_object_set (self->panel, "parameters", parameters, NULL);
526 }
527 }
528
529 static gboolean
530 is_hotspot_connection (NMConnection *connection)
531 {
532 NMSettingConnection *sc;
533 NMSettingWireless *sw;
534 NMSettingIPConfig *sip;
535 NMSetting *setting;
536
537 sc = nm_connection_get_setting_connection (connection);
538 if (g_strcmp0 (nm_setting_connection_get_connection_type (sc), "802-11-wireless") != 0) {
539 return FALSE;
540 }
541 sw = nm_connection_get_setting_wireless (connection);
542 if (g_strcmp0 (nm_setting_wireless_get_mode (sw), "adhoc") != 0 &&
543 g_strcmp0 (nm_setting_wireless_get_mode (sw), "ap") != 0) {
544 return FALSE;
545 }
546 setting = nm_connection_get_setting_by_name (connection, NM_SETTING_WIRELESS_SETTING_NAME);
547 if (!setting)
548 return FALSE;
549
550 sip = nm_connection_get_setting_ip4_config (connection);
551 if (g_strcmp0 (nm_setting_ip_config_get_method (sip), "shared") != 0) {
552 return FALSE;
553 }
554
555 return TRUE;
556 }
557
558 static void
559 show_hotspot_ui (NetDeviceWifi *self)
560 {
561 /* show hotspot tab */
562 gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->hotspot_box));
563 }
564
565 static void
566 activate_cb (GObject *source_object,
567 GAsyncResult *res,
568 gpointer user_data)
569 {
570 g_autoptr(GError) error = NULL;
571
572 if (nm_client_activate_connection_finish (NM_CLIENT (source_object), res, &error) == NULL) {
573 g_warning ("Failed to add new connection: (%d) %s",
574 error->code,
575 error->message);
576 return;
577 }
578
579 /* show hotspot tab */
580 nm_device_wifi_refresh_ui (user_data);
581 }
582
583 static void
584 activate_new_cb (GObject *source_object,
585 GAsyncResult *res,
586 gpointer user_data)
587 {
588 NMActiveConnection *conn;
589 g_autoptr(GError) error = NULL;
590
591 conn = nm_client_add_and_activate_connection_finish (NM_CLIENT (source_object),
592 res, &error);
593 if (!conn) {
594 g_warning ("Failed to add new connection: (%d) %s",
595 error->code,
596 error->message);
597 return;
598 }
599
600 /* show hotspot tab */
601 nm_device_wifi_refresh_ui (user_data);
602 }
603
604 static NMConnection *
605 net_device_wifi_get_hotspot_connection (NetDeviceWifi *self)
606 {
607 GSList *connections, *l;
608 NMConnection *c = NULL;
609
610 connections = net_device_get_valid_connections (self->client, self->device);
611 for (l = connections; l; l = l->next) {
612 NMConnection *tmp = l->data;
613 if (is_hotspot_connection (tmp)) {
614 c = tmp;
615 break;
616 }
617 }
618 g_slist_free (connections);
619
620 return c;
621 }
622
623 static void
624 overwrite_ssid_cb (GObject *source_object,
625 GAsyncResult *res,
626 gpointer user_data)
627 {
628 g_autoptr(GError) error = NULL;
629 NMRemoteConnection *connection;
630 NMConnection *c;
631 NetDeviceWifi *self;
632
633 connection = NM_REMOTE_CONNECTION (source_object);
634
635 if (!nm_remote_connection_commit_changes_finish (connection, res, &error)) {
636 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
637 g_warning ("Failed to save hotspot's settings to disk: %s",
638 error->message);
639 return;
640 }
641
642 self = user_data;
643 c = net_device_wifi_get_hotspot_connection (self);
644
645 g_debug ("activate existing hotspot connection\n");
646 nm_client_activate_connection_async (self->client,
647 c,
648 self->device,
649 NULL,
650 self->cancellable,
651 activate_cb,
652 self);
653 }
654
655 static void
656 on_wifi_hotspot_dialog_response_cb (GtkDialog *dialog,
657 gint response,
658 NetDeviceWifi *self)
659 {
660 if (response == GTK_RESPONSE_APPLY) {
661 NMConnection *connection;
662
663 connection = cc_wifi_hotspot_dialog_get_connection (self->hotspot_dialog);
664 if (NM_IS_REMOTE_CONNECTION (connection))
665 nm_remote_connection_commit_changes_async (NM_REMOTE_CONNECTION (connection),
666 TRUE,
667 self->cancellable,
668 overwrite_ssid_cb,
669 self);
670 else
671 nm_client_add_and_activate_connection_async (self->client,
672 connection,
673 self->device,
674 NULL,
675 self->cancellable,
676 activate_new_cb,
677 self);
678 }
679
680 gtk_widget_set_visible (GTK_WIDGET (self->hotspot_dialog), FALSE);
681 }
682
683 static void
684 start_hotspot (NetDeviceWifi *self)
685 {
686 GtkNative *native;
687 NMConnection *c;
688 g_autofree gchar *hostname = NULL;
689 g_autofree gchar *ssid = NULL;
690
691 native = gtk_widget_get_native (GTK_WIDGET (self));
692
693 if (!self->hotspot_dialog) {
694 self->hotspot_dialog = cc_wifi_hotspot_dialog_new (GTK_WINDOW (native));
695 g_object_ref_sink (self->hotspot_dialog);
696 }
697 cc_wifi_hotspot_dialog_set_device (self->hotspot_dialog, NM_DEVICE_WIFI (self->device));
698 hostname = cc_hostname_get_display_hostname (cc_hostname_get_default ());
699 ssid = pretty_hostname_to_ssid (hostname);
700 cc_wifi_hotspot_dialog_set_hostname (self->hotspot_dialog, ssid);
701 c = net_device_wifi_get_hotspot_connection (self);
702 if (c)
703 cc_wifi_hotspot_dialog_set_connection (self->hotspot_dialog, c);
704
705 g_signal_connect_after (self->hotspot_dialog, "response", G_CALLBACK (on_wifi_hotspot_dialog_response_cb), self);
706 gtk_window_present (GTK_WINDOW (self->hotspot_dialog));
707 }
708
709 static void
710 stop_shared_connection (NetDeviceWifi *self)
711 {
712 const GPtrArray *connections;
713 const GPtrArray *devices;
714 gint i;
715 NMActiveConnection *c;
716 gboolean found = FALSE;
717
718 connections = nm_client_get_active_connections (self->client);
719 for (i = 0; connections && i < connections->len; i++) {
720 c = (NMActiveConnection *)connections->pdata[i];
721
722 devices = nm_active_connection_get_devices (c);
723 if (devices && devices->pdata[0] == self->device) {
724 nm_client_deactivate_connection_async (self->client, c, NULL, NULL, NULL);
725 found = TRUE;
726 break;
727 }
728 }
729
730 if (!found) {
731 g_warning ("Could not stop hotspot connection as no connection attached to the device could be found.");
732 return;
733 }
734
735 nm_device_wifi_refresh_ui (self);
736 }
737
738 static void
739 show_wifi_list (NetDeviceWifi *self)
740 {
741 gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->listbox_box));
742 }
743
744 static void
745 net_device_wifi_dispose (GObject *object)
746 {
747 NetDeviceWifi *self = NET_DEVICE_WIFI (object);
748
749 if (self->hotspot_dialog) {
750 gtk_window_destroy (GTK_WINDOW (self->hotspot_dialog));
751 g_object_unref (self->hotspot_dialog);
752 self->hotspot_dialog = NULL;
753 }
754
755 G_OBJECT_CLASS (net_device_wifi_parent_class)->dispose (object);
756 }
757
758 static void
759 net_device_wifi_finalize (GObject *object)
760 {
761 NetDeviceWifi *self = NET_DEVICE_WIFI (object);
762
763 g_cancellable_cancel (self->cancellable);
764 g_clear_object (&self->cancellable);
765 disable_scan_timeout (self);
766
767 g_clear_object (&self->client);
768 g_clear_object (&self->device);
769 g_clear_object (&self->saved_networks_list);
770 g_clear_pointer (&self->selected_ssid_title, g_free);
771 g_clear_pointer (&self->selected_connection_id, g_free);
772 g_clear_pointer (&self->selected_ap_id, g_free);
773
774 G_OBJECT_CLASS (net_device_wifi_parent_class)->finalize (object);
775 }
776
777 static void
778 net_device_wifi_get_property (GObject *object,
779 guint prop_id,
780 GValue *value,
781 GParamSpec *pspec)
782 {
783 NetDeviceWifi *self = NET_DEVICE_WIFI (object);
784
785 switch (prop_id) {
786 case PROP_SCANNING:
787 g_value_set_boolean (value, self->scanning);
788 break;
789 default:
790 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
791 break;
792 }
793 }
794
795 static void
796 really_forgotten (GObject *source_object,
797 GAsyncResult *res,
798 gpointer user_data)
799 {
800 g_autoptr(NetDeviceWifi) self = NET_DEVICE_WIFI (user_data);
801 g_autoptr(GError) error = NULL;
802
803 cc_wifi_connection_list_thaw (self->saved_networks_list);
804
805 if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (source_object), res, &error))
806 g_warning ("failed to delete connection %s: %s",
807 nm_object_get_path (NM_OBJECT (source_object)),
808 error->message);
809
810 if (cc_wifi_connection_list_is_empty (self->saved_networks_list)) {
811 adw_dialog_close (self->saved_networks_dialog);
812 gtk_widget_set_visible (GTK_WIDGET (self->saved_network_row), FALSE);
813 }
814 }
815
816 static void
817 really_forget (gpointer data)
818 {
819 CcWifiConnectionRow *row = CC_WIFI_CONNECTION_ROW (data);
820 NMRemoteConnection *connection;
821 NetDeviceWifi *self;
822
823 self = NET_DEVICE_WIFI (g_object_get_data (G_OBJECT (row), "net"));
824
825 connection = NM_REMOTE_CONNECTION (cc_wifi_connection_row_get_connection (row));
826
827 nm_remote_connection_delete_async (connection, self->cancellable, really_forgotten, g_object_ref (self));
828 }
829
830 static void
831 undo_forget (gpointer data)
832 {
833 CcWifiConnectionRow *row = CC_WIFI_CONNECTION_ROW (data);
834 NetDeviceWifi *self;
835
836 self = NET_DEVICE_WIFI (g_object_get_data (G_OBJECT (row), "net"));
837
838 gtk_widget_set_visible (GTK_WIDGET (row), TRUE);
839
840 cc_wifi_connection_list_thaw (self->saved_networks_list);
841 }
842
843 static void
844 on_saved_networks_forget_dismissed (NetDeviceWifi *self)
845 {
846 self->saved_networks_undo_toast = NULL;
847
848 /* Since the free function is set to either undo or really forget,
849 this always does the right thing */
850 g_clear_pointer (&self->saved_networks_forgotten_rows, g_ptr_array_unref);
851 }
852
853 static void
854 on_saved_networks_forget_undo (NetDeviceWifi *self)
855 {
856 g_ptr_array_set_free_func (self->saved_networks_forgotten_rows, undo_forget);
857 }
858
859 static void
860 forget_selected (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list)
861 {
862 g_autofree gchar *message = NULL;
863
864 cc_wifi_connection_list_freeze (list);
865
866 gtk_widget_set_visible (GTK_WIDGET (row), FALSE);
867 g_object_set_data (G_OBJECT (row), "net", self);
868
869 /* Add row to the array of rows to be handled by the undo toast */
870 if (!self->saved_networks_forgotten_rows)
871 self->saved_networks_forgotten_rows = g_ptr_array_new_with_free_func (really_forget);
872
873 g_ptr_array_add (self->saved_networks_forgotten_rows, row);
874
875 /* If undo toast does not exist, create it, otherwise update title and extend timeout */
876 if (!self->saved_networks_undo_toast) {
877 message = g_strdup (_("Network deleted"));
878 self->saved_networks_undo_toast = adw_toast_new (message);
879 adw_toast_set_button_label (self->saved_networks_undo_toast, _("_Undo"));
880
881 g_signal_connect_swapped (self->saved_networks_undo_toast,
882 "button-clicked",
883 G_CALLBACK (on_saved_networks_forget_undo),
884 self);
885
886 g_signal_connect_swapped (self->saved_networks_undo_toast,
887 "dismissed",
888 G_CALLBACK (on_saved_networks_forget_dismissed),
889 self);
890 } else {
891 /* Translators: %d is the number of network connections deleted. */
892 message = g_strdup_printf (ngettext ("%d network deleted",
893 "%d networks deleted",
894 self->saved_networks_forgotten_rows->len),
895 self->saved_networks_forgotten_rows->len);
896
897 adw_toast_set_title (self->saved_networks_undo_toast, message);
898
899 g_object_ref (self->saved_networks_undo_toast);
900 }
901
902 adw_toast_overlay_add_toast (self->saved_networks_toast_overlay, self->saved_networks_undo_toast);
903 }
904
905 static gint
906 history_sort (gconstpointer a, gconstpointer b, gpointer data)
907 {
908 guint64 ta, tb;
909 NMConnection *ca, *cb;
910 NMSettingConnection *sc;
911
912 ca = cc_wifi_connection_row_get_connection (CC_WIFI_CONNECTION_ROW ((gpointer) a));
913 cb = cc_wifi_connection_row_get_connection (CC_WIFI_CONNECTION_ROW ((gpointer) b));
914
915 if (ca) {
916 sc = nm_connection_get_setting_connection (ca);
917 ta = nm_setting_connection_get_timestamp (sc);
918 } else {
919 ta = 0;
920 }
921
922 if (cb) {
923 sc = nm_connection_get_setting_connection (cb);
924 tb = nm_setting_connection_get_timestamp (sc);
925 } else {
926 tb = 0;
927 }
928
929 if (ta > tb) return -1;
930 if (tb > ta) return 1;
931
932 return 0;
933 }
934
935 static gint
936 ap_sort (gconstpointer a, gconstpointer b, gpointer data)
937 {
938 NetDeviceWifi *self = data;
939 CcWifiConnectionRow *a_row = CC_WIFI_CONNECTION_ROW ((gpointer) a);
940 CcWifiConnectionRow *b_row = CC_WIFI_CONNECTION_ROW ((gpointer) b);
941 NMActiveConnection *active_connection;
942 gboolean a_configured, b_configured;
943 NMAccessPoint *apa, *apb;
944 guint sa, sb;
945
946 /* Show the connected AP first */
947 active_connection = nm_device_get_active_connection (NM_DEVICE (self->device));
948 if (active_connection != NULL) {
949 NMConnection *connection = NM_CONNECTION (nm_active_connection_get_connection (active_connection));
950 if (connection == cc_wifi_connection_row_get_connection (a_row))
951 return -1;
952 else if (connection == cc_wifi_connection_row_get_connection (b_row))
953 return 1;
954 }
955
956 /* Show configured networks before non-configured */
957 a_configured = cc_wifi_connection_row_get_connection (a_row) != NULL;
958 b_configured = cc_wifi_connection_row_get_connection (b_row) != NULL;
959 if (a_configured != b_configured) {
960 if (a_configured) return -1;
961 if (b_configured) return 1;
962 }
963
964 /* Show higher strength networks above lower strength ones */
965
966 apa = cc_wifi_connection_row_best_access_point (a_row);
967 apb = cc_wifi_connection_row_best_access_point (b_row);
968
969 if (apa)
970 sa = nm_access_point_get_strength (apa);
971 else
972 sa = 0;
973
974 if (apb)
975 sb = nm_access_point_get_strength (apb);
976 else
977 sb = 0;
978
979 if (sa > sb) return -1;
980 if (sb > sa) return 1;
981
982 return 0;
983 }
984
985 static void
986 show_details_for_row (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list)
987 {
988 NMConnection *connection;
989 NMAccessPoint *ap;
990 NetConnectionEditor *editor;
991
992 connection = cc_wifi_connection_row_get_connection (row);
993 ap = cc_wifi_connection_row_best_access_point (row);
994
995 editor = net_connection_editor_new (connection, self->device, ap, self->client);
996 gtk_window_set_transient_for (GTK_WINDOW (editor), GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (row))));
997 gtk_window_present (GTK_WINDOW (editor));
998 }
999
1000 static void
1001 show_qr_code_for_row (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list)
1002 {
1003 NMConnection *connection;
1004 GtkWidget *dialog;
1005
1006 connection = cc_wifi_connection_row_get_connection (row);
1007
1008 // getting a new "local" connection, since we don't want to populate the secrets of the original connection
1009 connection = NM_CONNECTION (nm_client_get_connection_by_id (self->client, nm_connection_get_id (connection)));
1010
1011 dialog = cc_qr_code_dialog_new (connection);
1012 adw_dialog_present (ADW_DIALOG (dialog), GTK_WIDGET (self));
1013 }
1014
1015 static void
1016 on_connect_hidden_network (NetDeviceWifi *self)
1017 {
1018 connect_hidden (self);
1019 }
1020
1021 static void
1022 on_wifi_hotspot_network (NetDeviceWifi *self)
1023 {
1024 start_hotspot (self);
1025 }
1026
1027 static void
1028 ap_activated (NetDeviceWifi *self, GtkListBoxRow *row)
1029 {
1030 CcWifiConnectionRow *c_row;
1031 NMConnection *connection;
1032 NMAccessPoint *ap;
1033
1034 /* The mockups want a row to connecto hidden networks; this could
1035 * be handeled here. */
1036 if (!CC_IS_WIFI_CONNECTION_ROW (row))
1037 return;
1038
1039 c_row = CC_WIFI_CONNECTION_ROW (row);
1040
1041 connection = cc_wifi_connection_row_get_connection (c_row);
1042 ap = cc_wifi_connection_row_best_access_point (c_row);
1043
1044 if (ap != NULL) {
1045 if (connection != NULL) {
1046 nm_client_activate_connection_async (self->client,
1047 connection,
1048 self->device, NULL, self->cancellable,
1049 connection_activate_cb, self);
1050 } else {
1051 GBytes *ssid;
1052 const gchar *object_path;
1053
1054 ssid = nm_access_point_get_ssid (ap);
1055 object_path = nm_object_get_path (NM_OBJECT (ap));
1056 wireless_try_to_connect (self, ssid, object_path);
1057 }
1058 }
1059 }
1060
1061 static void
1062 net_device_wifi_class_init (NetDeviceWifiClass *klass)
1063 {
1064 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1065 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1066
1067 object_class->dispose = net_device_wifi_dispose;
1068 object_class->finalize = net_device_wifi_finalize;
1069 object_class->get_property = net_device_wifi_get_property;
1070
1071 g_object_class_install_property (object_class,
1072 PROP_SCANNING,
1073 g_param_spec_boolean ("scanning",
1074 "Scanning",
1075 "Whether the device is scanning for access points",
1076 FALSE,
1077 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1078
1079 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/network-wifi.ui");
1080
1081 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, wifi_headerbar_title);
1082 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, device_enable_row);
1083 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_network_row);
1084 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, connect_hidden_row);
1085 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_row);
1086 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, details_box);
1087 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_box);
1088 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_name_row);
1089 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_security_row);
1090 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_password_row);
1091 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, listbox_box);
1092 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, stack);
1093 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_networks_dialog);
1094 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_networks_toast_overlay);
1095 gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_networks_box);
1096
1097 gtk_widget_class_bind_template_callback (widget_class, device_off_switch_changed_cb);
1098 gtk_widget_class_bind_template_callback (widget_class, on_connect_hidden_network);
1099 gtk_widget_class_bind_template_callback (widget_class, on_wifi_hotspot_network);
1100 }
1101
1102 static void
1103 net_device_wifi_init (NetDeviceWifi *self)
1104 {
1105 gtk_widget_init_template (GTK_WIDGET (self));
1106
1107 self->cancellable = g_cancellable_new ();
1108 }
1109
1110
1111 static void
1112 nm_client_on_permission_change (NetDeviceWifi *self) {
1113 NMClientPermissionResult perm;
1114 NMDeviceWifiCapabilities caps;
1115
1116 if (nm_client_get_permissions_state (self->client) != NM_TERNARY_TRUE) {
1117 /* permissions aren't ready yet */
1118 return;
1119 }
1120
1121 /* only enable the button if the user can create a hotspot */
1122 perm = nm_client_get_permission_result (self->client, NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN);
1123 caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (self->device));
1124 if (perm != NM_CLIENT_PERMISSION_RESULT_YES &&
1125 perm != NM_CLIENT_PERMISSION_RESULT_AUTH) {
1126 gtk_widget_set_tooltip_text (GTK_WIDGET (self->hotspot_row), _("System policy prohibits use as a Hotspot"));
1127 gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), FALSE);
1128 } else if (!(caps & (NM_WIFI_DEVICE_CAP_AP | NM_WIFI_DEVICE_CAP_ADHOC))) {
1129 gtk_widget_set_tooltip_text (GTK_WIDGET (self->hotspot_row), _("Wireless device does not support Hotspot mode"));
1130 gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), FALSE);
1131 } else {
1132 gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), nm_client_wireless_get_enabled (self->client));
1133 }
1134 }
1135
1136 NetDeviceWifi *
1137 net_device_wifi_new (CcPanel *panel, NMClient *client, NMDevice *device)
1138 {
1139 NetDeviceWifi *self;
1140 GtkListBox *listbox;
1141 CcWifiConnectionList *list;
1142
1143 self = g_object_new (net_device_wifi_get_type (), NULL);
1144 self->panel = panel;
1145 self->client = g_object_ref (client);
1146 self->device = g_object_ref (device);
1147
1148 g_signal_connect_object (client, "notify::wireless-enabled",
1149 G_CALLBACK (wireless_enabled_toggled), self, G_CONNECT_SWAPPED);
1150
1151 g_signal_connect_object (device, "state-changed", G_CALLBACK (nm_device_wifi_refresh_ui), self, G_CONNECT_SWAPPED);
1152
1153 /* Set up the main Visible Networks list */
1154 list = cc_wifi_connection_list_new (client, NM_DEVICE_WIFI (device), TRUE, TRUE, FALSE, FALSE);
1155 cc_wifi_connection_list_set_placeholder_text (list, _("Searching for networks…"));
1156 gtk_box_append (self->listbox_box, GTK_WIDGET (list));
1157
1158 listbox = cc_wifi_connection_list_get_list_box (list);
1159 gtk_list_box_set_sort_func (listbox, (GtkListBoxSortFunc)ap_sort, self, NULL);
1160
1161 g_signal_connect_object (listbox, "row-activated",
1162 G_CALLBACK (ap_activated), self, G_CONNECT_SWAPPED);
1163 g_signal_connect_object (list, "configure",
1164 G_CALLBACK (show_details_for_row), self, G_CONNECT_SWAPPED);
1165 g_signal_connect_object (list, "show-qr-code",
1166 G_CALLBACK (show_qr_code_for_row), self, G_CONNECT_SWAPPED);
1167 g_signal_connect_object (client, "notify",
1168 G_CALLBACK(nm_client_on_permission_change), self, G_CONNECT_SWAPPED);
1169
1170 /* Set up the Saved Networks list */
1171 list = cc_wifi_connection_list_new (self->client, NM_DEVICE_WIFI (device), FALSE, FALSE, FALSE, TRUE);
1172 cc_wifi_connection_list_set_placeholder_text (list, _("No saved networks"));
1173 self->saved_networks_list = g_object_ref_sink (list);
1174 adw_preferences_group_add (self->saved_networks_box, GTK_WIDGET (list));
1175
1176 listbox = cc_wifi_connection_list_get_list_box (list);
1177 gtk_list_box_set_sort_func (listbox, (GtkListBoxSortFunc)history_sort, NULL, NULL);
1178
1179 g_signal_connect_object (list, "configure",
1180 G_CALLBACK (show_details_for_row), self, G_CONNECT_SWAPPED);
1181 g_signal_connect_object (list, "forget",
1182 G_CALLBACK (forget_selected), self, G_CONNECT_SWAPPED);
1183 g_signal_connect_object (list, "show_qr_code",
1184 G_CALLBACK (show_qr_code_for_row), self, G_CONNECT_SWAPPED);
1185
1186 nm_client_on_permission_change(self);
1187
1188 nm_device_wifi_refresh_ui (self);
1189
1190 return self;
1191 }
1192
1193 NMDevice *
1194 net_device_wifi_get_device (NetDeviceWifi *self)
1195 {
1196 g_return_val_if_fail (NET_IS_DEVICE_WIFI (self), NULL);
1197 return self->device;
1198 }
1199
1200 void
1201 net_device_wifi_set_title (NetDeviceWifi *self, const gchar *title)
1202 {
1203 g_return_if_fail (NET_IS_DEVICE_WIFI (self));
1204 adw_window_title_set_title (self->wifi_headerbar_title, title);
1205 }
1206
1207 GtkWidget *
1208 net_device_wifi_get_header_widget (NetDeviceWifi *self)
1209 {
1210 g_return_val_if_fail (NET_IS_DEVICE_WIFI (self), NULL);
1211 return GTK_WIDGET (self->details_box);
1212 }
1213
1214 GtkWidget *
1215 net_device_wifi_get_title_widget (NetDeviceWifi *self)
1216 {
1217 g_return_val_if_fail (NET_IS_DEVICE_WIFI (self), NULL);
1218 return GTK_WIDGET (self->wifi_headerbar_title);
1219 }
1220
1221 void
1222 net_device_wifi_turn_off_hotspot (NetDeviceWifi *self)
1223 {
1224 g_return_if_fail (NET_IS_DEVICE_WIFI (self));
1225
1226 stop_shared_connection (self);
1227 }
1228