Control: tags -1 + patch
Hi Richard,
On Thu, 20 Jun 2024 17:45:26 +0300,
Jeremy BĂcha wrote:
> The Debian GNOME maintainers would like to stop building libsoup2.4.
> Therefore, please stop using libsoup2.4.
Please find attached a patch which I tested extensively:
* I tried numerous downloads and cancelled them.
* I wiped ~/.gbonds several times and triggered full downloads.
* I simulated errors from soup_session_send_finish and
json_parser_load_from_stream by introducing intentional typo in
TREASURY_HOST and TREASURY_URL, respectively.
* Ran the program under gdb and valgrind with GBONDS_DEBUG_UPDATE
and G_DEBUG set, which revealed a few issues (fixed).
The main problem is easily reproducible with the version in unstable:
just interrupt the download by clicking the "Cancel" button and retry
again. The download hangs and there's a GLib critical message logged.
There are also a few problems which stem from improper use of the
GCancellable object, insufficient guards in free_download_data and a
few memory leaks as well.
One addition unrelated to the libsoup migration is making the text in
the GtkProgressBar visible.
I made it as a separate patch so that it would be easier for you to
review the changes but if you prefer, I can merge it into
use-treasury-api.patch.
Let me know if I missed something.
As I've put significant efforts in this package by porting it from
BonoboUI to GTK 3 I'd be sad to see it removed from the archive.
>From 6e1426461d899c4aa4ebef9b3721a6bd391eec97 Mon Sep 17 00:00:00 2001
From: Yavor Doganov <[email protected]>
Date: Tue, 14 Oct 2025 15:36:10 +0300
Subject: [PATCH] Port to libsoup3 (Closes: #1073944).
---
debian/control | 2 +-
debian/patches/libsoup3.patch | 307 ++++++++++++++++++++++++++++++++++
debian/patches/series | 1 +
3 files changed, 309 insertions(+), 1 deletion(-)
create mode 100644 debian/patches/libsoup3.patch
diff --git a/debian/control b/debian/control
index 564c8ba..49e7c04 100644
--- a/debian/control
+++ b/debian/control
@@ -6,7 +6,7 @@ Build-Depends: debhelper-compat (= 13),
intltool,
libgtk-3-dev,
libjson-glib-dev,
- libsoup2.4-dev,
+ libsoup-3.0-dev,
libtool,
libxml2-dev,
Standards-Version: 4.6.0
diff --git a/debian/patches/libsoup3.patch b/debian/patches/libsoup3.patch
new file mode 100644
index 0000000..8ee94c5
--- /dev/null
+++ b/debian/patches/libsoup3.patch
@@ -0,0 +1,307 @@
+Description: Port to libsoup3.
+Bug-Debian: https://bugs.debian.org/1073944
+Author: Yavor Doganov <[email protected]>
+Forwarded: no
+Last-Update: 2025-10-14
+---
+
+--- gbonds.orig/configure.in
++++ gbonds/configure.in
+@@ -31,13 +31,10 @@
+
+ PKG_CHECK_MODULES(GBONDS, gtk+-3.0 >= $GTK_REQUIRED \
+ json-glib-1.0
+-libsoup-2.4 >= 2.56
++libsoup-3.0
+ libxml-2.0 >= $LIBXML_REQUIRED \
+ )
+
+-GBONDS_CFLAGS="$GBONDS_CFLAGS -DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_56"
+-GBONDS_CFLAGS="$GBONDS_CFLAGS -DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_2_56"
+-
+ AC_SUBST(GBONDS_CFLAGS)
+ AC_SUBST(GBONDS_LIBS)
+
+--- gbonds.orig/src/update.c
++++ gbonds/src/update.c
+@@ -97,14 +97,15 @@
+
+ static void add_start_page( GtkAssistant *wdruid,
+ GdkPixbuf *logo );
+-static void add_downloading_page( GtkAssistant *wdruid, GdkPixbuf *logo );
++static void add_downloading_page( GtkAssistant *wdruid, GdkPixbuf *logo,
++ DownloadCallbackData *data );
+ static void prepare_downloading_page( GtkAssistant *druid, GtkWidget *page,
+ gpointer data );
+
+ static void download_page ( DownloadCallbackData *data);
+-static void download_cb ( SoupSession *session, SoupMessage *msg,
++static void download_cb ( GObject *source, GAsyncResult *res,
+ gpointer user_data);
+-static void start_download ();
++static void start_download ( DownloadCallbackData *data );
+
+ static void download_done( DownloadCallbackData *data );
+ static void no_download( DownloadCallbackData *data );
+@@ -130,6 +131,7 @@
+ void
+ gb_update_druid (void)
+ {
++ DownloadCallbackData *data;
+ GdkPixbuf *logo;
+ gchar *file;
+
+@@ -145,14 +147,18 @@
+ DRUID_WIDTH, DRUID_HEIGHT);
+
+ add_start_page (GTK_ASSISTANT (update_druid), logo);
+- add_downloading_page (GTK_ASSISTANT (update_druid), logo);
++ data = g_new0 (DownloadCallbackData, 1);
++ add_downloading_page (GTK_ASSISTANT (update_druid), logo, data);
+ add_finish_page (GTK_ASSISTANT (update_druid), logo);
+ g_object_unref (logo);
+
++ if (!update_cancel_flag)
++ update_cancel_flag = g_cancellable_new ();
++
+ g_signal_connect (update_druid, "cancel",
+- G_CALLBACK (cancel_cb), NULL);
++ G_CALLBACK (cancel_cb), data);
+ g_signal_connect (update_druid, "close",
+- G_CALLBACK (destroy_cb), NULL);
++ G_CALLBACK (destroy_cb), data);
+
+ gtk_widget_show_all (update_druid);
+
+@@ -298,7 +304,8 @@
+ /* PRIVATE. Create and add "downloading" page to druid. */
+ /*--------------------------------------------------------------------------*/
+ static void
+-add_downloading_page( GtkAssistant *wdruid, GdkPixbuf *logo )
++add_downloading_page( GtkAssistant *wdruid, GdkPixbuf *logo,
++ DownloadCallbackData *data )
+ {
+ GtkWidget *wvbox, *wvbox2, *wvbox3, *wvbox4;
+ GtkWidget *whbox2, *whbox3, *whbox4;
+@@ -334,6 +341,7 @@
+ gtk_box_pack_start( GTK_BOX(whbox3), page_label, FALSE, TRUE, 0 );
+
+ page_progress = gtk_progress_bar_new();
++ gtk_progress_bar_set_show_text( GTK_PROGRESS_BAR(page_progress), TRUE );
+ gtk_progress_bar_set_text( GTK_PROGRESS_BAR(page_progress), "" );
+ gtk_box_pack_start( GTK_BOX(wvbox3), page_progress, FALSE, TRUE, 0 );
+
+@@ -342,7 +350,7 @@
+ gtk_assistant_set_page_type (wdruid, wvbox,
+ GTK_ASSISTANT_PAGE_CUSTOM);
+ g_signal_connect_after( G_OBJECT(wdruid), "prepare",
+- G_CALLBACK(prepare_downloading_page), NULL );
++ G_CALLBACK(prepare_downloading_page), data );
+
+ gb_debug (DEBUG_UPDATE, "END");
+ }
+@@ -367,11 +375,11 @@
+
+ custom_widget = gtk_button_new_with_mnemonic (_("_Cancel"));
+ g_signal_connect (custom_widget, "clicked",
+- G_CALLBACK (cancel_cb), NULL);
++ G_CALLBACK (cancel_cb), data);
+ gtk_widget_show (custom_widget);
+ gtk_assistant_add_action_widget (druid, custom_widget);
+
+- start_download ();
++ start_download (data);
+
+ gb_debug (DEBUG_UPDATE, "END");
+ }
+@@ -416,10 +424,9 @@
+ /* PRIVATE. Begin the asynchronous process of downloading data. */
+ /*--------------------------------------------------------------------------*/
+ static void
+-start_download ()
++start_download (DownloadCallbackData *data)
+ {
+ gchar *status_string;
+- DownloadCallbackData *data;
+
+ gb_debug (DEBUG_UPDATE, "START");
+
+@@ -428,17 +435,11 @@
+ gtk_label_set_text (GTK_LABEL (status_label), status_string);
+ g_free (status_string);
+
+- if (!update_cancel_flag)
+- update_cancel_flag = g_cancellable_new ();
+-
+ update_progress_bar (GTK_PROGRESS_BAR (page_progress), 0, 0);
+
+- data = g_new0 ( DownloadCallbackData, 1 );
+- data->session = g_object_new (
+- SOUP_TYPE_SESSION,
+- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+- SOUP_SESSION_USER_AGENT, PACKAGE_STRING,
+- SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
++ data->session = soup_session_new_with_options (
++ "user-agent", PACKAGE_STRING,
++ "accept-language-auto", TRUE,
+ NULL);
+ data->year = GB_YEAR(download_date);
+ data->month = GB_MONTH(download_date);
+@@ -471,7 +472,14 @@
+ gb_debug (DEBUG_UPDATE, "URI: %s", uri);
+
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+- soup_session_queue_message (data->session, msg, download_cb, data);
++ /* Keep the session object alive in case the async operation
++ gets cancelled and it is freed in destroy_cb before
++ download_cb completes. */
++ g_object_ref (data->session);
++ soup_session_send_async (data->session, msg, G_PRIORITY_DEFAULT,
++ update_cancel_flag, download_cb, data);
++ g_free (uri);
++ g_object_unref (msg);
+
+ gb_debug (DEBUG_UPDATE, "END");
+ }
+@@ -663,9 +671,11 @@
+ /* PRIVATE. Process one page of data from the API. */
+ /*--------------------------------------------------------------------------*/
+ static void
+-download_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
++download_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+ {
+ DownloadCallbackData *data = (DownloadCallbackData *)user_data;
++ GError *err = NULL;
++ GInputStream *body;
+ gboolean result;
+ JsonNode *root;
+ JsonObject *root_object;
+@@ -674,35 +684,44 @@
+
+ gb_debug (DEBUG_UPDATE, "START");
+
+- if (g_cancellable_is_cancelled (update_cancel_flag)) {
+- free_download_data (data);
++ body = soup_session_send_finish (SOUP_SESSION (source), res, &err);
++ /* Drop the extra ref added in download_page. */
++ g_object_unref (SOUP_SESSION (source));
++
++ if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
++ g_error_free (err);
+ gb_debug (DEBUG_UPDATE, "END -- CANCEL");
+ return;
+ }
+
+- if (msg->status_code == SOUP_STATUS_SSL_FAILED) {
+- GTlsCertificateFlags flags;
+-
+- if (soup_message_get_https_status (msg, NULL, &flags))
+- io_error ("%d %s (0x%x)\n", msg->status_code,
+- msg->reason_phrase, flags);
+- else
++ if (err) {
++ if (err->domain == G_TLS_ERROR)
+ io_error ("%d %s (no handshake status)\n",
+- msg->status_code, msg->reason_phrase);
+- gb_debug (DEBUG_UPDATE, "END -- ERROR");
+- return;
+- } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
+- io_error ("%d %s\n", msg->status_code, msg->reason_phrase);
+- gb_debug (DEBUG_UPDATE, "END -- ERROR");
+- return;
++ err->code, err->message);
++ else {
++ io_error ("%d %s\n", err->code, err->message);
++ g_error_free (err);
++ gb_debug (DEBUG_UPDATE, "END -- ERROR");
++ return;
++ }
+ }
+
+ if (!data->parser)
+ data->parser = json_parser_new ();
+- result = json_parser_load_from_data (
+- data->parser, msg->response_body->data, -1, NULL);
++ result = json_parser_load_from_stream (
++ data->parser, body, update_cancel_flag, &err);
++ g_input_stream_close (body, NULL, NULL);
++ g_object_unref (body);
++
++ if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
++ g_error_free (err);
++ gb_debug (DEBUG_UPDATE, "END -- CANCEL");
++ return;
++ }
+ if (!result) {
+- io_error (_("Failed to parse JSON response."));
++ io_error (_("Failed to parse JSON response: %s."),
++ err->message);
++ g_error_free (err);
+ gb_debug (DEBUG_UPDATE, "END -- FAILED TO PARSE");
+ return;
+ }
+@@ -912,8 +931,6 @@
+ custom_widget);
+ gtk_assistant_next_page (GTK_ASSISTANT (update_druid));
+
+- free_download_data (data);
+-
+ gb_debug (DEBUG_UPDATE, "END");
+ }
+
+@@ -958,22 +975,24 @@
+ custom_widget);
+ gtk_assistant_next_page (GTK_ASSISTANT (update_druid));
+
+- free_download_data (data);
+-
+ gb_debug (DEBUG_UPDATE, "END");
+ }
+
+ static void free_download_data( DownloadCallbackData *data )
+ {
+- g_object_unref (data->session);
+- g_object_unref (data->parser);
++ if (data->session)
++ g_object_unref (data->session);
++ if (data->parser)
++ g_object_unref (data->parser);
+ if (data->file) {
+ fclose (data->file);
+ unlink (data->tmp_filename);
+ g_free (data->tmp_filename);
+ g_free (data->filename);
+ }
+- g_free (data->dir);
++ if (data->dir)
++ g_free (data->dir);
++
+ g_free (data);
+ }
+
+@@ -1010,10 +1029,12 @@
+ {
+ gb_debug (DEBUG_UPDATE, "START");
+
+- if (update_cancel_flag)
+- g_cancellable_cancel (update_cancel_flag);
++ if (update_cancel_flag) {
++ g_cancellable_cancel (update_cancel_flag);
++ g_clear_object (&update_cancel_flag);
++ }
+
+- gtk_widget_destroy (update_druid);
++ destroy_cb (druid, data);
+
+ gb_debug (DEBUG_UPDATE, "END");
+ }
+@@ -1028,6 +1049,9 @@
+ {
+ gb_debug (DEBUG_UPDATE, "START");
+
++ if (update_cancel_flag)
++ g_clear_object (&update_cancel_flag);
++ free_download_data (data);
+ gtk_widget_destroy (update_druid);
+
+ gb_debug (DEBUG_UPDATE, "END");
diff --git a/debian/patches/series b/debian/patches/series
index 60888c2..f5d394c 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -17,3 +17,4 @@ gsettings-port
no-rarian-compat
extern-gb_prefs
use-treasury-api.patch
+libsoup3.patch
--
2.51.0