summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--panels/network/cc-qr-code-dialog.c191
-rw-r--r--panels/network/cc-qr-code-dialog.h33
-rw-r--r--panels/network/cc-qr-code-dialog.ui66
-rw-r--r--panels/network/cc-qr-code.c170
-rw-r--r--panels/network/cc-qr-code.h12
-rw-r--r--panels/network/cc-wifi-connection-list.c18
-rw-r--r--panels/network/cc-wifi-connection-row.c17
-rw-r--r--panels/network/cc-wifi-connection-row.ui15
-rw-r--r--panels/network/cc-wifi-panel.c188
-rw-r--r--panels/network/meson.build2
-rw-r--r--panels/network/net-device-wifi.c25
-rw-r--r--panels/network/network-wifi.ui2
-rw-r--r--panels/network/network.gresource.xml2
-rw-r--r--panels/network/qr-code-symbolic.svg2
-rw-r--r--tests/network/test-wifi-text.c1
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)