/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2011-2012 Richard Hughes * * 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 #include #include #include #include #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 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); }