gnome-control-center/panels/network/net-device-wifi.c
Automeris naranja 8fc42119e4 net-device-wifi: Rename "Searching networks…" to "Searching for networks…"
"Searching networks" is incorrect, because the lack of the
preposition "for" implies that something is being searching
in networks, which isn't the case. This placeholder is
supposed to mean that networks are being searched/discovered.

See also: https://gitlab.gnome.org/GNOME/gnome-bluetooth/-/merge_requests/186#note_2093252
2024-05-02 18:28:56 -03:00

1227 lines
45 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2011-2012 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <glib-object.h>
#include <glib/gi18n.h>
#include <netinet/ether.h>
#include <NetworkManager.h>
#include <polkit/polkit.h>
#include "cc-wifi-hotspot-dialog.h"
#include "cc-hostname.h"
#include "hostname-helper.h"
#include "network-dialogs.h"
#include "panel-common.h"
#include "cc-list-row.h"
#include "cc-qr-code-dialog.h"
#include "connection-editor/net-connection-editor.h"
#include "net-device-wifi.h"
#include "cc-wifi-connection-list.h"
#include "cc-wifi-connection-row.h"
#define PERIODIC_WIFI_SCAN_TIMEOUT 15
static void nm_device_wifi_refresh_ui (NetDeviceWifi *self);
static void show_wifi_list (NetDeviceWifi *self);
static void show_hotspot_ui (NetDeviceWifi *self);
static void nm_client_on_permission_change (NetDeviceWifi *self);
struct _NetDeviceWifi
{
AdwBin parent;
AdwWindowTitle *wifi_headerbar_title;
AdwPreferencesGroup *details_box;
GtkSwitch *device_off_switch;
GtkBox *header_box;
GtkPopover *header_button_popover;
GtkBox *hotspot_box;
CcListRow *hotspot_name_row;
CcListRow *hotspot_security_row;
CcListRow *hotspot_password_row;
GtkBox *listbox_box;
GtkStack *stack;
AdwPreferencesGroup *saved_networks_box;
CcWifiConnectionList *saved_networks_list;
AdwDialog *saved_networks_dialog;
AdwToastOverlay *saved_networks_toast_overlay;
AdwToast *saved_networks_undo_toast;
GPtrArray *saved_networks_forgotten_rows;
AdwSwitchRow *device_enable_row;
CcListRow *saved_network_row;
CcListRow *connect_hidden_row;
CcListRow *hotspot_row;
CcPanel *panel;
NMClient *client;
NMDevice *device;
gboolean updating_device;
gchar *selected_ssid_title;
gchar *selected_connection_id;
gchar *selected_ap_id;
CcWifiHotspotDialog *hotspot_dialog;
gint64 last_scan;
gboolean scanning;
guint monitor_scanning_id;
guint scan_id;
GCancellable *cancellable;
};
enum {
PROP_0,
PROP_SCANNING,
PROP_LAST,
};
G_DEFINE_TYPE (NetDeviceWifi, net_device_wifi, ADW_TYPE_BIN)
static void
disable_scan_timeout (NetDeviceWifi *self)
{
g_debug ("Disabling periodic Wi-Fi scan");
g_clear_handle_id (&self->monitor_scanning_id, g_source_remove);
g_clear_handle_id (&self->scan_id, g_source_remove);
}
static void
wireless_enabled_toggled (NetDeviceWifi *self)
{
gboolean enabled;
enabled = nm_client_wireless_get_enabled (self->client);
self->updating_device = TRUE;
g_object_set (self->device_enable_row, "active", enabled, NULL);
if (!enabled) {
disable_scan_timeout (self);
gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), FALSE);
} else {
nm_client_on_permission_change(self);
}
gtk_widget_set_sensitive (GTK_WIDGET (self->connect_hidden_row), enabled);
self->updating_device = FALSE;
}
static NMConnection *
find_connection_for_device (NetDeviceWifi *self,
NMDevice *device)
{
return net_device_get_find_connection (self->client, device);
}
static gboolean
connection_is_shared (NMConnection *c)
{
NMSettingIPConfig *s_ip4;
s_ip4 = nm_connection_get_setting_ip4_config (c);
if (g_strcmp0 (nm_setting_ip_config_get_method (s_ip4),
NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0) {
return FALSE;
}
return TRUE;
}
static gboolean
device_is_hotspot (NetDeviceWifi *self)
{
NMConnection *c;
if (nm_device_get_active_connection (self->device) == NULL)
return FALSE;
c = find_connection_for_device (self, self->device);
if (c == NULL)
return FALSE;
return connection_is_shared (c);
}
static GBytes *
device_get_hotspot_ssid (NetDeviceWifi *self,
NMDevice *device)
{
NMConnection *c;
NMSettingWireless *sw;
c = find_connection_for_device (self, device);
if (c == NULL)
return NULL;
sw = nm_connection_get_setting_wireless (c);
return nm_setting_wireless_get_ssid (sw);
}
static void
get_secrets_cb (GObject *source_object,
GAsyncResult *res,
gpointer data)
{
NetDeviceWifi *self = data;
g_autoptr(GVariant) secrets = NULL;
g_autoptr(GError) error = NULL;
secrets = nm_remote_connection_get_secrets_finish (NM_REMOTE_CONNECTION (source_object), res, &error);
if (!secrets) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Could not get secrets: %s", error->message);
return;
}
nm_connection_update_secrets (NM_CONNECTION (source_object),
NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
secrets, NULL);
nm_device_wifi_refresh_ui (self);
}
static void
device_get_hotspot_security_details (NetDeviceWifi *self,
NMDevice *device,
gchar **secret,
gchar **security)
{
NMConnection *c;
NMSettingWirelessSecurity *sws;
const gchar *key_mgmt;
const gchar *tmp_secret;
const gchar *tmp_security;
c = find_connection_for_device (self, device);
if (c == NULL)
return;
sws = nm_connection_get_setting_wireless_security (c);
if (sws == NULL)
return;
tmp_secret = NULL;
tmp_security = C_("Wifi security", "None");
/* Key management values:
* "none" = WEP or no password protection
* "wpa-psk" = WPAv2 Ad-Hoc mode (eg IBSS RSN) and AP-mode WPA v1 and v2
*/
key_mgmt = nm_setting_wireless_security_get_key_mgmt (sws);
if (strcmp (key_mgmt, "none") == 0) {
tmp_secret = nm_setting_wireless_security_get_wep_key (sws, 0);
tmp_security = _("WEP");
}
else if (strcmp (key_mgmt, "wpa-psk") == 0) {
tmp_secret = nm_setting_wireless_security_get_psk (sws);
tmp_security = _("WPA");
} else {
g_warning ("unhandled security key-mgmt: %s", key_mgmt);
}
/* If we don't have secrets, request them from NM and bail.
* We'll refresh the UI when secrets arrive.
*/
if (tmp_secret == NULL) {
nm_remote_connection_get_secrets_async ((NMRemoteConnection*)c,
NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
self->cancellable,
get_secrets_cb,
self);
return;
}
if (secret)
*secret = g_strdup (tmp_secret);
if (security)
*security = g_strdup (tmp_security);
}
static void
nm_device_wifi_refresh_hotspot (NetDeviceWifi *self)
{
GBytes *ssid;
g_autofree gchar *hotspot_secret = NULL;
g_autofree gchar *hotspot_security = NULL;
g_autofree gchar *hotspot_ssid = NULL;
/* refresh hotspot ui */
ssid = device_get_hotspot_ssid (self, self->device);
if (ssid)
hotspot_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
device_get_hotspot_security_details (self,
self->device,
&hotspot_secret,
&hotspot_security);
g_debug ("Refreshing hotspot labels to name: '%s', security key: '%s', security: '%s'",
hotspot_ssid, hotspot_secret, hotspot_security);
cc_list_row_set_secondary_label (self->hotspot_name_row, hotspot_ssid);
gtk_widget_set_visible (GTK_WIDGET (self->hotspot_name_row), hotspot_ssid != NULL);
cc_list_row_set_secondary_label (self->hotspot_password_row, hotspot_secret);
gtk_widget_set_visible (GTK_WIDGET (self->hotspot_password_row), hotspot_secret != NULL);
cc_list_row_set_secondary_label (self->hotspot_security_row, hotspot_security);
gtk_widget_set_visible (GTK_WIDGET (self->hotspot_security_row), hotspot_security != NULL);
}
static void
set_scanning (NetDeviceWifi *self,
gboolean scanning,
gint64 last_scan)
{
gboolean scanning_changed = self->scanning != scanning;
self->scanning = scanning;
self->last_scan = last_scan;
if (scanning_changed)
g_object_notify (G_OBJECT (self), "scanning");
}
static gboolean
update_scanning (gpointer user_data)
{
NetDeviceWifi *self = user_data;
gint64 last_scan;
last_scan = nm_device_wifi_get_last_scan (NM_DEVICE_WIFI (self->device));
/* The last_scan property is updated after the device finished scanning,
* so notify about it and stop monitoring for changes.
*/
if (self->last_scan != last_scan) {
set_scanning (self, FALSE, last_scan);
self->monitor_scanning_id = 0;
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
static gboolean
request_scan (gpointer user_data)
{
NetDeviceWifi *self = user_data;
g_debug ("Periodic Wi-Fi scan requested");
set_scanning (self, TRUE,
nm_device_wifi_get_last_scan (NM_DEVICE_WIFI (self->device)));
if (self->monitor_scanning_id == 0) {
self->monitor_scanning_id = g_timeout_add (1500, update_scanning,
self);
}
nm_device_wifi_request_scan_async (NM_DEVICE_WIFI (self->device),
self->cancellable, NULL, NULL);
return G_SOURCE_CONTINUE;
}
static void
nm_device_wifi_refresh_ui (NetDeviceWifi *self)
{
g_autofree gchar *status = NULL;
if (device_is_hotspot (self)) {
nm_device_wifi_refresh_hotspot (self);
show_hotspot_ui (self);
disable_scan_timeout (self);
return;
}
if (self->scan_id == 0 &&
nm_client_wireless_get_enabled (self->client)) {
self->scan_id = g_timeout_add_seconds (PERIODIC_WIFI_SCAN_TIMEOUT,
request_scan, self);
request_scan (self);
}
/* keep this in sync with the signal handler setup in cc_network_panel_init */
wireless_enabled_toggled (self);
status = panel_device_status_to_localized_string (self->device, NULL);
adw_window_title_set_subtitle (self->wifi_headerbar_title, status);
/* update list of APs */
show_wifi_list (self);
gtk_widget_set_visible (GTK_WIDGET (self->saved_network_row), !cc_wifi_connection_list_is_empty (self->saved_networks_list));
}
static void
device_off_switch_changed_cb (NetDeviceWifi *self)
{
gboolean active;
if (self->updating_device)
return;
active = adw_switch_row_get_active (self->device_enable_row);
nm_client_dbus_set_property (self->client,
NM_DBUS_PATH,
NM_DBUS_INTERFACE,
"WirelessEnabled",
g_variant_new_boolean (active),
-1,
NULL, NULL, NULL);
if (!active)
disable_scan_timeout (self);
gtk_widget_set_sensitive (GTK_WIDGET (self->connect_hidden_row), active);
}
static void
connect_hidden (NetDeviceWifi *self)
{
GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self));
cc_network_panel_connect_to_hidden_network (GTK_WIDGET (native), self->client);
}
static void
connection_add_activate_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
NMActiveConnection *conn;
g_autoptr(GError) error = NULL;
conn = nm_client_add_and_activate_connection_finish (NM_CLIENT (source_object), res, &error);
if (!conn) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
nm_device_wifi_refresh_ui (user_data);
/* failed to activate */
g_warning ("Failed to add and activate connection '%d': %s",
error->code,
error->message);
}
return;
}
}
static void
connection_activate_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
if (!nm_client_activate_connection_finish (NM_CLIENT (source_object), res, &error)) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
nm_device_wifi_refresh_ui (user_data);
/* failed to activate */
g_debug ("Failed to add and activate connection '%d': %s",
error->code,
error->message);
}
return;
}
}
static gboolean
is_8021x (NMDevice *device,
const char *ap_object_path)
{
NM80211ApSecurityFlags wpa_flags, rsn_flags;
NMAccessPoint *ap;
ap = nm_device_wifi_get_access_point_by_path (NM_DEVICE_WIFI (device),
ap_object_path);
if (!ap)
return FALSE;
rsn_flags = nm_access_point_get_rsn_flags (ap);
if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
return TRUE;
wpa_flags = nm_access_point_get_wpa_flags (ap);
if (wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
return TRUE;
return FALSE;
}
static void
wireless_try_to_connect (NetDeviceWifi *self,
GBytes *ssid,
const gchar *ap_object_path)
{
const gchar *ssid_target;
if (self->updating_device)
return;
if (ap_object_path == NULL || ap_object_path[0] == 0)
return;
ssid_target = nm_utils_escape_ssid ((gpointer) g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
g_debug ("try to connect to WIFI network %s [%s]",
ssid_target, ap_object_path);
/* activate the connection */
if (!is_8021x (self->device, ap_object_path)) {
g_autoptr(GPermission) permission = NULL;
gboolean allowed_to_share = FALSE;
g_autoptr(NMConnection) partial = NULL;
permission = polkit_permission_new_sync ("org.freedesktop.NetworkManager.settings.modify.system",
NULL, NULL, NULL);
if (permission)
allowed_to_share = g_permission_get_allowed (permission);
if (!allowed_to_share) {
NMSettingConnection *s_con;
s_con = (NMSettingConnection *)nm_setting_connection_new ();
nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
partial = nm_simple_connection_new ();
nm_connection_add_setting (partial, NM_SETTING (s_con));
}
g_debug ("no existing connection found for %s, creating and activating one", ssid_target);
nm_client_add_and_activate_connection_async (self->client,
partial,
self->device,
ap_object_path,
self->cancellable,
connection_add_activate_cb,
self);
} else {
g_autoptr(GVariantBuilder) builder = NULL;
GVariant *parameters;
g_debug ("no existing connection found for %s, creating", ssid_target);
builder = g_variant_builder_new (G_VARIANT_TYPE ("av"));
g_variant_builder_add (builder, "v", g_variant_new_string ("connect-8021x-wifi"));
g_variant_builder_add (builder, "v", g_variant_new_string (nm_object_get_path (NM_OBJECT (self->device))));
g_variant_builder_add (builder, "v", g_variant_new_string (ap_object_path));
parameters = g_variant_new ("av", builder);
g_object_set (self->panel, "parameters", parameters, NULL);
}
}
static gboolean
is_hotspot_connection (NMConnection *connection)
{
NMSettingConnection *sc;
NMSettingWireless *sw;
NMSettingIPConfig *sip;
NMSetting *setting;
sc = nm_connection_get_setting_connection (connection);
if (g_strcmp0 (nm_setting_connection_get_connection_type (sc), "802-11-wireless") != 0) {
return FALSE;
}
sw = nm_connection_get_setting_wireless (connection);
if (g_strcmp0 (nm_setting_wireless_get_mode (sw), "adhoc") != 0 &&
g_strcmp0 (nm_setting_wireless_get_mode (sw), "ap") != 0) {
return FALSE;
}
setting = nm_connection_get_setting_by_name (connection, NM_SETTING_WIRELESS_SETTING_NAME);
if (!setting)
return FALSE;
sip = nm_connection_get_setting_ip4_config (connection);
if (g_strcmp0 (nm_setting_ip_config_get_method (sip), "shared") != 0) {
return FALSE;
}
return TRUE;
}
static void
show_hotspot_ui (NetDeviceWifi *self)
{
/* show hotspot tab */
gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->hotspot_box));
}
static void
activate_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
if (nm_client_activate_connection_finish (NM_CLIENT (source_object), res, &error) == NULL) {
g_warning ("Failed to add new connection: (%d) %s",
error->code,
error->message);
return;
}
/* show hotspot tab */
nm_device_wifi_refresh_ui (user_data);
}
static void
activate_new_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
NMActiveConnection *conn;
g_autoptr(GError) error = NULL;
conn = nm_client_add_and_activate_connection_finish (NM_CLIENT (source_object),
res, &error);
if (!conn) {
g_warning ("Failed to add new connection: (%d) %s",
error->code,
error->message);
return;
}
/* show hotspot tab */
nm_device_wifi_refresh_ui (user_data);
}
static NMConnection *
net_device_wifi_get_hotspot_connection (NetDeviceWifi *self)
{
GSList *connections, *l;
NMConnection *c = NULL;
connections = net_device_get_valid_connections (self->client, self->device);
for (l = connections; l; l = l->next) {
NMConnection *tmp = l->data;
if (is_hotspot_connection (tmp)) {
c = tmp;
break;
}
}
g_slist_free (connections);
return c;
}
static void
overwrite_ssid_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
NMRemoteConnection *connection;
NMConnection *c;
NetDeviceWifi *self;
connection = NM_REMOTE_CONNECTION (source_object);
if (!nm_remote_connection_commit_changes_finish (connection, res, &error)) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to save hotspot's settings to disk: %s",
error->message);
return;
}
self = user_data;
c = net_device_wifi_get_hotspot_connection (self);
g_debug ("activate existing hotspot connection\n");
nm_client_activate_connection_async (self->client,
c,
self->device,
NULL,
self->cancellable,
activate_cb,
self);
}
static void
on_wifi_hotspot_dialog_response_cb (GtkDialog *dialog,
gint response,
NetDeviceWifi *self)
{
if (response == GTK_RESPONSE_APPLY) {
NMConnection *connection;
connection = cc_wifi_hotspot_dialog_get_connection (self->hotspot_dialog);
if (NM_IS_REMOTE_CONNECTION (connection))
nm_remote_connection_commit_changes_async (NM_REMOTE_CONNECTION (connection),
TRUE,
self->cancellable,
overwrite_ssid_cb,
self);
else
nm_client_add_and_activate_connection_async (self->client,
connection,
self->device,
NULL,
self->cancellable,
activate_new_cb,
self);
}
gtk_widget_set_visible (GTK_WIDGET (self->hotspot_dialog), FALSE);
}
static void
start_hotspot (NetDeviceWifi *self)
{
GtkNative *native;
NMConnection *c;
g_autofree gchar *hostname = NULL;
g_autofree gchar *ssid = NULL;
native = gtk_widget_get_native (GTK_WIDGET (self));
if (!self->hotspot_dialog) {
self->hotspot_dialog = cc_wifi_hotspot_dialog_new (GTK_WINDOW (native));
g_object_ref_sink (self->hotspot_dialog);
}
cc_wifi_hotspot_dialog_set_device (self->hotspot_dialog, NM_DEVICE_WIFI (self->device));
hostname = cc_hostname_get_display_hostname (cc_hostname_get_default ());
ssid = pretty_hostname_to_ssid (hostname);
cc_wifi_hotspot_dialog_set_hostname (self->hotspot_dialog, ssid);
c = net_device_wifi_get_hotspot_connection (self);
if (c)
cc_wifi_hotspot_dialog_set_connection (self->hotspot_dialog, c);
g_signal_connect_after (self->hotspot_dialog, "response", G_CALLBACK (on_wifi_hotspot_dialog_response_cb), self);
gtk_window_present (GTK_WINDOW (self->hotspot_dialog));
}
static void
stop_shared_connection (NetDeviceWifi *self)
{
const GPtrArray *connections;
const GPtrArray *devices;
gint i;
NMActiveConnection *c;
gboolean found = FALSE;
connections = nm_client_get_active_connections (self->client);
for (i = 0; connections && i < connections->len; i++) {
c = (NMActiveConnection *)connections->pdata[i];
devices = nm_active_connection_get_devices (c);
if (devices && devices->pdata[0] == self->device) {
nm_client_deactivate_connection_async (self->client, c, NULL, NULL, NULL);
found = TRUE;
break;
}
}
if (!found) {
g_warning ("Could not stop hotspot connection as no connection attached to the device could be found.");
return;
}
nm_device_wifi_refresh_ui (self);
}
static void
show_wifi_list (NetDeviceWifi *self)
{
gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->listbox_box));
}
static void
net_device_wifi_dispose (GObject *object)
{
NetDeviceWifi *self = NET_DEVICE_WIFI (object);
if (self->hotspot_dialog) {
gtk_window_destroy (GTK_WINDOW (self->hotspot_dialog));
g_object_unref (self->hotspot_dialog);
self->hotspot_dialog = NULL;
}
G_OBJECT_CLASS (net_device_wifi_parent_class)->dispose (object);
}
static void
net_device_wifi_finalize (GObject *object)
{
NetDeviceWifi *self = NET_DEVICE_WIFI (object);
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
disable_scan_timeout (self);
g_clear_object (&self->client);
g_clear_object (&self->device);
g_clear_object (&self->saved_networks_list);
g_clear_pointer (&self->selected_ssid_title, g_free);
g_clear_pointer (&self->selected_connection_id, g_free);
g_clear_pointer (&self->selected_ap_id, g_free);
G_OBJECT_CLASS (net_device_wifi_parent_class)->finalize (object);
}
static void
net_device_wifi_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
NetDeviceWifi *self = NET_DEVICE_WIFI (object);
switch (prop_id) {
case PROP_SCANNING:
g_value_set_boolean (value, self->scanning);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
really_forgotten (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(NetDeviceWifi) self = NET_DEVICE_WIFI (user_data);
g_autoptr(GError) error = NULL;
cc_wifi_connection_list_thaw (self->saved_networks_list);
if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (source_object), res, &error))
g_warning ("failed to delete connection %s: %s",
nm_object_get_path (NM_OBJECT (source_object)),
error->message);
if (cc_wifi_connection_list_is_empty (self->saved_networks_list)) {
adw_dialog_close (self->saved_networks_dialog);
gtk_widget_set_visible (GTK_WIDGET (self->saved_network_row), FALSE);
}
}
static void
really_forget (gpointer data)
{
CcWifiConnectionRow *row = CC_WIFI_CONNECTION_ROW (data);
NMRemoteConnection *connection;
NetDeviceWifi *self;
self = NET_DEVICE_WIFI (g_object_get_data (G_OBJECT (row), "net"));
connection = NM_REMOTE_CONNECTION (cc_wifi_connection_row_get_connection (row));
nm_remote_connection_delete_async (connection, self->cancellable, really_forgotten, g_object_ref (self));
}
static void
undo_forget (gpointer data)
{
CcWifiConnectionRow *row = CC_WIFI_CONNECTION_ROW (data);
NetDeviceWifi *self;
self = NET_DEVICE_WIFI (g_object_get_data (G_OBJECT (row), "net"));
gtk_widget_set_visible (GTK_WIDGET (row), TRUE);
cc_wifi_connection_list_thaw (self->saved_networks_list);
}
static void
on_saved_networks_forget_dismissed (NetDeviceWifi *self)
{
self->saved_networks_undo_toast = NULL;
/* Since the free function is set to either undo or really forget,
this always does the right thing */
g_clear_pointer (&self->saved_networks_forgotten_rows, g_ptr_array_unref);
}
static void
on_saved_networks_forget_undo (NetDeviceWifi *self)
{
g_ptr_array_set_free_func (self->saved_networks_forgotten_rows, undo_forget);
}
static void
forget_selected (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list)
{
g_autofree gchar *message = NULL;
cc_wifi_connection_list_freeze (list);
gtk_widget_set_visible (GTK_WIDGET (row), FALSE);
g_object_set_data (G_OBJECT (row), "net", self);
/* Add row to the array of rows to be handled by the undo toast */
if (!self->saved_networks_forgotten_rows)
self->saved_networks_forgotten_rows = g_ptr_array_new_with_free_func (really_forget);
g_ptr_array_add (self->saved_networks_forgotten_rows, row);
/* If undo toast does not exist, create it, otherwise update title and extend timeout */
if (!self->saved_networks_undo_toast) {
message = g_strdup (_("Network deleted"));
self->saved_networks_undo_toast = adw_toast_new (message);
adw_toast_set_button_label (self->saved_networks_undo_toast, _("_Undo"));
g_signal_connect_swapped (self->saved_networks_undo_toast,
"button-clicked",
G_CALLBACK (on_saved_networks_forget_undo),
self);
g_signal_connect_swapped (self->saved_networks_undo_toast,
"dismissed",
G_CALLBACK (on_saved_networks_forget_dismissed),
self);
} else {
/* Translators: %d is the number of network connections deleted. */
message = g_strdup_printf (ngettext ("%d network deleted",
"%d networks deleted",
self->saved_networks_forgotten_rows->len),
self->saved_networks_forgotten_rows->len);
adw_toast_set_title (self->saved_networks_undo_toast, message);
g_object_ref (self->saved_networks_undo_toast);
}
adw_toast_overlay_add_toast (self->saved_networks_toast_overlay, self->saved_networks_undo_toast);
}
static gint
history_sort (gconstpointer a, gconstpointer b, gpointer data)
{
guint64 ta, tb;
NMConnection *ca, *cb;
NMSettingConnection *sc;
ca = cc_wifi_connection_row_get_connection (CC_WIFI_CONNECTION_ROW ((gpointer) a));
cb = cc_wifi_connection_row_get_connection (CC_WIFI_CONNECTION_ROW ((gpointer) b));
if (ca) {
sc = nm_connection_get_setting_connection (ca);
ta = nm_setting_connection_get_timestamp (sc);
} else {
ta = 0;
}
if (cb) {
sc = nm_connection_get_setting_connection (cb);
tb = nm_setting_connection_get_timestamp (sc);
} else {
tb = 0;
}
if (ta > tb) return -1;
if (tb > ta) return 1;
return 0;
}
static gint
ap_sort (gconstpointer a, gconstpointer b, gpointer data)
{
NetDeviceWifi *self = data;
CcWifiConnectionRow *a_row = CC_WIFI_CONNECTION_ROW ((gpointer) a);
CcWifiConnectionRow *b_row = CC_WIFI_CONNECTION_ROW ((gpointer) b);
NMActiveConnection *active_connection;
gboolean a_configured, b_configured;
NMAccessPoint *apa, *apb;
guint sa, sb;
/* Show the connected AP first */
active_connection = nm_device_get_active_connection (NM_DEVICE (self->device));
if (active_connection != NULL) {
NMConnection *connection = NM_CONNECTION (nm_active_connection_get_connection (active_connection));
if (connection == cc_wifi_connection_row_get_connection (a_row))
return -1;
else if (connection == cc_wifi_connection_row_get_connection (b_row))
return 1;
}
/* Show configured networks before non-configured */
a_configured = cc_wifi_connection_row_get_connection (a_row) != NULL;
b_configured = cc_wifi_connection_row_get_connection (b_row) != NULL;
if (a_configured != b_configured) {
if (a_configured) return -1;
if (b_configured) return 1;
}
/* Show higher strength networks above lower strength ones */
apa = cc_wifi_connection_row_best_access_point (a_row);
apb = cc_wifi_connection_row_best_access_point (b_row);
if (apa)
sa = nm_access_point_get_strength (apa);
else
sa = 0;
if (apb)
sb = nm_access_point_get_strength (apb);
else
sb = 0;
if (sa > sb) return -1;
if (sb > sa) return 1;
return 0;
}
static void
show_details_for_row (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list)
{
NMConnection *connection;
NMAccessPoint *ap;
NetConnectionEditor *editor;
connection = cc_wifi_connection_row_get_connection (row);
ap = cc_wifi_connection_row_best_access_point (row);
editor = net_connection_editor_new (connection, self->device, ap, self->client);
gtk_window_set_transient_for (GTK_WINDOW (editor), GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (row))));
gtk_window_present (GTK_WINDOW (editor));
}
static void
show_qr_code_for_row (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list)
{
NMConnection *connection;
GtkWidget *dialog;
connection = cc_wifi_connection_row_get_connection (row);
// getting a new "local" connection, since we don't want to populate the secrets of the original connection
connection = NM_CONNECTION (nm_client_get_connection_by_id (self->client, nm_connection_get_id (connection)));
dialog = cc_qr_code_dialog_new (connection);
adw_dialog_present (ADW_DIALOG (dialog), GTK_WIDGET (self));
}
static void
on_connect_hidden_network (NetDeviceWifi *self)
{
connect_hidden (self);
}
static void
on_wifi_hotspot_network (NetDeviceWifi *self)
{
start_hotspot (self);
}
static void
ap_activated (NetDeviceWifi *self, GtkListBoxRow *row)
{
CcWifiConnectionRow *c_row;
NMConnection *connection;
NMAccessPoint *ap;
/* The mockups want a row to connecto hidden networks; this could
* be handeled here. */
if (!CC_IS_WIFI_CONNECTION_ROW (row))
return;
c_row = CC_WIFI_CONNECTION_ROW (row);
connection = cc_wifi_connection_row_get_connection (c_row);
ap = cc_wifi_connection_row_best_access_point (c_row);
if (ap != NULL) {
if (connection != NULL) {
nm_client_activate_connection_async (self->client,
connection,
self->device, NULL, self->cancellable,
connection_activate_cb, self);
} else {
GBytes *ssid;
const gchar *object_path;
ssid = nm_access_point_get_ssid (ap);
object_path = nm_object_get_path (NM_OBJECT (ap));
wireless_try_to_connect (self, ssid, object_path);
}
}
}
static void
net_device_wifi_class_init (NetDeviceWifiClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = net_device_wifi_dispose;
object_class->finalize = net_device_wifi_finalize;
object_class->get_property = net_device_wifi_get_property;
g_object_class_install_property (object_class,
PROP_SCANNING,
g_param_spec_boolean ("scanning",
"Scanning",
"Whether the device is scanning for access points",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/network-wifi.ui");
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, wifi_headerbar_title);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, device_enable_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_network_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, connect_hidden_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, details_box);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_box);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_name_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_security_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, hotspot_password_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, listbox_box);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, stack);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_networks_dialog);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_networks_toast_overlay);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, saved_networks_box);
gtk_widget_class_bind_template_callback (widget_class, device_off_switch_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_connect_hidden_network);
gtk_widget_class_bind_template_callback (widget_class, on_wifi_hotspot_network);
}
static void
net_device_wifi_init (NetDeviceWifi *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->cancellable = g_cancellable_new ();
}
static void
nm_client_on_permission_change (NetDeviceWifi *self) {
NMClientPermissionResult perm;
NMDeviceWifiCapabilities caps;
if (nm_client_get_permissions_state (self->client) != NM_TERNARY_TRUE) {
/* permissions aren't ready yet */
return;
}
/* only enable the button if the user can create a hotspot */
perm = nm_client_get_permission_result (self->client, NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN);
caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (self->device));
if (perm != NM_CLIENT_PERMISSION_RESULT_YES &&
perm != NM_CLIENT_PERMISSION_RESULT_AUTH) {
gtk_widget_set_tooltip_text (GTK_WIDGET (self->hotspot_row), _("System policy prohibits use as a Hotspot"));
gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), FALSE);
} else if (!(caps & (NM_WIFI_DEVICE_CAP_AP | NM_WIFI_DEVICE_CAP_ADHOC))) {
gtk_widget_set_tooltip_text (GTK_WIDGET (self->hotspot_row), _("Wireless device does not support Hotspot mode"));
gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), FALSE);
} else {
gtk_widget_set_sensitive (GTK_WIDGET (self->hotspot_row), nm_client_wireless_get_enabled (self->client));
}
}
NetDeviceWifi *
net_device_wifi_new (CcPanel *panel, NMClient *client, NMDevice *device)
{
NetDeviceWifi *self;
GtkListBox *listbox;
CcWifiConnectionList *list;
self = g_object_new (net_device_wifi_get_type (), NULL);
self->panel = panel;
self->client = g_object_ref (client);
self->device = g_object_ref (device);
g_signal_connect_object (client, "notify::wireless-enabled",
G_CALLBACK (wireless_enabled_toggled), self, G_CONNECT_SWAPPED);
g_signal_connect_object (device, "state-changed", G_CALLBACK (nm_device_wifi_refresh_ui), self, G_CONNECT_SWAPPED);
/* Set up the main Visible Networks list */
list = cc_wifi_connection_list_new (client, NM_DEVICE_WIFI (device), TRUE, TRUE, FALSE, FALSE);
cc_wifi_connection_list_set_placeholder_text (list, _("Searching for networks…"));
gtk_box_append (self->listbox_box, GTK_WIDGET (list));
listbox = cc_wifi_connection_list_get_list_box (list);
gtk_list_box_set_sort_func (listbox, (GtkListBoxSortFunc)ap_sort, self, NULL);
g_signal_connect_object (listbox, "row-activated",
G_CALLBACK (ap_activated), self, G_CONNECT_SWAPPED);
g_signal_connect_object (list, "configure",
G_CALLBACK (show_details_for_row), self, G_CONNECT_SWAPPED);
g_signal_connect_object (list, "show-qr-code",
G_CALLBACK (show_qr_code_for_row), self, G_CONNECT_SWAPPED);
g_signal_connect_object (client, "notify",
G_CALLBACK(nm_client_on_permission_change), self, G_CONNECT_SWAPPED);
/* Set up the Saved Networks list */
list = cc_wifi_connection_list_new (self->client, NM_DEVICE_WIFI (device), FALSE, FALSE, FALSE, TRUE);
cc_wifi_connection_list_set_placeholder_text (list, _("No saved networks"));
self->saved_networks_list = g_object_ref_sink (list);
adw_preferences_group_add (self->saved_networks_box, GTK_WIDGET (list));
listbox = cc_wifi_connection_list_get_list_box (list);
gtk_list_box_set_sort_func (listbox, (GtkListBoxSortFunc)history_sort, NULL, NULL);
g_signal_connect_object (list, "configure",
G_CALLBACK (show_details_for_row), self, G_CONNECT_SWAPPED);
g_signal_connect_object (list, "forget",
G_CALLBACK (forget_selected), self, G_CONNECT_SWAPPED);
g_signal_connect_object (list, "show_qr_code",
G_CALLBACK (show_qr_code_for_row), self, G_CONNECT_SWAPPED);
nm_client_on_permission_change(self);
nm_device_wifi_refresh_ui (self);
return self;
}
NMDevice *
net_device_wifi_get_device (NetDeviceWifi *self)
{
g_return_val_if_fail (NET_IS_DEVICE_WIFI (self), NULL);
return self->device;
}
void
net_device_wifi_set_title (NetDeviceWifi *self, const gchar *title)
{
g_return_if_fail (NET_IS_DEVICE_WIFI (self));
adw_window_title_set_title (self->wifi_headerbar_title, title);
}
GtkWidget *
net_device_wifi_get_header_widget (NetDeviceWifi *self)
{
g_return_val_if_fail (NET_IS_DEVICE_WIFI (self), NULL);
return GTK_WIDGET (self->details_box);
}
GtkWidget *
net_device_wifi_get_title_widget (NetDeviceWifi *self)
{
g_return_val_if_fail (NET_IS_DEVICE_WIFI (self), NULL);
return GTK_WIDGET (self->wifi_headerbar_title);
}
void
net_device_wifi_turn_off_hotspot (NetDeviceWifi *self)
{
g_return_if_fail (NET_IS_DEVICE_WIFI (self));
stop_shared_connection (self);
}