diff options
Diffstat (limited to 'plugins/core/gs-plugin-provenance.c')
-rw-r--r-- | plugins/core/gs-plugin-provenance.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/plugins/core/gs-plugin-provenance.c b/plugins/core/gs-plugin-provenance.c new file mode 100644 index 0000000..95e940b --- /dev/null +++ b/plugins/core/gs-plugin-provenance.c @@ -0,0 +1,292 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com> + * Copyright (C) 2018 Kalev Lember <klember@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> + +#include <gnome-software.h> + +#include "gs-plugin-provenance.h" + +/* + * SECTION: + * Sets the package provenance to TRUE if installed by an official + * software source. Also sets compulsory quirk when a required repository. + * + * This plugin executes entirely in the main thread. + */ + +struct _GsPluginProvenance { + GsPlugin parent; + + GSettings *settings; + GHashTable *repos; /* gchar *name ~> guint flags */ + GPtrArray *provenance_wildcards; /* non-NULL, when have names with wildcards */ + GPtrArray *compulsory_wildcards; /* non-NULL, when have names with wildcards */ +}; + +G_DEFINE_TYPE (GsPluginProvenance, gs_plugin_provenance, GS_TYPE_PLUGIN) + +static GHashTable * +gs_plugin_provenance_remove_by_flag (GHashTable *old_repos, + GsAppQuirk quirk) +{ + GHashTable *new_repos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init (&iter, old_repos); + while (g_hash_table_iter_next (&iter, &key, &value)) { + guint flags = GPOINTER_TO_UINT (value); + flags = flags & (~quirk); + if (flags != 0) + g_hash_table_insert (new_repos, g_strdup (key), GUINT_TO_POINTER (flags)); + } + return new_repos; +} + +static void +gs_plugin_provenance_add_quirks (GsApp *app, + guint quirks) +{ + if ((quirks & GS_APP_QUIRK_PROVENANCE) != 0) + gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE); + if ((quirks & GS_APP_QUIRK_COMPULSORY) != 0 && + gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) + gs_app_add_quirk (app, GS_APP_QUIRK_COMPULSORY); +} + +static gchar ** +gs_plugin_provenance_get_sources (GsPluginProvenance *self, + const gchar *key) +{ + const gchar *tmp; + tmp = g_getenv ("GS_SELF_TEST_PROVENANCE_SOURCES"); + if (tmp != NULL) { + if (g_strcmp0 (key, "required-repos") == 0) + return NULL; + g_debug ("using custom provenance sources of %s", tmp); + return g_strsplit (tmp, ",", -1); + } + return g_settings_get_strv (self->settings, key); +} + +static void +gs_plugin_provenance_settings_changed_cb (GSettings *settings, + const gchar *key, + gpointer user_data) +{ + GsPluginProvenance *self = GS_PLUGIN_PROVENANCE (user_data); + GsAppQuirk quirk = GS_APP_QUIRK_NONE; + GPtrArray **pwildcards = NULL; + + if (g_strcmp0 (key, "official-repos") == 0) { + quirk = GS_APP_QUIRK_PROVENANCE; + pwildcards = &self->provenance_wildcards; + } else if (g_strcmp0 (key, "required-repos") == 0) { + quirk = GS_APP_QUIRK_COMPULSORY; + pwildcards = &self->compulsory_wildcards; + } + + if (quirk != GS_APP_QUIRK_NONE) { + /* The keys are stolen by the hash table, thus free only the array */ + g_autofree gchar **repos = NULL; + g_autoptr(GHashTable) old_repos = self->repos; + g_autoptr(GPtrArray) old_wildcards = *pwildcards; + GHashTable *new_repos = gs_plugin_provenance_remove_by_flag (old_repos, quirk); + GPtrArray *new_wildcards = NULL; + repos = gs_plugin_provenance_get_sources (self, key); + for (guint ii = 0; repos && repos[ii]; ii++) { + gchar *repo = g_steal_pointer (&(repos[ii])); + if (strchr (repo, '*') || + strchr (repo, '?') || + strchr (repo, '[')) { + if (new_wildcards == NULL) + new_wildcards = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (new_wildcards, repo); + } else { + g_hash_table_insert (new_repos, repo, + GUINT_TO_POINTER (quirk | + GPOINTER_TO_UINT (g_hash_table_lookup (new_repos, repo)))); + } + } + if (new_wildcards != NULL) + g_ptr_array_add (new_wildcards, NULL); + self->repos = new_repos; + *pwildcards = new_wildcards; + } +} + +static void +gs_plugin_provenance_init (GsPluginProvenance *self) +{ + self->settings = g_settings_new ("org.gnome.software"); + self->repos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + g_signal_connect (self->settings, "changed", + G_CALLBACK (gs_plugin_provenance_settings_changed_cb), self); + gs_plugin_provenance_settings_changed_cb (self->settings, "official-repos", self); + gs_plugin_provenance_settings_changed_cb (self->settings, "required-repos", self); + + /* after the package source is set */ + gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_RUN_AFTER, "dummy"); + gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_RUN_AFTER, "packagekit"); + gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_RUN_AFTER, "rpm-ostree"); +} + +static void +gs_plugin_provenance_dispose (GObject *object) +{ + GsPluginProvenance *self = GS_PLUGIN_PROVENANCE (object); + + g_clear_pointer (&self->repos, g_hash_table_unref); + g_clear_pointer (&self->provenance_wildcards, g_ptr_array_unref); + g_clear_pointer (&self->compulsory_wildcards, g_ptr_array_unref); + g_clear_object (&self->settings); + + G_OBJECT_CLASS (gs_plugin_provenance_parent_class)->dispose (object); +} + +static gboolean +gs_plugin_provenance_find_repo_flags (GHashTable *repos, + GPtrArray *provenance_wildcards, + GPtrArray *compulsory_wildcards, + const gchar *repo, + guint *out_flags) +{ + if (repo == NULL || *repo == '\0') + return FALSE; + *out_flags = GPOINTER_TO_UINT (g_hash_table_lookup (repos, repo)); + if (provenance_wildcards != NULL && + gs_utils_strv_fnmatch ((gchar **) provenance_wildcards->pdata, repo)) + *out_flags |= GS_APP_QUIRK_PROVENANCE; + if (compulsory_wildcards != NULL && + gs_utils_strv_fnmatch ((gchar **) compulsory_wildcards->pdata, repo)) + *out_flags |= GS_APP_QUIRK_COMPULSORY; + return *out_flags != 0; +} + +static gboolean +refine_app (GsPlugin *plugin, + GsApp *app, + GsPluginRefineFlags flags, + GHashTable *repos, + GPtrArray *provenance_wildcards, + GPtrArray *compulsory_wildcards, + GCancellable *cancellable, + GError **error) +{ + const gchar *origin; + guint quirks; + + /* not required */ + if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0) + return TRUE; + if (gs_app_has_quirk (app, GS_APP_QUIRK_PROVENANCE)) + return TRUE; + + /* Software sources/repositories are represented as #GsApps too. Add the + * provenance quirk to the system-configured repositories (but not + * user-configured ones). */ + if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) { + if (gs_plugin_provenance_find_repo_flags (repos, provenance_wildcards, compulsory_wildcards, gs_app_get_id (app), &quirks) && + gs_app_get_scope (app) != AS_COMPONENT_SCOPE_USER) + gs_plugin_provenance_add_quirks (app, quirks); + return TRUE; + } + + /* simple case */ + origin = gs_app_get_origin (app); + if (gs_plugin_provenance_find_repo_flags (repos, provenance_wildcards, compulsory_wildcards, origin, &quirks)) { + gs_plugin_provenance_add_quirks (app, quirks); + return TRUE; + } + + /* this only works for packages */ + origin = gs_app_get_source_id_default (app); + if (origin == NULL) + return TRUE; + origin = g_strrstr (origin, ";"); + if (origin == NULL) + return TRUE; + if (g_str_has_prefix (origin + 1, "installed:")) + origin += 10; + if (gs_plugin_provenance_find_repo_flags (repos, provenance_wildcards, compulsory_wildcards, origin + 1, &quirks)) + gs_plugin_provenance_add_quirks (app, quirks); + + return TRUE; +} + +static void +gs_plugin_provenance_refine_async (GsPlugin *plugin, + GsAppList *list, + GsPluginRefineFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GsPluginProvenance *self = GS_PLUGIN_PROVENANCE (plugin); + g_autoptr(GTask) task = NULL; + g_autoptr(GError) local_error = NULL; + g_autoptr(GHashTable) repos = NULL; + g_autoptr(GPtrArray) provenance_wildcards = NULL; + g_autoptr(GPtrArray) compulsory_wildcards = NULL; + + task = g_task_new (plugin, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_provenance_refine_async); + + /* nothing to do here */ + if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0) { + g_task_return_boolean (task, TRUE); + return; + } + + repos = g_hash_table_ref (self->repos); + provenance_wildcards = self->provenance_wildcards != NULL ? g_ptr_array_ref (self->provenance_wildcards) : NULL; + compulsory_wildcards = self->compulsory_wildcards != NULL ? g_ptr_array_ref (self->compulsory_wildcards) : NULL; + + /* nothing to search */ + if (g_hash_table_size (repos) == 0 && provenance_wildcards == NULL && compulsory_wildcards == NULL) { + g_task_return_boolean (task, TRUE); + return; + } + + for (guint i = 0; i < gs_app_list_length (list); i++) { + GsApp *app = gs_app_list_index (list, i); + if (!refine_app (plugin, app, flags, repos, provenance_wildcards, compulsory_wildcards, cancellable, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + return; + } + } + + g_task_return_boolean (task, TRUE); +} + +static gboolean +gs_plugin_provenance_refine_finish (GsPlugin *plugin, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +gs_plugin_provenance_class_init (GsPluginProvenanceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass); + + object_class->dispose = gs_plugin_provenance_dispose; + + plugin_class->refine_async = gs_plugin_provenance_refine_async; + plugin_class->refine_finish = gs_plugin_provenance_refine_finish; +} + +GType +gs_plugin_query_type (void) +{ + return GS_TYPE_PLUGIN_PROVENANCE; +} |