summaryrefslogtreecommitdiffstats
path: root/plugins/core/gs-plugin-provenance.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/core/gs-plugin-provenance.c')
-rw-r--r--plugins/core/gs-plugin-provenance.c292
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;
+}