summaryrefslogtreecommitdiffstats
path: root/plugins/packagekit/gs-packagekit-task.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/packagekit/gs-packagekit-task.c')
-rw-r--r--plugins/packagekit/gs-packagekit-task.c280
1 files changed, 280 insertions, 0 deletions
diff --git a/plugins/packagekit/gs-packagekit-task.c b/plugins/packagekit/gs-packagekit-task.c
new file mode 100644
index 0000000..7727ce3
--- /dev/null
+++ b/plugins/packagekit/gs-packagekit-task.c
@@ -0,0 +1,280 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Red Hat <www.redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "gs-packagekit-task.h"
+
+/**
+ * SECTION:gs-packagekit-task
+ * @short_description: PkTask subclass which implements vfuncs for user interaction during a task
+ *
+ * #GsPackagekitTask is a subclass of #PkTask which represents a single
+ * operation on PackageKit.
+ *
+ * By subclassing #PkTask, it can implement vfuncs which allow decisions
+ * to be made about the task while it’s running. For example, to decide
+ * what to do if an untrusted package needs to be installed.
+ *
+ * Since: 42
+ */
+
+typedef struct {
+ GWeakRef plugin_weakref; /* GsPlugin * */
+ GsPluginAction action;
+ GsPackagekitHelper *helper;
+
+} GsPackagekitTaskPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsPackagekitTask, gs_packagekit_task, PK_TYPE_TASK)
+
+static gboolean
+gs_packagekit_task_user_accepted (PkTask *task,
+ const gchar *title,
+ const gchar *msg,
+ const gchar *details,
+ const gchar *accept_label)
+{
+ GsPackagekitTask *gs_task = GS_PACKAGEKIT_TASK (task);
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (gs_task);
+ g_autoptr(GsPlugin) plugin = NULL;
+ gboolean accepts = FALSE;
+
+ plugin = g_weak_ref_get (&priv->plugin_weakref);
+ if (plugin)
+ accepts = gs_plugin_ask_untrusted (plugin, title, msg, details, accept_label);
+
+ return accepts;
+}
+
+typedef struct {
+ GWeakRef task_weakref;
+ guint request;
+ gchar *title;
+ gchar *msg;
+ gchar *details;
+ gchar *accept_label;
+} QuestionData;
+
+static QuestionData *
+question_data_new (GsPackagekitTask *task,
+ guint request,
+ const gchar *title,
+ const gchar *msg,
+ const gchar *details,
+ const gchar *accept_label)
+{
+ QuestionData *qd;
+
+ qd = g_slice_new0 (QuestionData);
+ g_weak_ref_init (&qd->task_weakref, task);
+ qd->request = request;
+ qd->title = g_strdup (title);
+ qd->msg = g_strdup (msg);
+ qd->details = g_strdup (details);
+ qd->accept_label = g_strdup (accept_label);
+
+ return qd;
+}
+
+static void
+question_data_free (gpointer ptr)
+{
+ QuestionData *qd = ptr;
+ g_weak_ref_clear (&qd->task_weakref);
+ g_free (qd->title);
+ g_free (qd->msg);
+ g_free (qd->details);
+ g_free (qd->accept_label);
+ g_slice_free (QuestionData, qd);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (QuestionData, question_data_free)
+
+static gboolean
+gs_packagekit_task_question_idle_cb (gpointer user_data)
+{
+ QuestionData *qd = user_data;
+ g_autoptr(PkTask) task = NULL;
+
+ task = g_weak_ref_get (&qd->task_weakref);
+ if (task) {
+ if (gs_packagekit_task_user_accepted (task, qd->title, qd->msg, qd->details, qd->accept_label))
+ pk_task_user_accepted (task, qd->request);
+ else
+ pk_task_user_declined (task, qd->request);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+gs_packagekit_task_schedule_question (GsPackagekitTask *task,
+ guint request,
+ const gchar *title,
+ const gchar *msg,
+ const gchar *details,
+ const gchar *accept_label)
+{
+ g_autoptr(QuestionData) qd = NULL;
+
+ qd = question_data_new (task, request, title, msg, details, accept_label);
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_packagekit_task_question_idle_cb, g_steal_pointer (&qd), question_data_free);
+}
+
+/* This may be called in a PackageKit worker thread. */
+static void
+gs_packagekit_task_untrusted_question (PkTask *task,
+ guint request,
+ PkResults *results)
+{
+ GsPackagekitTask *gs_task = GS_PACKAGEKIT_TASK (task);
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (gs_task);
+ g_autoptr(PkError) error = NULL;
+ const gchar *title;
+ const gchar *msg;
+ const gchar *details;
+ const gchar *accept_label;
+
+ switch (priv->action) {
+ case GS_PLUGIN_ACTION_INSTALL:
+ title = _("Install Unsigned Software?");
+ msg = _("Software that is to be installed is not signed. It will not be possible to verify the origin of updates to this software, or whether updates have been tampered with.");
+ accept_label = _("_Install");
+ break;
+ case GS_PLUGIN_ACTION_DOWNLOAD:
+ title = _("Download Unsigned Software?");
+ msg = _("Unsigned updates are available. Without a signature, it is not possible to verify the origin of the update, or whether it has been tampered with.");
+ accept_label = _("_Download");
+ break;
+ case GS_PLUGIN_ACTION_UPDATE:
+ title = _("Update Unsigned Software?");
+ msg = _("Unsigned updates are available. Without a signature, it is not possible to verify the origin of the update, or whether it has been tampered with. Software updates will be disabled until unsigned updates are either removed or updated.");
+ accept_label = _("_Update");
+ break;
+ default:
+ pk_task_user_declined (task, request);
+ return;
+ }
+
+ error = pk_results_get_error_code (results);
+ if (error)
+ details = pk_error_get_details (error);
+ else
+ details = NULL;
+
+ gs_packagekit_task_schedule_question (gs_task, request, title, msg, details, accept_label);
+}
+
+static void
+gs_packagekit_task_finalize (GObject *object)
+{
+ GsPackagekitTask *task = GS_PACKAGEKIT_TASK (object);
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (task);
+
+ g_weak_ref_clear (&priv->plugin_weakref);
+ g_clear_object (&priv->helper);
+
+ G_OBJECT_CLASS (gs_packagekit_task_parent_class)->finalize (object);
+}
+
+static void
+gs_packagekit_task_class_init (GsPackagekitTaskClass *klass)
+{
+ GObjectClass *object_class;
+ PkTaskClass *task_class;
+
+ task_class = PK_TASK_CLASS (klass);
+ task_class->untrusted_question = gs_packagekit_task_untrusted_question;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gs_packagekit_task_finalize;
+}
+
+static void
+gs_packagekit_task_init (GsPackagekitTask *task)
+{
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (task);
+
+ g_weak_ref_init (&priv->plugin_weakref, NULL);
+}
+
+PkTask *
+gs_packagekit_task_new (GsPlugin *plugin)
+{
+ GsPackagekitTask *task;
+ GsPackagekitTaskPrivate *priv;
+
+ g_return_val_if_fail (GS_IS_PLUGIN (plugin), NULL);
+
+ task = g_object_new (GS_TYPE_PACKAGEKIT_TASK, NULL);
+ priv = gs_packagekit_task_get_instance_private (task);
+
+ g_weak_ref_set (&priv->plugin_weakref, plugin);
+
+ return PK_TASK (task);
+}
+
+void
+gs_packagekit_task_setup (GsPackagekitTask *task,
+ GsPluginAction action,
+ gboolean interactive)
+{
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (task);
+
+ g_return_if_fail (GS_IS_PACKAGEKIT_TASK (task));
+
+ priv->action = action;
+
+ /* The :interactive and :background properties have slightly different
+ * purposes:
+ * - :interactive controls whether the task can create interactive
+ * authentication (polkit) prompts
+ * - :background controls the scheduling of the task relative to other
+ * PackageKit tasks from this client and other clients
+ * However, we always want to set them both based on the same
+ * conditions. */
+ pk_client_set_interactive (PK_CLIENT (task), interactive);
+ pk_client_set_background (PK_CLIENT (task), !interactive);
+}
+
+GsPluginAction
+gs_packagekit_task_get_action (GsPackagekitTask *task)
+{
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (task);
+
+ g_return_val_if_fail (GS_IS_PACKAGEKIT_TASK (task), GS_PLUGIN_ACTION_UNKNOWN);
+
+ return priv->action;
+}
+
+void
+gs_packagekit_task_take_helper (GsPackagekitTask *task,
+ GsPackagekitHelper *helper)
+{
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (task);
+
+ g_return_if_fail (GS_IS_PACKAGEKIT_TASK (task));
+
+ if (priv->helper != helper) {
+ g_clear_object (&priv->helper);
+ priv->helper = helper;
+ }
+}
+
+GsPackagekitHelper *
+gs_packagekit_task_get_helper (GsPackagekitTask *task)
+{
+ GsPackagekitTaskPrivate *priv = gs_packagekit_task_get_instance_private (task);
+
+ g_return_val_if_fail (GS_IS_PACKAGEKIT_TASK (task), NULL);
+
+ return priv->helper;
+}