about summary refs log tree commit diff
path: root/src/nmtui/nmt-wifi-qr-dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nmtui/nmt-wifi-qr-dialog.c')
-rw-r--r--src/nmtui/nmt-wifi-qr-dialog.c165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/nmtui/nmt-wifi-qr-dialog.c b/src/nmtui/nmt-wifi-qr-dialog.c
new file mode 100644
index 00000000..d8e7711f
--- /dev/null
+++ b/src/nmtui/nmt-wifi-qr-dialog.c
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-wifi-qr-dialog
+ * @short_description: A dialog that shares a Wi-Fi connection as a QR code
+ *
+ * Shows the "WIFI:" provisioning URI of a saved Wi-Fi connection as a
+ * scannable QR code, along with the SSID and password. If the password is a
+ * passphrase that cannot be read, it shows a warning instead of a QR code.
+ */
+
+#include "libnm-client-aux-extern/nm-default-client.h"
+
+#include "nmt-wifi-qr-dialog.h"
+
+#include "libnm-glib-aux/nm-secret-utils.h"
+#include "libnmc-base/nm-client-utils.h"
+#include "nmt-utils.h"
+
+static NMConnection *
+clone_with_secrets(NMConnection *connection)
+{
+    NMConnection              *clone   = nm_simple_connection_new_clone(connection);
+    gs_unref_variant GVariant *secrets = NULL;
+
+    if (!NM_IS_REMOTE_CONNECTION(connection))
+        return clone;
+
+    secrets = nmt_sync_get_secrets(NM_REMOTE_CONNECTION(connection),
+                                   NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+                                   NULL);
+    if (secrets)
+        nm_connection_update_secrets(clone,
+                                     NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+                                     secrets,
+                                     NULL);
+
+    return clone;
+}
+
+static gboolean
+qr_fits_screen(const char *qr)
+{
+    gs_strfreev char **lines = NULL;
+    int                screen_w;
+    int                screen_h;
+    int                qr_rows;
+    int                qr_cols;
+
+    newtGetScreenSize(&screen_w, &screen_h);
+
+    lines   = g_strsplit(qr, "\n", -1);
+    qr_rows = g_strv_length(lines);
+    qr_cols = lines[0] ? (int) g_utf8_strlen(lines[0], -1) : 0;
+
+    /* Reserve room for the form border, title, the SSID/password labels and
+     * the button row. */
+    return qr_cols + 4 <= screen_w && qr_rows + 8 <= screen_h;
+}
+
+/**
+ * nmt_wifi_qr_dialog_run:
+ * @connection: a Wi-Fi #NMConnection
+ *
+ * Fetches @connection's secrets and shows a modal dialog with a QR code that
+ * encodes its Wi-Fi credentials, or a warning if the password cannot be read.
+ * Returns when the user closes the dialog.
+ */
+void
+nmt_wifi_qr_dialog_run(NMConnection *connection)
+{
+    gs_unref_object NMConnection *clone = NULL;
+    gs_unref_object NmtNewtForm  *form  = NULL;
+    NMSettingWireless            *s_wireless;
+    NMSettingWirelessSecurity    *s_wsec;
+    NmtNewtGrid                  *grid;
+    NmtNewtWidget                *widget;
+    NmtNewtButtonBox             *bbox;
+    GBytes                       *ssid_bytes;
+    gs_free char                 *ssid     = NULL;
+    nm_auto_free_secret char     *uri      = NULL;
+    nm_auto_free_secret char     *qr       = NULL;
+    const char                   *key_mgmt = NULL;
+    const char                   *psk      = NULL;
+    gboolean                      no_password;
+    int                           row = 0;
+
+    g_return_if_fail(NM_IS_CONNECTION(connection));
+
+    if (!nm_connection_get_setting_wireless(connection))
+        return;
+
+    clone      = clone_with_secrets(connection);
+    s_wireless = nm_connection_get_setting_wireless(clone);
+
+    ssid_bytes = nm_setting_wireless_get_ssid(s_wireless);
+    if (ssid_bytes)
+        ssid =
+            nm_utils_ssid_to_utf8(g_bytes_get_data(ssid_bytes, NULL), g_bytes_get_size(ssid_bytes));
+
+    s_wsec = nm_connection_get_setting_wireless_security(clone);
+    if (s_wsec) {
+        key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec);
+        psk      = nm_setting_wireless_security_get_psk(s_wsec);
+    }
+
+    /* A QR code for a secured network without its password connects to nothing,
+     * so don't render one; show a warning instead. */
+    no_password = nmc_wifi_key_mgmt_uses_psk(key_mgmt) && (!psk || !psk[0]);
+
+    if (!no_password) {
+        uri = nmc_wifi_qr_uri_new(ssid, key_mgmt, psk, nm_setting_wireless_get_hidden(s_wireless));
+        qr  = nmc_wifi_qr_render_string(uri);
+    }
+
+    form = nmt_newt_form_new(_("Share Wi-Fi"));
+
+    widget = nmt_newt_grid_new();
+    nmt_newt_form_set_content(form, widget);
+    grid = NMT_NEWT_GRID(widget);
+
+    if (no_password) {
+        widget = nmt_newt_textbox_new(0, 50);
+        nmt_newt_textbox_set_text(
+            NMT_NEWT_TEXTBOX(widget),
+            _("Warning: cannot read the Wi-Fi password due to insufficient privileges."));
+    } else if (qr && qr_fits_screen(qr)) {
+        widget = nmt_newt_textbox_new(NMT_NEWT_TEXTBOX_SET_BACKGROUND, 0);
+        nmt_newt_textbox_set_text(NMT_NEWT_TEXTBOX(widget), qr);
+    } else {
+        widget = nmt_newt_textbox_new(0, 50);
+        nmt_newt_textbox_set_text(NMT_NEWT_TEXTBOX(widget),
+                                  qr ? _("The terminal is too small to display the QR code.")
+                                     : _("The Wi-Fi credentials could not be encoded."));
+    }
+    nmt_newt_grid_add(grid, widget, 0, row++);
+    nmt_newt_widget_set_padding(widget, 0, 0, 0, 1);
+
+    if (ssid) {
+        gs_free char *label = g_strdup_printf("%s: %s", _("SSID"), ssid);
+
+        widget = nmt_newt_label_new(label);
+        nmt_newt_grid_add(grid, widget, 0, row++);
+    }
+
+    if (psk) {
+        nm_auto_free_secret char *label = g_strdup_printf("%s: %s", _("Password"), psk);
+
+        widget = nmt_newt_label_new(label);
+        nmt_newt_grid_add(grid, widget, 0, row++);
+    }
+
+    widget = nmt_newt_button_box_new(NMT_NEWT_BUTTON_BOX_HORIZONTAL);
+    nmt_newt_grid_add(grid, widget, 0, row++);
+    nmt_newt_widget_set_padding(widget, 0, 1, 0, 0);
+    bbox = NMT_NEWT_BUTTON_BOX(widget);
+
+    widget = nmt_newt_button_box_add_end(bbox, _("Close"));
+    nmt_newt_widget_set_exit_on_activate(widget, TRUE);
+
+    nmt_newt_form_run_sync(form);
+}