diff --git a/.gitignore b/.gitignore index ba3b2b3..4e53948 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,95 @@ -/gnome-software-*.tar.xz -/gnome-software-*-build +/gnome-software-0.1.tar.xz +/gnome-software-3.9.1.tar.xz +/gnome-software-3.9.2.tar.xz +/gnome-software-3.9.3.tar.xz +/fedora-20.xml.gz +/fedora-20-icons.tar.gz +/gnome-software-3.10.0.tar.xz +/gnome-software-3.11.1.tar.xz +/fedora-20-codecs.xml +/web-apps.xml +/gnome-software-3.11.2.tar.xz +/gnome-software-3.11.3.tar.xz +/gnome-software-3.11.4.tar.xz +/fedora-rawhide-icons.tar.gz +/fedora-rawhide.xml.gz +/gnome-software-3.11.5.tar.xz +/gnome-software-3.11.90.tar.xz +/gnome-software-3.11.91.tar.xz +/gnome-software-3.11.92.tar.xz +/gnome-software-3.12.0.tar.xz +/gnome-software-3.12.1.tar.xz +/gnome-software-3.13.1.tar.xz +/fedora-21-icons.tar.gz +/fedora-21.xml.gz +/gnome-software-3.13.2.tar.xz +/gnome-software-3.13.3-7491627.tar.xz +/gnome-software-3.13.3.tar.xz +/gnome-software-3.13.4.tar.xz +/gnome-software-3.13.5-5c89189.tar.xz +/gnome-software-3.13.90.tar.xz +/gnome-software-3.13.91.tar.xz +/gnome-software-3.13.92.tar.xz +/gnome-software-3.14.0.tar.xz +/gnome-software-3.14.1.tar.xz +/gnome-software-3.14.2.tar.xz +/gnome-software-3.15.2.tar.xz +/gnome-software-3.15.4.tar.xz +/gnome-software-3.15.90.tar.xz +/gnome-software-3.15.91.tar.xz +/gnome-software-3.15.92.tar.xz +/gnome-software-3.16.0.tar.xz +/gnome-software-3.16.1.tar.xz +/gnome-software-3.16.2.tar.xz +/gnome-software-3.17.1.tar.xz +/gnome-software-3.17.2.tar.xz +/gnome-software-3.17.3.tar.xz +/gnome-software-3.17.90.tar.xz +/gnome-software-3.17.91.tar.xz +/gnome-software-3.17.92.tar.xz +/gnome-software-3.18.0.tar.xz +/gnome-software-3.18.1.tar.xz +/gnome-software-3.18.2.tar.xz +/gnome-software-3.18.3.tar.xz +/gnome-software-3.19.4.tar.xz +/gnome-software-3.19.90.tar.xz +/gnome-software-3.19.91.tar.xz +/gnome-software-3.19.92.tar.xz +/gnome-software-3.20.0.tar.xz +/gnome-software-3.20.1.tar.xz +/gnome-software-3.20.2.tar.xz +/gnome-software-3.21.1.tar.xz +/gnome-software-3.21.2.tar.xz +/gnome-software-3.21.4.tar.xz +/gnome-software-3.21.90.tar.xz +/gnome-software-3.21.91.tar.xz +/gnome-software-3.21.92.tar.xz +/gnome-software-3.22.0.tar.xz +/gnome-software-3.22.1.tar.xz +/gnome-software-3.22.2.tar.xz +/gnome-software-3.23.2.tar.xz +/gnome-software-3.23.3.tar.xz +/gnome-software-3.23.90.tar.xz +/gnome-software-3.23.91.tar.xz +/gnome-software-3.23.92.tar.xz +/gnome-software-3.24.0.tar.xz +/gnome-software-3.24.1.tar.xz +/gnome-software-3.24.2.tar.xz +/gnome-software-3.24.3.tar.xz +/gnome-software-3.25.3.tar.xz +/gnome-software-3.25.4.tar.xz +/gnome-software-3.25.90.tar.xz +/gnome-software-3.25.91.tar.xz +/gnome-software-3.26.0.tar.xz +/gnome-software-3.26.1.tar.xz +/gnome-software-3.26.2.tar.xz +/gnome-software-3.26.3.tar.xz +/gnome-software-3.26.4.tar.xz +/gnome-software-3.26.5.tar.xz +/gnome-software-3.26.6.tar.xz +/gnome-software-3.26.7.tar.xz +/f28.png +/gnome-software-3.28.0.tar.xz +/gnome-software-3.28.1.tar.xz +/gnome-software-3.28.2.tar.xz +/f29.png diff --git a/0001-Include-the-distro-version-in-Fedora-distro-upgrade-.patch b/0001-Include-the-distro-version-in-Fedora-distro-upgrade-.patch new file mode 100644 index 0000000..f51c077 --- /dev/null +++ b/0001-Include-the-distro-version-in-Fedora-distro-upgrade-.patch @@ -0,0 +1,55 @@ +From 7afc2c4e3f4ffac704b28098eae3136313d791d3 Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Mon, 8 Oct 2018 11:02:50 +0200 +Subject: [PATCH 1/2] Include the distro version in Fedora distro upgrade app + IDs + +If we use just "org.fedoraproject.Fedora", GsAppList deduplicates based +on the app ID and only keeps on of the returned apps. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1628497 +--- + .../gs-plugin-fedora-pkgdb-collections.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/plugins/fedora-pkgdb-collections/gs-plugin-fedora-pkgdb-collections.c b/plugins/fedora-pkgdb-collections/gs-plugin-fedora-pkgdb-collections.c +index fc644ad47..5b8137e3d 100644 +--- a/plugins/fedora-pkgdb-collections/gs-plugin-fedora-pkgdb-collections.c ++++ b/plugins/fedora-pkgdb-collections/gs-plugin-fedora-pkgdb-collections.c +@@ -1,6 +1,6 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * +- * Copyright (C) 2016-2017 Kalev Lember ++ * Copyright (C) 2016-2018 Kalev Lember + * Copyright (C) 2017 Richard Hughes + * + * Licensed under the GNU General Public License Version 2 +@@ -280,6 +280,7 @@ static GsApp * + _create_upgrade_from_info (GsPlugin *plugin, PkgdbItem *item) + { + GsApp *app; ++ g_autofree gchar *app_id = NULL; + g_autofree gchar *app_version = NULL; + g_autofree gchar *background = NULL; + g_autofree gchar *cache_key = NULL; +@@ -293,7 +294,7 @@ _create_upgrade_from_info (GsPlugin *plugin, PkgdbItem *item) + if (app != NULL) + return app; + +- /* create app */ ++ app_id = g_strdup_printf ("org.fedoraproject.Fedora-%u", item->version); + app_version = g_strdup_printf ("%u", item->version); + + /* icon from disk */ +@@ -302,7 +303,7 @@ _create_upgrade_from_info (GsPlugin *plugin, PkgdbItem *item) + as_icon_set_filename (ic, "/usr/share/pixmaps/fedora-logo-sprite.png"); + + /* create */ +- app = gs_app_new ("org.fedoraproject.Fedora"); ++ app = gs_app_new (app_id); + gs_app_set_state (app, AS_APP_STATE_AVAILABLE); + gs_app_set_kind (app, AS_APP_KIND_OS_UPGRADE); + gs_app_set_name (app, GS_APP_QUALITY_LOWEST, item->name); +-- +2.19.1 + diff --git a/0001-dnf5-plugin.patch b/0001-dnf5-plugin.patch deleted file mode 100644 index 3b4b355..0000000 --- a/0001-dnf5-plugin.patch +++ /dev/null @@ -1,6770 +0,0 @@ -at commit 786bab5f1928920251ee9e93d51953ff9dd9faa4 -Date: Mon Sep 1 18:23:09 2025 +0200 - -diff --git a/meson_options.txt b/meson_options.txt -index cd49cf529..27fa15e8b 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -3,6 +3,7 @@ option('installed_tests', type : 'boolean', value : false, description : 'enable - option('man', type : 'boolean', value : true, description : 'enable man pages') - option('packagekit', type : 'boolean', value : true, description : 'enable PackageKit support') - option('packagekit_autoremove', type : 'boolean', value : false, description : 'autoremove packages in PackageKit') -+option('dnf5', type : 'boolean', value : true, description : 'enable dnf5 support') - option('polkit', type : 'boolean', value : true, description : 'enable PolKit support') - option('eos_updater', type : 'boolean', value : false, description : 'enable eos-updater support') - option('fwupd', type : 'boolean', value : true, description : 'enable fwupd support') -diff --git a/plugins/dnf5/gs-dnf5-progress-helper.c b/plugins/dnf5/gs-dnf5-progress-helper.c -new file mode 100644 -index 000000000..cd8b861df ---- /dev/null -+++ b/plugins/dnf5/gs-dnf5-progress-helper.c -@@ -0,0 +1,552 @@ -+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- -+ * vi:set noexpandtab tabstop=8 shiftwidth=8: -+ * -+ * Copyright (C) 2024 Red Hat -+ * -+ * SPDX-License-Identifier: GPL-2.0+ -+ */ -+ -+#include -+ -+#include -+ -+#include "gs-dnf5-generated.h" -+ -+#include "gs-dnf5-progress-helper.h" -+ -+struct _GsDnf5ProgressHelper -+{ -+ GObject parent; -+ -+ GsDnf5RpmRpm *rpm_proxy; /* (owned) */ -+ GsDnf5Base *base_proxy; /* (owned) */ -+ GsApp *app; /* (owned) (nullable) */ -+ GsAppList *list; /* (owned) (nullable) */ -+ gchar *session_object_path; /* (owned) */ -+ GHashTable *ongoing_downloads; /* (owned); gchar * (download_id) ~> DownloadInfo * */ -+ GsAppState recover_download_state; -+ guint64 to_download_total; -+ guint64 downloaded_total; -+ -+ gulong download_add_new_id; -+ gulong download_progress_id; -+ gulong download_mirror_failure_id; -+ gulong download_end_id; -+ gulong transaction_before_begin_id; -+ gulong transaction_after_complete_id; -+ gulong transaction_elem_progress_id; -+ gulong transaction_action_progress_id; -+ gulong transaction_action_start_id; -+ gulong transaction_action_stop_id; -+ gulong transaction_script_error_id; -+ gulong transaction_script_start_id; -+ gulong transaction_script_stop_id; -+ gulong transaction_transaction_progress_id; -+ gulong transaction_transaction_start_id; -+ gulong transaction_transaction_stop_id; -+ gulong transaction_unpack_error_id; -+ gulong transaction_verify_progress_id; -+ gulong transaction_verify_start_id; -+ gulong transaction_verify_stop_id; -+}; -+ -+G_DEFINE_TYPE (GsDnf5ProgressHelper, gs_dnf5_progress_helper, G_TYPE_OBJECT) -+ -+static void -+gs_dnf5_helper_set_progress (GsDnf5ProgressHelper *self, -+ guint progress) -+{ -+ if (self->app != NULL) -+ gs_app_set_progress (self->app, progress); -+ if (self->list != NULL) -+ gs_app_list_override_progress (self->list, progress); -+} -+ -+static void -+gs_dnf5_update_download_progress (GsDnf5ProgressHelper *self) -+{ -+ if (self->to_download_total > 0) -+ gs_dnf5_helper_set_progress (self, 100 * self->downloaded_total / self->to_download_total); -+} -+ -+typedef struct { -+ guint64 downloaded; -+ guint64 total; -+} DownloadInfo; -+ -+static void -+gs_dnf5_download_add_new_cb (GsDnf5Base *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_download_id, -+ const gchar *arg_description, -+ gint64 arg_total_to_download, -+ GsDnf5ProgressHelper *self) -+{ -+ DownloadInfo *download_info; -+ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: download_id:'%s' description:'%s' total_to_download:%" G_GINT64_FORMAT, -+ G_STRFUNC, arg_download_id, arg_description, arg_total_to_download); -+ g_return_if_fail (arg_download_id != NULL); -+ -+ if (arg_total_to_download <= 0) -+ return; -+ -+ if (g_hash_table_size (self->ongoing_downloads) == 0) { -+ if (self->app != NULL) { -+ self->recover_download_state = gs_app_get_state (self->app); -+ gs_app_set_state (self->app, GS_APP_STATE_DOWNLOADING); -+ } -+ self->to_download_total = 0; -+ self->downloaded_total = 0; -+ } -+ -+ download_info = g_new0 (DownloadInfo, 1); -+ download_info->total = arg_total_to_download; -+ -+ g_hash_table_insert (self->ongoing_downloads, g_strdup (arg_download_id), download_info); -+ self->to_download_total += arg_total_to_download; -+ -+ if (self->app != NULL) -+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); -+ gs_dnf5_update_download_progress (self); -+} -+ -+static void -+gs_dnf5_download_end_cb (GsDnf5Base *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_download_id, -+ guint arg_transfer_status, -+ const gchar *arg_message, -+ GsDnf5ProgressHelper *self) -+{ -+ DownloadInfo *download_info; -+ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: download_id:'%s' transfer_status:%u message:'%s'", -+ G_STRFUNC, arg_download_id, arg_transfer_status, arg_message); -+ g_return_if_fail (arg_download_id != NULL); -+ -+ download_info = g_hash_table_lookup (self->ongoing_downloads, arg_download_id); -+ if (download_info != NULL) { -+ self->downloaded_total -= download_info->downloaded; -+ self->downloaded_total += download_info->total; -+ -+ /* remove after being done with the `download_info`, because it frees it */ -+ g_hash_table_remove (self->ongoing_downloads, arg_download_id); -+ download_info = NULL; -+ -+ /* it's the last package to be downloaded */ -+ if (g_hash_table_size (self->ongoing_downloads) == 0) { -+ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN); -+ if (self->app != NULL) { -+ gs_app_set_state (self->app, self->recover_download_state); -+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_UNKNOWN, 0); -+ } -+ } else { -+ gs_dnf5_update_download_progress (self); -+ } -+ } -+} -+ -+static void -+gs_dnf5_download_mirror_failure_cb (GsDnf5Base *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_download_id, -+ const gchar *arg_message, -+ const gchar *arg_url, -+ const gchar *arg_metadata, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: download_id:'%s' message:'%s' url:'%s' metadata:'%s'", -+ G_STRFUNC, arg_download_id, arg_message, arg_url, arg_metadata); -+} -+ -+static void -+gs_dnf5_download_progress_cb (GsDnf5Base *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_download_id, -+ gint64 arg_total_to_download, -+ gint64 arg_downloaded, -+ GsDnf5ProgressHelper *self) -+{ -+ DownloadInfo *download_info; -+ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: download_id:'%s' total_to_download:%" G_GINT64_FORMAT " downloaded:%" G_GINT64_FORMAT, -+ G_STRFUNC, arg_download_id, arg_total_to_download, arg_downloaded); -+ -+ download_info = g_hash_table_lookup (self->ongoing_downloads, arg_download_id); -+ if (download_info != NULL) { -+ self->downloaded_total += arg_downloaded - download_info->downloaded; -+ download_info->downloaded = arg_downloaded; -+ if (download_info->downloaded != (guint64) arg_total_to_download) { -+ self->to_download_total -= download_info->total; -+ download_info->total = arg_total_to_download; -+ self->to_download_total += download_info->total; -+ if (self->app != NULL) -+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total); -+ } -+ gs_dnf5_update_download_progress (self); -+ } -+} -+ -+static void -+gs_dnf5_transaction_before_begin_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ gs_dnf5_helper_set_progress (self, 0); -+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_after_complete_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ gboolean arg_success, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: success:%d", G_STRFUNC, arg_success); -+ gs_dnf5_helper_set_progress (self, 100); -+} -+ -+static void -+gs_dnf5_transaction_elem_progress_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ guint64 arg_processed, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s' progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_processed, arg_total); -+ if (arg_total != 0) -+ gs_dnf5_helper_set_progress (self, arg_processed * 100 / arg_total); -+} -+ -+static void -+gs_dnf5_transaction_action_progress_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ guint64 arg_processed, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s' progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_processed, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_action_start_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ guint arg_action, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s' action:%u total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_action, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_action_stop_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s' total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_script_error_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ guint arg_scriptlet_type, -+ guint64 arg_return_code, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s' scriptlet-type:%u return-code:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_scriptlet_type, arg_return_code); -+} -+ -+static void -+gs_dnf5_transaction_script_start_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ guint arg_scriptlet_type, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s' scriptlet-type:%u", G_STRFUNC, arg_nevra, arg_scriptlet_type); -+} -+ -+static void -+gs_dnf5_transaction_script_stop_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ guint arg_scriptlet_type, -+ guint64 arg_return_code, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s' scriptlet-type:%u return-code:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_scriptlet_type, arg_return_code); -+} -+ -+static void -+gs_dnf5_transaction_transaction_progress_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ guint64 arg_processed, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_processed, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_transaction_start_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_transaction_stop_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_unpack_error_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_nevra, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: nevra:'%s'", G_STRFUNC, arg_nevra); -+} -+ -+static void -+gs_dnf5_transaction_verify_progress_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ guint64 arg_processed, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_processed, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_verify_start_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); -+} -+ -+static void -+gs_dnf5_transaction_verify_stop_cb (GsDnf5RpmRpm *object, -+ const gchar *arg_session_object_path, -+ guint64 arg_total, -+ GsDnf5ProgressHelper *self) -+{ -+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0) -+ return; -+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total); -+} -+ -+static void -+gs_dnf5_progress_helper_dispose (GObject *object) -+{ -+ GsDnf5ProgressHelper *self = GS_DNF5_PROGRESS_HELPER (object); -+ GObject *base_proxy = G_OBJECT (self->base_proxy); -+ GObject *rpm_proxy = G_OBJECT (self->rpm_proxy); -+ -+ #define clear_signal_handler(_proxy, _id) G_STMT_START { \ -+ if (_id) { \ -+ g_signal_handler_disconnect (_proxy, _id); \ -+ _id = 0; \ -+ } \ -+ } G_STMT_END -+ -+ clear_signal_handler (base_proxy, self->download_add_new_id); -+ clear_signal_handler (base_proxy, self->download_progress_id); -+ clear_signal_handler (base_proxy, self->download_mirror_failure_id); -+ clear_signal_handler (base_proxy, self->download_end_id); -+ -+ clear_signal_handler (rpm_proxy, self->transaction_before_begin_id); -+ clear_signal_handler (rpm_proxy, self->transaction_after_complete_id); -+ clear_signal_handler (rpm_proxy, self->transaction_elem_progress_id); -+ clear_signal_handler (rpm_proxy, self->transaction_action_progress_id); -+ clear_signal_handler (rpm_proxy, self->transaction_action_start_id); -+ clear_signal_handler (rpm_proxy, self->transaction_action_stop_id); -+ clear_signal_handler (rpm_proxy, self->transaction_script_error_id); -+ clear_signal_handler (rpm_proxy, self->transaction_script_start_id); -+ clear_signal_handler (rpm_proxy, self->transaction_script_stop_id); -+ clear_signal_handler (rpm_proxy, self->transaction_transaction_progress_id); -+ clear_signal_handler (rpm_proxy, self->transaction_transaction_start_id); -+ clear_signal_handler (rpm_proxy, self->transaction_transaction_stop_id); -+ clear_signal_handler (rpm_proxy, self->transaction_unpack_error_id); -+ clear_signal_handler (rpm_proxy, self->transaction_verify_progress_id); -+ clear_signal_handler (rpm_proxy, self->transaction_verify_start_id); -+ clear_signal_handler (rpm_proxy, self->transaction_verify_stop_id); -+ -+ #undef clear_signal_handler -+ -+ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN); -+ -+ G_OBJECT_CLASS (gs_dnf5_progress_helper_parent_class)->dispose (object); -+} -+ -+static void -+gs_dnf5_progress_helper_finalize (GObject *object) -+{ -+ GsDnf5ProgressHelper *self = GS_DNF5_PROGRESS_HELPER (object); -+ -+ g_clear_object (&self->base_proxy); -+ g_clear_object (&self->rpm_proxy); -+ g_clear_object (&self->app); -+ g_clear_object (&self->list); -+ g_clear_pointer (&self->session_object_path, g_free); -+ g_clear_pointer (&self->ongoing_downloads, g_hash_table_destroy); -+ -+ G_OBJECT_CLASS (gs_dnf5_progress_helper_parent_class)->finalize (object); -+} -+ -+static void -+gs_dnf5_progress_helper_class_init (GsDnf5ProgressHelperClass *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ -+ object_class->dispose = gs_dnf5_progress_helper_dispose; -+ object_class->finalize = gs_dnf5_progress_helper_finalize; -+} -+ -+static void -+gs_dnf5_progress_helper_init (GsDnf5ProgressHelper *self) -+{ -+ self->ongoing_downloads = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); -+} -+ -+GsDnf5ProgressHelper * -+gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy, -+ GsDnf5RpmRpm *rpm_proxy, -+ GsApp *progress_app, -+ GsAppList *progress_list, -+ const gchar *session_object_path) -+{ -+ GsDnf5ProgressHelper *self; -+ -+ g_return_val_if_fail (GS_DNF5_IS_BASE (base_proxy), NULL); -+ g_return_val_if_fail (GS_DNF5_IS_RPM_RPM (rpm_proxy), NULL); -+ g_return_val_if_fail (GS_IS_APP (progress_app) || GS_IS_APP_LIST (progress_list), NULL); -+ g_return_val_if_fail (session_object_path != NULL, NULL); -+ -+ self = g_object_new (GS_TYPE_DNF5_PROGRESS_HELPER, NULL); -+ self->base_proxy = g_object_ref (base_proxy); -+ self->rpm_proxy = g_object_ref (rpm_proxy); -+ self->app = progress_app ? g_object_ref (progress_app) : NULL; -+ self->list = progress_list ? g_object_ref (progress_list) : NULL; -+ self->session_object_path = g_strdup (session_object_path); -+ -+ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN); -+ -+ self->download_add_new_id = -+ g_signal_connect_object (base_proxy, "download_add_new", -+ G_CALLBACK (gs_dnf5_download_add_new_cb), self, 0); -+ self->download_progress_id = -+ g_signal_connect_object (base_proxy, "download_progress", -+ G_CALLBACK (gs_dnf5_download_progress_cb), self, 0); -+ self->download_mirror_failure_id = -+ g_signal_connect_object (base_proxy, "download_mirror_failure", -+ G_CALLBACK (gs_dnf5_download_mirror_failure_cb), self, 0); -+ self->download_end_id = -+ g_signal_connect_object (base_proxy, "download_end", -+ G_CALLBACK (gs_dnf5_download_end_cb), self, 0); -+ -+ self->transaction_before_begin_id = -+ g_signal_connect_object (rpm_proxy, "transaction_before_begin", -+ G_CALLBACK (gs_dnf5_transaction_before_begin_cb), self, 0); -+ self->transaction_after_complete_id = -+ g_signal_connect_object (rpm_proxy, "transaction_after_complete", -+ G_CALLBACK (gs_dnf5_transaction_after_complete_cb), self, 0); -+ self->transaction_elem_progress_id = -+ g_signal_connect_object (rpm_proxy, "transaction_elem_progress", -+ G_CALLBACK (gs_dnf5_transaction_elem_progress_cb), self, 0); -+ self->transaction_action_progress_id = -+ g_signal_connect_object (rpm_proxy, "transaction_action_progress", -+ G_CALLBACK (gs_dnf5_transaction_action_progress_cb), self, 0); -+ self->transaction_action_start_id = -+ g_signal_connect_object (rpm_proxy, "transaction_action_start", -+ G_CALLBACK (gs_dnf5_transaction_action_start_cb), self, 0); -+ self->transaction_action_stop_id = -+ g_signal_connect_object (rpm_proxy, "transaction_action_stop", -+ G_CALLBACK (gs_dnf5_transaction_action_stop_cb), self, 0); -+ self->transaction_script_error_id = -+ g_signal_connect_object (rpm_proxy, "transaction_script_error", -+ G_CALLBACK (gs_dnf5_transaction_script_error_cb), self, 0); -+ self->transaction_script_start_id = -+ g_signal_connect_object (rpm_proxy, "transaction_script_start", -+ G_CALLBACK (gs_dnf5_transaction_script_start_cb), self, 0); -+ self->transaction_script_stop_id = -+ g_signal_connect_object (rpm_proxy, "transaction_script_stop", -+ G_CALLBACK (gs_dnf5_transaction_script_stop_cb), self, 0); -+ self->transaction_transaction_progress_id = -+ g_signal_connect_object (rpm_proxy, "transaction_transaction_progress", -+ G_CALLBACK (gs_dnf5_transaction_transaction_progress_cb), self, 0); -+ self->transaction_transaction_start_id = -+ g_signal_connect_object (rpm_proxy, "transaction_transaction_start", -+ G_CALLBACK (gs_dnf5_transaction_transaction_start_cb), self, 0); -+ self->transaction_transaction_stop_id = -+ g_signal_connect_object (rpm_proxy, "transaction_transaction_stop", -+ G_CALLBACK (gs_dnf5_transaction_transaction_stop_cb), self, 0); -+ self->transaction_unpack_error_id = -+ g_signal_connect_object (rpm_proxy, "transaction_unpack_error", -+ G_CALLBACK (gs_dnf5_transaction_unpack_error_cb), self, 0); -+ self->transaction_verify_progress_id = -+ g_signal_connect_object (rpm_proxy, "transaction_verify_progress", -+ G_CALLBACK (gs_dnf5_transaction_verify_progress_cb), self, 0); -+ self->transaction_verify_start_id = -+ g_signal_connect_object (rpm_proxy, "transaction_verify_start", -+ G_CALLBACK (gs_dnf5_transaction_verify_start_cb), self, 0); -+ self->transaction_verify_stop_id = -+ g_signal_connect_object (rpm_proxy, "transaction_verify_stop", -+ G_CALLBACK (gs_dnf5_transaction_verify_stop_cb), self, 0); -+ -+ return self; -+} -diff --git a/plugins/dnf5/gs-dnf5-progress-helper.h b/plugins/dnf5/gs-dnf5-progress-helper.h -new file mode 100644 -index 000000000..5d522468c ---- /dev/null -+++ b/plugins/dnf5/gs-dnf5-progress-helper.h -@@ -0,0 +1,31 @@ -+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- -+ * vi:set noexpandtab tabstop=8 shiftwidth=8: -+ * -+ * Copyright (C) 2024 Red Hat -+ * -+ * SPDX-License-Identifier: GPL-2.0+ -+ */ -+ -+#pragma once -+ -+#include -+#include -+ -+#include -+ -+#include "gs-dnf5-generated.h" -+ -+G_BEGIN_DECLS -+ -+#define GS_TYPE_DNF5_PROGRESS_HELPER (gs_dnf5_progress_helper_get_type ()) -+ -+G_DECLARE_FINAL_TYPE (GsDnf5ProgressHelper, gs_dnf5_progress_helper, GS, DNF5_PROGRESS_HELPER, GObject) -+ -+GsDnf5ProgressHelper * -+ gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy, -+ GsDnf5RpmRpm *rpm_proxy, -+ GsApp *progress_app, -+ GsAppList *progress_list, -+ const gchar *session_object_path); -+ -+G_END_DECLS -diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c -new file mode 100644 -index 000000000..82f3e19a8 ---- /dev/null -+++ b/plugins/dnf5/gs-plugin-dnf5.c -@@ -0,0 +1,4589 @@ -+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- -+ * vi:set noexpandtab tabstop=8 shiftwidth=8: -+ * -+ * Copyright (C) 2024 Red Hat -+ * -+ * SPDX-License-Identifier: GPL-2.0+ -+ */ -+ -+/* -+ * SECTION: dnf5 plugin. -+ * -+ * Talks to dnf5 to manage packages through dnf. -+ */ -+ -+#include -+ -+#include -+#include -+ -+#include "gs-app-private.h" -+ -+#include "gs-worker-thread.h" -+#include "gs-dnf5-generated.h" -+#include "gs-dnf5-rpm-generated.h" -+#include "gs-dnf5-progress-helper.h" -+ -+#include "gs-plugin-dnf5.h" -+ -+#include "../packagekit/gs-markdown.h" -+ -+#define GS_DNF5_RELEASEVER_DEFAULT NULL -+#define GS_DNF5_INTERFACE_RPM_DNF "org.rpm.dnf.v0" -+#define GS_DNF5_OBJECT_PATH_RPM_DNF "/org/rpm/dnf/v0" -+ -+#define GS_DNF5_ADVISORIES_KEY "gs-dnf5::advisories" -+#define GS_DNF5_CHANGELOGS_KEY "gs-dnf5::changelogs" -+ -+/* for how long a session can be left opened until it's auto-closed */ -+#define GS_SESSION_LIFETIME_SECS (5 * 60) -+ -+struct _GsPluginDnf5 -+{ -+ GsPlugin parent; -+ -+ GsWorkerThread *worker; /* (owned) */ -+ GDBusConnection *connection; -+ GsDnf5RpmTransaction *rpm_transaction_proxy; -+ guint rpm_transaction_watch_id; -+ gint calling_rpm; -+ -+ struct _SessionData { -+ GMutex mutex; -+ GCond cond; -+ guint autoclose_timer; -+ GsDnf5SessionManager *proxy; -+ gchar *object_path; -+ guint n_used; -+ gint needs_reset; -+ } session_data; -+ -+ struct _DependencySizesData { -+ GMutex mutex; -+ GPtrArray *apps; /* (element-type GsApp) */ -+ GCancellable *cancellable; /* non-NULL, when an op is running */ -+ } dependency_sizes_data; -+}; -+ -+G_DEFINE_TYPE (GsPluginDnf5, gs_plugin_dnf5, GS_TYPE_PLUGIN) -+ -+#define assert_in_worker(self) \ -+ g_assert (gs_worker_thread_is_in_worker_context (self->worker)) -+ -+static gint -+gs_dnf5_get_priority_for_interactivity (gboolean interactive) -+{ -+ return interactive ? G_PRIORITY_DEFAULT : G_PRIORITY_LOW; -+} -+ -+static void -+gs_dnf5_convert_error (GError **error) -+{ -+ gboolean not_authorized; -+ if (error == NULL || *error == NULL) -+ return; -+ -+ /* The message is not localized in the dnf5, thus this should work */ -+ not_authorized = strstr ((*error)->message, "GDBus.Error:org.rpm.dnf.v0.Error: Not authorized") != NULL; -+ -+ g_dbus_error_strip_remote_error (*error); -+ gs_utils_error_convert_gdbus (error); -+ if (not_authorized && g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) { -+ (*error)->domain = GS_PLUGIN_ERROR; -+ (*error)->code = GS_PLUGIN_ERROR_AUTH_REQUIRED; -+ } else if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) || ( -+ !gs_utils_error_convert_gdbus (error) && -+ !gs_utils_error_convert_gio (error))) { -+ (*error)->domain = GS_PLUGIN_ERROR; -+ (*error)->code = GS_PLUGIN_ERROR_FAILED; -+ } -+} -+ -+static void -+gs_dnf5_report_error (GsPluginDnf5 *self, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GsApp *app, -+ const GError *error, -+ gboolean interactive) -+{ -+ g_autoptr(GsPluginEvent) event = NULL; -+ -+ if (!event_callback) -+ return; -+ -+ event = gs_plugin_event_new ("error", error, -+ app ? "app" : NULL, app, -+ NULL); -+ if (interactive) -+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INTERACTIVE); -+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING); -+ event_callback (GS_PLUGIN (self), event, event_user_data); -+} -+ -+/* The session reset is used to refresh information about the packages, -+ like after a package install/uninstall, it might not be noticed by -+ the daemon internal structures that the package is installed/uninstalled. */ -+static void -+gs_dnf5_mark_session_needs_reset (GsPluginDnf5 *self) -+{ -+ g_atomic_int_set (&self->session_data.needs_reset, 1); -+} -+ -+static void -+gs_dnf5_rpm_start_transaction_cb (GsDnf5RpmTransaction *proxy, -+ const gchar *dbcookie, -+ guint transaction_id, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = user_data; -+ -+ g_debug ("%s: %p: dbcookie:'%s' transaction_id:%u", G_STRFUNC, self, dbcookie, transaction_id); -+} -+ -+static void -+gs_dnf5_rpm_end_transaction_cb (GsDnf5RpmTransaction *proxy, -+ const gchar *dbcookie, -+ guint transaction_id, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = user_data; -+ -+ g_debug ("%s: %p: dbcookie:'%s' transaction_id:%u", G_STRFUNC, self, dbcookie, transaction_id); -+ -+ /* the transactions are for install/uninstall, thus -+ mark the connection to require a reset() */ -+ gs_dnf5_mark_session_needs_reset (self); -+ -+ /* also do not know what precisely changed, thus a heavy hammer */ -+ gs_plugin_reload (GS_PLUGIN (self)); -+} -+ -+static void -+gs_dnf5_got_rpm_transaction_proxy_cb (GObject *source_object, -+ GAsyncResult *result, -+ gpointer user_data) -+{ -+ g_autoptr(GsPluginDnf5) self = user_data; -+ g_autoptr(GError) local_error = NULL; -+ -+ self->rpm_transaction_proxy = gs_dnf5_rpm_transaction_proxy_new_finish (result, &local_error); -+ -+ if (self->rpm_transaction_proxy != NULL) { -+ g_debug ("Opened RPM Transaction D-Bus proxy"); -+ -+ g_signal_connect_object (self->rpm_transaction_proxy, "start-transaction", -+ G_CALLBACK (gs_dnf5_rpm_start_transaction_cb), self, 0); -+ g_signal_connect_object (self->rpm_transaction_proxy, "end-transaction", -+ G_CALLBACK (gs_dnf5_rpm_end_transaction_cb), self, 0); -+ } else { -+ g_debug ("Failed to open RPM Transaction proxy: %s", local_error ? local_error->message : "Unknown error"); -+ } -+} -+ -+static void -+gs_dnf5_rpm_transaction_appeared_cb (GDBusConnection *connection, -+ const gchar *name, -+ const gchar *name_owner, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = user_data; -+ -+ g_debug ("%s: name:'%s' owner:'%s' existing-proxy:%p calling_rpm:%d", -+ G_STRFUNC, name, name_owner, self->rpm_transaction_proxy, -+ g_atomic_int_get (&self->calling_rpm)); -+ -+ if (name_owner != NULL && *name_owner != '\0' && !g_atomic_int_get (&self->calling_rpm)) { -+ g_clear_object (&self->rpm_transaction_proxy); -+ gs_dnf5_rpm_transaction_proxy_new (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ "org.rpm.announce", -+ "/org/rpm/Transaction", -+ NULL, -+ gs_dnf5_got_rpm_transaction_proxy_cb, -+ g_object_ref (self)); -+ } -+} -+ -+static void -+gs_dnf5_rpm_transaction_vanished_cb (GDBusConnection *connection, -+ const gchar *name, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = user_data; -+ -+ g_debug ("%s: name:'%s' existing-proxy:%p", G_STRFUNC, name, self->rpm_transaction_proxy); -+ -+ g_clear_object (&self->rpm_transaction_proxy); -+} -+ -+static void -+gs_dnf5_bus_get_cb (GObject *source_object, -+ GAsyncResult *result, -+ gpointer user_data) -+{ -+ g_autoptr(GTask) task = user_data; -+ g_autoptr(GError) local_error = NULL; -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (g_task_get_source_object (task)); -+ -+ self->connection = g_bus_get_finish (result, &local_error); -+ gs_dnf5_convert_error (&local_error); -+ -+ if (self->connection != NULL) { -+ self->rpm_transaction_watch_id = g_bus_watch_name_on_connection (self->connection, -+ "org.rpm.announce", -+ G_BUS_NAME_WATCHER_FLAGS_NONE, -+ gs_dnf5_rpm_transaction_appeared_cb, -+ gs_dnf5_rpm_transaction_vanished_cb, -+ self, -+ NULL); -+ } -+ -+ if (self->connection != NULL) -+ g_task_return_boolean (task, TRUE); -+ else -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+} -+ -+static void -+gs_plugin_dnf5_setup_async (GsPlugin *plugin, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ g_autoptr(GTask) task = NULL; -+ -+ g_debug ("dnf5 setup"); -+ -+ /* Start up a worker thread to process all the plugin’s function calls. */ -+ self->worker = gs_worker_thread_new ("gs-plugin-dnf5"); -+ -+ task = g_task_new (plugin, cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_setup_async); -+ -+ g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, gs_dnf5_bus_get_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_setup_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+static void -+gs_dnf5_shutdown_cb (GObject *source_object, -+ GAsyncResult *result, -+ gpointer user_data) -+{ -+ g_autoptr(GTask) task = user_data; -+ GsPluginDnf5 *self = g_task_get_source_object (task); -+ g_autoptr(GsWorkerThread) worker = NULL; -+ g_autoptr(GError) local_error = NULL; -+ -+ worker = g_steal_pointer (&self->worker); -+ -+ if (!gs_worker_thread_shutdown_finish (worker, result, &local_error)) -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ else -+ g_task_return_boolean (task, TRUE); -+} -+ -+static void -+gs_plugin_dnf5_shutdown_async (GsPlugin *plugin, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ g_autoptr(GTask) task = NULL; -+ g_autoptr(GCancellable) dependency_sizes_cancellable = NULL; -+ -+ task = g_task_new (plugin, cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_shutdown_async); -+ -+ /* stop any dependency sizes ongoing operation */ -+ g_mutex_lock (&self->dependency_sizes_data.mutex); -+ if (self->dependency_sizes_data.cancellable) -+ dependency_sizes_cancellable = g_object_ref (self->dependency_sizes_data.cancellable); -+ g_mutex_unlock (&self->dependency_sizes_data.mutex); -+ if (dependency_sizes_cancellable != NULL) -+ g_cancellable_cancel (dependency_sizes_cancellable); -+ -+ /* Stop the worker thread. */ -+ gs_worker_thread_shutdown_async (self->worker, cancellable, gs_dnf5_shutdown_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_shutdown_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+static void -+gs_dnf5_close_session_real (GsDnf5SessionManager *proxy, -+ const gchar *session_path) -+{ -+ g_autoptr(GError) local_error = NULL; -+ gboolean result = FALSE; -+ -+ /* Not passing operation's cancellable, because it can be cancelled by the user, -+ but the session needs to be closed. */ -+ if (gs_dnf5_session_manager_call_close_session_sync (proxy, session_path, &result, NULL, &local_error)) -+ g_warn_if_fail (result); -+ else -+ g_debug ("Failed to close session: %s", local_error->message); -+} -+ -+static gboolean -+gs_dnf5_autoclose_session_cb (gpointer user_data) -+{ -+ GsPluginDnf5 *self = user_data; -+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); -+ -+ self->session_data.autoclose_timer = 0; -+ if (self->session_data.n_used == 0 && self->session_data.proxy != NULL) { -+ gs_dnf5_close_session_real (self->session_data.proxy, self->session_data.object_path); -+ -+ g_clear_object (&self->session_data.proxy); -+ g_clear_pointer (&self->session_data.object_path, g_free); -+ } -+ -+ return G_SOURCE_REMOVE; -+} -+ -+static gboolean -+gs_dnf5_reset_session_sync (GsPluginDnf5 *self, -+ const gchar *session_path, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autoptr(GsDnf5Base) base_proxy = NULL; -+ g_autofree gchar *error_msg = NULL; -+ gboolean success = FALSE; -+ -+ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ if (base_proxy == NULL) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Base proxy: "); -+ return FALSE; -+ } -+ -+ if (!gs_dnf5_base_call_reset_sync (base_proxy, &success, &error_msg, cancellable, error)) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to call Base::reset(): "); -+ return FALSE; -+ } -+ -+ if (!success) { -+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to execute Base::reset(): %s", error_msg ? error_msg : "Unknown error"); -+ return FALSE; -+ } -+ -+ return TRUE; -+} -+ -+static gchar * /* (transfer full) */ -+gs_dnf5_open_session (GsPluginDnf5 *self, -+ const gchar *releasever, -+ GsDnf5SessionManager **out_proxy, /* (transfer full) */ -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autofree gchar *session_path = NULL; -+ -+ if (releasever == GS_DNF5_RELEASEVER_DEFAULT) { -+ if (self->session_data.autoclose_timer) { -+ g_source_remove (self->session_data.autoclose_timer); -+ self->session_data.autoclose_timer = 0; -+ } -+ -+ if (self->session_data.proxy != NULL) { -+ GsDnf5SessionManager *proxy = self->session_data.proxy; -+ -+ while (g_atomic_int_get (&self->session_data.needs_reset) && -+ !g_cancellable_is_cancelled (cancellable)) { -+ g_autoptr(GError) local_error = NULL; -+ if (self->session_data.n_used > 0) { -+ g_debug ("Existing session needs reset, waiting for opened operations to finish"); -+ while (self->session_data.n_used && g_atomic_int_get (&self->session_data.needs_reset)) { -+ g_cond_wait_until (&self->session_data.cond, &self->session_data.mutex, -+ /* check whether was cancelled up to three times per second */ -+ g_get_monotonic_time () + (G_TIME_SPAN_SECOND / 3)); -+ -+ if (g_cancellable_set_error_if_cancelled (cancellable, error)) -+ return NULL; -+ } -+ } -+ -+ if (g_cancellable_set_error_if_cancelled (cancellable, error)) -+ return NULL; -+ -+ if (g_atomic_int_get (&self->session_data.needs_reset)) { -+ gboolean success; -+ -+ success = gs_dnf5_reset_session_sync (self, self->session_data.object_path, cancellable, &local_error); -+ g_atomic_int_set (&self->session_data.needs_reset, 0); -+ -+ /* let any waiter for session reset know the conditions changed */ -+ g_cond_broadcast (&self->session_data.cond); -+ -+ if (success) { -+ g_debug ("Successfully reset existing session"); -+ } else { -+ g_debug ("Failed to reset existing session: %s", local_error ? local_error->message : "Unknown error"); -+ break; -+ } -+ } else { -+ g_debug ("Wanted to reset the session, but someone else did that already, thus skipping the reset"); -+ break; -+ } -+ } -+ -+ if (g_cancellable_set_error_if_cancelled (cancellable, error)) -+ return NULL; -+ -+ if (proxy == self->session_data.proxy) { -+ self->session_data.n_used++; -+ g_debug ("Using existing session"); -+ *out_proxy = g_object_ref (self->session_data.proxy); -+ return g_strdup (self->session_data.object_path); -+ } -+ } -+ } -+ -+ g_debug ("Creating new session"); -+ *out_proxy = gs_dnf5_session_manager_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_NONE, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ GS_DNF5_OBJECT_PATH_RPM_DNF, -+ cancellable, -+ error); -+ -+ if (*out_proxy == NULL) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to get Session Manager: "); -+ return NULL; -+ } -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ -+ if (releasever != GS_DNF5_RELEASEVER_DEFAULT) { -+ g_variant_builder_add (options_builder, "{sv}", "releasever", -+ g_variant_new_string (releasever)); -+ } else { -+ g_autoptr(GVariantBuilder) config = NULL; -+ -+ config = g_variant_builder_new (G_VARIANT_TYPE ("a{ss}")); -+ g_variant_builder_add (config, "{ss}", "optional_metadata_types", -+ "appstream"); -+ -+ g_variant_builder_add (options_builder, "{sv}", "config", -+ g_variant_builder_end (config)); -+ } -+ -+ if (!gs_dnf5_session_manager_call_open_session_sync (*out_proxy, -+ g_variant_builder_end (options_builder), -+ &session_path, -+ cancellable, -+ error)) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to open session: "); -+ return NULL; -+ } -+ -+ if (releasever == GS_DNF5_RELEASEVER_DEFAULT) { -+ g_assert (self->session_data.proxy == NULL); -+ self->session_data.proxy = g_object_ref (*out_proxy); -+ self->session_data.object_path = g_strdup (session_path); -+ self->session_data.n_used = 1; -+ } -+ -+ return g_steal_pointer (&session_path); -+} -+ -+static void -+gs_dnf5_close_session (GsPluginDnf5 *self, -+ GsDnf5SessionManager *proxy, -+ const gchar *session_path) -+{ -+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); -+ -+ if (self->session_data.proxy == proxy) { -+ g_assert (self->session_data.n_used > 0); -+ self->session_data.n_used--; -+ -+ /* let any waiter for session reset know the n_used changed */ -+ g_cond_broadcast (&self->session_data.cond); -+ -+ if (self->session_data.n_used == 0) { -+ g_assert (self->session_data.autoclose_timer == 0); -+ self->session_data.autoclose_timer = g_timeout_add_seconds (GS_SESSION_LIFETIME_SECS, gs_dnf5_autoclose_session_cb, self); -+ } -+ } else { -+ gs_dnf5_close_session_real (proxy, session_path); -+ } -+} -+ -+/* Returns whether to continue */ -+typedef gboolean (* GsDnf5ForeachFunc) (GsPluginDnf5 *self, -+ GVariant *value, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error); -+ -+static gboolean -+gs_dnf5_foreach_item (GsPluginDnf5 *self, -+ GVariant *array, -+ GsDnf5ForeachFunc func, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GVariantIter iter; -+ GVariant *child; -+ -+ g_variant_iter_init (&iter, array); -+ while (child = g_variant_iter_next_value (&iter), child != NULL) { -+ g_autoptr(GError) local_error = NULL; -+ gboolean done; -+ -+ done = !func (self, child, user_data, cancellable, &local_error); -+ g_variant_unref (child); -+ -+ if (local_error) { -+ g_propagate_error (error, g_steal_pointer (&local_error)); -+ return FALSE; -+ } -+ -+ if (g_cancellable_set_error_if_cancelled (cancellable, error)) -+ return FALSE; -+ -+ if (done) -+ break; -+ } -+ -+ return TRUE; -+} -+ -+static void -+gs_dnf5_app_set_str (GVariantDict *dict, -+ const gchar *key, -+ GsApp *app, -+ void (*set_func) (GsApp *app, -+ const gchar *value)) -+{ -+ g_autoptr(GVariant) value = NULL; -+ -+ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_STRING); -+ if (value != NULL) -+ set_func (app, g_variant_get_string (value, NULL)); -+} -+ -+static void -+gs_dnf5_app_set_str2 (GVariantDict *dict, -+ const gchar *key, -+ GsApp *app, -+ void (*set_func) (GsApp *app, -+ GsAppQuality quality, -+ const gchar *value)) -+{ -+ g_autoptr(GVariant) value = NULL; -+ -+ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_STRING); -+ if (value != NULL) -+ set_func (app, GS_APP_QUALITY_NORMAL, g_variant_get_string (value, NULL)); -+} -+ -+static void -+gs_dnf5_app_set_size (GVariantDict *dict, -+ const gchar *key, -+ GsApp *app, -+ void (*set_func) (GsApp *app, -+ GsSizeType size_type, -+ guint64 size_bytes)) -+{ -+ g_autoptr(GVariant) value = NULL; -+ -+ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_UINT64); -+ if (value != NULL) -+ set_func (app, GS_SIZE_TYPE_VALID, g_variant_get_uint64 (value)); -+} -+ -+static gchar * -+gs_dnf5_dup_version_from_dict (GVariantDict *dict) -+{ -+ g_autoptr(GVariant) version = NULL; -+ -+ version = g_variant_dict_lookup_value (dict, "version", G_VARIANT_TYPE_STRING); -+ if (version != NULL) { -+ const gchar *version_str = g_variant_get_string (version, NULL); -+ if (version_str != NULL && *version_str != '\0') { -+ g_autoptr(GVariant) release = NULL; -+ const gchar *release_str = NULL; -+ release = g_variant_dict_lookup_value (dict, "release", G_VARIANT_TYPE_STRING); -+ if (release != NULL) -+ release_str = g_variant_get_string (release, NULL); -+ if (release_str != NULL && *release_str != '\0') { -+ g_autofree gchar *version_release_str = g_strconcat (version_str, "-", release_str, NULL); -+ return g_steal_pointer (&version_release_str); -+ } else { -+ return g_strdup (version_str); -+ } -+ } -+ } -+ -+ return NULL; -+} -+ -+static void -+gs_dnf5_app_set_version (GVariantDict *dict, -+ GsApp *app, -+ void (*set_func) (GsApp *app, -+ const gchar *value)) -+{ -+ g_autofree gchar *ver_str = gs_dnf5_dup_version_from_dict (dict); -+ if (ver_str != NULL) -+ set_func (app, ver_str); -+} -+ -+static void -+gs_plugin_dnf5_set_packaging_format (GsApp *app) -+{ -+ gs_app_set_metadata (app, "GnomeSoftware::PackagingFormat", "RPM"); -+ gs_app_set_metadata (app, "GnomeSoftware::PackagingBaseCssColor", "error_color"); -+} -+ -+static void -+gs_dnf5_update_app_state (GsApp *app, -+ GsAppState state, -+ GVariantDict *dict) -+{ -+ if (state == GS_APP_STATE_UNKNOWN) { -+ g_autoptr(GVariant) value = NULL; -+ value = g_variant_dict_lookup_value (dict, "is_installed", G_VARIANT_TYPE_BOOLEAN); -+ if (value == NULL) -+ return; -+ if (g_variant_get_boolean (value)) -+ state = GS_APP_STATE_INSTALLED; -+ else -+ state = GS_APP_STATE_AVAILABLE; -+ } -+ /* The state can be only installed, available or updatable */ -+ if (gs_app_get_state (app) == state || -+ gs_app_get_state (app) == GS_APP_STATE_UPDATABLE) -+ return; -+ if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) -+ gs_app_set_state (app, state); -+ else if (gs_app_get_state (app) != GS_APP_STATE_INSTALLED || -+ state == GS_APP_STATE_UPDATABLE) -+ gs_app_set_state (app, state); -+} -+ -+typedef enum { -+ GS_DNF5_READ_PACKAGE_FLAG_NONE = 0, -+ GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED = 1 << 0, -+ GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS = 1 << 1 -+} GsDnf5ReadPackageFlags; -+ -+static void -+gs_dnf5_update_app_changelogs (GsApp *app, -+ GsDnf5ReadPackageFlags flags, -+ GVariantDict *dict) -+{ -+ GVariantIter iter; -+ GVariant *child; -+ g_autoptr(GString) changes = NULL; -+ g_autoptr(GVariant) changelogs = NULL; -+ -+ changelogs = g_variant_dict_lookup_value (dict, "changelogs", NULL); -+ if (changelogs == NULL) -+ return; -+ -+ g_variant_iter_init (&iter, changelogs); -+ while (child = g_variant_iter_next_value (&iter), child != NULL) { -+ gint64 when = 0; -+ const gchar *whom = NULL; -+ const gchar *what = NULL; -+ -+ g_variant_get (child, "(x&s&s)", &when, &whom, &what); -+ -+ if (whom != NULL && what != NULL) { -+ g_autofree gchar *tmp = NULL; -+ const gchar *eml_start, *eml_end; -+ -+ /* Hide the email address in the changes */ -+ eml_start = strchr (whom, '<'); -+ eml_end = strrchr (whom, '>'); -+ if (eml_start != NULL && eml_start < eml_end) { -+ if (g_ascii_isspace (eml_end[1])) -+ eml_end++; -+ tmp = g_malloc0 (strlen (whom) + 1); -+ strncpy (tmp, whom, eml_start - whom); -+ strcat (tmp, eml_end + 1); -+ whom = tmp; -+ } -+ -+ if (changes == NULL) -+ changes = g_string_new (""); -+ else -+ g_string_append (changes, "\n\n"); -+ g_string_append (changes, whom); -+ g_string_append_c (changes, '\n'); -+ g_string_append (changes, what); -+ } -+ -+ g_variant_unref (child); -+ } -+ -+ if (changes != NULL) { -+ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS) != 0) -+ gs_app_set_update_details_text (app, changes->str); -+ else -+ gs_app_set_metadata (app, GS_DNF5_CHANGELOGS_KEY, changes->str); -+ } -+} -+ -+static GsApp * /* (transfer full) */ -+gs_dnf5_read_package_from_dict (GsPluginDnf5 *self, -+ GVariantDict *dict, -+ GsDnf5ReadPackageFlags flags, -+ GsAppState set_state) -+{ -+ g_autoptr(GsApp) app = NULL; -+ g_autoptr(GVariant) value = NULL; -+ g_autofree gchar *name = NULL; -+ -+ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED) != 0) { -+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); -+ if (value != NULL) { -+ const gchar *name_const = g_variant_get_string (value, NULL); -+ app = gs_plugin_cache_lookup (GS_PLUGIN (self), name_const); -+ if (app == NULL) -+ name = g_strdup (name_const); -+ g_clear_pointer (&value, g_variant_unref); -+ } -+ } -+ -+ if (app == NULL) { -+ app = gs_app_new (NULL); -+ gs_app_set_management_plugin (app, GS_PLUGIN (self)); -+ gs_app_set_metadata (app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); -+ gs_plugin_dnf5_set_packaging_format (app); -+ gs_app_set_kind (app, AS_COMPONENT_KIND_GENERIC); -+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE); -+ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM); -+ -+ gs_dnf5_app_set_str (dict, "name", app, gs_app_add_source); -+ gs_dnf5_app_set_str (dict, "nevra", app, gs_app_add_source_id); -+ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_name); -+ gs_dnf5_app_set_str2 (dict, "summary", app, gs_app_set_summary); -+ gs_dnf5_app_set_str2 (dict, "description", app, gs_app_set_description); -+ gs_dnf5_app_set_str2 (dict, "license", app, gs_app_set_license); -+ gs_dnf5_app_set_size (dict, "install_size", app, gs_app_set_size_installed); -+ gs_dnf5_app_set_size (dict, "download_size", app, gs_app_set_size_download); -+ -+ value = g_variant_dict_lookup_value (dict, "url", G_VARIANT_TYPE_STRING); -+ if (value != NULL) { -+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, g_variant_get_string (value, NULL)); -+ g_clear_pointer (&value, g_variant_unref); -+ } -+ -+ value = g_variant_dict_lookup_value (dict, "buildtime", G_VARIANT_TYPE_UINT64); -+ if (value != NULL) { -+ gs_app_set_install_date (app, (gint64) g_variant_get_uint64 (value)); -+ g_clear_pointer (&value, g_variant_unref); -+ } -+ -+ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED) != 0) -+ gs_plugin_cache_add (GS_PLUGIN (self), name, app); -+ } -+ -+ gs_dnf5_update_app_changelogs (app, flags, dict); -+ gs_dnf5_update_app_state (app, set_state, dict); -+ -+ if (set_state == GS_APP_STATE_UPDATABLE) { -+ gs_dnf5_app_set_version (dict, app, gs_app_set_update_version); -+ gs_app_add_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT); -+ } else { -+ gs_dnf5_app_set_version (dict, app, gs_app_set_version); -+ } -+ -+ return g_steal_pointer (&app); -+} -+ -+typedef struct { -+ GsAppList *list; -+ GsAppState set_state; -+ GHashTable *nevra_to_app; /* (nullable): gchar *nevra ~> GsApp * */ -+ GHashTable *replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> gchar *old_version */ -+ GHashTable *used_replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> NULL */ -+ gboolean cover_unused_replaces; -+ gint64 timestamp; -+} ReadPackageData; -+ -+static gboolean -+gs_dnf5_read_package_cb (GsPluginDnf5 *self, -+ GVariant *package_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ ReadPackageData *rpd = user_data; -+ GsAppList *list = rpd->list; -+ GHashTable *nevra_to_app = rpd->nevra_to_app; -+ g_autoptr(GsApp) app = NULL; -+ g_autoptr(GVariantDict) dict = NULL; -+ -+ dict = g_variant_dict_new (package_array); -+ -+ app = gs_dnf5_read_package_from_dict (self, dict, GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED | GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS, rpd->set_state); -+ -+ if (nevra_to_app != NULL) { -+ g_autoptr(GVariant) value = NULL; -+ value = g_variant_dict_lookup_value (dict, "nevra", G_VARIANT_TYPE_STRING); -+ if (value != NULL) { -+ const gchar *nevra_const = g_variant_get_string (value, NULL); -+ if (nevra_const != NULL && *nevra_const != '\0') -+ g_hash_table_insert (nevra_to_app, g_strdup (nevra_const), g_object_ref (app)); -+ } -+ } -+ -+ gs_app_list_add (list, app); -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_read_repo_cb (GsPluginDnf5 *self, -+ GVariant *package_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GsAppList *list = user_data; -+ g_autoptr(GsApp) app = NULL; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) value = NULL; -+ const gchar *id; -+ -+ dict = g_variant_dict_new (package_array); -+ -+ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_STRING); -+ if (value == NULL) -+ return TRUE; -+ -+ id = g_variant_get_string (value, NULL); -+ if (id == NULL || *id == '\0') -+ return TRUE; -+ -+ /* Skip sub-repos */ -+ if (g_str_has_suffix (id, "-source") || -+ g_str_has_suffix (id, "-debuginfo") || -+ g_str_has_suffix (id, "-testing")) -+ return TRUE; -+ -+ g_clear_pointer (&value, g_variant_unref); -+ id = NULL; -+ -+ app = gs_app_new (NULL); -+ gs_app_set_management_plugin (app, GS_PLUGIN (self)); -+ gs_app_set_kind (app, AS_COMPONENT_KIND_REPOSITORY); -+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE); -+ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM); -+ gs_app_add_quirk (app, GS_APP_QUIRK_NOT_LAUNCHABLE); -+ gs_app_set_metadata (app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); -+ gs_plugin_dnf5_set_packaging_format (app); -+ gs_app_set_metadata (app, "GnomeSoftware::SortKey", "300"); -+ gs_app_set_origin_ui (app, _("Packages")); -+ -+ gs_dnf5_app_set_str (dict, "id", app, gs_app_set_id); -+ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_name); -+ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_description); -+ -+ value = g_variant_dict_lookup_value (dict, "enabled", G_VARIANT_TYPE_BOOLEAN); -+ if (value != NULL) { -+ gs_app_set_state (app, g_variant_get_boolean (value) ? -+ GS_APP_STATE_INSTALLED : GS_APP_STATE_AVAILABLE); -+ g_clear_pointer (&value, g_variant_unref); -+ } else { -+ gs_app_set_state (app, GS_APP_STATE_INSTALLED); -+ } -+ -+ gs_app_list_add (list, app); -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_refine_from_advisory_packages_cb (GsPluginDnf5 *self, -+ GVariant *array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GHashTable *nevra_to_app = user_data; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) var_nevra = NULL; -+ -+ dict = g_variant_dict_new (array); -+ -+ var_nevra = g_variant_dict_lookup_value (dict, "nevra", G_VARIANT_TYPE_STRING); -+ if (var_nevra != NULL) { -+ const gchar *nevra = g_variant_get_string (var_nevra, NULL); -+ if (nevra != NULL) { -+ GsApp *app; -+ -+ app = g_hash_table_lookup (nevra_to_app, nevra); -+ if (app != NULL) -+ gs_app_set_update_urgency (app, AS_URGENCY_KIND_CRITICAL); -+ } -+ } -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_refine_from_advisory_collections_cb (GsPluginDnf5 *self, -+ GVariant *array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GHashTable *nevra_to_app = user_data; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) packages = NULL; -+ gboolean res = TRUE; -+ -+ dict = g_variant_dict_new (array); -+ -+ packages = g_variant_dict_lookup_value (dict, "packages", G_VARIANT_TYPE_ARRAY); -+ if (packages != NULL) -+ res = gs_dnf5_foreach_item (self, packages, gs_dnf5_refine_from_advisory_packages_cb, nevra_to_app, cancellable, error); -+ -+ return res; -+} -+ -+static gboolean -+gs_dnf5_refine_from_advisory_cb (GsPluginDnf5 *self, -+ GVariant *array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GHashTable *nevra_to_app = user_data; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) collections = NULL; -+ g_autoptr(GVariant) severity = NULL; -+ gboolean res = TRUE; -+ -+ dict = g_variant_dict_new (array); -+ -+ /* ensure only advisories with severity 'critical' are traversed */ -+ severity = g_variant_dict_lookup_value (dict, "severity", G_VARIANT_TYPE_STRING); -+ if (severity == NULL || g_variant_get_string (severity, NULL) == NULL || -+ g_ascii_strcasecmp (g_variant_get_string (severity, NULL), "critical") != 0) -+ return TRUE; -+ -+ collections = g_variant_dict_lookup_value (dict, "collections", G_VARIANT_TYPE_ARRAY); -+ if (collections != NULL) -+ res = gs_dnf5_foreach_item (self, collections, gs_dnf5_refine_from_advisory_collections_cb, nevra_to_app, cancellable, error); -+ -+ return res; -+} -+ -+static GVariant * -+gs_dnf5_dup_package_attrs (void) -+{ -+ g_autoptr(GVariantBuilder) attrs_builder = NULL; -+ -+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (attrs_builder, "s", "nevra"); -+ g_variant_builder_add (attrs_builder, "s", "name"); -+ g_variant_builder_add (attrs_builder, "s", "epoch"); -+ g_variant_builder_add (attrs_builder, "s", "version"); -+ g_variant_builder_add (attrs_builder, "s", "release"); -+ g_variant_builder_add (attrs_builder, "s", "arch"); -+ g_variant_builder_add (attrs_builder, "s", "repo_id"); -+ g_variant_builder_add (attrs_builder, "s", "install_size"); -+ g_variant_builder_add (attrs_builder, "s", "download_size"); -+ g_variant_builder_add (attrs_builder, "s", "is_installed"); -+ g_variant_builder_add (attrs_builder, "s", "summary"); -+ g_variant_builder_add (attrs_builder, "s", "url"); -+ g_variant_builder_add (attrs_builder, "s", "license"); -+ g_variant_builder_add (attrs_builder, "s", "description"); -+ g_variant_builder_add (attrs_builder, "s", "buildtime"); -+ g_variant_builder_add (attrs_builder, "s", "changelogs"); -+ -+ return g_variant_builder_end (attrs_builder); -+} -+ -+static void -+gs_dnf5_gather_async_result_cb (GObject *source_object, -+ GAsyncResult *result, -+ gpointer user_data) -+{ -+ GAsyncResult **out_result = user_data; -+ *out_result = g_object_ref (result); -+} -+ -+static void -+gs_dnf5_confirm_key_response_cb (GObject *source_object, -+ GAsyncResult *result, -+ gpointer user_data) -+{ -+ g_autoptr(GError) local_error = NULL; -+ -+ if (!gs_dnf5_rpm_repo_call_confirm_key_finish (GS_DNF5_RPM_REPO (source_object), result, &local_error)) -+ g_debug ("Failed to confirm key: %s", local_error->message); -+} -+ -+typedef struct { -+ gchar *title; -+ gchar *msg; -+ gchar *details; -+ gchar *accept_label; -+ gchar *key_id; -+ gchar *key_filename; -+ gchar *session_path; -+ GCancellable *cancellable; /* (owned) */ -+ GsPluginDnf5 *self; /* (owned) */ -+ GWeakRef repo_proxy_weakref; /* GsDnf5RpmRepo */ -+ gboolean interactive; -+ gboolean confirmed_by_repo; -+} QuestionData; -+ -+static void -+question_data_free (QuestionData *data) -+{ -+ if (data != NULL) { -+ g_free (data->title); -+ g_free (data->msg); -+ g_free (data->details); -+ g_free (data->accept_label); -+ g_free (data->key_id); -+ g_free (data->key_filename); -+ g_free (data->session_path); -+ g_clear_object (&data->cancellable); -+ g_clear_object (&data->self); -+ g_weak_ref_clear (&data->repo_proxy_weakref); -+ g_free (data); -+ } -+} -+ -+static gboolean -+gs_dnf5_ask_question_idle_cb (gpointer user_data) -+{ -+ QuestionData *data = user_data; -+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; -+ -+ repo_proxy = g_weak_ref_get (&data->repo_proxy_weakref); -+ if (repo_proxy != NULL) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ gboolean confirmed; -+ -+ /* auto-accept new keys, just as PacakgeKit plugin used to do */ -+ confirmed = data->confirmed_by_repo; -+ if (!confirmed) -+ confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean (data->interactive)); -+ -+ gs_dnf5_rpm_repo_call_confirm_key_with_options (repo_proxy, -+ data->key_id, -+ confirmed, -+ g_variant_builder_end (options_builder), -+ data->cancellable, -+ gs_dnf5_confirm_key_response_cb, -+ NULL); -+ } -+ -+ return G_SOURCE_REMOVE; -+} -+ -+static gboolean -+gs_dnf5_read_package_repo_ids_cb (GsPluginDnf5 *self, -+ GVariant *package_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GPtrArray **out_repo_ids = user_data; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) value = NULL; -+ -+ dict = g_variant_dict_new (package_array); -+ -+ value = g_variant_dict_lookup_value (dict, "from_repo_id", G_VARIANT_TYPE_STRING); -+ if (value != NULL) { -+ const gchar *repo_id_const = g_variant_get_string (value, NULL); -+ if (repo_id_const != NULL && *repo_id_const != '\0') { -+ if (*out_repo_ids == NULL) -+ *out_repo_ids = g_ptr_array_new_with_free_func (g_free); -+ g_ptr_array_add (*out_repo_ids, g_strdup (repo_id_const)); -+ } -+ } -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_read_repo_gpg_info_cb (GsPluginDnf5 *self, -+ GVariant *package_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ guint *out_n_good = user_data; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) id_value = NULL; -+ g_autoptr(GVariant) gpgcheck_value = NULL; -+ -+ dict = g_variant_dict_new (package_array); -+ -+ id_value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_STRING); -+ gpgcheck_value = g_variant_dict_lookup_value (dict, "gpgcheck", G_VARIANT_TYPE_BOOLEAN); -+ -+ if (id_value != NULL && gpgcheck_value != NULL) { -+ gboolean gpgcheck = g_variant_get_boolean (gpgcheck_value); -+ -+ if (gpgcheck) -+ *out_n_good = (*out_n_good) + 1; -+ } -+ -+ return TRUE; -+} -+ -+static gpointer -+gs_dnf5_check_key_source_thread (gpointer user_data) -+{ -+ QuestionData *data = user_data; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ g_autoptr(GPtrArray) repo_ids = NULL; -+ g_autoptr(GError) local_error = NULL; -+ gboolean success; -+ -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (data->self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ data->session_path, -+ data->cancellable, -+ &local_error); -+ success = rpm_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); -+ } -+ -+ /* try to get whether the key comes from any package */ -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariantBuilder) package_attrs_builder = NULL; -+ g_autoptr(GVariantBuilder) whatprovides_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ -+ package_attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (package_attrs_builder, "s", "from_repo_id"); -+ -+ whatprovides_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (whatprovides_builder, "s", data->key_filename); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ g_variant_builder_end (package_attrs_builder)); -+ g_variant_builder_add (options_builder, "{sv}", "whatprovides", -+ g_variant_builder_end (whatprovides_builder)); -+ g_variant_builder_add (options_builder, "{sv}", "scope", -+ g_variant_new_string ("installed")); -+ -+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ data->cancellable, -+ &local_error); -+ -+ if (success) { -+ success = gs_dnf5_foreach_item (data->self, result, gs_dnf5_read_package_repo_ids_cb, &repo_ids, data->cancellable, &local_error); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call rpm::list: "); -+ } -+ } -+ -+ if (success && repo_ids != NULL) { -+ g_autoptr(GVariantBuilder) patterns_builder = NULL; -+ guint n_verify = 0; -+ -+ for (guint i = 0; i < repo_ids->len; i++) { -+ const gchar *repo_id = g_ptr_array_index (repo_ids, i); -+ /* those are special repos, like `@commandline` */ -+ if (*repo_id != '@') { -+ if (patterns_builder == NULL) -+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (patterns_builder, "s", repo_id); -+ n_verify++; -+ } -+ } -+ -+ if (patterns_builder != NULL) { -+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; -+ -+ repo_proxy = g_weak_ref_get (&data->repo_proxy_weakref); -+ if (repo_proxy != NULL) { -+ g_autoptr(GVariantBuilder) repo_attrs_builder = NULL; -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ -+ repo_attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (repo_attrs_builder, "s", "id"); -+ g_variant_builder_add (repo_attrs_builder, "s", "gpgcheck"); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "patterns", -+ g_variant_builder_end (patterns_builder)); -+ g_variant_builder_add (options_builder, "{sv}", "repo_attrs", -+ g_variant_builder_end (repo_attrs_builder)); -+ -+ success = gs_dnf5_rpm_repo_call_list_sync (repo_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ data->cancellable, -+ &local_error); -+ -+ if (success) { -+ guint n_good = 0; -+ -+ success = gs_dnf5_foreach_item (data->self, result, gs_dnf5_read_repo_gpg_info_cb, &n_good, data->cancellable, &local_error); -+ -+ data->confirmed_by_repo = success && n_verify == n_good; -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call repo::list: "); -+ } -+ } else { -+ success = FALSE; -+ g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, "Repo proxy vanished"); -+ } -+ } -+ } -+ -+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free); -+ -+ return NULL; -+} -+ -+typedef struct { -+ GsPluginDnf5 *self; /* (not owned) */ -+ const gchar *session_path; /* (not owned) */ -+ GsDnf5RpmRepo *repo_proxy; /* (not owned) */ -+ GCancellable *cancellable; /* (not owned) */ -+ gboolean interactive; -+} RepoKeyImportData; -+ -+static void -+gs_dnf5_repo_key_import_request_cb (GsDnf5Base *object, -+ const gchar *arg_session_object_path, -+ const gchar *arg_key_id, -+ const gchar *const *arg_user_ids, -+ const gchar *arg_key_fingerprint, -+ const gchar *arg_key_url, -+ gint64 arg_timestamp, -+ RepoKeyImportData *repo_key_import_data) -+{ -+ QuestionData *data; -+ g_autoptr(GString) user_ids = NULL; -+ g_autofree gchar *key_filename = NULL; -+ g_autoptr(GString) str_fingerprint = NULL; -+ GString *details = NULL; -+ const gchar *fingerprint; -+ const gchar *from; -+ -+ if (g_strcmp0 (repo_key_import_data->session_path, arg_session_object_path) != 0) -+ return; -+ -+ user_ids = g_string_new (NULL); -+ for (guint i = 0; arg_user_ids != NULL && arg_user_ids[i] != NULL; i++) { -+ if (i > 0) -+ g_string_append (user_ids, ", "); -+ g_string_append_c (user_ids, '"'); -+ g_string_append (user_ids, arg_user_ids[i]); -+ g_string_append_c (user_ids, '"'); -+ } -+ -+ g_debug ("%s: key_id:'%s' user_ids:[%s] key_fingerprint:'%s' key_url:'%s' timestamp:%" G_GINT64_FORMAT, -+ G_STRFUNC, arg_key_id, user_ids->str, arg_key_fingerprint, arg_key_url, arg_timestamp); -+ -+ if (g_ascii_strncasecmp (arg_key_url, "file:", 5) == 0) -+ key_filename = g_filename_from_uri (arg_key_url, NULL, NULL); -+ -+ if ((strlen (arg_key_fingerprint) % 4) == 0) { -+ str_fingerprint = g_string_new (arg_key_fingerprint); -+ for (gint i = str_fingerprint->len - 4; i > 0; i -= 4) { -+ g_string_insert_c (str_fingerprint, i, ' '); -+ } -+ } -+ -+ details = g_string_new (""); -+ -+ from = key_filename != NULL ? key_filename : arg_key_url; -+ fingerprint = str_fingerprint != NULL ? str_fingerprint->str : arg_key_fingerprint; -+ -+ #define add_nonempty_line(_format, _value) G_STMT_START { \ -+ if ((_value) != NULL && *(_value) != '\0') { \ -+ g_string_append_printf (details, _format, _value); \ -+ g_string_append_c (details, '\n'); \ -+ } \ -+ } G_STMT_END -+ -+ /* Translators: the '%s' is replaced with the key user name */ -+ add_nonempty_line (_("Key user: %s"), user_ids->str); -+ /* Translators: the '%s' is replaced with the key fingerprint, a few hex digits */ -+ add_nonempty_line (_("Fingerprint: %s"), fingerprint); -+ /* Translators: the '%s' is replaced with the local path or a URI to the key */ -+ add_nonempty_line (_("From: %s"), from); -+ -+ #undef add_nonempty_line -+ -+ data = g_new0 (QuestionData, 1); -+ data->title = g_strdup (_("Import Key")); -+ /* Translators: the '%s' is replaced with the key ID, usually a few hex digits */ -+ data->msg = g_strdup_printf (_("Do you want to import key %s?"), arg_key_id); -+ data->details = g_string_free (details, FALSE); -+ data->accept_label = g_strdup (_("_Import Key")); -+ data->key_id = g_strdup (arg_key_id); -+ data->key_filename = g_steal_pointer (&key_filename); -+ data->session_path = g_strdup (repo_key_import_data->session_path); -+ data->self = g_object_ref (repo_key_import_data->self); -+ data->interactive = repo_key_import_data->interactive; -+ data->confirmed_by_repo = FALSE; -+ g_set_object (&data->cancellable, repo_key_import_data->cancellable); -+ g_weak_ref_init (&data->repo_proxy_weakref, repo_key_import_data->repo_proxy); -+ -+ if (data->key_filename != NULL) -+ g_thread_unref (g_thread_new ("gs-dnf5-check-key-source_thread", gs_dnf5_check_key_source_thread, data)); -+ else -+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free); -+} -+ -+static void -+gs_dnf5_goal_cancelled_cb (GObject *source_object, -+ GAsyncResult *result, -+ gpointer user_data) -+{ -+ g_autoptr(GCancellable) run_cancellable = user_data; -+ g_autoptr(GError) local_error = NULL; -+ g_autofree gchar *error_message = NULL; -+ gboolean succeeded = FALSE; -+ -+ if (gs_dnf5_goal_call_cancel_finish (GS_DNF5_GOAL (source_object), &succeeded, &error_message, result, &local_error)) { -+ if (succeeded) { -+ g_debug ("%s: Transaction cancelled by the user", G_STRFUNC); -+ g_cancellable_cancel (run_cancellable); -+ } else { -+ g_debug ("%s: Cannot cancel transaction: %s", G_STRFUNC, error_message); -+ } -+ } else if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { -+ g_debug ("%s: Failed to cancel transaction: %s", G_STRFUNC, local_error->message); -+ } -+} -+ -+static gboolean -+gs_dnf5_reset_transaction_sync (GsPluginDnf5 *self, -+ const gchar *session_path, -+ GsDnf5RpmRpm *rpm_proxy, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autoptr(GsDnf5Goal) goal_proxy = NULL; -+ -+ goal_proxy = gs_dnf5_goal_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ if (goal_proxy == NULL) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Goal proxy: "); -+ return FALSE; -+ } -+ -+ return gs_dnf5_goal_call_reset_sync (goal_proxy, cancellable, error); -+} -+ -+/* this is needed to receive D-Bus signals about progress in the worker thread */ -+static gboolean -+gs_dnf5_goal_call_do_transaction_sync_wrapper (GsDnf5Goal *proxy, -+ GVariant *arg_options, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autoptr(GMainContext) main_context = g_main_context_ref_thread_default (); -+ g_autoptr(GAsyncResult) async_result = NULL; -+ gs_dnf5_goal_call_do_transaction (proxy, arg_options, cancellable, gs_dnf5_gather_async_result_cb, &async_result); -+ while (!async_result) -+ g_main_context_iteration (main_context, TRUE); -+ return gs_dnf5_goal_call_do_transaction_finish (proxy, async_result, error); -+} -+ -+typedef struct { -+ GsDnf5Goal *goal_proxy; -+ GCancellable *run_cancellable; -+} TransactionData; -+ -+static void -+gs_dnf5_transaction_cancelled_cb (GCancellable *parent_cancellable, -+ TransactionData *transaction_data) -+{ -+ gs_dnf5_goal_call_cancel (transaction_data->goal_proxy, transaction_data->run_cancellable, -+ gs_dnf5_goal_cancelled_cb, g_object_ref (transaction_data->run_cancellable)); -+} -+ -+typedef enum { -+ GS_DNF5_TRANSACTION_FLAG_NONE = 0, -+ GS_DNF5_TRANSACTION_FLAG_OFFLINE = 1 << 0, -+ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY = 1 << 1, -+ GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING = 1 << 2, /* negative flag, to not have it everywhere */ -+ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE = 1 << 3, -+ GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY = 1 << 4 -+} GsDnf5TransactionFlags; -+ -+static gboolean -+gs_dnf5_run_transaction (GsPluginDnf5 *self, -+ const gchar *session_path, -+ GsDnf5RpmRpm *rpm_proxy, -+ GsApp *progress_app, -+ GsAppList *progress_list, -+ GsDnf5TransactionFlags flags, -+ GVariant **out_transaction, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autoptr(GsDnf5Base) base_proxy = NULL; -+ g_autoptr(GsDnf5Goal) goal_proxy = NULL; -+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) transaction = NULL; -+ g_autoptr(GsDnf5ProgressHelper) helper = NULL; -+ g_autoptr(GCancellable) run_cancellable = NULL; -+ RepoKeyImportData repo_key_import_data; -+ TransactionData transaction_data; -+ gulong repo_key_import_id; -+ gulong cancellable_id = 0; -+ guint result = 0; -+ gboolean interactive = (flags & GS_DNF5_TRANSACTION_FLAG_INTERACTIVE) != 0; -+ gboolean success; -+ -+ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ if (base_proxy == NULL) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Base proxy: "); -+ return FALSE; -+ } -+ -+ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ if (repo_proxy == NULL) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Repo proxy: "); -+ return FALSE; -+ } -+ -+ goal_proxy = gs_dnf5_goal_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ if (goal_proxy == NULL) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Goal proxy: "); -+ return FALSE; -+ } -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (goal_proxy), G_MAXINT); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ -+ g_variant_builder_add (options_builder, "{sv}", "allow_erasing", -+ g_variant_new_boolean ((flags & GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING) == 0)); -+ -+ if (progress_app != NULL || progress_list != NULL) -+ helper = gs_dnf5_progress_helper_new (base_proxy, rpm_proxy, progress_app, progress_list, session_path); -+ -+ repo_key_import_data.self = self; -+ repo_key_import_data.session_path = session_path; -+ repo_key_import_data.repo_proxy = repo_proxy; -+ repo_key_import_data.interactive = interactive; -+ repo_key_import_data.cancellable = cancellable; -+ repo_key_import_id = g_signal_connect (base_proxy, "repo_key_import_request", -+ G_CALLBACK (gs_dnf5_repo_key_import_request_cb), &repo_key_import_data); -+ -+ if (!gs_dnf5_goal_call_resolve_sync (goal_proxy, -+ g_variant_builder_end (options_builder), -+ &transaction, -+ &result, -+ cancellable, -+ error)) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to resolve transaction: "); -+ g_signal_handler_disconnect (base_proxy, repo_key_import_id); -+ return FALSE; -+ } -+ -+ if (out_transaction != NULL && transaction != NULL) -+ *out_transaction = g_variant_ref_sink (transaction); -+ -+ /* 0 ... all fine, 1 ... there are warnings, 2 ... failed */ -+ if (result == 2) { -+ g_auto(GStrv) problems = NULL; -+ -+ if (gs_dnf5_goal_call_get_transaction_problems_string_sync (goal_proxy, &problems, cancellable, error) && -+ problems != NULL) { -+ /* extra space at the end, but no harm, it reuses translated string */ -+ GString *msg = g_string_new ("Failed to resolve transaction: "); -+ for (guint i = 0; problems[i] != NULL; i++) { -+ g_string_append_c (msg, '\n'); -+ g_string_append (msg, problems[i]); -+ } -+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, msg->str); -+ } else { -+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, -+ _("Unknown error")); -+ g_prefix_error (error, "Failed to resolve transaction: "); -+ } -+ g_signal_handler_disconnect (base_proxy, repo_key_import_id); -+ return FALSE; -+ } -+ -+ g_clear_pointer (&options_builder, g_variant_builder_unref); -+ -+ if ((flags & GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY) != 0) { -+ g_signal_handler_disconnect (base_proxy, repo_key_import_id); -+ return TRUE; -+ } -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ if ((flags & GS_DNF5_TRANSACTION_FLAG_OFFLINE) != 0) { -+ g_variant_builder_add (options_builder, "{sv}", "offline", -+ g_variant_new_boolean (TRUE)); -+ } -+ if ((flags & GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY) != 0) { -+ g_variant_builder_add (options_builder, "{sv}", "downloadonly", -+ g_variant_new_boolean (TRUE)); -+ } -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean (interactive)); -+ -+ run_cancellable = g_cancellable_new (); -+ if (cancellable != NULL) { -+ transaction_data.goal_proxy = goal_proxy; -+ transaction_data.run_cancellable = run_cancellable; -+ -+ cancellable_id = g_cancellable_connect (cancellable, G_CALLBACK (gs_dnf5_transaction_cancelled_cb), -+ &transaction_data, NULL); -+ } -+ -+ success = gs_dnf5_goal_call_do_transaction_sync_wrapper (goal_proxy, -+ g_variant_builder_end (options_builder), -+ run_cancellable, -+ error); -+ -+ g_cancellable_disconnect (cancellable, cancellable_id); -+ g_signal_handler_disconnect (base_proxy, repo_key_import_id); -+ -+ /* in case there is a pending async call result for the cancel() invocation */ -+ g_cancellable_cancel (run_cancellable); -+ -+ if (!success) { -+ g_auto(GStrv) problems = NULL; -+ -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to run transaction: "); -+ -+ if (error != NULL && *error != NULL && -+ gs_dnf5_goal_call_get_transaction_problems_string_sync (goal_proxy, &problems, cancellable, NULL) && -+ problems != NULL) { -+ GString *msg = g_string_new ((*error)->message); -+ for (guint i = 0; problems[i] != NULL; i++) { -+ g_string_append_c (msg, '\n'); -+ g_string_append (msg, problems[i]); -+ } -+ g_clear_error (error); -+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, msg->str); -+ } -+ } -+ -+ return success; -+} -+ -+enum OpKindType { OP_KIND_UNKNOWN, OP_KIND_INSTALL, OP_KIND_UPGRADE, OP_KIND_DOWNGRADE, OP_KIND_REINSTALL, OP_KIND_REMOVE, OP_KIND_REPLACED }; -+ -+static enum OpKindType -+gs_dnf5_get_package_op_kind (GVariant *item_array) -+{ -+ struct _op_kinds { -+ const gchar *name; -+ enum OpKindType kind; -+ } op_kinds[] = { -+ { "Install", OP_KIND_INSTALL }, -+ { "Upgrade", OP_KIND_UPGRADE }, -+ { "Downgrade", OP_KIND_DOWNGRADE }, -+ { "Reinstall", OP_KIND_REINSTALL }, -+ { "Remove", OP_KIND_REMOVE }, -+ { "Replaced", OP_KIND_REPLACED } -+ }; -+ const gchar *str = NULL; -+ -+ g_variant_get_child (item_array, 0, "&s", &str); -+ if (str == NULL || g_ascii_strcasecmp (str, "package") != 0) -+ return OP_KIND_UNKNOWN; -+ -+ g_variant_get_child (item_array, 1, "&s", &str); -+ if (str == NULL) -+ return OP_KIND_UNKNOWN; -+ -+ for (guint i = 0; i < G_N_ELEMENTS (op_kinds); i++) { -+ if (g_ascii_strcasecmp (str, op_kinds[i].name) == 0) { -+ return op_kinds[i].kind; -+ } -+ } -+ -+ return OP_KIND_UNKNOWN; -+} -+ -+static gboolean -+gs_dnf5_gather_update_replaces_cb (GsPluginDnf5 *self, -+ GVariant *item_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ ReadPackageData *rpd = user_data; -+ enum OpKindType op_kind = gs_dnf5_get_package_op_kind (item_array); -+ -+ if (op_kind == OP_KIND_REPLACED && rpd->replaces) { -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) value = NULL; -+ -+ value = g_variant_get_child_value (item_array, 4); -+ dict = g_variant_dict_new (value); -+ g_clear_pointer (&value, g_variant_unref); -+ -+ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_INT32); -+ if (value != NULL) { -+ gint32 id = g_variant_get_int32 (value); -+ gchar *version = gs_dnf5_dup_version_from_dict (dict); -+ if (version != NULL) -+ g_hash_table_insert (rpd->replaces, GINT_TO_POINTER (id), version); -+ } -+ } -+ -+ return TRUE; -+} -+ -+typedef struct _UpdateReplacesData { -+ GsApp *app; -+ GHashTable *replaces; -+ GHashTable *used_replaces; -+} UpdateReplacesData; -+ -+static gboolean -+gs_dnf5_gather_update_replaced_pkgs_cb (GsPluginDnf5 *self, -+ GVariant *item_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ UpdateReplacesData *upd = user_data; -+ gint32 replaces_id = g_variant_get_int32 (item_array); -+ const gchar *old_version = g_hash_table_lookup (upd->replaces, GINT_TO_POINTER (replaces_id)); -+ if (old_version != NULL) { -+ gs_app_set_version (upd->app, old_version); -+ g_hash_table_add (upd->used_replaces, GINT_TO_POINTER (replaces_id)); -+ } -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_gather_updates_from_transaction_cb (GsPluginDnf5 *self, -+ GVariant *item_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ ReadPackageData *rpd = user_data; -+ enum OpKindType op_kind = gs_dnf5_get_package_op_kind (item_array); -+ const gchar *str; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) value = NULL; -+ g_autoptr(GsApp) upd_app = NULL; -+ g_autoptr(GString) nevra = NULL; -+ GsApp *known_app; -+ -+ if (op_kind == OP_KIND_UNKNOWN || (!rpd->cover_unused_replaces && op_kind == OP_KIND_REPLACED) || -+ (rpd->cover_unused_replaces && op_kind != OP_KIND_REPLACED)) -+ return TRUE; -+ -+ value = g_variant_get_child_value (item_array, 4); -+ dict = g_variant_dict_new (value); -+ g_clear_pointer (&value, g_variant_unref); -+ -+ if (rpd->cover_unused_replaces) { -+ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_INT32); -+ if (value != NULL) { -+ gint32 id = g_variant_get_int32 (value); -+ if (g_hash_table_contains (rpd->used_replaces, GINT_TO_POINTER (id))) -+ return TRUE; -+ } else { -+ return TRUE; -+ } -+ -+ g_clear_pointer (&value, g_variant_unref); -+ } -+ -+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); -+ if (value == NULL) -+ return TRUE; -+ str = g_variant_get_string (value, NULL); -+ if (str == NULL) -+ return TRUE; -+ -+ nevra = g_string_new (str); -+ -+ g_clear_pointer (&value, g_variant_unref); -+ value = g_variant_dict_lookup_value (dict, "evr", G_VARIANT_TYPE_STRING); -+ if (value == NULL) -+ return TRUE; -+ str = g_variant_get_string (value, NULL); -+ if (str == NULL) -+ return TRUE; -+ -+ g_string_append_c (nevra, '-'); -+ g_string_append (nevra, str); -+ -+ g_clear_pointer (&value, g_variant_unref); -+ value = g_variant_dict_lookup_value (dict, "arch", G_VARIANT_TYPE_STRING); -+ if (value == NULL) -+ return TRUE; -+ str = g_variant_get_string (value, NULL); -+ if (str == NULL) -+ return TRUE; -+ -+ g_string_append_c (nevra, '.'); -+ g_string_append (nevra, str); -+ -+ known_app = g_hash_table_lookup (rpd->nevra_to_app, nevra->str); -+ if (known_app != NULL) { -+ upd_app = g_object_ref (known_app); -+ if (gs_app_get_version (upd_app) == NULL) -+ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_version); -+ else -+ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_update_version); -+ -+ if (rpd->replaces != NULL) { -+ g_autoptr(GVariantDict) dict2 = NULL; -+ -+ g_clear_pointer (&value, g_variant_unref); -+ value = g_variant_get_child_value (item_array, 3); -+ dict2 = g_variant_dict_new (value); -+ g_clear_pointer (&value, g_variant_unref); -+ value = g_variant_dict_lookup_value (dict2, "replaces", G_VARIANT_TYPE_ARRAY); -+ if (value != NULL) { -+ UpdateReplacesData upd = { 0, }; -+ -+ upd.app = upd_app; -+ upd.replaces = rpd->replaces; -+ upd.used_replaces = rpd->used_replaces; -+ -+ if (!gs_dnf5_foreach_item (self, value, gs_dnf5_gather_update_replaced_pkgs_cb, &upd, cancellable, error)) -+ return FALSE; -+ } -+ } -+ } else { -+ upd_app = gs_app_new (NULL); -+ gs_app_set_management_plugin (upd_app, GS_PLUGIN (self)); -+ gs_app_set_metadata (upd_app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); -+ gs_plugin_dnf5_set_packaging_format (upd_app); -+ gs_app_set_kind (upd_app, AS_COMPONENT_KIND_GENERIC); -+ gs_app_set_bundle_kind (upd_app, AS_BUNDLE_KIND_PACKAGE); -+ gs_app_set_scope (upd_app, AS_COMPONENT_SCOPE_SYSTEM); -+ gs_app_add_quirk (upd_app, GS_APP_QUIRK_HIDE_EVERYWHERE); -+ gs_app_add_quirk (upd_app, GS_APP_QUIRK_NEEDS_REBOOT); -+ -+ gs_dnf5_app_set_str (dict, "name", upd_app, gs_app_add_source); -+ gs_dnf5_app_set_str2 (dict, "name", upd_app, gs_app_set_name); -+ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_version); -+ gs_app_add_source_id (upd_app, nevra->str); -+ -+ gs_app_list_add (rpd->list, upd_app); -+ g_hash_table_insert (rpd->nevra_to_app, g_string_free (g_steal_pointer (&nevra), FALSE), g_object_ref (upd_app)); -+ } -+ -+ gs_dnf5_app_set_size (dict, "install_size", upd_app, gs_app_set_size_installed); -+ gs_dnf5_app_set_size (dict, "download_size", upd_app, gs_app_set_size_download); -+ -+ switch (op_kind) { -+ case OP_KIND_INSTALL: -+ gs_app_set_state (upd_app, GS_APP_STATE_AVAILABLE); -+ break; -+ case OP_KIND_UPGRADE: -+ case OP_KIND_DOWNGRADE: -+ case OP_KIND_REINSTALL: -+ gs_app_set_state (upd_app, GS_APP_STATE_UPDATABLE); -+ break; -+ case OP_KIND_REMOVE: -+ case OP_KIND_REPLACED: -+ gs_app_set_state (upd_app, GS_APP_STATE_UNAVAILABLE); -+ gs_app_set_size_download (upd_app, GS_SIZE_TYPE_VALID, 0); -+ break; -+ case OP_KIND_UNKNOWN: -+ default: -+ g_warn_if_reached (); -+ break; -+ } -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_read_history_packages_cb (GsPluginDnf5 *self, -+ GVariant *array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ ReadPackageData *rpd = user_data; -+ g_autoptr(GsApp) app = NULL; -+ g_autoptr(GVariantDict) dict = NULL; -+ -+ dict = g_variant_dict_new (array); -+ -+ app = gs_dnf5_read_package_from_dict (self, dict, GS_DNF5_READ_PACKAGE_FLAG_NONE, rpd->set_state); -+ if (app != NULL) { -+ g_autoptr(GVariant) value = NULL; -+ value = g_variant_dict_lookup_value (dict, "original_evr", G_VARIANT_TYPE_STRING); -+ if (value != NULL) { -+ const gchar *str = g_variant_get_string (value, NULL); -+ if (str != NULL) { -+ const gchar *delim = strchr (str, ':'); -+ /* skip the epoch, if set, because it's skipped in the current version too */ -+ gs_app_set_version (app, delim ? delim + 1 : str); -+ } -+ } -+ -+ g_clear_pointer (&value, g_variant_unref); -+ value = g_variant_dict_lookup_value (dict, "advisories", G_VARIANT_TYPE_ARRAY); -+ if (value != NULL) { -+ gs_app_set_metadata_variant (app, GS_DNF5_ADVISORIES_KEY, value); -+ } -+ -+ /* do not know the exact change date/time, it uses the build time and the one from the settings as a fallback */ -+ if (!gs_app_get_install_date (app)) -+ gs_app_set_install_date (app, rpd->timestamp); -+ gs_app_list_add (rpd->list, app); -+ } -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_read_history_items_cb (GsPluginDnf5 *self, -+ GVariant *array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ struct _known_changes { -+ const gchar *type_of_change; -+ GsAppState set_state; -+ } known_changes[] = { -+ { "installed", GS_APP_STATE_UPDATABLE }, -+ { "removed", GS_APP_STATE_UNAVAILABLE }, -+ { "upgraded", GS_APP_STATE_UPDATABLE }, -+ { "downgraded", GS_APP_STATE_UPDATABLE } -+ }; -+ ReadPackageData *rpd = user_data; -+ g_autoptr(GVariant) value = NULL; -+ const gchar *type_of_change; -+ guint ii; -+ -+ value = g_variant_get_child_value (array, 0); -+ if (value == NULL) -+ return TRUE; -+ -+ type_of_change = g_variant_get_string (value, NULL); -+ if (!type_of_change) -+ return TRUE; -+ -+ for (ii = 0; ii < G_N_ELEMENTS (known_changes); ii++) { -+ if (g_strcmp0 (type_of_change, known_changes[ii].type_of_change) == 0) { -+ g_autoptr(GVariant) packages = NULL; -+ packages = g_variant_get_child_value (array, 1); -+ if (packages != NULL) { -+ rpd->set_state = known_changes[ii].set_state; -+ gs_dnf5_foreach_item (self, packages, gs_dnf5_read_history_packages_cb, rpd, cancellable, error); -+ } -+ break; -+ } -+ } -+ -+ return TRUE; -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_list_apps_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsAppQueryTristate is_for_update = GS_APP_QUERY_TRISTATE_UNSET; -+ GsAppQueryTristate is_historical_update = GS_APP_QUERY_TRISTATE_UNSET; -+ const AsComponentKind *component_kinds = NULL; -+ GsPluginListAppsData *data = task_data; -+ const gchar * const *provides_files = NULL; -+ const gchar *provides_tag = NULL; -+ GsAppQueryProvidesType provides_type = GS_APP_QUERY_PROVIDES_UNKNOWN; -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GsAppList) list = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ g_autoptr(GError) local_error = NULL; -+ gboolean success = TRUE; -+ -+ assert_in_worker (self); -+ -+ if (data->query != NULL) { -+ is_for_update = gs_app_query_get_is_for_update (data->query); -+ is_historical_update = gs_app_query_get_is_historical_update (data->query); -+ component_kinds = gs_app_query_get_component_kinds (data->query); -+ provides_files = gs_app_query_get_provides_files (data->query); -+ provides_type = gs_app_query_get_provides (data->query, &provides_tag); -+ } -+ -+ if ((is_for_update == GS_APP_QUERY_TRISTATE_UNSET && -+ is_historical_update == GS_APP_QUERY_TRISTATE_UNSET && -+ component_kinds == NULL && -+ provides_tag == NULL && -+ provides_files == NULL) || -+ is_for_update == GS_APP_QUERY_TRISTATE_FALSE || -+ is_historical_update == GS_APP_QUERY_TRISTATE_FALSE || -+ (component_kinds != NULL && !gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) || -+ gs_app_query_get_n_properties_set (data->query) != 1) { -+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, -+ "Unsupported query"); -+ return; -+ } -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ return; -+ } -+ -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = rpm_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); -+ } -+ -+ if (success) { -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ list = gs_app_list_new (); -+ -+ if (is_for_update == GS_APP_QUERY_TRISTATE_TRUE) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ gs_dnf5_dup_package_attrs ()); -+ g_variant_builder_add (options_builder, "{sv}", "with_provides", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_filenames", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_src", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "scope", -+ g_variant_new_string ("upgrades")); -+ -+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ &local_error); -+ -+ if (success) { -+ g_autoptr(GHashTable) nevra_to_app = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); -+ g_autoptr(GHashTable) replaces = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); -+ g_autoptr(GHashTable) used_replaces = g_hash_table_new (g_direct_hash, g_direct_equal); -+ ReadPackageData rpd = { 0, }; -+ rpd.list = list; -+ rpd.set_state = GS_APP_STATE_UPDATABLE; -+ rpd.nevra_to_app = nevra_to_app; -+ rpd.replaces = replaces; -+ rpd.used_replaces = used_replaces; -+ -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error); -+ -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_autoptr(GVariant) transaction = NULL; -+ const gchar * const packages[2] = { NULL, NULL }; -+ -+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error); -+ -+ success = success && -+ gs_dnf5_rpm_rpm_call_upgrade_sync (rpm_proxy, -+ packages, -+ g_variant_builder_end (options_builder2), -+ cancellable, -+ &local_error); -+ success = success && -+ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, NULL, -+ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY, -+ &transaction, cancellable, &local_error); -+ if (success && transaction != NULL) { -+ success = gs_dnf5_foreach_item (self, transaction, -+ gs_dnf5_gather_update_replaces_cb, &rpd, -+ cancellable, &local_error); -+ rpd.cover_unused_replaces = FALSE; -+ success = success && -+ gs_dnf5_foreach_item (self, transaction, -+ gs_dnf5_gather_updates_from_transaction_cb, &rpd, -+ cancellable, &local_error); -+ rpd.cover_unused_replaces = TRUE; -+ success = success && -+ gs_dnf5_foreach_item (self, transaction, -+ gs_dnf5_gather_updates_from_transaction_cb, &rpd, -+ cancellable, &local_error); -+ } -+ } -+ -+ if (success && g_hash_table_size (nevra_to_app) > 0) { -+ g_autoptr(GsDnf5Advisory) advisory_proxy = NULL; -+ g_autoptr(GError) local_error2 = NULL; -+ gboolean adv_success; -+ -+ advisory_proxy = gs_dnf5_advisory_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error2); -+ adv_success = advisory_proxy != NULL; -+ -+ if (adv_success) { -+ g_autoptr(GVariantBuilder) adv_options_builder = NULL; -+ g_autoptr(GVariantBuilder) attrs_builder = NULL; -+ g_autoptr(GVariantBuilder) severities_builder = NULL; -+ g_autoptr(GVariant) adv_result = NULL; -+ -+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (attrs_builder, "s", "severity"); -+ g_variant_builder_add (attrs_builder, "s", "collections"); -+ -+ severities_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (severities_builder, "s", "critical"); -+ -+ adv_options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (adv_options_builder, "{sv}", "advisory_attrs", -+ g_variant_builder_end (attrs_builder)); -+ g_variant_builder_add (adv_options_builder, "{sv}", "availability", -+ g_variant_new_string ("updates")); -+ g_variant_builder_add (adv_options_builder, "{sv}", "severities", -+ g_variant_builder_end (severities_builder)); -+ -+ adv_success = gs_dnf5_advisory_call_list_sync (advisory_proxy, -+ g_variant_builder_end (adv_options_builder), -+ &adv_result, -+ cancellable, -+ &local_error2); -+ if (adv_success) { -+ adv_success = gs_dnf5_foreach_item (self, adv_result, -+ gs_dnf5_refine_from_advisory_cb, nevra_to_app, -+ cancellable, &local_error2); -+ } else { -+ g_debug ("Failed to call Advisory::list: %s", local_error2->message); -+ } -+ } else { -+ g_debug ("Failed to create Advisory proxy: %s", local_error2->message); -+ } -+ } -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call Rpm::list: "); -+ } -+ } else if (is_historical_update == GS_APP_QUERY_TRISTATE_TRUE) { -+ g_autoptr(GSettings) settings = NULL; -+ gint64 timestamp; -+ -+ /* use the option the GUI part uses */ -+ settings = g_settings_new ("org.gnome.software"); -+ timestamp = (gint64) g_settings_get_uint64 (settings, "packagekit-historical-updates-timestamp"); -+ -+ if (timestamp > 0) { -+ g_autoptr(GsDnf5History) history_proxy = NULL; -+ -+ history_proxy = gs_dnf5_history_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = history_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create History proxy: "); -+ } -+ -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "since", -+ g_variant_new_int64 (timestamp)); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ gs_dnf5_dup_package_attrs ()); -+ -+ success = gs_dnf5_history_call_recent_changes_sync (history_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ &local_error); -+ -+ if (success) { -+ ReadPackageData rpd = { 0, }; -+ -+ rpd.list = list; -+ rpd.set_state = GS_APP_STATE_UNKNOWN; -+ rpd.nevra_to_app = NULL; -+ rpd.timestamp = timestamp; -+ -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_history_items_cb, &rpd, cancellable, &local_error); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call recent_changes: "); -+ } -+ } -+ } -+ } else if (gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) { -+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; -+ -+ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = repo_proxy != NULL; -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariantBuilder) attrs_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (repo_proxy), G_MAXINT); -+ -+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (attrs_builder, "s", "id"); -+ g_variant_builder_add (attrs_builder, "s", "name"); -+ g_variant_builder_add (attrs_builder, "s", "enabled"); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "repo_attrs", -+ g_variant_builder_end (attrs_builder)); -+ g_variant_builder_add (options_builder, "{sv}", "enable_disable", -+ g_variant_new_string ("all")); -+ -+ success = gs_dnf5_rpm_repo_call_list_sync (repo_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ &local_error); -+ -+ if (success) { -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_repo_cb, list, cancellable, &local_error); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call list: "); -+ } -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Rpm repo proxy: "); -+ } -+ } else if (provides_files != NULL) { -+ g_autoptr(GVariantBuilder) patterns_builder = NULL; -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ -+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ for (guint i = 0; provides_files[i] != NULL; i++) { -+ g_variant_builder_add (patterns_builder, "s", provides_files[i]); -+ } -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "scope", -+ g_variant_new_string ("all")); -+ g_variant_builder_add (options_builder, "{sv}", "with_nevra", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_provides", -+ g_variant_new_boolean (TRUE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_filenames", -+ g_variant_new_boolean (TRUE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_src", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ gs_dnf5_dup_package_attrs ()); -+ g_variant_builder_add (options_builder, "{sv}", "patterns", -+ g_variant_builder_end (patterns_builder)); -+ -+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ &local_error); -+ -+ if (success) { -+ ReadPackageData rpd = { 0, }; -+ rpd.list = list; -+ rpd.set_state = GS_APP_STATE_UNKNOWN; -+ rpd.nevra_to_app = NULL; -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call list: "); -+ } -+ } else if (provides_tag != NULL) { -+ g_autoptr(GVariantBuilder) patterns_builder = NULL; -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ g_autofree gchar *pattern1 = NULL, *pattern2 = NULL; -+ -+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ -+ switch (provides_type) { -+ case GS_APP_QUERY_PROVIDES_PACKAGE_NAME: -+ g_variant_builder_add (patterns_builder, "s", provides_tag); -+ break; -+ case GS_APP_QUERY_PROVIDES_GSTREAMER: -+ pattern1 = g_strdup_printf ("gstreamer0.10(%s)", provides_tag); -+ pattern2 = g_strdup_printf ("gstreamer1(%s)", provides_tag); -+ break; -+ case GS_APP_QUERY_PROVIDES_FONT: -+ pattern1 = g_strdup_printf ("font(%s)", provides_tag); -+ break; -+ case GS_APP_QUERY_PROVIDES_MIME_HANDLER: -+ pattern1 = g_strdup_printf ("mimehandler(%s)", provides_tag); -+ break; -+ case GS_APP_QUERY_PROVIDES_PS_DRIVER: -+ pattern1 = g_strdup_printf ("postscriptdriver(%s)", provides_tag); -+ break; -+ case GS_APP_QUERY_PROVIDES_PLASMA: -+ pattern1 = g_strdup_printf ("plasma4(%s)", provides_tag); -+ pattern2 = g_strdup_printf ("plasma5(%s)", provides_tag); -+ break; -+ case GS_APP_QUERY_PROVIDES_UNKNOWN: -+ default: -+ g_assert_not_reached (); -+ } -+ if (pattern1 != NULL) -+ g_variant_builder_add (patterns_builder, "s", pattern1); -+ if (pattern2 != NULL) -+ g_variant_builder_add (patterns_builder, "s", pattern2); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "scope", -+ g_variant_new_string ("all")); -+ g_variant_builder_add (options_builder, "{sv}", "with_nevra", -+ g_variant_new_boolean (provides_type == GS_APP_QUERY_PROVIDES_PACKAGE_NAME)); -+ g_variant_builder_add (options_builder, "{sv}", "with_provides", -+ g_variant_new_boolean (provides_type != GS_APP_QUERY_PROVIDES_PACKAGE_NAME)); -+ g_variant_builder_add (options_builder, "{sv}", "with_filenames", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_src", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ gs_dnf5_dup_package_attrs ()); -+ g_variant_builder_add (options_builder, "{sv}", "patterns", -+ g_variant_builder_end (patterns_builder)); -+ -+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ &local_error); -+ -+ if (success) { -+ ReadPackageData rpd = { 0, }; -+ rpd.list = list; -+ rpd.set_state = GS_APP_STATE_UNKNOWN; -+ rpd.nevra_to_app = NULL; -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call list: "); -+ } -+ } else { -+ g_assert_not_reached (); -+ } -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ if (success) { -+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_list_apps_async (GsPlugin *plugin, -+ GsAppQuery *query, -+ GsPluginListAppsFlags flags, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = gs_plugin_list_apps_data_new_task (plugin, query, flags, event_callback, event_user_data, -+ cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_list_apps_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_list_apps_thread_cb, g_steal_pointer (&task)); -+} -+ -+static GsAppList * -+gs_plugin_dnf5_list_apps_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_pointer (G_TASK (result), error); -+} -+ -+static gboolean -+gs_dnf5_gather_dependency_sizes_cb (GsPluginDnf5 *self, -+ GVariant *item_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GsApp *main_app = user_data; -+ const gchar *str; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) value = NULL; -+ g_autoptr(GsApp) dep_app = NULL; -+ -+ g_variant_get_child (item_array, 0, "&s", &str); -+ if (str == NULL || g_ascii_strcasecmp (str, "package") != 0) -+ return TRUE; -+ -+ g_variant_get_child (item_array, 2, "&s", &str); -+ if (str == NULL || g_ascii_strcasecmp (str, "dependency") != 0) -+ return TRUE; -+ -+ value = g_variant_get_child_value (item_array, 4); -+ dict = g_variant_dict_new (value); -+ g_clear_pointer (&value, g_variant_unref); -+ -+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); -+ if (value == NULL) -+ return TRUE; -+ str = g_variant_get_string (value, NULL); -+ if (str == NULL) -+ return TRUE; -+ /* the app itself is in the list too */ -+ if (g_strcmp0 (gs_app_get_default_source (main_app), str) == 0) -+ return TRUE; -+ -+ /* always create a new app, no cached can be used here */ -+ dep_app = gs_app_new (NULL); -+ gs_app_set_management_plugin (dep_app, GS_PLUGIN (self)); -+ gs_app_set_metadata (dep_app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self))); -+ gs_plugin_dnf5_set_packaging_format (dep_app); -+ gs_app_set_kind (dep_app, AS_COMPONENT_KIND_GENERIC); -+ gs_app_set_bundle_kind (dep_app, AS_BUNDLE_KIND_PACKAGE); -+ gs_app_set_scope (dep_app, AS_COMPONENT_SCOPE_SYSTEM); -+ gs_app_add_quirk (dep_app, GS_APP_QUIRK_HIDE_EVERYWHERE); -+ -+ gs_dnf5_app_set_str (dict, "name", dep_app, gs_app_add_source); -+ gs_dnf5_app_set_str (dict, "nevra", dep_app, gs_app_add_source_id); -+ gs_dnf5_app_set_str2 (dict, "name", dep_app, gs_app_set_name); -+ gs_dnf5_app_set_str2 (dict, "summary", dep_app, gs_app_set_summary); -+ gs_dnf5_app_set_str2 (dict, "description", dep_app, gs_app_set_description); -+ gs_dnf5_app_set_str2 (dict, "license", dep_app, gs_app_set_license); -+ gs_dnf5_app_set_size (dict, "install_size", dep_app, gs_app_set_size_installed); -+ gs_dnf5_app_set_size (dict, "download_size", dep_app, gs_app_set_size_download); -+ -+ gs_dnf5_update_app_state (dep_app, GS_APP_STATE_UNKNOWN, dict); -+ -+ gs_app_add_related (main_app, dep_app); -+ -+ return TRUE; -+} -+ -+static gpointer -+gs_dnf5_dependency_sizes_thread (gpointer thread_data) -+{ -+ g_autofree GWeakRef *weakref = thread_data; -+ g_autoptr(GsPluginDnf5) self = g_weak_ref_get (weakref); -+ g_autoptr(GCancellable) cancellable = NULL; -+ g_autoptr(GError) local_error = NULL; -+ guint index = 0; -+ -+ if (self == NULL) -+ return NULL; -+ -+ g_mutex_lock (&self->dependency_sizes_data.mutex); -+ -+ if (self->dependency_sizes_data.cancellable != NULL) -+ cancellable = g_object_ref (self->dependency_sizes_data.cancellable); -+ -+ while (self->dependency_sizes_data.apps != NULL && index < self->dependency_sizes_data.apps->len && -+ !g_cancellable_is_cancelled (self->dependency_sizes_data.cancellable)) { -+ GsApp *app = g_ptr_array_index (self->dependency_sizes_data.apps, index); -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ gboolean success; -+ -+ index++; -+ -+ if (gs_app_is_installed (app) || -+ gs_app_list_length (gs_app_get_related (app)) > 0) -+ continue; -+ -+ g_mutex_unlock (&self->dependency_sizes_data.mutex); -+ -+ /* need to use temporary session, because the goals are piled up and cannot be cancelled yet */ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); -+ -+ success = session_path != NULL; -+ if (!success) -+ gs_dnf5_convert_error (&local_error); -+ -+ if (success) { -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = rpm_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); -+ } -+ } -+ -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_autoptr(GVariant) transaction = NULL; -+ const gchar *package_name = gs_app_get_default_source (app); -+ const gchar *packages[2] = { NULL, NULL }; -+ -+ packages[0] = package_name; -+ -+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error); -+ -+ success = success && -+ gs_dnf5_rpm_rpm_call_install_sync (rpm_proxy, -+ (const gchar * const *) packages, -+ g_variant_builder_end (options_builder), -+ cancellable, -+ &local_error); -+ success = success && -+ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, NULL, -+ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY, -+ &transaction, cancellable, &local_error); -+ if (success && transaction != NULL) -+ success = gs_dnf5_foreach_item (self, transaction, gs_dnf5_gather_dependency_sizes_cb, app, cancellable, &local_error); -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ g_mutex_lock (&self->dependency_sizes_data.mutex); -+ -+ if (!success) -+ break; -+ } -+ -+ g_clear_pointer (&self->dependency_sizes_data.apps, g_ptr_array_unref); -+ g_clear_object (&self->dependency_sizes_data.cancellable); -+ g_mutex_unlock (&self->dependency_sizes_data.mutex); -+ -+ if (local_error != NULL) -+ g_debug ("Failed to get app dependency sizes: %s", local_error->message); -+ -+ return NULL; -+} -+ -+static gboolean -+gs_dnf5_get_dependency_sizes_sync (GsPluginDnf5 *self, -+ GHashTable *apps, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex); -+ GHashTableIter iter; -+ gpointer value = NULL; -+ gboolean need_thread = FALSE; -+ -+ g_hash_table_iter_init (&iter, apps); -+ while (g_hash_table_iter_next (&iter, NULL, &value)) { -+ GsApp *app = value; -+ GsAppList *related = gs_app_get_related (app); -+ -+ if (gs_app_has_quirk (app, GS_APP_QUIRK_HIDE_EVERYWHERE) || -+ gs_app_get_state (app) == GS_APP_STATE_AVAILABLE_LOCAL || -+ gs_app_is_installed (app) || -+ gs_app_list_length (related) > 0) -+ continue; -+ -+ /* getting list of the dependencies to be installed can take a long time, thus run it -+ in a new thread, to not block the refine job */ -+ if (self->dependency_sizes_data.apps == NULL) { -+ need_thread = TRUE; -+ self->dependency_sizes_data.apps = g_ptr_array_new_with_free_func (g_object_unref); -+ } -+ -+ if (!g_ptr_array_find (self->dependency_sizes_data.apps, app, NULL)) -+ g_ptr_array_add (self->dependency_sizes_data.apps, g_object_ref (app)); -+ } -+ -+ if (need_thread) { -+ GWeakRef *weakref = g_new0 (GWeakRef, 1); -+ -+ g_assert (self->dependency_sizes_data.cancellable == NULL); -+ self->dependency_sizes_data.cancellable = g_cancellable_new (); -+ -+ g_weak_ref_set (weakref, self); -+ -+ g_thread_unref (g_thread_new ("gs-dnf5-dependency-sizes", gs_dnf5_dependency_sizes_thread, g_steal_pointer (&weakref))); -+ } -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_refine_cb (GsPluginDnf5 *self, -+ GVariant *package_array, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GHashTable *apps = user_data; -+ const gchar *name; -+ GsApp *app; -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) value = NULL; -+ -+ dict = g_variant_dict_new (package_array); -+ -+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); -+ if (value == NULL) -+ return TRUE; -+ name = g_variant_get_string (value, NULL); -+ if (name == NULL) -+ return TRUE; -+ app = g_hash_table_lookup (apps, name); -+ if (app == NULL) -+ return TRUE; -+ -+ if (gs_app_has_management_plugin (app, NULL) && -+ gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE && -+ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) { -+ gs_app_set_management_plugin (app, GS_PLUGIN (self)); -+ gs_plugin_dnf5_set_packaging_format (app); -+ } -+ -+ gs_dnf5_app_set_version (dict, app, gs_app_set_version); -+ gs_dnf5_app_set_size (dict, "install_size", app, gs_app_set_size_installed); -+ gs_dnf5_app_set_size (dict, "download_size", app, gs_app_set_size_download); -+ -+ gs_dnf5_update_app_state (app, GS_APP_STATE_UNKNOWN, dict); -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_add_advisories_cb (GsPluginDnf5 *self, -+ GVariant *value, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GHashTable *advisories = user_data; /* gchar *name ~> gchar *description */ -+ g_autofree gchar *str = NULL; -+ -+ str = g_variant_dup_string (value, NULL); -+ if (str != NULL && *str != '\0') -+ g_hash_table_insert (advisories, g_steal_pointer (&str), NULL); -+ -+ return TRUE; -+} -+ -+static gboolean -+gs_dnf5_gather_update_description_for_advisory_cb (GsPluginDnf5 *self, -+ GVariant *value, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GHashTable *advisories = user_data; /* gchar *name ~> gchar *description */ -+ g_autoptr(GVariantDict) dict = NULL; -+ g_autoptr(GVariant) name = NULL; -+ g_autoptr(GVariant) description = NULL; -+ const gchar *name_str, *description_str; -+ -+ dict = g_variant_dict_new (value); -+ -+ name = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING); -+ if (name == NULL) -+ return TRUE; -+ -+ description = g_variant_dict_lookup_value (dict, "description", G_VARIANT_TYPE_STRING); -+ if (description == NULL) -+ return TRUE; -+ -+ name_str = g_variant_get_string (name, NULL); -+ description_str = g_variant_get_string (description, NULL); -+ -+ if (name_str != NULL && description_str != NULL && *description_str != '\0') -+ g_hash_table_replace (advisories, g_strdup (name_str), g_strdup (description_str)); -+ -+ return TRUE; -+} -+ -+typedef struct _AdvisoryUpdateDescriptionData { -+ GHashTable *advisories; /* gchar *name ~> gchar *description */ -+ GString *description; -+} AdvisoryUpdateDescriptionData; -+ -+static gboolean -+gs_dnf5_refine_update_description_from_advisory_cb (GsPluginDnf5 *self, -+ GVariant *value, -+ gpointer user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ AdvisoryUpdateDescriptionData *adv_data = user_data; -+ const gchar *name = NULL; -+ -+ name = g_variant_get_string (value, NULL); -+ if (name != NULL && *name != '\0') { -+ const gchar *description = g_hash_table_lookup (adv_data->advisories, name); -+ if (description != NULL && *description != '\0') { -+ if (adv_data->description == NULL) { -+ adv_data->description = g_string_new (description); -+ } else { -+ g_string_append (adv_data->description, "\n\n"); -+ g_string_append (adv_data->description, description); -+ } -+ } -+ } -+ -+ return TRUE; -+} -+ -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_refine_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPlugin *plugin = GS_PLUGIN (self); -+ GsPluginRefineData *data = task_data; -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GHashTable) apps = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ g_autoptr(GError) local_error = NULL; -+ gboolean success = TRUE; -+ -+ assert_in_worker (self); -+ -+ apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); -+ for (guint i = 0; i < gs_app_list_length (data->list); i++) { -+ GsApp *app = gs_app_list_index (data->list, i); -+ if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && ( -+ gs_app_get_state (app) == GS_APP_STATE_UNKNOWN || -+ (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS) != 0 || -+ ((data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_SIZE_DATA) != 0 && -+ gs_app_list_length (gs_app_get_related (app)) == 0))) { -+ if (gs_app_get_default_source (app) != NULL && -+ ((gs_app_has_management_plugin (app, NULL) && -+ gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE && -+ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) || -+ gs_app_has_management_plugin (app, plugin))) -+ g_hash_table_insert (apps, g_strdup (gs_app_get_default_source (app)), g_object_ref (app)); -+ } -+ } -+ -+ if (g_hash_table_size (apps) == 0) { -+ g_task_return_boolean (task, TRUE); -+ return; -+ } -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ return; -+ } -+ -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = rpm_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); -+ } -+ -+ if (success && (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS) != 0) { -+ g_autoptr(GHashTable) advisories = NULL; /* gchar *name ~> gchar *description */ -+ GHashTableIter iter; -+ gpointer value = NULL; -+ -+ data->require_flags = data->require_flags & (~GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS); -+ -+ advisories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); -+ -+ g_hash_table_iter_init (&iter, apps); -+ while (success && g_hash_table_iter_next (&iter, NULL, &value)) { -+ GsApp *app = value; -+ GVariant *advisories_array; -+ -+ advisories_array = gs_app_get_metadata_variant (app, GS_DNF5_ADVISORIES_KEY); -+ if (advisories_array != NULL) -+ success = gs_dnf5_foreach_item (self, advisories_array, gs_dnf5_add_advisories_cb, advisories, cancellable, &local_error); -+ } -+ -+ if (success && g_hash_table_size (advisories) > 0) { -+ g_autoptr(GsDnf5Advisory) advisory_proxy = NULL; -+ -+ advisory_proxy = gs_dnf5_advisory_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = advisory_proxy != NULL; -+ -+ if (success) { -+ g_autoptr(GVariantBuilder) adv_options_builder = NULL; -+ g_autoptr(GVariantBuilder) attrs_builder = NULL; -+ g_autoptr(GVariantBuilder) names_builder = NULL; -+ g_autoptr(GVariant) adv_result = NULL; -+ gpointer key = NULL; -+ -+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (attrs_builder, "s", "name"); -+ g_variant_builder_add (attrs_builder, "s", "description"); -+ -+ names_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_hash_table_iter_init (&iter, advisories); -+ while (g_hash_table_iter_next (&iter, &key, NULL)) { -+ const gchar *name = key; -+ g_variant_builder_add (names_builder, "s", name); -+ } -+ -+ adv_options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (adv_options_builder, "{sv}", "advisory_attrs", -+ g_variant_builder_end (attrs_builder)); -+ g_variant_builder_add (adv_options_builder, "{sv}", "names", -+ g_variant_builder_end (names_builder)); -+ g_variant_builder_add (adv_options_builder, "{sv}", "availability", -+ g_variant_new_string ("all")); -+ -+ success = gs_dnf5_advisory_call_list_sync (advisory_proxy, -+ g_variant_builder_end (adv_options_builder), -+ &adv_result, -+ cancellable, -+ &local_error); -+ if (success) { -+ success = gs_dnf5_foreach_item (self, adv_result, -+ gs_dnf5_gather_update_description_for_advisory_cb, advisories, -+ cancellable, &local_error); -+ if (success) { -+ g_hash_table_iter_init (&iter, apps); -+ while (success && g_hash_table_iter_next (&iter, NULL, &value)) { -+ GsApp *app = value; -+ GVariant *advisories_array; -+ -+ advisories_array = gs_app_get_metadata_variant (app, GS_DNF5_ADVISORIES_KEY); -+ if (advisories_array != NULL) { -+ AdvisoryUpdateDescriptionData adv_data = { NULL, }; -+ adv_data.advisories = advisories; -+ adv_data.description = NULL; -+ -+ success = gs_dnf5_foreach_item (self, advisories_array, -+ gs_dnf5_refine_update_description_from_advisory_cb, -+ &adv_data, cancellable, &local_error); -+ -+ if (adv_data.description) { -+ g_autoptr(GsMarkdown) markdown = gs_markdown_new (GS_MARKDOWN_OUTPUT_PANGO); -+ g_autofree gchar *markup = gs_markdown_parse (markdown, adv_data.description->str); -+ if (markup != NULL && *markup != '\0') -+ gs_app_set_update_details_markup (app, markup); -+ else -+ gs_app_set_update_details_text (app, adv_data.description->str); -+ g_string_free (adv_data.description, TRUE); -+ } -+ } -+ } -+ -+ if (!success) -+ g_debug ("Failed to refine update description from advisory: %s", local_error->message); -+ } else { -+ g_debug ("Failed to gather update description for advisory: %s", local_error->message); -+ } -+ } else { -+ g_debug ("Failed to call Advisory::list: %s", local_error->message); -+ } -+ } else { -+ g_debug ("Failed to create Advisory proxy: %s", local_error->message); -+ } -+ } -+ -+ /* fallback to changelogs where advisory was not known */ -+ g_hash_table_iter_init (&iter, apps); -+ while (success && g_hash_table_iter_next (&iter, NULL, &value)) { -+ GsApp *app = value; -+ if (!gs_app_get_update_details_markup (app)) { -+ const gchar *changelogs = gs_app_get_metadata_item (app, GS_DNF5_CHANGELOGS_KEY); -+ if (changelogs != NULL && *changelogs != '\0') { -+ gs_app_set_update_details_text (app, changelogs); -+ gs_app_set_metadata (app, GS_DNF5_CHANGELOGS_KEY, NULL); -+ } -+ } -+ } -+ } -+ -+ if (success && data->require_flags != 0) { -+ g_autoptr(GVariantBuilder) attrs_builder = NULL; -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariantBuilder) patterns_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ GHashTableIter iter; -+ gpointer key = NULL; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ -+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_hash_table_iter_init (&iter, apps); -+ while (g_hash_table_iter_next (&iter, &key, NULL)) { -+ const gchar *name = key; -+ g_variant_builder_add (patterns_builder, "s", name); -+ } -+ -+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (attrs_builder, "s", "name"); -+ g_variant_builder_add (attrs_builder, "s", "version"); -+ g_variant_builder_add (attrs_builder, "s", "install_size"); -+ g_variant_builder_add (attrs_builder, "s", "download_size"); -+ g_variant_builder_add (attrs_builder, "s", "is_installed"); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "patterns", -+ g_variant_builder_end (patterns_builder)); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ g_variant_builder_end (attrs_builder)); -+ g_variant_builder_add (options_builder, "{sv}", "with_nevra", -+ g_variant_new_boolean (TRUE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_provides", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_filenames", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_src", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "scope", -+ g_variant_new_string ("all")); -+ g_variant_builder_add (options_builder, "{sv}", "latest_limit", -+ g_variant_new_int32 (1)); -+ -+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ &local_error); -+ -+ if (success) { -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_refine_cb, apps, cancellable, &local_error); -+ -+ if (success && -+ (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_SIZE_DATA) != 0) { -+ /* do not panic when cannot get dependency sizes for the apps */ -+ if (!gs_dnf5_get_dependency_sizes_sync (self, apps, cancellable, &local_error)) { -+ g_debug ("Failed to get dependency sizes: %s", local_error->message); -+ g_clear_error (&local_error); -+ } -+ } -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call list: "); -+ } -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ if (success) { -+ g_task_return_boolean (task, TRUE); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_refine_async (GsPlugin *plugin, -+ GsAppList *list, -+ GsPluginRefineFlags job_flags, -+ GsPluginRefineRequireFlags require_flags, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (job_flags & GS_PLUGIN_REFINE_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = gs_plugin_refine_data_new_task (plugin, list, job_flags, require_flags, -+ event_callback, event_user_data, -+ cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_refine_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_refine_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_refine_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_refresh_metadata_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPluginRefreshMetadataData *data = task_data; -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5Base) base_proxy = NULL; -+ g_autoptr(GError) local_error = NULL; -+ gboolean success = TRUE; -+ -+ assert_in_worker (self); -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ return; -+ } -+ -+ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = base_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Base proxy: "); -+ } -+ -+ if (success && data->cache_age_secs < 60) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autofree gchar *op_error_msg = NULL; -+ gboolean op_success = FALSE; -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean ((data->flags & GS_PLUGIN_REFRESH_METADATA_FLAGS_INTERACTIVE) != 0)); -+ -+ success = gs_dnf5_base_call_clean_with_options_sync (base_proxy, -+ "expire-cache", -+ g_variant_builder_end (options_builder), -+ &op_success, -+ &op_error_msg, -+ cancellable, -+ &local_error); -+ if (!success) -+ g_debug ("Failed to call expire-cache: %s", local_error->message); -+ else if (!op_success) -+ g_debug ("Failed to expire-cache: %s", op_error_msg); -+ -+ /* ignore expire-cache error, only log about it */ -+ g_clear_error (&local_error); -+ -+ /* reset the session, to take the expiration into the effect */ -+ success = gs_dnf5_reset_session_sync (self, session_path, cancellable, &local_error); -+ } -+ -+ if (success) { -+ gboolean result = FALSE; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (base_proxy), G_MAXINT); -+ -+ success = gs_dnf5_base_call_read_all_repos_sync (base_proxy, &result, cancellable, &local_error); -+ -+ /* 'result' is ignored here, due to no error provided */ -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call read-all-repos: "); -+ } -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ if (success) { -+ g_task_return_boolean (task, TRUE); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_refresh_metadata_async (GsPlugin *plugin, -+ guint64 cache_age_secs, -+ GsPluginRefreshMetadataFlags flags, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_REFRESH_METADATA_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = g_task_new (self, cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_refresh_metadata_async); -+ g_task_set_task_data (task, -+ gs_plugin_refresh_metadata_data_new (cache_age_secs, flags, event_callback, event_user_data), -+ (GDestroyNotify) gs_plugin_refresh_metadata_data_free); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_refresh_metadata_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_refresh_metadata_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+void -+gs_plugin_adopt_app (GsPlugin *plugin, -+ GsApp *app) -+{ -+ if (gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE && -+ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) { -+ gs_app_set_management_plugin (app, plugin); -+ gs_plugin_dnf5_set_packaging_format (app); -+ return; -+ } else if (gs_app_get_kind (app) == AS_COMPONENT_KIND_OPERATING_SYSTEM) { -+ gs_app_set_management_plugin (app, plugin); -+ } -+} -+ -+static gboolean -+gs_dnf5_install_update (GsPlugin *plugin, -+ GsAppList *list, -+ gboolean is_install, -+ gboolean interactive, -+ gboolean can_apply, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GPtrArray) packages = NULL; -+ g_autoptr(GsAppList) used_apps = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ g_autoptr(GsApp) progress_app = NULL; -+ GsAppList *progress_list = is_install ? NULL : list; -+ gboolean success; -+ -+ /* queue for install if installation needs the network */ -+ if (!gs_plugin_get_network_available (plugin)) { -+ if (is_install) { -+ for (guint i = 0; i < gs_app_list_length (list); i++) { -+ GsApp *app = gs_app_list_index (list, i); -+ if (gs_app_has_management_plugin (app, plugin)) { -+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) { -+ g_warn_if_reached (); -+ continue; -+ } -+ -+ gs_app_set_state (app, GS_APP_STATE_QUEUED_FOR_INSTALL); -+ } -+ } -+ } -+ return TRUE; -+ } -+ -+ packages = g_ptr_array_new_with_free_func (g_free); -+ used_apps = gs_app_list_new (); -+ -+ for (guint i = 0; i < gs_app_list_length (list); i++) { -+ GsApp *app = gs_app_list_index (list, i); -+ if (gs_app_has_management_plugin (app, plugin)) { -+ const gchar *source = gs_app_get_default_source (app); -+ if (source != NULL) { -+ GFile *local_file = gs_app_get_local_file (app); -+ if (local_file != NULL) -+ g_ptr_array_add (packages, g_file_get_uri (local_file)); -+ else -+ g_ptr_array_add (packages, g_strdup (source)); -+ gs_app_list_add (used_apps, app); -+ if (progress_list == NULL && progress_app == NULL) -+ progress_app = g_object_ref (app); -+ } -+ } else if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_PROXY)) { -+ GsAppList *related = gs_app_get_related (app); -+ gboolean is_os_update = gs_app_get_special_kind (app) == GS_APP_SPECIAL_KIND_OS_UPDATE; -+ for (guint j = 0; j < gs_app_list_length (related); j++) { -+ GsApp *rel_app = gs_app_list_index (related, j); -+ /* add only updatable packages into the list when it's an OS Update, otherwise -+ dnf5 claims "Packages for argument '$pkgname' available, but not installed." */ -+ if (gs_app_has_management_plugin (rel_app, plugin) && (!is_install || !is_os_update || -+ gs_app_get_state (rel_app) == GS_APP_STATE_UPDATABLE)) { -+ const gchar *source = gs_app_get_default_source (rel_app); -+ if (source != NULL) { -+ GFile *local_file = gs_app_get_local_file (rel_app); -+ if (local_file != NULL) -+ g_ptr_array_add (packages, g_file_get_uri (local_file)); -+ else -+ g_ptr_array_add (packages, g_strdup (source)); -+ gs_app_list_add (used_apps, rel_app); -+ } -+ } -+ } -+ if (progress_list == NULL && progress_app == NULL) -+ progress_app = g_object_ref (app); -+ } -+ } -+ -+ if (packages->len == 0) { -+ g_set_error_literal (error, -+ GS_PLUGIN_ERROR, -+ GS_PLUGIN_ERROR_NOT_SUPPORTED, -+ "installing not available"); -+ return FALSE; -+ } -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (error); -+ return FALSE; -+ } -+ -+ for (guint i = 0; i < gs_app_list_length (used_apps); i++) { -+ GsApp *app = gs_app_list_index (used_apps, i); -+ g_autoptr(GsAppList) addons = NULL; -+ -+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) { -+ g_warn_if_reached (); -+ continue; -+ } -+ -+ if (is_install) { -+ gs_app_set_state (app, GS_APP_STATE_INSTALLING); -+ addons = gs_app_dup_addons (app); -+ } -+ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { -+ GsApp *addon = gs_app_list_index (addons, j); -+ if (gs_app_get_to_be_installed (addon)) { -+ const gchar *addon_source = gs_app_get_default_source (app); -+ if (addon_source != NULL) { -+ g_ptr_array_add (packages, g_strdup (addon_source)); -+ if (is_install) -+ gs_app_set_state (addon, GS_APP_STATE_INSTALLING); -+ } -+ } -+ } -+ } -+ -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ success = rpm_proxy != NULL; -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ GsDnf5TransactionFlags flags = (interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE) | -+ (!can_apply ? GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY : GS_DNF5_TRANSACTION_FLAG_NONE); -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean (interactive)); -+ -+ /* NULL-terminate the array, to be like strv */ -+ g_ptr_array_add (packages, NULL); -+ -+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, error); -+ -+ if (success && is_install) { -+ success = gs_dnf5_rpm_rpm_call_install_sync (rpm_proxy, -+ (const gchar * const *) packages->pdata, -+ g_variant_builder_end (options_builder), -+ cancellable, -+ error); -+ } else if (success) { -+ /* ignore package list for update, it's an offline action, which should update -+ everything possible, not only a specific package */ -+ const gchar * const all_packages[2] = { NULL, NULL }; -+ success = gs_dnf5_rpm_rpm_call_upgrade_sync (rpm_proxy, -+ all_packages, -+ g_variant_builder_end (options_builder), -+ cancellable, -+ error); -+ } -+ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, progress_list, -+ flags | (is_install ? 0 : GS_DNF5_TRANSACTION_FLAG_OFFLINE), -+ NULL, cancellable, error); -+ if (success && !is_install && can_apply) { -+ /* ensure the update is finished with the 'reboot' action */ -+ g_autoptr(GsDnf5Offline) offline_proxy = NULL; -+ -+ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ success = offline_proxy != NULL; -+ if (success) { -+ g_autofree gchar *op_error_msg = NULL; -+ gboolean op_success = FALSE; -+ -+ g_clear_pointer (&options_builder, g_variant_builder_unref); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean (interactive)); -+ -+ success = gs_dnf5_offline_call_set_finish_action_with_options_sync (offline_proxy, -+ "reboot", -+ g_variant_builder_end (options_builder), -+ &op_success, -+ &op_error_msg, -+ cancellable, -+ error); -+ if (success && !op_success) { -+ success = FALSE; -+ if (op_error_msg != NULL) { -+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg); -+ g_prefix_error_literal (error, "Failed to set finish action: "); -+ } -+ } else if (!success) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to set finish action: "); -+ } else { -+ g_autoptr(GSettings) settings = g_settings_new ("org.gnome.software"); -+ /* to check anything installed/updated after today */ -+ g_settings_set (settings, "packagekit-historical-updates-timestamp", "t", g_get_real_time () / G_USEC_PER_SEC); -+ } -+ } else { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Offline proxy: "); -+ } -+ } -+ } else { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ if (success) { -+ gs_dnf5_mark_session_needs_reset (self); -+ } else { -+ gs_dnf5_convert_error (error); -+ -+ if (error != NULL && *error != NULL) { -+ GsApp *app = NULL; -+ if (gs_app_list_length (list) == 1) -+ app = gs_app_list_index (list, 0); -+ -+ gs_dnf5_report_error (self, event_callback, event_user_data, app, *error, interactive); -+ } -+ } -+ -+ for (guint i = 0; i < gs_app_list_length (used_apps); i++) { -+ GsApp *app = gs_app_list_index (used_apps, i); -+ g_autoptr(GsAppList) addons = NULL; -+ -+ if (gs_app_get_state (app) == GS_APP_STATE_INSTALLING) { -+ if (success) { -+ gs_app_set_state (app, GS_APP_STATE_INSTALLED); -+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); -+ } else { -+ gs_app_set_state_recover (app); -+ } -+ } else if (success) { -+ /* apps prepared for offline update are downloaded */ -+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); -+ } -+ -+ if (is_install) -+ addons = gs_app_dup_addons (app); -+ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { -+ GsApp *addon = gs_app_list_index (addons, j); -+ if (gs_app_get_state (addon) == GS_APP_STATE_INSTALLING) { -+ if (success) { -+ gs_app_set_state (addon, GS_APP_STATE_INSTALLED); -+ gs_app_set_size_download (addon, GS_SIZE_TYPE_VALID, 0); -+ } else { -+ gs_app_set_state_recover (addon); -+ } -+ } else if (success) { -+ /* apps prepared for offline update are downloaded */ -+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); -+ } -+ } -+ } -+ -+ return success; -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_install_apps_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPlugin *plugin = GS_PLUGIN (self); -+ GsPluginInstallAppsData *data = task_data; -+ g_autoptr(GError) local_error = NULL; -+ gboolean success; -+ -+ assert_in_worker (self); -+ -+ g_atomic_int_inc (&self->calling_rpm); -+ -+ success = gs_dnf5_install_update (plugin, data->apps, TRUE, -+ (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0, -+ (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_NO_APPLY) == 0, -+ data->event_callback, data->event_user_data, -+ cancellable, &local_error); -+ -+ g_atomic_int_dec_and_test (&self->calling_rpm); -+ -+ if (success) -+ g_task_return_boolean (task, TRUE); -+ else -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+} -+ -+static void -+gs_plugin_dnf5_install_apps_async (GsPlugin *plugin, -+ GsAppList *apps, -+ GsPluginInstallAppsFlags flags, -+ GsPluginProgressCallback progress_callback, -+ gpointer progress_user_data, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, -+ gpointer app_needs_user_action_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = gs_plugin_install_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, -+ event_callback, event_user_data, -+ app_needs_user_action_callback, app_needs_user_action_data, -+ cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_install_apps_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_install_apps_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_install_apps_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_uninstall_apps_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPlugin *plugin = GS_PLUGIN (self); -+ GsPluginUninstallAppsData *data = task_data; -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GPtrArray) touched_apps = NULL; -+ g_autoptr(GPtrArray) packages = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ g_autoptr(GsApp) progress_app = NULL; -+ g_autoptr(GError) local_error = NULL; -+ gboolean success; -+ -+ assert_in_worker (self); -+ -+ touched_apps = g_ptr_array_new_with_free_func (g_object_unref); -+ packages = g_ptr_array_new_with_free_func (g_free); -+ -+ for (guint i = 0; i < gs_app_list_length (data->apps); i++) { -+ g_autoptr(GsAppList) addons = NULL; -+ GsApp *app = gs_app_list_index (data->apps, i); -+ const gchar *source; -+ if (!gs_app_has_management_plugin (app, plugin)) -+ continue; -+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) { -+ g_warn_if_reached (); -+ continue; -+ } -+ source = gs_app_get_default_source (app); -+ if (source == NULL) { -+ g_task_return_new_error (task, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED, -+ "remove not available"); -+ return; -+ } -+ g_ptr_array_add (packages, g_strdup (source)); -+ g_ptr_array_add (touched_apps, g_object_ref (app)); -+ gs_app_set_state (app, GS_APP_STATE_REMOVING); -+ -+ if (progress_app == NULL) -+ progress_app = g_object_ref (app); -+ -+ addons = gs_app_dup_addons (app); -+ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) { -+ GsApp *addon = gs_app_list_index (addons, j); -+ if (gs_app_get_state (addon) == GS_APP_STATE_INSTALLED) { -+ const gchar *addon_source = gs_app_get_default_source (addon); -+ if (addon_source != NULL) { -+ g_ptr_array_add (packages, g_strdup (addon_source)); -+ g_ptr_array_add (touched_apps, g_object_ref (addon)); -+ gs_app_set_state (addon, GS_APP_STATE_REMOVING); -+ } -+ } -+ } -+ } -+ -+ if (packages->len == 0) { -+ g_task_return_boolean (task, TRUE); -+ return; -+ } -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ return; -+ } -+ -+ g_atomic_int_inc (&self->calling_rpm); -+ -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = rpm_proxy != NULL; -+ if (success) { -+ GsDnf5TransactionFlags flags = (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0 ? -+ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE; -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "strict", -+ g_variant_new_boolean (FALSE)); -+ -+ /* NULL-terminate the array, to be like strv */ -+ g_ptr_array_add (packages, NULL); -+ -+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error); -+ -+ success = success && -+ gs_dnf5_rpm_rpm_call_remove_sync (rpm_proxy, -+ (const gchar * const *) packages->pdata, -+ g_variant_builder_end (options_builder), -+ cancellable, -+ &local_error); -+ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, flags, NULL, cancellable, &local_error); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: "); -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ g_atomic_int_dec_and_test (&self->calling_rpm); -+ -+ if (success) { -+ gs_dnf5_mark_session_needs_reset (self); -+ } else { -+ GsApp *app = NULL; -+ if (gs_app_list_length (data->apps) == 1) -+ app = gs_app_list_index (data->apps, 0); -+ -+ gs_dnf5_convert_error (&local_error); -+ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, app, -+ local_error, (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0); -+ } -+ -+ for (guint i = 0; i < touched_apps->len; i++) { -+ GsApp *app = g_ptr_array_index (touched_apps, i); -+ if (gs_app_get_state (app) == GS_APP_STATE_REMOVING) { -+ if (success) -+ gs_app_set_state (app, GS_APP_STATE_UNKNOWN); -+ else -+ gs_app_set_state_recover (app); -+ } -+ } -+ -+ if (success) -+ g_task_return_boolean (task, TRUE); -+ else -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+} -+ -+static void -+gs_plugin_dnf5_uninstall_apps_async (GsPlugin *plugin, -+ GsAppList *apps, -+ GsPluginUninstallAppsFlags flags, -+ GsPluginProgressCallback progress_callback, -+ gpointer progress_user_data, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, -+ gpointer app_needs_user_action_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = gs_plugin_uninstall_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, -+ event_callback, event_user_data, -+ app_needs_user_action_callback, app_needs_user_action_data, -+ cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_uninstall_apps_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_uninstall_apps_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_uninstall_apps_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_update_apps_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPlugin *plugin = GS_PLUGIN (self); -+ GsPluginUpdateAppsData *data = task_data; -+ g_autoptr(GError) local_error = NULL; -+ gboolean success; -+ -+ assert_in_worker (self); -+ -+ g_atomic_int_inc (&self->calling_rpm); -+ -+ success = gs_dnf5_install_update (plugin, data->apps, FALSE, -+ (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0, -+ (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_NO_APPLY) == 0, -+ data->event_callback, data->event_user_data, -+ cancellable, &local_error); -+ -+ g_atomic_int_dec_and_test (&self->calling_rpm); -+ -+ if (success) -+ g_task_return_boolean (task, TRUE); -+ else -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+} -+ -+static void -+gs_plugin_dnf5_update_apps_async (GsPlugin *plugin, -+ GsAppList *apps, -+ GsPluginUpdateAppsFlags flags, -+ GsPluginProgressCallback progress_callback, -+ gpointer progress_user_data, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback, -+ gpointer app_needs_user_action_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = gs_plugin_update_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data, -+ event_callback, event_user_data, -+ app_needs_user_action_callback, app_needs_user_action_data, -+ cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_update_apps_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_update_apps_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_update_apps_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+static gboolean -+gs_dnf5_manage_repository_sync (GsPluginDnf5 *self, -+ GsApp *repository, -+ gboolean enable, -+ gboolean interactive, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL; -+ gboolean success; -+ -+ if (!gs_app_has_management_plugin (repository, GS_PLUGIN (self))) -+ return TRUE; -+ -+ if (gs_app_get_kind (repository) != AS_COMPONENT_KIND_REPOSITORY) { -+ g_set_error (error, -+ GS_PLUGIN_ERROR, -+ GS_PLUGIN_ERROR_NOT_SUPPORTED, -+ "wrong app kind (%s) passed to manage_repository", -+ as_component_kind_to_string (gs_app_get_kind (repository))); -+ return FALSE; -+ } -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (error); -+ return FALSE; -+ } -+ -+ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ success = repo_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); -+ } -+ -+ if (success) { -+ const gchar *ids[2] = { NULL, NULL }; -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (repo_proxy), G_MAXINT); -+ -+ ids[0] = gs_app_get_id (repository); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean (interactive)); -+ -+ if (enable) -+ success = gs_dnf5_rpm_repo_call_enable_with_options_sync (repo_proxy, ids, g_variant_builder_end (options_builder), cancellable, error); -+ else -+ success = gs_dnf5_rpm_repo_call_disable_with_options_sync (repo_proxy, ids, g_variant_builder_end (options_builder), cancellable, error); -+ -+ if (success) { -+ if (enable) -+ gs_app_set_state (repository, GS_APP_STATE_INSTALLED); -+ else -+ gs_app_set_state (repository, GS_APP_STATE_AVAILABLE); -+ } else { -+ gs_dnf5_convert_error (error); -+ g_prefix_error (error, "Failed to call %s: ", enable ? "enable" : "disable"); -+ } -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ return success; -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_enable_repository_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPluginManageRepositoryData *data = task_data; -+ g_autoptr(GError) local_error = NULL; -+ -+ assert_in_worker (self); -+ -+ if (gs_dnf5_manage_repository_sync (self, data->repository, TRUE, -+ (data->flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0, -+ cancellable, &local_error)) { -+ g_task_return_boolean (task, TRUE); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_enable_repository_async (GsPlugin *plugin, -+ GsApp *repository, -+ GsPluginManageRepositoryFlags flags, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, event_callback, event_user_data, -+ cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_enable_repository_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_enable_repository_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_enable_repository_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_disable_repository_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPluginManageRepositoryData *data = task_data; -+ g_autoptr(GError) local_error = NULL; -+ -+ assert_in_worker (self); -+ -+ if (gs_dnf5_manage_repository_sync (self, data->repository, FALSE, -+ (data->flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0, -+ cancellable, &local_error)) { -+ g_task_return_boolean (task, TRUE); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_disable_repository_async (GsPlugin *plugin, -+ GsApp *repository, -+ GsPluginManageRepositoryFlags flags, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0; -+ g_autoptr(GTask) task = NULL; -+ -+ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, event_callback, event_user_data, -+ cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_disable_repository_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_disable_repository_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_disable_repository_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+static gboolean -+gs_dnf5_pick_rpm_desktop_file_cb (GsPlugin *plugin, -+ GsApp *app, -+ const gchar *filename, -+ GKeyFile *key_file, -+ gpointer user_data) -+{ -+ return strstr (filename, "/snapd/") == NULL && -+ strstr (filename, "/snap/") == NULL && -+ strstr (filename, "/flatpak/") == NULL && -+ g_key_file_has_group (key_file, "Desktop Entry") && -+ !g_key_file_has_key (key_file, "Desktop Entry", "X-Flatpak", NULL) && -+ !g_key_file_has_key (key_file, "Desktop Entry", "X-SnapInstanceName", NULL); -+} -+ -+static void -+gs_plugin_dnf5_launch_async (GsPlugin *plugin, -+ GsApp *app, -+ GsPluginLaunchFlags flags, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ gs_plugin_app_launch_filtered_async (plugin, app, flags, -+ gs_dnf5_pick_rpm_desktop_file_cb, NULL, -+ cancellable, -+ callback, user_data); -+} -+ -+static gboolean -+gs_plugin_dnf5_launch_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return gs_plugin_app_launch_filtered_finish (plugin, result, error); -+} -+ -+static gboolean -+gs_dnf5_file_to_app_sync (GsPluginDnf5 *self, -+ GsAppList *list, -+ GFile *file, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autofree gchar *content_type = NULL; -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ gboolean success = TRUE; -+ const gchar *mimetypes[] = { -+ "application/x-app-package", -+ "application/x-deb", -+ "application/vnd.debian.binary-package", -+ "application/x-redhat-package-manager", -+ "application/x-rpm", -+ NULL }; -+ -+ /* does this match any of the mimetypes we support */ -+ content_type = gs_utils_get_content_type (file, cancellable, error); -+ if (content_type == NULL) -+ return FALSE; -+ if (!g_strv_contains (mimetypes, content_type)) -+ return TRUE; -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (error); -+ return FALSE; -+ } -+ -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ success = rpm_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); -+ } -+ -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariantBuilder) patterns_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ g_autofree gchar *file_uri = NULL; -+ -+ file_uri = g_file_get_path (file); -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ -+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); -+ g_variant_builder_add (patterns_builder, "s", file_uri); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "patterns", -+ g_variant_builder_end (patterns_builder)); -+ g_variant_builder_add (options_builder, "{sv}", "package_attrs", -+ gs_dnf5_dup_package_attrs ()); -+ g_variant_builder_add (options_builder, "{sv}", "with_provides", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_filenames", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "with_src", -+ g_variant_new_boolean (FALSE)); -+ g_variant_builder_add (options_builder, "{sv}", "scope", -+ g_variant_new_string ("all")); -+ -+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy, -+ g_variant_builder_end (options_builder), -+ &result, -+ cancellable, -+ error); -+ -+ if (success) { -+ g_autofree gchar *basename = g_file_get_basename (file); -+ ReadPackageData rpd = { 0, }; -+ rpd.list = list; -+ rpd.set_state = GS_APP_STATE_AVAILABLE_LOCAL; -+ rpd.nevra_to_app = NULL; -+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, error); -+ -+ if (success) { -+ for (guint i = 0; i < gs_app_list_length (list); i++) { -+ GsApp *app = gs_app_list_index (list, i); -+ /* it's already downloaded, because it's a local file */ -+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); -+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE); -+ gs_app_set_local_file (app, file); -+ gs_app_set_metadata (app, "GnomeSoftware::packagename-value", basename); -+ } -+ } -+ } else { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to call list: "); -+ } -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ return success; -+} -+ -+static void -+gs_dnf5_file_to_app_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ g_autoptr(GsAppList) list = gs_app_list_new (); -+ g_autoptr(GError) local_error = NULL; -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPluginFileToAppData *data = task_data; -+ -+ if (gs_dnf5_file_to_app_sync (self, list, data->file, cancellable, &local_error)) { -+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); -+ } else if (local_error != NULL) { -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } else { -+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); -+ } -+} -+ -+static void -+gs_plugin_dnf5_file_to_app_async (GsPlugin *plugin, -+ GFile *file, -+ GsPluginFileToAppFlags flags, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ g_autoptr(GTask) task = NULL; -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ gboolean interactive = (flags & GS_PLUGIN_FILE_TO_APP_FLAGS_INTERACTIVE) != 0; -+ -+ task = gs_plugin_file_to_app_data_new_task (plugin, file, flags, event_callback, event_user_data, cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_file_to_app_async); -+ -+ /* Queue a job to get the apps. */ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_file_to_app_thread_cb, g_steal_pointer (&task)); -+} -+ -+static GsAppList * -+gs_plugin_dnf5_file_to_app_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_pointer (G_TASK (result), error); -+} -+ -+static gboolean -+gs_dnf5_system_upgrade_sync (GsPluginDnf5 *self, -+ const gchar *releasever, -+ GsApp *progress_app, -+ gboolean download_only, -+ gboolean interactive, -+ GCancellable *cancellable, -+ GError **error) -+{ -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GsAppList) list = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5Offline) offline_proxy = NULL; -+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL; -+ gboolean success; -+ -+ session_path = gs_dnf5_open_session (self, releasever, &session_manager, cancellable, error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (error); -+ return FALSE; -+ } -+ -+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ success = rpm_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Rpm proxy: "); -+ } -+ -+ if (success) { -+ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ error); -+ success = offline_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to create Offline proxy: "); -+ } -+ } -+ -+ if (success) { -+ gboolean pending = FALSE; -+ g_autoptr(GVariant) transaction_status = NULL; -+ -+ if (gs_dnf5_offline_call_get_status_sync (offline_proxy, &pending, &transaction_status, cancellable, error) && -+ pending && transaction_status != NULL) { -+ const gchar *target_releasever = NULL; -+ gboolean found = g_variant_lookup (transaction_status, "target_releasever", "&s", &target_releasever); -+ if (found && g_strcmp0 (target_releasever, releasever) == 0) { -+ g_debug ("There is prepared an upgrade for version '%s' already, skipping preparation", releasever); -+ gs_dnf5_close_session (self, session_manager, session_path); -+ return TRUE; -+ } else if (found && target_releasever != NULL) { -+ g_debug ("There is prepared an update for version '%s', but needs version '%s', redo preparation", target_releasever, releasever); -+ } -+ } -+ } -+ -+ if (success) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autoptr(GVariant) result = NULL; -+ -+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean (interactive)); -+ -+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, error); -+ -+ success = success && -+ gs_dnf5_rpm_rpm_call_system_upgrade_sync (rpm_proxy, -+ g_variant_builder_end (options_builder), -+ cancellable, -+ error); -+ -+ if (success) { -+ GsDnf5TransactionFlags flags = interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE; -+ -+ success = gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, flags | GS_DNF5_TRANSACTION_FLAG_OFFLINE, NULL, cancellable, error); -+ -+ if (success && !download_only) { -+ g_autofree gchar *op_error_msg = NULL; -+ gboolean op_success = FALSE; -+ -+ g_clear_pointer (&options_builder, g_variant_builder_unref); -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean (interactive)); -+ -+ /* ensure the upgrade is finished with the 'reboot' action */ -+ success = gs_dnf5_offline_call_set_finish_action_with_options_sync (offline_proxy, "reboot", g_variant_builder_end (options_builder), -+ &op_success, &op_error_msg, cancellable, error); -+ if (success && !op_success) { -+ success = FALSE; -+ if (op_error_msg != NULL) { -+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg); -+ g_prefix_error_literal (error, "Failed to set finish action: "); -+ } -+ } else if (!success) { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to set finish action: "); -+ } -+ } -+ } else { -+ gs_dnf5_convert_error (error); -+ g_prefix_error_literal (error, "Failed to call system_upgrade: "); -+ } -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ return success; -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_download_upgrade_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPluginDownloadUpgradeData *data = task_data; -+ g_autoptr(GError) local_error = NULL; -+ -+ assert_in_worker (self); -+ -+ gs_app_set_state (data->app, GS_APP_STATE_DOWNLOADING); -+ -+ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, TRUE, -+ (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0, -+ cancellable, &local_error)) { -+ gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE); -+ g_task_return_boolean (task, TRUE); -+ } else { -+ gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE); -+ gs_dnf5_convert_error (&local_error); -+ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, data->app, -+ local_error, (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_download_upgrade_async (GsPlugin *plugin, -+ GsApp *app, -+ GsPluginDownloadUpgradeFlags flags, -+ GsPluginEventCallback event_callback, -+ void *event_user_data, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ g_autoptr(GTask) task = NULL; -+ gboolean interactive = (flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0; -+ -+ task = gs_plugin_download_upgrade_data_new_task (plugin, app, flags, event_callback, event_user_data, cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_download_upgrade_async); -+ -+ /* only process this app if was created by this plugin */ -+ if (!gs_app_has_management_plugin (app, plugin)) { -+ g_task_return_boolean (task, TRUE); -+ return; -+ } -+ -+ /* check is distro-upgrade */ -+ if (gs_app_get_kind (app) != AS_COMPONENT_KIND_OPERATING_SYSTEM) { -+ g_task_return_boolean (task, TRUE); -+ return; -+ } -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_download_upgrade_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_download_upgrade_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_trigger_upgrade_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPluginTriggerUpgradeData *data = task_data; -+ g_autoptr(GError) local_error = NULL; -+ -+ assert_in_worker (self); -+ -+ gs_app_set_state (data->app, GS_APP_STATE_PENDING_INSTALL); -+ -+ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, FALSE, -+ (data->flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0, -+ cancellable, &local_error)) { -+ gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE); -+ g_task_return_boolean (task, TRUE); -+ } else { -+ gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE); -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_trigger_upgrade_async (GsPlugin *plugin, -+ GsApp *app, -+ GsPluginTriggerUpgradeFlags flags, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ g_autoptr(GTask) task = NULL; -+ gboolean interactive = (flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0; -+ -+ task = gs_plugin_trigger_upgrade_data_new_task (plugin, app, flags, cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_trigger_upgrade_async); -+ -+ /* only process this app if was created by this plugin */ -+ if (!gs_app_has_management_plugin (app, plugin)) { -+ g_task_return_boolean (task, TRUE); -+ return; -+ } -+ -+ /* check is distro-upgrade */ -+ if (gs_app_get_kind (app) != AS_COMPONENT_KIND_OPERATING_SYSTEM) { -+ g_task_return_boolean (task, TRUE); -+ return; -+ } -+ -+ gs_app_set_state (app, GS_APP_STATE_PENDING_INSTALL); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_trigger_upgrade_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_trigger_upgrade_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+/* Run in @worker. */ -+static void -+gs_dnf5_cancel_offline_update_thread_cb (GTask *task, -+ gpointer source_object, -+ gpointer task_data, -+ GCancellable *cancellable) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object); -+ GsPluginCancelOfflineUpdateData *data = task_data; -+ g_autofree gchar *session_path = NULL; -+ g_autoptr(GsAppList) list = NULL; -+ g_autoptr(GsDnf5SessionManager) session_manager = NULL; -+ g_autoptr(GsDnf5Offline) offline_proxy = NULL; -+ gboolean success; -+ g_autoptr(GError) local_error = NULL; -+ -+ assert_in_worker (self); -+ -+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error); -+ if (session_path == NULL) { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ return; -+ } -+ -+ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection, -+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, -+ GS_DNF5_INTERFACE_RPM_DNF, -+ session_path, -+ cancellable, -+ &local_error); -+ success = offline_proxy != NULL; -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to create Offline proxy: "); -+ } -+ -+ if (success) { -+ gboolean pending = FALSE; -+ g_autoptr(GVariant) transaction_status = NULL; -+ -+ if (gs_dnf5_offline_call_get_status_sync (offline_proxy, &pending, &transaction_status, cancellable, &local_error) && -+ pending && transaction_status != NULL) { -+ g_autoptr(GVariantBuilder) options_builder = NULL; -+ g_autofree gchar *op_error_msg = NULL; -+ gboolean op_success = FALSE; -+ -+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); -+ g_variant_builder_add (options_builder, "{sv}", "interactive", -+ g_variant_new_boolean ((data->flags & GS_PLUGIN_CANCEL_OFFLINE_UPDATE_FLAGS_INTERACTIVE) != 0)); -+ -+ success = gs_dnf5_offline_call_cancel_with_options_sync (offline_proxy, g_variant_builder_end (options_builder), -+ &op_success, &op_error_msg, cancellable, &local_error); -+ if (!success) { -+ gs_dnf5_convert_error (&local_error); -+ g_prefix_error_literal (&local_error, "Failed to call OfflineProxy::cancel: "); -+ } else if (!op_success) { -+ success = FALSE; -+ if (op_error_msg != NULL) { -+ g_set_error_literal (&local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg); -+ g_prefix_error_literal (&local_error, "Failed to call OfflineProxy::cancel op: "); -+ } else { -+ g_set_error_literal (&local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, "Failed to call OfflineProxy::cancel op"); -+ } -+ } -+ } -+ } -+ -+ gs_dnf5_close_session (self, session_manager, session_path); -+ -+ if (success) { -+ g_task_return_boolean (task, TRUE); -+ } else { -+ gs_dnf5_convert_error (&local_error); -+ g_task_return_error (task, g_steal_pointer (&local_error)); -+ } -+} -+ -+static void -+gs_plugin_dnf5_cancel_offline_update_async (GsPlugin *plugin, -+ GsPluginCancelOfflineUpdateFlags flags, -+ GCancellable *cancellable, -+ GAsyncReadyCallback callback, -+ gpointer user_data) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin); -+ g_autoptr(GTask) task = NULL; -+ gboolean interactive = (flags & GS_PLUGIN_CANCEL_OFFLINE_UPDATE_FLAGS_INTERACTIVE) != 0; -+ -+ task = gs_plugin_cancel_offline_update_data_new_task (plugin, flags, cancellable, callback, user_data); -+ g_task_set_source_tag (task, gs_plugin_dnf5_cancel_offline_update_async); -+ -+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive), -+ gs_dnf5_cancel_offline_update_thread_cb, g_steal_pointer (&task)); -+} -+ -+static gboolean -+gs_plugin_dnf5_cancel_offline_update_finish (GsPlugin *plugin, -+ GAsyncResult *result, -+ GError **error) -+{ -+ return g_task_propagate_boolean (G_TASK (result), error); -+} -+ -+static void -+gs_plugin_dnf5_dispose (GObject *object) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (object); -+ -+ if (self->rpm_transaction_watch_id != 0) { -+ g_bus_unwatch_name (self->rpm_transaction_watch_id); -+ self->rpm_transaction_watch_id = 0; -+ } -+ -+ g_clear_object (&self->worker); -+ g_clear_object (&self->connection); -+ g_clear_object (&self->rpm_transaction_proxy); -+ -+ g_mutex_lock (&self->session_data.mutex); -+ if (self->session_data.autoclose_timer) { -+ g_source_remove (self->session_data.autoclose_timer); -+ self->session_data.autoclose_timer = 0; -+ } -+ -+ if (self->session_data.proxy != NULL) { -+ gs_dnf5_close_session_real (self->session_data.proxy, self->session_data.object_path); -+ -+ g_clear_object (&self->session_data.proxy); -+ g_clear_pointer (&self->session_data.object_path, g_free); -+ } -+ g_mutex_unlock (&self->session_data.mutex); -+ -+ g_mutex_lock (&self->dependency_sizes_data.mutex); -+ g_clear_pointer (&self->dependency_sizes_data.apps, g_ptr_array_unref); -+ g_clear_object (&self->dependency_sizes_data.cancellable); -+ g_mutex_unlock (&self->dependency_sizes_data.mutex); -+ -+ G_OBJECT_CLASS (gs_plugin_dnf5_parent_class)->dispose (object); -+} -+ -+static void -+gs_plugin_dnf5_finalize (GObject *object) -+{ -+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (object); -+ -+ g_mutex_clear (&self->session_data.mutex); -+ g_cond_clear (&self->session_data.cond); -+ g_mutex_clear (&self->dependency_sizes_data.mutex); -+ -+ G_OBJECT_CLASS (gs_plugin_dnf5_parent_class)->finalize (object); -+} -+ -+static void -+gs_plugin_dnf5_class_init (GsPluginDnf5Class *klass) -+{ -+ GObjectClass *object_class = G_OBJECT_CLASS (klass); -+ GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass); -+ -+ object_class->dispose = gs_plugin_dnf5_dispose; -+ object_class->finalize = gs_plugin_dnf5_finalize; -+ -+ plugin_class->setup_async = gs_plugin_dnf5_setup_async; -+ plugin_class->setup_finish = gs_plugin_dnf5_setup_finish; -+ plugin_class->shutdown_async = gs_plugin_dnf5_shutdown_async; -+ plugin_class->shutdown_finish = gs_plugin_dnf5_shutdown_finish; -+ plugin_class->list_apps_async = gs_plugin_dnf5_list_apps_async; -+ plugin_class->list_apps_finish = gs_plugin_dnf5_list_apps_finish; -+ plugin_class->refine_async = gs_plugin_dnf5_refine_async; -+ plugin_class->refine_finish = gs_plugin_dnf5_refine_finish; -+ plugin_class->refresh_metadata_async = gs_plugin_dnf5_refresh_metadata_async; -+ plugin_class->refresh_metadata_finish = gs_plugin_dnf5_refresh_metadata_finish; -+ plugin_class->update_apps_async = gs_plugin_dnf5_update_apps_async; -+ plugin_class->update_apps_finish = gs_plugin_dnf5_update_apps_finish; -+ plugin_class->enable_repository_async = gs_plugin_dnf5_enable_repository_async; -+ plugin_class->enable_repository_finish = gs_plugin_dnf5_enable_repository_finish; -+ plugin_class->disable_repository_async = gs_plugin_dnf5_disable_repository_async; -+ plugin_class->disable_repository_finish = gs_plugin_dnf5_disable_repository_finish; -+ plugin_class->install_apps_async = gs_plugin_dnf5_install_apps_async; -+ plugin_class->install_apps_finish = gs_plugin_dnf5_install_apps_finish; -+ plugin_class->uninstall_apps_async = gs_plugin_dnf5_uninstall_apps_async; -+ plugin_class->uninstall_apps_finish = gs_plugin_dnf5_uninstall_apps_finish; -+ plugin_class->launch_async = gs_plugin_dnf5_launch_async; -+ plugin_class->launch_finish = gs_plugin_dnf5_launch_finish; -+ plugin_class->file_to_app_async = gs_plugin_dnf5_file_to_app_async; -+ plugin_class->file_to_app_finish = gs_plugin_dnf5_file_to_app_finish; -+ plugin_class->download_upgrade_async = gs_plugin_dnf5_download_upgrade_async; -+ plugin_class->download_upgrade_finish = gs_plugin_dnf5_download_upgrade_finish; -+ plugin_class->trigger_upgrade_async = gs_plugin_dnf5_trigger_upgrade_async; -+ plugin_class->trigger_upgrade_finish = gs_plugin_dnf5_trigger_upgrade_finish; -+ plugin_class->cancel_offline_update_async = gs_plugin_dnf5_cancel_offline_update_async; -+ plugin_class->cancel_offline_update_finish = gs_plugin_dnf5_cancel_offline_update_finish; -+} -+ -+static void -+gs_plugin_dnf5_init (GsPluginDnf5 *self) -+{ -+ GsPlugin *plugin = GS_PLUGIN (self); -+ -+ g_mutex_init (&self->session_data.mutex); -+ g_cond_init (&self->session_data.cond); -+ g_mutex_init (&self->dependency_sizes_data.mutex); -+ -+ /* getting app properties from appstream is quicker */ -+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "appstream"); -+ -+ /* like appstream, we need the icon plugin to load cached icons into pixbufs */ -+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_BEFORE, "icons"); -+ -+ /* prioritize over packagekit */ -+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit"); -+} -+ -+GType -+gs_plugin_query_type (void) -+{ -+ return GS_TYPE_PLUGIN_DNF5; -+} -diff --git a/plugins/dnf5/gs-plugin-dnf5.h b/plugins/dnf5/gs-plugin-dnf5.h -new file mode 100644 -index 000000000..319d5843c ---- /dev/null -+++ b/plugins/dnf5/gs-plugin-dnf5.h -@@ -0,0 +1,20 @@ -+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- -+ * vi:set noexpandtab tabstop=8 shiftwidth=8: -+ * -+ * Copyright (C) 2024 Red Hat -+ * -+ * SPDX-License-Identifier: GPL-2.0+ -+ */ -+ -+#pragma once -+ -+#include -+#include -+ -+G_BEGIN_DECLS -+ -+#define GS_TYPE_PLUGIN_DNF5 (gs_plugin_dnf5_get_type ()) -+ -+G_DECLARE_FINAL_TYPE (GsPluginDnf5, gs_plugin_dnf5, GS, PLUGIN_DNF5, GsPlugin) -+ -+G_END_DECLS -diff --git a/plugins/dnf5/meson.build b/plugins/dnf5/meson.build -new file mode 100644 -index 000000000..12b2137e1 ---- /dev/null -+++ b/plugins/dnf5/meson.build -@@ -0,0 +1,42 @@ -+cargs = ['-DG_LOG_DOMAIN="GsDnf5"'] -+ -+dnf5_generated = gnome.gdbus_codegen( -+ 'gs-dnf5-generated', -+ sources: [ 'org.rpm.dnf.v0.Advisory.xml', -+ 'org.rpm.dnf.v0.Base.xml', -+ 'org.rpm.dnf.v0.comps.Group.xml', -+ 'org.rpm.dnf.v0.Goal.xml', -+ 'org.rpm.dnf.v0.History.xml', -+ 'org.rpm.dnf.v0.Offline.xml', -+ 'org.rpm.dnf.v0.rpm.Repo.xml', -+ 'org.rpm.dnf.v0.rpm.Rpm.xml', -+ 'org.rpm.dnf.v0.SessionManager.xml' -+ ], -+ interface_prefix : 'org.rpm.dnf.v0', -+ namespace : 'GsDnf5' -+) -+ -+dnf5_generated += gnome.gdbus_codegen( -+ 'gs-dnf5-rpm-generated', -+ sources: [ 'org.rpm.Transaction.xml' ], -+ interface_prefix : 'org.rpm', -+ namespace : 'GsDnf5Rpm' -+) -+ -+shared_module( -+ 'gs_plugin_dnf5', -+ dnf5_generated, -+ sources : [ -+ 'gs-dnf5-progress-helper.c', -+ 'gs-plugin-dnf5.c', -+ '../packagekit/gs-markdown.c', -+ ], -+ include_directories : [ -+ include_directories('../..'), -+ include_directories('../../lib'), -+ ], -+ install : true, -+ install_dir: plugin_dir, -+ c_args : cargs, -+ dependencies : [ plugin_libs ], -+) -diff --git a/plugins/dnf5/org.rpm.Transaction.xml b/plugins/dnf5/org.rpm.Transaction.xml -new file mode 100644 -index 000000000..7d0cbab48 ---- /dev/null -+++ b/plugins/dnf5/org.rpm.Transaction.xml -@@ -0,0 +1,26 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml b/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml -new file mode 100644 -index 000000000..6aecee9fa ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml -@@ -0,0 +1,74 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.Base.xml b/plugins/dnf5/org.rpm.dnf.v0.Base.xml -new file mode 100644 -index 000000000..e596e60b9 ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.Base.xml -@@ -0,0 +1,176 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.Goal.xml b/plugins/dnf5/org.rpm.dnf.v0.Goal.xml -new file mode 100644 -index 000000000..097e9dc7e ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.Goal.xml -@@ -0,0 +1,122 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.History.xml b/plugins/dnf5/org.rpm.dnf.v0.History.xml -new file mode 100644 -index 000000000..5df5184b0 ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.History.xml -@@ -0,0 +1,84 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.Offline.xml b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml -new file mode 100644 -index 000000000..8d84c22aa ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml -@@ -0,0 +1,151 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml b/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml -new file mode 100644 -index 000000000..a0facd692 ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml -@@ -0,0 +1,69 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml b/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml -new file mode 100644 -index 000000000..b12e0ac6d ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml -@@ -0,0 +1,61 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml -new file mode 100644 -index 000000000..50b2ba3bf ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml -@@ -0,0 +1,147 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml -new file mode 100644 -index 000000000..f24b14d39 ---- /dev/null -+++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml -@@ -0,0 +1,470 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/plugins/meson.build b/plugins/meson.build -index a108e47e5..a131a5cd3 100644 ---- a/plugins/meson.build -+++ b/plugins/meson.build -@@ -16,6 +16,9 @@ subdir('fedora-pkgdb-collections') - if get_option('dkms') - subdir('dkms') - endif -+if get_option('dnf5') -+ subdir('dnf5') -+endif - if get_option('eos_updater') - subdir('eos-updater') - endif -diff --git a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c -index 627a5143e..64451bccc 100644 ---- a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c -+++ b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c -@@ -168,6 +168,7 @@ gs_plugin_rpm_ostree_init (GsPluginRpmOstree *self) - * more sense to use a custom plugin instead of using PackageKit. - */ - gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_CONFLICTS, "packagekit"); -+ gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_CONFLICTS, "dnf5"); - - /* need pkgname */ - gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_RUN_AFTER, "appstream"); -diff --git a/po/POTFILES.in b/po/POTFILES.in -index 71aa486e2..d3e909dee 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -112,6 +112,7 @@ lib/gs-utils.c - src/org.gnome.Software.desktop.in - plugins/dkms/gs-plugin-dkms.c - plugins/core/gs-plugin-generic-updates.c -+plugins/dnf5/gs-plugin-dnf5.c - plugins/eos-updater/gs-plugin-eos-updater.c - plugins/epiphany/gs-plugin-epiphany.c - plugins/epiphany/org.gnome.Software.Plugin.Epiphany.metainfo.xml.in -diff --git a/src/meson.build b/src/meson.build -index f8b918539..7df81d130 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -245,7 +245,7 @@ if get_option('fwupd') - ) - endif - --if get_option('packagekit') or get_option('rpm_ostree') -+if get_option('packagekit') or get_option('rpm_ostree') or get_option('dnf5') - i18n.merge_file( - input: - configure_file( diff --git a/0002-Sort-distro-upgrades-in-the-plugin-loader.patch b/0002-Sort-distro-upgrades-in-the-plugin-loader.patch new file mode 100644 index 0000000..d68517a --- /dev/null +++ b/0002-Sort-distro-upgrades-in-the-plugin-loader.patch @@ -0,0 +1,76 @@ +From be925f12287faee9d4c65635a3e69d18617b0213 Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Mon, 8 Oct 2018 11:34:15 +0200 +Subject: [PATCH 2/2] Sort distro upgrades in the plugin loader + +... so that we can rely on the order in UI code. +--- + lib/gs-plugin-loader.c | 13 +++++++++++++ + src/gs-update-monitor.c | 5 +++-- + src/gs-updates-page.c | 3 ++- + 3 files changed, 18 insertions(+), 3 deletions(-) + +diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c +index 2fb02e3c6..033438a88 100644 +--- a/lib/gs-plugin-loader.c ++++ b/lib/gs-plugin-loader.c +@@ -1484,6 +1484,13 @@ gs_plugin_loader_app_sort_match_value_cb (GsApp *app1, GsApp *app2, gpointer use + return 0; + } + ++static gint ++gs_plugin_loader_app_sort_version_cb (GsApp *app1, GsApp *app2, gpointer user_data) ++{ ++ return as_utils_vercmp (gs_app_get_version (app1), ++ gs_app_get_version (app2)); ++} ++ + /******************************************************************************/ + + static gboolean +@@ -3673,6 +3680,12 @@ gs_plugin_loader_job_process_async (GsPluginLoader *plugin_loader, + gs_plugin_loader_app_sort_name_cb); + } + break; ++ case GS_PLUGIN_ACTION_GET_DISTRO_UPDATES: ++ if (gs_plugin_job_get_sort_func (plugin_job) == NULL) { ++ gs_plugin_job_set_sort_func (plugin_job, ++ gs_plugin_loader_app_sort_version_cb); ++ } ++ break; + default: + break; + } +diff --git a/src/gs-update-monitor.c b/src/gs-update-monitor.c +index 3441b161f..2f39ae0bd 100644 +--- a/src/gs-update-monitor.c ++++ b/src/gs-update-monitor.c +@@ -308,8 +308,9 @@ get_upgrades_finished_cb (GObject *object, + g_settings_set (monitor->settings, "upgrade-notification-timestamp", "x", + g_date_time_to_unix (now)); + +- /* just get the first result : FIXME, do we sort these by date? */ +- app = gs_app_list_index (apps, 0); ++ /* rely on the app list already being sorted with the ++ * chronologically newest release last */ ++ app = gs_app_list_index (apps, gs_app_list_length (apps) - 1); + + /* TRANSLATORS: this is a distro upgrade, the replacement would be the + * distro name, e.g. 'Fedora' */ +diff --git a/src/gs-updates-page.c b/src/gs-updates-page.c +index cd9db05d4..e84293e03 100644 +--- a/src/gs-updates-page.c ++++ b/src/gs-updates-page.c +@@ -1084,7 +1084,8 @@ gs_updates_page_get_upgrades_cb (GObject *source_object, + gs_updates_page_clear_flag (self, GS_UPDATES_PAGE_FLAG_HAS_UPGRADES); + gtk_widget_set_visible (self->upgrade_banner, FALSE); + } else { +- /* rely on the app list already being sorted */ ++ /* rely on the app list already being sorted with the ++ * chronologically newest release last */ + GsApp *app = gs_app_list_index (list, gs_app_list_length (list) - 1); + g_debug ("got upgrade %s", gs_app_get_id (app)); + gs_upgrade_banner_set_app (GS_UPGRADE_BANNER (self->upgrade_banner), app); +-- +2.19.1 + diff --git a/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch b/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch deleted file mode 100644 index 33140df..0000000 --- a/0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch +++ /dev/null @@ -1,686 +0,0 @@ -From 0c79d9fb08a348a45a18f15427c945a63b44982e Mon Sep 17 00:00:00 2001 -Date: Mon, 24 Nov 2025 16:06:51 +0100 -Subject: [PATCH 1/3] flatpak: Fix XbSilo lifetime in - gs_flatpak_refine_wildcard() - -The function uses pre-cached data in the hash tables, consisting of XbNode-s -from an XbSilo. The nodes are referenced, but the silos the node belongs to -is not referenced (by the XbNode). - -This change keeps the XbSilo alive as long as it's needed for the nodes -in the pre-cached data. It also does not call gs_flatpak_ref_silo() multiple -times for the same GsFlatpak instance, which could invalidate the XbNode-s -in the pre-cached data when a change in the silo or in the installation -had been received while the function was still processing the apps. - -Related downstream bug https://bugzilla.redhat.com/show_bug.cgi?id=2416542 -Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/2900 ---- - plugins/flatpak/gs-flatpak.c | 18 ++++++++--- - plugins/flatpak/gs-flatpak.h | 4 +++ - plugins/flatpak/gs-plugin-flatpak.c | 49 ++++++++++++++++++++--------- - 3 files changed, 51 insertions(+), 20 deletions(-) - -diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c -index b409475c7..a40ab54fb 100644 ---- a/plugins/flatpak/gs-flatpak.c -+++ b/plugins/flatpak/gs-flatpak.c -@@ -3748,16 +3748,19 @@ gboolean - gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - GsAppList *list, GsPluginRefineRequireFlags require_flags, - gboolean interactive, -+ XbSilo **inout_silo, -+ gchar **inout_silo_filename, -+ GHashTable **inout_installed_by_desktopid, - GHashTable **inout_components_by_id, - GHashTable **inout_components_by_bundle, - GCancellable *cancellable, GError **error) - { - const gchar *id; - GPtrArray* components = NULL; -+ XbSilo *silo; -+ GHashTable *silo_installed_by_desktopid; -+ const gchar *silo_filename; - g_autoptr(GError) error_local = NULL; -- g_autoptr(XbSilo) silo = NULL; -- g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; -- g_autofree gchar *silo_filename = NULL; - - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); - -@@ -3766,10 +3769,15 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, - if (id == NULL) - return TRUE; - -- silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); -- if (silo == NULL) -+ if (*inout_silo == NULL) -+ *inout_silo = gs_flatpak_ref_silo (self, interactive, inout_silo_filename, inout_installed_by_desktopid, cancellable, error); -+ if (*inout_silo == NULL) - return FALSE; - -+ silo = *inout_silo; -+ silo_filename = *inout_silo_filename; -+ silo_installed_by_desktopid = *inout_installed_by_desktopid; -+ - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardQuerySilo, "Flatpak (query silo)", NULL); - - if (*inout_components_by_id != NULL) { -diff --git a/plugins/flatpak/gs-flatpak.h b/plugins/flatpak/gs-flatpak.h -index 66ff59967..34e1416b5 100644 ---- a/plugins/flatpak/gs-flatpak.h -+++ b/plugins/flatpak/gs-flatpak.h -@@ -11,6 +11,7 @@ - - #include - #include -+#include - - G_BEGIN_DECLS - -@@ -108,6 +109,9 @@ gboolean gs_flatpak_refine_wildcard (GsFlatpak *self, - GsAppList *list, - GsPluginRefineRequireFlags require_flags, - gboolean interactive, -+ XbSilo **inout_silo, -+ gchar **inout_silo_filename, -+ GHashTable **inout_installed_by_desktopid, - GHashTable **inout_components_by_id, - GHashTable **inout_components_by_bundle, - GCancellable *cancellable, -diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c -index 3d0503d46..f5d4a1d67 100644 ---- a/plugins/flatpak/gs-plugin-flatpak.c -+++ b/plugins/flatpak/gs-plugin-flatpak.c -@@ -667,12 +667,29 @@ gs_plugin_flatpak_refine_app (GsPluginFlatpak *self, - return gs_flatpak_refine_app (flatpak, app, require_flags, interactive, FALSE, event_callback, event_user_data, cancellable, error); - } - -+typedef struct { -+ XbSilo *silo; -+ gchar *silo_filename; -+ GHashTable *installed_by_desktopid; -+ GHashTable *components_by_id; -+ GHashTable *components_by_bundle; -+} RefineInstallationData; -+ - static void --unref_nonnull_hash_table (gpointer ptr) -+refine_installation_data_free (gpointer ptr) - { -- GHashTable *hash_table = ptr; -- if (hash_table != NULL) -- g_hash_table_unref (hash_table); -+ RefineInstallationData *data = ptr; -+ -+ if (data == NULL) -+ return; -+ -+ g_clear_pointer (&data->silo_filename, g_free); -+ g_clear_pointer (&data->installed_by_desktopid, g_hash_table_unref); -+ g_clear_pointer (&data->components_by_id, g_hash_table_unref); -+ g_clear_pointer (&data->components_by_bundle, g_hash_table_unref); -+ /* free the silo as the last, just in case, because the data from it is in the above hash tables */ -+ g_clear_object (&data->silo); -+ g_free (data); - } - - static gboolean -@@ -761,8 +778,7 @@ refine_thread_cb (GTask *task, - gboolean interactive = (data->job_flags & GS_PLUGIN_REFINE_FLAGS_INTERACTIVE) != 0; - GsPluginEventCallback event_callback = data->event_callback; - void *event_user_data = data->event_user_data; -- g_autoptr(GPtrArray) array_components_by_id = NULL; /* (element-type GHashTable) */ -- g_autoptr(GPtrArray) array_components_by_bundle = NULL; /* (element-type GHashTable) */ -+ g_autoptr(GPtrArray) installation_data = NULL; /* (element-type RefineInstallationData) */ - g_autoptr(GsAppList) app_list = NULL; - g_autoptr(GError) local_error = NULL; - -@@ -783,10 +799,9 @@ refine_thread_cb (GTask *task, - * (e.g. inserting an app in the list on every call results in - * an infinite loop) */ - app_list = gs_app_list_copy (list); -- array_components_by_id = g_ptr_array_new_full (self->installations->len, unref_nonnull_hash_table); -- g_ptr_array_set_size (array_components_by_id, self->installations->len); -- array_components_by_bundle = g_ptr_array_new_full (self->installations->len, unref_nonnull_hash_table); -- g_ptr_array_set_size (array_components_by_bundle, self->installations->len); -+ -+ installation_data = g_ptr_array_new_full (self->installations->len, refine_installation_data_free); -+ g_ptr_array_set_size (installation_data, self->installations->len); - - for (guint j = 0; j < gs_app_list_length (app_list); j++) { - GsApp *app = gs_app_list_index (app_list, j); -@@ -796,16 +811,20 @@ refine_thread_cb (GTask *task, - - for (guint i = 0; i < self->installations->len; i++) { - GsFlatpak *flatpak = g_ptr_array_index (self->installations, i); -- GHashTable *components_by_id = array_components_by_id->pdata[i]; -- GHashTable *components_by_bundle = array_components_by_bundle->pdata[i]; -+ RefineInstallationData *inst_data = g_ptr_array_index (installation_data, i); -+ -+ if (inst_data == NULL) { -+ inst_data = g_new0 (RefineInstallationData, 1); -+ installation_data->pdata[i] = inst_data; -+ } - -- if (!gs_flatpak_refine_wildcard (flatpak, app, list, require_flags, interactive, &components_by_id, &components_by_bundle, -+ if (!gs_flatpak_refine_wildcard (flatpak, app, list, require_flags, interactive, &inst_data->silo, -+ &inst_data->silo_filename, &inst_data->installed_by_desktopid, -+ &inst_data->components_by_id, &inst_data->components_by_bundle, - cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } -- array_components_by_id->pdata[i] = components_by_id; -- array_components_by_bundle->pdata[i] = components_by_bundle; - } - } - --- -GitLab - - -From 546c6ee17b5959d2a696a6cb346a1f8b94aba303 Mon Sep 17 00:00:00 2001 -Date: Wed, 7 Jan 2026 18:37:52 +0100 -Subject: [PATCH 2/3] flatpak: Simplify wildcard app lookup function - -By flipping the order of traversing app and installation the code -can be simplified to not have too many in/out arguments. - -Suggested by Philip Withnall ---- - plugins/flatpak/gs-flatpak.c | 251 ++++++++++++++-------------- - plugins/flatpak/gs-flatpak.h | 9 +- - plugins/flatpak/gs-plugin-flatpak.c | 61 +------ - 3 files changed, 130 insertions(+), 191 deletions(-) - -diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c -index a40ab54fb..782b0bb48 100644 ---- a/plugins/flatpak/gs-flatpak.c -+++ b/plugins/flatpak/gs-flatpak.c -@@ -3745,167 +3745,158 @@ gs_flatpak_refine_app (GsFlatpak *self, - } - - gboolean --gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, -- GsAppList *list, GsPluginRefineRequireFlags require_flags, -- gboolean interactive, -- XbSilo **inout_silo, -- gchar **inout_silo_filename, -- GHashTable **inout_installed_by_desktopid, -- GHashTable **inout_components_by_id, -- GHashTable **inout_components_by_bundle, -- GCancellable *cancellable, GError **error) -+gs_flatpak_refine_wildcards (GsFlatpak *self, -+ GPtrArray *wildcard_apps, -+ GsAppList *list, -+ GsPluginRefineRequireFlags require_flags, -+ gboolean interactive, -+ GCancellable *cancellable, -+ GError **error) - { -- const gchar *id; -- GPtrArray* components = NULL; -- XbSilo *silo; -- GHashTable *silo_installed_by_desktopid; -- const gchar *silo_filename; - g_autoptr(GError) error_local = NULL; -+ g_autoptr(XbSilo) silo = NULL; -+ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; -+ g_autoptr(GHashTable) components_by_id = NULL; -+ g_autoptr(GHashTable) components_by_bundle = NULL; -+ g_autoptr(GPtrArray) components_with_id = NULL; -+ g_autoptr(GPtrArray) bundles = NULL; -+ g_autofree gchar *silo_filename = NULL; - - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); - -- /* not enough info to find */ -- id = gs_app_get_id (app); -- if (id == NULL) -- return TRUE; -- -- if (*inout_silo == NULL) -- *inout_silo = gs_flatpak_ref_silo (self, interactive, inout_silo_filename, inout_installed_by_desktopid, cancellable, error); -- if (*inout_silo == NULL) -+ silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); -+ if (silo == NULL) - return FALSE; - -- silo = *inout_silo; -- silo_filename = *inout_silo_filename; -- silo_installed_by_desktopid = *inout_installed_by_desktopid; -- - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardQuerySilo, "Flatpak (query silo)", NULL); - -- if (*inout_components_by_id != NULL) { -- components = g_hash_table_lookup (*inout_components_by_id, gs_app_get_id (app)); -- } else { -- g_autoptr(GPtrArray) components_with_id = NULL; -- *inout_components_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); -- components_with_id = xb_silo_query (silo, "components/component/id", 0, &error_local); -- if (components_with_id == NULL) { -- if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) -- return TRUE; -- g_propagate_error (error, g_steal_pointer (&error_local)); -- return FALSE; -+ components_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); -+ components_with_id = xb_silo_query (silo, "components/component/id", 0, &error_local); -+ if (components_with_id == NULL) { -+ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) -+ return TRUE; -+ g_propagate_error (error, g_steal_pointer (&error_local)); -+ return FALSE; -+ } -+ -+ for (guint i = 0; i < components_with_id->len; i++) { -+ XbNode *node = g_ptr_array_index (components_with_id, i); -+ XbNode *comp_node = xb_node_get_parent (node); -+ const gchar *comp_id = xb_node_get_text (node); -+ GPtrArray *comps = g_hash_table_lookup (components_by_id, comp_id); -+ if (comps == NULL) { -+ comps = g_ptr_array_new_with_free_func (g_object_unref); -+ g_hash_table_insert (components_by_id, g_strdup (comp_id), comps); - } -- for (guint i = 0; i < components_with_id->len; i++) { -- XbNode *node = g_ptr_array_index (components_with_id, i); -- XbNode *comp_node = xb_node_get_parent (node); -- const gchar *comp_id = xb_node_get_text (node); -- GPtrArray *comps = g_hash_table_lookup (*inout_components_by_id, comp_id); -- if (comps == NULL) { -- comps = g_ptr_array_new_with_free_func (g_object_unref); -- g_hash_table_insert (*inout_components_by_id, g_strdup (comp_id), comps); -+ g_ptr_array_add (comps, comp_node); -+ } -+ -+ components_by_bundle = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); -+ bundles = xb_silo_query (silo, "/components/component/bundle[@type='flatpak']", 0, NULL); -+ for (guint b = 0; bundles != NULL && b < bundles->len; b++) { -+ XbNode *bundle_node = g_ptr_array_index (bundles, b); -+ g_autoptr(XbNode) component_node = xb_node_get_parent (bundle_node); -+ g_autoptr(XbNode) components_node = xb_node_get_parent (component_node); -+ const gchar *origin = xb_node_get_attr (components_node, "origin"); -+ if (origin != NULL) { -+ const gchar *bundle = xb_node_get_text (bundle_node); -+ if (bundle != NULL) { -+ g_autofree gchar *key = g_strconcat (origin, "\n", bundle, NULL); -+ g_hash_table_insert (components_by_bundle, g_steal_pointer (&key), g_steal_pointer (&component_node)); - } -- g_ptr_array_add (comps, comp_node); -- if (components == NULL && g_strcmp0 (id, comp_id) == 0) -- components = comps; - } - } - - GS_PROFILER_END_SCOPED (FlatpakRefineWildcardQuerySilo); - -- if (components == NULL) -- return TRUE; -- - gs_flatpak_ensure_remote_title (self, interactive, cancellable); - -- if (*inout_components_by_bundle == NULL) { -- g_autoptr(GPtrArray) bundles = NULL; -- -- *inout_components_by_bundle = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); -- bundles = xb_silo_query (silo, "/components/component/bundle[@type='flatpak']", 0, NULL); -- for (guint b = 0; bundles != NULL && b < bundles->len; b++) { -- XbNode *bundle_node = g_ptr_array_index (bundles, b); -- g_autoptr(XbNode) component_node = xb_node_get_parent (bundle_node); -- g_autoptr(XbNode) components_node = xb_node_get_parent (component_node); -- const gchar *origin = xb_node_get_attr (components_node, "origin"); -- if (origin != NULL) { -- const gchar *bundle = xb_node_get_text (bundle_node); -- if (bundle != NULL) { -- g_autofree gchar *key = g_strconcat (origin, "\n", bundle, NULL); -- g_hash_table_insert (*inout_components_by_bundle, g_steal_pointer (&key), g_steal_pointer (&component_node)); -- } -- } -- } -- } -+ for (guint j = 0; j < wildcard_apps->len; j++) { -+ GsApp *app = g_ptr_array_index (wildcard_apps, j); -+ GPtrArray *components = NULL; -+ const gchar *id; - -+ /* not enough info to find */ -+ id = gs_app_get_id (app); -+ if (id == NULL) -+ continue; - -- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardGenerateApps, "Flatpak (create app)", NULL); -- for (guint i = 0; i < components->len; i++) { -- XbNode *component = g_ptr_array_index (components, i); -- g_autoptr(GsApp) new = NULL; -+ components = g_hash_table_lookup (components_by_id, id); -+ if (components == NULL) -+ continue; - -- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardCreateAppstreamApp, "Flatpak (create Appstream app)", NULL); -- new = gs_appstream_create_app (self->plugin, silo, component, silo_filename ? silo_filename : "", -- self->scope, error); -- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardCreateAppstreamApp); -+ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardGenerateApps, "Flatpak (create app)", NULL); -+ for (guint i = 0; i < components->len; i++) { -+ XbNode *component = g_ptr_array_index (components, i); -+ g_autoptr(GsApp) new = NULL; - -- if (new == NULL) -- return FALSE; -+ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardCreateAppstreamApp, "Flatpak (create Appstream app)", NULL); -+ new = gs_appstream_create_app (self->plugin, silo, component, silo_filename ? silo_filename : "", -+ self->scope, error); -+ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardCreateAppstreamApp); - -- gs_flatpak_claim_app (self, new); -- -- /* The appstream plugin did not find the component in the plugin's cache, -- thus read the required info from the 'bundle' element. */ -- if (gs_flatpak_app_get_ref_name (new) == NULL || -- gs_flatpak_app_get_ref_arch (new) == NULL) { -- const gchar *xref_str = NULL; -- g_autoptr(XbNode) child = NULL; -- g_autoptr(XbNode) next = NULL; -- for (child = xb_node_get_child (component); child != NULL && xref_str == NULL; -- g_object_unref (child), child = g_steal_pointer (&next)) { -- next = xb_node_get_next (child); -- if (g_strcmp0 (xb_node_get_element (child), "bundle") == 0 && -- g_strcmp0 (xb_node_get_attr (child, "type"), "flatpak") == 0) { -- xref_str = xb_node_get_text (child); -- break; -+ if (new == NULL) -+ return FALSE; -+ -+ gs_flatpak_claim_app (self, new); -+ -+ /* The appstream plugin did not find the component in the plugin's cache, -+ thus read the required info from the 'bundle' element. */ -+ if (gs_flatpak_app_get_ref_name (new) == NULL || -+ gs_flatpak_app_get_ref_arch (new) == NULL) { -+ const gchar *xref_str = NULL; -+ g_autoptr(XbNode) child = NULL; -+ g_autoptr(XbNode) next = NULL; -+ for (child = xb_node_get_child (component); child != NULL && xref_str == NULL; -+ g_object_unref (child), child = g_steal_pointer (&next)) { -+ next = xb_node_get_next (child); -+ if (g_strcmp0 (xb_node_get_element (child), "bundle") == 0 && -+ g_strcmp0 (xb_node_get_attr (child, "type"), "flatpak") == 0) { -+ xref_str = xb_node_get_text (child); -+ break; -+ } - } -- } -- if (xref_str != NULL) { -- g_auto(GStrv) split = NULL; -- -- /* get the kind/name/arch/branch */ -- split = g_strsplit (xref_str, "/", -1); -- if (g_strv_length (split) == 4) { -- const gchar *comp_type = xb_node_get_attr (component, "type"); -- AsComponentKind kind = as_component_kind_from_string (comp_type); -- if (kind != AS_COMPONENT_KIND_UNKNOWN) -- gs_app_set_kind (new, kind); -- else if (g_ascii_strcasecmp (split[0], "app") == 0) -- gs_app_set_kind (new, AS_COMPONENT_KIND_DESKTOP_APP); -- else if (g_ascii_strcasecmp (split[0], "runtime") == 0) -- gs_flatpak_set_runtime_kind_from_id (new); -- gs_flatpak_app_set_ref_name (new, split[1]); -- gs_flatpak_app_set_ref_arch (new, split[2]); -- gs_app_set_branch (new, split[3]); -- gs_app_set_metadata (new, "GnomeSoftware::packagename-value", xref_str); -+ if (xref_str != NULL) { -+ g_auto(GStrv) split = NULL; -+ -+ /* get the kind/name/arch/branch */ -+ split = g_strsplit (xref_str, "/", -1); -+ if (g_strv_length (split) == 4) { -+ const gchar *comp_type = xb_node_get_attr (component, "type"); -+ AsComponentKind kind = as_component_kind_from_string (comp_type); -+ if (kind != AS_COMPONENT_KIND_UNKNOWN) -+ gs_app_set_kind (new, kind); -+ else if (g_ascii_strcasecmp (split[0], "app") == 0) -+ gs_app_set_kind (new, AS_COMPONENT_KIND_DESKTOP_APP); -+ else if (g_ascii_strcasecmp (split[0], "runtime") == 0) -+ gs_flatpak_set_runtime_kind_from_id (new); -+ gs_flatpak_app_set_ref_name (new, split[1]); -+ gs_flatpak_app_set_ref_arch (new, split[2]); -+ gs_app_set_branch (new, split[3]); -+ gs_app_set_metadata (new, "GnomeSoftware::packagename-value", xref_str); -+ } - } - } -- } - -- if (gs_flatpak_app_get_ref_name (new) == NULL || -- gs_flatpak_app_get_ref_arch (new) == NULL) { -- g_debug ("Failed to get ref info for '%s' from wildcard '%s', skipping it...", gs_app_get_id (new), id); -- } else { -- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardRefineNewApp, "Flatpak (refine new app)", NULL); -- if (!gs_flatpak_refine_app_internal (self, new, require_flags, interactive, FALSE, *inout_components_by_bundle, -- silo, silo_filename, silo_installed_by_desktopid, cancellable, error)) -- return FALSE; -- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardRefineNewApp); -+ if (gs_flatpak_app_get_ref_name (new) == NULL || -+ gs_flatpak_app_get_ref_arch (new) == NULL) { -+ g_debug ("Failed to get ref info for '%s' from wildcard '%s', skipping it...", gs_app_get_id (new), id); -+ } else { -+ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardRefineNewApp, "Flatpak (refine new app)", NULL); -+ if (!gs_flatpak_refine_app_internal (self, new, require_flags, interactive, FALSE, components_by_bundle, -+ silo, silo_filename, silo_installed_by_desktopid, cancellable, error)) -+ return FALSE; -+ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardRefineNewApp); - -- GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardSubsumeMetadata, "Flatpak (subsume metadata)", NULL); -- gs_app_subsume_metadata (new, app); -- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardSubsumeMetadata); -+ GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardSubsumeMetadata, "Flatpak (subsume metadata)", NULL); -+ gs_app_subsume_metadata (new, app); -+ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardSubsumeMetadata); - -- gs_app_list_add (list, new); -+ gs_app_list_add (list, new); -+ } - } -+ GS_PROFILER_END_SCOPED (FlatpakRefineWildcardGenerateApps); - } -- GS_PROFILER_END_SCOPED (FlatpakRefineWildcardGenerateApps); - - GS_PROFILER_END_SCOPED (FlatpakRefineWildcard); - -diff --git a/plugins/flatpak/gs-flatpak.h b/plugins/flatpak/gs-flatpak.h -index 34e1416b5..1f7d35a8f 100644 ---- a/plugins/flatpak/gs-flatpak.h -+++ b/plugins/flatpak/gs-flatpak.h -@@ -104,16 +104,11 @@ gboolean gs_flatpak_refine_app_state (GsFlatpak *self, - void *event_user_data, - GCancellable *cancellable, - GError **error); --gboolean gs_flatpak_refine_wildcard (GsFlatpak *self, -- GsApp *app, -+gboolean gs_flatpak_refine_wildcards (GsFlatpak *self, -+ GPtrArray *wildcard_apps, - GsAppList *list, - GsPluginRefineRequireFlags require_flags, - gboolean interactive, -- XbSilo **inout_silo, -- gchar **inout_silo_filename, -- GHashTable **inout_installed_by_desktopid, -- GHashTable **inout_components_by_id, -- GHashTable **inout_components_by_bundle, - GCancellable *cancellable, - GError **error); - gboolean gs_flatpak_launch (GsFlatpak *self, -diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c -index f5d4a1d67..c06b1fb5f 100644 ---- a/plugins/flatpak/gs-plugin-flatpak.c -+++ b/plugins/flatpak/gs-plugin-flatpak.c -@@ -667,31 +667,6 @@ gs_plugin_flatpak_refine_app (GsPluginFlatpak *self, - return gs_flatpak_refine_app (flatpak, app, require_flags, interactive, FALSE, event_callback, event_user_data, cancellable, error); - } - --typedef struct { -- XbSilo *silo; -- gchar *silo_filename; -- GHashTable *installed_by_desktopid; -- GHashTable *components_by_id; -- GHashTable *components_by_bundle; --} RefineInstallationData; -- --static void --refine_installation_data_free (gpointer ptr) --{ -- RefineInstallationData *data = ptr; -- -- if (data == NULL) -- return; -- -- g_clear_pointer (&data->silo_filename, g_free); -- g_clear_pointer (&data->installed_by_desktopid, g_hash_table_unref); -- g_clear_pointer (&data->components_by_id, g_hash_table_unref); -- g_clear_pointer (&data->components_by_bundle, g_hash_table_unref); -- /* free the silo as the last, just in case, because the data from it is in the above hash tables */ -- g_clear_object (&data->silo); -- g_free (data); --} -- - static gboolean - refine_app (GsPluginFlatpak *self, - GsApp *app, -@@ -778,8 +753,7 @@ refine_thread_cb (GTask *task, - gboolean interactive = (data->job_flags & GS_PLUGIN_REFINE_FLAGS_INTERACTIVE) != 0; - GsPluginEventCallback event_callback = data->event_callback; - void *event_user_data = data->event_user_data; -- g_autoptr(GPtrArray) installation_data = NULL; /* (element-type RefineInstallationData) */ -- g_autoptr(GsAppList) app_list = NULL; -+ g_autoptr(GPtrArray) wildcard_apps = g_ptr_array_new_with_free_func (g_object_unref); /* (element-type GsApp) (owned) */ - g_autoptr(GError) local_error = NULL; - - assert_in_worker (self); -@@ -790,38 +764,17 @@ refine_thread_cb (GTask *task, - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } -- } -- -- /* Refine wildcards. -- * -- * Use a copy of the list for the loop because a function called -- * on the plugin may affect the list which can lead to problems -- * (e.g. inserting an app in the list on every call results in -- * an infinite loop) */ -- app_list = gs_app_list_copy (list); -- -- installation_data = g_ptr_array_new_full (self->installations->len, refine_installation_data_free); -- g_ptr_array_set_size (installation_data, self->installations->len); -- -- for (guint j = 0; j < gs_app_list_length (app_list); j++) { -- GsApp *app = gs_app_list_index (app_list, j); - -- if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD)) -- continue; -+ if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && gs_app_get_id (app) != NULL) -+ g_ptr_array_add (wildcard_apps, g_object_ref (app)); -+ } - -+ /* Refine wildcards. */ -+ if (wildcard_apps->len > 0) { - for (guint i = 0; i < self->installations->len; i++) { - GsFlatpak *flatpak = g_ptr_array_index (self->installations, i); -- RefineInstallationData *inst_data = g_ptr_array_index (installation_data, i); -- -- if (inst_data == NULL) { -- inst_data = g_new0 (RefineInstallationData, 1); -- installation_data->pdata[i] = inst_data; -- } - -- if (!gs_flatpak_refine_wildcard (flatpak, app, list, require_flags, interactive, &inst_data->silo, -- &inst_data->silo_filename, &inst_data->installed_by_desktopid, -- &inst_data->components_by_id, &inst_data->components_by_bundle, -- cancellable, &local_error)) { -+ if (!gs_flatpak_refine_wildcards (flatpak, wildcard_apps, list, require_flags, interactive, cancellable, &local_error)) { - g_task_return_error (task, g_steal_pointer (&local_error)); - return; - } --- -GitLab - - -From 8472d717356a801e6c0171156738ea48480fa695 Mon Sep 17 00:00:00 2001 -Date: Wed, 7 Jan 2026 18:45:47 +0100 -Subject: [PATCH 3/3] flatpak: Hold silo lock when refining wildcard apps - -The refine can take a long time, thus hold the silo lock to not have -it invalidated from another thread. Object reference is not enough -for the XbSilo, it breaks as soon as the underlying file changes. ---- - plugins/flatpak/gs-flatpak.c | 16 +++++++++++----- - 1 file changed, 11 insertions(+), 5 deletions(-) - -diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c -index 782b0bb48..c5437125d 100644 ---- a/plugins/flatpak/gs-flatpak.c -+++ b/plugins/flatpak/gs-flatpak.c -@@ -59,7 +59,7 @@ struct _GsFlatpak { - AsComponentScope scope; - GsPlugin *plugin; - XbSilo *silo; -- GMutex silo_lock; -+ GRecMutex silo_lock; - gchar *silo_filename; - GHashTable *silo_installed_by_desktopid; - gint silo_change_stamp; -@@ -1187,11 +1187,11 @@ gs_flatpak_ref_silo (GsFlatpak *self, - g_autoptr(GFile) file = NULL; - g_autoptr(GPtrArray) xremotes = NULL; - g_autoptr(GPtrArray) desktop_paths = NULL; -- g_autoptr(GMutexLocker) locker = NULL; -+ g_autoptr(GRecMutexLocker) locker = NULL; - g_autoptr(XbBuilder) builder = NULL; - g_autoptr(GMainContext) old_thread_default = NULL; - -- locker = g_mutex_locker_new (&self->silo_lock); -+ locker = g_rec_mutex_locker_new (&self->silo_lock); - /* everything is okay */ - if (self->silo != NULL && xb_silo_is_valid (self->silo) && - g_atomic_int_get (&self->silo_change_stamp_current) == g_atomic_int_get (&self->silo_change_stamp)) { -@@ -3754,6 +3754,7 @@ gs_flatpak_refine_wildcards (GsFlatpak *self, - GError **error) - { - g_autoptr(GError) error_local = NULL; -+ g_autoptr(GRecMutexLocker) silo_locker = NULL; - g_autoptr(XbSilo) silo = NULL; - g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; - g_autoptr(GHashTable) components_by_id = NULL; -@@ -3764,6 +3765,11 @@ gs_flatpak_refine_wildcards (GsFlatpak *self, - - GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcard, "Flatpak (refine wildcard)", NULL); - -+ /* the refine can take a long time, thus hold the silo lock to not have -+ it invalidated from another thread; object reference is not enough -+ for the XbSilo, it breaks as soon as the underlying file changes */ -+ silo_locker = g_rec_mutex_locker_new (&self->silo_lock); -+ - silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); - if (silo == NULL) - return FALSE; -@@ -4810,7 +4816,7 @@ gs_flatpak_finalize (GObject *object) - g_object_unref (self->plugin); - g_hash_table_unref (self->broken_remotes); - g_mutex_clear (&self->broken_remotes_mutex); -- g_mutex_clear (&self->silo_lock); -+ g_rec_mutex_clear (&self->silo_lock); - g_hash_table_unref (self->app_silos); - g_mutex_clear (&self->app_silos_mutex); - g_clear_pointer (&self->remote_title, g_hash_table_unref); -@@ -4831,7 +4837,7 @@ gs_flatpak_init (GsFlatpak *self) - { - /* XbSilo needs external locking as we destroy the silo and build a new - * one when something changes */ -- g_mutex_init (&self->silo_lock); -+ g_rec_mutex_init (&self->silo_lock); - - g_mutex_init (&self->installed_refs_mutex); - self->installed_refs = NULL; --- -GitLab - diff --git a/116.patch b/116.patch new file mode 100644 index 0000000..cf9d7e8 --- /dev/null +++ b/116.patch @@ -0,0 +1,38 @@ +From 086f4b4bf3ce478a9a1cb009f56a7c08d1aa6e57 Mon Sep 17 00:00:00 2001 +From: Adam Williamson +Date: Thu, 11 Oct 2018 19:12:01 -0700 +Subject: [PATCH] Ensure generic-updates plugin runs after packagekit-refine + +9eab01082b07acd525b8a390ab6b43136cf400a5 made generic-updates +check the 'scope' and 'kind' for a package update before adding +it to the generic 'OS Updates' "app" which is used to display +updates for packages that aren't appdata-ified. The 'scope' and +'kind' for package updates are set by packagekit-refine...but +that change did not ensure that generic-updates runs *after* +packagekit-refine. This can result in gnome-software failing to +'see' these generic updates on a first update scan, and only +picking them up on a retry. To fix this, we should ensure that +packagekit-refine runs before generic-updates. + +https://bugzilla.redhat.com/show_bug.cgi?id=1638563 + +Signed-off-by: Adam Williamson +--- + plugins/core/gs-plugin-generic-updates.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/plugins/core/gs-plugin-generic-updates.c b/plugins/core/gs-plugin-generic-updates.c +index d8e2db39..ef79c65b 100644 +--- a/plugins/core/gs-plugin-generic-updates.c ++++ b/plugins/core/gs-plugin-generic-updates.c +@@ -28,6 +28,7 @@ void + gs_plugin_initialize (GsPlugin *plugin) + { + gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "appstream"); ++ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "packagekit-refine"); + } + + static gboolean +-- +2.18.1 + diff --git a/dnf5-plugin/.gitignore b/dnf5-plugin/.gitignore deleted file mode 100644 index b86f2cd..0000000 --- a/dnf5-plugin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -checkout-gs-dnf5 diff --git a/dnf5-plugin/update-patch.sh b/dnf5-plugin/update-patch.sh deleted file mode 100755 index 481ffcf..0000000 --- a/dnf5-plugin/update-patch.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -if [ ! -d checkout-gs-dnf5 ]; then - git clone --branch main https://gitlab.gnome.org/mcrha/gnome-software.git checkout-gs-dnf5 && \ - cd checkout-gs-dnf5 && \ - git checkout -b wip/dnf5daemon origin/wip/dnf5daemon && \ - cd - >/dev/null - - if [ "$?" != "0" ]; then - echo "Failed to clone dnf5-plugin repository" 1>&2 - exit 1; - fi -fi - -PATCH_PATH=../0001-dnf5-plugin.patch - -cd checkout-gs-dnf5 && \ -echo "Updating gs-dnf5 git 'main' repository" && \ -git checkout main && \ -git pull --rebase && \ -echo "Updating gs-dnf5 git 'dnf5-pugin' repository" && \ -git checkout wip/dnf5daemon && \ -git pull --rebase && \ -echo -n "at " >../${PATCH_PATH} && \ -git log HEAD | head -n 5 | grep -E "commit|Date" >>../${PATCH_PATH} && \ -echo "" >>../${PATCH_PATH} && \ -git diff main >>../${PATCH_PATH} && \ -cd - >/dev/null - -if [ "$?" = "0" ]; then - echo "Patch '${PATCH_PATH}' updated" -else - echo "Failed to update patch '${PATCH_PATH}'" 1>&2 - exit 1 -fi diff --git a/gnome-software.spec b/gnome-software.spec index c080ede..88c46ab 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -1,132 +1,87 @@ -%global appstream_version 0.16.4 -%global flatpak_version 1.14.1 -%global fwupd_version 1.6.2 -%global glib2_version 2.76.0 -%global gtk4_version 4.16.0 -%global json_glib_version 1.6.0 -%global libadwaita_version 1.6.0 -%global libxmlb_version 0.3.4 -%global packagekit_version 1.2.5 -%global dnf5_version 5.2.16 - -# Disable WebApps for RHEL builds -%bcond webapps %[!0%{?rhel}] -# Disable parental control for RHEL builds -%bcond malcontent %[!0%{?rhel}] -# Disable rpm-ostree support for RHEL builds -%bcond rpmostree %[!0%{?rhel}] -# Disable DKMS/akmods support for RHEL builds -%bcond dkms %[!0%{?rhel}] - -%bcond packagekit 0 -%bcond dnf5 1 - -# this is not a library version -%define gs_plugin_version 23 - -%global tarball_version %%(echo %{version} | tr '~' '.') - -%global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ +%global glib2_version 2.46.0 +%global gtk3_version 3.22.4 +%global json_glib_version 1.2.0 +%global packagekit_version 1.1.1 +%global appstream_glib_version 0.7.8 +%global libsoup_version 2.52.0 +%global gsettings_desktop_schemas_version 3.12.0 +%global gnome_desktop_version 3.18.0 +%global fwupd_version 1.0.7 +%global flatpak_version 0.9.4 Name: gnome-software -Version: 49.3 -Release: 1%{?dist} +Version: 3.28.2 +Release: 4%{?dist} Summary: A software center for GNOME -License: GPL-2.0-or-later -URL: https://apps.gnome.org/Software -Source0: https://download.gnome.org/sources/gnome-software/49/%{name}-%{tarball_version}.tar.xz +License: GPLv2+ +URL: https://wiki.gnome.org/Apps/Software +Source0: https://download.gnome.org/sources/gnome-software/3.28/%{name}-%{version}.tar.xz +# /usr/share/backgrounds/f28/default/standard/f28.png from f28-backgrounds-base +Source1: f28.png +# /usr/share/backgrounds/f29/default/wide/f29.png from f29-backgrounds-base +Source2: f29.png -%if %{with dnf5} -# to update the patch enter the ./dnf5-plugin/ directory and run from -# it the ./update-patch.sh script -Patch: 0001-dnf5-plugin.patch -%endif - -# https://bugzilla.redhat.com/show_bug.cgi?id=2416542 -Patch: 0002-rhbug2416542-crash-under-gs_flatpak_refine_wildcard.patch - -# ostree and flatpak not on i686 for Fedora and RHEL 10 -# https://github.com/containers/composefs/pull/229#issuecomment-1838735764 -%if 0%{?fedora} || 0%{?rhel} >= 10 -ExcludeArch: %{ix86} -%endif +Patch0: 0001-Include-the-distro-version-in-Fedora-distro-upgrade-.patch +Patch1: 0002-Sort-distro-upgrades-in-the-plugin-loader.patch +# Fixes an ordering issue which often causes the first scan for updates +# to miss 'generic' ones +# https://gitlab.gnome.org/GNOME/gnome-software/merge_requests/116 +# https://bugzilla.redhat.com/show_bug.cgi?id=1638563 +Patch2: 116.patch +BuildRequires: gettext +BuildRequires: libxslt BuildRequires: docbook-style-xsl BuildRequires: desktop-file-utils -BuildRequires: gcc -BuildRequires: gettext -BuildRequires: git-core +BuildRequires: fwupd-devel >= %{fwupd_version} +BuildRequires: glib2-devel >= %{glib2_version} +BuildRequires: gnome-desktop3-devel +BuildRequires: gsettings-desktop-schemas-devel >= %{gsettings_desktop_schemas_version} +BuildRequires: gspell-devel +BuildRequires: gtk3-devel >= %{gtk3_version} BuildRequires: gtk-doc -BuildRequires: itstool -BuildRequires: libxslt +BuildRequires: json-glib-devel >= %{json_glib_version} +BuildRequires: libappstream-glib-devel >= %{appstream_glib_version} +BuildRequires: libsoup-devel BuildRequires: meson -BuildRequires: pkgconfig(appstream) >= %{appstream_version} -BuildRequires: pkgconfig(flatpak) >= %{flatpak_version} -BuildRequires: pkgconfig(fwupd) >= %{fwupd_version} -BuildRequires: pkgconfig(gdk-pixbuf-2.0) -BuildRequires: pkgconfig(gio-unix-2.0) >= %{glib2_version} -BuildRequires: pkgconfig(glib-2.0) >= %{glib2_version} -BuildRequires: pkgconfig(gmodule-2.0) >= %{glib2_version} -BuildRequires: pkgconfig(gsettings-desktop-schemas) -BuildRequires: pkgconfig(gtk4) >= %{gtk4_version} -BuildRequires: pkgconfig(gudev-1.0) -BuildRequires: pkgconfig(json-glib-1.0) >= %{json_glib_version} -BuildRequires: pkgconfig(libadwaita-1) >= %{libadwaita_version} -BuildRequires: pkgconfig(libdnf) -BuildRequires: pkgconfig(libsoup-3.0) -%if %{with malcontent} -BuildRequires: pkgconfig(malcontent-0) -%endif -BuildRequires: pkgconfig(ostree-1) -%if %{with packagekit} -BuildRequires: pkgconfig(packagekit-glib2) >= %{packagekit_version} -%endif -BuildRequires: pkgconfig(polkit-gobject-1) -BuildRequires: pkgconfig(rpm) -%if %{with rpmostree} -BuildRequires: pkgconfig(rpm-ostree-1) -%endif -BuildRequires: pkgconfig(sysprof-capture-4) -BuildRequires: pkgconfig(xmlb) >= %{libxmlb_version} -BuildRequires: systemd +BuildRequires: PackageKit-glib-devel >= %{packagekit_version} +BuildRequires: polkit-devel +BuildRequires: libsecret-devel +BuildRequires: flatpak-devel >= %{flatpak_version} +BuildRequires: ostree-devel +BuildRequires: libgudev1-devel +BuildRequires: valgrind-devel +BuildRequires: liboauth-devel +BuildRequires: snapd-glib-devel +%if 0%{?fedora} == 27 +Requires: appstream-data >= 27-9 +%else Requires: appstream-data -Requires: appstream%{?_isa} >= %{appstream_version} -%if %{with webapps} -Requires: epiphany-runtime%{?_isa} %endif -%if %{with dnf5} -Requires: dnf5daemon-server%{?_isa} >= %{dnf5_version} -Requires: dnf5daemon-server-polkit -Requires: libdnf5-plugin-appstream%{?_isa} -Requires: rpm-plugin-dbus-announce%{?_isa} +%if 0%{?fedora} +Requires: epiphany-runtime %endif Requires: flatpak%{?_isa} >= %{flatpak_version} Requires: flatpak-libs%{?_isa} >= %{flatpak_version} Requires: fwupd%{?_isa} >= %{fwupd_version} Requires: glib2%{?_isa} >= %{glib2_version} -%if !0%{?rhel} -Requires: gnome-app-list -%endif +Requires: gnome-desktop3%{?_isa} >= %{gnome_desktop_version} # gnome-menus is needed for app folder .directory entries Requires: gnome-menus%{?_isa} -Requires: gsettings-desktop-schemas%{?_isa} -Requires: gtk4 >= %{gtk4_version} +Requires: gsettings-desktop-schemas%{?_isa} >= %{gsettings_desktop_schemas_version} +Requires: gtk3%{?_isa} >= %{gtk3_version} Requires: json-glib%{?_isa} >= %{json_glib_version} Requires: iso-codes -Requires: libadwaita >= %{libadwaita_version} +Requires: libappstream-glib%{?_isa} >= %{appstream_glib_version} # librsvg2 is needed for gdk-pixbuf svg loader Requires: librsvg2%{?_isa} -Requires: libxmlb%{?_isa} >= %{libxmlb_version} +Requires: libsoup%{?_isa} >= %{libsoup_version} +Requires: PackageKit%{?_isa} >= %{packagekit_version} -%if %{with packagekit} -Recommends: PackageKit%{?_isa} >= %{packagekit_version} -%endif -Recommends: %{name}-fedora-langpacks - -Obsoletes: gnome-software-snap < 3.33.1 -Obsoletes: gnome-software-editor < 3.35.1 +# this is not a library version +%define gs_plugin_version 11 %description gnome-software is an application that makes it easy to add, remove @@ -140,79 +95,39 @@ Requires: %{name}%{?_isa} = %{version}-%{release} These development files are for building gnome-software plugins outside the source tree. Most users do not need this subpackage installed. -%package fedora-langpacks -Summary: Contains fedora-langpacks plugin +%package editor +Summary: Banner designer for GNOME Software Requires: %{name}%{?_isa} = %{version}-%{release} -%description fedora-langpacks -The fedora-langpacks plugin ensures langpacks packages are installed -for the current locale. +%description editor +Editor for designing banners for GNOME Software. -%if %{with rpmostree} -%package rpm-ostree -Summary: rpm-ostree backend for gnome-software +%package snap +Summary: Support for Ubuntu Snap packages Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: rpm-ostree%{?_isa} -Supplements: (gnome-software%{?_isa} and rpm-ostree%{?_isa}) +Supplements: (gnome-software%{?_isa} and snapd%{?_isa}) -%description rpm-ostree -gnome-software is an application that makes it easy to add, remove -and update software in the GNOME desktop. - -This package includes the rpm-ostree backend. -%endif +%description snap +Adds support for Snap packages from the Snap store. %prep -%autosetup -p1 -S gendiff -n %{name}-%{tarball_version} +%autosetup -p1 %build %meson \ - -Dsnap=false \ -%if %{with malcontent} - -Dmalcontent=true \ -%else - -Dmalcontent=false \ -%endif - -Dgudev=true \ -%if %{with packagekit} - -Dpackagekit=true \ - -Dpackagekit_autoremove=true \ -%else - -Dpackagekit=false \ -%endif -%if %{with dnf5} - -Ddnf5=true \ -%endif - -Dexternal_appstream=false \ -%if %{with rpmostree} - -Drpm_ostree=true \ -%else - -Drpm_ostree=false \ -%endif -%if %{with webapps} - -Dwebapps=true \ - -Dhardcoded_foss_webapps=true \ - -Dhardcoded_proprietary_webapps=false \ -%else - -Dwebapps=false \ - -Dhardcoded_foss_webapps=false \ - -Dhardcoded_proprietary_webapps=false \ -%endif -%if %{with dkms} - -Ddkms=true \ -%else - -Ddkms=false \ -%endif - -Dtests=false + -Denable-snap=true \ + -Denable-gudev=true \ + -Denable-packagekit=true \ + -Denable-external-appstream=false \ + -Denable-rpm-ostree=false \ + -Denable-tests=false \ + -Denable-ubuntuone=false \ + -Denable-ubuntu-reviews=false %meson_build %install %meson_install -# remove unneeded dpkg and dummy plugins -rm %{buildroot}%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_dpkg.so -rm %{buildroot}%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_dummy.so - # make the software center load faster desktop-file-edit %{buildroot}%{_datadir}/applications/org.gnome.Software.desktop \ --set-key=X-AppInstall-Package --set-value=%{name} @@ -220,113 +135,591 @@ desktop-file-edit %{buildroot}%{_datadir}/applications/org.gnome.Software.deskto # set up for Fedora cat >> %{buildroot}%{_datadir}/glib-2.0/schemas/org.gnome.software-fedora.gschema.override << FOE [org.gnome.software] -%if 0%{?rhel} -official-repos = [ 'rhel-%{?rhel}' ] -%else -official-repos = [ 'anaconda', 'fedora', 'fedora-debuginfo', 'fedora-source', 'koji-override-0', 'koji-override-1', 'rawhide', 'rawhide-debuginfo', 'rawhide-source', 'updates', 'updates-debuginfo', 'updates-source', 'updates-testing', 'updates-testing-debuginfo', 'updates-testing-source', 'fedora-modular', 'fedora-modular-debuginfo', 'fedora-modular-source', 'rawhide-modular', 'rawhide-modular-debuginfo', 'rawhide-modular-source', 'fedora-cisco-openh264', 'fedora-cisco-openh264-debuginfo' ] -required-repos = [ 'fedora', 'updates' ] -packaging-format-preference = [ 'flatpak:fedora-testing', 'flatpak:fedora', 'rpm' ] -%endif +official-repos = [ 'fedora', 'fedora-debuginfo', 'fedora-source', 'koji-override-0', 'koji-override-1', 'rawhide', 'rawhide-debuginfo', 'rawhide-source', 'updates', 'updates-debuginfo', 'updates-source', 'updates-testing', 'updates-testing-debuginfo', 'updates-testing-source' ] FOE +# Install upgrade background image +mkdir -p %{buildroot}%{_datadir}/gnome-software/backgrounds +cp -a %{SOURCE1} %{buildroot}%{_datadir}/gnome-software/backgrounds/ +cp -a %{SOURCE2} %{buildroot}%{_datadir}/gnome-software/backgrounds/ + %find_lang %name --with-gnome %check desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop +%post +touch --no-create %{_datadir}/icons/hicolor &>/dev/null || : + +%postun +if [ $1 -eq 0 ]; then + touch --no-create %{_datadir}/icons/hicolor &> /dev/null || : + gtk-update-icon-cache %{_datadir}/icons/hicolor &> /dev/null || : + glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : +fi + +%posttrans +gtk-update-icon-cache %{_datadir}/icons/hicolor &> /dev/null || : +glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : + %files -f %{name}.lang -%doc AUTHORS NEWS README.md +%doc AUTHORS README.md %license COPYING %{_bindir}/gnome-software -%{_datadir}/applications/gnome-software-local-file-flatpak.desktop -%{_datadir}/applications/gnome-software-local-file-fwupd.desktop -%if %{with packagekit} || %{with rpmostree} || %{with dnf5} -%{_datadir}/applications/gnome-software-local-file-packagekit.desktop -%endif +%{_datadir}/applications/gnome-software-local-file.desktop %{_datadir}/applications/org.gnome.Software.desktop -%{_datadir}/bash-completion/completions/gnome-software -%{_mandir}/man1/gnome-software.1* -%{_datadir}/icons/hicolor/*/apps/org.gnome.Software.svg -%{_datadir}/icons/hicolor/symbolic/apps/org.gnome.Software-symbolic.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-addon.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-application.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-codecs.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-driver.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-firmware.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-input-sources.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-language.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-os-updates.svg -%{_datadir}/icons/hicolor/scalable/categories/system-component-runtime.svg -%{_datadir}/metainfo/org.gnome.Software.metainfo.xml -%if %{with webapps} +%dir %{_datadir}/gnome-software +%{_datadir}/gnome-software/*.png +%{_mandir}/man1/gnome-software.1.gz +%{_datadir}/icons/hicolor/*/apps/* +%{_datadir}/gnome-software/backgrounds/ +%{_datadir}/gnome-software/featured-*.svg +%{_datadir}/gnome-software/featured-*.jpg +%{_datadir}/metainfo/org.gnome.Software.appdata.xml %{_datadir}/metainfo/org.gnome.Software.Plugin.Epiphany.metainfo.xml -%endif %{_datadir}/metainfo/org.gnome.Software.Plugin.Flatpak.metainfo.xml %{_datadir}/metainfo/org.gnome.Software.Plugin.Fwupd.metainfo.xml -%dir %{_libdir}/gnome-software/plugins-%{gs_plugin_version} -%{_libdir}/gnome-software/libgnomesoftware.so.%{gs_plugin_version} -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_appstream.so -%if %{with dnf5} -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_dnf5.so -%endif -%if %{with webapps} -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_epiphany.so -%endif -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_fedora-pkgdb-collections.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_flatpak.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_fwupd.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_generic-updates.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-blocklist.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_icons.so -%if %{with malcontent} -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_malcontent.so -%endif -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_modalias.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_os-release.so -%if %{with packagekit} -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_packagekit.so -%endif -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_provenance-license.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_provenance.so -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_repos.so -%if %{with webapps} -%dir %{_datadir}/swcatalog -%dir %{_datadir}/swcatalog/xml -%{_datadir}/swcatalog/xml/gnome-pwa-list-foss.xml -%endif -%if %{with packagekit} +%{_datadir}/metainfo/org.gnome.Software.Plugin.Odrs.metainfo.xml +%{_datadir}/metainfo/org.gnome.Software.Plugin.Steam.metainfo.xml +%dir %{_libdir}/gs-plugins-%{gs_plugin_version} +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_appstream.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_desktop-categories.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_desktop-menu-path.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_dpkg.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_dummy.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_epiphany.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_fedora-pkgdb-collections.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_flatpak.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_fwupd.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_generic-updates.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-blacklist.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-featured.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-popular.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_icons.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_key-colors-metadata.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_key-colors.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_modalias.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_odrs.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_os-release.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-history.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-local.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-offline.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-proxy.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-refine-repos.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-refine.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-refresh.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-upgrade.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-url-to-app.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_provenance-license.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_provenance.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_repos.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_rewrite-resource.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_shell-extensions.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_steam.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_systemd-updates.so +%{_sysconfdir}/xdg/autostart/gnome-software-service.desktop +%{_datadir}/app-info/xmls/org.gnome.Software.Featured.xml %{_datadir}/dbus-1/services/org.freedesktop.PackageKit.service -%endif %{_datadir}/dbus-1/services/org.gnome.Software.service %{_datadir}/gnome-shell/search-providers/org.gnome.Software-search-provider.ini %{_datadir}/glib-2.0/schemas/org.gnome.software.gschema.xml %{_datadir}/glib-2.0/schemas/org.gnome.software-fedora.gschema.override %{_libexecdir}/gnome-software-cmd %{_libexecdir}/gnome-software-restarter -%{_userunitdir}/gnome-software.service -%if %{with dkms} -%{_datadir}/polkit-1/actions/org.gnome.software.dkms-helper.policy -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_dkms.so -%{_libexecdir}/gnome-software-dkms-helper -%endif - -%files fedora-langpacks -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_fedora-langpacks.so - -%if %{with rpmostree} -%files rpm-ostree -%{_libdir}/gnome-software/plugins-%{gs_plugin_version}/libgs_plugin_rpm-ostree.so -%endif +%files snap +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_snap.so +%{_datadir}/metainfo/org.gnome.Software.Plugin.Snap.metainfo.xml %files devel %{_libdir}/pkgconfig/gnome-software.pc %dir %{_includedir}/gnome-software %{_includedir}/gnome-software/*.h -%{_libdir}/gnome-software/libgnomesoftware.so -%dir %{_datadir}/gtk-doc -%dir %{_datadir}/gtk-doc/html -%{_datadir}/gtk-doc/html/gnome-software/ +%{_datadir}/gtk-doc/html/gnome-software + +%files editor +%{_bindir}/gnome-software-editor +%{_datadir}/applications/org.gnome.Software.Editor.desktop +%{_mandir}/man1/gnome-software-editor.1* %changelog -%autochangelog +* Thu Oct 11 2018 Adam Williamson - 3.28.2-4 +- Backport MR#116 to fix bug with generic updates (#1638563) + +* Mon Oct 08 2018 Kalev Lember - 3.28.2-3 +- Backport fixes for N+2 distro upgrades (#1628497) + +* Thu Sep 06 2018 Kalev Lember - 3.28.2-2 +- Add artwork for F29 Beta upgrades + +* Wed May 09 2018 Kalev Lember - 3.28.2-1 +- Update to 3.28.2 + +* Wed Apr 18 2018 Kalev Lember - 3.28.1-1.1 +- Sync F28 upgrade background image with final F28 artwork + +* Mon Apr 09 2018 Kalev Lember - 3.28.1-1 +- Update to 3.28.1 + +* Thu Mar 15 2018 Kalev Lember - 3.28.0-4 +- Fix opening results from gnome-shell search provider + +* Wed Mar 14 2018 Kalev Lember - 3.28.0-3 +- Fix crash on initial run with no network (#1554986) + +* Tue Mar 13 2018 Kalev Lember - 3.28.0-2 +- Backport an upstream patch to fix shell extensions app ID + +* Mon Mar 12 2018 Kalev Lember - 3.28.0-1 +- Update to 3.28.0 +- Move org.gnome.Software.Featured.xml from -editor to main package + +* Fri Mar 02 2018 Kalev Lember - 3.26.7-1 +- Update to 3.26.7 +- Ship a background image for F28 beta upgrades + +* Thu Feb 08 2018 Kalev Lember - 3.26.6-1 +- Update to 3.26.6 +- Add fedora-workstation-repositories to nonfree-sources schema defaults + +* Mon Jan 08 2018 Kalev Lember - 3.26.5-1 +- Update to 3.26.5 +- Drop unused --without packagekit option + +* Sun Dec 17 2017 Kalev Lember - 3.26.4-1 +- Update to 3.26.4 + +* Tue Nov 21 2017 Kalev Lember - 3.26.3-1 +- Update to 3.26.3 + +* Thu Nov 09 2017 Kalev Lember - 3.26.2-1 +- Update to 3.26.2 + +* Wed Oct 25 2017 Kalev Lember - 3.26.1-3 +- Fix "too many results returned" error after distro upgrades (#1496489) + +* Tue Oct 10 2017 Kalev Lember - 3.26.1-2 +- Backport a flatpakref installation fix + +* Sun Oct 08 2017 Kalev Lember - 3.26.1-1 +- Update to 3.26.1 + +* Mon Sep 11 2017 Kalev Lember - 3.26.0-1 +- Update to 3.26.0 + +* Sun Aug 27 2017 Kalev Lember - 3.25.91-1 +- Update to 3.25.91 + +* Tue Aug 15 2017 Kalev Lember - 3.25.90-1 +- Update to 3.25.90 + +* Fri Aug 11 2017 Igor Gnatenko - 3.25.4-6 +- Rebuilt after RPM update (№ 3) + +* Thu Aug 10 2017 Igor Gnatenko - 3.25.4-5 +- Rebuilt for RPM soname bump + +* Thu Aug 10 2017 Igor Gnatenko - 3.25.4-4 +- Rebuilt for RPM soname bump + +* Wed Aug 02 2017 Fedora Release Engineering - 3.25.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 3.25.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Jul 21 2017 Kalev Lember - 3.25.4-1 +- Update to 3.25.4 + +* Tue Jul 18 2017 Kalev Lember - 3.25.3-6 +- Drop a meson workaround now that meson is fixed + +* Wed Jun 28 2017 Neal Gompa - 3.25.3-5 +- Actually properly enable snap subpackage after removing conditional + +* Wed Jun 28 2017 Neal Gompa - 3.25.3-4 +- Remove unnecessary arch-specific conditional for snap subpackage + +* Tue Jun 27 2017 Neal Gompa - 3.25.3-3 +- Ensure snap subpackage is installed if snapd is installed + +* Fri Jun 23 2017 Richard Hughes - 3.24.3-2 +- Enable the snap subpackage + +* Fri Jun 23 2017 Kalev Lember - 3.25.3-1 +- Update to 3.25.3 +- Switch to the meson build system +- Add an -editor subpackage with new banner editor + +* Mon May 15 2017 Richard Hughes - 3.24.3-1 +- Update to 3.23.3 +- Fix a common crash when installing flatpakrepo files +- Ensure we show the banner when upgrades are available + +* Tue May 09 2017 Kalev Lember - 3.24.2-1 +- Update to 3.24.2 + +* Tue Apr 25 2017 Adam Williamson - 3.24.1-2 +- Backport crasher fix from upstream (RHBZ #1444669 / BGO #781217) + +* Tue Apr 11 2017 Kalev Lember - 3.24.1-1 +- Update to 3.24.1 + +* Tue Mar 21 2017 Kalev Lember - 3.24.0-1 +- Update to 3.24.0 + +* Thu Mar 16 2017 Kalev Lember - 3.23.92-1 +- Update to 3.23.92 + +* Mon Feb 27 2017 Richard Hughes - 3.23.91-1 +- Update to 3.23.91 + +* Mon Feb 13 2017 Richard Hughes - 3.23.90-1 +- Update to 3.23.90 + +* Fri Feb 10 2017 Fedora Release Engineering - 3.23.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Dec 15 2016 Richard Hughes - 3.23.3-1 +- Update to 3.23.3 + +* Wed Nov 23 2016 Kalev Lember - 3.23.2-1 +- Update to 3.23.2 + +* Tue Nov 08 2016 Kalev Lember - 3.22.2-1 +- Update to 3.22.2 + +* Wed Oct 12 2016 Kalev Lember - 3.22.1-1 +- Update to 3.22.1 + +* Mon Sep 19 2016 Kalev Lember - 3.22.0-1 +- Update to 3.22.0 + +* Wed Sep 14 2016 Kalev Lember - 3.21.92-1 +- Update to 3.21.92 +- Don't set group tags + +* Thu Sep 01 2016 Kalev Lember - 3.21.91-1 +- Update to 3.21.91 + +* Wed Aug 17 2016 Kalev Lember - 3.21.90-2 +- Rebuilt for fixed libappstream-glib headers + +* Wed Aug 17 2016 Kalev Lember - 3.21.90-1 +- Update to 3.21.90 +- Tighten -devel subpackage dependencies + +* Thu Jul 28 2016 Richard Hughes - 3.21.4-2 +- Allow building without PackageKit for the atomic workstation. + +* Mon Jul 18 2016 Richard Hughes - 3.21.4-1 +- Update to 3.21.4 + +* Thu May 26 2016 Kalev Lember - 3.21.2-2 +- Build with flatpak support + +* Mon May 23 2016 Richard Hughes - 3.21.2-1 +- Update to 3.21.2 + +* Tue May 10 2016 Kalev Lember - 3.21.1-2 +- Require PackageKit 1.1.1 for system upgrade support + +* Mon Apr 25 2016 Richard Hughes - 3.21.1-1 +- Update to 3.21.1 + +* Mon Apr 25 2016 Richard Hughes - 3.20.2-1 +- Update to 3.20.1 +- Allow popular and featured apps to match any plugin +- Do not make the ODRS plugin depend on xdg-app +- Fix many of the os-upgrade issues and implement the latest mockups +- Make all the plugins more threadsafe +- Return all update descriptions newer than the installed version +- Show some non-fatal error messages if installing fails +- Use a background PackageKit transaction when downloading upgrades + +* Wed Apr 13 2016 Kalev Lember - 3.20.1-1 +- Update to 3.20.1 + +* Fri Apr 01 2016 Richard Hughes - 3.20.1-2 +- Set the list of official sources +- Compile with xdg-app support + +* Tue Mar 22 2016 Kalev Lember - 3.20.0-1 +- Update to 3.20.0 + +* Mon Mar 14 2016 Richard Hughes - 3.19.92-1 +- Update to 3.19.92 + +* Thu Mar 03 2016 Kalev Lember - 3.19.91-2 +- Set minimum required json-glib version + +* Mon Feb 29 2016 Richard Hughes - 3.19.91-1 +- Update to 3.19.91 + +* Mon Feb 15 2016 Richard Hughes - 3.19.90-1 +- Update to 3.19.90 + +* Wed Feb 03 2016 Fedora Release Engineering - 3.19.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Fri Jan 15 2016 Richard Hughes - 3.19.4-1 +- Update to 3.19.4 + +* Thu Dec 03 2015 Kalev Lember - 3.18.3-2 +- Require librsvg2 for the gdk-pixbuf svg loader + +* Thu Nov 05 2015 Richard Hughes - 3.18.3-1 +- Update to 3.18.3 +- Use the correct user agent string when downloading firmware +- Fix a crash in the limba plugin +- Fix installing web applications + +* Mon Oct 26 2015 Kalev Lember - 3.18.2-2 +- Fix apps reappearing as installed a few seconds after removal (#1275163) + +* Thu Oct 15 2015 Kalev Lember - 3.18.2-1 +- Update to 3.18.2 + +* Tue Oct 13 2015 Kalev Lember - 3.18.1-1 +- Update to 3.18.1 + +* Wed Oct 07 2015 Kalev Lember - 3.18.0-2 +- Backport two crasher fixes from upstream + +* Mon Sep 21 2015 Kalev Lember - 3.18.0-1 +- Update to 3.18.0 + +* Tue Sep 15 2015 Kalev Lember - 3.17.92-2 +- Update dependency versions + +* Tue Sep 15 2015 Richard Hughes - 3.17.92-1 +- Update to 3.17.92 + +* Thu Sep 10 2015 Richard Hughes - 3.17.91-2 +- Fix firmware updates + +* Thu Sep 03 2015 Kalev Lember - 3.17.91-1 +- Update to 3.17.91 + +* Wed Aug 19 2015 Kalev Lember - 3.17.90-1 +- Update to 3.17.90 + +* Wed Aug 12 2015 Richard Hughes - 3.17.3-1 +- Update to 3.17.3 + +* Wed Jul 22 2015 David King - 3.17.2-3 +- Bump for new gnome-desktop3 + +* Wed Jun 17 2015 Fedora Release Engineering - 3.17.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Jun 05 2015 Kalev Lember - 3.17.2-1 +- Update to 3.17.2 + +* Mon May 25 2015 Kalev Lember - 3.17.1-1 +- Update to 3.17.1 + +* Fri May 15 2015 Kalev Lember - 3.16.2-2 +- Fix a crash under Wayland (#1221968) + +* Mon May 11 2015 Kalev Lember - 3.16.2-1 +- Update to 3.16.2 + +* Tue Apr 14 2015 Kalev Lember - 3.16.1-1 +- Update to 3.16.1 + +* Mon Mar 23 2015 Kalev Lember - 3.16.0-1 +- Update to 3.16.0 + +* Mon Mar 16 2015 Kalev Lember - 3.15.92-1 +- Update to 3.15.92 +- Use license macro for the COPYING file +- Add a patch to adapt to gnome-terminal desktop file rename + +* Mon Mar 02 2015 Kalev Lember - 3.15.91-1 +- Update to 3.15.91 + +* Sat Feb 21 2015 Kalev Lember - 3.15.90-3 +- Export DisplayName property on the packagekit session service + +* Thu Feb 19 2015 Kalev Lember - 3.15.90-2 +- Backport a crash fix + +* Tue Feb 17 2015 Richard Hughes - 3.15.90-1 +- Update to 3.15.90 + +* Mon Jan 19 2015 Richard Hughes - 3.15.4-1 +- Update to 3.15.4 + +* Tue Nov 25 2014 Kalev Lember - 3.15.2-1 +- Update to 3.15.2 + +* Thu Nov 13 2014 Richard Hughes - 3.14.2-3 +- Fix non-Fedora build + +* Tue Nov 11 2014 Richard Hughes - 3.14.2-2 +- Backport a patch to fix compilation + +* Mon Nov 10 2014 Kalev Lember - 3.14.2-1 +- Update to 3.14.2 + +* Sat Nov 08 2014 Kalev Lember - 3.14.1-3 +- Update the list of system apps + +* Sat Nov 01 2014 David King - 3.14.1-2 +- Rebuild for new libappstream-glib (#1156494) + +* Mon Oct 13 2014 Kalev Lember - 3.14.1-1 +- Update to 3.14.1 + +* Thu Oct 09 2014 Kalev Lember - 3.14.0-2 +- Depend on gnome-menus for app folder directory entries + +* Mon Sep 22 2014 Kalev Lember - 3.14.0-1 +- Update to 3.14.0 + +* Wed Sep 17 2014 Kalev Lember - 3.13.92-2 +- Set minimum required dependency versions (#1136343) + +* Tue Sep 16 2014 Kalev Lember - 3.13.92-1 +- Update to 3.13.92 +- Replace gnome-system-log with gnome-logs in the system apps list + +* Tue Sep 02 2014 Kalev Lember - 3.13.91-1 +- Update to 3.13.91 + +* Tue Aug 19 2014 Richard Hughes - 3.13.90-1 +- Update to 3.13.90 + +* Sat Aug 16 2014 Fedora Release Engineering - 3.13.5-0.2.git5c89189 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Mon Aug 11 2014 Kalev Lember - 3.13.5-0.1.git5c89189 +- Update to 3.13.5 git snapshot +- Ship HighContrast icons + +* Sun Aug 03 2014 Kalev Lember - 3.13.4-2 +- Replace Epiphany with Firefox in the system apps list + +* Wed Jul 23 2014 Kalev Lember - 3.13.4-1 +- Update to 3.13.4 + +* Wed Jun 25 2014 Kalev Lember - 3.13.3-1 +- Update to 3.13.3 + +* Thu Jun 12 2014 Richard Hughes - 3.13.3-0.2.git7491627 +- Depend on the newly-created appstream-data package and stop shipping + the metadata here. + +* Sat Jun 07 2014 Kalev Lember - 3.13.3-0.1.git7491627 +- Update to 3.13.3 git snapshot + +* Wed May 28 2014 Richard Hughes - 3.13.2-2 +- Rebuild with new metadata. + +* Wed May 28 2014 Kalev Lember - 3.13.2-1 +- Update to 3.13.2 + +* Thu May 15 2014 Kalev Lember - 3.13.1-4 +- Depend on gsettings-desktop-schemas + +* Mon May 12 2014 Richard Hughes - 3.13.1-3 +- Update the metadata and use appstream-util to install the metadata. + +* Wed May 07 2014 Kalev Lember - 3.13.1-2 +- Drop gnome-icon-theme dependency + +* Mon Apr 28 2014 Richard Hughes - 3.13.1-1 +- Update to 3.13.1 + +* Fri Apr 11 2014 Kalev Lember - 3.12.1-2 +- Rebuild with new metadata. + +* Fri Apr 11 2014 Richard Hughes - 3.12.1-1 +- Update to 3.12.1 + +* Mon Mar 24 2014 Richard Hughes - 3.12.0-1 +- Update to 3.12.0 + +* Thu Mar 20 2014 Richard Hughes - 3.11.92-1 +- Update to 3.11.92 + +* Tue Mar 18 2014 Richard Hughes - 3.11.91-2 +- Rebuild with new metadata. + +* Sat Mar 08 2014 Richard Hughes - 3.11.91-1 +- Update to 3.11.91 + +* Tue Feb 18 2014 Richard Hughes - 3.11.90-1 +- Update to 3.11.90 + +* Mon Feb 03 2014 Richard Hughes - 3.11.5-2 +- Require epiphany-runtime rather than the full application + +* Mon Feb 03 2014 Richard Hughes - 3.11.5-1 +- Update to 3.11.5 + +* Thu Jan 30 2014 Richard Hughes - 3.11.4-3 +- Rebuild for libpackagekit-glib soname bump + +* Wed Jan 22 2014 Richard Hughes - 3.11.4-2 +- Rebuild with metadata that has the correct screenshot url. + +* Thu Jan 16 2014 Richard Hughes - 3.11.4-1 +- Update to 3.11.4 + +* Tue Dec 17 2013 Richard Hughes - 3.11.3-1 +- Update to 3.11.3 + +* Tue Nov 19 2013 Richard Hughes - 3.11.2-1 +- Update to 3.11.2 + +* Tue Oct 29 2013 Richard Hughes - 3.11.1-1 +- Update to 3.11.1 +- Add a gnome shell search provider +- Add a module to submit the user rating to the fedora-tagger web service +- Add support for 'missing' codecs that we know exist but we can't install +- Add support for epiphany web applications +- Handle offline installation sensibly +- Save the user rating if the user clicks the rating stars +- Show a modal error message if install or remove actions failed +- Show a star rating on the application details page +- Show font screenshots +- Show more detailed version numbers when required +- Show screenshots to each application + +* Wed Sep 25 2013 Richard Hughes 3.10.0-1 +- New upstream release. +- New metadata for fedora, updates and updates-testing +- Add a plugin to query the PackageKit prepared-update file directly +- Do not clear the offline-update trigger if rebooting succeeded +- Do not load incompatible projects when parsing AppStream data +- Lots of updated translations +- Show the window right away when starting + +* Fri Sep 13 2013 Richard Hughes 3.9.3-1 +- New upstream release. +- Lots of new and fixed UI and updated metadata for Fedora 20 + +* Tue Sep 03 2013 Richard Hughes 3.9.2-1 +- New upstream release. +- Allow stock items in the AppStream XML +- Extract the AppStream URL and description from the XML +- Only present the window when the overview is complete +- Return the subcategories sorted by name + +* Mon Sep 02 2013 Richard Hughes 3.9.1-1 +- New upstream release which is a technical preview for the alpha. + +* Sun Sep 01 2013 Richard Hughes 0.1-3 +- Use buildroot not RPM_BUILD_ROOT +- Own all gnome-software directories +- Drop gtk-update-icon-cache requires and the mime database functionality + +* Thu Aug 29 2013 Richard Hughes 0.1-2 +- Add call to desktop-file-validate and fix other review comments. + +* Wed Aug 28 2013 Richard Hughes 0.1-1 +- First release for Fedora package review + diff --git a/rpminspect.yaml b/rpminspect.yaml deleted file mode 100644 index ef70fcd..0000000 --- a/rpminspect.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -runpath: - allowed_paths: - - /usr/lib/gnome-software - - /usr/lib/rpm-ostree - - /usr/lib64/gnome-software - - /usr/lib64/rpm-ostree diff --git a/sources b/sources index c24e704..8c11af2 100644 --- a/sources +++ b/sources @@ -1 +1,3 @@ -SHA512 (gnome-software-49.3.tar.xz) = 0414ea55ad3b83bcd50514985e3d2026207801c040934805bb26d162e88f0619a9d70aa6ba324a29acfab02573490b928c83594b54f57ec2a8ad00e6c127f657 +SHA512 (gnome-software-3.28.2.tar.xz) = ca22ea589c19f3c5aae32fb2540646d3b3c8faf86f857dd414caf61b716bc50dea16281955c594a629f684da5ac87eeada6ac68247a9b0545600b739fa436dd7 +SHA512 (f28.png) = 468ece7c735d3f11f863c347aa22752ff5805effa47e4e3b97dd3b777ab5705423edc4e51c3491885128e684f243362e79f9f8ec42277bd0673abea48a36d5b2 +SHA512 (f29.png) = 7bcb036c9edf069e65edeabbb08460321dedf996d40ae71a8e1e1ba4131288e60a15c5c7e3115229a2850cade018f0ff935da3ee439768597e37830cad5e77bb