diff options
-rw-r--r-- | panels/network/cc-qr-code-dialog.c | 191 | ||||
-rw-r--r-- | panels/network/cc-qr-code-dialog.h | 33 | ||||
-rw-r--r-- | panels/network/cc-qr-code-dialog.ui | 66 | ||||
-rw-r--r-- | panels/network/cc-qr-code.c | 170 | ||||
-rw-r--r-- | panels/network/cc-qr-code.h | 12 | ||||
-rw-r--r-- | panels/network/cc-wifi-connection-list.c | 18 | ||||
-rw-r--r-- | panels/network/cc-wifi-connection-row.c | 17 | ||||
-rw-r--r-- | panels/network/cc-wifi-connection-row.ui | 15 | ||||
-rw-r--r-- | panels/network/cc-wifi-panel.c | 188 | ||||
-rw-r--r-- | panels/network/meson.build | 2 | ||||
-rw-r--r-- | panels/network/net-device-wifi.c | 25 | ||||
-rw-r--r-- | panels/network/network-wifi.ui | 2 | ||||
-rw-r--r-- | panels/network/network.gresource.xml | 2 | ||||
-rw-r--r-- | panels/network/qr-code-symbolic.svg | 2 | ||||
-rw-r--r-- | tests/network/test-wifi-text.c | 1 |
15 files changed, 553 insertions, 191 deletions
diff --git a/panels/network/cc-qr-code-dialog.c b/panels/network/cc-qr-code-dialog.c new file mode 100644 index 000000000..be1b8d9bc --- /dev/null +++ b/panels/network/cc-qr-code-dialog.c @@ -0,0 +1,191 @@ +/* + * 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-qr-code-dialog.h" +#include "cc-qr-code.h" + +#define QR_IMAGE_SIZE 200 + +struct _CcQrCodeDialog +{ + GtkWindow parent_instance; + NMConnection *connection; + GtkWidget *qr_image; + GtkWidget *qr_subtitle; +}; + +enum +{ + PROP_0, + PROP_CONNECTION, + PROP_LAST +}; + +G_DEFINE_TYPE (CcQrCodeDialog, cc_qr_code_dialog, GTK_TYPE_WINDOW) + +static GParamSpec *props[PROP_LAST]; + +static void +cc_qr_code_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcQrCodeDialog *self = CC_QR_CODE_DIALOG (object); + + switch (prop_id) + { + case PROP_CONNECTION: + g_value_set_object (value, self->connection); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_qr_code_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcQrCodeDialog *self = CC_QR_CODE_DIALOG (object); + + switch (prop_id) + { + case PROP_CONNECTION: + g_assert (self->connection == NULL); + self->connection = g_value_dup_object (value); + g_assert (self->connection != NULL); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_qr_code_dialog_constructed (GObject *object) +{ + g_autoptr (GVariant) variant = NULL; + g_autoptr (GError) error = NULL; + g_autofree gchar *qr_connection_string = NULL; + g_autofree gchar *subtitle_text = NULL; + g_autofree gchar *ssid_text = NULL; + NMSettingWireless *setting; + CcQrCodeDialog *self; + CcQrCode *qr_code; + GBytes *ssid; + + self = CC_QR_CODE_DIALOG (object); + + G_OBJECT_CLASS (cc_qr_code_dialog_parent_class)->constructed (object); + + variant = nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (self->connection), + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL, + &error); + if (!variant) + { + g_warning ("Couldn't get secrets: %s", error->message); + return; + } + + if (!nm_connection_update_secrets (self->connection, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + variant, + &error)) + { + g_warning ("Couldn't update secrets: %s", error->message); + return; + } + + setting = nm_connection_get_setting_wireless (self->connection); + ssid = nm_setting_wireless_get_ssid (setting); + ssid_text = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); + + /* + * translators: This is the format string for the text shown underneath the Wi-Fi QR code. + * The string placeholder will be replaced by the Wi-Fi networks SSID. + */ + subtitle_text = g_markup_printf_escaped (_("Scan the QR code to connect to the network <b>%s</b>."), + ssid_text); + gtk_label_set_markup (GTK_LABEL (self->qr_subtitle), subtitle_text); + + qr_code = cc_qr_code_new (); + qr_connection_string = get_qr_string_for_connection (self->connection); + if (cc_qr_code_set_text (qr_code, qr_connection_string)) + { + gint scale = gtk_widget_get_scale_factor (self->qr_image); + GdkPaintable *paintable = cc_qr_code_get_paintable (qr_code, QR_IMAGE_SIZE * scale); + gtk_picture_set_paintable (GTK_PICTURE (self->qr_image), paintable); + } + else + { + // TODO what should happen in this case? + } +} + +static void +cc_qr_code_dialog_finalize (GObject *object) +{ + CcQrCodeDialog *self = CC_QR_CODE_DIALOG (object); + + g_clear_object (&self->connection); + + G_OBJECT_CLASS (cc_qr_code_dialog_parent_class)->finalize (object); +} + +void +cc_qr_code_dialog_class_init (CcQrCodeDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->constructed = cc_qr_code_dialog_constructed; + object_class->get_property = cc_qr_code_dialog_get_property; + object_class->set_property = cc_qr_code_dialog_set_property; + object_class->finalize = cc_qr_code_dialog_finalize; + + props[PROP_CONNECTION] = g_param_spec_object ("connection", "Connection", + "The NMConnection for which to show a QR code", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_LAST, props); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/cc-qr-code-dialog.ui"); + gtk_widget_class_bind_template_child (widget_class, CcQrCodeDialog, qr_image); + gtk_widget_class_bind_template_child (widget_class, CcQrCodeDialog, qr_subtitle); +} + +void +cc_qr_code_dialog_init (CcQrCodeDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GtkWidget * +cc_qr_code_dialog_new (NMConnection *connection) +{ + return g_object_new (CC_TYPE_QR_CODE_DIALOG, + "connection", connection, + NULL); +} diff --git a/panels/network/cc-qr-code-dialog.h b/panels/network/cc-qr-code-dialog.h new file mode 100644 index 000000000..2090ae865 --- /dev/null +++ b/panels/network/cc-qr-code-dialog.h @@ -0,0 +1,33 @@ +/* + * Copyright © 2022 + * + * 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/>. + */ + +#pragma once + +#include <adwaita.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +typedef struct _CcQrCodeDialog CcQrCodeDialog; + +#define CC_TYPE_QR_CODE_DIALOG (cc_qr_code_dialog_get_type ()) +G_DECLARE_FINAL_TYPE (CcQrCodeDialog, cc_qr_code_dialog, CC, QR_CODE_DIALOG, GtkWindow) + +GtkWidget *cc_qr_code_dialog_new (NMConnection *connection); + +G_END_DECLS + diff --git a/panels/network/cc-qr-code-dialog.ui b/panels/network/cc-qr-code-dialog.ui new file mode 100644 index 000000000..1ee94abd6 --- /dev/null +++ b/panels/network/cc-qr-code-dialog.ui @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="CcQrCodeDialog" parent="GtkWindow"> + <property name="default-width">350</property> + <property name="default-height">500</property> + <property name="height-request">350</property> + <property name="modal">True</property> + <property name="destroy-with-parent">True</property> + <property name="title" translatable="yes">Share Network</property> + + <child type="titlebar"> + <object class="GtkHeaderBar"> + <property name="show-title-buttons">True</property> + <style> + <class name="flat" /> + </style> + </object> + </child> + + <child> + <object class="AdwClamp"> + + <child> + <object class="GtkBox"> + <property name="spacing">24</property> + <property name="orientation">vertical</property> + <property name="margin-top">32</property> + <property name="margin-bottom">32</property> + <property name="margin-start">32</property> + <property name="margin-end">32</property> + + <child> + <object class="GtkPicture" id="qr_image"> + <property name="halign">center</property> + <property name="can-shrink">True</property> + <property name="alternative-text" translatable="yes">QR Code</property> + <style> + <class name="frame"/> + <class name="qr-image"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel" id="qr_title"> + <property name="label" translatable="yes">Scan to Connect</property> + <property name="css-classes">title-1</property> + </object> + </child> + + <child> + <object class="GtkLabel" id="qr_subtitle"> + <property name="wrap">True</property> + <property name="halign">center</property> + <property name="justify">center</property> + <property name="natural-wrap-mode">none</property> + </object> + </child> + + </object> + </child> + + </object> + </child> + </template> +</interface> diff --git a/panels/network/cc-qr-code.c b/panels/network/cc-qr-code.c index 8bb8a14cf..5b694a009 100644 --- a/panels/network/cc-qr-code.c +++ b/panels/network/cc-qr-code.c @@ -26,7 +26,7 @@ #define G_LOG_DOMAIN "cc-qr-code" #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #endif #include "cc-qr-code.h" @@ -45,20 +45,19 @@ struct _CcQrCode { - GObject parent_instance; + GObject parent_instance; - gchar *text; - GdkTexture *texture; - gint size; + gchar *text; + GdkTexture *texture; + gint size; }; G_DEFINE_TYPE (CcQrCode, cc_qr_code, G_TYPE_OBJECT) - static void cc_qr_code_finalize (GObject *object) { - CcQrCode *self = (CcQrCode *)object; + CcQrCode *self = (CcQrCode *) object; g_clear_object (&self->texture); g_clear_pointer (&self->text, g_free); @@ -104,16 +103,17 @@ cc_qr_code_set_text (CcQrCode *self, static void cc_fill_pixel (GByteArray *array, - guint8 value, - int pixel_size) + guint8 value, + int pixel_size) { guint i; - for (i = 0; i < pixel_size; i++) { - g_byte_array_append (array, &value, 1); /* R */ - g_byte_array_append (array, &value, 1); /* G */ - g_byte_array_append (array, &value, 1); /* B */ - } + for (i = 0; i < pixel_size; i++) + { + g_byte_array_append (array, &value, 1); /* R */ + g_byte_array_append (array, &value, 1); /* G */ + g_byte_array_append (array, &value, 1); /* B */ + } } GdkPaintable * @@ -122,7 +122,7 @@ cc_qr_code_get_paintable (CcQrCode *self, { uint8_t qr_code[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; uint8_t temp_buf[qrcodegen_BUFFER_LEN_FOR_VERSION (qrcodegen_VERSION_MAX)]; - g_autoptr(GBytes) bytes = NULL; + g_autoptr (GBytes) bytes = NULL; GByteArray *qr_matrix; gint pixel_size, qr_size, total_size; gint column, row, i; @@ -140,7 +140,7 @@ cc_qr_code_get_paintable (CcQrCode *self, if (self->texture && self->size == size) return GDK_PAINTABLE (self->texture); - self->size = size; + self->size = size; success = qrcodegen_encodeText (self->text, temp_buf, @@ -154,7 +154,7 @@ cc_qr_code_get_paintable (CcQrCode *self, if (!success) return NULL; - qr_size = qrcodegen_getSize(qr_code); + qr_size = qrcodegen_getSize (qr_code); pixel_size = MAX (1, size / (qr_size)); total_size = qr_size * pixel_size; qr_matrix = g_byte_array_sized_new (total_size * total_size * pixel_size * BYTES_PER_R8G8B8); @@ -184,3 +184,139 @@ cc_qr_code_get_paintable (CcQrCode *self, return GDK_PAINTABLE (self->texture); } + +static gchar * +escape_string (const gchar *str, + gboolean quote) +{ + GString *string; + const char *next; + + if (!str) + return NULL; + + string = g_string_new (""); + if (quote) + g_string_append_c (string, '"'); + + while ((next = strpbrk (str, "\\;,:\""))) + { + g_string_append_len (string, str, next - str); + g_string_append_c (string, '\\'); + g_string_append_c (string, *next); + str = next + 1; + } + + g_string_append (string, str); + if (quote) + g_string_append_c (string, '"'); + + return g_string_free (string, FALSE); +} + +static const gchar * +get_connection_security_type (NMConnection *c) +{ + NMSettingWirelessSecurity *setting; + const char *key_mgmt; + + g_return_val_if_fail (c, "nopass"); + + setting = nm_connection_get_setting_wireless_security (c); + + if (!setting) + return "nopass"; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting); + + /* No IEEE 802.1x */ + if (g_strcmp0 (key_mgmt, "none") == 0) + return "WEP"; + + if (g_strcmp0 (key_mgmt, "wpa-none") == 0 || + g_strcmp0 (key_mgmt, "wpa-psk") == 0) + return "WPA"; + + return "nopass"; +} + +static gchar * +get_wifi_password (NMConnection *c) +{ + NMSettingWirelessSecurity *setting; + const gchar *sec_type, *password; + gint wep_index; + + sec_type = get_connection_security_type (c); + setting = nm_connection_get_setting_wireless_security (c); + + if (g_str_equal (sec_type, "nopass")) + return NULL; + + if (g_str_equal (sec_type, "WEP")) + { + wep_index = nm_setting_wireless_security_get_wep_tx_keyidx (setting); + password = nm_setting_wireless_security_get_wep_key (setting, wep_index); + } + else + { + password = nm_setting_wireless_security_get_psk (setting); + } + + return escape_string (password, FALSE); +} + +/* Generate a string representing the connection + * An example generated text: + * WIFI:S:ssid;T:WPA;P:my-valid-pass;H:true; + * Where, + * S = ssid, T = security, P = password, H = hidden (Optional) + * + * See https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11 + */ +gchar * +get_qr_string_for_connection (NMConnection *c) +{ + NMSettingWireless *setting; + g_autofree char *ssid_text = NULL; + g_autofree char *escaped_ssid = NULL; + g_autofree char *password_str = NULL; + GString *string; + GBytes *ssid; + gboolean hidden; + + setting = nm_connection_get_setting_wireless (c); + ssid = nm_setting_wireless_get_ssid (setting); + + if (!ssid) + return NULL; + + string = g_string_new ("WIFI:S:"); + + /* SSID */ + ssid_text = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + escaped_ssid = escape_string (ssid_text, FALSE); + g_string_append (string, escaped_ssid); + g_string_append_c (string, ';'); + + /* Security type */ + g_string_append (string, "T:"); + g_string_append (string, get_connection_security_type (c)); + g_string_append_c (string, ';'); + + /* Password */ + g_string_append (string, "P:"); + password_str = get_wifi_password (c); + if (password_str) + g_string_append (string, password_str); + g_string_append_c (string, ';'); + + /* WiFi Hidden */ + hidden = nm_setting_wireless_get_hidden (setting); + if (hidden) + g_string_append (string, "H:true"); + g_string_append_c (string, ';'); + + return g_string_free (string, FALSE); +} diff --git a/panels/network/cc-qr-code.h b/panels/network/cc-qr-code.h index 844dd2ab0..01da8ab75 100644 --- a/panels/network/cc-qr-code.h +++ b/panels/network/cc-qr-code.h @@ -24,6 +24,7 @@ #pragma once +#include <NetworkManager.h> #include <gtk/gtk.h> #include <glib-object.h> #include <cairo.h> @@ -34,10 +35,11 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (CcQrCode, cc_qr_code, CC, QR_CODE, GObject) -CcQrCode *cc_qr_code_new (void); -gboolean cc_qr_code_set_text (CcQrCode *self, - const gchar *text); -GdkPaintable *cc_qr_code_get_paintable (CcQrCode *self, - gint size); +CcQrCode *cc_qr_code_new (void); +gboolean cc_qr_code_set_text (CcQrCode *self, + const gchar *text); +GdkPaintable *cc_qr_code_get_paintable (CcQrCode *self, + gint size); +gchar *get_qr_string_for_connection (NMConnection *c); G_END_DECLS diff --git a/panels/network/cc-wifi-connection-list.c b/panels/network/cc-wifi-connection-list.c index 6a11d2034..72df35f16 100644 --- a/panels/network/cc-wifi-connection-list.c +++ b/panels/network/cc-wifi-connection-list.c @@ -59,7 +59,9 @@ static void on_device_ap_removed_cb (CcWifiConnectionList *self, NMAccessPoint *ap, NMDeviceWifi *device); static void on_row_configured_cb (CcWifiConnectionList *self, - CcWifiConnectionRow *row); + CcWifiConnectionRow *row); +static void on_row_show_qr_code_cb (CcWifiConnectionList *self, + CcWifiConnectionRow *row); G_DEFINE_TYPE (CcWifiConnectionList, cc_wifi_connection_list, ADW_TYPE_BIN) @@ -134,6 +136,7 @@ cc_wifi_connection_list_row_add (CcWifiConnectionList *self, gtk_list_box_append (self->listbox, GTK_WIDGET (res)); g_signal_connect_object (res, "configure", G_CALLBACK (on_row_configured_cb), self, G_CONNECT_SWAPPED); + g_signal_connect_object (res, "show-qr-code", G_CALLBACK (on_row_show_qr_code_cb), self, G_CONNECT_SWAPPED); g_signal_emit_by_name (self, "add-row", res); @@ -172,7 +175,7 @@ clear_widget (CcWifiConnectionList *self) g_ptr_array_index (self->connections_row, i) = NULL; g_signal_emit_by_name (self, "remove-row", row); gtk_list_box_remove (self->listbox, GTK_WIDGET (row)); - } + } /* Reset the internal state */ g_ptr_array_set_size (self->connections, 0); @@ -252,6 +255,12 @@ on_row_configured_cb (CcWifiConnectionList *self, CcWifiConnectionRow *row) } static void +on_row_show_qr_code_cb (CcWifiConnectionList *self, CcWifiConnectionRow *row) +{ + g_signal_emit_by_name (self, "show_qr_code", row); +} + +static void on_access_point_property_changed (CcWifiConnectionList *self, GParamSpec *pspec, NMAccessPoint *ap) @@ -715,6 +724,11 @@ cc_wifi_connection_list_class_init (CcWifiConnectionListClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW); + g_signal_new ("show_qr_code", + CC_TYPE_WIFI_CONNECTION_LIST, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW); g_signal_new ("add-row", CC_TYPE_WIFI_CONNECTION_LIST, G_SIGNAL_RUN_LAST, diff --git a/panels/network/cc-wifi-connection-row.c b/panels/network/cc-wifi-connection-row.c index 608dda6a8..257a74eff 100644 --- a/panels/network/cc-wifi-connection-row.c +++ b/panels/network/cc-wifi-connection-row.c @@ -38,6 +38,7 @@ struct _CcWifiConnectionRow GtkSpinner *connecting_spinner; GtkImage *encrypted_icon; GtkButton *options_button; + GtkButton *qr_code_button; GtkImage *strength_icon; }; @@ -70,6 +71,7 @@ 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 void qr_code_clicked_cb (CcWifiConnectionRow *self); static NMAccessPointSecurity get_access_point_security (NMAccessPoint *ap) @@ -263,6 +265,7 @@ update_ui (CcWifiConnectionRow *self) gtk_widget_set_visible (GTK_WIDGET (self->active_label), active); gtk_widget_set_visible (GTK_WIDGET (self->options_button), active || connecting || self->known_connection); + gtk_widget_set_visible (GTK_WIDGET (self->qr_code_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) { @@ -461,9 +464,11 @@ cc_wifi_connection_row_class_init (CcWifiConnectionRowClass *klass) 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, qr_code_button); gtk_widget_class_bind_template_child (widget_class, CcWifiConnectionRow, strength_icon); gtk_widget_class_bind_template_callback (widget_class, configure_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, qr_code_clicked_cb); props[PROP_CHECKABLE] = g_param_spec_boolean ("checkable", "checkable", "Whether to show a checkbox to select the row", @@ -504,7 +509,11 @@ cc_wifi_connection_row_class_init (CcWifiConnectionRowClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - + g_signal_new ("show-qr-code", + CC_TYPE_WIFI_CONNECTION_ROW, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); } static void @@ -513,6 +522,12 @@ configure_clicked_cb (CcWifiConnectionRow *self) g_signal_emit_by_name (self, "configure"); } +static void +qr_code_clicked_cb (CcWifiConnectionRow *self) +{ + g_signal_emit_by_name (self, "show-qr-code"); +} + void cc_wifi_connection_row_init (CcWifiConnectionRow *self) { diff --git a/panels/network/cc-wifi-connection-row.ui b/panels/network/cc-wifi-connection-row.ui index d02dfe6bb..5ace5d9e6 100644 --- a/panels/network/cc-wifi-connection-row.ui +++ b/panels/network/cc-wifi-connection-row.ui @@ -52,6 +52,21 @@ </object> </child> <child> + <object class="GtkButton" id="qr_code_button"> + <property name="name">qr_code_button</property> + <property name="icon_name">qr-code-symbolic</property> + <property name="halign">center</property> + <property name="valign">center</property> + <signal name="clicked" handler="qr_code_clicked_cb" object="CcWifiConnectionRow" swapped="yes"/> + <accessibility> + <property name="label" translatable="yes">Show QR-Code</property> + </accessibility> + <style> + <class name="flat"/> + </style> + </object> + </child> + <child> <object class="GtkButton" id="options_button"> <property name="name">options_button</property> <property name="icon_name">emblem-system-symbolic</property> diff --git a/panels/network/cc-wifi-panel.c b/panels/network/cc-wifi-panel.c index dc3ce14b1..8e0b17b05 100644 --- a/panels/network/cc-wifi-panel.c +++ b/panels/network/cc-wifi-panel.c @@ -150,163 +150,6 @@ cc_wifi_panel_static_init_func (void) /* Auxiliary methods */ -static gchar * -escape_string (const gchar *str, - gboolean quote) -{ - GString *string; - const char *next; - - if (!str) - return NULL; - - string = g_string_new (""); - if (quote) - g_string_append_c (string, '"'); - - while ((next = strpbrk (str, "\\;,:\""))) - { - g_string_append_len (string, str, next - str); - g_string_append_c (string, '\\'); - g_string_append_c (string, *next); - str = next + 1; - } - - g_string_append (string, str); - if (quote) - g_string_append_c (string, '"'); - - return g_string_free (string, FALSE); -} - -static const gchar * -get_connection_security_type (NMConnection *c) -{ - NMSettingWirelessSecurity *setting; - const char *key_mgmt; - - g_return_val_if_fail (c, "nopass"); - - setting = nm_connection_get_setting_wireless_security (c); - - if (!setting) - return "nopass"; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting); - - /* No IEEE 802.1x */ - if (g_strcmp0 (key_mgmt, "none") == 0) - return "WEP"; - - if (g_strcmp0 (key_mgmt, "wpa-none") == 0 || - g_strcmp0 (key_mgmt, "wpa-psk") == 0) - return "WPA"; - - return "nopass"; -} - -static gchar * -get_wifi_password (NMConnection *c) -{ - NMSettingWirelessSecurity *setting; - g_autoptr(GVariant) secrets = NULL; - g_autoptr(GError) error = NULL; - const gchar *sec_type, *password; - gint wep_index; - - g_assert (NM_IS_REMOTE_CONNECTION (c)); - - sec_type = get_connection_security_type (c); - setting = nm_connection_get_setting_wireless_security (c); - - if (g_str_equal (sec_type, "nopass")) - return NULL; - - secrets = nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (c), - NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, - NULL, &error); - if (!error) - nm_connection_update_secrets (c, - NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, - secrets, &error); - if (error) - { - g_warning ("Error: %s", error->message); - return NULL; - } - - if (g_str_equal (sec_type, "WEP")) - { - wep_index = nm_setting_wireless_security_get_wep_tx_keyidx (setting); - password = nm_setting_wireless_security_get_wep_key (setting, wep_index); - } - else - { - password = nm_setting_wireless_security_get_psk (setting); - } - - return escape_string (password, FALSE); -} - -/* Generate a string representing the hotspot @connection - * An example generated text: - * WIFI:S:hotspot;T:WPA;P:my-valid-pass;H:true; - * Where, - * S = ssid, T = security, P = password, H = hidden (Optional) - * - * See https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11 - */ -static gchar * -get_qr_string_for_hotspot (NMClient *nm_client, - NMConnection *c) -{ - NMSettingWireless *setting; - g_autofree char *ssid_text = NULL; - g_autofree char *escaped_ssid = NULL; - g_autofree char *password_str = NULL; - GString *string; - GBytes *ssid; - gboolean hidden; - - g_assert (NM_IS_CLIENT (nm_client)); - g_assert (NM_IS_REMOTE_CONNECTION (c)); - - setting = nm_connection_get_setting_wireless (c); - ssid = nm_setting_wireless_get_ssid (setting); - - if (!ssid) - return NULL; - - string = g_string_new ("WIFI:S:"); - - /* SSID */ - ssid_text = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), - g_bytes_get_size (ssid)); - escaped_ssid = escape_string (ssid_text, FALSE); - g_string_append (string, escaped_ssid); - g_string_append_c (string, ';'); - - /* Security type */ - g_string_append (string, "T:"); - g_string_append (string, get_connection_security_type (c)); - g_string_append_c (string, ';'); - - /* Password */ - g_string_append (string, "P:"); - password_str = get_wifi_password (c); - if (password_str) - g_string_append (string, password_str); - g_string_append_c (string, ';'); - - /* WiFi Hidden */ - hidden = nm_setting_wireless_get_hidden (setting); - if (hidden) - g_string_append (string, "H:true"); - g_string_append_c (string, ';'); - - return g_string_free (string, FALSE); -} - static NMConnection * wifi_device_get_hotspot (CcWifiPanel *self, NMDevice *device) @@ -348,19 +191,34 @@ wifi_panel_update_qr_image_cb (CcWifiPanel *self) if (hotspot) { g_autofree gchar *str = NULL; + g_autoptr (GVariant) secrets = NULL; + g_autoptr (GError) error = NULL; if (!self->qr_code) self->qr_code = cc_qr_code_new (); - str = get_qr_string_for_hotspot (self->client, hotspot); - if (cc_qr_code_set_text (self->qr_code, str)) + secrets = nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (hotspot), + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL, &error); + if (!error) { + nm_connection_update_secrets (hotspot, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, &error); + + str = get_qr_string_for_connection (hotspot); + if (cc_qr_code_set_text (self->qr_code, str)) + { + GdkPaintable *paintable; + gint scale; + + scale = gtk_widget_get_scale_factor (GTK_WIDGET (self->wifi_qr_image)); + paintable = cc_qr_code_get_paintable (self->qr_code, QR_IMAGE_SIZE * scale); + gtk_picture_set_paintable (self->wifi_qr_image, paintable); + } + } + else { - GdkPaintable *paintable; - gint scale; - - scale = gtk_widget_get_scale_factor (GTK_WIDGET (self->wifi_qr_image)); - paintable = cc_qr_code_get_paintable (self->qr_code, QR_IMAGE_SIZE * scale); - gtk_picture_set_paintable (self->wifi_qr_image, paintable); + g_warning ("Error: %s", error->message); } } diff --git a/panels/network/meson.build b/panels/network/meson.build index c9ec1cbb7..9229f7646 100644 --- a/panels/network/meson.build +++ b/panels/network/meson.build @@ -30,6 +30,7 @@ endforeach sources = files( 'cc-qr-code.c', + 'cc-qr-code-dialog.c', 'cc-network-panel.c', 'cc-net-proxy-page.c', 'cc-wifi-connection-row.c', @@ -49,6 +50,7 @@ sources = files( resource_data = files( 'cc-network-panel.ui', 'cc-net-proxy-page.c', + 'cc-qr-code-dialog.ui', 'cc-wifi-connection-row.ui', 'cc-wifi-panel.ui', 'cc-wifi-hotspot-dialog.ui', diff --git a/panels/network/net-device-wifi.c b/panels/network/net-device-wifi.c index 8cd998d9c..418c82ee9 100644 --- a/panels/network/net-device-wifi.c +++ b/panels/network/net-device-wifi.c @@ -34,6 +34,7 @@ #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" @@ -990,6 +991,24 @@ show_details_for_row (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConne } static void +show_qr_code_for_row (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list) +{ + NMConnection *connection; + GtkNative *native; + 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); + native = gtk_widget_get_native (GTK_WIDGET (self)); + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (native)); + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void on_connection_list_row_added_cb (NetDeviceWifi *self, CcWifiConnectionRow *row, CcWifiConnectionList *list) @@ -1093,6 +1112,10 @@ show_history (NetDeviceWifi *self) 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); for (child = gtk_widget_get_first_child (GTK_WIDGET (listbox)); child; @@ -1255,6 +1278,8 @@ net_device_wifi_new (CcPanel *panel, NMClient *client, NMDevice *device) 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); diff --git a/panels/network/network-wifi.ui b/panels/network/network-wifi.ui index 96cd73b72..aba532c56 100644 --- a/panels/network/network-wifi.ui +++ b/panels/network/network-wifi.ui @@ -19,7 +19,7 @@ </object> </child> - <!-- Wi-Fi Hotspot deails --> + <!-- Wi-Fi Hotspot details --> <child> <object class="GtkStackPage"> <property name="child"> diff --git a/panels/network/network.gresource.xml b/panels/network/network.gresource.xml index ec109c42b..931b0172f 100644 --- a/panels/network/network.gresource.xml +++ b/panels/network/network.gresource.xml @@ -6,6 +6,7 @@ <file preprocess="xml-stripblanks">cc-net-proxy-page.ui</file> <file preprocess="xml-stripblanks">cc-wifi-connection-row.ui</file> <file preprocess="xml-stripblanks">cc-wifi-hotspot-dialog.ui</file> + <file preprocess="xml-stripblanks">cc-qr-code-dialog.ui</file> <file preprocess="xml-stripblanks">network-bluetooth.ui</file> <file preprocess="xml-stripblanks">network-vpn.ui</file> <file preprocess="xml-stripblanks">network-wifi.ui</file> @@ -20,5 +21,6 @@ <!-- Wi-Fi panel icons --> <file preprocess="xml-stripblanks">lock-small-symbolic.svg</file> <file preprocess="xml-stripblanks">warning-small-symbolic.svg</file> + <file preprocess="xml-stripblanks">qr-code-symbolic.svg</file> </gresource> </gresources> diff --git a/panels/network/qr-code-symbolic.svg b/panels/network/qr-code-symbolic.svg new file mode 100644 index 000000000..80963008c --- /dev/null +++ b/panels/network/qr-code-symbolic.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 1 1 v 5 h 5 v -5 z m 7 0 v 1 h 1 v -1 z m 0 1 h -1 v 1 h 1 z m 0 1 v 1 h -1 v 2 h 2 v -3 z m 2 -2 v 5 h 5 v -5 z m -7 2 h 1 v 1 h -1 z m 9 0 h 1 v 1 h -1 z m -11 4 v 1 h 1 v -1 z m 1 1 v 1 h 1 v -1 z m 1 0 h 1 v 1 h 2 v -2 h -3 z m 4 -1 v 4 h 1 v -1 h 2 v -3 z m 3 3 v 1 h 1 v -1 z m 1 0 h 1 v -1 h -1 z m 1 -1 h 1 v 1 h 1 v -2 h 1 v -1 h -3 z m 1 1 h -1 v 1 h 1 z m -3 1 h -1 v 1 h 1 z m 0 1 v 1 h 1 v -1 z m -1 0 h -2 v 3 h 1 v -1 h 1 z m 0 2 v 1 h 1 v -1 z m -1 -6 h 1 v 1 h -1 z m -7 2 v 5 h 5 v -5 z m 13 1 v 1 h -2 v 3 h 1 v -1 h 1 v -1 h 1 v -2 z m 0 3 v 1 h 1 v -1 z m -11 -2 h 1 v 1 h -1 z m 0 0" fill="#222222"/></svg> diff --git a/tests/network/test-wifi-text.c b/tests/network/test-wifi-text.c index 39c79cbcf..ac4de34db 100644 --- a/tests/network/test-wifi-text.c +++ b/tests/network/test-wifi-text.c @@ -32,6 +32,7 @@ /* Including ‘.c’ file to test static functions */ #include "cc-wifi-panel.c" +#include "cc-qr-code.c" static void test_escape_qr_string (void) |