gnome-control-center/panels/network/net-device-wifi.c
Hendrik Müller 48497080b3 wifi: Fix bug in Known Wi-Fi Networks dialog
The "Forget" button would only update it's sensitivity after the first
select and deselect, when selecting and deselecting rows in the
"Known Wi-Fi Networks" dialog.
When selecting the first row, it would go from disabled to enabled.
Then deselecting that row would cause the button to go from enabled to
disabled.
Selecting any rows after that would no longer update the sensitivity and
make the dialog essentially useless.

The issue was, that the signals "add" and "remove" where being
expected to be emitted when the connection list updates its rows.
However, neither CcWifiConnectionList nor GtkListBox emit these signals.
The fix was, to emit these two signals at the appropriate locations.
The signals have also been renamed to "add-row" and "remove-row" to
make their purpose more clear.

Closes https://gitlab.gnome.org/GNOME/gnome-control-center/-/issues/1824
2022-08-03 11:05:16 +00:00

1303 lines
48 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 "hostname-helper.h"
#include "network-dialogs.h"
#include "panel-common.h"
#include "cc-list-row.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);
struct _NetDeviceWifi
{
AdwBin parent;
GtkBox *center_box;
GtkListBoxRow *connect_hidden_row;
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;
GtkListBoxRow *start_hotspot_row;
GtkLabel *status_label;
GtkLabel *title_label;
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");
if (self->monitor_scanning_id > 0) {
g_source_remove (self->monitor_scanning_id);
self->monitor_scanning_id = 0;
}
if (self->scan_id > 0) {
g_source_remove (self->scan_id);
self->scan_id = 0;
}
}
static void
wireless_enabled_toggled (NetDeviceWifi *self)
{
gboolean enabled;
enabled = nm_client_wireless_get_enabled (self->client);
self->updating_device = TRUE;
gtk_switch_set_active (self->device_off_switch, enabled);
if (!enabled)
disable_scan_timeout (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
* "wpa-none" = WPAv1 Ad-Hoc mode (not supported in NM >= 0.9.4)
* "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-none") == 0 ||
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);
gtk_label_set_label (self->status_label, status);
/* update list of APs */
show_wifi_list (self);
}
static void
device_off_switch_changed_cb (NetDeviceWifi *self)
{
gboolean active;
if (self->updating_device)
return;
active = gtk_switch_get_active (self->device_off_switch);
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);
gtk_popover_popdown (self->header_button_popover);
}
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 gchar *
get_hostname (void)
{
g_autoptr(GDBusConnection) bus = NULL;
g_autoptr(GVariant) res = NULL;
g_autoptr(GVariant) inner = NULL;
g_autoptr(GError) error = NULL;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (bus == NULL) {
g_warning ("Failed to get system bus connection: %s", error->message);
return NULL;
}
res = g_dbus_connection_call_sync (bus,
"org.freedesktop.hostname1",
"/org/freedesktop/hostname1",
"org.freedesktop.DBus.Properties",
"Get",
g_variant_new ("(ss)",
"org.freedesktop.hostname1",
"PrettyHostname"),
(GVariantType*)"(v)",
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (res == NULL) {
g_warning ("Getting pretty hostname failed: %s", error->message);
return NULL;
}
g_variant_get (res, "(v)", &inner);
return g_variant_dup_string (inner, 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_respnse_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_hide (GTK_WIDGET (self->hotspot_dialog));
}
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));
cc_wifi_hotspot_dialog_set_device (self->hotspot_dialog, NM_DEVICE_WIFI (self->device));
hostname = get_hostname ();
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_respnse_cb), self);
gtk_window_present (GTK_WINDOW (self->hotspot_dialog));
gtk_popover_popdown (self->header_button_popover);
}
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_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_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(CcWifiConnectionList) list = user_data;
g_autoptr(GError) error = NULL;
cc_wifi_connection_list_thaw (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);
}
static void
really_forget (GtkDialog *dialog, gint response, gpointer data)
{
GtkWidget *forget = data;
CcWifiConnectionRow *row;
GList *rows;
GList *r;
NMRemoteConnection *connection;
NetDeviceWifi *self;
gtk_window_destroy (GTK_WINDOW (dialog));
if (response != GTK_RESPONSE_OK)
return;
self = NET_DEVICE_WIFI (g_object_get_data (G_OBJECT (forget), "net"));
rows = g_object_steal_data (G_OBJECT (forget), "rows");
for (r = rows; r; r = r->next) {
row = r->data;
connection = NM_REMOTE_CONNECTION (cc_wifi_connection_row_get_connection (row));
nm_remote_connection_delete_async (connection, self->cancellable, really_forgotten, g_object_ref (data));
gtk_widget_hide (GTK_WIDGET (row));
}
g_list_free (rows);
}
static void
forget_selected (GtkButton *forget, CcWifiConnectionList *list)
{
GtkNative *native;
GtkWidget *dialog;
native = gtk_widget_get_native (GTK_WIDGET (forget));
dialog = gtk_message_dialog_new (GTK_WINDOW (native),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_OTHER,
GTK_BUTTONS_NONE,
NULL);
gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog),
_("Network details for the selected networks, including passwords and any custom configuration will be lost."));
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Forget"), GTK_RESPONSE_OK,
NULL);
g_signal_connect (dialog, "response",
G_CALLBACK (really_forget), list);
gtk_window_present (GTK_WINDOW (dialog));
}
static void
check_toggled (CcWifiConnectionRow *row, GParamSpec *pspec, CcWifiConnectionList *list)
{
GtkWidget *forget;
gboolean active;
GList *rows;
active = cc_wifi_connection_row_get_checked (row);
rows = g_object_steal_data (G_OBJECT (list), "rows");
if (active) {
cc_wifi_connection_list_freeze (list);
rows = g_list_prepend (rows, row);
} else {
cc_wifi_connection_list_thaw (list);
rows = g_list_remove (rows, row);
}
g_object_set_data_full (G_OBJECT (list), "rows", rows, (GDestroyNotify)g_list_free);
forget = g_object_get_data (G_OBJECT (list), "forget");
gtk_widget_set_sensitive (forget, rows != NULL);
}
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
on_connection_list_row_added_cb (NetDeviceWifi *self,
CcWifiConnectionRow *row,
CcWifiConnectionList *list)
{
g_signal_connect (row, "notify::checked",
G_CALLBACK (check_toggled), list);
}
static void
on_connection_list_row_removed_cb (NetDeviceWifi *self,
CcWifiConnectionRow *row,
CcWifiConnectionList *list)
{
GList *rows = g_object_steal_data (G_OBJECT (list), "rows");
GList *item;
GtkWidget *forget;
item = g_list_find (rows, row);
if (item)
{
rows = g_list_remove (rows, row);
cc_wifi_connection_list_thaw (list);
}
g_object_set_data_full (G_OBJECT (list), "rows", rows, (GDestroyNotify)g_list_free);
forget = g_object_get_data (G_OBJECT (list), "forget");
gtk_widget_set_sensitive (forget, rows != NULL);
}
static void
on_connection_list_row_activated_cb (NetDeviceWifi *self,
CcWifiConnectionRow *row,
CcWifiConnectionList *list)
{
cc_wifi_connection_row_set_checked (row, !cc_wifi_connection_row_get_checked (row));
}
static void
show_history (NetDeviceWifi *self)
{
GtkNative *native;
GtkWidget *dialog;
GtkWidget *page;
GtkWidget *list_group;
GtkWidget *forget_group;
GtkListBox *listbox;
GtkWidget *list;
GtkWidget *forget;
GtkWidget *child;
dialog = adw_preferences_window_new ();
adw_preferences_window_set_search_enabled (ADW_PREFERENCES_WINDOW (dialog), FALSE);
adw_preferences_window_set_can_navigate_back (ADW_PREFERENCES_WINDOW (dialog), FALSE);
native = gtk_widget_get_native (GTK_WIDGET (self));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (native));
gtk_window_set_title (GTK_WINDOW (dialog), _("Known Wi-Fi Networks"));
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 400);
page = adw_preferences_page_new ();
adw_preferences_window_add (ADW_PREFERENCES_WINDOW (dialog), ADW_PREFERENCES_PAGE (page));
list_group = adw_preferences_group_new ();
adw_preferences_page_add (ADW_PREFERENCES_PAGE (page), ADW_PREFERENCES_GROUP (list_group));
list = GTK_WIDGET (cc_wifi_connection_list_new (self->client,
NM_DEVICE_WIFI (self->device),
FALSE, FALSE, TRUE));
listbox = cc_wifi_connection_list_get_list_box (CC_WIFI_CONNECTION_LIST (list));
gtk_list_box_set_selection_mode (listbox, GTK_SELECTION_NONE);
gtk_list_box_set_sort_func (listbox, (GtkListBoxSortFunc)history_sort, NULL, NULL);
adw_preferences_group_add (ADW_PREFERENCES_GROUP (list_group), list);
/* translators: This is the label for the "Forget wireless network" functionality */
forget = gtk_button_new_with_mnemonic (C_("Wi-Fi Network", "_Forget"));
gtk_widget_set_sensitive (forget, FALSE);
gtk_style_context_add_class (gtk_widget_get_style_context (forget), "destructive-action");
g_signal_connect (forget, "clicked",
G_CALLBACK (forget_selected), list);
forget_group = adw_preferences_group_new ();
adw_preferences_group_add (ADW_PREFERENCES_GROUP (forget_group), forget);
adw_preferences_page_add (ADW_PREFERENCES_PAGE (page), ADW_PREFERENCES_GROUP (forget_group));
g_object_set_data (G_OBJECT (list), "forget", forget);
g_object_set_data (G_OBJECT (list), "net", self);
g_signal_connect_object (list, "add-row",
G_CALLBACK (on_connection_list_row_added_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (list, "remove-row",
G_CALLBACK (on_connection_list_row_removed_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (listbox, "row-activated",
G_CALLBACK (on_connection_list_row_activated_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (list, "configure",
G_CALLBACK (show_details_for_row),
self,
G_CONNECT_SWAPPED);
for (child = gtk_widget_get_first_child (GTK_WIDGET (listbox));
child;
child = gtk_widget_get_next_sibling (child))
{
on_connection_list_row_added_cb (self,
CC_WIFI_CONNECTION_ROW (child),
CC_WIFI_CONNECTION_LIST (list));
}
gtk_window_present (GTK_WINDOW (dialog));
gtk_popover_popdown (self->header_button_popover);
}
static void
on_popover_row_activated_cb (GtkListBox *list,
GtkListBoxRow *row,
gpointer user_data)
{
NetDeviceWifi *self = user_data;
if (row == self->connect_hidden_row)
connect_hidden (self);
else if (row == self->start_hotspot_row)
start_hotspot (self);
else
show_history (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->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, center_box);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, connect_hidden_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, device_off_switch);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, header_box);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, header_button_popover);
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, start_hotspot_row);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, status_label);
gtk_widget_class_bind_template_child (widget_class, NetDeviceWifi, title_label);
gtk_widget_class_bind_template_callback (widget_class, device_off_switch_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, on_popover_row_activated_cb);
}
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->start_hotspot_row), _("System policy prohibits use as a Hotspot"));
gtk_widget_set_sensitive (GTK_WIDGET (self->start_hotspot_row), FALSE);
} else if (!(caps & (NM_WIFI_DEVICE_CAP_AP | NM_WIFI_DEVICE_CAP_ADHOC))) {
gtk_widget_set_tooltip_text (GTK_WIDGET (self->start_hotspot_row), _("Wireless device does not support Hotspot mode"));
gtk_widget_set_sensitive (GTK_WIDGET (self->start_hotspot_row), FALSE);
} else
gtk_widget_set_sensitive (GTK_WIDGET (self->start_hotspot_row), TRUE);
}
NetDeviceWifi *
net_device_wifi_new (CcPanel *panel, NMClient *client, NMDevice *device)
{
NetDeviceWifi *self;
GtkListBox *listbox;
GtkWidget *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);
list = GTK_WIDGET (cc_wifi_connection_list_new (client, NM_DEVICE_WIFI (device), TRUE, TRUE, FALSE));
gtk_box_append (self->listbox_box, list);
listbox = cc_wifi_connection_list_get_list_box (CC_WIFI_CONNECTION_LIST (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 (client, "notify",
G_CALLBACK(nm_client_on_permission_change), 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));
gtk_label_set_label (self->title_label, 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->header_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->center_box);
}
void
net_device_wifi_turn_off_hotspot (NetDeviceWifi *self)
{
g_return_if_fail (NET_IS_DEVICE_WIFI (self));
stop_shared_connection (self);
}