From 11f6f591575b5c5aa4f7e30e07d60a1d62d35ce2 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 16 May 2023 06:04:32 +0000 Subject: [PATCH] import gnome-software-3.36.1-11.el8 --- .gitignore | 1 + .gnome-software.metadata | 1 + ...basic-auth-support-to-flatpak-plugin.patch | 813 ++++++++ ...sktop-and-appdata-names-to-match-wha.patch | 61 + ...istic-for-detecting-old-style-AppStr.patch | 69 + ...bflow-auth-support-to-flatpak-plugin.patch | 104 + SOURCES/0003-hide-some-errors.patch | 61 + .../add-back-shell-extensions-support.patch | 1758 +++++++++++++++++ SOURCES/be-able-to-disable-odrs.patch | 27 + SOURCES/crash-when-run-as-root.patch | 57 + SOURCES/devel-install-headers.patch | 36 + SOURCES/flatpak-same-runtime-origin.patch | 117 ++ ...gnome-software-3.36.1-unrelated-refs.patch | 26 + ...es-page-keep-showing-installing-apps.patch | 97 + SPECS/gnome-software.spec | 790 ++++++++ 15 files changed, 4018 insertions(+) create mode 100644 .gitignore create mode 100644 .gnome-software.metadata create mode 100644 SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch create mode 100644 SOURCES/0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch create mode 100644 SOURCES/0001-Improve-the-heuristic-for-detecting-old-style-AppStr.patch create mode 100644 SOURCES/0002-Add-webflow-auth-support-to-flatpak-plugin.patch create mode 100644 SOURCES/0003-hide-some-errors.patch create mode 100644 SOURCES/add-back-shell-extensions-support.patch create mode 100644 SOURCES/be-able-to-disable-odrs.patch create mode 100644 SOURCES/crash-when-run-as-root.patch create mode 100644 SOURCES/devel-install-headers.patch create mode 100644 SOURCES/flatpak-same-runtime-origin.patch create mode 100644 SOURCES/gnome-software-3.36.1-unrelated-refs.patch create mode 100644 SOURCES/gs-updates-page-keep-showing-installing-apps.patch create mode 100644 SPECS/gnome-software.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de98123 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/gnome-software-3.36.1.tar.xz diff --git a/.gnome-software.metadata b/.gnome-software.metadata new file mode 100644 index 0000000..33f7de1 --- /dev/null +++ b/.gnome-software.metadata @@ -0,0 +1 @@ +9c6342f47f5ad90deac7cc8250f559146f506fff SOURCES/gnome-software-3.36.1.tar.xz diff --git a/SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch b/SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch new file mode 100644 index 0000000..40b256a --- /dev/null +++ b/SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch @@ -0,0 +1,813 @@ +From b6a41a1b9e9020a23dbc418183ebe4746b6ec027 Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Mon, 18 May 2020 14:45:35 +0200 +Subject: [PATCH 1/2] Add basic auth support to flatpak plugin + +This is useful for e.g. OCI remotes that can use basic auth. + +All user visible strings in the basic auth dialog are taken from the +flatpak CLI client. +--- + lib/gs-plugin-loader.c | 29 +++- + lib/gs-plugin-loader.h | 7 +- + lib/gs-plugin.c | 68 +++++++++- + lib/gs-plugin.h | 13 +- + plugins/flatpak/gs-plugin-flatpak.c | 55 +++++++- + po/POTFILES.in | 2 + + src/gnome-software.gresource.xml | 1 + + src/gs-basic-auth-dialog.c | 130 ++++++++++++++++++ + src/gs-basic-auth-dialog.h | 28 ++++ + src/gs-basic-auth-dialog.ui | 203 ++++++++++++++++++++++++++++ + src/gs-shell.c | 25 +++- + src/meson.build | 1 + + 12 files changed, 556 insertions(+), 6 deletions(-) + create mode 100644 src/gs-basic-auth-dialog.c + create mode 100644 src/gs-basic-auth-dialog.h + create mode 100644 src/gs-basic-auth-dialog.ui + +diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c +index 979f3d5d..35382e3f 100644 +--- a/lib/gs-plugin-loader.c ++++ b/lib/gs-plugin-loader.c +@@ -1,7 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2018 Richard Hughes +- * Copyright (C) 2014-2018 Kalev Lember ++ * Copyright (C) 2014-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -74,6 +74,7 @@ enum { + SIGNAL_PENDING_APPS_CHANGED, + SIGNAL_UPDATES_CHANGED, + SIGNAL_RELOAD, ++ SIGNAL_BASIC_AUTH_START, + SIGNAL_LAST + }; + +@@ -2016,6 +2017,23 @@ gs_plugin_loader_status_changed_cb (GsPlugin *plugin, + 0, app, status); + } + ++static void ++gs_plugin_loader_basic_auth_start_cb (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data, ++ GsPluginLoader *plugin_loader) ++{ ++ g_debug ("emitting basic-auth-start %s", realm); ++ g_signal_emit (plugin_loader, ++ signals[SIGNAL_BASIC_AUTH_START], 0, ++ remote, ++ realm, ++ callback, ++ user_data); ++} ++ + static gboolean + gs_plugin_loader_job_actions_changed_delay_cb (gpointer user_data) + { +@@ -2102,6 +2120,9 @@ gs_plugin_loader_open_plugin (GsPluginLoader *plugin_loader, + g_signal_connect (plugin, "status-changed", + G_CALLBACK (gs_plugin_loader_status_changed_cb), + plugin_loader); ++ g_signal_connect (plugin, "basic-auth-start", ++ G_CALLBACK (gs_plugin_loader_basic_auth_start_cb), ++ plugin_loader); + g_signal_connect (plugin, "report-event", + G_CALLBACK (gs_plugin_loader_report_event_cb), + plugin_loader); +@@ -2712,6 +2733,12 @@ gs_plugin_loader_class_init (GsPluginLoaderClass *klass) + G_STRUCT_OFFSET (GsPluginLoaderClass, reload), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); ++ signals [SIGNAL_BASIC_AUTH_START] = ++ g_signal_new ("basic-auth-start", ++ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GsPluginLoaderClass, basic_auth_start), ++ NULL, NULL, g_cclosure_marshal_generic, ++ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER); + } + + static void +diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h +index 74cbfa53..e88ea2d1 100644 +--- a/lib/gs-plugin-loader.h ++++ b/lib/gs-plugin-loader.h +@@ -1,7 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2017 Richard Hughes +- * Copyright (C) 2015 Kalev Lember ++ * Copyright (C) 2015-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -31,6 +31,11 @@ struct _GsPluginLoaderClass + void (*pending_apps_changed) (GsPluginLoader *plugin_loader); + void (*updates_changed) (GsPluginLoader *plugin_loader); + void (*reload) (GsPluginLoader *plugin_loader); ++ void (*basic_auth_start) (GsPluginLoader *plugin_loader, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data); + }; + + GsPluginLoader *gs_plugin_loader_new (void); +diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c +index 5aed1058..3f63fa97 100644 +--- a/lib/gs-plugin.c ++++ b/lib/gs-plugin.c +@@ -1,7 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2013-2016 Richard Hughes +- * Copyright (C) 2014-2018 Kalev Lember ++ * Copyright (C) 2014-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -87,6 +87,7 @@ enum { + SIGNAL_RELOAD, + SIGNAL_REPORT_EVENT, + SIGNAL_ALLOW_UPDATES, ++ SIGNAL_BASIC_AUTH_START, + SIGNAL_LAST + }; + +@@ -851,6 +852,64 @@ gs_plugin_status_update (GsPlugin *plugin, GsApp *app, GsPluginStatus status) + g_source_attach (idle_source, NULL); + } + ++typedef struct { ++ GsPlugin *plugin; ++ gchar *remote; ++ gchar *realm; ++ GCallback callback; ++ gpointer user_data; ++} GsPluginBasicAuthHelper; ++ ++static gboolean ++gs_plugin_basic_auth_start_cb (gpointer user_data) ++{ ++ GsPluginBasicAuthHelper *helper = user_data; ++ g_signal_emit (helper->plugin, ++ signals[SIGNAL_BASIC_AUTH_START], 0, ++ helper->remote, ++ helper->realm, ++ helper->callback, ++ helper->user_data); ++ g_free (helper->remote); ++ g_free (helper->realm); ++ g_slice_free (GsPluginBasicAuthHelper, helper); ++ return FALSE; ++} ++ ++/** ++ * gs_plugin_basic_auth_start: ++ * @plugin: a #GsPlugin ++ * @remote: a string ++ * @realm: a string ++ * @callback: callback to invoke to submit the user/password ++ * @user_data: callback data to pass to the callback ++ * ++ * Emit the basic-auth-start signal in the main thread. ++ * ++ * Since: 3.38 ++ **/ ++void ++gs_plugin_basic_auth_start (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data) ++{ ++ GsPluginBasicAuthHelper *helper; ++ g_autoptr(GSource) idle_source = NULL; ++ ++ helper = g_slice_new0 (GsPluginBasicAuthHelper); ++ helper->plugin = plugin; ++ helper->remote = g_strdup (remote); ++ helper->realm = g_strdup (realm); ++ helper->callback = callback; ++ helper->user_data = user_data; ++ ++ idle_source = g_idle_source_new (); ++ g_source_set_callback (idle_source, gs_plugin_basic_auth_start_cb, helper, NULL); ++ g_source_attach (idle_source, NULL); ++} ++ + static gboolean + gs_plugin_app_launch_cb (gpointer user_data) + { +@@ -1959,6 +2018,13 @@ gs_plugin_class_init (GsPluginClass *klass) + G_STRUCT_OFFSET (GsPluginClass, allow_updates), + NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); ++ ++ signals [SIGNAL_BASIC_AUTH_START] = ++ g_signal_new ("basic-auth-start", ++ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GsPluginClass, basic_auth_start), ++ NULL, NULL, g_cclosure_marshal_generic, ++ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER); + } + + static void +diff --git a/lib/gs-plugin.h b/lib/gs-plugin.h +index 7dd2d864..d07afd3b 100644 +--- a/lib/gs-plugin.h ++++ b/lib/gs-plugin.h +@@ -1,6 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2012-2016 Richard Hughes ++ * Copyright (C) 2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -37,7 +38,12 @@ struct _GsPluginClass + GsPluginEvent *event); + void (*allow_updates) (GsPlugin *plugin, + gboolean allow_updates); +- gpointer padding[26]; ++ void (*basic_auth_start) (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data); ++ gpointer padding[25]; + }; + + typedef struct GsPluginData GsPluginData; +@@ -116,5 +122,10 @@ void gs_plugin_report_event (GsPlugin *plugin, + void gs_plugin_set_allow_updates (GsPlugin *plugin, + gboolean allow_updates); + gboolean gs_plugin_get_network_available (GsPlugin *plugin); ++void gs_plugin_basic_auth_start (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data); + + G_END_DECLS +diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c +index 4d6a81ba..2518025d 100644 +--- a/plugins/flatpak/gs-plugin-flatpak.c ++++ b/plugins/flatpak/gs-plugin-flatpak.c +@@ -2,7 +2,7 @@ + * + * Copyright (C) 2016 Joaquim Rocha + * Copyright (C) 2016-2018 Richard Hughes +- * Copyright (C) 2017-2018 Kalev Lember ++ * Copyright (C) 2017-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -456,6 +456,55 @@ _group_apps_by_installation (GsPlugin *plugin, + return g_steal_pointer (&applist_by_flatpaks); + } + ++#if FLATPAK_CHECK_VERSION(1,6,0) ++typedef struct { ++ FlatpakTransaction *transaction; ++ guint id; ++} BasicAuthData; ++ ++static void ++basic_auth_data_free (BasicAuthData *data) ++{ ++ g_object_unref (data->transaction); ++ g_slice_free (BasicAuthData, data); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BasicAuthData, basic_auth_data_free) ++ ++static void ++_basic_auth_cb (const gchar *user, const gchar *password, gpointer user_data) ++{ ++ g_autoptr(BasicAuthData) data = user_data; ++ ++ g_debug ("Submitting basic auth data"); ++ ++ /* NULL user aborts the basic auth request */ ++ flatpak_transaction_complete_basic_auth (data->transaction, data->id, user, password, NULL /* options */); ++} ++ ++static gboolean ++_basic_auth_start (FlatpakTransaction *transaction, ++ const char *remote, ++ const char *realm, ++ GVariant *options, ++ guint id, ++ GsPlugin *plugin) ++{ ++ BasicAuthData *data; ++ ++ if (!gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE)) ++ return FALSE; ++ ++ data = g_slice_new0 (BasicAuthData); ++ data->transaction = g_object_ref (transaction); ++ data->id = id; ++ ++ g_debug ("Login required remote %s (realm %s)\n", remote, realm); ++ gs_plugin_basic_auth_start (plugin, remote, realm, G_CALLBACK (_basic_auth_cb), data); ++ return TRUE; ++} ++#endif ++ + static FlatpakTransaction * + _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak, + GCancellable *cancellable, GError **error) +@@ -491,6 +540,10 @@ _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak, + /* connect up signals */ + g_signal_connect (transaction, "ref-to-app", + G_CALLBACK (_ref_to_app), plugin); ++#if FLATPAK_CHECK_VERSION(1,6,0) ++ g_signal_connect (transaction, "basic-auth-start", ++ G_CALLBACK (_basic_auth_start), plugin); ++#endif + + /* use system installations as dependency sources for user installations */ + flatpak_transaction_add_default_dependency_sources (transaction); +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 20721c4a..a44a6ad3 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -10,6 +10,8 @@ src/gs-app-row.c + src/gs-app-row.ui + src/gs-app-tile.c + src/gs-app-tile.ui ++src/gs-basic-auth-dialog.c ++src/gs-basic-auth-dialog.ui + lib/gs-category.c + src/gs-category-page.c + src/gs-category-page.ui +diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml +index 3eaabca2..459ecf82 100644 +--- a/src/gnome-software.gresource.xml ++++ b/src/gnome-software.gresource.xml +@@ -4,6 +4,7 @@ + gnome-software.ui + gs-app-addon-row.ui + gs-app-row.ui ++ gs-basic-auth-dialog.ui + gs-category-page.ui + gs-category-tile.ui + gs-details-page.ui +diff --git a/src/gs-basic-auth-dialog.c b/src/gs-basic-auth-dialog.c +new file mode 100644 +index 00000000..c690a327 +--- /dev/null ++++ b/src/gs-basic-auth-dialog.c +@@ -0,0 +1,130 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2020 Kalev Lember ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include "config.h" ++ ++#include "gs-basic-auth-dialog.h" ++ ++#include ++#include ++#include ++ ++struct _GsBasicAuthDialog ++{ ++ GtkDialog parent_instance; ++ ++ GsBasicAuthCallback callback; ++ gpointer callback_data; ++ ++ /* template widgets */ ++ GtkButton *login_button; ++ GtkLabel *description_label; ++ GtkEntry *user_entry; ++ GtkEntry *password_entry; ++}; ++ ++G_DEFINE_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GTK_TYPE_DIALOG) ++ ++static void ++cancel_button_clicked_cb (GsBasicAuthDialog *dialog) ++{ ++ /* abort the basic auth request */ ++ dialog->callback (NULL, NULL, dialog->callback_data); ++ ++ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); ++} ++ ++static void ++login_button_clicked_cb (GsBasicAuthDialog *dialog) ++{ ++ const gchar *user; ++ const gchar *password; ++ ++ user = gtk_entry_get_text (dialog->user_entry); ++ password = gtk_entry_get_text (dialog->password_entry); ++ ++ /* submit the user/password to basic auth */ ++ dialog->callback (user, password, dialog->callback_data); ++ ++ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); ++} ++ ++static void ++dialog_validate (GsBasicAuthDialog *dialog) ++{ ++ const gchar *user; ++ const gchar *password; ++ gboolean valid_user; ++ gboolean valid_password; ++ ++ /* require user */ ++ user = gtk_entry_get_text (dialog->user_entry); ++ valid_user = user != NULL && strlen (user) != 0; ++ ++ /* require password */ ++ password = gtk_entry_get_text (dialog->password_entry); ++ valid_password = password != NULL && strlen (password) != 0; ++ ++ gtk_widget_set_sensitive (GTK_WIDGET (dialog->login_button), valid_user && valid_password); ++} ++ ++static void ++update_description (GsBasicAuthDialog *dialog, const gchar *remote, const gchar *realm) ++{ ++ g_autofree gchar *description = NULL; ++ ++ /* TRANSLATORS: This is a description for entering user/password */ ++ description = g_strdup_printf (_("Login required remote %s (realm %s)"), ++ remote, realm); ++ gtk_label_set_text (dialog->description_label, description); ++} ++ ++static void ++gs_basic_auth_dialog_init (GsBasicAuthDialog *dialog) ++{ ++ gtk_widget_init_template (GTK_WIDGET (dialog)); ++} ++ ++static void ++gs_basic_auth_dialog_class_init (GsBasicAuthDialogClass *klass) ++{ ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ ++ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-basic-auth-dialog.ui"); ++ ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, login_button); ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, description_label); ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, user_entry); ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, password_entry); ++ ++ gtk_widget_class_bind_template_callback (widget_class, dialog_validate); ++ gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb); ++ gtk_widget_class_bind_template_callback (widget_class, login_button_clicked_cb); ++} ++ ++GtkWidget * ++gs_basic_auth_dialog_new (GtkWindow *parent, ++ const gchar *remote, ++ const gchar *realm, ++ GsBasicAuthCallback callback, ++ gpointer callback_data) ++{ ++ GsBasicAuthDialog *dialog; ++ ++ dialog = g_object_new (GS_TYPE_BASIC_AUTH_DIALOG, ++ "use-header-bar", TRUE, ++ "transient-for", parent, ++ "modal", TRUE, ++ NULL); ++ dialog->callback = callback; ++ dialog->callback_data = callback_data; ++ ++ update_description (dialog, remote, realm); ++ dialog_validate (dialog); ++ ++ return GTK_WIDGET (dialog); ++} +diff --git a/src/gs-basic-auth-dialog.h b/src/gs-basic-auth-dialog.h +new file mode 100644 +index 00000000..ec5f1d03 +--- /dev/null ++++ b/src/gs-basic-auth-dialog.h +@@ -0,0 +1,28 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2020 Kalev Lember ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#pragma once ++ ++#include ++ ++#include "gnome-software-private.h" ++ ++G_BEGIN_DECLS ++ ++typedef void (*GsBasicAuthCallback) (const gchar *user, const gchar *password, gpointer callback_data); ++ ++#define GS_TYPE_BASIC_AUTH_DIALOG (gs_basic_auth_dialog_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GS, BASIC_AUTH_DIALOG, GtkDialog) ++ ++GtkWidget *gs_basic_auth_dialog_new (GtkWindow *parent, ++ const gchar *remote, ++ const gchar *realm, ++ GsBasicAuthCallback callback, ++ gpointer callback_data); ++ ++G_END_DECLS +diff --git a/src/gs-basic-auth-dialog.ui b/src/gs-basic-auth-dialog.ui +new file mode 100644 +index 00000000..339e831d +--- /dev/null ++++ b/src/gs-basic-auth-dialog.ui +@@ -0,0 +1,203 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ horizontal ++ ++ ++ ++ ++ ++ +diff --git a/src/gs-shell.c b/src/gs-shell.c +index 009776ad..41503cf8 100644 +--- a/src/gs-shell.c ++++ b/src/gs-shell.c +@@ -2,7 +2,7 @@ + * + * Copyright (C) 2013-2017 Richard Hughes + * Copyright (C) 2013 Matthias Clasen +- * Copyright (C) 2014-2018 Kalev Lember ++ * Copyright (C) 2014-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -18,6 +18,7 @@ + + #include "gs-common.h" + #include "gs-shell.h" ++#include "gs-basic-auth-dialog.h" + #include "gs-details-page.h" + #include "gs-installed-page.h" + #include "gs-metered-data-dialog.h" +@@ -362,6 +363,25 @@ scheduler_ready_cb (GObject *source_object, + } + #endif /* HAVE_MOGWAI */ + ++static void ++gs_shell_basic_auth_start_cb (GsPluginLoader *plugin_loader, ++ const gchar *remote, ++ const gchar *realm, ++ GsBasicAuthCallback callback, ++ gpointer callback_data, ++ GsShell *shell) ++{ ++ GsShellPrivate *priv = gs_shell_get_instance_private (shell); ++ GtkWidget *dialog; ++ ++ dialog = gs_basic_auth_dialog_new (priv->main_window, remote, realm, callback, callback_data); ++ gs_shell_modal_dialog_present (shell, GTK_DIALOG (dialog)); ++ ++ /* just destroy */ ++ g_signal_connect_swapped (dialog, "response", ++ G_CALLBACK (gtk_widget_destroy), dialog); ++} ++ + static void + free_back_entry (BackEntry *entry) + { +@@ -2126,6 +2146,9 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can + g_signal_connect_object (priv->plugin_loader, "notify::network-metered", + G_CALLBACK (gs_shell_network_metered_notify_cb), + shell, 0); ++ g_signal_connect_object (priv->plugin_loader, "basic-auth-start", ++ G_CALLBACK (gs_shell_basic_auth_start_cb), ++ shell, 0); + priv->cancellable = g_object_ref (cancellable); + + priv->settings = g_settings_new ("org.gnome.software"); +diff --git a/src/meson.build b/src/meson.build +index cbd0a511..6581e77c 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -20,6 +20,7 @@ gnome_software_sources = [ + 'gs-application.c', + 'gs-app-row.c', + 'gs-app-tile.c', ++ 'gs-basic-auth-dialog.c', + 'gs-category-page.c', + 'gs-category-tile.c', + 'gs-common.c', +-- +2.26.2 + diff --git a/SOURCES/0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch b/SOURCES/0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch new file mode 100644 index 0000000..45ecdf3 --- /dev/null +++ b/SOURCES/0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch @@ -0,0 +1,61 @@ +From 9dce785c2a71e81c410c6e314dd4d4db6cc02808 Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Wed, 3 Jun 2020 16:35:03 +0200 +Subject: [PATCH] Fix hardcoded desktop and appdata names to match what's in + RHEL 8.3 + +--- + data/assets/org.gnome.Software.Featured.xml | 2 +- + plugins/core/gs-plugin-hardcoded-popular.c | 2 +- + src/gs-folders.c | 6 +++--- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/data/assets/org.gnome.Software.Featured.xml b/data/assets/org.gnome.Software.Featured.xml +index d1d920be..822fc6a9 100644 +--- a/data/assets/org.gnome.Software.Featured.xml ++++ b/data/assets/org.gnome.Software.Featured.xml +@@ -145,7 +145,7 @@ text-shadow: none; + + + +- org.gimp.GIMP ++ gimp.desktop + + border-color: #4a8c30; + text-shadow: none; +diff --git a/plugins/core/gs-plugin-hardcoded-popular.c b/plugins/core/gs-plugin-hardcoded-popular.c +index 3998a813..03b44475 100644 +--- a/plugins/core/gs-plugin-hardcoded-popular.c ++++ b/plugins/core/gs-plugin-hardcoded-popular.c +@@ -29,7 +29,7 @@ gs_plugin_add_popular (GsPlugin *plugin, + "org.gnome.clocks.desktop", + "org.gnome.Dictionary.desktop", + "org.gnome.Documents.desktop", +- "org.gnome.Evince", ++ "evince.desktop", + "org.gnome.gedit.desktop", + "org.gnome.Maps.desktop", + "org.gnome.Weather", +diff --git a/src/gs-folders.c b/src/gs-folders.c +index fa068f0e..589cc1e2 100644 +--- a/src/gs-folders.c ++++ b/src/gs-folders.c +@@ -560,12 +560,12 @@ gs_folders_convert (void) + "org.gnome.DejaDup.desktop", + "org.gnome.Dictionary.desktop", + "org.gnome.DiskUtility.desktop", +- "org.gnome.eog.desktop", +- "org.gnome.Evince.desktop", ++ "eog.desktop", ++ "evince.desktop", + "org.gnome.FileRoller.desktop", + "org.gnome.fonts.desktop", + "org.gnome.Screenshot.desktop", +- "org.gnome.seahorse.Application.desktop", ++ "seahorse.desktop", + "org.gnome.Terminal.desktop", + "org.gnome.tweaks.desktop", + "org.gnome.Usage.desktop", +-- +2.18.2 + diff --git a/SOURCES/0001-Improve-the-heuristic-for-detecting-old-style-AppStr.patch b/SOURCES/0001-Improve-the-heuristic-for-detecting-old-style-AppStr.patch new file mode 100644 index 0000000..37ad96d --- /dev/null +++ b/SOURCES/0001-Improve-the-heuristic-for-detecting-old-style-AppStr.patch @@ -0,0 +1,69 @@ +From bc31889bf90e14776e4404cd58e9b0244efc4f2e Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Tue, 9 Jun 2020 21:42:41 +0200 +Subject: [PATCH] Improve the heuristic for detecting old-style AppStream + override files + +The heuristic was trying to detect old-style AppStream override files +that have the following structure: + + + org.kde.amarok.desktop + + AudioVideo + Featured + + + +When it found one, it gave it the wildcard quirk to avoid leaking the +result to the installed apps list. + +This however incorrectly tripped on old appdata files that didn't +specify name and relied on filling the missing name/summary/icon from +the desktop file. + +Fix this by tightening the heuristic and also look for + that none of the override files should have. + +This fixes RHEL firefox package to correctly show up when clicking on +'Show Details' in GNOME Shell. +--- + plugins/core/gs-appstream.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/plugins/core/gs-appstream.c b/plugins/core/gs-appstream.c +index a387f2e0..da9ba970 100644 +--- a/plugins/core/gs-appstream.c ++++ b/plugins/core/gs-appstream.c +@@ -746,6 +746,13 @@ gs_appstream_refine_app (GsPlugin *plugin, + gs_app_remove_quirk (app, GS_APP_QUIRK_HIDE_EVERYWHERE); + } + ++ /* try to detect old-style AppStream 'override' ++ * files without the merge attribute */ ++ if (xb_node_query_text (component, "name", NULL) == NULL && ++ xb_node_query_text (component, "metadata_license", NULL) == NULL) { ++ gs_app_add_quirk (app, GS_APP_QUIRK_IS_WILDCARD); ++ } ++ + /* set id */ + tmp = xb_node_query_text (component, "id", NULL); + if (tmp != NULL && gs_app_get_id (app) == NULL) +@@ -770,13 +777,8 @@ gs_appstream_refine_app (GsPlugin *plugin, + + /* set name */ + tmp = xb_node_query_text (component, "name", NULL); +- if (tmp != NULL) { ++ if (tmp != NULL) + gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, tmp); +- } else { +- /* this is a heuristic, but works even with old-style AppStream +- * files without the merge attribute */ +- gs_app_add_quirk (app, GS_APP_QUIRK_IS_WILDCARD); +- } + + /* set summary */ + tmp = xb_node_query_text (component, "summary", NULL); +-- +2.26.2 + diff --git a/SOURCES/0002-Add-webflow-auth-support-to-flatpak-plugin.patch b/SOURCES/0002-Add-webflow-auth-support-to-flatpak-plugin.patch new file mode 100644 index 0000000..2d0d0c8 --- /dev/null +++ b/SOURCES/0002-Add-webflow-auth-support-to-flatpak-plugin.patch @@ -0,0 +1,104 @@ +From de3afc6463aeb0e2d637a0360d1b96acffdf4e6d Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Tue, 19 May 2020 14:28:10 +0200 +Subject: [PATCH 2/2] Add webflow auth support to flatpak plugin + +This is just the minimal support, launching the auth page in the default +web browser when libflatpak signals that we need to do the webflow auth. + +Possible improvements could include doing either a webkitgtk dialog, or +maybe asking for confirmation before launching the web browser. +--- + plugins/flatpak/gs-plugin-flatpak.c | 69 +++++++++++++++++++++++++++++ + 1 file changed, 69 insertions(+) + +diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c +index 2518025d..a453cec8 100644 +--- a/plugins/flatpak/gs-plugin-flatpak.c ++++ b/plugins/flatpak/gs-plugin-flatpak.c +@@ -503,6 +503,71 @@ _basic_auth_start (FlatpakTransaction *transaction, + gs_plugin_basic_auth_start (plugin, remote, realm, G_CALLBACK (_basic_auth_cb), data); + return TRUE; + } ++ ++static gboolean ++_webflow_start (FlatpakTransaction *transaction, ++ const char *remote, ++ const char *url, ++ GVariant *options, ++ guint id, ++ GsPlugin *plugin) ++{ ++ const char *browser; ++ g_autoptr(GError) error_local = NULL; ++ ++ if (!gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE)) ++ return FALSE; ++ ++ g_debug ("Authentication required for remote '%s'", remote); ++ ++ /* Allow hard overrides with $BROWSER */ ++ browser = g_getenv ("BROWSER"); ++ if (browser != NULL) { ++ const char *args[3] = { NULL, url, NULL }; ++ args[0] = browser; ++ if (!g_spawn_async (NULL, (char **)args, NULL, G_SPAWN_SEARCH_PATH, ++ NULL, NULL, NULL, &error_local)) { ++ g_autoptr(GsPluginEvent) event = NULL; ++ ++ g_warning ("Failed to start browser %s: %s", browser, error_local->message); ++ ++ event = gs_plugin_event_new (); ++ gs_flatpak_error_convert (&error_local); ++ gs_plugin_event_set_error (event, error_local); ++ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING); ++ gs_plugin_report_event (plugin, event); ++ ++ return FALSE; ++ } ++ } else { ++ if (!g_app_info_launch_default_for_uri (url, NULL, &error_local)) { ++ g_autoptr(GsPluginEvent) event = NULL; ++ ++ g_warning ("Failed to show url: %s", error_local->message); ++ ++ event = gs_plugin_event_new (); ++ gs_flatpak_error_convert (&error_local); ++ gs_plugin_event_set_error (event, error_local); ++ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING); ++ gs_plugin_report_event (plugin, event); ++ ++ return FALSE; ++ } ++ } ++ ++ g_debug ("Waiting for browser..."); ++ ++ return TRUE; ++} ++ ++static void ++_webflow_done (FlatpakTransaction *transaction, ++ GVariant *options, ++ guint id, ++ GsPlugin *plugin) ++{ ++ g_debug ("Browser done"); ++} + #endif + + static FlatpakTransaction * +@@ -543,6 +608,10 @@ _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak, + #if FLATPAK_CHECK_VERSION(1,6,0) + g_signal_connect (transaction, "basic-auth-start", + G_CALLBACK (_basic_auth_start), plugin); ++ g_signal_connect (transaction, "webflow-start", ++ G_CALLBACK (_webflow_start), plugin); ++ g_signal_connect (transaction, "webflow-done", ++ G_CALLBACK (_webflow_done), plugin); + #endif + + /* use system installations as dependency sources for user installations */ +-- +2.26.2 + diff --git a/SOURCES/0003-hide-some-errors.patch b/SOURCES/0003-hide-some-errors.patch new file mode 100644 index 0000000..437948d --- /dev/null +++ b/SOURCES/0003-hide-some-errors.patch @@ -0,0 +1,61 @@ +diff -up gnome-software-3.36.1/lib/gs-plugin-loader.c.12 gnome-software-3.36.1/lib/gs-plugin-loader.c +--- gnome-software-3.36.1/lib/gs-plugin-loader.c.12 2022-09-22 12:17:59.367881179 +0200 ++++ gnome-software-3.36.1/lib/gs-plugin-loader.c 2022-09-22 12:17:59.392881205 +0200 +@@ -333,10 +333,17 @@ gs_plugin_job_to_failed_event (GsPluginJ + + /* invalid */ + if (error->domain != GS_PLUGIN_ERROR) { +- g_warning ("not GsPlugin error %s:%i: %s", +- g_quark_to_string (error->domain), +- error->code, +- error->message); ++ if (g_strcmp0 (BUILD_TYPE, "debug") == 0) { ++ g_warning ("not GsPlugin error %s:%i: %s", ++ g_quark_to_string (error->domain), ++ error->code, ++ error->message); ++ } else { ++ g_debug ("not GsPlugin error %s:%i: %s", ++ g_quark_to_string (error->domain), ++ error->code, ++ error->message); ++ } + g_set_error_literal (&error_copy, + GS_PLUGIN_ERROR, + GS_PLUGIN_ERROR_FAILED, +diff -up gnome-software-3.36.1/meson.build.12 gnome-software-3.36.1/meson.build +--- gnome-software-3.36.1/meson.build.12 2022-09-22 12:18:09.900892043 +0200 ++++ gnome-software-3.36.1/meson.build 2022-09-22 12:18:19.311901744 +0200 +@@ -9,6 +9,7 @@ conf = configuration_data() + conf.set_quoted('VERSION', meson.project_version()) + conf.set_quoted('PACKAGE_NAME', meson.project_name()) + conf.set_quoted('PACKAGE_VERSION', meson.project_version()) ++conf.set_quoted('BUILD_TYPE', get_option('buildtype')) + + # this refers to the gnome-software plugin API version + # this is not in any way related to a package or soname version +diff -up gnome-software-3.36.1/src/gs-shell.c.12 gnome-software-3.36.1/src/gs-shell.c +--- gnome-software-3.36.1/src/gs-shell.c.12 2022-09-22 12:17:59.301881111 +0200 ++++ gnome-software-3.36.1/src/gs-shell.c 2022-09-22 12:17:59.392881205 +0200 +@@ -2044,10 +2044,17 @@ gs_shell_rescan_events (GsShell *shell) + !g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_CANCELLED)) { +- g_warning ("not handling error %s for action %s: %s", +- gs_plugin_error_to_string (error->code), +- gs_plugin_action_to_string (action), +- error->message); ++ if (g_strcmp0 (BUILD_TYPE, "debug") == 0) { ++ g_warning ("not handling error %s for action %s: %s", ++ gs_plugin_error_to_string (error->code), ++ gs_plugin_action_to_string (action), ++ error->message); ++ } else { ++ g_debug ("not handling error %s for action %s: %s", ++ gs_plugin_error_to_string (error->code), ++ gs_plugin_action_to_string (action), ++ error->message); ++ } + } + gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INVALID); + return; diff --git a/SOURCES/add-back-shell-extensions-support.patch b/SOURCES/add-back-shell-extensions-support.patch new file mode 100644 index 0000000..d0eb41d --- /dev/null +++ b/SOURCES/add-back-shell-extensions-support.patch @@ -0,0 +1,1758 @@ +From 82a85d6bbacb148e355680446c4bbd6a091ad2eb Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Wed, 3 Jun 2020 15:50:41 +0200 +Subject: [PATCH 1/2] Revert "Remove support for Shell extensions" + +This reverts commit 77bdc3855c26de0edc2ce5d73bb6be861721c37b. +--- + contrib/gnome-software.spec.in | 1 + + meson_options.txt | 1 + + plugins/core/gs-appstream.c | 14 +- + plugins/core/gs-desktop-common.c | 3 + + plugins/meson.build | 3 + + plugins/packagekit/gs-plugin-packagekit.c | 3 + + plugins/shell-extensions/gs-appstream.c | 1 + + plugins/shell-extensions/gs-appstream.h | 1 + + .../gs-plugin-shell-extensions.c | 1159 +++++++++++++++++ + plugins/shell-extensions/gs-self-test.c | 156 +++ + plugins/shell-extensions/meson.build | 47 + + src/gs-category-page.c | 25 + + src/gs-category-page.ui | 45 + + src/gs-details-page.c | 14 +- + src/gs-repo-row.c | 3 +- + src/gs-repos-dialog.c | 3 +- + src/gs-summary-tile.c | 15 +- + 17 files changed, 1478 insertions(+), 16 deletions(-) + create mode 120000 plugins/shell-extensions/gs-appstream.c + create mode 120000 plugins/shell-extensions/gs-appstream.h + create mode 100644 plugins/shell-extensions/gs-plugin-shell-extensions.c + create mode 100644 plugins/shell-extensions/gs-self-test.c + create mode 100644 plugins/shell-extensions/meson.build + +diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in +index 587a72e6..7f415f51 100644 +--- a/contrib/gnome-software.spec.in ++++ b/contrib/gnome-software.spec.in +@@ -183,6 +183,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_provenance.so + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_repos.so + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_rewrite-resource.so ++%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_shell-extensions.so + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_systemd-updates.so + %{_sysconfdir}/xdg/autostart/gnome-software-service.desktop + %{_datadir}/app-info/xmls/org.gnome.Software.Featured.xml +diff --git a/meson_options.txt b/meson_options.txt +index f6068ba5..eba69ea2 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -10,6 +10,7 @@ option('fwupd', type : 'boolean', value : true, description : 'enable fwupd supp + option('flatpak', type : 'boolean', value : true, description : 'enable Flatpak support') + option('malcontent', type : 'boolean', value : true, description : 'enable parental controls support using libmalcontent') + option('rpm_ostree', type : 'boolean', value : false, description : 'enable rpm-ostree support') ++option('shell_extensions', type : 'boolean', value : true, description : 'enable shell extensions support') + option('odrs', type : 'boolean', value : true, description : 'enable ODRS support') + option('gudev', type : 'boolean', value : true, description : 'enable GUdev support') + option('snap', type : 'boolean', value : false, description : 'enable Snap support') +diff --git a/plugins/core/gs-appstream.c b/plugins/core/gs-appstream.c +index a387f2e0..e507ac0f 100644 +--- a/plugins/core/gs-appstream.c ++++ b/plugins/core/gs-appstream.c +@@ -30,15 +30,6 @@ gs_appstream_create_app (GsPlugin *plugin, XbSilo *silo, XbNode *component, GErr + if (gs_app_has_quirk (app_new, GS_APP_QUIRK_IS_WILDCARD)) + return g_steal_pointer (&app_new); + +- /* no longer supported */ +- if (gs_app_get_kind (app_new) == AS_APP_KIND_SHELL_EXTENSION) { +- g_set_error (error, +- GS_PLUGIN_ERROR, +- GS_PLUGIN_ERROR_NOT_SUPPORTED, +- "shell extensions no longer supported"); +- return NULL; +- } +- + /* look for existing object */ + app = gs_plugin_cache_lookup (plugin, gs_app_get_unique_id (app_new)); + if (app != NULL) +@@ -1548,6 +1539,11 @@ gs_appstream_component_add_extra_info (GsPlugin *plugin, XbBuilderNode *componen + gs_appstream_component_add_category (component, "Addon"); + gs_appstream_component_add_category (component, "Font"); + break; ++ case AS_APP_KIND_SHELL_EXTENSION: ++ gs_appstream_component_add_category (component, "Addon"); ++ gs_appstream_component_add_category (component, "ShellExtension"); ++ gs_appstream_component_add_icon (component, "application-x-addon-symbolic"); ++ break; + case AS_APP_KIND_DRIVER: + gs_appstream_component_add_category (component, "Addon"); + gs_appstream_component_add_category (component, "Driver"); +diff --git a/plugins/core/gs-desktop-common.c b/plugins/core/gs-desktop-common.c +index 33ae3fa2..17ed029d 100644 +--- a/plugins/core/gs-desktop-common.c ++++ b/plugins/core/gs-desktop-common.c +@@ -203,6 +203,9 @@ static const GsDesktopMap map_addons[] = { + { "language-packs", NC_("Menu of Add-ons", "Language Packs"), + { "Addon::LanguagePack", + NULL} }, ++ { "shell-extensions", NC_("Menu of Add-ons", "Shell Extensions"), ++ { "Addon::ShellExtension", ++ NULL} }, + { "localization", NC_("Menu of Add-ons", "Localization"), + { "Addon::Localization", + NULL} }, +diff --git a/plugins/meson.build b/plugins/meson.build +index d749b3df..d30f14d4 100644 +--- a/plugins/meson.build ++++ b/plugins/meson.build +@@ -39,6 +39,9 @@ subdir('repos') + if get_option('rpm_ostree') + subdir('rpm-ostree') + endif ++if get_option('shell_extensions') ++ subdir('shell-extensions') ++endif + if get_option('snap') + subdir('snap') + endif +diff --git a/plugins/packagekit/gs-plugin-packagekit.c b/plugins/packagekit/gs-plugin-packagekit.c +index d0bdabae..2c4e1644 100644 +--- a/plugins/packagekit/gs-plugin-packagekit.c ++++ b/plugins/packagekit/gs-plugin-packagekit.c +@@ -688,5 +688,8 @@ gs_plugin_launch (GsPlugin *plugin, + if (g_strcmp0 (gs_app_get_management_plugin (app), + gs_plugin_get_name (plugin)) != 0) + return TRUE; ++ /* these are handled by the shell extensions plugin */ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SHELL_EXTENSION) ++ return TRUE; + return gs_plugin_app_launch (plugin, app, error); + } +diff --git a/plugins/shell-extensions/gs-appstream.c b/plugins/shell-extensions/gs-appstream.c +new file mode 120000 +index 00000000..96326ab0 +--- /dev/null ++++ b/plugins/shell-extensions/gs-appstream.c +@@ -0,0 +1 @@ ++../core/gs-appstream.c +\ No newline at end of file +diff --git a/plugins/shell-extensions/gs-appstream.h b/plugins/shell-extensions/gs-appstream.h +new file mode 120000 +index 00000000..4eabcb3c +--- /dev/null ++++ b/plugins/shell-extensions/gs-appstream.h +@@ -0,0 +1 @@ ++../core/gs-appstream.h +\ No newline at end of file +diff --git a/plugins/shell-extensions/gs-plugin-shell-extensions.c b/plugins/shell-extensions/gs-plugin-shell-extensions.c +new file mode 100644 +index 00000000..80a5d0eb +--- /dev/null ++++ b/plugins/shell-extensions/gs-plugin-shell-extensions.c +@@ -0,0 +1,1159 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2016-2018 Richard Hughes ++ * Copyright (C) 2018 Kalev Lember ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "gs-appstream.h" ++ ++#define SHELL_EXTENSIONS_API_URI "https://extensions.gnome.org/" ++ ++/* ++ * Things we want from the API: ++ * ++ * - Size on disk/download ++ * - Existing review data for each extension? ++ * - A local icon for an installed shell extension ++ * ++ * See https://git.gnome.org/browse/extensions-web/tree/sweettooth/extensions/views.py ++ * for the source to the web application. ++ */ ++ ++struct GsPluginData { ++ GDBusProxy *proxy; ++ gchar *shell_version; ++ GsApp *cached_origin; ++ GSettings *settings; ++ XbSilo *silo; ++ GRWLock silo_lock; ++}; ++ ++typedef enum { ++ GS_PLUGIN_SHELL_EXTENSION_STATE_ENABLED = 1, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_DISABLED = 2, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_ERROR = 3, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_OUT_OF_DATE = 4, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_DOWNLOADING = 5, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_INITIALIZED = 6, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_UNINSTALLED = 99, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_LAST ++} GsPluginShellExtensionState; ++ ++typedef enum { ++ GS_PLUGIN_SHELL_EXTENSION_KIND_SYSTEM = 1, ++ GS_PLUGIN_SHELL_EXTENSION_KIND_PER_USER = 2, ++ GS_PLUGIN_SHELL_EXTENSION_KIND_LAST ++} GsPluginShellExtensionKind; ++ ++static gboolean _check_silo (GsPlugin *plugin, GCancellable *cancellable, GError **error); ++ ++void ++gs_plugin_initialize (GsPlugin *plugin) ++{ ++ GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData)); ++ ++ /* XbSilo needs external locking as we destroy the silo and build a new ++ * one when something changes */ ++ g_rw_lock_init (&priv->silo_lock); ++ ++ /* add source */ ++ priv->cached_origin = gs_app_new (gs_plugin_get_name (plugin)); ++ gs_app_set_kind (priv->cached_origin, AS_APP_KIND_SOURCE); ++ gs_app_set_origin_hostname (priv->cached_origin, SHELL_EXTENSIONS_API_URI); ++ gs_app_set_origin (priv->cached_origin, _("GNOME")); ++ ++ priv->settings = g_settings_new ("org.gnome.software"); ++ ++ /* add the source to the plugin cache which allows us to match the ++ * unique ID to a GsApp when creating an event */ ++ gs_plugin_cache_add (plugin, ++ gs_app_get_unique_id (priv->cached_origin), ++ priv->cached_origin); ++} ++ ++void ++gs_plugin_destroy (GsPlugin *plugin) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_free (priv->shell_version); ++ if (priv->proxy != NULL) ++ g_object_unref (priv->proxy); ++ if (priv->silo != NULL) ++ g_object_unref (priv->silo); ++ g_object_unref (priv->cached_origin); ++ g_object_unref (priv->settings); ++ g_rw_lock_clear (&priv->silo_lock); ++} ++ ++void ++gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app) ++{ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SHELL_EXTENSION && ++ gs_app_get_scope (app) == AS_APP_SCOPE_USER) { ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ } ++} ++ ++static AsAppState ++gs_plugin_shell_extensions_convert_state (guint value) ++{ ++ switch (value) { ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_DISABLED: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_DOWNLOADING: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_ENABLED: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_ERROR: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_INITIALIZED: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_OUT_OF_DATE: ++ return AS_APP_STATE_INSTALLED; ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_UNINSTALLED: ++ return AS_APP_STATE_AVAILABLE; ++ default: ++ g_warning ("unknown state %u", value); ++ } ++ return AS_APP_STATE_UNKNOWN; ++} ++ ++static GsApp * ++gs_plugin_shell_extensions_parse_installed (GsPlugin *plugin, ++ const gchar *uuid, ++ GVariantIter *iter, ++ GError **error) ++{ ++ const gchar *tmp; ++ gchar *str; ++ GVariant *val; ++ g_autofree gchar *id = NULL; ++ g_autoptr(AsIcon) ic = NULL; ++ g_autoptr(GsApp) app = NULL; ++ ++ id = as_utils_appstream_id_build (uuid); ++ app = gs_app_new (id); ++ gs_app_set_metadata (app, "GnomeSoftware::Creator", ++ gs_plugin_get_name (plugin)); ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ gs_app_set_metadata (app, "shell-extensions::uuid", uuid); ++ gs_app_set_kind (app, AS_APP_KIND_SHELL_EXTENSION); ++ gs_app_set_license (app, GS_APP_QUALITY_NORMAL, "GPL-2.0+"); ++ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "GNOME Shell Extension"); ++ gs_app_set_origin_hostname (app, SHELL_EXTENSIONS_API_URI); ++ gs_app_set_origin (app, _("GNOME")); ++ while (g_variant_iter_loop (iter, "{sv}", &str, &val)) { ++ if (g_strcmp0 (str, "description") == 0) { ++ g_autofree gchar *tmp1 = NULL; ++ g_autofree gchar *tmp2 = NULL; ++ tmp1 = as_markup_import (g_variant_get_string (val, NULL), ++ AS_MARKUP_CONVERT_FORMAT_SIMPLE, ++ NULL); ++ tmp2 = as_markup_convert_simple (tmp1, error); ++ if (tmp2 == NULL) { ++ gs_utils_error_convert_appstream (error); ++ return NULL; ++ } ++ gs_app_set_description (app, GS_APP_QUALITY_NORMAL, tmp2); ++ continue; ++ } ++ if (g_strcmp0 (str, "name") == 0) { ++ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, ++ g_variant_get_string (val, NULL)); ++ continue; ++ } ++ if (g_strcmp0 (str, "version") == 0) { ++ guint val_int = (guint) g_variant_get_double (val); ++ g_autofree gchar *version = g_strdup_printf ("%u", val_int); ++ gs_app_set_version (app, version); ++ continue; ++ } ++ if (g_strcmp0 (str, "url") == 0) { ++ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, ++ g_variant_get_string (val, NULL)); ++ continue; ++ } ++ if (g_strcmp0 (str, "type") == 0) { ++ guint val_int = (guint) g_variant_get_double (val); ++ switch (val_int) { ++ case GS_PLUGIN_SHELL_EXTENSION_KIND_SYSTEM: ++ gs_app_set_scope (app, AS_APP_SCOPE_SYSTEM); ++ break; ++ case GS_PLUGIN_SHELL_EXTENSION_KIND_PER_USER: ++ gs_app_set_scope (app, AS_APP_SCOPE_USER); ++ break; ++ default: ++ g_warning ("%s unknown type %u", uuid, val_int); ++ break; ++ } ++ continue; ++ } ++ if (g_strcmp0 (str, "state") == 0) { ++ AsAppState st; ++ guint val_int = (guint) g_variant_get_double (val); ++ st = gs_plugin_shell_extensions_convert_state (val_int); ++ gs_app_set_state (app, st); ++ continue; ++ } ++ if (g_strcmp0 (str, "error") == 0) { ++ tmp = g_variant_get_string (val, NULL); ++ if (tmp != NULL && tmp[0] != '\0') { ++ g_warning ("unhandled shell error: %s", tmp); ++ } ++ continue; ++ } ++ if (g_strcmp0 (str, "hasPrefs") == 0) { ++ if (g_variant_get_boolean (val)) ++ gs_app_set_metadata (app, "shell-extensions::has-prefs", ""); ++ continue; ++ } ++ if (g_strcmp0 (str, "extension-id") == 0) { ++ tmp = g_variant_get_string (val, NULL); ++ gs_app_set_metadata (app, "shell-extensions::extension-id", tmp); ++ continue; ++ } ++ if (g_strcmp0 (str, "path") == 0) { ++ tmp = g_variant_get_string (val, NULL); ++ gs_app_set_metadata (app, "shell-extensions::path", tmp); ++ continue; ++ } ++ } ++ ++ /* hardcode icon */ ++ ic = as_icon_new (); ++ as_icon_set_kind (ic, AS_ICON_KIND_STOCK); ++ as_icon_set_name (ic, "application-x-addon-symbolic"); ++ gs_app_add_icon (app, ic); ++ ++ /* add categories */ ++ gs_app_add_category (app, "Addon"); ++ gs_app_add_category (app, "ShellExtension"); ++ ++ return g_steal_pointer (&app); ++} ++ ++static void ++gs_plugin_shell_extensions_changed_cb (GDBusProxy *proxy, ++ const gchar *sender_name, ++ const gchar *signal_name, ++ GVariant *parameters, ++ GsPlugin *plugin) ++{ ++ if (g_strcmp0 (signal_name, "ExtensionStatusChanged") == 0) { ++ AsAppState st; ++ GsApp *app; ++ const gchar *error_str; ++ const gchar *uuid; ++ guint state; ++ ++ /* get what changed */ ++ g_variant_get (parameters, "(&si&s)", ++ &uuid, &state, &error_str); ++ ++ /* find it in the cache; do we care? */ ++ app = gs_plugin_cache_lookup (plugin, uuid); ++ if (app == NULL) { ++ g_debug ("no app for changed %s", uuid); ++ return; ++ } ++ ++ /* set the new state in the UI */ ++ st = gs_plugin_shell_extensions_convert_state (state); ++ gs_app_set_state (app, st); ++ ++ /* not sure what to do here */ ++ if (error_str != NULL && error_str[0] != '\0') { ++ g_warning ("%s has error: %s", ++ gs_app_get_id (app), ++ error_str); ++ } ++ } ++} ++ ++gboolean ++gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *name_owner = NULL; ++ g_autoptr(GVariant) version = NULL; ++ ++ if (priv->proxy != NULL) ++ return TRUE; ++ priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION, ++ NULL, ++ "org.gnome.Shell", ++ "/org/gnome/Shell", ++ "org.gnome.Shell.Extensions", ++ cancellable, ++ error); ++ if (priv->proxy == NULL) { ++ gs_utils_error_convert_gio (error); ++ return FALSE; ++ } ++ ++ /* not running under Shell */ ++ name_owner = g_dbus_proxy_get_name_owner (priv->proxy); ++ if (name_owner == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "gnome-shell is not running"); ++ return FALSE; ++ } ++ ++ g_signal_connect (priv->proxy, "g-signal", ++ G_CALLBACK (gs_plugin_shell_extensions_changed_cb), plugin); ++ ++ /* get the GNOME Shell version */ ++ version = g_dbus_proxy_get_cached_property (priv->proxy, ++ "ShellVersion"); ++ if (version == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "unable to get shell version"); ++ return FALSE; ++ } ++ priv->shell_version = g_variant_dup_string (version, NULL); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_installed (GsPlugin *plugin, ++ GsAppList *list, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ GVariantIter *ext_iter; ++ gchar *ext_uuid; ++ g_autoptr(GVariantIter) iter = NULL; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* installed */ ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "ListExtensions", ++ NULL, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_utils_error_convert_gdbus (error); ++ gs_utils_error_convert_gio (error); ++ return FALSE; ++ } ++ ++ /* parse each installed extension */ ++ g_variant_get (retval, "(a{sa{sv}})", &iter); ++ while (g_variant_iter_loop (iter, "{sa{sv}}", &ext_uuid, &ext_iter)) { ++ g_autoptr(GsApp) app = NULL; ++ ++ /* search in the cache */ ++ app = gs_plugin_cache_lookup (plugin, ext_uuid); ++ if (app != NULL) { ++ gs_app_list_add (list, app); ++ continue; ++ } ++ ++ /* parse the data into an GsApp */ ++ app = gs_plugin_shell_extensions_parse_installed (plugin, ++ ext_uuid, ++ ext_iter, ++ error); ++ if (app == NULL) ++ return FALSE; ++ ++ /* ignore system installed */ ++ if (gs_app_get_scope (app) == AS_APP_SCOPE_SYSTEM) ++ continue; ++ ++ /* save in the cache */ ++ gs_plugin_cache_add (plugin, ext_uuid, app); ++ ++ /* add to results */ ++ gs_app_list_add (list, app); ++ } ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_sources (GsPlugin *plugin, ++ GsAppList *list, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autoptr(GsApp) app = NULL; ++ ++ /* create something that we can use to enable/disable */ ++ app = gs_app_new ("org.gnome.extensions"); ++ gs_app_set_kind (app, AS_APP_KIND_SOURCE); ++ gs_app_set_scope (app, AS_APP_SCOPE_USER); ++ if (g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ gs_app_set_state (app, AS_APP_STATE_INSTALLED); ++ else ++ gs_app_set_state (app, AS_APP_STATE_AVAILABLE); ++ gs_app_add_quirk (app, GS_APP_QUIRK_NOT_LAUNCHABLE); ++ gs_app_set_name (app, GS_APP_QUALITY_LOWEST, ++ _("GNOME Shell Extensions Repository")); ++ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, ++ SHELL_EXTENSIONS_API_URI); ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ gs_app_list_add (list, app); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_refine_app (GsPlugin *plugin, ++ GsApp *app, ++ GsPluginRefineFlags flags, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *uuid; ++ g_autofree gchar *xpath = NULL; ++ g_autoptr(GError) error_local = NULL; ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbNode) component = NULL; ++ ++ /* repo not enabled */ ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ ++ /* only process this app if was created by this plugin */ ++ if (g_strcmp0 (gs_app_get_management_plugin (app), ++ gs_plugin_get_name (plugin)) != 0) ++ return TRUE; ++ ++ /* can we get the AppStream-created app state using the cache */ ++ uuid = gs_app_get_metadata_item (app, "shell-extensions::uuid"); ++ if (uuid != NULL && gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) { ++ GsApp *app_cache = gs_plugin_cache_lookup (plugin, uuid); ++ if (app_cache != NULL) { ++ g_debug ("copy cached state for %s", ++ gs_app_get_id (app)); ++ gs_app_set_state (app, gs_app_get_state (app_cache)); ++ } ++ } ++ ++ /* assume apps are available if they exist in AppStream metadata */ ++ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) ++ gs_app_set_state (app, AS_APP_STATE_AVAILABLE); ++ ++ /* FIXME: assume these are small */ ++ if (gs_app_get_size_installed (app) == 0) ++ gs_app_set_size_installed (app, 1024 * 50); ++ if (gs_app_get_size_download (app) == 0) ++ gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE); ++ ++ ++ /* check silo is valid */ ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ ++ /* find the component using the UUID */ ++ if (uuid == NULL) ++ return TRUE; ++ ++ xpath = g_strdup_printf ("components/component/custom/" ++ "value[@key='shell-extensions::uuid'][text()='%s']/../..", ++ uuid); ++ component = xb_silo_query_first (priv->silo, xpath, &error_local); ++ if (component == NULL) { ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) ++ return TRUE; ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) ++ return TRUE; ++ g_propagate_error (error, g_steal_pointer (&error_local)); ++ return FALSE; ++ } ++ return gs_appstream_refine_app (plugin, app, priv->silo, component, flags, error); ++} ++ ++gboolean ++gs_plugin_refine_wildcard (GsPlugin *plugin, ++ GsApp *app, ++ GsAppList *list, ++ GsPluginRefineFlags refine_flags, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *id; ++ g_autofree gchar *xpath = NULL; ++ g_autoptr(GError) error_local = NULL; ++ g_autoptr(GPtrArray) components = NULL; ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ ++ /* repo not enabled */ ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ ++ /* check silo is valid */ ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ ++ /* not enough info to find */ ++ id = gs_app_get_id (app); ++ if (id == NULL) ++ return TRUE; ++ ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ ++ /* find all apps */ ++ xpath = g_strdup_printf ("components/component/id[text()='%s']/..", id); ++ components = xb_silo_query (priv->silo, xpath, 0, &error_local); ++ if (components == NULL) { ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) ++ return TRUE; ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) ++ return TRUE; ++ g_propagate_error (error, g_steal_pointer (&error_local)); ++ return FALSE; ++ } ++ for (guint i = 0; i < components->len; i++) { ++ XbNode *component = g_ptr_array_index (components, i); ++ g_autoptr(GsApp) new = NULL; ++ new = gs_appstream_create_app (plugin, priv->silo, component, error); ++ if (new == NULL) ++ return FALSE; ++ gs_app_subsume_metadata (new, app); ++ if (!gs_appstream_refine_app (plugin, new, priv->silo, component, ++ refine_flags, error)) ++ return FALSE; ++ gs_app_list_add (list, new); ++ } ++ ++ /* success */ ++ return TRUE; ++} ++ ++static gboolean ++gs_plugin_shell_extensions_parse_version (GsPlugin *plugin, ++ const gchar *component_id, ++ XbBuilderNode *app, ++ JsonObject *ver_map, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ JsonObject *json_ver = NULL; ++ gint64 version; ++ g_autofree gchar *shell_version = NULL; ++ ++ /* look for version, major.minor.micro */ ++ if (json_object_has_member (ver_map, priv->shell_version)) { ++ json_ver = json_object_get_object_member (ver_map, ++ priv->shell_version); ++ } ++ ++ /* look for version, major.minor */ ++ if (json_ver == NULL) { ++ g_auto(GStrv) ver_majmin = NULL; ++ ver_majmin = g_strsplit (priv->shell_version, ".", -1); ++ if (g_strv_length (ver_majmin) >= 2) { ++ g_autofree gchar *tmp = NULL; ++ tmp = g_strdup_printf ("%s.%s", ver_majmin[0], ver_majmin[1]); ++ if (json_object_has_member (ver_map, tmp)) ++ json_ver = json_object_get_object_member (ver_map, tmp); ++ } ++ } ++ ++ /* FIXME: mark as incompatible? */ ++ if (json_ver == NULL) ++ return TRUE; ++ ++ /* parse the version */ ++ version = json_object_get_int_member (json_ver, "version"); ++ if (version == 0) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no version in map!"); ++ return FALSE; ++ } ++ shell_version = g_strdup_printf ("%" G_GINT64_FORMAT, version); ++ ++ /* add a dummy release */ ++ xb_builder_node_insert_text (app, "release", NULL, ++ "version", shell_version, ++ NULL); ++ return TRUE; ++} ++ ++ ++ ++static XbBuilderNode * ++gs_plugin_shell_extensions_parse_app (GsPlugin *plugin, ++ JsonObject *json_app, ++ GError **error) ++{ ++ JsonObject *json_ver_map; ++ const gchar *tmp; ++ g_autofree gchar *component_id = NULL; ++ g_autoptr(XbBuilderNode) app = NULL; ++ g_autoptr(XbBuilderNode) categories = NULL; ++ g_autoptr(XbBuilderNode) metadata = NULL; ++ ++ app = xb_builder_node_new ("component"); ++ xb_builder_node_set_attr (app, "kind", "shell-extension"); ++ xb_builder_node_insert_text (app, "project_license", "GPL-2.0+", NULL); ++ categories = xb_builder_node_insert (app, "categories", NULL); ++ xb_builder_node_insert_text (categories, "category", "Addon", NULL); ++ xb_builder_node_insert_text (categories, "category", "ShellExtension", NULL); ++ metadata = xb_builder_node_insert (app, "custom", NULL); ++ ++ tmp = json_object_get_string_member (json_app, "description"); ++ if (tmp != NULL) { ++ g_auto(GStrv) paras = g_strsplit (tmp, "\n", -1); ++ g_autoptr(XbBuilderNode) desc = xb_builder_node_insert (app, "description", NULL); ++ for (guint i = 0; paras[i] != NULL; i++) ++ xb_builder_node_insert_text (desc, "p", paras[i], NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "screenshot"); ++ if (tmp != NULL) { ++ g_autoptr(XbBuilderNode) screenshots = NULL; ++ g_autoptr(XbBuilderNode) screenshot = NULL; ++ g_autofree gchar *uri = NULL; ++ screenshots = xb_builder_node_insert (app, "screenshots", NULL); ++ screenshot = xb_builder_node_insert (screenshots, "screenshot", ++ "kind", "default", ++ NULL); ++ uri = g_build_path ("/", SHELL_EXTENSIONS_API_URI, tmp, NULL); ++ xb_builder_node_insert_text (screenshot, "image", uri, ++ "kind", "source", ++ NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "name"); ++ if (tmp != NULL) ++ xb_builder_node_insert_text (app, "name", tmp, NULL); ++ tmp = json_object_get_string_member (json_app, "uuid"); ++ if (tmp != NULL) { ++ component_id = as_utils_appstream_id_build (tmp); ++ xb_builder_node_insert_text (app, "id", component_id, NULL); ++ xb_builder_node_insert_text (metadata, "value", tmp, ++ "key", "shell-extensions::uuid", ++ NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "link"); ++ if (tmp != NULL) { ++ g_autofree gchar *uri = NULL; ++ uri = g_build_filename (SHELL_EXTENSIONS_API_URI, tmp, NULL); ++ xb_builder_node_insert_text (app, "url", uri, ++ "type", "homepage", ++ NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "icon"); ++ if (tmp != NULL) { ++ /* just use a stock icon as the remote icons are ++ * sometimes missing, poor quality and low resolution */ ++ xb_builder_node_insert_text (app, "icon", ++ "application-x-addon-symbolic", ++ "type", "stock", ++ NULL); ++ } ++ ++ /* try to get version */ ++ json_ver_map = json_object_get_object_member (json_app, "shell_version_map"); ++ if (json_ver_map != NULL) { ++ if (!gs_plugin_shell_extensions_parse_version (plugin, ++ component_id, ++ app, ++ json_ver_map, ++ error)) ++ return NULL; ++ } ++ ++ return g_steal_pointer (&app); ++} ++ ++static GInputStream * ++gs_plugin_appstream_load_json_cb (XbBuilderSource *self, ++ XbBuilderSourceCtx *ctx, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPlugin *plugin = GS_PLUGIN (user_data); ++ JsonArray *json_extensions_array; ++ JsonNode *json_extensions; ++ JsonNode *json_root; ++ JsonObject *json_item; ++ gchar *xml; ++ g_autoptr(JsonParser) json_parser = NULL; ++ g_autoptr(XbBuilderNode) apps = NULL; ++ ++ /* parse the data and find the success */ ++ json_parser = json_parser_new (); ++ if (!json_parser_load_from_stream (json_parser, ++ xb_builder_source_ctx_get_stream (ctx), ++ cancellable, error)) { ++ gs_utils_error_convert_json_glib (error); ++ return NULL; ++ } ++ json_root = json_parser_get_root (json_parser); ++ if (json_root == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no data root"); ++ return NULL; ++ } ++ if (json_node_get_node_type (json_root) != JSON_NODE_OBJECT) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no data object"); ++ return NULL; ++ } ++ json_item = json_node_get_object (json_root); ++ if (json_item == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no data object"); ++ return NULL; ++ } ++ ++ /* load extensions */ ++ apps = xb_builder_node_new ("components"); ++ json_extensions = json_object_get_member (json_item, "extensions"); ++ if (json_extensions == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no extensions object"); ++ return NULL; ++ } ++ json_extensions_array = json_node_get_array (json_extensions); ++ if (json_extensions_array == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no extensions array"); ++ return NULL; ++ } ++ ++ /* parse each app */ ++ for (guint i = 0; i < json_array_get_length (json_extensions_array); i++) { ++ JsonNode *json_extension; ++ JsonObject *json_extension_obj; ++ g_autoptr(XbBuilderNode) component = NULL; ++ ++ json_extension = json_array_get_element (json_extensions_array, i); ++ json_extension_obj = json_node_get_object (json_extension); ++ component = gs_plugin_shell_extensions_parse_app (plugin, ++ json_extension_obj, ++ error); ++ if (component == NULL) ++ return NULL; ++ xb_builder_node_add_child (apps, component); ++ } ++ ++ /* convert back to XML */ ++ xml = xb_builder_node_export (apps, XB_NODE_EXPORT_FLAG_ADD_HEADER, error); ++ if (xml == NULL) ++ return NULL; ++ return g_memory_input_stream_new_from_data (xml, -1, g_free); ++} ++ ++static gboolean ++gs_plugin_shell_extensions_refresh (GsPlugin *plugin, ++ guint cache_age, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *fn = NULL; ++ g_autofree gchar *uri = NULL; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(GsApp) app_dl = gs_app_new (gs_plugin_get_name (plugin)); ++ ++ /* get cache filename */ ++ fn = gs_utils_get_cache_filename ("shell-extensions", ++ "gnome.json", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ error); ++ if (fn == NULL) ++ return FALSE; ++ ++ /* check age */ ++ file = g_file_new_for_path (fn); ++ if (g_file_query_exists (file, NULL)) { ++ guint age = gs_utils_get_file_age (file); ++ if (age < cache_age) { ++ g_debug ("%s is only %u seconds old, ignoring", fn, age); ++ return TRUE; ++ } ++ } ++ ++ /* download the file */ ++ uri = g_strdup_printf ("%s/static/extensions.json", ++ SHELL_EXTENSIONS_API_URI); ++ gs_app_set_summary_missing (app_dl, ++ /* TRANSLATORS: status text when downloading */ ++ _("Downloading shell extension metadata…")); ++ if (!gs_plugin_download_file (plugin, app_dl, uri, fn, cancellable, error)) { ++ gs_utils_error_add_origin_id (error, priv->cached_origin); ++ return FALSE; ++ } ++ ++ /* be explicit */ ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ if (priv->silo != NULL) ++ xb_silo_invalidate (priv->silo); ++ ++ return TRUE; ++} ++ ++static gboolean ++_check_silo (GsPlugin *plugin, GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *blobfn = NULL; ++ g_autofree gchar *fn = NULL; ++ g_autoptr(GError) error_local = NULL; ++ g_autoptr(GFile) blobfile = NULL; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GRWLockReaderLocker) reader_locker = NULL; ++ g_autoptr(GRWLockWriterLocker) writer_locker = NULL; ++ g_autoptr(XbBuilder) builder = xb_builder_new (); ++ g_autoptr(XbBuilderNode) info = NULL; ++ g_autoptr(XbBuilderSource) source = xb_builder_source_new (); ++ ++ reader_locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ /* everything is okay */ ++ if (priv->silo != NULL && xb_silo_is_valid (priv->silo)) { ++ g_debug ("silo already valid"); ++ return TRUE; ++ } ++ g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); ++ ++ /* drat! silo needs regenerating */ ++ writer_locker = g_rw_lock_writer_locker_new (&priv->silo_lock); ++ g_clear_object (&priv->silo); ++ ++ /* verbose profiling */ ++ if (g_getenv ("GS_XMLB_VERBOSE") != NULL) { ++ xb_builder_set_profile_flags (builder, ++ XB_SILO_PROFILE_FLAG_XPATH | ++ XB_SILO_PROFILE_FLAG_DEBUG); ++ } ++ ++ /* add metadata */ ++ info = xb_builder_node_insert (NULL, "info", NULL); ++ xb_builder_node_insert_text (info, "scope", "user", NULL); ++ xb_builder_source_set_info (source, info); ++ ++ /* add support for JSON files */ ++ fn = gs_utils_get_cache_filename ("shell-extensions", ++ "gnome.json", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ error); ++ if (fn == NULL) ++ return FALSE; ++ xb_builder_source_add_adapter (source, "application/json", ++ gs_plugin_appstream_load_json_cb, ++ plugin, NULL); ++ file = g_file_new_for_path (fn); ++ if (!xb_builder_source_load_file (source, file, ++ XB_BUILDER_SOURCE_FLAG_WATCH_FILE, ++ cancellable, ++ error)) { ++ return FALSE; ++ } ++ xb_builder_import_source (builder, source); ++ ++ /* create binary cache */ ++ blobfn = gs_utils_get_cache_filename ("shell-extensions", ++ "extensions-web.xmlb", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ error); ++ if (blobfn == NULL) ++ return FALSE; ++ blobfile = g_file_new_for_path (blobfn); ++ g_debug ("ensuring %s", blobfn); ++ priv->silo = xb_builder_ensure (builder, blobfile, ++ XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID, ++ NULL, &error_local); ++ if (priv->silo == NULL) { ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_FAILED, ++ "failed to compile %s", ++ error_local->message); ++ return FALSE; ++ } ++ ++ /* success */ ++ return TRUE; ++} ++ ++static void ++_claim_components (GsPlugin *plugin, GsAppList *list) ++{ ++ for (guint i = 0; i < gs_app_list_length (list); i++) { ++ GsApp *app = gs_app_list_index (list, i); ++ gs_app_set_kind (app, AS_APP_KIND_SHELL_EXTENSION); ++ gs_app_set_origin_hostname (app, SHELL_EXTENSIONS_API_URI); ++ gs_app_set_origin (app, _("GNOME")); ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ gs_app_set_summary (app, GS_APP_QUALITY_LOWEST, ++ /* TRANSLATORS: the one-line summary */ ++ _("GNOME Shell Extension")); ++ } ++} ++ ++gboolean ++gs_plugin_add_search (GsPlugin *plugin, gchar **values, GsAppList *list, ++ GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(GsAppList) list_tmp = gs_app_list_new (); ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ if (!gs_appstream_search (plugin, priv->silo, (const gchar * const *) values, list_tmp, ++ cancellable, error)) ++ return FALSE; ++ _claim_components (plugin, list_tmp); ++ gs_app_list_add_list (list, list_tmp); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_category_apps (GsPlugin *plugin, GsCategory *category, GsAppList *list, ++ GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ return gs_appstream_add_category_apps (plugin, priv->silo, category, ++ list, cancellable, error); ++} ++ ++gboolean ++gs_plugin_refresh (GsPlugin *plugin, ++ guint cache_age, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ if (!gs_plugin_shell_extensions_refresh (plugin, ++ cache_age, ++ cancellable, ++ error)) ++ return FALSE; ++ return _check_silo (plugin, cancellable, error); ++} ++ ++gboolean ++gs_plugin_app_remove (GsPlugin *plugin, ++ GsApp *app, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *uuid; ++ gboolean ret; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* only process this app if was created by this plugin */ ++ if (g_strcmp0 (gs_app_get_management_plugin (app), ++ gs_plugin_get_name (plugin)) != 0) ++ return TRUE; ++ ++ /* disable repository */ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SOURCE) { ++ gs_app_set_state (app, AS_APP_STATE_REMOVING); ++ g_settings_set_boolean (priv->settings, "enable-shell-extensions-repo", FALSE); ++ /* remove appstream data */ ++ ret = gs_plugin_shell_extensions_refresh (plugin, ++ G_MAXUINT, ++ cancellable, ++ error); ++ gs_app_set_state (app, AS_APP_STATE_AVAILABLE); ++ return ret; ++ } ++ ++ /* remove */ ++ gs_app_set_state (app, AS_APP_STATE_REMOVING); ++ uuid = gs_app_get_metadata_item (app, "shell-extensions::uuid"); ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "UninstallExtension", ++ g_variant_new ("(s)", uuid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_utils_error_convert_gio (error); ++ gs_app_set_state_recover (app); ++ return FALSE; ++ } ++ ++ /* not sure why this would fail -- perhaps installed in /usr? */ ++ g_variant_get (retval, "(b)", &ret); ++ if (!ret) { ++ gs_app_set_state_recover (app); ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "failed to uninstall %s", ++ gs_app_get_id (app)); ++ return FALSE; ++ } ++ ++ /* state is not known: we don't know if we can re-install this app */ ++ gs_app_set_state (app, AS_APP_STATE_UNKNOWN); ++ ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_app_install (GsPlugin *plugin, ++ GsApp *app, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *uuid; ++ const gchar *retstr; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* only process this app if was created by this plugin */ ++ if (g_strcmp0 (gs_app_get_management_plugin (app), ++ gs_plugin_get_name (plugin)) != 0) ++ return TRUE; ++ ++ /* enable repository */ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SOURCE) { ++ gboolean ret; ++ ++ gs_app_set_state (app, AS_APP_STATE_INSTALLING); ++ g_settings_set_boolean (priv->settings, "enable-shell-extensions-repo", TRUE); ++ /* refresh metadata */ ++ ret = gs_plugin_shell_extensions_refresh (plugin, ++ G_MAXUINT, ++ cancellable, ++ error); ++ gs_app_set_state (app, AS_APP_STATE_INSTALLED); ++ return ret; ++ } ++ ++ /* install */ ++ uuid = gs_app_get_metadata_item (app, "shell-extensions::uuid"); ++ gs_app_set_state (app, AS_APP_STATE_INSTALLING); ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "InstallRemoteExtension", ++ g_variant_new ("(s)", uuid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_app_set_state_recover (app); ++ return FALSE; ++ } ++ g_variant_get (retval, "(&s)", &retstr); ++ ++ /* user declined download */ ++ if (g_strcmp0 (retstr, "cancelled") == 0) { ++ gs_app_set_state_recover (app); ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_CANCELLED, ++ "extension %s download was cancelled", ++ gs_app_get_id (app)); ++ return FALSE; ++ } ++ g_debug ("shell returned: %s", retstr); ++ ++ /* state is known */ ++ gs_app_set_state (app, AS_APP_STATE_INSTALLED); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_launch (GsPlugin *plugin, ++ GsApp *app, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *uuid = NULL; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* launch both PackageKit-installed and user-installed */ ++ if (gs_app_get_kind (app) != AS_APP_KIND_SHELL_EXTENSION) ++ return TRUE; ++ ++ uuid = g_strdup (gs_app_get_metadata_item (app, "shell-extensions::uuid")); ++ if (uuid == NULL) { ++ const gchar *suffix = ".shell-extension"; ++ const gchar *id = gs_app_get_id (app); ++ /* PackageKit-installed extension ID generated by appstream-builder */ ++ if (g_str_has_suffix (id, suffix)) ++ uuid = g_strndup (id, strlen (id) - strlen (suffix)); ++ } ++ if (uuid == NULL) { ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_FAILED, ++ "no uuid set for %s", ++ gs_app_get_id (app)); ++ return FALSE; ++ } ++ /* launch */ ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "LaunchExtensionPrefs", ++ g_variant_new ("(s)", uuid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_utils_error_convert_gio (error); ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_categories (GsPlugin *plugin, ++ GPtrArray *list, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ ++ /* repo not enabled */ ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ ++ /* just ensure there is any data, no matter how old */ ++ return gs_plugin_shell_extensions_refresh (plugin, ++ G_MAXUINT, ++ cancellable, ++ error); ++} +diff --git a/plugins/shell-extensions/gs-self-test.c b/plugins/shell-extensions/gs-self-test.c +new file mode 100644 +index 00000000..f96ee60d +--- /dev/null ++++ b/plugins/shell-extensions/gs-self-test.c +@@ -0,0 +1,156 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2017 Richard Hughes ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "gnome-software-private.h" ++ ++#include "gs-test.h" ++ ++static void ++gs_plugins_shell_extensions_installed_func (GsPluginLoader *plugin_loader) ++{ ++ GsApp *app; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GsAppList) list = NULL; ++ g_autoptr(GsPluginJob) plugin_job = NULL; ++ ++ /* no shell-extensions, abort */ ++ if (!gs_plugin_loader_get_enabled (plugin_loader, "shell-extensions")) { ++ g_test_skip ("not enabled"); ++ return; ++ } ++ ++ /* get installed packages */ ++ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_GET_INSTALLED, ++ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_CATEGORIES, ++ NULL); ++ list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error); ++ gs_test_flush_main_context (); ++ g_assert_no_error (error); ++ g_assert (list != NULL); ++ ++ /* no shell-extensions installed, abort */ ++ if (gs_app_list_length (list) < 1) { ++ g_test_skip ("no shell extensions installed"); ++ return; ++ } ++ ++ /* test properties */ ++ app = gs_app_list_lookup (list, "*/*/*/*/background-logo_fedorahosted.org/*"); ++ if (app == NULL) { ++ g_test_skip ("not found"); ++ return; ++ } ++ ++ g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_INSTALLED); ++ g_assert_cmpint (gs_app_get_scope (app), ==, AS_APP_SCOPE_USER); ++ g_assert_cmpstr (gs_app_get_name (app), ==, "Background Logo"); ++ g_assert_cmpstr (gs_app_get_summary (app), ==, "GNOME Shell Extension"); ++ g_assert_cmpstr (gs_app_get_description (app), ==, ++ "Overlay a tasteful logo on the background to " ++ "enhance the user experience"); ++ g_assert_cmpstr (gs_app_get_license (app), ==, "GPL-2.0+"); ++ g_assert_cmpstr (gs_app_get_management_plugin (app), ==, "shell-extensions"); ++ g_assert (gs_app_has_category (app, "Addon")); ++ g_assert (gs_app_has_category (app, "ShellExtension")); ++ g_assert_cmpstr (gs_app_get_metadata_item (app, "shell-extensions::has-prefs"), ==, ""); ++ g_assert_cmpstr (gs_app_get_metadata_item (app, "shell-extensions::uuid"), ==, ++ "background-logo@fedorahosted.org"); ++} ++ ++static void ++gs_plugins_shell_extensions_remote_func (GsPluginLoader *plugin_loader) ++{ ++ const gchar *cachedir = "/var/tmp/gs-self-test"; ++ gboolean ret; ++ g_autofree gchar *fn = NULL; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GPtrArray) components = NULL; ++ g_autoptr(GsPluginJob) plugin_job = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ ++ /* no shell-extensions, abort */ ++ if (!gs_plugin_loader_get_enabled (plugin_loader, "shell-extensions")) { ++ g_test_skip ("not enabled"); ++ return; ++ } ++ ++ /* ensure files are removed */ ++ g_setenv ("GS_SELF_TEST_CACHEDIR", cachedir, TRUE); ++ gs_utils_rmtree (cachedir, NULL); ++ ++ /* refresh the metadata */ ++ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFRESH, ++ "age", (guint64) G_MAXUINT, ++ NULL); ++ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ ++ /* ensure file was populated */ ++ silo = xb_silo_new (); ++ fn = gs_utils_get_cache_filename ("shell-extensions", ++ "extensions-web.xmlb", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (fn); ++ file = g_file_new_for_path (fn); ++ ret = xb_silo_load_from_file (silo, file, ++ XB_SILO_LOAD_FLAG_NONE, ++ NULL, &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ components = xb_silo_query (silo, "components/component", 0, &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (components); ++ g_assert_cmpint (components->len, >, 20); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ gboolean ret; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GsPluginLoader) plugin_loader = NULL; ++ const gchar *whitelist[] = { ++ "shell-extensions", ++ NULL ++ }; ++ ++ g_test_init (&argc, &argv, NULL); ++ g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); ++ ++ /* only critical and error are fatal */ ++ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); ++ ++ /* we can only load this once per process */ ++ plugin_loader = gs_plugin_loader_new (); ++ gs_plugin_loader_add_location (plugin_loader, LOCALPLUGINDIR); ++ ret = gs_plugin_loader_setup (plugin_loader, ++ (gchar**) whitelist, ++ NULL, ++ NULL, ++ &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ ++ /* plugin tests go here */ ++ g_test_add_data_func ("/gnome-software/plugins/shell-extensions/installed", ++ plugin_loader, ++ (GTestDataFunc) gs_plugins_shell_extensions_installed_func); ++ g_test_add_data_func ("/gnome-software/plugins/shell-extensions/remote", ++ plugin_loader, ++ (GTestDataFunc) gs_plugins_shell_extensions_remote_func); ++ ++ return g_test_run (); ++} +diff --git a/plugins/shell-extensions/meson.build b/plugins/shell-extensions/meson.build +new file mode 100644 +index 00000000..3a008f7b +--- /dev/null ++++ b/plugins/shell-extensions/meson.build +@@ -0,0 +1,47 @@ ++cargs = ['-DG_LOG_DOMAIN="GsPluginShellExtensions"'] ++cargs += ['-DLOCALPLUGINDIR="' + meson.current_build_dir() + '"'] ++ ++shared_module( ++ 'gs_plugin_shell-extensions', ++ sources : [ ++ 'gs-appstream.c', ++ 'gs-plugin-shell-extensions.c' ++ ], ++ include_directories : [ ++ include_directories('../..'), ++ include_directories('../../lib'), ++ ], ++ install : true, ++ install_dir: plugin_dir, ++ c_args : cargs, ++ dependencies : [ ++ plugin_libs, ++ libxmlb, ++ ], ++ link_with : [ ++ libgnomesoftware ++ ] ++) ++ ++if get_option('tests') ++ e = executable( ++ 'gs-self-test-shell-extensions', ++ compiled_schemas, ++ sources : [ ++ 'gs-self-test.c' ++ ], ++ include_directories : [ ++ include_directories('../..'), ++ include_directories('../../lib'), ++ ], ++ dependencies : [ ++ plugin_libs, ++ libxmlb, ++ ], ++ link_with : [ ++ libgnomesoftware ++ ], ++ c_args : cargs, ++ ) ++ test('gs-self-test-shell-extensions', e, suite: ['plugins', 'shell-extensions'], env: test_env) ++endif +diff --git a/src/gs-category-page.c b/src/gs-category-page.c +index 10467436..a786d48d 100644 +--- a/src/gs-category-page.c ++++ b/src/gs-category-page.c +@@ -38,6 +38,8 @@ struct _GsCategoryPage + guint sort_name_handler_id; + SubcategorySortType sort_type; + ++ GtkWidget *infobar_category_shell_extensions; ++ GtkWidget *button_category_shell_extensions; + GtkWidget *category_detail_box; + GtkWidget *scrolledwindow_category; + GtkWidget *subcats_filter_label; +@@ -321,6 +323,12 @@ gs_category_page_reload (GsPage *page) + gtk_widget_set_visible (self->subcats_sort_button, TRUE); + } + ++ /* show the shell extensions header */ ++ if (g_strcmp0 (gs_category_get_id (self->subcategory), "shell-extensions") == 0) ++ gtk_widget_set_visible (self->infobar_category_shell_extensions, TRUE); ++ else ++ gtk_widget_set_visible (self->infobar_category_shell_extensions, FALSE); ++ + if (self->sort_rating_handler_id > 0) { + g_signal_handler_disconnect (self->sort_rating_button, + self->sort_rating_handler_id); +@@ -524,6 +532,18 @@ gs_category_page_dispose (GObject *object) + G_OBJECT_CLASS (gs_category_page_parent_class)->dispose (object); + } + ++static void ++button_shell_extensions_cb (GtkButton *button, GsCategoryPage *self) ++{ ++ gboolean ret; ++ g_autoptr(GError) error = NULL; ++ const gchar *argv[] = { "gnome-shell-extension-prefs", NULL }; ++ ret = g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH, ++ NULL, NULL, NULL, &error); ++ if (!ret) ++ g_warning ("failed to exec %s: %s", argv[0], error->message); ++} ++ + static gboolean + gs_category_page_setup (GsPage *page, + GsShell *shell, +@@ -545,6 +565,9 @@ gs_category_page_setup (GsPage *page, + + adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (self->scrolledwindow_category)); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (self->category_detail_box), adj); ++ ++ g_signal_connect (self->button_category_shell_extensions, "clicked", ++ G_CALLBACK (button_shell_extensions_cb), self); + return TRUE; + } + +@@ -563,6 +586,8 @@ gs_category_page_class_init (GsCategoryPageClass *klass) + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-category-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, category_detail_box); ++ gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, infobar_category_shell_extensions); ++ gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, button_category_shell_extensions); + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, scrolledwindow_category); + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, subcats_filter_label); + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, subcats_filter_button_label); +diff --git a/src/gs-category-page.ui b/src/gs-category-page.ui +index cbb82840..2a3c789f 100644 +--- a/src/gs-category-page.ui ++++ b/src/gs-category-page.ui +@@ -97,6 +97,51 @@ + 24 + + ++ ++ ++ False ++ True ++ True ++ other ++ 24 ++ 24 ++ 24 ++ ++ ++ ++ 6 ++ end ++ ++ ++ Extension Settings ++ True ++ True ++ True ++ True ++ ++ ++ ++ ++ ++ ++ start ++ True ++ 16 ++ ++ ++ True ++ True ++ Extensions are used at your own risk. If you have any system problems, it is recommended to disable them. ++ True ++ ++ ++ ++ ++ ++ ++ + + + True +diff --git a/src/gs-details-page.c b/src/gs-details-page.c +index 8eb07bd3..37a717bf 100644 +--- a/src/gs-details-page.c ++++ b/src/gs-details-page.c +@@ -923,9 +923,17 @@ gs_details_page_refresh_buttons (GsDetailsPage *self) + break; + } + +- gtk_button_set_label (GTK_BUTTON (self->button_details_launch), +- /* TRANSLATORS: A label for a button to execute the selected application. */ +- _("_Launch")); ++ if (gs_app_get_kind (self->app) == AS_APP_KIND_SHELL_EXTENSION) { ++ gtk_button_set_label (GTK_BUTTON (self->button_details_launch), ++ /* TRANSLATORS: A label for a button to show the settings for ++ the selected shell extension. */ ++ _("Extension Settings")); ++ } else { ++ gtk_button_set_label (GTK_BUTTON (self->button_details_launch), ++ /* TRANSLATORS: A label for a button to execute the selected ++ application. */ ++ _("_Launch")); ++ } + + /* don't show the launch and shortcut buttons if the app doesn't have a desktop ID */ + if (gs_app_get_id (self->app) == NULL) { +diff --git a/src/gs-repo-row.c b/src/gs-repo-row.c +index 35b35045..7ce7e618 100644 +--- a/src/gs-repo-row.c ++++ b/src/gs-repo-row.c +@@ -68,7 +68,8 @@ repo_supports_removal (GsApp *repo) + /* can't remove a repo, only enable/disable existing ones */ + if (g_strcmp0 (management_plugin, "fwupd") == 0 || + g_strcmp0 (management_plugin, "packagekit") == 0 || +- g_strcmp0 (management_plugin, "rpm-ostree") == 0) ++ g_strcmp0 (management_plugin, "rpm-ostree") == 0 || ++ g_strcmp0 (management_plugin, "shell-extensions") == 0) + return FALSE; + + return TRUE; +diff --git a/src/gs-repos-dialog.c b/src/gs-repos-dialog.c +index 93830308..7c11dc78 100644 +--- a/src/gs-repos-dialog.c ++++ b/src/gs-repos-dialog.c +@@ -136,7 +136,8 @@ repo_supports_removal (GsApp *repo) + /* can't remove a repo, only enable/disable existing ones */ + if (g_strcmp0 (management_plugin, "fwupd") == 0 || + g_strcmp0 (management_plugin, "packagekit") == 0 || +- g_strcmp0 (management_plugin, "rpm-ostree") == 0) ++ g_strcmp0 (management_plugin, "rpm-ostree") == 0 || ++ g_strcmp0 (management_plugin, "shell-extensions") == 0) + return FALSE; + + return TRUE; +diff --git a/src/gs-summary-tile.c b/src/gs-summary-tile.c +index 446200de..90e810c9 100644 +--- a/src/gs-summary-tile.c ++++ b/src/gs-summary-tile.c +@@ -41,7 +41,7 @@ gs_summary_tile_refresh (GsAppTile *self) + const GdkPixbuf *pixbuf; + gboolean installed; + g_autofree gchar *name = NULL; +- const gchar *summary; ++ g_autofree gchar *summary = NULL; + const gchar *css; + + if (app == NULL) +@@ -53,7 +53,18 @@ gs_summary_tile_refresh (GsAppTile *self) + /* set name */ + gtk_label_set_label (GTK_LABEL (tile->name), gs_app_get_name (app)); + +- summary = gs_app_get_summary (app); ++ /* some kinds have boring summaries */ ++ switch (gs_app_get_kind (app)) { ++ case AS_APP_KIND_SHELL_EXTENSION: ++ summary = g_strdup (gs_app_get_description (app)); ++ if (summary != NULL) ++ g_strdelimit (summary, "\n\t", ' '); ++ break; ++ default: ++ summary = g_strdup (gs_app_get_summary (app)); ++ break; ++ } ++ + gtk_label_set_label (GTK_LABEL (tile->summary), summary); + gtk_widget_set_visible (tile->summary, summary && summary[0]); + +-- +2.18.2 + + +From 420396f45380ce4da2a609f139e5f7f47a6d5d5d Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Wed, 3 Jun 2020 15:50:44 +0200 +Subject: [PATCH 2/2] Revert "Update POTFILES.in" + +This reverts commit 9afc91a6de17117a32e4d36210230ba218e8ea7d. +--- + po/POTFILES.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/po/POTFILES.in b/po/POTFILES.in +index a44a6ad3..c38c56e2 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -88,5 +88,6 @@ plugins/fwupd/gs-plugin-fwupd.c + plugins/fwupd/org.gnome.Software.Plugin.Fwupd.metainfo.xml.in + plugins/odrs/gs-plugin-odrs.c + plugins/odrs/org.gnome.Software.Plugin.Odrs.metainfo.xml.in ++plugins/shell-extensions/gs-plugin-shell-extensions.c + plugins/snap/gs-plugin-snap.c + plugins/snap/org.gnome.Software.Plugin.Snap.metainfo.xml.in +-- +2.18.2 + diff --git a/SOURCES/be-able-to-disable-odrs.patch b/SOURCES/be-able-to-disable-odrs.patch new file mode 100644 index 0000000..68a992a --- /dev/null +++ b/SOURCES/be-able-to-disable-odrs.patch @@ -0,0 +1,27 @@ +From b50003ed83cab2c6cf6654e5972d6ee3e2303eb6 Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Tue, 16 Feb 2021 16:09:08 +0100 +Subject: [PATCH] odrs: Cannot be disabled by filling empty 'review-server' + setting + +Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1143 +--- + plugins/odrs/gs-plugin-odrs.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/plugins/odrs/gs-plugin-odrs.c b/plugins/odrs/gs-plugin-odrs.c +index f0c0c3b91..2c9bd878d 100644 +--- a/plugins/odrs/gs-plugin-odrs.c ++++ b/plugins/odrs/gs-plugin-odrs.c +@@ -175,6 +175,8 @@ gs_plugin_initialize (GsPlugin *plugin) + + /* set name of MetaInfo file */ + gs_plugin_set_appstream_id (plugin, "org.gnome.Software.Plugin.Odrs"); ++ ++ gs_plugin_set_enabled (plugin, priv->review_server && *priv->review_server); + } + + static GArray * +-- +GitLab + diff --git a/SOURCES/crash-when-run-as-root.patch b/SOURCES/crash-when-run-as-root.patch new file mode 100644 index 0000000..8ea4921 --- /dev/null +++ b/SOURCES/crash-when-run-as-root.patch @@ -0,0 +1,57 @@ +From 98dbef8b5a547d3d920d377022e20d2e63519b2f Mon Sep 17 00:00:00 2001 +From: Milan Crha +Date: Tue, 16 Feb 2021 12:58:19 +0100 +Subject: [PATCH 1/2] GsApplication: Crash when run as root + +The `search_provider` is not initialized in this case, leading +to NULL dereference. Skip the call in such cases, because the search +provider is not that important. + +Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1142 +--- + src/gs-application.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/gs-application.c b/src/gs-application.c +index 48f0c6aac..fdb5f55c4 100644 +--- a/src/gs-application.c ++++ b/src/gs-application.c +@@ -912,7 +912,8 @@ static void + gs_application_setup_search_provider (GsApplication *app) + { + gs_application_initialize_plugins (app); +- gs_shell_search_provider_setup (app->search_provider, app->plugin_loader); ++ if (app->search_provider) ++ gs_shell_search_provider_setup (app->search_provider, app->plugin_loader); + } + + static void +-- +GitLab + + +From 0055bfa8535bb7c5ccd9ace244d71b2885a47daa Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Mon, 22 Feb 2021 11:41:05 +0000 +Subject: [PATCH 2/2] Apply 1 suggestion(s) to 1 file(s) + +--- + src/gs-application.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/gs-application.c b/src/gs-application.c +index fdb5f55c4..f05f6f718 100644 +--- a/src/gs-application.c ++++ b/src/gs-application.c +@@ -49,7 +49,7 @@ struct _GsApplication { + #ifdef HAVE_PACKAGEKIT + GsDbusHelper *dbus_helper; + #endif +- GsShellSearchProvider *search_provider; ++ GsShellSearchProvider *search_provider; /* (nullable) (owned) */ + GSettings *settings; + GSimpleActionGroup *action_map; + guint shell_loaded_handler_id; +-- +GitLab + diff --git a/SOURCES/devel-install-headers.patch b/SOURCES/devel-install-headers.patch new file mode 100644 index 0000000..5059bfd --- /dev/null +++ b/SOURCES/devel-install-headers.patch @@ -0,0 +1,36 @@ +From 5b0e476a17129be0d8b451467aded9c8ae861218 Mon Sep 17 00:00:00 2001 +From: Rasmus Thomsen +Date: Fri, 17 Apr 2020 18:55:21 +0200 +Subject: [PATCH] build: install more headers + +These are required for building out of tree plugins +--- + lib/meson.build | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/lib/meson.build b/lib/meson.build +index 1c00d4f3e..6a2e803e5 100644 +--- a/lib/meson.build ++++ b/lib/meson.build +@@ -4,12 +4,18 @@ cargs += ['-DLOCALPLUGINDIR=""'] + install_headers([ + 'gnome-software.h', + 'gs-app.h', ++ 'gs-app-collation.h', + 'gs-app-list.h', ++ 'gs-autocleanups.h', + 'gs-category.h', ++ 'gs-ioprio.h', + 'gs-metered.h', + 'gs-os-release.h', + 'gs-plugin.h', + 'gs-plugin-event.h', ++ 'gs-plugin-job.h', ++ 'gs-plugin-loader.h', ++ 'gs-plugin-loader-sync.h', + 'gs-plugin-types.h', + 'gs-plugin-vfuncs.h', + 'gs-utils.h' +-- +GitLab + diff --git a/SOURCES/flatpak-same-runtime-origin.patch b/SOURCES/flatpak-same-runtime-origin.patch new file mode 100644 index 0000000..3ef4ed5 --- /dev/null +++ b/SOURCES/flatpak-same-runtime-origin.patch @@ -0,0 +1,117 @@ +diff -up gnome-software-3.36.1/plugins/flatpak/gs-flatpak.c.10 gnome-software-3.36.1/plugins/flatpak/gs-flatpak.c +--- gnome-software-3.36.1/plugins/flatpak/gs-flatpak.c.10 2020-05-22 16:51:19.868380300 +0200 ++++ gnome-software-3.36.1/plugins/flatpak/gs-flatpak.c 2021-06-21 21:24:00.233689928 +0200 +@@ -1844,12 +1844,13 @@ gs_flatpak_refine_app_state (GsFlatpak * + } + + static GsApp * +-gs_flatpak_create_runtime (GsFlatpak *self, GsApp *parent, const gchar *runtime) ++gs_flatpak_create_runtime (GsFlatpak *self, GsApp *parent, const gchar *runtime, GCancellable *cancellable) + { + g_autofree gchar *source = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GsApp) app_cache = NULL; + g_autoptr(GsApp) app = NULL; ++ const gchar *origin; + + /* get the name/arch/branch */ + split = g_strsplit (runtime, "/", -1); +@@ -1864,6 +1865,24 @@ gs_flatpak_create_runtime (GsFlatpak *se + gs_app_set_kind (app, AS_APP_KIND_RUNTIME); + gs_app_set_branch (app, split[2]); + ++ origin = gs_app_get_origin (parent); ++ if (origin != NULL) { ++ g_autoptr(FlatpakRemoteRef) xref = NULL; ++ ++ xref = flatpak_installation_fetch_remote_ref_sync (self->installation, ++ origin, ++ FLATPAK_REF_KIND_RUNTIME, ++ gs_app_get_id (app), ++ gs_flatpak_app_get_ref_arch (parent), ++ gs_app_get_branch (app), ++ cancellable, ++ NULL); ++ ++ /* Prefer runtime from the same origin as the parent application */ ++ if (xref) ++ gs_app_set_origin (app, origin); ++ } ++ + /* search in the cache */ + app_cache = gs_plugin_cache_lookup (self->plugin, gs_app_get_unique_id (app)); + if (app_cache != NULL) { +@@ -1898,6 +1917,7 @@ gs_flatpak_set_app_metadata (GsFlatpak * + GsApp *app, + const gchar *data, + gsize length, ++ GCancellable *cancellable, + GError **error) + { + gboolean secure = TRUE; +@@ -1951,7 +1971,7 @@ gs_flatpak_set_app_metadata (GsFlatpak * + gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED_SECURE); + + /* create runtime */ +- app_runtime = gs_flatpak_create_runtime (self, app, runtime); ++ app_runtime = gs_flatpak_create_runtime (self, app, runtime, cancellable); + if (app_runtime != NULL) { + gs_plugin_refine_item_scope (self, app_runtime); + gs_app_set_runtime (app, app_runtime); +@@ -2046,7 +2066,7 @@ gs_plugin_refine_item_metadata (GsFlatpa + } + + /* parse key file */ +- if (!gs_flatpak_set_app_metadata (self, app, str, len, error)) ++ if (!gs_flatpak_set_app_metadata (self, app, str, len, cancellable, error)) + return FALSE; + return TRUE; + } +@@ -2506,6 +2526,7 @@ gs_flatpak_file_to_app_bundle (GsFlatpak + if (!gs_flatpak_set_app_metadata (self, app, + g_bytes_get_data (metadata, NULL), + g_bytes_get_size (metadata), ++ cancellable, + error)) + return NULL; + +diff -up gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c.10 gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c +--- gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c.10 2021-06-21 20:38:42.585590510 +0200 ++++ gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c 2021-06-21 21:23:00.977665979 +0200 +@@ -852,6 +852,36 @@ gs_plugin_app_install (GsPlugin *plugin, + ref, NULL, error)) { + gs_flatpak_error_convert (error); + return FALSE; ++ } else { ++ GsApp *runtime; ++ ++ runtime = gs_app_get_runtime (app); ++ if (runtime != NULL && gs_app_get_origin (runtime) != NULL) { ++ g_autoptr(FlatpakInstalledRef) runtime_ref = NULL; ++ ++ runtime_ref = flatpak_installation_get_installed_ref (gs_flatpak_get_installation (flatpak), ++ gs_flatpak_app_get_ref_kind (runtime), ++ gs_flatpak_app_get_ref_name (runtime), ++ gs_flatpak_app_get_ref_arch (runtime), ++ gs_app_get_branch (runtime), ++ cancellable, ++ NULL); ++ if (runtime_ref == NULL) { ++ g_autoptr(GError) error_local = NULL; ++ ++ g_clear_pointer (&ref, g_free); ++ ref = gs_flatpak_app_get_ref_display (runtime); ++ if (!flatpak_transaction_add_install (transaction, gs_app_get_origin (runtime), ref, NULL, &error_local)) { ++ if (g_error_matches (error_local, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED)) { ++ g_clear_error (&error_local); ++ } else { ++ g_propagate_error (error, g_steal_pointer (&error_local)); ++ gs_flatpak_error_convert (error); ++ return FALSE; ++ } ++ } ++ } ++ } + } + } + diff --git a/SOURCES/gnome-software-3.36.1-unrelated-refs.patch b/SOURCES/gnome-software-3.36.1-unrelated-refs.patch new file mode 100644 index 0000000..a63d2d2 --- /dev/null +++ b/SOURCES/gnome-software-3.36.1-unrelated-refs.patch @@ -0,0 +1,26 @@ +diff -up gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c.unrelated-refs gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c +--- gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c.unrelated-refs 2020-10-09 14:07:16.407235531 -0400 ++++ gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c 2020-10-09 14:08:48.775544689 -0400 +@@ -696,6 +696,11 @@ gs_plugin_app_remove (GsPlugin *plugin, + gs_flatpak_error_convert (error); + return FALSE; + } ++ ++ /* add to the transaction cache for quick look up -- other unrelated ++ * refs will be matched using gs_plugin_flatpak_find_app_by_ref() */ ++ gs_flatpak_transaction_add_app (transaction, app); ++ + ref = gs_flatpak_app_get_ref_display (app); + if (!flatpak_transaction_add_uninstall (transaction, ref, error)) { + gs_flatpak_error_convert (error); +@@ -899,6 +904,10 @@ gs_plugin_flatpak_update (GsPlugin *plug + gs_flatpak_error_convert (error); + return FALSE; + } ++ ++ /* add to the transaction cache for quick look up -- other unrelated ++ * refs will be matched using gs_plugin_flatpak_find_app_by_ref() */ ++ gs_flatpak_transaction_add_app (transaction, app); + } + + /* run transaction */ diff --git a/SOURCES/gs-updates-page-keep-showing-installing-apps.patch b/SOURCES/gs-updates-page-keep-showing-installing-apps.patch new file mode 100644 index 0000000..c077eeb --- /dev/null +++ b/SOURCES/gs-updates-page-keep-showing-installing-apps.patch @@ -0,0 +1,97 @@ +diff -up gnome-software-3.36.1/lib/gs-plugin.c.1888404 gnome-software-3.36.1/lib/gs-plugin.c +--- gnome-software-3.36.1/lib/gs-plugin.c.1888404 2021-05-24 13:50:34.302612057 +0200 ++++ gnome-software-3.36.1/lib/gs-plugin.c 2021-05-24 13:50:39.160609728 +0200 +@@ -1398,6 +1398,44 @@ gs_plugin_cache_lookup (GsPlugin *plugin + } + + /** ++ * gs_plugin_cache_lookup_by_state: ++ * @plugin: a #GsPlugin ++ * @list: a #GsAppList to add applications to ++ * @state: a #AsAppState ++ * ++ * Adds each cached #GsApp with state @state into the @list. ++ * When the state is %AS_APP_STATE_UNKNOWN, then adds all ++ * cached applications. ++ * ++ * Since: 3.36.1-8 ++ **/ ++void ++gs_plugin_cache_lookup_by_state (GsPlugin *plugin, ++ GsAppList *list, ++ AsAppState state) ++{ ++ GsPluginPrivate *priv; ++ GHashTableIter iter; ++ gpointer value; ++ g_autoptr(GMutexLocker) locker = NULL; ++ ++ g_return_if_fail (GS_IS_PLUGIN (plugin)); ++ g_return_if_fail (GS_IS_APP_LIST (list)); ++ ++ priv = gs_plugin_get_instance_private (plugin); ++ locker = g_mutex_locker_new (&priv->cache_mutex); ++ ++ g_hash_table_iter_init (&iter, priv->cache); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) { ++ GsApp *app = value; ++ ++ if (state == AS_APP_STATE_UNKNOWN || ++ state == gs_app_get_state (app)) ++ gs_app_list_add (list, app); ++ } ++} ++ ++/** + * gs_plugin_cache_remove: + * @plugin: a #GsPlugin + * @key: a key which matches +diff -up gnome-software-3.36.1/lib/gs-plugin.h.1888404 gnome-software-3.36.1/lib/gs-plugin.h +--- gnome-software-3.36.1/lib/gs-plugin.h.1888404 2021-05-24 13:50:34.302612057 +0200 ++++ gnome-software-3.36.1/lib/gs-plugin.h 2021-05-24 13:50:39.160609728 +0200 +@@ -102,6 +102,9 @@ gboolean gs_plugin_check_distro_id (Gs + const gchar *distro_id); + GsApp *gs_plugin_cache_lookup (GsPlugin *plugin, + const gchar *key); ++void gs_plugin_cache_lookup_by_state (GsPlugin *plugin, ++ GsAppList *list, ++ AsAppState state); + void gs_plugin_cache_add (GsPlugin *plugin, + const gchar *key, + GsApp *app); +diff -up gnome-software-3.36.1/lib/gs-plugin-loader.c.1888404 gnome-software-3.36.1/lib/gs-plugin-loader.c +--- gnome-software-3.36.1/lib/gs-plugin-loader.c.1888404 2021-05-24 13:50:34.302612057 +0200 ++++ gnome-software-3.36.1/lib/gs-plugin-loader.c 2021-05-24 13:50:39.159609728 +0200 +@@ -1273,7 +1273,7 @@ static gboolean + gs_plugin_loader_app_is_valid_updatable (GsApp *app, gpointer user_data) + { + return gs_plugin_loader_app_is_valid (app, user_data) && +- gs_app_is_updatable (app); ++ (gs_app_is_updatable (app) || gs_app_get_state (app) == AS_APP_STATE_INSTALLING); + } + + static gboolean +diff -up gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c.1888404 gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c +--- gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c.1888404 2021-05-24 13:50:34.310612054 +0200 ++++ gnome-software-3.36.1/plugins/flatpak/gs-plugin-flatpak.c 2021-05-24 13:50:39.161609727 +0200 +@@ -218,6 +218,7 @@ gs_plugin_add_updates (GsPlugin *plugin, + if (!gs_flatpak_add_updates (flatpak, list, cancellable, error)) + return FALSE; + } ++ gs_plugin_cache_lookup_by_state (plugin, list, AS_APP_STATE_INSTALLING); + return TRUE; + } + +diff -up gnome-software-3.36.1/src/gs-updates-page.c.1888404 gnome-software-3.36.1/src/gs-updates-page.c +--- gnome-software-3.36.1/src/gs-updates-page.c.1888404 2021-05-24 13:50:39.161609727 +0200 ++++ gnome-software-3.36.1/src/gs-updates-page.c 2021-05-24 13:51:00.211599638 +0200 +@@ -139,7 +139,8 @@ gs_updates_page_invalidate (GsUpdatesPag + static GsUpdatesSectionKind + _get_app_section (GsApp *app) + { +- if (gs_app_get_state (app) == AS_APP_STATE_UPDATABLE_LIVE) { ++ if (gs_app_get_state (app) == AS_APP_STATE_UPDATABLE_LIVE || ++ gs_app_get_state (app) == AS_APP_STATE_INSTALLING) { + if (gs_app_get_kind (app) == AS_APP_KIND_FIRMWARE) + return GS_UPDATES_SECTION_KIND_ONLINE_FIRMWARE; + return GS_UPDATES_SECTION_KIND_ONLINE; diff --git a/SPECS/gnome-software.spec b/SPECS/gnome-software.spec new file mode 100644 index 0000000..6f4af30 --- /dev/null +++ b/SPECS/gnome-software.spec @@ -0,0 +1,790 @@ +%global glib2_version 2.56.0 +%global gtk3_version 3.22.4 +%global json_glib_version 1.2.0 +%global packagekit_version 1.1.1 +%global appstream_glib_version 0.7.14-3 +%global libsoup_version 2.52.0 +%global gsettings_desktop_schemas_version 3.12.0 +%global gnome_desktop_version 3.18.0 +%global fwupd_version 1.0.7 +%global flatpak_version 0.9.4 +%global libxmlb_version 0.1.7 + +%global fwupd_arches aarch64 ppc64le s390x x86_64 + +Name: gnome-software +Version: 3.36.1 +Release: 11%{?dist} +Summary: A software center for GNOME + +License: GPLv2+ +URL: https://wiki.gnome.org/Apps/Software +Source0: https://download.gnome.org/sources/gnome-software/3.36/%{name}-%{version}.tar.xz + +# Add support for basic auth and webflow auth in flatpak plugin +# https://gitlab.gnome.org/GNOME/gnome-software/-/merge_requests/467 +Patch0: 0001-Add-basic-auth-support-to-flatpak-plugin.patch +Patch1: 0002-Add-webflow-auth-support-to-flatpak-plugin.patch +# Add back shell extensions support as we don't have the extensions app in RHEL 8.3 +# https://bugzilla.redhat.com/show_bug.cgi?id=1839774 +Patch2: add-back-shell-extensions-support.patch +# Fix hardcoded desktop and appdata names to match what's in RHEL 8.3 +Patch3: 0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch +# Fix 'Show Details' to correctly work for rpm-installed firefox +Patch4: 0001-Improve-the-heuristic-for-detecting-old-style-AppStr.patch +# Fix flatpak updates and removals when same ref occurs in multiple remotes +Patch5: gnome-software-3.36.1-unrelated-refs.patch + +Patch6: be-able-to-disable-odrs.patch +Patch7: crash-when-run-as-root.patch +Patch8: gs-updates-page-keep-showing-installing-apps.patch +Patch9: flatpak-same-runtime-origin.patch +Patch10: devel-install-headers.patch +Patch11: 0003-hide-some-errors.patch + +BuildRequires: gcc +BuildRequires: gettext +BuildRequires: libxslt +BuildRequires: docbook-style-xsl +BuildRequires: desktop-file-utils +%ifarch %{fwupd_arches} +BuildRequires: fwupd-devel >= %{fwupd_version} +%endif +BuildRequires: glib2-devel >= %{glib2_version} +BuildRequires: gnome-desktop3-devel +BuildRequires: gnome-online-accounts-devel +BuildRequires: gsettings-desktop-schemas-devel >= %{gsettings_desktop_schemas_version} +BuildRequires: gspell-devel +BuildRequires: gtk3-devel >= %{gtk3_version} +BuildRequires: gtk-doc +BuildRequires: json-glib-devel >= %{json_glib_version} +BuildRequires: libappstream-glib-devel >= %{appstream_glib_version} +BuildRequires: libsoup-devel +BuildRequires: libxmlb-devel >= %{libxmlb_version} +BuildRequires: meson +BuildRequires: PackageKit-glib-devel >= %{packagekit_version} +BuildRequires: polkit-devel +BuildRequires: flatpak-devel >= %{flatpak_version} +%if 0%{?fedora} +BuildRequires: libdnf-devel +BuildRequires: ostree-devel +BuildRequires: rpm-devel +BuildRequires: rpm-ostree-devel +%endif +BuildRequires: libgudev1-devel +%ifarch %{valgrind_arches} +BuildRequires: valgrind-devel +%endif + +Requires: appstream-data +Requires: flatpak%{?_isa} >= %{flatpak_version} +Requires: flatpak-libs%{?_isa} >= %{flatpak_version} +%ifarch %{fwupd_arches} +Requires: fwupd%{?_isa} >= %{fwupd_version} +%endif +Requires: glib2%{?_isa} >= %{glib2_version} +Requires: gnome-desktop3%{?_isa} >= %{gnome_desktop_version} +# gnome-menus is needed for app folder .directory entries +Requires: gnome-menus%{?_isa} +Requires: gsettings-desktop-schemas%{?_isa} >= %{gsettings_desktop_schemas_version} +Requires: gtk3%{?_isa} >= %{gtk3_version} +Requires: json-glib%{?_isa} >= %{json_glib_version} +Requires: iso-codes +Requires: libappstream-glib%{?_isa} >= %{appstream_glib_version} +# librsvg2 is needed for gdk-pixbuf svg loader +Requires: librsvg2%{?_isa} +Requires: libsoup%{?_isa} >= %{libsoup_version} +Requires: PackageKit%{?_isa} >= %{packagekit_version} +Requires: libxmlb%{?_isa} >= %{libxmlb_version} + +Obsoletes: gnome-software-editor < 3.35.1 + +# this is not a library version +%define gs_plugin_version 13 + +%description +gnome-software is an application that makes it easy to add, remove +and update software in the GNOME desktop. + +%package devel +Summary: Headers for building external gnome-software plugins +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +These development files are for building gnome-software plugins outside +the source tree. Most users do not need this subpackage installed. + +%prep +%autosetup -p1 -S gendiff + +%build +%meson \ + -Dsnap=false \ +%ifnarch %{valgrind_arches} + -Dvalgrind=false \ +%endif +%ifnarch %{fwupd_arches} + -Dfwupd=false \ +%endif + -Dgudev=true \ + -Dpackagekit=true \ + -Dexternal_appstream=false \ + -Dmalcontent=false \ + -Drpm_ostree=false \ + -Dtests=false +%meson_build + +%install +%meson_install + +# remove unneeded dpkg plugin +rm %{buildroot}%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_dpkg.so + +# make the software center load faster +desktop-file-edit %{buildroot}%{_datadir}/applications/org.gnome.Software.desktop \ + --set-key=X-AppInstall-Package --set-value=%{name} + +# set up for Fedora +cat >> %{buildroot}%{_datadir}/glib-2.0/schemas/org.gnome.software-fedora.gschema.override << FOE +[org.gnome.software] +official-repos = [ 'rhel-7' ] +FOE + +%find_lang %name --with-gnome + +%check +desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop + +%files -f %{name}.lang +%doc AUTHORS README.md +%license COPYING +%{_bindir}/gnome-software +%{_datadir}/applications/gnome-software-local-file.desktop +%{_datadir}/applications/org.gnome.Software.desktop +%dir %{_datadir}/gnome-software +%{_datadir}/gnome-software/*.png +%{_mandir}/man1/gnome-software.1.gz +%{_datadir}/icons/hicolor/*/apps/org.gnome.Software.svg +%{_datadir}/icons/hicolor/symbolic/apps/org.gnome.Software-symbolic.svg +%{_datadir}/icons/hicolor/scalable/status/software-installed-symbolic.svg +%{_datadir}/gnome-software/featured-*.svg +%{_datadir}/gnome-software/featured-*.jpg +%{_datadir}/metainfo/org.gnome.Software.appdata.xml +%{_datadir}/metainfo/org.gnome.Software.Plugin.Flatpak.metainfo.xml +%ifarch %{fwupd_arches} +%{_datadir}/metainfo/org.gnome.Software.Plugin.Fwupd.metainfo.xml +%endif +%{_datadir}/metainfo/org.gnome.Software.Plugin.Odrs.metainfo.xml +%dir %{_libdir}/gs-plugins-%{gs_plugin_version} +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_appstream.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_desktop-categories.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_desktop-menu-path.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_dummy.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_fedora-langpacks.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_fedora-pkgdb-collections.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_flatpak.so +%ifarch %{fwupd_arches} +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_fwupd.so +%endif +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_generic-updates.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-blacklist.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-popular.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_icons.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_key-colors-metadata.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_key-colors.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_modalias.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_odrs.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_os-release.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-history.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-local.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-offline.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-proxy.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-refine-repos.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-refine.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-refresh.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-upgrade.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit-url-to-app.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_packagekit.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_provenance-license.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_provenance.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_repos.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_rewrite-resource.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_shell-extensions.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_systemd-updates.so +%{_sysconfdir}/xdg/autostart/gnome-software-service.desktop +%{_datadir}/app-info/xmls/org.gnome.Software.Featured.xml +%{_datadir}/dbus-1/services/org.freedesktop.PackageKit.service +%{_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 + +%files devel +%{_libdir}/pkgconfig/gnome-software.pc +%dir %{_includedir}/gnome-software +%{_includedir}/gnome-software/*.h +%{_datadir}/gtk-doc/html/gnome-software + +%changelog +* Thu Sep 22 2022 Milan Crha - 3.36.1-11 +- Resolves: #2124772 (Hide some errors in non-debug builds) + +* Thu Jul 08 2021 Milan Crha - 3.36.1-10 +- Resolves: #1978505 (Development package is missing important header files) + +* Mon Jun 21 2021 Milan Crha - 3.36.1-9 +- Resolves: #1972545 (flatpak: Prefer runtime from the same origin as the application) + +* Mon May 24 2021 Milan Crha - 3.36.1-8 +- Resolves: #1888404 (Updates page hides ongoing updates on refresh) + +* Mon May 24 2021 Milan Crha - 3.36.1-7 +- Resolves: #1873297 (Crash when run as root) + +* Mon May 24 2021 Milan Crha - 3.36.1-6 +- Resolves: #1791478 (Cannot completely disable ODRS (GNOME Ratings)) + +* Wed Feb 17 2021 Milan Crha - 3.36.1-5 +- Fix flatpak updates and removals when same ref occurs in multiple remotes +- Resolves: #1888407 + +* Thu Jun 11 2020 Kalev Lember - 3.36.1-4 +- Fix 'Show Details' to correctly work for rpm-installed firefox +- Resolves: #1845714 + +* Wed Jun 03 2020 Kalev Lember - 3.36.1-3 +- Upload correct 3.36.1 tarball +- Fix hardcoded desktop and appdata names to match what's in RHEL 8.3 +- Add back shell extensions support +- Resolves: #1839774 + +* Tue Jun 02 2020 Kalev Lember - 3.36.1-2 +- Add support for basic auth and webflow auth in flatpak plugin +- Resolves: #1815502 + +* Fri May 22 2020 Richard Hughes - 3.36.1-1 +- Update to 3.36.1 +- Resolves: #1797932 + +* Wed Jan 29 2020 Kalev Lember - 3.30.6-3 +- Fix issues with installing Cockpit +- Resolves: #1759913 + +* Fri Jul 12 2019 Kalev Lember - 3.30.6-2 +- Hide addons that are not available in repos +- Resolves: #1719779 + +* Tue Dec 18 2018 Kalev Lember - 3.30.6-1 +- Update to 3.30.6 + +* Fri Aug 3 2018 Florian Weimer - 3.28.2-3 +- Honor %%{valgrind_arches} + +* Wed Jul 18 2018 Richard Hughes - 3.28.2-2 +- Do not build the snapd plugin on RHEL. + +* Thu Jul 12 2018 Richard Hughes - 3.28.2-1 +- Update to 3.28.2 + +* 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 +