/* -*- 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 * * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" #include #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; }