diff options
Diffstat (limited to 'src/nmtui/nmt-wifi-qr-dialog.c')
| -rw-r--r-- | src/nmtui/nmt-wifi-qr-dialog.c | 165 |
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); +} |