diff --git a/.gitignore b/.gitignore index ba3b2b3..b4345b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /gnome-software-*.tar.xz -/gnome-software-*-build diff --git a/0001-crash-under-gs_appstream_gather_merge_data.patch b/0001-crash-under-gs_appstream_gather_merge_data.patch new file mode 100644 index 0000000..b52cefd --- /dev/null +++ b/0001-crash-under-gs_appstream_gather_merge_data.patch @@ -0,0 +1,1468 @@ +From f1140fd98666fbdb35dfe13a6b3f58fedcb1ad0b Mon Sep 17 00:00:00 2001 +Date: Mon, 29 Apr 2024 17:37:57 +0200 +Subject: [PATCH 1/3] gs-plugin-appstream: Simplify XbSilo locking + +Instead of holding RW lock on the internal silo variable, simply return +a reference to it and work with it like with an immutable object. That +simplifies locking and makes the code cleaner. It can also make the code +quicker, in some cases. +--- + plugins/core/gs-plugin-appstream.c | 194 ++++++++++++++++------------- + 1 file changed, 107 insertions(+), 87 deletions(-) + +diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c +index 7ccbac12a5..5749a175f3 100644 +--- a/plugins/core/gs-plugin-appstream.c ++++ b/plugins/core/gs-plugin-appstream.c +@@ -36,7 +36,7 @@ struct _GsPluginAppstream + GsWorkerThread *worker; /* (owned) */ + + XbSilo *silo; +- GRWLock silo_lock; ++ GMutex silo_lock; + gchar *silo_filename; + GHashTable *silo_installed_by_desktopid; + GHashTable *silo_installed_by_id; +@@ -65,7 +65,7 @@ gs_plugin_appstream_dispose (GObject *object) + g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); + g_clear_pointer (&self->silo_installed_by_id, g_hash_table_unref); + g_clear_object (&self->settings); +- g_rw_lock_clear (&self->silo_lock); ++ g_mutex_clear (&self->silo_lock); + g_clear_object (&self->worker); + g_clear_pointer (&self->file_monitors, g_ptr_array_unref); + +@@ -77,9 +77,7 @@ gs_plugin_appstream_init (GsPluginAppstream *self) + { + GApplication *application = g_application_get_default (); + +- /* XbSilo needs external locking as we destroy the silo and build a new +- * one when something changes */ +- g_rw_lock_init (&self->silo_lock); ++ g_mutex_init (&self->silo_lock); + + /* require settings */ + self->settings = g_settings_new ("org.gnome.software"); +@@ -530,10 +528,13 @@ gs_add_appstream_metainfo_location (GPtrArray *locations, const gchar *root) + g_build_filename (root, "appdata", NULL)); + } + +-static gboolean +-gs_plugin_appstream_check_silo (GsPluginAppstream *self, +- GCancellable *cancellable, +- GError **error) ++static XbSilo * ++gs_plugin_appstream_ref_silo (GsPluginAppstream *self, ++ gchar **out_silo_filename, ++ GHashTable **out_silo_installed_by_desktopid, ++ GHashTable **out_silo_installed_by_id, ++ GCancellable *cancellable, ++ GError **error) + { + const gchar *test_xml; + g_autofree gchar *blobfn = NULL; +@@ -541,21 +542,25 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + g_autoptr(XbNode) n = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) installed = NULL; +- g_autoptr(GRWLockReaderLocker) reader_locker = NULL; +- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; ++ g_autoptr(GMutexLocker) locker = NULL; + g_autoptr(GPtrArray) parent_appdata = g_ptr_array_new_with_free_func (g_free); + g_autoptr(GPtrArray) parent_appstream = NULL; + g_autoptr(GMainContext) old_thread_default = NULL; + +- reader_locker = g_rw_lock_reader_locker_new (&self->silo_lock); ++ locker = g_mutex_locker_new (&self->silo_lock); + /* everything is okay */ + if (self->silo != NULL && xb_silo_is_valid (self->silo) && +- g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) +- return TRUE; +- g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); ++ g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) { ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL) ++ *out_silo_installed_by_desktopid = self->silo_installed_by_desktopid ? g_hash_table_ref (self->silo_installed_by_desktopid) : NULL; ++ if (out_silo_installed_by_id != NULL) ++ *out_silo_installed_by_id = self->silo_installed_by_id ? g_hash_table_ref (self->silo_installed_by_id) : NULL; ++ return g_object_ref (self->silo); ++ } + + /* drat! silo needs regenerating */ +- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); + reload: + g_clear_object (&self->silo); + g_clear_pointer (&self->silo_filename, g_free); +@@ -594,7 +599,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!xb_builder_source_load_xml (source, test_xml, + XB_BUILDER_SOURCE_FLAG_NONE, + error)) +- return FALSE; ++ return NULL; + fixup1 = xb_builder_fixup_new ("AddOriginKeywords", + gs_plugin_appstream_add_origin_keyword_cb, + self, NULL); +@@ -640,7 +645,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!gs_plugin_appstream_load_appstream (self, builder, fn, cancellable, error)) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + } + for (guint i = 0; i < parent_appdata->len; i++) { +@@ -648,7 +653,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!gs_plugin_appstream_load_appdata (self, builder, fn, cancellable, error)) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + } + for (guint i = 0; i < parent_desktop->len; i++) { +@@ -657,7 +662,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (!gs_appstream_load_desktop_files (builder, dir, NULL, &file_monitor, cancellable, error)) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + gs_plugin_appstream_maybe_store_file_monitor (self, file_monitor); + } +@@ -678,7 +683,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + GS_UTILS_CACHE_FLAG_CREATE_DIRECTORY, + error); + if (blobfn == NULL) +- return FALSE; ++ return NULL; + file = g_file_new_for_path (blobfn); + g_debug ("ensuring %s", blobfn); + +@@ -696,7 +701,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + if (self->silo == NULL) { + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); +- return FALSE; ++ return NULL; + } + + if (old_thread_default != NULL) +@@ -717,7 +722,7 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + GS_PLUGIN_ERROR, + GS_PLUGIN_ERROR_NOT_SUPPORTED, + "No AppStream data found"); +- return FALSE; ++ return NULL; + } + + g_clear_object (&n); +@@ -768,7 +773,13 @@ gs_plugin_appstream_check_silo (GsPluginAppstream *self, + } + + /* success */ +- return TRUE; ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL) ++ *out_silo_installed_by_desktopid = self->silo_installed_by_desktopid ? g_hash_table_ref (self->silo_installed_by_desktopid) : NULL; ++ if (out_silo_installed_by_id != NULL) ++ *out_silo_installed_by_id = self->silo_installed_by_id ? g_hash_table_ref (self->silo_installed_by_id) : NULL; ++ return g_object_ref (self->silo); + } + + static void +@@ -776,7 +787,6 @@ gs_plugin_appstream_reload (GsPlugin *plugin) + { + GsPluginAppstream *self; + g_autoptr(GsAppList) list = NULL; +- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; + guint sz; + + g_return_if_fail (GS_IS_PLUGIN_APPSTREAM (plugin)); +@@ -790,9 +800,8 @@ gs_plugin_appstream_reload (GsPlugin *plugin) + } + + self = GS_PLUGIN_APPSTREAM (plugin); +- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); +- if (self->silo != NULL) +- xb_silo_invalidate (self->silo); ++ /* simpler than getting the silo and setting it invalid */ ++ g_atomic_int_inc (&self->file_monitor_stamp); + } + + static gint +@@ -834,11 +843,13 @@ setup_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) + g_task_return_error (task, g_steal_pointer (&local_error)); + else + g_task_return_boolean (task, TRUE); +@@ -910,21 +921,21 @@ url_to_app_thread_cb (GTask *task, + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); + GsPluginUrlToAppData *data = task_data; + g_autoptr(GsAppList) list = NULL; +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); + list = gs_app_list_new (); + +- if (gs_appstream_url_to_app (GS_PLUGIN (self), self->silo, list, data->url, cancellable, &local_error)) ++ if (gs_appstream_url_to_app (GS_PLUGIN (self), silo, list, data->url, cancellable, &local_error)) + g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); + else + g_task_return_error (task, g_steal_pointer (&local_error)); +@@ -1006,17 +1017,14 @@ gs_plugin_appstream_set_compulsory_quirk (GsApp *app, XbNode *component) + static gboolean + gs_plugin_appstream_refine_state (GsPluginAppstream *self, + GsApp *app, ++ GHashTable *silo_installed_by_id, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; +- + /* Ignore apps with no ID */ +- if (gs_app_get_id (app) == NULL) ++ if (gs_app_get_id (app) == NULL || silo_installed_by_id == NULL) + return TRUE; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- if (g_hash_table_contains (self->silo_installed_by_id, gs_app_get_id (app))) ++ if (g_hash_table_contains (silo_installed_by_id, gs_app_get_id (app))) + gs_app_set_state (app, GS_APP_STATE_INSTALLED); + return TRUE; + } +@@ -1027,20 +1035,21 @@ gs_plugin_refine_from_id (GsPluginAppstream *self, + GsPluginRefineFlags flags, + GHashTable *apps_by_id, + GHashTable *apps_by_origin_and_id, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + gboolean *found, + GError **error) + { + const gchar *id, *origin; + GPtrArray *components; +- g_autoptr(GRWLockReaderLocker) locker = NULL; + + /* not enough info to find */ + id = gs_app_get_id (app); + if (id == NULL) + return TRUE; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + origin = gs_app_get_origin_appstream (app); + + /* look in AppStream then fall back to AppData */ +@@ -1056,15 +1065,15 @@ gs_plugin_refine_from_id (GsPluginAppstream *self, + + for (guint i = 0; i < components->len; i++) { + XbNode *component = g_ptr_array_index (components, i); +- if (!gs_appstream_refine_app (GS_PLUGIN (self), app, self->silo, component, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) ++ if (!gs_appstream_refine_app (GS_PLUGIN (self), app, silo, component, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->default_scope, error)) + return FALSE; + gs_plugin_appstream_set_compulsory_quirk (app, component); + } + + /* if an installed desktop or appdata file exists set to installed */ + if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) { +- if (!gs_plugin_appstream_refine_state (self, app, error)) ++ if (!gs_plugin_appstream_refine_state (self, app, silo_installed_by_id, error)) + return FALSE; + } + +@@ -1077,6 +1086,10 @@ static gboolean + gs_plugin_refine_from_pkgname (GsPluginAppstream *self, + GsApp *app, + GsPluginRefineFlags flags, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + GError **error) + { + GPtrArray *sources = gs_app_get_sources (app); +@@ -1089,33 +1102,30 @@ gs_plugin_refine_from_pkgname (GsPluginAppstream *self, + /* find all apps when matching any prefixes */ + for (guint j = 0; j < sources->len; j++) { + const gchar *pkgname = g_ptr_array_index (sources, j); +- g_autoptr(GRWLockReaderLocker) locker = NULL; + g_autoptr(GString) xpath = g_string_new (NULL); + g_autoptr(XbNode) component = NULL; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + /* prefer actual apps and then fallback to anything else */ + xb_string_append_union (xpath, "components/component[@type='desktop-application']/pkgname[text()='%s']/..", pkgname); + xb_string_append_union (xpath, "components/component[@type='console-application']/pkgname[text()='%s']/..", pkgname); + xb_string_append_union (xpath, "components/component[@type='web-application']/pkgname[text()='%s']/..", pkgname); + xb_string_append_union (xpath, "components/component/pkgname[text()='%s']/..", pkgname); +- component = xb_silo_query_first (self->silo, xpath->str, &error_local); ++ component = xb_silo_query_first (silo, xpath->str, &error_local); + if (component == NULL) { + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + continue; + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } +- if (!gs_appstream_refine_app (GS_PLUGIN (self), app, self->silo, component, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) ++ if (!gs_appstream_refine_app (GS_PLUGIN (self), app, silo, component, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->default_scope, error)) + return FALSE; + gs_plugin_appstream_set_compulsory_quirk (app, component); + } + + /* if an installed desktop or appdata file exists set to installed */ + if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) { +- if (!gs_plugin_appstream_refine_state (self, app, error)) ++ if (!gs_plugin_appstream_refine_state (self, app, silo_installed_by_id, error)) + return FALSE; + } + +@@ -1153,6 +1163,10 @@ static gboolean refine_wildcard (GsPluginAppstream *self, + GsAppList *list, + GsPluginRefineFlags refine_flags, + GHashTable *apps_by_id, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + GCancellable *cancellable, + GError **error); + +@@ -1171,13 +1185,17 @@ refine_thread_cb (GTask *task, + g_autoptr(GHashTable) apps_by_id = NULL; + g_autoptr(GHashTable) apps_by_origin_and_id = NULL; + g_autoptr(GPtrArray) components = NULL; +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autofree gchar *silo_filename = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autoptr(GHashTable) silo_installed_by_id = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, &silo_filename, &silo_installed_by_desktopid, &silo_installed_by_id, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1185,9 +1203,7 @@ refine_thread_cb (GTask *task, + apps_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); + apps_by_origin_and_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- components = xb_silo_query (self->silo, "components/component/id", 0, NULL); ++ components = xb_silo_query (silo, "components/component/id", 0, NULL); + for (guint i = 0; components != NULL && i < components->len; i++) { + XbNode *node = g_ptr_array_index (components, i); + g_autoptr(XbNode) component_node = xb_node_get_parent (node); +@@ -1234,7 +1250,7 @@ refine_thread_cb (GTask *task, + } + + g_clear_pointer (&components, g_ptr_array_unref); +- components = xb_silo_query (self->silo, "component/id", 0, NULL); ++ components = xb_silo_query (silo, "component/id", 0, NULL); + for (guint i = 0; components != NULL && i < components->len; i++) { + XbNode *node = g_ptr_array_index (components, i); + g_autoptr(XbNode) component_node = xb_node_get_parent (node); +@@ -1262,12 +1278,14 @@ refine_thread_cb (GTask *task, + continue; + + /* find by ID then fall back to package name */ +- if (!gs_plugin_refine_from_id (self, app, flags, apps_by_id, apps_by_origin_and_id, &found, &local_error)) { ++ if (!gs_plugin_refine_from_id (self, app, flags, apps_by_id, apps_by_origin_and_id, silo, silo_filename, ++ silo_installed_by_desktopid, silo_installed_by_id, &found, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + if (!found) { +- if (!gs_plugin_refine_from_pkgname (self, app, flags, &local_error)) { ++ if (!gs_plugin_refine_from_pkgname (self, app, flags, silo, silo_filename, ++ silo_installed_by_desktopid, silo_installed_by_id, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1286,7 +1304,8 @@ refine_thread_cb (GTask *task, + GsApp *app = gs_app_list_index (app_list, j); + + if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && +- !refine_wildcard (self, app, list, flags, apps_by_id, cancellable, &local_error)) { ++ !refine_wildcard (self, app, list, flags, apps_by_id, silo, silo_filename, ++ silo_installed_by_desktopid, silo_installed_by_id,cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1311,20 +1330,21 @@ refine_wildcard (GsPluginAppstream *self, + GsAppList *list, + GsPluginRefineFlags refine_flags, + GHashTable *apps_by_id, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, ++ GHashTable *silo_installed_by_id, + GCancellable *cancellable, + GError **error) + { + const gchar *id; + GPtrArray *components; +- g_autoptr(GRWLockReaderLocker) locker = NULL; + + /* not enough info to find */ + id = gs_app_get_id (app); + if (id == NULL) + return TRUE; + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + components = g_hash_table_lookup (apps_by_id, id); + if (components == NULL) + return TRUE; +@@ -1333,20 +1353,20 @@ refine_wildcard (GsPluginAppstream *self, + g_autoptr(GsApp) new = NULL; + + /* new app */ +- new = gs_appstream_create_app (GS_PLUGIN (self), self->silo, component, self->silo_filename ? self->silo_filename : "", ++ new = gs_appstream_create_app (GS_PLUGIN (self), silo, component, silo_filename ? silo_filename : "", + self->default_scope, error); + if (new == NULL) + return FALSE; + gs_app_set_scope (new, AS_COMPONENT_SCOPE_SYSTEM); + gs_app_subsume_metadata (new, app); +- if (!gs_appstream_refine_app (GS_PLUGIN (self), new, self->silo, component, refine_flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->default_scope, error)) ++ if (!gs_appstream_refine_app (GS_PLUGIN (self), new, silo, component, refine_flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->default_scope, error)) + return FALSE; + gs_plugin_appstream_set_compulsory_quirk (new, component); + + /* if an installed desktop or appdata file exists set to installed */ + if (gs_app_get_state (new) == GS_APP_STATE_UNKNOWN) { +- if (!gs_plugin_appstream_refine_state (self, new, error)) ++ if (!gs_plugin_appstream_refine_state (self, new, silo_installed_by_id, error)) + return FALSE; + } + +@@ -1398,21 +1418,20 @@ refine_categories_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + GsPluginRefineCategoriesData *data = task_data; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- if (!gs_appstream_refine_category_sizes (self->silo, data->list, cancellable, &local_error)) { ++ if (!gs_appstream_refine_category_sizes (silo, data->list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1462,7 +1481,7 @@ list_apps_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GsAppList) list = gs_app_list_new (); + GsPluginListAppsData *data = task_data; + GDateTime *released_since = NULL; +@@ -1516,65 +1535,64 @@ list_apps_thread_cb (GTask *task, + } + + /* check silo is valid */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) { ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + +- locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- + if (released_since != NULL && +- !gs_appstream_add_recent (GS_PLUGIN (self), self->silo, list, age_secs, ++ !gs_appstream_add_recent (GS_PLUGIN (self), silo, list, age_secs, + cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (is_curated != GS_APP_QUERY_TRISTATE_UNSET && +- !gs_appstream_add_popular (self->silo, list, cancellable, &local_error)) { ++ !gs_appstream_add_popular (silo, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (is_featured != GS_APP_QUERY_TRISTATE_UNSET && +- !gs_appstream_add_featured (self->silo, list, cancellable, &local_error)) { ++ !gs_appstream_add_featured (silo, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (category != NULL && +- !gs_appstream_add_category_apps (GS_PLUGIN (self), self->silo, category, list, cancellable, &local_error)) { ++ !gs_appstream_add_category_apps (GS_PLUGIN (self), silo, category, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (is_installed == GS_APP_QUERY_TRISTATE_TRUE && +- !gs_appstream_add_installed (GS_PLUGIN (self), self->silo, list, cancellable, &local_error)) { ++ !gs_appstream_add_installed (GS_PLUGIN (self), silo, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (deployment_featured != NULL && +- !gs_appstream_add_deployment_featured (self->silo, deployment_featured, list, ++ !gs_appstream_add_deployment_featured (silo, deployment_featured, list, + cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (developers != NULL && +- !gs_appstream_search_developer_apps (GS_PLUGIN (self), self->silo, developers, list, cancellable, &local_error)) { ++ !gs_appstream_search_developer_apps (GS_PLUGIN (self), silo, developers, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (keywords != NULL && +- !gs_appstream_search (GS_PLUGIN (self), self->silo, keywords, list, cancellable, &local_error)) { ++ !gs_appstream_search (GS_PLUGIN (self), silo, keywords, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + + if (alternate_of != NULL && +- !gs_appstream_add_alternates (self->silo, alternate_of, list, cancellable, &local_error)) { ++ !gs_appstream_add_alternates (silo, alternate_of, list, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } +@@ -1623,12 +1641,14 @@ refresh_metadata_thread_cb (GTask *task, + GCancellable *cancellable) + { + GsPluginAppstream *self = GS_PLUGIN_APPSTREAM (source_object); ++ g_autoptr(XbSilo) silo = NULL; + g_autoptr(GError) local_error = NULL; + + assert_in_worker (self); + + /* Checking the silo will refresh it if needed. */ +- if (!gs_plugin_appstream_check_silo (self, cancellable, &local_error)) ++ silo = gs_plugin_appstream_ref_silo (self, NULL, NULL, NULL, cancellable, &local_error); ++ if (silo == NULL) + g_task_return_error (task, g_steal_pointer (&local_error)); + else + g_task_return_boolean (task, TRUE); +-- +GitLab + + +From cf04c32b5fb1fd267096fad8dd11a22429ad788a Mon Sep 17 00:00:00 2001 +Date: Mon, 29 Apr 2024 17:45:08 +0200 +Subject: [PATCH 2/3] gs-plugin-appstream: Rename file_monitor_stamp to + silo_change_stamp + +Since it's used also in the gs_plugin_appstream_reload() and works +like a change indicator, name it appropriately to avoid confusion. +--- + plugins/core/gs-plugin-appstream.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c +index 5749a175f3..99fe5a1432 100644 +--- a/plugins/core/gs-plugin-appstream.c ++++ b/plugins/core/gs-plugin-appstream.c +@@ -46,8 +46,8 @@ struct _GsPluginAppstream + GPtrArray *file_monitors; /* (owned) (element-type GFileMonitor) */ + /* The stamps help to avoid locking the silo lock in the main thread + and also to detect changes while loading other appstream data. */ +- gint file_monitor_stamp; /* the file monitor stamp, increased on every file monitor change */ +- gint file_monitor_stamp_current; /* the currently known file monitor stamp, checked for changes */ ++ gint silo_change_stamp; /* the silo change stamp, increased on every silo change */ ++ gint silo_change_stamp_current; /* the currently known silo change stamp, checked for changes */ + }; + + G_DEFINE_TYPE (GsPluginAppstream, gs_plugin_appstream, GS_TYPE_PLUGIN) +@@ -224,7 +224,7 @@ gs_plugin_appstream_file_monitor_changed_cb (GFileMonitor *monitor, + gpointer user_data) + { + GsPluginAppstream *self = user_data; +- g_atomic_int_inc (&self->file_monitor_stamp); ++ g_atomic_int_inc (&self->silo_change_stamp); + } + + static void +@@ -550,7 +550,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, + locker = g_mutex_locker_new (&self->silo_lock); + /* everything is okay */ + if (self->silo != NULL && xb_silo_is_valid (self->silo) && +- g_atomic_int_get (&self->file_monitor_stamp_current) == g_atomic_int_get (&self->file_monitor_stamp)) { ++ g_atomic_int_get (&self->silo_change_stamp_current) == g_atomic_int_get (&self->silo_change_stamp)) { + if (out_silo_filename != NULL) + *out_silo_filename = g_strdup (self->silo_filename); + if (out_silo_installed_by_desktopid != NULL) +@@ -568,7 +568,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, + g_clear_pointer (&blobfn, g_free); + self->default_scope = AS_COMPONENT_SCOPE_UNKNOWN; + g_ptr_array_set_size (self->file_monitors, 0); +- g_atomic_int_set (&self->file_monitor_stamp_current, g_atomic_int_get (&self->file_monitor_stamp)); ++ g_atomic_int_set (&self->silo_change_stamp_current, g_atomic_int_get (&self->silo_change_stamp)); + + /* FIXME: https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1422 */ + old_thread_default = g_main_context_ref_thread_default (); +@@ -707,7 +707,7 @@ gs_plugin_appstream_ref_silo (GsPluginAppstream *self, + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); + +- if (g_atomic_int_get (&self->file_monitor_stamp_current) != g_atomic_int_get (&self->file_monitor_stamp)) { ++ if (g_atomic_int_get (&self->silo_change_stamp_current) != g_atomic_int_get (&self->silo_change_stamp)) { + g_ptr_array_set_size (parent_appdata, 0); + g_ptr_array_set_size (parent_appstream, 0); + g_debug ("appstream: File monitors reported change while loading appstream data, reloading..."); +@@ -800,8 +800,8 @@ gs_plugin_appstream_reload (GsPlugin *plugin) + } + + self = GS_PLUGIN_APPSTREAM (plugin); +- /* simpler than getting the silo and setting it invalid */ +- g_atomic_int_inc (&self->file_monitor_stamp); ++ /* Invalidate the reference to the current silo */ ++ g_atomic_int_inc (&self->silo_change_stamp); + } + + static gint +-- +GitLab + + +From 59b301b8a878f3e81359909a8d4107715eb60a35 Mon Sep 17 00:00:00 2001 +Date: Tue, 30 Apr 2024 13:13:36 +0200 +Subject: [PATCH 3/3] flatpak: Simplify XbSilo locking + +Instead of holding RW lock on the internal silo variable, simply return +a reference to it and work with it like with an immutable object. That +simplifies locking and makes the code cleaner. It can also make the code +quicker, in some cases. +--- + plugins/flatpak/gs-flatpak.c | 307 +++++++++++++++++++---------------- + 1 file changed, 163 insertions(+), 144 deletions(-) + +diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c +index 2e175fd08e..9893cf88b7 100644 +--- a/plugins/flatpak/gs-flatpak.c ++++ b/plugins/flatpak/gs-flatpak.c +@@ -54,9 +54,11 @@ struct _GsFlatpak { + AsComponentScope scope; + GsPlugin *plugin; + XbSilo *silo; +- GRWLock silo_lock; ++ GMutex silo_lock; + gchar *silo_filename; + GHashTable *silo_installed_by_desktopid; ++ gint silo_change_stamp; ++ gint silo_change_stamp_current; + gchar *id; + guint changed_id; + GHashTable *app_silos; +@@ -654,10 +656,7 @@ gs_flatpak_create_source (GsFlatpak *self, FlatpakRemote *xremote) + static void + gs_flatpak_invalidate_silo (GsFlatpak *self) + { +- g_rw_lock_writer_lock (&self->silo_lock); +- if (self->silo != NULL) +- xb_silo_invalidate (self->silo); +- g_rw_lock_writer_unlock (&self->silo_lock); ++ g_atomic_int_inc (&self->silo_change_stamp); + } + + static void +@@ -1085,32 +1084,39 @@ gs_flatpak_rescan_installed (GsFlatpak *self, + g_debug ("Failed to read flatpak .desktop files in %s: %s", path, error_local->message); + } + +-static gboolean +-gs_flatpak_rescan_appstream_store (GsFlatpak *self, +- gboolean interactive, +- GCancellable *cancellable, +- GError **error) ++static XbSilo * ++gs_flatpak_ref_silo (GsFlatpak *self, ++ gboolean interactive, ++ gchar **out_silo_filename, ++ GHashTable **out_silo_installed_by_desktopid, ++ GCancellable *cancellable, ++ GError **error) + { + g_autofree gchar *blobfn = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) xremotes = NULL; + g_autoptr(GPtrArray) desktop_paths = NULL; +- g_autoptr(GRWLockReaderLocker) reader_locker = NULL; +- g_autoptr(GRWLockWriterLocker) writer_locker = NULL; ++ g_autoptr(GMutexLocker) locker = NULL; + g_autoptr(XbBuilder) builder = NULL; + g_autoptr(GMainContext) old_thread_default = NULL; + +- reader_locker = g_rw_lock_reader_locker_new (&self->silo_lock); ++ locker = g_mutex_locker_new (&self->silo_lock); + /* everything is okay */ +- if (self->silo != NULL && xb_silo_is_valid (self->silo)) +- return TRUE; +- g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); ++ 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)) { ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL && self->silo_installed_by_desktopid) ++ *out_silo_installed_by_desktopid = g_hash_table_ref (self->silo_installed_by_desktopid); ++ return g_object_ref (self->silo); ++ } + + /* drat! silo needs regenerating */ +- writer_locker = g_rw_lock_writer_locker_new (&self->silo_lock); ++ reload: + g_clear_object (&self->silo); + g_clear_pointer (&self->silo_filename, g_free); + g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); ++ g_atomic_int_set (&self->silo_change_stamp_current, g_atomic_int_get (&self->silo_change_stamp)); + + /* FIXME: https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1422 */ + old_thread_default = g_main_context_ref_thread_default (); +@@ -1138,7 +1144,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + error); + if (xremotes == NULL) { + gs_flatpak_error_convert (error); +- return FALSE; ++ return NULL; + } + for (guint i = 0; i < xremotes->len; i++) { + g_autoptr(GError) error_local = NULL; +@@ -1152,7 +1158,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + flatpak_remote_get_name (xremote), error_local->message); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + gs_flatpak_error_convert (error); +- return FALSE; ++ return NULL; + } + } + } +@@ -1176,7 +1182,7 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + GS_UTILS_CACHE_FLAG_CREATE_DIRECTORY, + error); + if (blobfn == NULL) +- return FALSE; ++ return NULL; + file = g_file_new_for_path (blobfn); + g_debug ("ensuring %s", blobfn); + +@@ -1195,6 +1201,17 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + if (old_thread_default != NULL) + g_main_context_push_thread_default (old_thread_default); + ++ if (g_atomic_int_get (&self->silo_change_stamp_current) != g_atomic_int_get (&self->silo_change_stamp)) { ++ g_clear_pointer (&blobfn, g_free); ++ g_clear_pointer (&xremotes, g_ptr_array_unref); ++ g_clear_pointer (&desktop_paths, g_ptr_array_unref); ++ g_clear_pointer (&old_thread_default, g_main_context_unref); ++ g_clear_object (&file); ++ g_clear_object (&builder); ++ g_debug ("flatpak: Reported change while loading appstream data, reloading..."); ++ goto reload; ++ } ++ + if (self->silo != NULL) { + g_autoptr(GPtrArray) installed = NULL; + g_autoptr(XbNode) info_filename = NULL; +@@ -1218,66 +1235,46 @@ gs_flatpak_rescan_appstream_store (GsFlatpak *self, + info_filename = xb_silo_query_first (self->silo, "/info/filename", NULL); + if (info_filename != NULL) + self->silo_filename = g_strdup (xb_node_get_text (info_filename)); ++ ++ if (out_silo_filename != NULL) ++ *out_silo_filename = g_strdup (self->silo_filename); ++ if (out_silo_installed_by_desktopid != NULL && self->silo_installed_by_desktopid) ++ *out_silo_installed_by_desktopid = g_hash_table_ref (self->silo_installed_by_desktopid); ++ return g_object_ref (self->silo); + } + +- /* success */ +- return self->silo != NULL; ++ return NULL; + } + + static gboolean + gs_flatpak_rescan_app_data (GsFlatpak *self, + gboolean interactive, ++ XbSilo **out_silo, ++ gchar **out_silo_filename, ++ GHashTable **out_silo_installed_by_desktopid, + GCancellable *cancellable, + GError **error) + { ++ g_autoptr(XbSilo) silo = NULL; ++ + if (self->requires_full_rescan) { + gboolean res = gs_flatpak_refresh (self, 60, interactive, cancellable, error); +- if (res) ++ if (res) { + self->requires_full_rescan = FALSE; +- else ++ } else { + gs_flatpak_internal_data_changed (self); +- return res; ++ return res; ++ } + } + +- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { ++ silo = gs_flatpak_ref_silo (self, interactive, out_silo_filename, out_silo_installed_by_desktopid, cancellable, error); ++ if (silo == NULL) { + gs_flatpak_internal_data_changed (self); + return FALSE; + } + +- return TRUE; +-} +- +-/* Returns with a read lock held on @self->silo_lock on success. +- The *locker should be NULL when being called. */ +-static gboolean +-ensure_flatpak_silo_with_locker (GsFlatpak *self, +- GRWLockReaderLocker **locker, +- gboolean interactive, +- GCancellable *cancellable, +- GError **error) +-{ +- /* should not hold the lock when called */ +- g_return_val_if_fail (*locker == NULL, FALSE); +- +- /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) +- return FALSE; +- +- *locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- +- while (self->silo == NULL) { +- g_clear_pointer (locker, g_rw_lock_reader_locker_free); +- +- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { +- gs_flatpak_internal_data_changed (self); +- return FALSE; +- } +- +- /* At this point either rescan_appstream_store() returned an error or it successfully +- * initialised self->silo. There is the possibility that another thread will invalidate +- * the silo before we regain the lock. If so, we’ll have to rescan again. */ +- *locker = g_rw_lock_reader_locker_new (&self->silo_lock); +- } ++ if (out_silo != NULL) ++ *out_silo = g_steal_pointer (&silo); + + return TRUE; + } +@@ -1412,6 +1409,7 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, + { + gboolean ret; + g_autoptr(GPtrArray) xremotes = NULL; ++ g_autoptr(XbSilo) silo = NULL; + + /* get remotes */ + xremotes = flatpak_installation_list_remotes (gs_flatpak_get_installation (self, interactive), +@@ -1498,7 +1496,8 @@ gs_flatpak_refresh_appstream (GsFlatpak *self, + } + + /* ensure the AppStream silo is up to date */ +- if (!gs_flatpak_rescan_appstream_store (self, interactive, cancellable, error)) { ++ silo = gs_flatpak_ref_silo (self, interactive, NULL, NULL, cancellable, error); ++ if (silo == NULL) { + gs_flatpak_internal_data_changed (self); + return FALSE; + } +@@ -1646,7 +1645,7 @@ gs_flatpak_add_sources (GsFlatpak *self, + FlatpakInstallation *installation = gs_flatpak_get_installation (self, interactive); + + /* refresh */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) + return FALSE; + + /* get installed apps and runtimes */ +@@ -2080,7 +2079,7 @@ gs_flatpak_add_updates (GsFlatpak *self, + FlatpakInstallation *installation = gs_flatpak_get_installation (self, interactive); + + /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) + return FALSE; + + /* get all the updatable apps and runtimes */ +@@ -2390,10 +2389,8 @@ gs_flatpak_create_fake_ref (GsApp *app, GError **error) + return xref; + } + +-/* the _unlocked() version doesn't call gs_flatpak_rescan_app_data, +- * in order to avoid taking the writer lock on self->silo_lock */ + static gboolean +-gs_flatpak_refine_app_state_unlocked (GsFlatpak *self, ++gs_flatpak_refine_app_state_internal (GsFlatpak *self, + GsApp *app, + gboolean interactive, + gboolean force_state_update, +@@ -2505,10 +2502,10 @@ gs_flatpak_refine_app_state (GsFlatpak *self, + GError **error) + { + /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, NULL, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_flatpak_refine_app_state_unlocked (self, app, interactive, force_state_update, cancellable, error); ++ return gs_flatpak_refine_app_state_internal (self, app, interactive, force_state_update, cancellable, error); + } + + static GsApp * +@@ -2594,7 +2591,7 @@ gs_flatpak_create_runtime (GsFlatpak *self, + gs_flatpak_app_set_ref_name (app, split[0]); + gs_flatpak_app_set_ref_arch (app, split[1]); + +- if (!gs_flatpak_refine_app_state_unlocked (self, app, interactive, FALSE, NULL, &local_error)) ++ if (!gs_flatpak_refine_app_state_internal (self, app, interactive, FALSE, NULL, &local_error)) + g_debug ("Failed to refine state for runtime '%s': %s", gs_app_get_unique_id (app), local_error->message); + + /* save in the cache */ +@@ -2984,7 +2981,7 @@ gs_plugin_refine_item_size (GsFlatpak *self, + + /* is the app_runtime already installed? */ + app_runtime = gs_app_get_runtime (app); +- if (!gs_flatpak_refine_app_state_unlocked (self, ++ if (!gs_flatpak_refine_app_state_internal (self, + app_runtime, + interactive, + FALSE, +@@ -3096,6 +3093,8 @@ gs_flatpak_refine_appstream_from_bytes (GsFlatpak *self, + GBytes *appstream_gz, + GsPluginRefineFlags flags, + gboolean interactive, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, + GCancellable *cancellable, + GError **error) + { +@@ -3229,8 +3228,8 @@ gs_flatpak_refine_appstream_from_bytes (GsFlatpak *self, + } + + /* copy details from AppStream to app */ +- if (!gs_appstream_refine_app (self->plugin, app, silo, component_node, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->scope, error)) ++ if (!gs_appstream_refine_app (self->plugin, app, silo, component_node, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->scope, error)) + return FALSE; + + if (gs_app_get_origin (app)) +@@ -3324,6 +3323,8 @@ static gboolean + gs_flatpak_refine_appstream (GsFlatpak *self, + GsApp *app, + XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, + GsPluginRefineFlags flags, + GHashTable *components_by_bundle, + gboolean interactive, +@@ -3410,11 +3411,12 @@ gs_flatpak_refine_appstream (GsFlatpak *self, + appstream_gz, + flags, + interactive, ++ silo_filename, silo_installed_by_desktopid, + cancellable, error); + } + +- if (!gs_appstream_refine_app (self->plugin, app, silo, component, flags, self->silo_installed_by_desktopid, +- self->silo_filename ? self->silo_filename : "", self->scope, error)) ++ if (!gs_appstream_refine_app (self->plugin, app, silo, component, flags, silo_installed_by_desktopid, ++ silo_filename ? silo_filename : "", self->scope, error)) + return FALSE; + + /* use the default release as the version number */ +@@ -3423,13 +3425,15 @@ gs_flatpak_refine_appstream (GsFlatpak *self, + } + + static gboolean +-gs_flatpak_refine_app_unlocked (GsFlatpak *self, ++gs_flatpak_refine_app_internal (GsFlatpak *self, + GsApp *app, + GsPluginRefineFlags flags, + gboolean interactive, + gboolean force_state_update, + GHashTable *components_by_bundle, +- GRWLockReaderLocker **locker, ++ XbSilo *silo, ++ const gchar *silo_filename, ++ GHashTable *silo_installed_by_desktopid, + GCancellable *cancellable, + GError **error) + { +@@ -3440,13 +3444,9 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, + if (gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_FLATPAK) + return TRUE; + +- g_clear_pointer (locker, g_rw_lock_reader_locker_free); +- +- if (!ensure_flatpak_silo_with_locker (self, locker, interactive, cancellable, error)) +- return FALSE; +- + /* always do AppStream properties */ +- if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, components_by_bundle, interactive, cancellable, error)) ++ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, ++ flags, components_by_bundle, interactive, cancellable, error)) + return FALSE; + + /* AppStream sets the source to appname/arch/branch */ +@@ -3456,7 +3456,7 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, + } + + /* check the installed state */ +- if (!gs_flatpak_refine_app_state_unlocked (self, app, interactive, force_state_update, cancellable, error)) { ++ if (!gs_flatpak_refine_app_state_internal (self, app, interactive, force_state_update, cancellable, error)) { + g_prefix_error (error, "failed to get state: "); + return FALSE; + } +@@ -3473,7 +3473,8 @@ gs_flatpak_refine_app_unlocked (GsFlatpak *self, + + /* if the state was changed, perhaps set the version from the release */ + if (old_state != gs_app_get_state (app)) { +- if (!gs_flatpak_refine_appstream (self, app, self->silo, flags, components_by_bundle, interactive, cancellable, error)) ++ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, ++ flags, components_by_bundle, interactive, cancellable, error)) + return FALSE; + } + +@@ -3567,11 +3568,16 @@ gs_flatpak_refine_addons (GsFlatpak *self, + gboolean interactive, + GCancellable *cancellable) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autofree gchar *silo_filename = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; + g_autoptr(GsAppList) addons = NULL; + g_autoptr(GString) errors = NULL; + guint ii, sz; + ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, &silo_filename, &silo_installed_by_desktopid, cancellable, NULL)) ++ return; ++ + addons = gs_app_dup_addons (parent_app); + sz = addons ? gs_app_list_length (addons) : 0; + +@@ -3582,7 +3588,8 @@ gs_flatpak_refine_addons (GsFlatpak *self, + if (state != gs_app_get_state (addon)) + continue; + +- if (!gs_flatpak_refine_app_unlocked (self, addon, flags, interactive, TRUE, NULL, &locker, cancellable, &local_error)) { ++ if (!gs_flatpak_refine_app_internal (self, addon, flags, interactive, TRUE, NULL, silo, silo_filename, ++ silo_installed_by_desktopid, cancellable, &local_error)) { + if (errors) + g_string_append_c (errors, '\n'); + else +@@ -3614,13 +3621,16 @@ gs_flatpak_refine_app (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autofree gchar *silo_filename = NULL; + + /* ensure valid */ +- if (!gs_flatpak_rescan_app_data (self, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, &silo_filename, &silo_installed_by_desktopid, cancellable, error)) + return FALSE; + +- return gs_flatpak_refine_app_unlocked (self, app, flags, interactive, force_state_update, NULL, &locker, cancellable, error); ++ return gs_flatpak_refine_app_internal (self, app, flags, interactive, force_state_update, NULL, ++ silo, silo_filename, silo_installed_by_desktopid, cancellable, error); + } + + gboolean +@@ -3634,7 +3644,9 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + const gchar *id; + GPtrArray* components = NULL; + g_autoptr(GError) error_local = NULL; +- g_autoptr(GRWLockReaderLocker) locker = 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); + +@@ -3643,7 +3655,8 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + if (id == NULL) + return TRUE; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); ++ if (silo == NULL) + return FALSE; + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardQuerySilo, "Flatpak (query silo)", NULL); +@@ -3653,7 +3666,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *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 (self->silo, "components/component/id", 0, &error_local); ++ 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; +@@ -3686,7 +3699,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + 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 (self->silo, "/components/component/bundle[@type='flatpak']", 0, NULL); ++ 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); +@@ -3709,7 +3722,7 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + g_autoptr(GsApp) new = NULL; + + GS_PROFILER_BEGIN_SCOPED (FlatpakRefineWildcardCreateAppstreamApp, "Flatpak (create Appstream app)", NULL); +- new = gs_appstream_create_app (self->plugin, self->silo, component, self->silo_filename ? self->silo_filename : "", ++ new = gs_appstream_create_app (self->plugin, silo, component, silo_filename ? silo_filename : "", + self->scope, error); + GS_PROFILER_END_SCOPED (FlatpakRefineWildcardCreateAppstreamApp); + +@@ -3761,7 +3774,8 @@ gs_flatpak_refine_wildcard (GsFlatpak *self, GsApp *app, + 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_unlocked (self, new, refine_flags, interactive, FALSE, *inout_components_by_bundle, &locker, cancellable, error)) ++ if (!gs_flatpak_refine_app_internal (self, new, refine_flags, interactive, FALSE, *inout_components_by_bundle, ++ silo, silo_filename, silo_installed_by_desktopid, cancellable, error)) + return FALSE; + GS_PROFILER_END_SCOPED (FlatpakRefineWildcardRefineNewApp); + +@@ -3894,10 +3908,18 @@ gs_flatpak_file_to_app_bundle (GsFlatpak *self, + /* load AppStream */ + appstream_gz = flatpak_bundle_ref_get_appstream (xref_bundle); + if (appstream_gz != NULL) { ++ g_autofree gchar *silo_filename = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autoptr(XbSilo) tmp_silo = NULL; ++ ++ tmp_silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); ++ if (tmp_silo == NULL) ++ return NULL; + if (!gs_flatpak_refine_appstream_from_bytes (self, app, NULL, NULL, + appstream_gz, + GS_PLUGIN_REFINE_FLAGS_REQUIRE_ID, + interactive, ++ silo_filename, silo_installed_by_desktopid, + cancellable, error)) + return NULL; + } else { +@@ -3995,6 +4017,9 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self, + g_autoptr(GsApp) app = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbSilo) silo = NULL; ++ g_autoptr(XbSilo) tmp_silo = NULL; ++ g_autoptr(GHashTable) silo_installed_by_desktopid = NULL; ++ g_autofree gchar *silo_filename = NULL; + g_autofree gchar *origin_url = NULL; + g_autofree gchar *ref_comment = NULL; + g_autofree gchar *ref_description = NULL; +@@ -4274,8 +4299,12 @@ gs_flatpak_file_to_app_ref (GsFlatpak *self, + g_debug ("showing AppStream data: %s", xml); + } + ++ tmp_silo = gs_flatpak_ref_silo (self, interactive, &silo_filename, &silo_installed_by_desktopid, cancellable, error); ++ if (tmp_silo == NULL) ++ return NULL; ++ + /* get extra AppStream data if available */ +- if (!gs_flatpak_refine_appstream (self, app, silo, ++ if (!gs_flatpak_refine_appstream (self, app, silo, silo_filename, silo_installed_by_desktopid, + GS_PLUGIN_REFINE_FLAGS_MASK, + NULL, + interactive, +@@ -4296,17 +4325,16 @@ gs_flatpak_search (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; + g_autoptr(GMutexLocker) app_silo_locker = NULL; + g_autoptr(GPtrArray) silos_to_remove = g_ptr_array_new (); ++ g_autoptr(XbSilo) silo = NULL; + GHashTableIter iter; + gpointer key, value; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_search (self->plugin, self->silo, values, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_search (self->plugin, silo, values, list_tmp, cancellable, error)) + return FALSE; + + gs_flatpak_ensure_remote_title (self, interactive, cancellable); +@@ -4353,8 +4381,8 @@ gs_flatpak_search (GsFlatpak *self, + } + + for (guint i = 0; i < silos_to_remove->len; i++) { +- const char *silo = g_ptr_array_index (silos_to_remove, i); +- g_hash_table_remove (self->app_silos, silo); ++ const char *app_ref = g_ptr_array_index (silos_to_remove, i); ++ g_hash_table_remove (self->app_silos, app_ref); + } + + return TRUE; +@@ -4369,17 +4397,16 @@ gs_flatpak_search_developer_apps (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; + g_autoptr(GMutexLocker) app_silo_locker = NULL; + g_autoptr(GPtrArray) silos_to_remove = g_ptr_array_new (); ++ g_autoptr(XbSilo) silo = NULL; + GHashTableIter iter; + gpointer key, value; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_search_developer_apps (self->plugin, self->silo, values, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_search_developer_apps (self->plugin, silo, values, list_tmp, cancellable, error)) + return FALSE; + + gs_flatpak_ensure_remote_title (self, interactive, cancellable); +@@ -4426,8 +4453,8 @@ gs_flatpak_search_developer_apps (GsFlatpak *self, + } + + for (guint i = 0; i < silos_to_remove->len; i++) { +- const char *silo = g_ptr_array_index (silos_to_remove, i); +- g_hash_table_remove (self->app_silos, silo); ++ const char *app_ref = g_ptr_array_index (silos_to_remove, i); ++ g_hash_table_remove (self->app_silos, app_ref); + } + + return TRUE; +@@ -4441,14 +4468,12 @@ gs_flatpak_add_category_apps (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_appstream_add_category_apps (self->plugin, self->silo, +- category, list, +- cancellable, error); ++ return gs_appstream_add_category_apps (self->plugin, silo, category, list, cancellable, error); + } + + gboolean +@@ -4458,12 +4483,12 @@ gs_flatpak_refine_category_sizes (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_appstream_refine_category_sizes (self->silo, list, cancellable, error); ++ return gs_appstream_refine_category_sizes (silo, list, cancellable, error); + } + + gboolean +@@ -4474,13 +4499,12 @@ gs_flatpak_add_popular (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_popular (self->silo, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_add_popular (silo, list_tmp, cancellable, error)) + return FALSE; + + gs_app_list_add_list (list, list_tmp); +@@ -4496,13 +4520,12 @@ gs_flatpak_add_featured (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_featured (self->silo, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_add_featured (silo, list_tmp, cancellable, error)) + return FALSE; + + gs_app_list_add_list (list, list_tmp); +@@ -4518,12 +4541,12 @@ gs_flatpak_add_deployment_featured (GsFlatpak *self, + GCancellable *cancellable, + GError **error) + { +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- return gs_appstream_add_deployment_featured (self->silo, deployments, list, cancellable, error); ++ return gs_appstream_add_deployment_featured (silo, deployments, list, cancellable, error); + } + + gboolean +@@ -4535,13 +4558,12 @@ gs_flatpak_add_alternates (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_alternates (self->silo, app, list_tmp, +- cancellable, error)) ++ if (!gs_appstream_add_alternates (silo, app, list_tmp, cancellable, error)) + return FALSE; + + gs_app_list_add_list (list, list_tmp); +@@ -4558,13 +4580,12 @@ gs_flatpak_add_recent (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_add_recent (self->plugin, self->silo, list_tmp, age, +- cancellable, error)) ++ if (!gs_appstream_add_recent (self->plugin, silo, list_tmp, age, cancellable, error)) + return FALSE; + + gs_flatpak_claim_app_list (self, list_tmp, interactive); +@@ -4582,12 +4603,12 @@ gs_flatpak_url_to_app (GsFlatpak *self, + GError **error) + { + g_autoptr(GsAppList) list_tmp = gs_app_list_new (); +- g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbSilo) silo = NULL; + +- if (!ensure_flatpak_silo_with_locker (self, &locker, interactive, cancellable, error)) ++ if (!gs_flatpak_rescan_app_data (self, interactive, &silo, NULL, NULL, cancellable, error)) + return FALSE; + +- if (!gs_appstream_url_to_app (self->plugin, self->silo, list_tmp, url, cancellable, error)) ++ if (!gs_appstream_url_to_app (self->plugin, silo, list_tmp, url, cancellable, error)) + return FALSE; + + gs_flatpak_claim_app_list (self, list_tmp, interactive); +@@ -4638,10 +4659,8 @@ gs_flatpak_finalize (GObject *object) + g_signal_handler_disconnect (self->monitor, self->changed_id); + self->changed_id = 0; + } +- if (self->silo != NULL) +- g_object_unref (self->silo); +- if (self->monitor != NULL) +- g_object_unref (self->monitor); ++ g_clear_object (&self->silo); ++ g_clear_object (&self->monitor); + g_clear_pointer (&self->silo_filename, g_free); + g_clear_pointer (&self->silo_installed_by_desktopid, g_hash_table_unref); + +@@ -4654,7 +4673,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_rw_lock_clear (&self->silo_lock); ++ g_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); +@@ -4675,7 +4694,7 @@ gs_flatpak_init (GsFlatpak *self) + { + /* XbSilo needs external locking as we destroy the silo and build a new + * one when something changes */ +- g_rw_lock_init (&self->silo_lock); ++ g_mutex_init (&self->silo_lock); + + g_mutex_init (&self->installed_refs_mutex); + self->installed_refs = NULL; +-- +GitLab + 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-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/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..d5170a3 100644 --- a/gnome-software.spec +++ b/gnome-software.spec @@ -1,13 +1,12 @@ -%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 appstream_version 0.14.0 +%global flatpak_version 1.9.1 +%global fwupd_version 1.5.6 +%global glib2_version 2.70.0 +%global gtk4_version 4.14.0 %global json_glib_version 1.6.0 -%global libadwaita_version 1.6.0 -%global libxmlb_version 0.3.4 +%global libadwaita_version 1.6~alpha +%global libxmlb_version 0.1.7 %global packagekit_version 1.2.5 -%global dnf5_version 5.2.16 # Disable WebApps for RHEL builds %bcond webapps %[!0%{?rhel}] @@ -18,33 +17,23 @@ # 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 +%define gs_plugin_version 21 %global tarball_version %%(echo %{version} | tr '~' '.') %global __provides_exclude_from ^%{_libdir}/%{name}/plugins-%{gs_plugin_version}/.*\\.so.*$ Name: gnome-software -Version: 49.3 +Version: 47.5 Release: 1%{?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 +Source0: https://download.gnome.org/sources/gnome-software/47/%{name}-%{tarball_version}.tar.xz -%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 +Patch01: 0001-crash-under-gs_appstream_gather_merge_data.patch # ostree and flatpak not on i686 for Fedora and RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 @@ -56,7 +45,6 @@ BuildRequires: docbook-style-xsl BuildRequires: desktop-file-utils BuildRequires: gcc BuildRequires: gettext -BuildRequires: git-core BuildRequires: gtk-doc BuildRequires: itstool BuildRequires: libxslt @@ -79,9 +67,7 @@ BuildRequires: pkgconfig(libsoup-3.0) 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} @@ -89,26 +75,17 @@ BuildRequires: pkgconfig(rpm-ostree-1) %endif BuildRequires: pkgconfig(sysprof-capture-4) BuildRequires: pkgconfig(xmlb) >= %{libxmlb_version} -BuildRequires: systemd 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} -%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 # gnome-menus is needed for app folder .directory entries Requires: gnome-menus%{?_isa} Requires: gsettings-desktop-schemas%{?_isa} @@ -120,9 +97,7 @@ Requires: libadwaita >= %{libadwaita_version} Requires: librsvg2%{?_isa} Requires: libxmlb%{?_isa} >= %{libxmlb_version} -%if %{with packagekit} Recommends: PackageKit%{?_isa} >= %{packagekit_version} -%endif Recommends: %{name}-fedora-langpacks Obsoletes: gnome-software-snap < 3.33.1 @@ -174,15 +149,8 @@ This package includes the rpm-ostree backend. -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 \ @@ -235,14 +203,12 @@ FOE desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %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/org.gnome.Software.desktop %{_datadir}/bash-completion/completions/gnome-software %{_mandir}/man1/gnome-software.1* @@ -266,9 +232,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %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 @@ -283,27 +246,23 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %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} +%{_sysconfdir}/xdg/autostart/org.gnome.Software.desktop %dir %{_datadir}/swcatalog %dir %{_datadir}/swcatalog/xml +%if %{with webapps} %{_datadir}/swcatalog/xml/gnome-pwa-list-foss.xml %endif -%if %{with packagekit} %{_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 @@ -329,4 +288,995 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_datadir}/gtk-doc/html/gnome-software/ %changelog -%autochangelog +* Fri Mar 14 2025 Milan Crha - 47.5-1 +- Update to 47.5 + +* Tue Jan 14 2025 Milan Crha - 47.4-1 +- Update to 47.4 + +* Tue Jan 07 2025 Milan Crha - 47.3-1 +- Update to 47.3 + +* Mon Dec 09 2024 Milan Crha - 47.2-2 +- Resolves: #2272232 (Crash under gs_appstream_gather_merge_data()) + +* Mon Nov 25 2024 Milan Crha - 47.2-1 +- Update to 47.2 + +* Thu Oct 10 2024 Milan Crha - 47.1-1 +- Update to 47.1 + +* Thu Sep 19 2024 Milan Crha - 47.0-2 +- Resolves: #2312882 (dkms: Fix callback user data in a reload() function) + +* Fri Sep 13 2024 Milan Crha - 47.0-1 +- Update to 47.0 + +* Fri Aug 30 2024 Milan Crha - 47~rc-1 +- Update to 47.rc + +* Fri Aug 02 2024 Milan Crha - 47~beta-1 +- Update to 47.beta +- Build with DKMS/akmods plugin in Fedora + +* Thu Jul 18 2024 Fedora Release Engineering - 47~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + +* Fri Jun 28 2024 Milan Crha - 47~alpha-1 +- Update to 47.alpha + +* Fri May 24 2024 Milan Crha - 46.2-1 +- Update to 46.2 + +* Thu Apr 25 2024 Milan Crha - 46.1-1 +- Update to 46.1 + +* Fri Apr 12 2024 Adam Williamson - 46.0-2 +- Backport MR #1949 to fix upgrading + +* Mon Mar 18 2024 Milan Crha - 46.0-1 +- Update to 46.0 + +* Fri Mar 01 2024 Milan Crha - 46~rc-1 +- Update to 46.rc + +* Fri Feb 09 2024 Milan Crha - 46~beta-1 +- Update to 46.beta + +* Fri Jan 26 2024 Milan Crha - 46~alpha-4 +- Resolves: #2260294 (Split fedora-langpacks plugin into a subpackage) + +* Wed Jan 24 2024 Fedora Release Engineering - 46~alpha-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Fri Jan 19 2024 Fedora Release Engineering - 46~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Fri Jan 05 2024 Milan Crha - 46~alpha-1 +- Update to 46.alpha + +* Fri Dec 01 2023 Milan Crha - 45.2-1 +- Update to 45.2 + +* Tue Nov 07 2023 Neal Gompa - 45.1-3 +- Fix appstream_version macro for prerelease appstream 1.0 package + +* Tue Nov 07 2023 Milan Crha - 45.1-2 +- Add patch to build with appstream 1.0 + +* Fri Oct 20 2023 Milan Crha - 45.1-1 +- Update to 45.1 + +* Fri Sep 15 2023 Milan Crha - 45.0-1 +- Update to 45.0 + +* Fri Sep 01 2023 Milan Crha - 45~rc-1 +- Update to 45.rc + +* Mon Jul 31 2023 Milan Crha - 45~beta-1 +- Update to 45.beta + +* Wed Jul 19 2023 Fedora Release Engineering - 45~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Fri Jun 30 2023 Milan Crha - 45~alpha-1 +- Update to 45.alpha + +* Thu Jun 22 2023 Tomas Popela - 44.2-2 +- Disable parental control (through malcontent) and rpm-ostree support in RHEL + +* Fri May 26 2023 Milan Crha - 44.2-1 +- Update to 44.2 + +* Fri May 19 2023 Milan Crha - 44.1-2 +- Rebuild for RPM + +* Fri Apr 21 2023 Milan Crha - 44.1-1 +- Update to 44.1 + +* Sun Mar 26 2023 Yaakov Selkowitz - 44.0-3 +- Fix libsoup runtime dependency + +* Fri Mar 24 2023 Milan Crha - 44.0-2 +- Resolves: #2181367 (Prefer Fedora Flatpaks before RPM before other sources for apps) + +* Fri Mar 17 2023 Milan Crha - 44.0-1 +- Update to 44.0 + +* Fri Mar 03 2023 Milan Crha - 44~rc-1 +- Update to 44.rc + +* Thu Feb 23 2023 Adam Williamson - 44~beta-2 +- Backport MR #1635 to fix update notifications + +* Tue Feb 14 2023 Milan Crha - 44.beta-1 +- Update to 44.beta + +* Thu Feb 09 2023 Michael Catanzaro - 44~alpha-3 +- Switch to libsoup 3 + +* Thu Jan 19 2023 Fedora Release Engineering - 44~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Mon Jan 09 2023 Milan Crha - 44.alpha-1 +- Update to 44.alpha + +* Fri Dec 02 2022 Milan Crha - 43.2-1 +- Update to 43.2 + +* Tue Nov 08 2022 Milan Crha - 43.1-3 +- Also skip gnome-pwa-list-foss.xml when building without WebApps + +* Tue Nov 08 2022 Milan Crha - 43.1-2 +- Disable WebApps for RHEL builds + +* Mon Oct 24 2022 Milan Crha - 43.1-1 +- Update to 43.1 + +* Wed Oct 05 2022 Milan Crha - 43.0-3 +- Resolves: #2132292 (rpm-ostree plugin refuses to update) + +* Tue Sep 27 2022 Kalev Lember - 43.0-2 +- Rebuild to fix sysprof-capture symbols leaking into libraries consuming it + +* Fri Sep 16 2022 Milan Crha - 43.0-1 +- Update to 43.0 + +* Tue Sep 13 2022 Milan Crha - 43.rc-2 +- Resolves: #2124869 (Cannot install RPM package file) + +* Fri Sep 02 2022 Milan Crha - 43.rc-1 +- Update to 43.rc + +* Wed Aug 17 2022 Milan Crha - 43.beta-3 +- Resolves: #2119089 (No enough apps to show for the "Editor's Choice" section) + +* Mon Aug 15 2022 Milan Crha - 43.beta-2 +- Add patch for install-queue + +* Fri Aug 05 2022 Milan Crha - 43.beta-1 +- Update to 43.beta + +* Thu Jul 21 2022 Fedora Release Engineering - 43.alpha-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Thu Jul 07 2022 Adam Williamson - 43.alpha-2 +- Backport MR #1401 to fix issue #1816 and fedora-workstation #107 + +* Fri Jul 01 2022 Milan Crha - 43.alpha-1 +- Update to 43.alpha + +* Fri Jun 17 2022 Richard Hughes - 42.2-4 +- Add patch to make fwupd user requests work + +* Thu Jun 16 2022 David King - 42.2-3 +- Filter private libraries from Provides +- Use pkgconfig for BuildRequires +- Improve directory onwership + +* Mon Jun 13 2022 Milan Crha - 42.2-2 +- Add patch for crash under gs_flatpak_refine_app_unlocked() + +* Mon May 30 2022 Milan Crha - 42.2-1 +- Update to 42.2 +- Add patch to correct order of the setup of the GsShell + +* Wed Apr 27 2022 Milan Crha - 42.1-1 +- Update to 42.1 + +* Fri Mar 18 2022 Milan Crha - 42.0-1 +- Update to 42.0 + +* Thu Mar 10 2022 Milan Crha - 42.rc-2 +- Add upstream patches for gs-download-utils (i#1677 and i#1679) + +* Mon Mar 07 2022 Milan Crha - 42.rc-1 +- Update to 42.rc + +* Mon Feb 21 2022 Milan Crha - 42.beta-3 +- Resolves: #2056082 (Enable PackageKit autoremove option) + +* Wed Feb 16 2022 Milan Crha - 42.beta-2 +- Resolves: #2054939 (Crash on a PackageKit app install) +- Add a temporary workaround for gtk_widget_measure error flood on GsAppRow + +* Fri Feb 11 2022 Milan Crha - 42.beta-1 +- Update to 42.beta + +* Thu Jan 20 2022 Fedora Release Engineering - 42~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Fri Jan 07 2022 Milan Crha - 42.alpha-1 +- Update to 42.alpha + +* Fri Dec 03 2021 Milan Crha - 41.2-1 +- Update to 41.2 + +* Fri Oct 29 2021 Milan Crha - 41.1-1 +- Update to 41.1 + +* Tue Oct 19 2021 Milan Crha - 41.0-6 +- Resolves: #2012863 (gs-installed-page: Change section on application state change) + +* Mon Oct 11 2021 Milan Crha - 41.0-5 +- Add patch to mark compulsory only repos, not apps from it + +* Fri Oct 08 2021 Milan Crha - 41.0-4 +- Resolves: #2011176 (flathub repo can't be added through gnome-software) +- Resolves: #2010660 (gs-repos-dialog: Can show also desktop applications) +- Resolves: #2010353 (Optional repos cannot be disabled) + +* Thu Oct 07 2021 Milan Crha - 41.0-3 +- Resolves: #2010740 (Refresh on repository setup change) + +* Mon Oct 04 2021 Milan Crha - 41.0-2 +- Resolves: #2009063 (Correct update notifications) + +* Mon Sep 20 2021 Milan Crha - 41.0-1 +- Update to 41.0 + +* Mon Sep 13 2021 Milan Crha - 41~rc-2 +- Resolves: #2003365 (packagekit: Ensure PkClient::interactive flag being set) + +* Wed Sep 08 2021 Milan Crha - 41~rc-1 +- Update to 41.rc + +* Wed Sep 01 2021 Milan Crha - 41~beta-3 +- Resolves: #1995817 (gs-updates-section: Check also dependencies' download size) + +* Tue Aug 24 2021 Kalev Lember - 41~beta-2 +- Enable parental controls support + +* Fri Aug 13 2021 Milan Crha - 41~beta-1 +- Update to 41.beta + +* Thu Jul 22 2021 Fedora Release Engineering - 41~alpha-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Wed Jul 21 2021 Milan Crha - 41~alpha-1 +- Update to 41.alpha + +* Mon Jul 12 2021 Milan Crha - 40.3-2 +- Add rpm-ostree patch to hide packages from the search results +- Add patch to implement what-provides search in the Flatpak plugin + +* Mon Jul 12 2021 Milan Crha - 40.3-1 +- Update to 40.3 + +* Wed Jun 23 2021 Milan Crha - 40.2-2 +- Add patch to automatically install application updates (i#1248) + +* Fri Jun 04 2021 Milan Crha - 40.2-1 +- Update to 40.2 + +* Mon May 03 2021 Milan Crha - 40.1-2 +- Add patch for crash under gs_details_page_refresh_all() (i#1227) + +* Mon May 03 2021 Milan Crha - 40.1-1 +- Update to 40.1 + +* Fri Mar 26 2021 Kalev Lember - 40.0-2 +- Rebuild to fix sysprof-capture symbols leaking into libraries consuming it + +* Mon Mar 22 2021 Kalev Lember - 40.0-1 +- Update to 40.0 + +* Thu Mar 18 2021 Adam Williamson - 40~rc-2 +- Backport a couple of bug fixes from upstream (icon display, crash bug) + +* Mon Mar 15 2021 Kalev Lember - 40~rc-1 +- Update to 40.rc + +* Wed Mar 10 2021 Adam Williamson - 40~beta-2 +- Backport MR #643 to fix update notifications on first run (#1930401) + +* Tue Feb 16 2021 Kalev Lember - 40~beta-1 +- Update to 40.beta + +* Mon Feb 08 2021 Richard Hughes - 3.38.1-1 +- New upstream version +- Fix package details not found for some packages +- Ignore harmless warnings when using unusual fwupd versions + +* Tue Jan 26 2021 Fedora Release Engineering - 3.38.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Sep 14 2020 Kalev Lember - 3.38.0-2 +- Revert an optimization that broke packagekit updates + +* Fri Sep 11 2020 Kalev Lember - 3.38.0-1 +- Update to 3.38.0 + +* Tue Sep 01 2020 Kalev Lember - 3.37.92-1 +- Update to 3.37.92 + +* Tue Aug 18 2020 Richard Hughes - 3.36.1-4 +- Rebuild for the libxmlb API bump. + +* Sat Aug 01 2020 Fedora Release Engineering - 3.36.1-3 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 27 2020 Fedora Release Engineering - 3.36.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Fri May 22 2020 Richard Hughes - 3.36.1-1 +- Update to 3.36.1 + +* Tue May 12 2020 Kalev Lember - 3.36.0-2 +- Backport various rpm-ostree backend fixes + +* Wed Mar 11 2020 Kalev Lember - 3.36.0-1 +- Update to 3.36.0 + +* Wed Mar 04 2020 Kalev Lember - 3.35.92-1 +- Update to 3.35.92 + +* Fri Feb 21 2020 Richard Hughes - 3.35.91-2 +- Backport a patch to fix a crash when looking at the application details. + +* Wed Feb 19 2020 Richard Hughes - 3.35.91-1 +- Update to 3.35.91. + +* Tue Jan 28 2020 Fedora Release Engineering - 3.35.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Mon Nov 25 2019 Richard Hughes - 3.35.2-1 +- Update to 3.35.2. + +* Fri Oct 18 2019 Kalev Lember - 3.34.1-6 +- Backport patches to fix a crash in gs_flatpak_get_installation (#1762689) + +* Mon Oct 14 2019 Kalev Lember - 3.34.1-5 +- Update renamed appstream ids for GNOME 3.34 + +* Fri Oct 11 2019 Richard Hughes - 3.34.1-4 +- Backport a simpler to correct the installed applications +- Resolves #1759193 + +* Fri Oct 11 2019 Richard Hughes - 3.34.1-3 +- Backport a better patch to correct the installed applications +- Resolves #1759193 + +* Thu Oct 10 2019 Richard Hughes - 3.34.1-2 +- Backport a patch to correct the applications shown in the installed list +- Resolves #1759193 + +* Mon Oct 07 2019 Kalev Lember - 3.34.1-1 +- Update to 3.34.1 + +* Wed Sep 25 2019 Kalev Lember - 3.34.0-2 +- Fix third party repo enabling not working (#1749566) + +* Mon Sep 09 2019 Kalev Lember - 3.34.0-1 +- Update to 3.34.0 + +* Thu Jul 25 2019 Fedora Release Engineering - 3.32.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Thu Jul 11 2019 Kalev Lember - 3.32.4-1 +- Update to 3.32.4 + +* Thu Jul 11 2019 Richard Hughes - 3.32.3-5 +- Disable the snap plugin. Canonical upstream are not going to be installing + gnome-software in the next LTS, prefering instead to ship a "Snap Store" + rather than GNOME Software. +- Enabling the snap plugin also enables the Snap Store which violated the same + rules which prevented us installing Flathub by default. +- The existing plugin is barely maintained and I don't want to be the one + responsible when it breaks. + +* Thu Jun 13 2019 Kalev Lember - 3.32.3-4 +- Rebuild for accidental libflatpak ABI break + +* Mon Jun 10 22:13:19 CET 2019 Igor Gnatenko - 3.32.3-3 +- Rebuild for RPM 4.15 + +* Mon Jun 10 15:42:01 CET 2019 Igor Gnatenko - 3.32.3-2 +- Rebuild for RPM 4.15 + +* Fri May 24 2019 Kalev Lember - 3.32.3-1 +- Update to 3.32.3 + +* Tue May 07 2019 Kalev Lember - 3.32.2-1 +- Update to 3.32.2 + +* Fri May 03 2019 Kalev Lember - 3.32.1-4 +- Update a patch to final upstream version + +* Tue Apr 30 2019 Kalev Lember - 3.32.1-3 +- Backport a number of rpm-ostree fixes + +* Tue Apr 16 2019 Adam Williamson - 3.32.1-2 +- Rebuild with Meson fix for #1699099 + +* Mon Apr 15 2019 Kalev Lember - 3.32.1-1 +- Update to 3.32.1 + +* Fri Apr 05 2019 Neal Gompa - 3.32.0-6 +- Require snapd instead of the obsolete snapd-login-service for snap subpackage + +* Wed Apr 03 2019 Kalev Lember - 3.32.0-5 +- Switch to system libdnf + +* Fri Mar 29 2019 Kalev Lember - 3.32.0-4 +- Rebuild for new rpm-ostree + +* Fri Mar 15 2019 Kalev Lember - 3.32.0-3 +- Add nm-connection-editor.desktop to Utilities folder (#1686851) + +* Tue Mar 12 2019 Kalev Lember - 3.32.0-2 +- Backport a patch to add shadows to app icons + +* Mon Mar 11 2019 Kalev Lember - 3.32.0-1 +- Update to 3.32.0 + +* Tue Mar 05 2019 Kalev Lember - 3.31.92-1 +- Update to 3.31.92 + +* Thu Feb 28 2019 Kalev Lember - 3.31.90-4 +- Change PackageKit requires to recommends + +* Wed Feb 27 2019 Kalev Lember - 3.31.90-3 +- Remove unneeded dpkg plugin + +* Mon Feb 25 2019 Kalev Lember - 3.31.90-2 +- Split rpm-ostree backend to its own subpackage + +* Sun Feb 24 2019 Kalev Lember - 3.31.90-1 +- Update to 3.31.90 +- Add "anaconda" repo to official repos list (#1679693) + +* Thu Jan 31 2019 Fedora Release Engineering - 3.31.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Wed Jan 16 2019 Kalev Lember - 3.31.2-1 +- Update to 3.31.2 + +* Fri Dec 14 2018 Kalev Lember - 3.31.1-2 +- Fix offline update notifications to show up (#1659231) + +* Tue Oct 09 2018 Kalev Lember - 3.31.1-1 +- Update to 3.31.1 + +* Fri Oct 05 2018 Kalev Lember - 3.30.2-1 +- Update to 3.30.2 + +* Wed Sep 26 2018 Kalev Lember - 3.30.1-2 +- Add modular repos to official repos list + +* Tue Sep 25 2018 Kalev Lember - 3.30.1-1 +- Update to 3.30.1 + +* Thu Sep 06 2018 Kalev Lember - 3.30.0-1 +- Update to 3.30.0 + +* Tue Aug 28 2018 Richard Hughes - 3.29.92-1 +- Update to 3.29.92 + +* Fri Jul 13 2018 Fedora Release Engineering - 3.29.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Wed May 09 2018 Kalev Lember - 3.29.1-1 +- Update to 3.29.1 + +* Mon Apr 09 2018 Kalev Lember - 3.28.1-1 +- Update to 3.28.1 + +* Thu Mar 29 2018 Kalev Lember - 3.28.0-5 +- Fix empty OS Updates showing up +- Make rpm-ostree update triggering work + +* 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 + +* Sun Mar 11 2018 Kalev Lember - 3.27.92-3 +- Rebuilt for gspell 1.8 + +* Wed Mar 07 2018 Kalev Lember - 3.27.92-2 +- Move org.gnome.Software.Featured.xml from -editor to main package + +* Mon Mar 05 2018 Kalev Lember - 3.27.92-1 +- Update to 3.27.92 + +* Sun Mar 04 2018 Neal Gompa - 3.27.90-4 +- Drop obsolete snapd-login-service requirement for snap plugin subpackage + +* Mon Feb 19 2018 Adam Williamson - 3.27.90-3 +- Backport fix for RHBZ #1546893 from upstream git + +* Mon Feb 19 2018 Kalev Lember - 3.27.90-2 +- Re-enable rpm-ostree plugin + +* Thu Feb 15 2018 Kalev Lember - 3.27.90-1 +- Update to 3.27.90 +- Temporarily disable the rpm-ostree plugin + +* Tue Feb 13 2018 Björn Esser - 3.27.4-4 +- Rebuild against newer gnome-desktop3 package + +* Thu Feb 08 2018 Kalev Lember - 3.27.4-3 +- Add fedora-workstation-repositories to nonfree-sources schema defaults + +* Wed Feb 07 2018 Fedora Release Engineering - 3.27.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Mon Jan 08 2018 Kalev Lember - 3.27.4-1 +- Update to 3.27.4 +- Drop unused --without packagekit option + +* Fri Jan 05 2018 Igor Gnatenko - 3.27.3-2 +- Remove obsolete scriptlets + +* Sat Dec 16 2017 Kalev Lember - 3.27.3-1 +- Update to 3.27.3 + +* Mon Nov 13 2017 Kalev Lember - 3.27.2-1 +- Update to 3.27.2 + +* Thu Nov 09 2017 Kalev Lember - 3.26.2-1 +- Update to 3.26.2 +- Re-enable fwupd support + +* Tue Oct 31 2017 Kalev Lember - 3.26.1-5 +- Enable the rpm-ostree plugin + +* Wed Oct 25 2017 Kalev Lember - 3.26.1-4 +- Fix "too many results returned" error after distro upgrades (#1496489) + +* Tue Oct 10 2017 Kalev Lember - 3.26.1-3 +- Backport a flatpakref installation fix + +* Mon Oct 09 2017 Richard Hughes - 3.26.1-2 +- Disable fwupd support until we get a 3.27.1 tarball + +* 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/sources b/sources index c24e704..1959476 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-software-49.3.tar.xz) = 0414ea55ad3b83bcd50514985e3d2026207801c040934805bb26d162e88f0619a9d70aa6ba324a29acfab02573490b928c83594b54f57ec2a8ad00e6c127f657 +SHA512 (gnome-software-47.5.tar.xz) = d179f8b28d8a110424a1df35a736ca3b8a5e20b82a902f7c5ffb4343da55b139583da347ad055e3e1dd29bb5cded2e73b86709ea70db583311049d58e4861778