Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package otpclient for openSUSE:Factory checked in at 2026-05-20 15:26:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/otpclient (Old) and /work/SRC/openSUSE:Factory/.otpclient.new.1966 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "otpclient" Wed May 20 15:26:24 2026 rev:47 rq:1354193 version:5.0.3 Changes: -------- --- /work/SRC/openSUSE:Factory/otpclient/otpclient.changes 2026-05-13 21:00:11.559254814 +0200 +++ /work/SRC/openSUSE:Factory/.otpclient.new.1966/otpclient.changes 2026-05-20 15:27:13.892839818 +0200 @@ -1,0 +2,23 @@ +Fri May 15 12:57:29 UTC 2026 - Paolo Stivanin <[email protected]> + +- Update to 5.0.3: + * Startup crash gdk_display_manager_get() was called before + gtk_init() when use-dark-theme is enabled on environments without + a portal-reported color scheme (#440). The dark-theme preference + was being applied before AdwApplication's startup chain ran, so + adw_style_manager_get_default() reached into an uninitialized GDK + display and aborted. Now applied after chaining up to the parent + startup(). + +------------------------------------------------------------------- +Thu May 14 09:52:08 UTC 2026 - Paolo Stivanin <[email protected]> + +- Update to 5.0.2: + * NEW: Add → Scan QR from Clipboard (#438). Reads a GdkTexture from + the focused display's clipboard, downloads it to RGBA, converts + to grayscale, and decodes via zbar. + * FIX: Scanning a QR that does not encode an otpauth:// URI now + surfaces a toast instead of silently doing nothing. Applies to + file and webcam scans too, not just the new clipboard path. + +------------------------------------------------------------------- Old: ---- v5.0.1.tar.gz v5.0.1.tar.gz.asc New: ---- v5.0.3.tar.gz v5.0.3.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ otpclient.spec ++++++ --- /var/tmp/diff_new_pack.6sXzRv/_old 2026-05-20 15:27:14.412861239 +0200 +++ /var/tmp/diff_new_pack.6sXzRv/_new 2026-05-20 15:27:14.412861239 +0200 @@ -18,7 +18,7 @@ %define uclname OTPClient Name: otpclient -Version: 5.0.1 +Version: 5.0.3 Release: 0 Summary: Simple GTK+ client for managing TOTP and HOTP License: GPL-3.0-or-later ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.6sXzRv/_old 2026-05-20 15:27:14.452862887 +0200 +++ /var/tmp/diff_new_pack.6sXzRv/_new 2026-05-20 15:27:14.456863052 +0200 @@ -1,5 +1,5 @@ -mtime: 1778662114 -commit: bbdd286af85cf7f1df67285cb9144e579c64b7798d63f49ae645111ea1e99e87 +mtime: 1778849880 +commit: 39467a0dac9cb4485ba8a91e93de7c06c0b8799cc0255b16b4dcca753a1061a1 url: https://src.opensuse.org/GNOME/otpclient revision: factory ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-05-15 14:58:00.000000000 +0200 @@ -0,0 +1,4 @@ +*.obscpio +*.osc +_build.* +.pbuild ++++++ v5.0.1.tar.gz -> v5.0.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/CMakeLists.txt new/OTPClient-5.0.3/CMakeLists.txt --- old/OTPClient-5.0.1/CMakeLists.txt 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/CMakeLists.txt 2026-05-15 14:48:27.000000000 +0200 @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(OTPClient VERSION "5.0.1" LANGUAGES "C") +project(OTPClient VERSION "5.0.3" LANGUAGES "C") include(GNUInstallDirs) configure_file("src/common/version.h.in" "version.h") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/README.md new/OTPClient-5.0.3/README.md --- old/OTPClient-5.0.1/README.md 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/README.md 2026-05-15 14:48:27.000000000 +0200 @@ -24,6 +24,7 @@ - Token grouping: assign tokens to groups (e.g. "Work", "Personal") via right-click, the edit dialog, or when adding a token; groups are preserved during Aegis, AuthenticatorPro, and 2FAS import/export +- Add tokens by scanning a QR code from an image file, the webcam, or the clipboard, or by entering the secret manually - QR code display for any token (re-pairing or sharing across devices) - Idle and screensaver auto-lock with configurable timeout - Configurable clipboard wipe; clipboard is also cleared on lock and app exit diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/data/com.github.paolostivanin.OTPClient.appdata.xml new/OTPClient-5.0.3/data/com.github.paolostivanin.OTPClient.appdata.xml --- old/OTPClient-5.0.1/data/com.github.paolostivanin.OTPClient.appdata.xml 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/data/com.github.paolostivanin.OTPClient.appdata.xml 2026-05-15 14:48:27.000000000 +0200 @@ -90,6 +90,23 @@ </content_rating> <releases> + <release version="5.0.3" date="2026-05-15"> + <description> + <p>Small bug-fix release in the 5.0.x line. Fixes a startup crash on systems where the XDG portal cannot auto-report a color scheme (typically XFCE) and the user has the dark-theme GSetting enabled. The dark-theme preference was being applied before AdwApplication's startup chain ran, so adw_style_manager_get_default() reached into an uninitialized GDK display and aborted.</p> + <ul> + <li>FIX: startup crash "gdk_display_manager_get() was called before gtk_init()" when use-dark-theme is enabled on environments without a portal-reported color scheme (#440)</li> + </ul> + </description> + </release> + <release version="5.0.2" date="2026-05-14"> + <description> + <p>Restores the "Scan QR from Clipboard" workflow that existed in 4.5.0 and was dropped in the GTK4 rewrite. Reads an image directly from the clipboard via the GTK4 clipboard API and feeds it into the existing zbar pipeline, sidestepping the file-roundtrip needed for browser-copied or screenshot-captured QR codes.</p> + <ul> + <li>NEW: Add → Scan QR from Clipboard (#438)</li> + <li>FIX: scanning a QR that does not encode an otpauth:// URI now surfaces a toast instead of silently doing nothing (applies to file and webcam scans too)</li> + </ul> + </description> + </release> <release version="5.0.1" date="2026-05-12"> <description> <p>Bug-fix release for 5.0.0. Importing a QR code into a database that already contained tokens left the previously-imported entries duplicated in the array; the duplicates also caused delete and group actions to appear to target the wrong row. Existing 5.0.0 databases that were corrupted by repeated QR imports can be cleaned up by removing the duplicate entries.</p> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/src/gui/otpclient-application.c new/OTPClient-5.0.3/src/gui/otpclient-application.c --- old/OTPClient-5.0.1/src/gui/otpclient-application.c 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/src/gui/otpclient-application.c 2026-05-15 14:48:27.000000000 +0200 @@ -785,13 +785,15 @@ self->otp_reveal_timeout = 10; } - /* Apply dark theme preference */ + G_APPLICATION_CLASS (otpclient_application_parent_class)->startup (application); + + /* Apply dark theme preference. Must run after the parent startup chain so + * that GTK/GDK has been initialized; otherwise adw_style_manager_ensure() + * calls gdk_display_manager_get() before gtk_init() and aborts (issue #440). */ if (self->use_dark_theme) adw_style_manager_set_color_scheme (adw_style_manager_get_default (), ADW_COLOR_SCHEME_FORCE_DARK); - G_APPLICATION_CLASS (otpclient_application_parent_class)->startup (application); - /* Clear the clipboard and exit cleanly on signal-driven termination so * we don't leave a copied OTP behind when the user kills the process. */ g_unix_signal_add (SIGINT, otpclient_application_signal_quit, application); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/src/gui/otpclient-window.c new/OTPClient-5.0.3/src/gui/otpclient-window.c --- old/OTPClient-5.0.1/src/gui/otpclient-window.c 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/src/gui/otpclient-window.c 2026-05-15 14:48:27.000000000 +0200 @@ -2419,6 +2419,50 @@ adw_dialog_present (ADW_DIALOG (dlg), GTK_WIDGET (self)); } +/* Shared tail for the "Scan QR from File / Webcam / Clipboard" actions. + * Takes ownership of @otpauth_uri (frees it before returning). */ +static void +add_token_from_otpauth_uri (OTPClientWindow *self, + gchar *otpauth_uri) +{ + OTPClientApplication *app = OTPCLIENT_APPLICATION ( + gtk_window_get_application (GTK_WINDOW (self))); + if (app == NULL) { + g_free (otpauth_uri); + return; + } + + DatabaseData *db_data = otpclient_application_get_db_data (app); + if (db_data == NULL) { + g_free (otpauth_uri); + return; + } + + GSList *otps = NULL; + set_otps_from_uris (otpauth_uri, &otps); + g_free (otpauth_uri); + + if (otps == NULL) { + show_error_toast (self, _("QR code is not an OTP URI")); + return; + } + + GError *err = NULL; + add_otps_to_db (otps, db_data); + free_otps_gslist (otps, g_slist_length (otps)); + update_db (db_data, &err); + if (err != NULL) { + show_error_toast (self, _("Failed to add scanned token: %s"), err->message); + g_clear_error (&err); + } else { + g_slist_free (db_data->data_to_add); + db_data->data_to_add = NULL; + reload_db (db_data, &err); + g_clear_error (&err); + } + on_db_modified (self); +} + static void on_qr_file_selected (GObject *source, GAsyncResult *result, @@ -2442,38 +2486,7 @@ return; } - OTPClientApplication *app = OTPCLIENT_APPLICATION ( - gtk_window_get_application (GTK_WINDOW (self))); - if (app == NULL) { - g_free (otpauth_uri); - return; - } - - DatabaseData *db_data = otpclient_application_get_db_data (app); - if (db_data == NULL) { - g_free (otpauth_uri); - return; - } - - GSList *otps = NULL; - set_otps_from_uris (otpauth_uri, &otps); - g_free (otpauth_uri); - - if (otps != NULL) { - add_otps_to_db (otps, db_data); - free_otps_gslist (otps, g_slist_length (otps)); - update_db (db_data, &err); - if (err != NULL) { - show_error_toast (self, _("Failed to add scanned token: %s"), err->message); - g_clear_error (&err); - } else { - g_slist_free (db_data->data_to_add); - db_data->data_to_add = NULL; - reload_db (db_data, &err); - g_clear_error (&err); - } - on_db_modified (self); - } + add_token_from_otpauth_uri (self, otpauth_uri); } static void @@ -2512,14 +2525,6 @@ (void) parameter; OTPClientWindow *self = OTPCLIENT_WINDOW (widget); - OTPClientApplication *app = OTPCLIENT_APPLICATION ( - gtk_window_get_application (GTK_WINDOW (self))); - if (app == NULL) - return; - - DatabaseData *db_data = otpclient_application_get_db_data (app); - if (db_data == NULL) - return; GError *err = NULL; gchar *otpauth_uri = webcam_scan_qrcode (&err); @@ -2529,25 +2534,59 @@ return; } - GSList *otps = NULL; - set_otps_from_uris (otpauth_uri, &otps); - g_free (otpauth_uri); + add_token_from_otpauth_uri (self, otpauth_uri); +} - if (otps != NULL) { - add_otps_to_db (otps, db_data); - free_otps_gslist (otps, g_slist_length (otps)); - update_db (db_data, &err); - if (err != NULL) { - show_error_toast (self, _("Failed to add scanned token: %s"), err->message); - g_clear_error (&err); - } else { - g_slist_free (db_data->data_to_add); - db_data->data_to_add = NULL; - reload_db (db_data, &err); - g_clear_error (&err); - } - on_db_modified (self); +static void +on_clipboard_texture_received (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + OTPClientWindow *self = OTPCLIENT_WINDOW (user_data); + GError *err = NULL; + g_autoptr (GdkTexture) texture = + gdk_clipboard_read_texture_finish (GDK_CLIPBOARD (source), result, &err); + if (texture == NULL) { + show_error_toast (self, _("No image on clipboard: %s"), + err ? err->message : _("unknown error")); + g_clear_error (&err); + return; + } + + gchar *otpauth_uri = qrcode_parse_texture (texture, &err); + if (otpauth_uri == NULL) { + show_error_toast (self, _("Could not read QR code: %s"), + err ? err->message : _("unknown error")); + g_clear_error (&err); + return; + } + + add_token_from_otpauth_uri (self, otpauth_uri); +} + +static void +action_add_qr_clipboard (GtkWidget *widget, + const char *action_name, + GVariant *parameter) +{ + (void) action_name; + (void) parameter; + + OTPClientWindow *self = OTPCLIENT_WINDOW (widget); + GdkDisplay *display = gtk_widget_get_display (widget); + if (display == NULL) { + show_error_toast (self, _("No display available")); + return; + } + + GdkClipboard *clipboard = gdk_display_get_clipboard (display); + if (clipboard == NULL) { + show_error_toast (self, _("Clipboard is not available")); + return; } + + gdk_clipboard_read_texture_async (clipboard, NULL, + on_clipboard_texture_received, self); } static void @@ -4142,6 +4181,7 @@ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, "win.add-manual", NULL); gtk_widget_class_install_action (widget_class, "win.add-qr-file", NULL, action_add_qr_file); gtk_widget_class_install_action (widget_class, "win.add-qr-webcam", NULL, action_add_qr_webcam); + gtk_widget_class_install_action (widget_class, "win.add-qr-clipboard", NULL, action_add_qr_clipboard); gtk_widget_class_install_action (widget_class, "win.import", NULL, action_import); gtk_widget_class_add_binding_action (widget_class, GDK_KEY_i, GDK_CONTROL_MASK, "win.import", NULL); gtk_widget_class_install_action (widget_class, "win.export", NULL, action_export); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/src/gui/qrcode-parser.c new/OTPClient-5.0.3/src/gui/qrcode-parser.c --- old/OTPClient-5.0.1/src/gui/qrcode-parser.c 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/src/gui/qrcode-parser.c 2026-05-15 14:48:27.000000000 +0200 @@ -4,6 +4,53 @@ #include "qrcode-parser.h" #include "gquarks.h" +/* Real-world QR code images for OTP enrollment are at most a few hundred + * pixels per side. Cap the dimensions before any allocation: a malicious + * source (PNG file, oversized clipboard texture) with width=height=65535 + * would otherwise request ~17 GB across the row pointer array, the per-row + * buffers, and the grayscale buffer. g_malloc aborts on huge allocations, + * which crashes the application — DoS via a single QR import. */ +#define MAX_QR_IMAGE_DIM 4096u + +static gchar * +scan_grayscale_buffer (const guchar *gray, + guint width, + guint height, + GError **error) +{ + zbar_image_scanner_t *scanner = zbar_image_scanner_create (); + zbar_image_scanner_set_config (scanner, 0, ZBAR_CFG_ENABLE, 1); + + zbar_image_t *image = zbar_image_create (); + zbar_image_set_format (image, zbar_fourcc ('Y', '8', '0', '0')); + zbar_image_set_size (image, width, height); + zbar_image_set_data (image, gray, width * height, NULL); + + gint n = zbar_scan_image (scanner, image); + gchar *result = NULL; + + if (n > 0) + { + const zbar_symbol_t *symbol = zbar_image_first_symbol (image); + if (symbol != NULL) + { + const gchar *data = zbar_symbol_get_data (symbol); + if (data != NULL) + result = g_strdup (data); + } + } + else + { + g_set_error (error, generic_error_gquark (), GENERIC_ERRCODE, + "No QR code found in the image"); + } + + zbar_image_destroy (image); + zbar_image_scanner_destroy (scanner); + + return result; +} + static gboolean load_png_image (const gchar *filepath, guchar **raw_data, @@ -52,13 +99,6 @@ *width = png_get_image_width (png, info); *height = png_get_image_height (png, info); - /* Real-world QR code images for OTP enrollment are at most a few hundred - * pixels per side. Cap the dimensions before any allocation: a malicious - * PNG with width=height=65535 (libpng's maximum) would otherwise request - * ~17 GB across the row pointer array, the per-row buffers, and the - * grayscale buffer below. g_malloc aborts on huge allocations, which - * crashes the application — DoS via a single QR import. */ - #define MAX_QR_IMAGE_DIM 4096u if (*width == 0 || *height == 0 || *width > MAX_QR_IMAGE_DIM || *height > MAX_QR_IMAGE_DIM) { @@ -139,36 +179,48 @@ if (!load_png_image (filepath, &raw_data, &width, &height, error)) return NULL; - zbar_image_scanner_t *scanner = zbar_image_scanner_create (); - zbar_image_scanner_set_config (scanner, 0, ZBAR_CFG_ENABLE, 1); + gchar *result = scan_grayscale_buffer (raw_data, width, height, error); + g_free (raw_data); - zbar_image_t *image = zbar_image_create (); - zbar_image_set_format (image, zbar_fourcc ('Y', '8', '0', '0')); - zbar_image_set_size (image, width, height); - zbar_image_set_data (image, raw_data, width * height, NULL); + return result; +} - gint n = zbar_scan_image (scanner, image); - gchar *result = NULL; +gchar * +qrcode_parse_texture (GdkTexture *texture, + GError **error) +{ + g_return_val_if_fail (texture != NULL, NULL); - if (n > 0) + int w = gdk_texture_get_width (texture); + int h = gdk_texture_get_height (texture); + if (w <= 0 || h <= 0 || + (guint) w > MAX_QR_IMAGE_DIM || (guint) h > MAX_QR_IMAGE_DIM) { - const zbar_symbol_t *symbol = zbar_image_first_symbol (image); - if (symbol != NULL) - { - const gchar *data = zbar_symbol_get_data (symbol); - if (data != NULL) - result = g_strdup (data); - } + g_set_error (error, generic_error_gquark (), GENERIC_ERRCODE, + "Image dimensions out of range (%dx%d, max %ux%u).", + w, h, MAX_QR_IMAGE_DIM, MAX_QR_IMAGE_DIM); + return NULL; } - else + + gsize stride = (gsize) w * 4; + guchar *rgba = g_malloc ((gsize) h * stride); + /* gdk_texture_download() always emits BGRA in the host byte order. */ + gdk_texture_download (texture, rgba, stride); + + guchar *gray = g_malloc ((gsize) w * (gsize) h); + for (int y = 0; y < h; y++) { - g_set_error (error, generic_error_gquark (), GENERIC_ERRCODE, - "No QR code found in the image"); + for (int x = 0; x < w; x++) + { + const guchar *px = &rgba[y * stride + x * 4]; + /* Channel order is B,G,R,A (cairo / GdkMemoryFormat default). */ + gray[y * w + x] = (guchar)(0.299 * px[2] + 0.587 * px[1] + 0.114 * px[0]); + } } + g_free (rgba); - zbar_image_destroy (image); - zbar_image_scanner_destroy (scanner); - g_free (raw_data); + gchar *result = scan_grayscale_buffer (gray, (guint) w, (guint) h, error); + g_free (gray); return result; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/src/gui/qrcode-parser.h new/OTPClient-5.0.3/src/gui/qrcode-parser.h --- old/OTPClient-5.0.1/src/gui/qrcode-parser.h 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/src/gui/qrcode-parser.h 2026-05-15 14:48:27.000000000 +0200 @@ -1,10 +1,14 @@ #pragma once #include <glib.h> +#include <gdk/gdk.h> G_BEGIN_DECLS gchar *qrcode_parse_image_file (const gchar *filepath, GError **error); +gchar *qrcode_parse_texture (GdkTexture *texture, + GError **error); + G_END_DECLS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/OTPClient-5.0.1/src/gui/ui/window.ui new/OTPClient-5.0.3/src/gui/ui/window.ui --- old/OTPClient-5.0.1/src/gui/ui/window.ui 2026-05-13 10:09:27.000000000 +0200 +++ new/OTPClient-5.0.3/src/gui/ui/window.ui 2026-05-15 14:48:27.000000000 +0200 @@ -270,6 +270,10 @@ <attribute name="label" translatable="yes">Scan QR from Webcam</attribute> <attribute name="action">win.add-qr-webcam</attribute> </item> + <item> + <attribute name="label" translatable="yes">Scan QR from Clipboard</attribute> + <attribute name="action">win.add-qr-clipboard</attribute> + </item> </section> <section> <item>
