diff options
Diffstat (limited to 'panels/network/cc-wifi-connection-row.c')
-rw-r--r-- | panels/network/cc-wifi-connection-row.c | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/panels/network/cc-wifi-connection-row.c b/panels/network/cc-wifi-connection-row.c new file mode 100644 index 0000000..608dda6 --- /dev/null +++ b/panels/network/cc-wifi-connection-row.c @@ -0,0 +1,678 @@ +/* + * Copyright © 2018 Red Hat Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <glib/gi18n.h> +#include <config.h> +#include "cc-wifi-connection-row.h" + +struct _CcWifiConnectionRow +{ + AdwActionRow parent_instance; + + gboolean constructed; + + gboolean checkable; + gboolean checked; + + NMDeviceWifi *device; + GPtrArray *aps; + NMConnection *connection; + gboolean known_connection; + + GtkLabel *active_label; + GtkCheckButton *checkbutton; + GtkSpinner *connecting_spinner; + GtkImage *encrypted_icon; + GtkButton *options_button; + GtkImage *strength_icon; +}; + +enum +{ + PROP_0, + PROP_CHECKABLE, + PROP_CHECKED, + PROP_DEVICE, + PROP_APS, + PROP_CONNECTION, + PROP_KNOWN_CONNECTION, + PROP_LAST +}; + +typedef enum +{ + NM_AP_SEC_UNKNOWN, + NM_AP_SEC_NONE, + NM_AP_SEC_WEP, + NM_AP_SEC_WPA, + NM_AP_SEC_WPA2, + NM_AP_SEC_SAE, + NM_AP_SEC_OWE, + NM_AP_SEC_OWE_TM +} NMAccessPointSecurity; + +G_DEFINE_TYPE (CcWifiConnectionRow, cc_wifi_connection_row, ADW_TYPE_ACTION_ROW) + +static GParamSpec *props[PROP_LAST]; + +static void configure_clicked_cb (CcWifiConnectionRow *self); + +static NMAccessPointSecurity +get_access_point_security (NMAccessPoint *ap) +{ + NM80211ApFlags flags; + NM80211ApSecurityFlags wpa_flags; + NM80211ApSecurityFlags rsn_flags; + NMAccessPointSecurity type; + + flags = nm_access_point_get_flags (ap); + wpa_flags = nm_access_point_get_wpa_flags (ap); + rsn_flags = nm_access_point_get_rsn_flags (ap); + + if (!(flags & NM_802_11_AP_FLAGS_PRIVACY) && + wpa_flags == NM_802_11_AP_SEC_NONE && + rsn_flags == NM_802_11_AP_SEC_NONE) + { + type = NM_AP_SEC_NONE; + } + else if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && + wpa_flags == NM_802_11_AP_SEC_NONE && + rsn_flags == NM_802_11_AP_SEC_NONE) + { + type = NM_AP_SEC_WEP; + } + else if (!(flags & NM_802_11_AP_FLAGS_PRIVACY) && + wpa_flags != NM_802_11_AP_SEC_NONE && + rsn_flags != NM_802_11_AP_SEC_NONE) + { + type = NM_AP_SEC_WPA; + } +#if NM_CHECK_VERSION(1,20,6) + else if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) + { + type = NM_AP_SEC_SAE; + } +#endif +#if NM_CHECK_VERSION(1,24,0) + else if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE) + { + type = NM_AP_SEC_OWE; + } +#endif +#if NM_CHECK_VERSION(1,26,0) + else if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM) + { + type = NM_AP_SEC_OWE_TM; + } +#endif + else + { + type = NM_AP_SEC_WPA2; + } + + return type; +} + +static NMAccessPointSecurity +get_connection_security (NMConnection *con) +{ + NMSettingWirelessSecurity *sws; + const gchar *key_mgmt; + + sws = nm_connection_get_setting_wireless_security (con); + g_debug ("getting security from %p", sws); + if (!sws) + return NM_AP_SEC_NONE; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (sws); + g_debug ("key management is %s", key_mgmt); + + if (!key_mgmt) + return NM_AP_SEC_NONE; + else if (g_str_equal (key_mgmt, "none")) + return NM_AP_SEC_WEP; + else if (g_str_equal (key_mgmt, "ieee8021x")) + return NM_AP_SEC_WEP; + else if (g_str_equal (key_mgmt, "wpa-eap")) + return NM_AP_SEC_WPA2; + else if (strncmp (key_mgmt, "wpa-", 4) == 0) + return NM_AP_SEC_WPA; + else if (g_str_equal (key_mgmt, "sae")) + return NM_AP_SEC_SAE; + else if (g_str_equal (key_mgmt, "owe")) + return NM_AP_SEC_OWE; + else + return NM_AP_SEC_UNKNOWN; +} + +static void +update_ui (CcWifiConnectionRow *self) +{ + GBytes *ssid; + g_autofree gchar *title = NULL; + NMActiveConnection *active_connection = NULL; + gboolean active; + gboolean connecting; + NMAccessPointSecurity security = NM_AP_SEC_UNKNOWN; + NMAccessPoint *best_ap; + guint8 strength = 0; + NMActiveConnectionState state; + + g_assert (self->device); + g_assert (self->connection || self->aps->len > 0); + + best_ap = cc_wifi_connection_row_best_access_point (self); + + if (self->connection) + { + active_connection = nm_device_get_active_connection (NM_DEVICE (self->device)); + if (active_connection && + NM_CONNECTION (nm_active_connection_get_connection (active_connection)) != self->connection) + active_connection = NULL; + } + + if (self->connection) + { + NMSettingWireless *sw; + const gchar *name = NULL; + g_autofree gchar *ssid_str = NULL; + gchar *ssid_pos; + + sw = nm_connection_get_setting_wireless (self->connection); + + ssid = nm_setting_wireless_get_ssid (sw); + ssid_str = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); + name = nm_connection_get_id (NM_CONNECTION (self->connection)); + + ssid_pos = strstr (name, ssid_str); + if (ssid_pos == name && strlen (name) == strlen (ssid_str)) + { + title = g_markup_escape_text (name, -1); + } + else if (ssid_pos) + { + g_autofree gchar *before = g_strndup (name, ssid_pos - name); + g_autofree gchar *after = g_strndup (ssid_pos + strlen (ssid_str), strlen(ssid_pos) - strlen(ssid_str)); + title = g_markup_printf_escaped ("<i>%s</i>%s<i>%s</i>", + before, ssid_str, after); + } + else + { + /* TRANSLATORS: This happens when the connection name does not contain the SSID. */ + title = g_markup_printf_escaped (C_("Wi-Fi Connection", "%s (SSID: %s)"), + name, ssid_str); + } + + adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self), title); + } + else + { + g_autofree char *title_escaped = NULL; + + ssid = nm_access_point_get_ssid (best_ap); + title = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); + title_escaped = g_markup_escape_text (title, -1); + adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self), title_escaped); + } + + if (active_connection) + { + state = nm_active_connection_get_state (active_connection); + + active = state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED; + connecting = state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING; + } + else + { + active = FALSE; + connecting = FALSE; + } + + if (self->connection) + security = get_connection_security (self->connection); + + if (best_ap != NULL) + { + security = get_access_point_security (best_ap); + strength = nm_access_point_get_strength (best_ap); + } + + gtk_widget_set_visible (GTK_WIDGET (self->connecting_spinner), connecting); + if (connecting) + { + gtk_spinner_start (self->connecting_spinner); + } + else + { + gtk_spinner_stop (self->connecting_spinner); + } + + gtk_widget_set_visible (GTK_WIDGET (self->active_label), active); + gtk_widget_set_visible (GTK_WIDGET (self->options_button), active || connecting || self->known_connection); + + if (security != NM_AP_SEC_UNKNOWN && security != NM_AP_SEC_NONE && security != NM_AP_SEC_OWE && security != NM_AP_SEC_OWE_TM) + { + const gchar *icon_name = "lock-small-symbolic"; + + gtk_widget_set_child_visible (GTK_WIDGET (self->encrypted_icon), TRUE); + if (security == NM_AP_SEC_WEP) + { + icon_name = "warning-small-symbolic"; + gtk_widget_set_tooltip_text (GTK_WIDGET (self->encrypted_icon), _("Insecure network (WEP)")); + } + else if (security == NM_AP_SEC_WPA) + { + gtk_widget_set_tooltip_text (GTK_WIDGET (self->encrypted_icon), _("Secure network (WPA)")); + } + else if (security == NM_AP_SEC_WPA2) + { + gtk_widget_set_tooltip_text (GTK_WIDGET (self->encrypted_icon), _("Secure network (WPA2)")); + } + else if (security == NM_AP_SEC_SAE) + { + gtk_widget_set_tooltip_text (GTK_WIDGET (self->encrypted_icon), _("Secure network (WPA3)")); + } + else + { + gtk_widget_set_tooltip_text (GTK_WIDGET (self->encrypted_icon), _("Secure network")); + } + + gtk_image_set_from_icon_name (self->encrypted_icon, icon_name); + } + else + { + gtk_widget_set_child_visible (GTK_WIDGET (self->encrypted_icon), FALSE); + } + + if (best_ap) + { + gchar *icon_name; + + if (strength < 20) + icon_name = "network-wireless-signal-none-symbolic"; + else if (strength < 40) + icon_name = "network-wireless-signal-weak-symbolic"; + else if (strength < 50) + icon_name = "network-wireless-signal-ok-symbolic"; + else if (strength < 80) + icon_name = "network-wireless-signal-good-symbolic"; + else + icon_name = "network-wireless-signal-excellent-symbolic"; + + g_object_set (self->strength_icon, "icon-name", icon_name, NULL); + gtk_widget_set_child_visible (GTK_WIDGET (self->strength_icon), TRUE); + } + else + { + gtk_widget_set_child_visible (GTK_WIDGET (self->strength_icon), FALSE); + } +} + +static void +cc_wifi_connection_row_constructed (GObject *object) +{ + CcWifiConnectionRow *self = CC_WIFI_CONNECTION_ROW (object); + + G_OBJECT_CLASS (cc_wifi_connection_row_parent_class)->constructed (object); + + /* Reparent the label into the checkbox */ + gtk_widget_set_visible (GTK_WIDGET (self->checkbutton), self->checkable); + + update_ui (CC_WIFI_CONNECTION_ROW (object)); +} + +static void +cc_wifi_connection_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcWifiConnectionRow *self = CC_WIFI_CONNECTION_ROW (object); + GPtrArray *ptr_array; + gint i; + + switch (prop_id) + { + case PROP_CHECKABLE: + g_value_set_boolean (value, self->checkable); + break; + + case PROP_CHECKED: + g_value_set_boolean (value, self->checked); + break; + + case PROP_DEVICE: + g_value_set_object (value, self->device); + break; + + case PROP_APS: + ptr_array = g_ptr_array_new_full (self->aps->len, NULL); + for (i = 0; i < self->aps->len; i++) + g_ptr_array_add (ptr_array, g_ptr_array_index (self->aps, i)); + + g_value_take_boxed (value, ptr_array); + break; + + case PROP_CONNECTION: + g_value_set_object (value, self->connection); + break; + + case PROP_KNOWN_CONNECTION: + g_value_set_boolean (value, self->known_connection); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_wifi_connection_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcWifiConnectionRow *self = CC_WIFI_CONNECTION_ROW (object); + GPtrArray *ptr_array; + gint i; + + switch (prop_id) + { + case PROP_CHECKABLE: + self->checkable = g_value_get_boolean (value); + break; + + case PROP_CHECKED: + self->checked = g_value_get_boolean (value); + break; + + case PROP_DEVICE: + self->device = g_value_dup_object (value); + break; + + case PROP_APS: + ptr_array = g_value_get_boxed (value); + g_ptr_array_set_size (self->aps, 0); + + if (ptr_array) + { + for (i = 0; i < ptr_array->len; i++) + g_ptr_array_add (self->aps, g_object_ref (g_ptr_array_index (ptr_array, i))); + } + if (self->constructed) + update_ui (self); + break; + + case PROP_CONNECTION: + self->connection = g_value_dup_object (value); + break; + + case PROP_KNOWN_CONNECTION: + self->known_connection = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_wifi_connection_row_finalize (GObject *object) +{ + CcWifiConnectionRow *self = CC_WIFI_CONNECTION_ROW (object); + + g_clear_object (&self->device); + g_clear_pointer (&self->aps, g_ptr_array_unref); + g_clear_object (&self->connection); + + G_OBJECT_CLASS (cc_wifi_connection_row_parent_class)->finalize (object); +} + + +void +cc_wifi_connection_row_class_init (CcWifiConnectionRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->constructed = cc_wifi_connection_row_constructed; + object_class->get_property = cc_wifi_connection_row_get_property; + object_class->set_property = cc_wifi_connection_row_set_property; + object_class->finalize = cc_wifi_connection_row_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/cc-wifi-connection-row.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcWifiConnectionRow, active_label); + gtk_widget_class_bind_template_child (widget_class, CcWifiConnectionRow, checkbutton); + gtk_widget_class_bind_template_child (widget_class, CcWifiConnectionRow, connecting_spinner); + gtk_widget_class_bind_template_child (widget_class, CcWifiConnectionRow, encrypted_icon); + gtk_widget_class_bind_template_child (widget_class, CcWifiConnectionRow, options_button); + gtk_widget_class_bind_template_child (widget_class, CcWifiConnectionRow, strength_icon); + + gtk_widget_class_bind_template_callback (widget_class, configure_clicked_cb); + + props[PROP_CHECKABLE] = g_param_spec_boolean ("checkable", "checkable", + "Whether to show a checkbox to select the row", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + props[PROP_CHECKED] = g_param_spec_boolean ("checked", "Checked", + "Whether the row is selected by checking it", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + props[PROP_DEVICE] = g_param_spec_object ("device", "WiFi Device", + "The WiFi Device for this connection/ap", + NM_TYPE_DEVICE_WIFI, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + props[PROP_APS] = g_param_spec_boxed ("aps", "Access Points", + "The access points for this connection (may be empty if a connection is given)", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + props[PROP_CONNECTION] = g_param_spec_object ("connection", "Connection", + "The NMConnection (may be NULL if there is an AP)", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + props[PROP_KNOWN_CONNECTION] = g_param_spec_boolean ("known-connection", "Known Connection", + "Whether this row is a known connection or not", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, + PROP_LAST, + props); + + g_signal_new ("configure", + CC_TYPE_WIFI_CONNECTION_ROW, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + +} + +static void +configure_clicked_cb (CcWifiConnectionRow *self) +{ + g_signal_emit_by_name (self, "configure"); +} + +void +cc_wifi_connection_row_init (CcWifiConnectionRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->aps = g_ptr_array_new_with_free_func (g_object_unref); + + g_object_bind_property (self, "checked", + self->checkbutton, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); +} + +CcWifiConnectionRow * +cc_wifi_connection_row_new (NMDeviceWifi *device, + NMConnection *connection, + GPtrArray *aps, + gboolean checkable, + gboolean known_connection) +{ + return g_object_new (CC_TYPE_WIFI_CONNECTION_ROW, + "device", device, + "connection", connection, + "aps", aps, + "checkable", checkable, + "known-connection", known_connection, + NULL); +} + +gboolean +cc_wifi_connection_row_get_checkable (CcWifiConnectionRow *self) +{ + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), FALSE); + + return self->checkable; +} + +gboolean +cc_wifi_connection_row_get_checked (CcWifiConnectionRow *self) +{ + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), FALSE); + + return self->checked; +} + +NMDeviceWifi* +cc_wifi_connection_row_get_device (CcWifiConnectionRow *self) +{ + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), NULL); + + return self->device; +} + +const GPtrArray* +cc_wifi_connection_row_get_access_points (CcWifiConnectionRow *self) +{ + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), NULL); + + return self->aps; +} + +NMConnection* +cc_wifi_connection_row_get_connection (CcWifiConnectionRow *self) +{ + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), NULL); + + return self->connection; +} + +void +cc_wifi_connection_row_set_checked (CcWifiConnectionRow *self, + gboolean value) +{ + g_return_if_fail (CC_WIFI_CONNECTION_ROW (self)); + + self->checked = value; + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CHECKED]); +} + +NMAccessPoint* +cc_wifi_connection_row_best_access_point (CcWifiConnectionRow *self) +{ + NMAccessPoint *best_ap = NULL; + NMAccessPoint *active_ap = NULL; + guint8 strength = 0; + gint i; + + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), NULL); + + if (self->aps->len == 0) + return NULL; + + active_ap = nm_device_wifi_get_active_access_point (self->device); + + for (i = 0; i < self->aps->len; i++) + { + NMAccessPoint *cur; + guint8 cur_strength; + + cur = g_ptr_array_index (self->aps, i); + + /* Prefer the active AP in all cases */ + if (cur == active_ap) + return cur; + + cur_strength = nm_access_point_get_strength (cur); + /* Use if we don't have an AP, this is the current AP, or it is better */ + if (!best_ap || cur_strength > strength) + { + best_ap = cur; + strength = cur_strength; + } + } + + return best_ap; +} + +void +cc_wifi_connection_row_add_access_point (CcWifiConnectionRow *self, + NMAccessPoint *ap) +{ + g_return_if_fail (CC_WIFI_CONNECTION_ROW (self)); + + g_ptr_array_add (self->aps, g_object_ref (ap)); + update_ui (self); +} + +gboolean +cc_wifi_connection_row_remove_access_point (CcWifiConnectionRow *self, + NMAccessPoint *ap) +{ + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), FALSE); + + if (!g_ptr_array_remove (self->aps, g_object_ref (ap))) + return FALSE; + + /* Object might be invalid; this is alright if it is deleted right away */ + if (self->aps->len > 0 || self->connection) + { + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_APS]); + update_ui (self); + } + + return self->aps->len == 0; +} + +gboolean +cc_wifi_connection_row_has_access_point (CcWifiConnectionRow *self, + NMAccessPoint *ap) +{ + g_return_val_if_fail (CC_WIFI_CONNECTION_ROW (self), FALSE); + + return g_ptr_array_find (self->aps, ap, NULL); +} + +void +cc_wifi_connection_row_update (CcWifiConnectionRow *self) +{ + update_ui (self); + + gtk_list_box_row_changed (GTK_LIST_BOX_ROW (self)); + +} + |