#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <syslog.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <packagekit-glib2/pk-common.h>
#include <packagekit-glib2/pk-common-private.h>
#include <packagekit-glib2/pk-enum.h>
#include <packagekit-glib2/pk-offline-private.h>
#include <packagekit-glib2/pk-package-id.h>
#include <packagekit-glib2/pk-package-ids.h>
#include <packagekit-glib2/pk-results.h>
#include <polkit/polkit.h>
#include "pk-backend.h"
#include "pk-dbus.h"
#include "pk-shared.h"
#include "pk-transaction-db.h"
#include "pk-transaction.h"
#include "pk-transaction-private.h"
#ifndef HAVE_POLKIT_0_114
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitDetails, g_object_unref)
#endif
static void pk_transaction_finalize (GObject *object);
static void pk_transaction_dispose (GObject *object);
static gchar *pk_transaction_get_content_type_for_file (const gchar *filename, GError **error);
static gboolean pk_transaction_is_supported_content_type (PkTransaction *transaction, const gchar *content_type);
#define PK_TRANSACTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_TRANSACTION, PkTransactionPrivate))
#define PK_TRANSACTION_UPDATES_CHANGED_TIMEOUT 100
#define PK_TRANSACTION_UID_INVALID G_MAXUINT32
#define PK_TRANSACTION_PID_INVALID G_MAXUINT32
#define PK_TRANSACTION_MAX_ITEMS_TO_RESOLVE 10000
struct PkTransactionPrivate
{
PkRoleEnum role;
PkStatusEnum status;
PkTransactionState state;
guint percentage;
guint elapsed_time;
guint remaining_time;
guint speed;
guint download_size_remaining;
gboolean finished;
gboolean emitted_finished;
gboolean allow_cancel;
gboolean waiting_for_auth;
gboolean emit_eula_required;
gboolean emit_signature_required;
gboolean emit_media_change_required;
gboolean caller_active;
gboolean exclusive;
guint32 client_uid;
guint32 client_pid;
guint watch_id;
PkBackend *backend;
PkBackendJob *job;
GKeyFile *conf;
PkDbus *dbus;
PolkitAuthority *authority;
PolkitSubject *subject;
GCancellable *cancellable;
gboolean skip_auth_check;
gboolean client_supports_plural_signals;
gboolean progress_changed;
GSource *progress_timeout_source;
gchar *last_package_id;
gchar *tid;
gchar *sender;
gchar *cmdline;
PkResults *results;
PkTransactionDb *transaction_db;
gboolean cached_force;
gboolean cached_allow_deps;
gboolean cached_autoremove;
gboolean cached_enabled;
PkBitfield cached_transaction_flags;
gchar *cached_package_id;
gchar **cached_package_ids;
gchar *cached_transaction_id;
gchar **cached_full_paths;
PkBitfield cached_filters;
gchar **cached_values;
gchar *cached_repo_id;
gchar *cached_key_id;
gchar *cached_parameter;
gchar *cached_value;
gchar *cached_directory;
gchar *cached_cat_id;
PkUpgradeKindEnum cached_upgrade_kind;
GPtrArray *supported_content_types;
guint registration_id;
GDBusConnection *connection;
GDBusNodeInfo *introspection;
};
typedef enum {
PK_TRANSACTION_ERROR_DENIED,
PK_TRANSACTION_ERROR_NOT_RUNNING,
PK_TRANSACTION_ERROR_NO_ROLE,
PK_TRANSACTION_ERROR_CANNOT_CANCEL,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
PK_TRANSACTION_ERROR_NO_SUCH_TRANSACTION,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
PK_TRANSACTION_ERROR_NO_SUCH_DIRECTORY,
PK_TRANSACTION_ERROR_TRANSACTION_EXISTS_WITH_ROLE,
PK_TRANSACTION_ERROR_REFUSED_BY_POLICY,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
PK_TRANSACTION_ERROR_SEARCH_PATH_INVALID,
PK_TRANSACTION_ERROR_FILTER_INVALID,
PK_TRANSACTION_ERROR_INPUT_INVALID,
PK_TRANSACTION_ERROR_INVALID_STATE,
PK_TRANSACTION_ERROR_INITIALIZE_FAILED,
PK_TRANSACTION_ERROR_COMMIT_FAILED,
PK_TRANSACTION_ERROR_INVALID_PROVIDE,
PK_TRANSACTION_ERROR_PACK_INVALID,
PK_TRANSACTION_ERROR_MIME_TYPE_NOT_SUPPORTED,
PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
PK_TRANSACTION_ERROR_LAST
} PkTransactionError;
enum {
SIGNAL_FINISHED,
SIGNAL_STATE_CHANGED,
SIGNAL_ALLOW_CANCEL_CHANGED,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = { 0 };
G_DEFINE_TYPE (PkTransaction, pk_transaction, G_TYPE_OBJECT)
GQuark
pk_transaction_error_quark (void)
{
static GQuark quark = 0;
if (!quark) {
quark = g_quark_from_static_string ("pk-transaction-error-quark");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_DENIED,
PK_DBUS_INTERFACE_TRANSACTION ".Denied");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_NOT_RUNNING,
PK_DBUS_INTERFACE_TRANSACTION ".NotRunning");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_NO_ROLE,
PK_DBUS_INTERFACE_TRANSACTION ".NoRole");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_CANNOT_CANCEL,
PK_DBUS_INTERFACE_TRANSACTION ".CannotCancel");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
PK_DBUS_INTERFACE_TRANSACTION ".NotSupported");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_NO_SUCH_TRANSACTION,
PK_DBUS_INTERFACE_TRANSACTION ".NoSuchTransaction");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
PK_DBUS_INTERFACE_TRANSACTION ".NoSuchFile");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_NO_SUCH_DIRECTORY,
PK_DBUS_INTERFACE_TRANSACTION ".NoSuchDirectory");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_TRANSACTION_EXISTS_WITH_ROLE,
PK_DBUS_INTERFACE_TRANSACTION ".TransactionExistsWithRole");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_REFUSED_BY_POLICY,
PK_DBUS_INTERFACE_TRANSACTION ".RefusedByPolicy");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
PK_DBUS_INTERFACE_TRANSACTION ".PackageIdInvalid");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
PK_DBUS_INTERFACE_TRANSACTION ".SearchInvalid");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_SEARCH_PATH_INVALID,
PK_DBUS_INTERFACE_TRANSACTION ".PathInvalid");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_FILTER_INVALID,
PK_DBUS_INTERFACE_TRANSACTION ".FilterInvalid");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_INPUT_INVALID,
PK_DBUS_INTERFACE_TRANSACTION ".InputInvalid");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_INVALID_STATE,
PK_DBUS_INTERFACE_TRANSACTION ".InvalidState");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_INITIALIZE_FAILED,
PK_DBUS_INTERFACE_TRANSACTION ".InitializeFailed");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_COMMIT_FAILED,
PK_DBUS_INTERFACE_TRANSACTION ".CommitFailed");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_INVALID_PROVIDE,
PK_DBUS_INTERFACE_TRANSACTION ".InvalidProvide");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_PACK_INVALID,
PK_DBUS_INTERFACE_TRANSACTION ".PackInvalid");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_MIME_TYPE_NOT_SUPPORTED,
PK_DBUS_INTERFACE_TRANSACTION ".MimeTypeNotSupported");
g_dbus_error_register_error (quark,
PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
PK_DBUS_INTERFACE_TRANSACTION ".NumberOfPackagesInvalid");
}
return quark;
}
static guint
pk_transaction_get_runtime (PkTransaction *transaction)
{
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), 0);
g_return_val_if_fail (transaction->priv->tid != NULL, 0);
return pk_backend_job_get_runtime (transaction->priv->job);
}
gboolean
pk_transaction_get_background (PkTransaction *transaction)
{
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
return pk_backend_job_get_background (transaction->priv->job);
}
static gboolean
pk_transaction_finish_invalidate_caches (PkTransaction *transaction)
{
PkTransactionPrivate *priv = transaction->priv;
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
if (pk_bitfield_contain (transaction->priv->cached_transaction_flags,
PK_TRANSACTION_FLAG_ENUM_SIMULATE))
goto out;
if (pk_bitfield_contain (transaction->priv->cached_transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD))
goto out;
if (priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES ||
priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
priv->role == PK_ROLE_ENUM_REMOVE_PACKAGES ||
priv->role == PK_ROLE_ENUM_REPO_ENABLE ||
priv->role == PK_ROLE_ENUM_REPO_SET_DATA ||
priv->role == PK_ROLE_ENUM_REPO_REMOVE ||
priv->role == PK_ROLE_ENUM_REFRESH_CACHE) {
pk_backend_updates_changed_delay (priv->backend,
PK_TRANSACTION_UPDATES_CHANGED_TIMEOUT);
}
out:
return TRUE;
}
static void pk_transaction_emit_properties_changed (PkTransaction *transaction,
const gchar *first_property_name,
GVariant *first_property_value,
...) G_GNUC_NULL_TERMINATED;
static void
pk_transaction_emit_properties_changed (PkTransaction *transaction,
const gchar *first_property_name,
GVariant *first_property_value,
...)
{
GVariantBuilder builder;
GVariantBuilder invalidated_builder;
va_list args;
const gchar *property_name;
GVariant *property_value;
g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
va_start (args, first_property_value);
for (property_name = first_property_name, property_value = first_property_value;
property_name != NULL;
property_name = va_arg (args, const gchar *), property_value = (property_name != NULL) ? va_arg (args, GVariant *) : NULL) {
g_variant_builder_add (&builder,
"{sv}",
property_name,
property_value);
}
va_end (args);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
g_variant_new ("(sa{sv}as)",
PK_DBUS_INTERFACE_TRANSACTION,
&builder,
&invalidated_builder),
NULL);
}
static void
pk_transaction_emit_property_changed (PkTransaction *transaction,
const gchar *property_name,
GVariant *property_value)
{
pk_transaction_emit_properties_changed (transaction,
property_name, property_value,
NULL);
}
static void
flush_progress_changed (PkTransaction *transaction)
{
PkTransactionPrivate *priv = transaction->priv;
if (!priv->progress_changed)
return;
pk_transaction_emit_properties_changed (transaction,
"Percentage", g_variant_new_uint32 (priv->percentage),
"ElapsedTime", g_variant_new_uint32 (priv->elapsed_time),
"RemainingTime", g_variant_new_uint32 (priv->remaining_time),
"Speed", g_variant_new_uint32 (priv->speed),
"DownloadSizeRemaining", g_variant_new_uint64 (priv->download_size_remaining),
NULL);
priv->progress_changed = FALSE;
}
static gboolean
progress_timeout_cb (gpointer user_data)
{
PkTransaction *transaction = PK_TRANSACTION (user_data);
flush_progress_changed (transaction);
return G_SOURCE_CONTINUE;
}
static void
schedule_progress_changed (PkTransaction *transaction)
{
transaction->priv->progress_changed = TRUE;
if (transaction->priv->progress_timeout_source == NULL) {
g_autoptr(GSource) source = NULL;
source = g_timeout_source_new (100 );
g_source_set_callback (source, G_SOURCE_FUNC (progress_timeout_cb), transaction, NULL);
#if GLIB_CHECK_VERSION(2, 70, 0)
g_source_set_static_name (source, "PkTransaction progress timeout");
#endif
g_source_attach (source, g_main_context_get_thread_default ());
transaction->priv->progress_timeout_source = g_steal_pointer (&source);
}
}
static void
unschedule_progress_changed (PkTransaction *transaction)
{
flush_progress_changed (transaction);
if (transaction->priv->progress_timeout_source != NULL) {
g_source_destroy (transaction->priv->progress_timeout_source);
g_clear_pointer (&transaction->priv->progress_timeout_source, g_source_unref);
}
}
static void
pk_transaction_progress_changed_emit (PkTransaction *transaction,
guint percentage,
guint elapsed,
guint remaining)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
if (transaction->priv->percentage == percentage &&
transaction->priv->elapsed_time == elapsed &&
transaction->priv->remaining_time == remaining)
return;
transaction->priv->percentage = percentage;
transaction->priv->elapsed_time = elapsed;
transaction->priv->remaining_time = remaining;
schedule_progress_changed (transaction);
}
static void
pk_transaction_allow_cancel_emit (PkTransaction *transaction, gboolean allow_cancel)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
if (transaction->priv->allow_cancel == allow_cancel)
return;
transaction->priv->allow_cancel = allow_cancel;
g_signal_emit (transaction, signals[SIGNAL_ALLOW_CANCEL_CHANGED], 0, allow_cancel);
pk_transaction_emit_property_changed (transaction,
"AllowCancel",
g_variant_new_boolean (allow_cancel));
}
static void
pk_transaction_status_changed_emit (PkTransaction *transaction, PkStatusEnum status)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
if (transaction->priv->status == status)
return;
transaction->priv->status = status;
flush_progress_changed (transaction);
pk_transaction_emit_property_changed (transaction,
"Status",
g_variant_new_uint32 (status));
}
static void
pk_transaction_finished_emit (PkTransaction *transaction,
PkExitEnum exit_enum,
guint time_ms)
{
g_assert (!transaction->priv->emitted_finished);
transaction->priv->emitted_finished = TRUE;
g_debug ("emitting finished '%s', %i",
pk_exit_enum_to_string (exit_enum),
time_ms);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Finished",
g_variant_new ("(uu)",
exit_enum,
time_ms),
NULL);
g_signal_emit (transaction, signals[SIGNAL_FINISHED], 0);
}
static void
pk_transaction_error_code_emit (PkTransaction *transaction,
PkErrorEnum error_enum,
const gchar *details)
{
g_debug ("emitting error-code %s, '%s'",
pk_error_enum_to_string (error_enum),
details);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"ErrorCode",
g_variant_new ("(us)",
error_enum,
details),
NULL);
}
static void
pk_transaction_allow_cancel_cb (PkBackendJob *job,
gboolean allow_cancel,
PkTransaction *transaction)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_debug ("emitting allow-cancel %i", allow_cancel);
pk_transaction_allow_cancel_emit (transaction, allow_cancel);
}
static void
pk_transaction_locked_changed_cb (PkBackendJob *job,
gboolean locked,
PkTransaction *transaction)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_debug ("backend job lock status changed: %i", locked);
if (locked)
pk_transaction_make_exclusive (transaction);
}
static void
pk_transaction_details_cb (PkBackendJob *job,
PkDetails *item,
PkTransaction *transaction)
{
GVariantBuilder builder;
PkGroupEnum group;
const gchar *tmp;
guint64 size;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_details (transaction->priv->results, item);
g_debug ("emitting details");
g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
g_variant_builder_add (&builder, "{sv}", "package-id",
g_variant_new_string (pk_details_get_package_id (item)));
group = pk_details_get_group (item);
if (group != PK_GROUP_ENUM_UNKNOWN)
g_variant_builder_add (&builder, "{sv}", "group",
g_variant_new_uint32 (group));
tmp = pk_details_get_summary (item);
if (tmp != NULL)
g_variant_builder_add (&builder, "{sv}", "summary",
g_variant_new_string (tmp));
tmp = pk_details_get_description (item);
if (tmp != NULL)
g_variant_builder_add (&builder, "{sv}", "description",
g_variant_new_string (tmp));
tmp = pk_details_get_url (item);
if (tmp != NULL)
g_variant_builder_add (&builder, "{sv}", "url",
g_variant_new_string (tmp));
tmp = pk_details_get_license (item);
if (tmp != NULL)
g_variant_builder_add (&builder, "{sv}", "license",
g_variant_new_string (tmp));
size = pk_details_get_size (item);
if (size != 0)
g_variant_builder_add (&builder, "{sv}", "size",
g_variant_new_uint64 (size));
size = pk_details_get_download_size (item);
if (size != G_MAXUINT64)
g_variant_builder_add (&builder, "{sv}", "download-size",
g_variant_new_uint64 (size));
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Details",
g_variant_new ("(a{sv})", &builder),
NULL);
}
static void
pk_transaction_error_code_cb (PkBackendJob *job,
PkError *item,
PkTransaction *transaction)
{
PkErrorEnum code;
g_autofree gchar *details = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_object_get (item,
"code", &code,
"details", &details,
NULL);
if (code == PK_ERROR_ENUM_UNKNOWN) {
g_warning ("%s emitted 'unknown error'",
pk_role_enum_to_string (transaction->priv->role));
}
pk_results_set_error_code (transaction->priv->results, item);
if (!transaction->priv->exclusive && code == PK_ERROR_ENUM_LOCK_REQUIRED) {
g_debug ("changing transaction to exclusive mode (after failing with lock-required)");
transaction->priv->exclusive = TRUE;
} else {
pk_transaction_error_code_emit (transaction, code, details);
}
}
static void
pk_transaction_files_cb (PkBackendJob *job,
PkFiles *item,
PkTransaction *transaction)
{
guint i;
g_autofree gchar *package_id = NULL;
g_auto(GStrv) files = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_object_get (item,
"package-id", &package_id,
"files", &files,
NULL);
if (transaction->priv->role == PK_ROLE_ENUM_DOWNLOAD_PACKAGES &&
transaction->priv->cached_directory != NULL) {
for (i = 0; files[i] != NULL; i++) {
if (!g_str_has_prefix (files[i], transaction->priv->cached_directory)) {
g_warning ("%s does not have the correct prefix (%s)",
files[i],
transaction->priv->cached_directory);
}
}
}
pk_results_add_files (transaction->priv->results, item);
g_debug ("emitting files %s", package_id);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Files",
g_variant_new ("(s^as)",
package_id != NULL ? package_id : "",
files),
NULL);
}
static void
pk_transaction_category_cb (PkBackendJob *job,
PkCategory *item,
PkTransaction *transaction)
{
g_autofree gchar *parent_id = NULL;
g_autofree gchar *cat_id = NULL;
g_autofree gchar *name = NULL;
g_autofree gchar *summary = NULL;
g_autofree gchar *icon = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_category (transaction->priv->results, item);
g_object_get (item,
"parent-id", &parent_id,
"cat-id", &cat_id,
"name", &name,
"summary", &summary,
"icon", &icon,
NULL);
g_debug ("emitting category %s, %s, %s, %s, %s ", parent_id, cat_id, name, summary, icon);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Category",
g_variant_new ("(sssss)",
parent_id != NULL ? parent_id : "",
cat_id,
name,
summary,
icon != NULL ? icon : ""),
NULL);
}
static void
pk_transaction_item_progress_cb (PkBackendJob *job,
PkItemProgress *item_progress,
PkTransaction *transaction)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_debug ("emitting item-progress %s, %s: %u",
pk_item_progress_get_package_id (item_progress),
pk_status_enum_to_string (pk_item_progress_get_status (item_progress)),
pk_item_progress_get_percentage (item_progress));
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"ItemProgress",
g_variant_new ("(suu)",
pk_item_progress_get_package_id (item_progress),
pk_item_progress_get_status (item_progress),
pk_item_progress_get_percentage (item_progress)),
NULL);
}
static void
pk_transaction_distro_upgrade_cb (PkBackendJob *job,
PkDistroUpgrade *item,
PkTransaction *transaction)
{
PkUpdateStateEnum state;
g_autofree gchar *name = NULL;
g_autofree gchar *summary = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_distro_upgrade (transaction->priv->results, item);
g_object_get (item,
"state", &state,
"name", &name,
"summary", &summary,
NULL);
g_debug ("emitting distro-upgrade %s, %s, %s",
pk_update_state_enum_to_string (state),
name, summary);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"DistroUpgrade",
g_variant_new ("(uss)",
state,
name,
summary != NULL ? summary : ""),
NULL);
}
static gchar *
pk_transaction_package_list_to_string (GPtrArray *array)
{
GString *string;
PkPackage *pkg;
guint i;
string = g_string_new ("");
for (i = 0; i < array->len; i++) {
pkg = g_ptr_array_index (array, i);
g_string_append_printf (string, "%s\t%s\t%s\n",
pk_info_enum_to_string (pk_package_get_info (pkg)),
pk_package_get_id (pkg),
pk_package_get_summary (pkg));
}
if (string->len != 0)
g_string_set_size (string, string->len-1);
return g_string_free (string, FALSE);
}
const gchar *
pk_transaction_state_to_string (PkTransactionState state)
{
if (state == PK_TRANSACTION_STATE_NEW)
return "new";
if (state == PK_TRANSACTION_STATE_WAITING_FOR_AUTH)
return "waiting-for-auth";
if (state == PK_TRANSACTION_STATE_READY)
return "ready";
if (state == PK_TRANSACTION_STATE_RUNNING)
return "running";
if (state == PK_TRANSACTION_STATE_FINISHED)
return "finished";
if (state == PK_TRANSACTION_STATE_ERROR)
return "error";
return NULL;
}
void
pk_transaction_set_state (PkTransaction *transaction, PkTransactionState state)
{
PkTransactionPrivate *priv = transaction->priv;
if (priv->state != PK_TRANSACTION_STATE_UNKNOWN &&
priv->state > state) {
g_warning ("cannot set %s, as already %s",
pk_transaction_state_to_string (state),
pk_transaction_state_to_string (priv->state));
return;
}
if (priv->state == state) {
g_warning ("cannot set %s, as already in that state",
pk_transaction_state_to_string (state));
return;
}
g_debug ("transaction now %s", pk_transaction_state_to_string (state));
priv->state = state;
g_signal_emit (transaction, signals[SIGNAL_STATE_CHANGED], 0, state);
if (priv->role == PK_ROLE_ENUM_REMOVE_PACKAGES ||
priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES) {
if (priv->client_pid != PK_TRANSACTION_PID_INVALID && priv->cmdline == NULL)
priv->cmdline = pk_get_cmdline_for_pid (priv->client_pid);
}
if (state == PK_TRANSACTION_STATE_READY &&
(priv->role == PK_ROLE_ENUM_REMOVE_PACKAGES ||
priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES)) {
pk_transaction_db_add (priv->transaction_db, priv->tid);
pk_transaction_db_set_role (priv->transaction_db, priv->tid, priv->role);
pk_transaction_db_set_uid (priv->transaction_db, priv->tid, priv->client_uid);
if (priv->cmdline != NULL)
pk_transaction_db_set_cmdline (priv->transaction_db, priv->tid, priv->cmdline);
syslog (LOG_DAEMON | LOG_DEBUG,
"new %s transaction %s scheduled from uid %i",
pk_role_enum_to_string (priv->role),
priv->tid, priv->client_uid);
}
if (state == PK_TRANSACTION_STATE_WAITING_FOR_AUTH) {
pk_transaction_status_changed_emit (transaction,
PK_STATUS_ENUM_WAITING_FOR_AUTH);
pk_transaction_progress_changed_emit (transaction,
PK_BACKEND_PERCENTAGE_INVALID,
0, 0);
} else if (state == PK_TRANSACTION_STATE_READY) {
pk_transaction_status_changed_emit (transaction,
PK_STATUS_ENUM_WAIT);
pk_transaction_progress_changed_emit (transaction,
PK_BACKEND_PERCENTAGE_INVALID,
0, 0);
}
}
PkTransactionState
pk_transaction_get_state (PkTransaction *transaction)
{
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), PK_TRANSACTION_STATE_UNKNOWN);
return transaction->priv->state;
}
guint
pk_transaction_get_uid (PkTransaction *transaction)
{
return transaction->priv->client_uid;
}
static void
pk_transaction_setup_mime_types (PkTransaction *transaction)
{
guint i;
g_auto(GStrv) mime_types = NULL;
mime_types = pk_backend_get_mime_types (transaction->priv->backend);
for (i = 0; mime_types[i] != NULL; i++) {
g_ptr_array_add (transaction->priv->supported_content_types,
g_strdup (mime_types[i]));
}
}
void
pk_transaction_set_backend (PkTransaction *transaction,
PkBackend *backend)
{
if (transaction->priv->backend != NULL)
g_object_unref (transaction->priv->backend);
transaction->priv->backend = g_object_ref (backend);
pk_transaction_setup_mime_types (transaction);
}
PkBackendJob *
pk_transaction_get_backend_job (PkTransaction *transaction)
{
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), NULL);
return transaction->priv->job;
}
gboolean
pk_transaction_is_finished_with_lock_required (PkTransaction *transaction)
{
g_autoptr(PkError) error_code = NULL;
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
error_code = pk_results_get_error_code (transaction->priv->results);
if (error_code != NULL &&
pk_error_get_code (error_code) == PK_ERROR_ENUM_LOCK_REQUIRED) {
return TRUE;
}
return FALSE;
}
static void
pk_transaction_offline_invalidate_check (PkTransaction *transaction)
{
PkPackage *pkg;
const gchar *package_id;
gchar **package_ids;
guint i;
g_autoptr(GError) error = NULL;
g_autoptr(PkPackageSack) sack = NULL;
g_autoptr(GPtrArray) invalidated = NULL;
sack = pk_offline_get_prepared_sack (NULL);
if (sack == NULL)
return;
package_ids = transaction->priv->cached_package_ids;
for (i = 0; package_ids[i] != NULL; i++) {
pkg = pk_package_sack_find_by_id_name_arch (sack, package_ids[i]);
if (pkg != NULL) {
g_debug ("%s modified %s, invalidating prepared-updates",
package_ids[i], pk_package_get_id (pkg));
if (!pk_offline_auth_invalidate (&error)) {
g_warning ("failed to invalidate: %s",
error->message);
}
g_object_unref (pkg);
return;
}
}
invalidated = pk_results_get_package_array (transaction->priv->results);
for (i = 0; i < invalidated->len; i++) {
package_id = pk_package_get_id (g_ptr_array_index (invalidated, i));
pkg = pk_package_sack_find_by_id_name_arch (sack, package_id);
if (pkg != NULL) {
g_debug ("%s modified %s, invalidating prepared-updates",
package_id, pk_package_get_id (pkg));
if (!pk_offline_auth_invalidate (&error)) {
g_warning ("failed to invalidate: %s",
error->message);
}
g_object_unref (pkg);
return;
}
}
}
static void
pk_transaction_offline_finished (PkTransaction *transaction)
{
PkBitfield transaction_flags;
gchar **package_ids;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) array = NULL;
transaction_flags = transaction->priv->cached_transaction_flags;
if (transaction->priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES &&
pk_bitfield_contain (transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD)) {
package_ids = transaction->priv->cached_package_ids;
if (!pk_offline_auth_set_prepared_ids (package_ids, &error)) {
g_warning ("failed to write offline update: %s",
error->message);
}
return;
}
transaction_flags = transaction->priv->cached_transaction_flags;
if (transaction->priv->role == PK_ROLE_ENUM_UPGRADE_SYSTEM &&
pk_bitfield_contain (transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD)) {
const gchar *version = transaction->priv->cached_value;
g_autofree gchar *name = NULL;
name = pk_get_distro_name (&error);
if (name == NULL) {
g_warning ("failed to get distro name: %s",
error->message);
return;
}
if (!pk_offline_auth_set_prepared_upgrade (name, version, &error)) {
g_warning ("failed to write offline system upgrade state: %s",
error->message);
return;
}
return;
}
switch (transaction->priv->role) {
case PK_ROLE_ENUM_GET_UPDATES:
array = pk_results_get_package_array (transaction->priv->results);
if (array->len == 0) {
if (!pk_offline_auth_invalidate (&error)) {
g_warning ("failed to invalidate: %s",
error->message);
}
}
break;
case PK_ROLE_ENUM_REFRESH_CACHE:
case PK_ROLE_ENUM_REPO_SET_DATA:
case PK_ROLE_ENUM_REPO_ENABLE:
if (!pk_offline_auth_invalidate (&error))
g_warning ("failed to invalidate: %s", error->message);
break;
case PK_ROLE_ENUM_UPDATE_PACKAGES:
case PK_ROLE_ENUM_INSTALL_PACKAGES:
case PK_ROLE_ENUM_REMOVE_PACKAGES:
pk_transaction_offline_invalidate_check (transaction);
break;
default:
break;
}
}
static void
pk_transaction_finished_cb (PkBackendJob *job, PkExitEnum exit_enum, PkTransaction *transaction)
{
guint time_ms;
guint i;
PkPackage *item;
PkInfoEnum info;
PkBitfield transaction_flags;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
if (transaction->priv->finished) {
g_warning ("Already finished");
return;
}
unschedule_progress_changed (transaction);
pk_results_set_exit_code (transaction->priv->results, exit_enum);
if (exit_enum != PK_EXIT_ENUM_CANCELLED &&
pk_transaction_is_finished_with_lock_required (transaction)) {
g_signal_emit (transaction, signals[SIGNAL_FINISHED], 0);
return;
}
transaction_flags = transaction->priv->cached_transaction_flags;
if (exit_enum == PK_EXIT_ENUM_SUCCESS &&
!pk_bitfield_contain (transaction_flags, PK_TRANSACTION_FLAG_ENUM_SIMULATE)) {
pk_transaction_offline_finished (transaction);
}
transaction->priv->finished = TRUE;
if (transaction->priv->emit_signature_required)
exit_enum = PK_EXIT_ENUM_KEY_REQUIRED;
else if (transaction->priv->emit_eula_required)
exit_enum = PK_EXIT_ENUM_EULA_REQUIRED;
else if (transaction->priv->emit_media_change_required)
exit_enum = PK_EXIT_ENUM_MEDIA_CHANGE_REQUIRED;
if (exit_enum == PK_EXIT_ENUM_SUCCESS)
pk_transaction_finish_invalidate_caches (transaction);
time_ms = pk_transaction_get_runtime (transaction);
g_debug ("backend was running for %i ms", time_ms);
if (transaction->priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES ||
transaction->priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
transaction->priv->role == PK_ROLE_ENUM_REMOVE_PACKAGES) {
g_autoptr(GPtrArray) array = NULL;
g_autofree gchar *packages = NULL;
array = pk_results_get_package_array (transaction->priv->results);
packages = pk_transaction_package_list_to_string (array);
if (!pk_strzero (packages))
pk_transaction_db_set_data (transaction->priv->transaction_db, transaction->priv->tid, packages);
for (i = 0; i < array->len; i++) {
item = g_ptr_array_index (array, i);
info = pk_package_get_info (item);
if (info == PK_INFO_ENUM_REMOVING ||
info == PK_INFO_ENUM_INSTALLING ||
info == PK_INFO_ENUM_UPDATING) {
syslog (LOG_DAEMON | LOG_DEBUG,
"in %s for %s package %s was %s for uid %i",
transaction->priv->tid,
pk_role_enum_to_string (transaction->priv->role),
pk_package_get_id (item),
pk_info_enum_to_string (info),
transaction->priv->client_uid);
}
}
}
if (transaction->priv->role == PK_ROLE_ENUM_REPO_ENABLE ||
transaction->priv->role == PK_ROLE_ENUM_REPO_REMOVE ||
transaction->priv->role == PK_ROLE_ENUM_REPO_SET_DATA) {
pk_backend_repo_list_changed (transaction->priv->backend);
}
if (exit_enum == PK_EXIT_ENUM_SUCCESS)
pk_transaction_db_action_time_reset (transaction->priv->transaction_db, transaction->priv->role);
if (exit_enum == PK_EXIT_ENUM_SUCCESS)
pk_transaction_db_set_finished (transaction->priv->transaction_db, transaction->priv->tid, TRUE, time_ms);
else
pk_transaction_db_set_finished (transaction->priv->transaction_db, transaction->priv->tid, FALSE, time_ms);
if (transaction->priv->client_uid != PK_TRANSACTION_UID_INVALID) {
syslog (LOG_DAEMON | LOG_DEBUG,
"%s transaction %s from uid %i finished with %s after %ims",
pk_role_enum_to_string (transaction->priv->role),
transaction->priv->tid,
transaction->priv->client_uid,
pk_exit_enum_to_string (exit_enum),
time_ms);
} else {
syslog (LOG_DAEMON | LOG_DEBUG,
"%s transaction %s finished with %s after %ims",
pk_role_enum_to_string (transaction->priv->role),
transaction->priv->tid,
pk_exit_enum_to_string (exit_enum),
time_ms);
}
pk_backend_job_disconnect_vfuncs (transaction->priv->job);
pk_backend_stop_job (transaction->priv->backend, transaction->priv->job);
pk_transaction_finished_emit (transaction, exit_enum, time_ms);
}
static void
pk_transaction_package_cb (PkBackend *backend,
PkPackage *item,
PkTransaction *transaction)
{
const gchar *role_text;
PkInfoEnum info;
PkInfoEnum update_severity;
const gchar *package_id;
const gchar *summary = NULL;
guint encoded_value;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
if (transaction->priv->finished) {
g_warning ("Already finished");
return;
}
info = pk_package_get_info (item);
if (transaction->priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
transaction->priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES) {
if (info == PK_INFO_ENUM_INSTALLED) {
role_text = pk_role_enum_to_string (transaction->priv->role);
g_warning ("%s emitted 'installed' rather than 'installing'",
role_text);
return;
}
}
if (pk_bitfield_contain (transaction->priv->cached_filters,
PK_FILTER_ENUM_NOT_INSTALLED)) {
if (info == PK_INFO_ENUM_INSTALLED) {
role_text = pk_role_enum_to_string (transaction->priv->role);
g_warning ("%s emitted package that was installed when "
"the ~installed filter is in place",
role_text);
return;
}
}
if (pk_bitfield_contain (transaction->priv->cached_filters,
PK_FILTER_ENUM_INSTALLED)) {
if (info == PK_INFO_ENUM_AVAILABLE) {
role_text = pk_role_enum_to_string (transaction->priv->role);
g_warning ("%s emitted package that was ~installed when "
"the installed filter is in place",
role_text);
return;
}
}
if (info != PK_INFO_ENUM_FINISHED)
pk_results_add_package (transaction->priv->results, item);
package_id = pk_package_get_id (item);
g_free (transaction->priv->last_package_id);
transaction->priv->last_package_id = g_strdup (package_id);
summary = pk_package_get_summary (item);
if (transaction->priv->role != PK_ROLE_ENUM_GET_PACKAGES) {
g_debug ("emit package %s, %s, %s",
pk_info_enum_to_string (info),
package_id,
summary);
}
g_assert ((PK_INFO_ENUM_LAST & (~0xFFFF)) == 0);
update_severity = pk_package_get_update_severity (item);
encoded_value = info | (((guint32) update_severity) << 16);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Package",
g_variant_new ("(uss)",
encoded_value,
package_id,
summary ? summary : ""),
NULL);
}
static void
pk_transaction_packages_cb (PkBackend *backend,
GPtrArray *package_array,
PkTransaction *transaction)
{
g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(uss)"));
g_autoptr(GVariant) package_array_variant = NULL;
guint n_added_packages = 0;
gboolean emitted = FALSE;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
if (transaction->priv->finished) {
g_warning ("Already finished");
return;
}
for (guint i = 0; i < package_array->len; i++) {
PkPackage *item = g_ptr_array_index (package_array, i);
const gchar *role_text;
PkInfoEnum info;
PkInfoEnum update_severity;
const gchar *package_id;
const gchar *summary = NULL;
guint encoded_value;
info = pk_package_get_info (item);
if (transaction->priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
transaction->priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES) {
if (info == PK_INFO_ENUM_INSTALLED) {
role_text = pk_role_enum_to_string (transaction->priv->role);
g_warning ("%s emitted 'installed' rather than 'installing'",
role_text);
continue;
}
}
if (pk_bitfield_contain (transaction->priv->cached_filters,
PK_FILTER_ENUM_NOT_INSTALLED)) {
if (info == PK_INFO_ENUM_INSTALLED) {
role_text = pk_role_enum_to_string (transaction->priv->role);
g_warning ("%s emitted package that was installed when "
"the ~installed filter is in place",
role_text);
continue;
}
}
if (pk_bitfield_contain (transaction->priv->cached_filters,
PK_FILTER_ENUM_INSTALLED)) {
if (info == PK_INFO_ENUM_AVAILABLE) {
role_text = pk_role_enum_to_string (transaction->priv->role);
g_warning ("%s emitted package that was ~installed when "
"the installed filter is in place",
role_text);
continue;
}
}
if (info != PK_INFO_ENUM_FINISHED)
pk_results_add_package (transaction->priv->results, item);
package_id = pk_package_get_id (item);
g_free (transaction->priv->last_package_id);
transaction->priv->last_package_id = g_strdup (package_id);
summary = pk_package_get_summary (item);
if (transaction->priv->role != PK_ROLE_ENUM_GET_PACKAGES) {
g_debug ("emit package %s, %s, %s",
pk_info_enum_to_string (info),
package_id,
summary);
}
g_assert ((PK_INFO_ENUM_LAST & (~0xFFFF)) == 0);
update_severity = pk_package_get_update_severity (item);
encoded_value = info | (((guint32) update_severity) << 16);
g_variant_builder_add (&builder,
"(uss)",
encoded_value,
package_id,
summary ? summary : "");
n_added_packages++;
}
if (n_added_packages == 0) {
g_debug ("Empty package array");
return;
}
package_array_variant = g_variant_ref_sink (g_variant_builder_end (&builder));
if (transaction->priv->client_supports_plural_signals &&
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Packages",
g_variant_new ("(@a(uss))",
package_array_variant),
NULL))
emitted = TRUE;
if (!emitted) {
GVariantIter iter;
g_autoptr(GVariant) child = NULL;
g_variant_iter_init (&iter, package_array_variant);
while ((child = g_variant_iter_next_value (&iter))) {
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Package",
child,
NULL);
g_clear_pointer (&child, g_variant_unref);
}
}
}
static void
pk_transaction_repo_detail_cb (PkBackend *backend,
PkRepoDetail *item,
PkTransaction *transaction)
{
gboolean enabled;
const gchar *repo_id;
const gchar *description;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_repo_detail (transaction->priv->results, item);
repo_id = pk_repo_detail_get_id (item);
description = pk_repo_detail_get_description (item);
enabled = pk_repo_detail_get_enabled (item);
g_debug ("emitting repo-detail %s, %s, %i", repo_id, description, enabled);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"RepoDetail",
g_variant_new ("(ssb)",
repo_id,
description != NULL ? description : "",
enabled),
NULL);
}
static void
pk_transaction_repo_signature_required_cb (PkBackend *backend,
PkRepoSignatureRequired *item,
PkTransaction *transaction)
{
PkSigTypeEnum type;
g_autofree gchar *package_id = NULL;
g_autofree gchar *repository_name = NULL;
g_autofree gchar *key_url = NULL;
g_autofree gchar *key_userid = NULL;
g_autofree gchar *key_id = NULL;
g_autofree gchar *key_fingerprint = NULL;
g_autofree gchar *key_timestamp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_repo_signature_required (transaction->priv->results, item);
g_object_get (item,
"type", &type,
"package-id", &package_id,
"repository-name", &repository_name,
"key-url", &key_url,
"key-userid", &key_userid,
"key-id", &key_id,
"key-fingerprint", &key_fingerprint,
"key-timestamp", &key_timestamp,
NULL);
g_debug ("emitting repo_signature_required %s, %s, %s, %s, %s, %s, %s, %s",
package_id, repository_name, key_url, key_userid, key_id,
key_fingerprint, key_timestamp,
pk_sig_type_enum_to_string (type));
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"RepoSignatureRequired",
g_variant_new ("(sssssssu)",
package_id,
repository_name,
key_url != NULL ? key_url : "",
key_userid != NULL ? key_userid : "",
key_id != NULL ? key_id : "",
key_fingerprint != NULL ? key_fingerprint : "",
key_timestamp != NULL ? key_timestamp : "",
type),
NULL);
transaction->priv->emit_signature_required = TRUE;
}
static void
pk_transaction_eula_required_cb (PkBackend *backend,
PkEulaRequired *item,
PkTransaction *transaction)
{
const gchar *eula_id;
const gchar *package_id;
const gchar *vendor_name;
const gchar *license_agreement;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_eula_required (transaction->priv->results, item);
eula_id = pk_eula_required_get_eula_id (item);
package_id = pk_eula_required_get_package_id (item);
vendor_name = pk_eula_required_get_vendor_name (item);
license_agreement = pk_eula_required_get_license_agreement (item);
g_debug ("emitting eula-required %s, %s, %s, %s",
eula_id, package_id, vendor_name, license_agreement);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"EulaRequired",
g_variant_new ("(ssss)",
eula_id,
package_id,
vendor_name != NULL ? vendor_name : "",
license_agreement != NULL ? license_agreement : ""),
NULL);
transaction->priv->emit_eula_required = TRUE;
}
static void
pk_transaction_media_change_required_cb (PkBackend *backend,
PkMediaChangeRequired *item,
PkTransaction *transaction)
{
PkMediaTypeEnum media_type;
g_autofree gchar *media_id = NULL;
g_autofree gchar *media_text = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_media_change_required (transaction->priv->results, item);
g_object_get (item,
"media-type", &media_type,
"media-id", &media_id,
"media-text", &media_text,
NULL);
g_debug ("emitting media-change-required %s, %s, %s",
pk_media_type_enum_to_string (media_type),
media_id,
media_text);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"MediaChangeRequired",
g_variant_new ("(uss)",
media_type,
media_id,
media_text != NULL ? media_text : ""),
NULL);
transaction->priv->emit_media_change_required = TRUE;
}
static void
pk_transaction_require_restart_cb (PkBackend *backend,
PkRequireRestart *item,
PkTransaction *transaction)
{
PkRequireRestart *item_tmp;
gboolean found = FALSE;
guint i;
PkRestartEnum restart;
g_autofree gchar *package_id = NULL;
g_autoptr(GPtrArray) array = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_object_get (item,
"package-id", &package_id,
"restart", &restart,
NULL);
array = pk_results_get_require_restart_array (transaction->priv->results);
for (i = 0; i < array->len; i++) {
g_autofree gchar *package_id_tmp = NULL;
item_tmp = g_ptr_array_index (array, i);
g_object_get (item_tmp,
"package-id", &package_id_tmp,
NULL);
if (g_strcmp0 (package_id, package_id_tmp) == 0) {
found = TRUE;
break;
}
}
if (found) {
g_debug ("ignoring %s (%s) as already sent",
pk_restart_enum_to_string (restart),
package_id);
return;
}
pk_results_add_require_restart (transaction->priv->results, item);
g_debug ("emitting require-restart %s, '%s'",
pk_restart_enum_to_string (restart),
package_id);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"RequireRestart",
g_variant_new ("(us)",
restart,
package_id),
NULL);
}
static void
pk_transaction_status_changed_cb (PkBackendJob *job,
PkStatusEnum status,
PkTransaction *transaction)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
if (status == PK_STATUS_ENUM_WAIT)
return;
if (transaction->priv->finished) {
g_warning ("Already finished, so can't proxy status %s",
pk_status_enum_to_string (status));
return;
}
pk_transaction_status_changed_emit (transaction, status);
}
static void
pk_transaction_update_detail_cb (PkBackend *backend,
PkUpdateDetail *item,
PkTransaction *transaction)
{
const gchar *changelog;
const gchar *issued;
const gchar *package_id;
const gchar *updated;
const gchar *update_text;
gchar **bugzilla_urls;
gchar **cve_urls;
gchar *empty[] = { NULL };
gchar **obsoletes;
gchar **updates;
gchar **vendor_urls;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
pk_results_add_update_detail (transaction->priv->results, item);
package_id = pk_update_detail_get_package_id (item);
updates = pk_update_detail_get_updates (item);
obsoletes = pk_update_detail_get_obsoletes (item);
vendor_urls = pk_update_detail_get_vendor_urls (item);
bugzilla_urls = pk_update_detail_get_bugzilla_urls (item);
cve_urls = pk_update_detail_get_cve_urls (item);
update_text = pk_update_detail_get_update_text (item);
changelog = pk_update_detail_get_changelog (item);
issued = pk_update_detail_get_issued (item);
updated = pk_update_detail_get_updated (item);
g_debug ("emitting update-detail for %s", package_id);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"UpdateDetail",
g_variant_new ("(s^as^as^as^as^asussuss)",
package_id,
updates != NULL ? updates : empty,
obsoletes != NULL ? obsoletes : empty,
vendor_urls != NULL ? vendor_urls : empty,
bugzilla_urls != NULL ? bugzilla_urls : empty,
cve_urls != NULL ? cve_urls : empty,
pk_update_detail_get_restart (item),
update_text != NULL ? update_text : "",
changelog != NULL ? changelog : "",
pk_update_detail_get_state (item),
issued != NULL ? issued : "",
updated != NULL ? updated : ""),
NULL);
}
static void
pk_transaction_update_details_cb (PkBackend *backend,
GPtrArray *update_details_array,
PkTransaction *transaction)
{
g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(sasasasasasussuss)"));
g_autoptr(GVariant) update_details_array_variant = NULL;
guint n_update_details = 0;
gboolean emitted = FALSE;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
for (guint i = 0; i < update_details_array->len; i++) {
PkUpdateDetail *item = g_ptr_array_index (update_details_array, i);
const gchar *changelog;
const gchar *issued;
const gchar *package_id;
const gchar *updated;
const gchar *update_text;
const gchar * const *bugzilla_urls;
const gchar * const *cve_urls;
const gchar * const empty[] = { NULL };
const gchar * const *obsoletes;
const gchar * const *updates;
const gchar * const *vendor_urls;
pk_results_add_update_detail (transaction->priv->results, item);
package_id = pk_update_detail_get_package_id (item);
updates = (const gchar * const *) pk_update_detail_get_updates (item);
obsoletes = (const gchar * const *) pk_update_detail_get_obsoletes (item);
vendor_urls = (const gchar * const *) pk_update_detail_get_vendor_urls (item);
bugzilla_urls = (const gchar * const *) pk_update_detail_get_bugzilla_urls (item);
cve_urls = (const gchar * const *) pk_update_detail_get_cve_urls (item);
update_text = pk_update_detail_get_update_text (item);
changelog = pk_update_detail_get_changelog (item);
issued = pk_update_detail_get_issued (item);
updated = pk_update_detail_get_updated (item);
g_debug ("emitting update-detail for %s", package_id);
g_variant_builder_add (&builder,
"(s^as^as^as^as^asussuss)",
package_id,
updates != NULL ? updates : empty,
obsoletes != NULL ? obsoletes : empty,
vendor_urls != NULL ? vendor_urls : empty,
bugzilla_urls != NULL ? bugzilla_urls : empty,
cve_urls != NULL ? cve_urls : empty,
pk_update_detail_get_restart (item),
update_text != NULL ? update_text : "",
changelog != NULL ? changelog : "",
pk_update_detail_get_state (item),
issued != NULL ? issued : "",
updated != NULL ? updated : "");
n_update_details++;
}
if (n_update_details == 0) {
g_debug ("Empty update details array");
return;
}
update_details_array_variant = g_variant_ref_sink (g_variant_builder_end (&builder));
if (transaction->priv->client_supports_plural_signals &&
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"UpdateDetails",
g_variant_new ("(@a(sasasasasasussuss))",
update_details_array_variant),
NULL))
emitted = TRUE;
if (!emitted) {
GVariantIter iter;
g_autoptr(GVariant) child = NULL;
g_variant_iter_init (&iter, update_details_array_variant);
while ((child = g_variant_iter_next_value (&iter))) {
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"UpdateDetail",
child,
NULL);
g_clear_pointer (&child, g_variant_unref);
}
}
}
static gboolean
pk_transaction_set_session_state (PkTransaction *transaction,
GError **error)
{
gboolean ret;
g_autofree gchar *session = NULL;
g_autofree gchar *proxy_http = NULL;
g_autofree gchar *proxy_https = NULL;
g_autofree gchar *proxy_ftp = NULL;
g_autofree gchar *proxy_socks = NULL;
g_autofree gchar *no_proxy = NULL;
g_autofree gchar *pac = NULL;
g_autofree gchar *cmdline = NULL;
PkTransactionPrivate *priv = transaction->priv;
if (!pk_dbus_connect (priv->dbus, error))
return FALSE;
session = pk_dbus_get_session (priv->dbus, priv->sender);
if (session == NULL) {
g_set_error_literal (error, 1, 0, "failed to get the session");
return FALSE;
}
ret = pk_transaction_db_get_proxy (priv->transaction_db,
priv->client_uid,
session,
&proxy_http,
&proxy_https,
&proxy_ftp,
&proxy_socks,
&no_proxy,
&pac);
if (!ret) {
g_set_error_literal (error, 1, 0,
"failed to get the proxy from the database");
return FALSE;
}
pk_backend_job_set_proxy (priv->job,
proxy_http,
proxy_https,
proxy_ftp,
proxy_socks,
no_proxy,
pac);
cmdline = g_strdup_printf ("PackageKit: %s",
pk_role_enum_to_string (priv->role));
pk_backend_job_set_uid (priv->job, priv->client_uid);
pk_backend_job_set_cmdline (priv->job, cmdline);
return TRUE;
}
static void
pk_transaction_speed_cb (PkBackendJob *job,
guint speed,
PkTransaction *transaction)
{
if (transaction->priv->speed == speed)
return;
transaction->priv->speed = speed;
schedule_progress_changed (transaction);
}
static void
pk_transaction_download_size_remaining_cb (PkBackendJob *job,
guint64 *download_size_remaining,
PkTransaction *transaction)
{
if (transaction->priv->download_size_remaining == *download_size_remaining)
return;
transaction->priv->download_size_remaining = *download_size_remaining;
schedule_progress_changed (transaction);
}
static void
pk_transaction_percentage_cb (PkBackendJob *job,
guint percentage,
PkTransaction *transaction)
{
if (transaction->priv->percentage == percentage)
return;
transaction->priv->percentage = percentage;
schedule_progress_changed (transaction);
}
gboolean
pk_transaction_run (PkTransaction *transaction)
{
GError *error = NULL;
PkExitEnum exit_status;
PkTransactionPrivate *priv = PK_TRANSACTION_GET_PRIVATE (transaction);
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
g_return_val_if_fail (priv->tid != NULL, FALSE);
g_return_val_if_fail (transaction->priv->backend != NULL, FALSE);
pk_transaction_status_changed_emit (transaction, PK_STATUS_ENUM_SETUP);
if (!pk_transaction_set_session_state (transaction, &error)) {
g_debug ("failed to set the session state (non-fatal): %s",
error->message);
g_clear_error (&error);
}
if (pk_backend_job_get_exit_code (priv->job) == PK_EXIT_ENUM_CANCELLED) {
exit_status = pk_backend_job_get_exit_code (priv->job);
pk_transaction_finished_emit (transaction, exit_status, 0);
return TRUE;
}
pk_backend_start_job (priv->backend, priv->job);
if (pk_backend_job_get_is_error_set (priv->job)) {
exit_status = pk_backend_job_get_exit_code (priv->job);
pk_transaction_finished_emit (transaction, exit_status, 0);
}
if (pk_backend_job_get_exit_code (priv->job) == PK_EXIT_ENUM_SKIP_TRANSACTION) {
pk_transaction_finished_emit (transaction, PK_EXIT_ENUM_SUCCESS, 0);
}
pk_backend_job_set_role (priv->job, priv->role);
g_debug ("setting role for %s to %s",
priv->tid,
pk_role_enum_to_string (priv->role));
pk_backend_job_set_percentage (priv->job, PK_BACKEND_PERCENTAGE_INVALID);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_LOCKED_CHANGED,
PK_BACKEND_JOB_VFUNC (pk_transaction_locked_changed_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_ALLOW_CANCEL,
PK_BACKEND_JOB_VFUNC (pk_transaction_allow_cancel_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_DETAILS,
PK_BACKEND_JOB_VFUNC (pk_transaction_details_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_ERROR_CODE,
PK_BACKEND_JOB_VFUNC (pk_transaction_error_code_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_FILES,
PK_BACKEND_JOB_VFUNC (pk_transaction_files_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_DISTRO_UPGRADE,
PK_BACKEND_JOB_VFUNC (pk_transaction_distro_upgrade_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_FINISHED,
PK_BACKEND_JOB_VFUNC (pk_transaction_finished_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_PACKAGE,
PK_BACKEND_JOB_VFUNC (pk_transaction_package_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_PACKAGES,
PK_BACKEND_JOB_VFUNC (pk_transaction_packages_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_ITEM_PROGRESS,
PK_BACKEND_JOB_VFUNC (pk_transaction_item_progress_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_PERCENTAGE,
PK_BACKEND_JOB_VFUNC (pk_transaction_percentage_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_SPEED,
PK_BACKEND_JOB_VFUNC (pk_transaction_speed_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_DOWNLOAD_SIZE_REMAINING,
PK_BACKEND_JOB_VFUNC (pk_transaction_download_size_remaining_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_REPO_DETAIL,
PK_BACKEND_JOB_VFUNC (pk_transaction_repo_detail_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_REPO_SIGNATURE_REQUIRED,
PK_BACKEND_JOB_VFUNC (pk_transaction_repo_signature_required_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_EULA_REQUIRED,
PK_BACKEND_JOB_VFUNC (pk_transaction_eula_required_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_MEDIA_CHANGE_REQUIRED,
PK_BACKEND_JOB_VFUNC (pk_transaction_media_change_required_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_REQUIRE_RESTART,
PK_BACKEND_JOB_VFUNC (pk_transaction_require_restart_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_STATUS_CHANGED,
PK_BACKEND_JOB_VFUNC (pk_transaction_status_changed_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_UPDATE_DETAIL,
PK_BACKEND_JOB_VFUNC (pk_transaction_update_detail_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_UPDATE_DETAILS,
PK_BACKEND_JOB_VFUNC (pk_transaction_update_details_cb),
transaction);
pk_backend_job_set_vfunc (priv->job,
PK_BACKEND_SIGNAL_CATEGORY,
PK_BACKEND_JOB_VFUNC (pk_transaction_category_cb),
transaction);
switch (priv->role) {
case PK_ROLE_ENUM_DEPENDS_ON:
pk_backend_depends_on (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_package_ids,
priv->cached_force);
break;
case PK_ROLE_ENUM_GET_UPDATE_DETAIL:
pk_backend_get_update_detail (priv->backend,
priv->job,
priv->cached_package_ids);
break;
case PK_ROLE_ENUM_RESOLVE:
pk_backend_resolve (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_package_ids);
break;
case PK_ROLE_ENUM_DOWNLOAD_PACKAGES:
pk_backend_download_packages (priv->backend,
priv->job,
priv->cached_package_ids,
priv->cached_directory);
break;
case PK_ROLE_ENUM_GET_DETAILS:
pk_backend_get_details (priv->backend,
priv->job,
priv->cached_package_ids);
break;
case PK_ROLE_ENUM_GET_DETAILS_LOCAL:
pk_backend_get_details_local (priv->backend,
priv->job,
priv->cached_full_paths);
break;
case PK_ROLE_ENUM_GET_FILES_LOCAL:
pk_backend_get_files_local (priv->backend,
priv->job,
priv->cached_full_paths);
break;
case PK_ROLE_ENUM_GET_DISTRO_UPGRADES:
pk_backend_get_distro_upgrades (priv->backend,
priv->job);
break;
case PK_ROLE_ENUM_GET_FILES:
pk_backend_get_files (priv->backend,
priv->job,
priv->cached_package_ids);
break;
case PK_ROLE_ENUM_REQUIRED_BY:
pk_backend_required_by (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_package_ids,
priv->cached_force);
break;
case PK_ROLE_ENUM_WHAT_PROVIDES:
pk_backend_what_provides (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_values);
break;
case PK_ROLE_ENUM_GET_UPDATES:
pk_backend_get_updates (priv->backend,
priv->job,
priv->cached_filters);
break;
case PK_ROLE_ENUM_GET_PACKAGES:
pk_backend_get_packages (priv->backend,
priv->job,
priv->cached_filters);
break;
case PK_ROLE_ENUM_SEARCH_DETAILS:
pk_backend_search_details (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_values);
break;
case PK_ROLE_ENUM_SEARCH_FILE:
pk_backend_search_files (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_values);
break;
case PK_ROLE_ENUM_SEARCH_GROUP:
pk_backend_search_groups (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_values);
break;
case PK_ROLE_ENUM_SEARCH_NAME:
pk_backend_search_names (priv->backend,
priv->job,
priv->cached_filters,
priv->cached_values);
break;
case PK_ROLE_ENUM_INSTALL_PACKAGES:
pk_backend_install_packages (priv->backend,
priv->job,
priv->cached_transaction_flags,
priv->cached_package_ids);
break;
case PK_ROLE_ENUM_INSTALL_FILES:
pk_backend_install_files (priv->backend,
priv->job,
priv->cached_transaction_flags,
priv->cached_full_paths);
break;
case PK_ROLE_ENUM_INSTALL_SIGNATURE:
pk_backend_install_signature (priv->backend,
priv->job,
PK_SIGTYPE_ENUM_GPG,
priv->cached_key_id,
priv->cached_package_id);
break;
case PK_ROLE_ENUM_REFRESH_CACHE:
pk_backend_refresh_cache (priv->backend,
priv->job,
priv->cached_force);
break;
case PK_ROLE_ENUM_REMOVE_PACKAGES:
pk_backend_remove_packages (priv->backend,
priv->job,
priv->cached_transaction_flags,
priv->cached_package_ids,
priv->cached_allow_deps,
priv->cached_autoremove);
break;
case PK_ROLE_ENUM_UPDATE_PACKAGES:
pk_backend_update_packages (priv->backend,
priv->job,
priv->cached_transaction_flags,
priv->cached_package_ids);
break;
case PK_ROLE_ENUM_GET_CATEGORIES:
pk_backend_get_categories (priv->backend,
priv->job);
break;
case PK_ROLE_ENUM_GET_REPO_LIST:
pk_backend_get_repo_list (priv->backend,
priv->job,
priv->cached_filters);
break;
case PK_ROLE_ENUM_REPO_ENABLE:
pk_backend_repo_enable (priv->backend,
priv->job,
priv->cached_repo_id,
priv->cached_enabled);
break;
case PK_ROLE_ENUM_REPO_SET_DATA:
pk_backend_repo_set_data (priv->backend,
priv->job,
priv->cached_repo_id,
priv->cached_parameter,
priv->cached_value);
break;
case PK_ROLE_ENUM_REPO_REMOVE:
pk_backend_repo_remove (priv->backend,
priv->job,
priv->cached_transaction_flags,
priv->cached_repo_id,
priv->cached_autoremove);
break;
case PK_ROLE_ENUM_UPGRADE_SYSTEM:
pk_backend_upgrade_system (priv->backend,
priv->job,
priv->cached_transaction_flags,
priv->cached_value,
priv->cached_upgrade_kind);
break;
case PK_ROLE_ENUM_REPAIR_SYSTEM:
pk_backend_repair_system (priv->backend,
priv->job,
priv->cached_transaction_flags);
break;
case PK_ROLE_ENUM_CANCEL:
case PK_ROLE_ENUM_GET_OLD_TRANSACTIONS:
case PK_ROLE_ENUM_ACCEPT_EULA:
g_warning ("role %s should be handled by engine",
pk_role_enum_to_string (priv->role));
break;
default:
g_error ("failed to run as role not assigned");
return FALSE;
break;
}
return TRUE;
}
const gchar *
pk_transaction_get_tid (PkTransaction *transaction)
{
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), NULL);
g_return_val_if_fail (transaction->priv->tid != NULL, NULL);
return transaction->priv->tid;
}
gboolean
pk_transaction_is_exclusive (PkTransaction *transaction)
{
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
return transaction->priv->exclusive;
}
void
pk_transaction_make_exclusive (PkTransaction *transaction)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_debug ("changing transaction to exclusive mode");
transaction->priv->exclusive = TRUE;
}
static void
pk_transaction_vanished_cb (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
PkTransaction *transaction = PK_TRANSACTION (user_data);
g_return_if_fail (PK_IS_TRANSACTION (transaction));
transaction->priv->caller_active = FALSE;
pk_transaction_emit_property_changed (transaction,
"CallerActive",
g_variant_new_boolean (transaction->priv->caller_active));
}
gboolean
pk_transaction_set_sender (PkTransaction *transaction, const gchar *sender)
{
PkTransactionPrivate *priv = transaction->priv;
g_autoptr(GError) error = NULL;
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
g_return_val_if_fail (sender != NULL, FALSE);
g_return_val_if_fail (transaction->priv->sender == NULL, FALSE);
g_debug ("setting sender to %s", sender);
priv->sender = g_strdup (sender);
priv->watch_id =
g_bus_watch_name (G_BUS_TYPE_SYSTEM,
sender,
G_BUS_NAME_WATCHER_FLAGS_NONE,
NULL,
pk_transaction_vanished_cb,
transaction,
NULL);
priv->subject = polkit_system_bus_name_new (sender);
if (!pk_dbus_connect (priv->dbus, &error)) {
g_warning ("cannot get UID: %s", error->message);
return FALSE;
}
if (!pk_dbus_get_uid_pid (priv->dbus, sender, &priv->client_uid, &priv->client_pid)) {
priv->client_uid = pk_dbus_get_uid (priv->dbus, sender);
priv->client_pid = pk_dbus_get_pid (priv->dbus, sender);
}
if (g_strcmp0 (sender, ":org.freedesktop.PackageKit") == 0) {
g_debug ("using self-check shortcut");
priv->cmdline = g_strdup ("/usr/sbin/packagekit");
}
return TRUE;
}
static gboolean
pk_transaction_finished_idle_cb (PkTransaction *transaction)
{
pk_transaction_finished_emit (transaction, PK_EXIT_ENUM_SUCCESS, 0);
return FALSE;
}
gboolean
pk_transaction_strvalidate (const gchar *text, GError **error)
{
guint length;
length = pk_strlen (text, 1024);
if (length == 0) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_INPUT_INVALID,
"Invalid input passed to daemon: zero length string");
return FALSE;
}
if (length >= 1024) {
g_set_error (error, PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_INPUT_INVALID,
"Invalid input passed to daemon: input too long: %u", length);
return FALSE;
}
if (!g_utf8_validate (text, -1, NULL)) {
g_set_error (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_INPUT_INVALID,
"Invalid input passed to daemon: %s", text);
return FALSE;
}
return TRUE;
}
static gboolean
pk_transaction_search_check_item (const gchar *values, GError **error)
{
guint size;
if (values == NULL) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
"Search is null. This isn't supposed to happen...");
return FALSE;
}
size = pk_strlen (values, 1024);
if (size == 0) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
"Search string zero length");
return FALSE;
}
if (strstr (values, "*") != NULL) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
"Invalid search containing '*'");
return FALSE;
}
if (strstr (values, "?") != NULL) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
"Invalid search containing '?'");
return FALSE;
}
if (size >= 1024) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
"The search string length is too large");
return FALSE;
}
return pk_transaction_strvalidate (values, error);
}
static gboolean
pk_transaction_search_check (gchar **values, GError **error)
{
guint i;
for (i = 0; values[i] != NULL; i++) {
if (!pk_transaction_search_check_item (values[i], error))
return FALSE;
}
return TRUE;
}
struct AuthorizeActionsData {
PkTransaction *transaction;
PkRoleEnum role;
GPtrArray *actions;
};
static gboolean
pk_transaction_authorize_actions (PkTransaction *transaction,
PkRoleEnum role,
GPtrArray *actions);
static void
pk_transaction_authorize_actions_finished_cb (GObject *source_object,
GAsyncResult *res,
struct AuthorizeActionsData *data)
{
const gchar *action_id = NULL;
PkTransactionPrivate *priv = data->transaction->priv;
g_autoptr(GError) error = NULL;
g_autoptr(PolkitAuthorizationResult) result = NULL;
g_assert (data->actions && data->actions->len > 0);
action_id = g_ptr_array_index (data->actions, 0);
result = polkit_authority_check_authorization_finish (priv->authority, res, &error);
if (g_cancellable_is_cancelled (priv->cancellable)) {
priv->waiting_for_auth = FALSE;
pk_transaction_status_changed_emit (data->transaction, PK_STATUS_ENUM_FINISHED);
pk_transaction_error_code_emit (data->transaction, PK_ERROR_ENUM_NOT_AUTHORIZED,
"The authentication was cancelled due to a timeout.");
pk_transaction_finished_emit (data->transaction, PK_EXIT_ENUM_FAILED, 0);
goto out;
}
if (result == NULL) {
g_autofree gchar *message = NULL;
priv->waiting_for_auth = FALSE;
g_warning ("failed to check for auth: %s", error->message);
pk_transaction_status_changed_emit (data->transaction, PK_STATUS_ENUM_FINISHED);
message = g_strdup_printf ("Failed to check for authentication: %s", error->message);
pk_transaction_error_code_emit (data->transaction,
PK_ERROR_ENUM_NOT_AUTHORIZED,
message);
pk_transaction_finished_emit (data->transaction, PK_EXIT_ENUM_FAILED, 0);
goto out;
}
if (!polkit_authorization_result_get_is_authorized (result)) {
priv->waiting_for_auth = FALSE;
pk_transaction_status_changed_emit (data->transaction, PK_STATUS_ENUM_FINISHED);
pk_transaction_error_code_emit (data->transaction, PK_ERROR_ENUM_NOT_AUTHORIZED,
"Failed to obtain authentication.");
pk_transaction_finished_emit (data->transaction, PK_EXIT_ENUM_FAILED, 0);
syslog (LOG_AUTH | LOG_NOTICE, "uid %i failed to obtain auth", priv->client_uid);
goto out;
}
if (data->actions->len <= 1) {
priv->waiting_for_auth = FALSE;
pk_transaction_set_state (data->transaction, PK_TRANSACTION_STATE_READY);
syslog (LOG_AUTH | LOG_INFO,
"uid %i obtained auth for %s",
priv->client_uid, action_id);
} else {
g_ptr_array_remove_index (data->actions, 0);
pk_transaction_authorize_actions (data->transaction, data->role, data->actions);
}
out:
g_object_unref (data->transaction);
g_ptr_array_unref (data->actions);
g_free (data);
}
static gboolean
pk_transaction_authorize_actions (PkTransaction *transaction,
PkRoleEnum role,
GPtrArray *actions)
{
const gchar *action_id = NULL;
g_autoptr(PolkitDetails) details = NULL;
g_autofree gchar *package_ids = NULL;
GString *string = NULL;
PkTransactionPrivate *priv = transaction->priv;
const gchar *text = NULL;
struct AuthorizeActionsData *data = NULL;
PolkitCheckAuthorizationFlags flags;
if (actions->len <= 0) {
g_debug ("No authentication required");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
return TRUE;
}
action_id = g_ptr_array_index (actions, 0);
syslog (LOG_AUTH | LOG_INFO,
"uid %i is trying to obtain %s auth (only_trusted:%i)",
priv->client_uid,
action_id,
pk_bitfield_contain (priv->cached_transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED));
pk_transaction_set_state (transaction,
PK_TRANSACTION_STATE_WAITING_FOR_AUTH);
priv->waiting_for_auth = TRUE;
details = polkit_details_new ();
if (priv->cached_package_id != NULL)
package_ids = g_strdup (priv->cached_package_id);
else if (priv->cached_package_ids != NULL)
package_ids = pk_package_ids_to_string (priv->cached_package_ids);
if (package_ids != NULL)
polkit_details_insert (details, "package_ids", package_ids);
if (priv->cmdline != NULL)
polkit_details_insert (details, "cmdline", priv->cmdline);
if ((role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
role == PK_ROLE_ENUM_UPDATE_PACKAGES) &&
!pk_bitfield_contain (priv->cached_transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED)) {
polkit_details_insert (details, "polkit.icon_name", "emblem-important");
string = g_string_new ("");
g_string_append (string, g_dgettext (GETTEXT_PACKAGE, N_("The software is not from a trusted source.")));
g_string_append (string, "\n");
if (priv->role == PK_ROLE_ENUM_UPDATE_PACKAGES) {
text = g_dngettext (GETTEXT_PACKAGE,
N_("Do not update this package unless you are sure it is safe to do so."),
N_("Do not update these packages unless you are sure it is safe to do so."),
g_strv_length (priv->cached_package_ids));
g_string_append (string, text);
}
if (priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES) {
text = g_dngettext (GETTEXT_PACKAGE,
N_("Do not install this package unless you are sure it is safe to do so."),
N_("Do not install these packages unless you are sure it is safe to do so."),
g_strv_length (priv->cached_package_ids));
g_string_append (string, text);
}
if (string->len > 0) {
polkit_details_insert (details, "polkit.gettext_domain", GETTEXT_PACKAGE);
polkit_details_insert (details, "polkit.message", string->str);
}
}
data = g_new (struct AuthorizeActionsData, 1);
data->transaction = g_object_ref (transaction);
data->role = role;
data->actions = g_ptr_array_ref (actions);
if (priv->authority == NULL) {
g_autoptr(GError) error = NULL;
priv->authority = polkit_authority_get_sync (NULL, &error);
if (priv->authority == NULL) {
g_warning ("failed to get polkit authority: %s", error->message);
return FALSE;
}
}
flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
if (pk_backend_job_get_interactive (priv->job))
flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
g_debug ("authorizing action %s", action_id);
polkit_authority_check_authorization (priv->authority,
priv->subject,
action_id,
details,
flags,
priv->cancellable,
(GAsyncReadyCallback) pk_transaction_authorize_actions_finished_cb,
data);
return TRUE;
}
static GPtrArray *
pk_transaction_role_to_actions (PkRoleEnum role, guint64 transaction_flags)
{
const gchar *policy = NULL;
GPtrArray *result = NULL;
gboolean check_install_untrusted = FALSE;
result = g_ptr_array_new_with_free_func (g_free);
if (result == NULL)
return result;
if ((role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
role == PK_ROLE_ENUM_INSTALL_FILES ||
role == PK_ROLE_ENUM_UPDATE_PACKAGES) &&
!pk_bitfield_contain (transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED)) {
g_ptr_array_add (result, g_strdup ("org.freedesktop.packagekit.package-install-untrusted"));
check_install_untrusted = TRUE;
}
if (role == PK_ROLE_ENUM_INSTALL_PACKAGES &&
pk_bitfield_contain (transaction_flags, PK_TRANSACTION_FLAG_ENUM_ALLOW_REINSTALL)) {
g_ptr_array_add (result, g_strdup ("org.freedesktop.packagekit.package-reinstall"));
}
if ((role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
role == PK_ROLE_ENUM_UPDATE_PACKAGES) &&
pk_bitfield_contain (transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ALLOW_DOWNGRADE)) {
g_ptr_array_add (result, g_strdup ("org.freedesktop.packagekit.package-downgrade"));
} else if (!check_install_untrusted) {
switch (role) {
case PK_ROLE_ENUM_UPDATE_PACKAGES:
policy = "org.freedesktop.packagekit.system-update";
break;
case PK_ROLE_ENUM_INSTALL_SIGNATURE:
policy = "org.freedesktop.packagekit.system-trust-signing-key";
break;
case PK_ROLE_ENUM_REPO_ENABLE:
case PK_ROLE_ENUM_REPO_SET_DATA:
case PK_ROLE_ENUM_REPO_REMOVE:
policy = "org.freedesktop.packagekit.system-sources-configure";
break;
case PK_ROLE_ENUM_REFRESH_CACHE:
policy = "org.freedesktop.packagekit.system-sources-refresh";
break;
case PK_ROLE_ENUM_REMOVE_PACKAGES:
policy = "org.freedesktop.packagekit.package-remove";
break;
case PK_ROLE_ENUM_INSTALL_PACKAGES:
case PK_ROLE_ENUM_INSTALL_FILES:
policy = "org.freedesktop.packagekit.package-install";
break;
case PK_ROLE_ENUM_ACCEPT_EULA:
policy = "org.freedesktop.packagekit.package-eula-accept";
break;
case PK_ROLE_ENUM_CANCEL:
policy = "org.freedesktop.packagekit.cancel-foreign";
break;
case PK_ROLE_ENUM_UPGRADE_SYSTEM:
policy = "org.freedesktop.packagekit.upgrade-system";
break;
case PK_ROLE_ENUM_REPAIR_SYSTEM:
policy = "org.freedesktop.packagekit.repair-system";
break;
default:
break;
}
if (policy != NULL)
g_ptr_array_add (result, g_strdup (policy));
}
return result;
}
static gboolean
pk_transaction_obtain_authorization (PkTransaction *transaction,
PkRoleEnum role,
GError **error)
{
g_autoptr(GPtrArray) actions = NULL;
PkTransactionPrivate *priv = transaction->priv;
g_autofree gchar *package_ids = NULL;
g_autoptr(PolkitDetails) details = NULL;
g_autoptr(GString) string = NULL;
g_return_val_if_fail (priv->sender != NULL, FALSE);
if (pk_bitfield_contain (transaction->priv->cached_transaction_flags,
PK_TRANSACTION_FLAG_ENUM_ONLY_DOWNLOAD) ||
pk_bitfield_contain (transaction->priv->cached_transaction_flags,
PK_TRANSACTION_FLAG_ENUM_SIMULATE) ||
priv->skip_auth_check == TRUE) {
g_debug ("No authentication required");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
return TRUE;
}
if (priv->subject == NULL) {
g_set_error (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_REFUSED_BY_POLICY,
"subject %s not found", priv->sender);
return FALSE;
}
actions = pk_transaction_role_to_actions (role, priv->cached_transaction_flags);
if (actions == NULL)
return FALSE;
return pk_transaction_authorize_actions (transaction, role, actions);
}
void
pk_transaction_skip_auth_checks (PkTransaction *transaction, gboolean skip_checks)
{
g_return_if_fail (PK_IS_TRANSACTION (transaction));
transaction->priv->skip_auth_check = skip_checks;
}
PkRoleEnum
pk_transaction_get_role (PkTransaction *transaction)
{
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
return transaction->priv->role;
}
static void
pk_transaction_set_role (PkTransaction *transaction, PkRoleEnum role)
{
transaction->priv->role = role;
if (role == PK_ROLE_ENUM_INSTALL_FILES ||
role == PK_ROLE_ENUM_INSTALL_PACKAGES ||
role == PK_ROLE_ENUM_REMOVE_PACKAGES ||
role == PK_ROLE_ENUM_UPDATE_PACKAGES ||
role == PK_ROLE_ENUM_UPGRADE_SYSTEM ||
role == PK_ROLE_ENUM_REPAIR_SYSTEM) {
pk_transaction_make_exclusive (transaction);
}
pk_transaction_emit_property_changed (transaction,
"Role",
g_variant_new_uint32 (role));
}
static void
pk_transaction_dbus_return (GDBusMethodInvocation *context, const GError *error)
{
if (context == NULL) {
if (error != NULL)
g_warning ("context null, and error: %s", error->message);
return;
}
if (error != NULL)
g_dbus_method_invocation_return_gerror (context, error);
else
g_dbus_method_invocation_return_value (context, NULL);
}
static void
pk_transaction_accept_eula (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
guint idle_id;
const gchar *eula_id = NULL;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(&s)",
&eula_id);
ret = pk_transaction_strvalidate (eula_id, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
pk_transaction_set_role (transaction, PK_ROLE_ENUM_ACCEPT_EULA);
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_ACCEPT_EULA,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
g_debug ("AcceptEula method called: %s", eula_id);
pk_backend_accept_eula (transaction->priv->backend, eula_id);
idle_id = g_idle_add ((GSourceFunc) pk_transaction_finished_idle_cb, transaction);
g_source_set_name_by_id (idle_id, "[PkTransaction] finished from accept");
out:
pk_transaction_dbus_return (context, error);
}
void
pk_transaction_cancel_bg (PkTransaction *transaction)
{
g_debug ("CancelBg method called on %s", transaction->priv->tid);
if (transaction->priv->state == PK_TRANSACTION_STATE_FINISHED)
return;
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_CANCEL)) {
g_warning ("Cancel not supported by backend");
return;
}
if (transaction->priv->state <= PK_TRANSACTION_STATE_READY) {
pk_transaction_finished_emit (transaction, PK_EXIT_ENUM_CANCELLED, 0);
return;
}
pk_backend_job_set_status (transaction->priv->job, PK_STATUS_ENUM_CANCEL);
pk_backend_job_set_allow_cancel (transaction->priv->job, FALSE);
pk_backend_job_set_exit_code (transaction->priv->job, PK_EXIT_ENUM_CANCELLED_PRIORITY);
pk_backend_cancel (transaction->priv->backend, transaction->priv->job);
}
static void
pk_transaction_cancel (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
const gchar *sender;
guint uid;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_debug ("Cancel method called on %s", transaction->priv->tid);
if (transaction->priv->state == PK_TRANSACTION_STATE_FINISHED) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_RUNNING,
"Transaction is already finished");
goto out;
}
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_CANCEL)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"Cancel not supported by backend");
goto out;
}
if (transaction->priv->finished) {
g_debug ("No point trying to cancel a finished transaction, ignoring");
pk_transaction_dbus_return (context, NULL);
goto out;
}
if (transaction->priv->role == PK_ROLE_ENUM_UNKNOWN) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_ROLE, "No role");
goto out;
}
sender = g_dbus_method_invocation_get_sender (context);
ret = (g_strcmp0 (transaction->priv->sender, sender) == 0);
if (ret) {
g_debug ("same sender, no need to check uid");
goto skip_uid;
}
if (transaction->priv->client_uid == PK_TRANSACTION_UID_INVALID) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_CANNOT_CANCEL,
"No context from caller to get UID from");
goto out;
}
if (!pk_dbus_connect (transaction->priv->dbus, &error))
goto out;
uid = pk_dbus_get_uid (transaction->priv->dbus, sender);
if (uid == PK_TRANSACTION_UID_INVALID) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_INVALID_STATE,
"unable to get uid of caller");
goto out;
}
if (transaction->priv->client_uid != uid) {
g_debug ("uid does not match (%i vs. %i)", transaction->priv->client_uid, uid);
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_CANCEL,
&error);
if (!ret)
goto out;
}
skip_uid:
if (transaction->priv->state <= PK_TRANSACTION_STATE_READY) {
g_autofree gchar *msg = NULL;
msg = g_strdup_printf ("%s was cancelled and was never run",
transaction->priv->tid);
pk_transaction_error_code_emit (transaction,
PK_ERROR_ENUM_TRANSACTION_CANCELLED,
msg);
pk_transaction_finished_emit (transaction, PK_EXIT_ENUM_CANCELLED, 0);
goto out;
}
pk_backend_job_set_status (transaction->priv->job, PK_STATUS_ENUM_CANCEL);
pk_backend_job_set_allow_cancel (transaction->priv->job, FALSE);
pk_backend_job_set_exit_code (transaction->priv->job, PK_EXIT_ENUM_CANCELLED);
pk_backend_cancel (transaction->priv->backend, transaction->priv->job);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_download_packages (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
gint retval;
gboolean store_in_cache;
g_autoptr(GError) error = NULL;
g_autofree gchar *directory = NULL;
g_autofree gchar **package_ids = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(b^a&s)",
&store_in_cache,
&package_ids);
package_ids_temp = pk_package_ids_to_string (package_ids);
g_debug ("DownloadPackages method called: %s", package_ids_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_DOWNLOAD_PACKAGES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"DownloadPackages not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid", package_ids_temp);
goto out;
}
if (!store_in_cache) {
directory = g_build_filename (LOCALSTATEDIR, "cache", "PackageKit",
"downloads", transaction->priv->tid, NULL);
retval = g_mkdir_with_parents (directory, 0775);
if (retval != 0) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_DENIED,
"cannot create %s", directory);
goto out;
}
}
transaction->priv->cached_package_ids = g_strdupv (package_ids);
transaction->priv->cached_directory = g_strdup (directory);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_DOWNLOAD_PACKAGES);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_categories (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_debug ("GetCategories method called");
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_CATEGORIES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetCategories not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_CATEGORIES);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_depends_on (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
gchar *package_ids_temp;
PkBitfield filter;
gboolean recursive;
g_autofree gchar **package_ids = NULL;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&sb)",
&filter,
&package_ids,
&recursive);
package_ids_temp = pk_package_ids_to_string (package_ids);
g_debug ("DependsOn method called: %s (recursive %i)", package_ids_temp, recursive);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_DEPENDS_ON)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"DependsOn not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid", package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
transaction->priv->cached_package_ids = g_strdupv (package_ids);
transaction->priv->cached_force = recursive;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_DEPENDS_ON);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_details (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
g_autofree gchar **package_ids = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(^a&s)",
&package_ids);
package_ids_temp = pk_package_ids_to_string (package_ids);
g_debug ("GetDetails method called: %s", package_ids_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_DETAILS)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetDetails not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid",
package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_package_ids = g_strdupv (package_ids);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_DETAILS);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_details_local (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
GError *error_local = NULL;
GError *error = NULL;
guint i;
guint length;
g_autofree gchar *content_type = NULL;
g_autofree gchar *files_temp = NULL;
g_autofree gchar **full_paths = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(^a&s)", &full_paths);
files_temp = pk_package_ids_to_string (full_paths);
g_debug ("GetDetailsLocal method called: %s", files_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_DETAILS_LOCAL)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetDetailsLocal not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
length = g_strv_length (full_paths);
if (length == 0) {
g_set_error_literal (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
"No filenames listed");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
length = g_strv_length (full_paths);
for (i = 0; i < length; i++) {
ret = g_file_test (full_paths[i], G_FILE_TEST_EXISTS);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
content_type = pk_transaction_get_content_type_for_file (full_paths[i],
&error_local);
if (content_type == NULL) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_is_supported_content_type (transaction, content_type);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
}
transaction->priv->cached_full_paths = g_strdupv (full_paths);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_DETAILS_LOCAL);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_files_local (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
GError *error_local = NULL;
guint i;
guint length;
g_autoptr(GError) error = NULL;
g_autofree gchar *content_type = NULL;
g_autofree gchar *files_temp = NULL;
g_autofree gchar **full_paths = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(^a&s)", &full_paths);
files_temp = pk_package_ids_to_string (full_paths);
g_debug ("GetFilesLocal method called: %s", files_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_FILES_LOCAL)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetFilesLocal not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
length = g_strv_length (full_paths);
if (length == 0) {
g_set_error_literal (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
"No filenames listed");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
length = g_strv_length (full_paths);
for (i = 0; i < length; i++) {
ret = g_file_test (full_paths[i], G_FILE_TEST_EXISTS);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
content_type = pk_transaction_get_content_type_for_file (full_paths[i],
&error_local);
if (content_type == NULL) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_is_supported_content_type (transaction, content_type);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
}
transaction->priv->cached_full_paths = g_strdupv (full_paths);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_FILES_LOCAL);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_distro_upgrades (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_debug ("GetDistroUpgrades method called");
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_DISTRO_UPGRADES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetDistroUpgrades not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_DISTRO_UPGRADES);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_files (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
g_autoptr(GError) error = NULL;
g_autofree gchar **package_ids = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(^a&s)",
&package_ids);
package_ids_temp = pk_package_ids_to_string (package_ids);
g_debug ("GetFiles method called: %s", package_ids_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_FILES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetFiles not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid", package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_package_ids = g_strdupv (package_ids);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_FILES);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_packages (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
PkBitfield filter;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t)",
&filter);
g_debug ("GetPackages method called: %" G_GUINT64_FORMAT, filter);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_PACKAGES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetPackages not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_PACKAGES);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_old_transactions (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
const gchar *cmdline;
const gchar *data;
const gchar *modified;
const gchar *tid;
gboolean succeeded;
GList *l;
GList *transactions = NULL;
guint duration;
guint idle_id;
guint number;
guint uid;
PkRoleEnum role;
PkTransactionPast *item;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(u)",
&number);
g_debug ("GetOldTransactions method called");
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_OLD_TRANSACTIONS);
transactions = pk_transaction_db_get_list (transaction->priv->transaction_db, number);
for (l = transactions; l != NULL; l = l->next) {
item = PK_TRANSACTION_PAST (l->data);
pk_results_add_transaction (transaction->priv->results, item);
role = pk_transaction_past_get_role (item);
tid = pk_transaction_past_get_id (item);
modified = pk_transaction_past_get_timespec (item);
succeeded = pk_transaction_past_get_succeeded (item);
duration = pk_transaction_past_get_duration (item);
data = pk_transaction_past_get_data (item);
uid = pk_transaction_past_get_uid (item);
cmdline = pk_transaction_past_get_cmdline (item);
g_debug ("adding transaction %s, %s, %i, %s, %i, %s, %i, %s",
tid, modified, succeeded,
pk_role_enum_to_string (role),
duration, data, uid, cmdline);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Transaction",
g_variant_new ("(osbuusus)",
tid,
modified,
succeeded,
role,
duration,
data != NULL ? data : "",
uid,
cmdline != NULL ? cmdline : ""),
NULL);
}
g_list_free_full (transactions, (GDestroyNotify) g_object_unref);
idle_id = g_idle_add ((GSourceFunc) pk_transaction_finished_idle_cb, transaction);
g_source_set_name_by_id (idle_id, "[PkTransaction] finished from get-old-transactions");
pk_transaction_dbus_return (context, NULL);
}
static void
pk_transaction_get_repo_list (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
PkBitfield filter;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t)",
&filter);
g_debug ("GetRepoList method called");
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_REPO_LIST)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetRepoList not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_REPO_LIST);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_required_by (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield filter;
gboolean recursive;
g_autoptr(GError) error = NULL;
g_autofree gchar **package_ids = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&sb)",
&filter,
&package_ids,
&recursive);
package_ids_temp = pk_package_ids_to_string (package_ids);
g_debug ("RequiredBy method called: %s (recursive %i)", package_ids_temp, recursive);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_REQUIRED_BY)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"RequiredBy not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid", package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
transaction->priv->cached_package_ids = g_strdupv (package_ids);
transaction->priv->cached_force = recursive;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_REQUIRED_BY);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_get_update_detail (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
GError *error = NULL;
g_autofree gchar **package_ids = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(^a&s)",
&package_ids);
package_ids_temp = pk_package_ids_to_string (package_ids);
g_debug ("GetUpdateDetail method called: %s", package_ids_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_UPDATE_DETAIL)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetUpdateDetail not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid",
package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_package_ids = g_strdupv (package_ids);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_UPDATE_DETAIL);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
void
pk_transaction_get_updates (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
PkBitfield filter;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t)",
&filter);
g_debug ("GetUpdates method called");
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_GET_UPDATES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"GetUpdates not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_GET_UPDATES);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static gchar *
pk_transaction_get_content_type_for_file (const gchar *filename, GError **error)
{
g_autoptr(GError) error_local = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(GFileInfo) info = NULL;
file = g_file_new_for_path (filename);
info = g_file_query_info (file, "standard::content-type",
G_FILE_QUERY_INFO_NONE, NULL, &error_local);
if (info == NULL) {
g_set_error (error, 1, 0,
"failed to get file attributes for %s: %s",
filename, error_local->message);
return NULL;
}
return g_file_info_get_attribute_as_string (info, "standard::content-type");
}
static gboolean
pk_transaction_is_supported_content_type (PkTransaction *transaction,
const gchar *content_type)
{
const gchar *tmp;
GPtrArray *array = transaction->priv->supported_content_types;
guint i;
for (i = 0; i < array->len; i++) {
tmp = g_ptr_array_index (array, i);
if (g_strcmp0 (tmp, content_type) == 0)
return TRUE;
}
return FALSE;
}
static void
pk_transaction_install_files (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
GError *error_local = NULL;
guint length;
guint i;
PkBitfield transaction_flags;
g_autoptr(GError) error = NULL;
g_autofree gchar *content_type = NULL;
g_autofree gchar **full_paths = NULL;
g_autofree gchar *full_paths_temp = NULL;
g_autofree gchar *transaction_flags_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&transaction_flags,
&full_paths);
full_paths_temp = pk_package_ids_to_string (full_paths);
transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
g_debug ("InstallFiles method called: %s (transaction_flags: %s)",
full_paths_temp, transaction_flags_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_INSTALL_FILES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"InstallFiles not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
length = g_strv_length (full_paths);
for (i = 0; i < length; i++) {
ret = g_file_test (full_paths[i], G_FILE_TEST_EXISTS);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
content_type = pk_transaction_get_content_type_for_file (full_paths[i], &error_local);
if (content_type == NULL) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_is_supported_content_type (transaction, content_type);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NO_SUCH_FILE,
"File %s is not found or unsupported", full_paths[i]);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
}
transaction->priv->cached_transaction_flags = transaction_flags;
transaction->priv->cached_full_paths = g_strdupv (full_paths);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_INSTALL_FILES);
pk_transaction_emit_property_changed (transaction,
"TransactionFlags",
g_variant_new_uint64 (transaction_flags));
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_INSTALL_FILES,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
void
pk_transaction_install_packages (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield transaction_flags;
g_autoptr(GError) error = NULL;
g_autofree gchar **package_ids = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_autofree gchar *transaction_flags_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&transaction_flags,
&package_ids);
package_ids_temp = pk_package_ids_to_string (package_ids);
transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
g_debug ("InstallPackages method called: %s (transaction_flags: %s)",
package_ids_temp, transaction_flags_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_INSTALL_PACKAGES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"InstallPackages not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid", package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_transaction_flags = transaction_flags;
transaction->priv->cached_package_ids = g_strdupv (package_ids);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_INSTALL_PACKAGES);
pk_transaction_emit_property_changed (transaction,
"TransactionFlags",
g_variant_new_uint64 (transaction_flags));
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_INSTALL_PACKAGES,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_install_signature (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
const gchar *key_id;
const gchar *package_id;
PkSigTypeEnum sig_type;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(u&s&s)",
&sig_type,
&key_id,
&package_id);
g_debug ("InstallSignature method called: %s, %s, %s",
pk_sig_type_enum_to_string (sig_type),
key_id,
package_id);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_INSTALL_SIGNATURE)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"InstallSignature not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_strvalidate (key_id, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_id_check (package_id);
if (!ret && !g_str_has_prefix (package_id, ";;;")) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id '%s' is not valid", package_id);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_package_id = g_strdup (package_id);
transaction->priv->cached_key_id = g_strdup (key_id);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_INSTALL_SIGNATURE);
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_INSTALL_SIGNATURE,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_refresh_cache (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
gboolean force;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(b)",
&force);
g_debug ("RefreshCache method called: %i", force);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_REFRESH_CACHE)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"RefreshCache not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_force = force;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_REFRESH_CACHE);
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_REFRESH_CACHE,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_remove_packages (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
gboolean allow_deps;
gboolean autoremove;
PkBitfield transaction_flags;
g_autoptr(GError) error = NULL;
g_autofree gchar **package_ids = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_autofree gchar *transaction_flags_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&sbb)",
&transaction_flags,
&package_ids,
&allow_deps,
&autoremove);
package_ids_temp = pk_package_ids_to_string (package_ids);
transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
g_debug ("RemovePackages method called: %s, %i, %i (transaction_flags: %s)",
package_ids_temp, allow_deps, autoremove, transaction_flags_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_REMOVE_PACKAGES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"RemovePackages not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid", package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_transaction_flags = transaction_flags;
transaction->priv->cached_package_ids = g_strdupv (package_ids);
transaction->priv->cached_allow_deps = allow_deps;
transaction->priv->cached_autoremove = autoremove;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_REMOVE_PACKAGES);
pk_transaction_emit_property_changed (transaction,
"TransactionFlags",
g_variant_new_uint64 (transaction_flags));
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_REMOVE_PACKAGES,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_repo_enable (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
const gchar *repo_id;
gboolean enabled;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(&sb)",
&repo_id,
&enabled);
g_debug ("RepoEnable method called: %s, %i", repo_id, enabled);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_REPO_ENABLE)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"RepoEnable not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_strvalidate (repo_id, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_repo_id = g_strdup (repo_id);
transaction->priv->cached_enabled = enabled;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_REPO_ENABLE);
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_REPO_ENABLE,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_repo_set_data (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
const gchar *repo_id;
const gchar *parameter;
const gchar *value;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(&s&s&s)",
&repo_id,
¶meter,
&value);
g_debug ("RepoSetData method called: %s, %s, %s",
repo_id, parameter, value);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_REPO_SET_DATA)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"RepoSetData not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_strvalidate (repo_id, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_repo_id = g_strdup (repo_id);
transaction->priv->cached_parameter = g_strdup (parameter);
transaction->priv->cached_value = g_strdup (value);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_REPO_SET_DATA);
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_REPO_SET_DATA,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_repo_remove (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
PkBitfield transaction_flags;
const gchar *repo_id;
gboolean autoremove;
gboolean ret;
g_autoptr(GError) error = NULL;
g_autofree gchar *tmp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t&sb)",
&transaction_flags,
&repo_id,
&autoremove);
tmp = pk_transaction_flag_bitfield_to_string (transaction_flags);
g_debug ("RepoRemove method called: %s, %s, %i",
tmp, repo_id, autoremove);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_REPO_REMOVE)) {
g_set_error_literal (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"RepoSetData not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_strvalidate (repo_id, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_repo_id = g_strdup (repo_id);
transaction->priv->cached_transaction_flags = transaction_flags;
transaction->priv->cached_autoremove = autoremove;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_REPO_REMOVE);
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_REPO_REMOVE,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_resolve (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
guint i;
guint length;
PkBitfield filter;
g_autoptr(GError) error = NULL;
g_autofree gchar **packages = NULL;
g_autofree gchar *packages_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&filter,
&packages);
packages_temp = pk_package_ids_to_string (packages);
g_debug ("Resolve method called: %" G_GUINT64_FORMAT ", %s",
filter, packages_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_RESOLVE)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"Resolve not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
length = g_strv_length (packages);
if (length == 0) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_INPUT_INVALID,
"Too few items to process");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
if (length > PK_TRANSACTION_MAX_ITEMS_TO_RESOLVE) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_INPUT_INVALID,
"Too many items to process (%i/%i)",
length, PK_TRANSACTION_MAX_ITEMS_TO_RESOLVE);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
for (i = 0; i < length; i++) {
ret = pk_transaction_strvalidate (packages[i], &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
}
transaction->priv->cached_package_ids = g_strdupv (packages);
transaction->priv->cached_filters = filter;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_RESOLVE);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
void
pk_transaction_search_details (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield filter;
g_autofree gchar **values = NULL;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&filter,
&values);
g_debug ("SearchDetails method called: %" G_GUINT64_FORMAT ", %s",
filter, values[0]);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_SEARCH_DETAILS)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"SearchDetails not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_search_check (values, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
transaction->priv->cached_values = g_strdupv (values);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_SEARCH_DETAILS);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_search_files (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
guint i;
PkBitfield filter;
g_autofree gchar **values = NULL;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&filter,
&values);
g_debug ("SearchFiles method called: %" G_GUINT64_FORMAT ", %s",
filter, values[0]);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_SEARCH_FILE)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"SearchFiles not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_search_check (values, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
for (i = 0; values[i] != NULL; i++) {
if (values[i][0] != '/' && strstr (values[i], "/") != NULL) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_SEARCH_PATH_INVALID,
"Invalid search path");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
}
transaction->priv->cached_filters = filter;
transaction->priv->cached_values = g_strdupv (values);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_SEARCH_FILE);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_search_groups (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
guint i;
PkBitfield filter;
g_autofree gchar **values = NULL;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&filter,
&values);
g_debug ("SearchGroups method called: %" G_GUINT64_FORMAT ", %s",
filter, values[0]);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_SEARCH_GROUP)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"SearchGroups not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_search_check (values, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
for (i = 0; values[i] != NULL; i++) {
if (strstr (values[i], " ") != NULL) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_SEARCH_INVALID,
"Invalid search containing spaces");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
}
transaction->priv->cached_filters = filter;
transaction->priv->cached_values = g_strdupv (values);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_SEARCH_GROUP);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
void
pk_transaction_search_names (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield filter;
g_autofree gchar **values = NULL;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&filter,
&values);
g_debug ("SearchNames method called: %" G_GUINT64_FORMAT ", %s",
filter, values[0]);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_SEARCH_NAME)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"SearchNames not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_search_check (values, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
transaction->priv->cached_values = g_strdupv (values);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_SEARCH_NAME);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static gboolean
pk_transaction_set_hint (PkTransaction *transaction,
const gchar *key,
const gchar *value,
GError **error)
{
PkTransactionPrivate *priv = transaction->priv;
if (g_strcmp0 (key, "locale") == 0) {
pk_backend_job_set_locale (priv->job, value);
return TRUE;
}
if (g_strcmp0 (key, "frontend-socket") == 0) {
if (value == NULL || value[0] == '\0') {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"Could not set frontend-socket to nothing");
return FALSE;
}
if (value[0] != '/') {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"frontend-socket has to be an absolute path");
return FALSE;
}
if (!g_file_test (value, G_FILE_TEST_EXISTS)) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"frontend-socket does not exist");
return FALSE;
}
pk_backend_job_set_frontend_socket (priv->job, value);
return TRUE;
}
if (g_strcmp0 (key, "background") == 0) {
if (g_strcmp0 (value, "true") == 0) {
pk_backend_job_set_background (priv->job, TRUE);
} else if (g_strcmp0 (value, "false") == 0) {
pk_backend_job_set_background (priv->job, FALSE);
} else {
g_set_error (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"background hint expects true or false, not %s", value);
return FALSE;
}
return TRUE;
}
if (g_strcmp0 (key, "interactive") == 0) {
if (g_strcmp0 (value, "true") == 0) {
pk_backend_job_set_interactive (priv->job, TRUE);
} else if (g_strcmp0 (value, "false") == 0) {
pk_backend_job_set_interactive (priv->job, FALSE);
} else {
g_set_error (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"interactive hint expects true or false, not %s", value);
return FALSE;
}
return TRUE;
}
if (g_strcmp0 (key, "cache-age") == 0) {
guint cache_age;
if (!pk_strtouint (value, &cache_age)) {
g_set_error (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"cannot parse cache age value %s", value);
return FALSE;
}
if (cache_age == 0) {
g_set_error_literal (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"cannot set a cache age of zero");
return FALSE;
}
pk_backend_job_set_cache_age (priv->job, cache_age);
return TRUE;
}
if (g_strcmp0 (key, "details-with-deps-size") == 0) {
if (g_strcmp0 (value, "true") == 0) {
pk_backend_job_set_details_with_deps_size (priv->job, TRUE);
} else if (g_strcmp0 (value, "false") == 0) {
pk_backend_job_set_details_with_deps_size (priv->job, FALSE);
} else {
g_set_error (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"details-with-deps-size hint expects true or false, not %s", value);
return FALSE;
}
return TRUE;
}
if (g_strcmp0 (key, "supports-plural-signals") == 0) {
if (g_strcmp0 (value, "true") != 0) {
g_set_error (error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"supports-plural-signals hint expects true only, not %s", value);
return FALSE;
}
g_debug ("Client has set supports-plural-signals=true");
priv->client_supports_plural_signals = TRUE;
return TRUE;
}
g_warning ("unknown option: %s with value %s", key, value);
return TRUE;
}
static void
pk_transaction_set_hints (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
guint i;
g_autofree gchar **hints = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *dbg = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(^a&s)", &hints);
dbg = g_strjoinv (", ", (gchar**) hints);
g_debug ("SetHints method called: %s", dbg);
for (i = 0; hints[i] != NULL; i++) {
g_auto(GStrv) sections = NULL;
sections = g_strsplit (hints[i], "=", 2);
if (g_strv_length (sections) == 2) {
ret = pk_transaction_set_hint (transaction,
sections[0],
sections[1],
&error);
if (!ret)
goto out;
} else {
g_set_error (&error, PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"Could not parse hint '%s'", hints[i]);
goto out;
}
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_update_packages (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield transaction_flags;
g_autoptr(GError) error = NULL;
g_autofree gchar **package_ids = NULL;
g_autofree gchar *package_ids_temp = NULL;
g_autofree gchar *transaction_flags_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&transaction_flags,
&package_ids);
package_ids_temp = pk_package_ids_to_string (package_ids);
transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
g_debug ("UpdatePackages method called: %s (transaction_flags: %s)",
package_ids_temp, transaction_flags_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_UPDATE_PACKAGES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"UpdatePackages not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_package_ids_check (package_ids);
if (!ret) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_PACKAGE_ID_INVALID,
"The package id's '%s' are not valid",
package_ids_temp);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_transaction_flags = transaction_flags;
transaction->priv->cached_package_ids = g_strdupv (package_ids);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_UPDATE_PACKAGES);
pk_transaction_emit_property_changed (transaction,
"TransactionFlags",
g_variant_new_uint64 (transaction_flags));
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_UPDATE_PACKAGES,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_what_provides (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield filter;
g_autofree gchar **values = NULL;
g_autoptr(GError) error = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t^a&s)",
&filter,
&values);
g_debug ("WhatProvides method called: %s",
values[0]);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_WHAT_PROVIDES)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"WhatProvides not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
ret = pk_transaction_search_check (values, &error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_filters = filter;
transaction->priv->cached_values = g_strdupv (values);
pk_transaction_set_role (transaction, PK_ROLE_ENUM_WHAT_PROVIDES);
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_upgrade_system (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield transaction_flags;
PkUpgradeKindEnum upgrade_kind;
const gchar *distro_id;
g_autoptr(GError) error = NULL;
g_autofree gchar *transaction_flags_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t&su)",
&transaction_flags,
&distro_id,
&upgrade_kind);
transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
g_debug ("UpgradeSystem method called: %s: %s (transaction_flags: %s)",
distro_id,
pk_upgrade_kind_enum_to_string (upgrade_kind),
transaction_flags_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_UPGRADE_SYSTEM)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"UpgradeSystem not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_transaction_flags = transaction_flags;
transaction->priv->cached_value = g_strdup (distro_id);
transaction->priv->cached_upgrade_kind = upgrade_kind;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_UPGRADE_SYSTEM);
pk_transaction_emit_property_changed (transaction,
"TransactionFlags",
g_variant_new_uint64 (transaction_flags));
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_UPGRADE_SYSTEM,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static void
pk_transaction_repair_system (PkTransaction *transaction,
GVariant *params,
GDBusMethodInvocation *context)
{
gboolean ret;
PkBitfield transaction_flags;
g_autoptr(GError) error = NULL;
g_autofree gchar *transaction_flags_temp = NULL;
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_return_if_fail (transaction->priv->tid != NULL);
g_variant_get (params, "(t)", &transaction_flags);
transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags);
g_debug ("RepairSystem method called (transaction_flags: %s)",
transaction_flags_temp);
if (!pk_backend_is_implemented (transaction->priv->backend,
PK_ROLE_ENUM_REPAIR_SYSTEM)) {
g_set_error (&error,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_NOT_SUPPORTED,
"RepairSystem not supported by backend");
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
transaction->priv->cached_transaction_flags = transaction_flags;
pk_transaction_set_role (transaction, PK_ROLE_ENUM_REPAIR_SYSTEM);
pk_transaction_emit_property_changed (transaction,
"TransactionFlags",
g_variant_new_uint64 (transaction_flags));
ret = pk_transaction_obtain_authorization (transaction,
PK_ROLE_ENUM_REPAIR_SYSTEM,
&error);
if (!ret) {
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_ERROR);
goto out;
}
out:
pk_transaction_dbus_return (context, error);
}
static GVariant *
_g_variant_new_maybe_string (const gchar *value)
{
if (value == NULL)
return g_variant_new_string ("");
return g_variant_new_string (value);
}
static GVariant *
pk_transaction_get_property (GDBusConnection *connection_, const gchar *sender,
const gchar *object_path, const gchar *interface_name,
const gchar *property_name, GError **error,
gpointer user_data)
{
PkTransaction *transaction = PK_TRANSACTION (user_data);
PkTransactionPrivate *priv = transaction->priv;
flush_progress_changed (transaction);
if (g_strcmp0 (property_name, "Role") == 0)
return g_variant_new_uint32 (priv->role);
if (g_strcmp0 (property_name, "Status") == 0)
return g_variant_new_uint32 (priv->status);
if (g_strcmp0 (property_name, "LastPackage") == 0)
return _g_variant_new_maybe_string (priv->last_package_id);
if (g_strcmp0 (property_name, "Uid") == 0)
return g_variant_new_uint32 (priv->client_uid);
if (g_strcmp0 (property_name, "Sender") == 0)
return _g_variant_new_maybe_string (priv->sender);
if (g_strcmp0 (property_name, "Percentage") == 0)
return g_variant_new_uint32 (transaction->priv->percentage);
if (g_strcmp0 (property_name, "AllowCancel") == 0)
return g_variant_new_boolean (priv->allow_cancel);
if (g_strcmp0 (property_name, "CallerActive") == 0)
return g_variant_new_boolean (priv->caller_active);
if (g_strcmp0 (property_name, "ElapsedTime") == 0)
return g_variant_new_uint32 (priv->elapsed_time);
if (g_strcmp0 (property_name, "Speed") == 0)
return g_variant_new_uint32 (priv->speed);
if (g_strcmp0 (property_name, "DownloadSizeRemaining") == 0)
return g_variant_new_uint64 (priv->download_size_remaining);
if (g_strcmp0 (property_name, "TransactionFlags") == 0)
return g_variant_new_uint64 (priv->cached_transaction_flags);
if (g_strcmp0 (property_name, "RemainingTime") == 0)
return g_variant_new_uint32 (priv->remaining_time);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY,
"Unknown transaction property ‘%s’", property_name);
return NULL;
}
static void
pk_transaction_method_call (GDBusConnection *connection_, const gchar *sender,
const gchar *object_path, const gchar *interface_name,
const gchar *method_name, GVariant *parameters,
GDBusMethodInvocation *invocation, gpointer user_data)
{
PkTransaction *transaction = PK_TRANSACTION (user_data);
g_return_if_fail (transaction->priv->sender != NULL);
if (g_strcmp0 (transaction->priv->sender, sender) != 0) {
g_dbus_method_invocation_return_error (invocation,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_REFUSED_BY_POLICY,
"sender does not match (%s vs %s)",
sender,
transaction->priv->sender);
return;
}
if (g_strcmp0 (method_name, "SetHints") == 0) {
pk_transaction_set_hints (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "AcceptEula") == 0) {
pk_transaction_accept_eula (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "Cancel") == 0) {
pk_transaction_cancel (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "DownloadPackages") == 0) {
pk_transaction_download_packages (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetCategories") == 0) {
pk_transaction_get_categories (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "DependsOn") == 0) {
pk_transaction_depends_on (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetDetails") == 0) {
pk_transaction_get_details (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetDetailsLocal") == 0) {
pk_transaction_get_details_local (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetFilesLocal") == 0) {
pk_transaction_get_files_local (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetFiles") == 0) {
pk_transaction_get_files (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetOldTransactions") == 0) {
pk_transaction_get_old_transactions (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetPackages") == 0) {
pk_transaction_get_packages (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetRepoList") == 0) {
pk_transaction_get_repo_list (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "RequiredBy") == 0) {
pk_transaction_required_by (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetUpdateDetail") == 0) {
pk_transaction_get_update_detail (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetUpdates") == 0) {
pk_transaction_get_updates (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "GetDistroUpgrades") == 0) {
pk_transaction_get_distro_upgrades (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "InstallFiles") == 0) {
pk_transaction_install_files (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "InstallPackages") == 0) {
pk_transaction_install_packages (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "InstallSignature") == 0) {
pk_transaction_install_signature (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "RefreshCache") == 0) {
pk_transaction_refresh_cache (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "RemovePackages") == 0) {
pk_transaction_remove_packages (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "RepoEnable") == 0) {
pk_transaction_repo_enable (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "RepoSetData") == 0) {
pk_transaction_repo_set_data (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "RepoRemove") == 0) {
pk_transaction_repo_remove (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "Resolve") == 0) {
pk_transaction_resolve (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "SearchDetails") == 0) {
pk_transaction_search_details (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "SearchFiles") == 0) {
pk_transaction_search_files (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "SearchGroups") == 0) {
pk_transaction_search_groups (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "SearchNames") == 0) {
pk_transaction_search_names (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "UpdatePackages") == 0) {
pk_transaction_update_packages (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "WhatProvides") == 0) {
pk_transaction_what_provides (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "UpgradeSystem") == 0) {
pk_transaction_upgrade_system (transaction, parameters, invocation);
return;
}
if (g_strcmp0 (method_name, "RepairSystem") == 0) {
pk_transaction_repair_system (transaction, parameters, invocation);
return;
}
g_dbus_method_invocation_return_error (invocation,
PK_TRANSACTION_ERROR,
PK_TRANSACTION_ERROR_INVALID_STATE,
"method from %s not recognised",
sender);
}
gboolean
pk_transaction_set_tid (PkTransaction *transaction, const gchar *tid)
{
static const GDBusInterfaceVTable interface_vtable = {
.method_call = pk_transaction_method_call,
.get_property = pk_transaction_get_property,
.set_property = NULL
};
g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
g_return_val_if_fail (tid != NULL, FALSE);
g_return_val_if_fail (transaction->priv->tid == NULL, FALSE);
transaction->priv->tid = g_strdup (tid);
transaction->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
g_assert (transaction->priv->connection != NULL);
transaction->priv->registration_id =
g_dbus_connection_register_object (transaction->priv->connection,
tid,
transaction->priv->introspection->interfaces[0],
&interface_vtable,
transaction,
NULL,
NULL);
g_assert (transaction->priv->registration_id > 0);
return TRUE;
}
void
pk_transaction_reset_after_lock_error (PkTransaction *transaction)
{
PkTransactionPrivate *priv = PK_TRANSACTION_GET_PRIVATE (transaction);
g_return_if_fail (PK_IS_TRANSACTION (transaction));
g_object_unref (priv->results);
priv->results = pk_results_new ();
priv->state = PK_TRANSACTION_STATE_READY;
pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
g_debug ("transaction has been reset after lock-required issue.");
}
static void
pk_transaction_class_init (PkTransactionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = pk_transaction_dispose;
object_class->finalize = pk_transaction_finalize;
signals[SIGNAL_FINISHED] =
g_signal_new ("finished",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[SIGNAL_STATE_CHANGED] =
g_signal_new ("state-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
signals[SIGNAL_ALLOW_CANCEL_CHANGED] =
g_signal_new ("allow-cancel-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
g_type_class_add_private (klass, sizeof (PkTransactionPrivate));
}
static void
pk_transaction_init (PkTransaction *transaction)
{
gboolean ret;
g_autoptr(GError) error = NULL;
transaction->priv = PK_TRANSACTION_GET_PRIVATE (transaction);
transaction->priv->allow_cancel = TRUE;
transaction->priv->caller_active = TRUE;
transaction->priv->cached_transaction_flags = PK_TRANSACTION_FLAG_ENUM_NONE;
transaction->priv->cached_filters = PK_FILTER_ENUM_NONE;
transaction->priv->client_uid = PK_TRANSACTION_UID_INVALID;
transaction->priv->client_pid = PK_TRANSACTION_PID_INVALID;
transaction->priv->role = PK_ROLE_ENUM_UNKNOWN;
transaction->priv->status = PK_STATUS_ENUM_WAIT;
transaction->priv->percentage = PK_BACKEND_PERCENTAGE_INVALID;
transaction->priv->state = PK_TRANSACTION_STATE_UNKNOWN;
transaction->priv->dbus = pk_dbus_new ();
transaction->priv->results = pk_results_new ();
transaction->priv->supported_content_types = g_ptr_array_new_with_free_func (g_free);
transaction->priv->cancellable = g_cancellable_new ();
transaction->priv->transaction_db = pk_transaction_db_new ();
ret = pk_transaction_db_load (transaction->priv->transaction_db, &error);
if (!ret)
g_error ("PkEngine: failed to load transaction db: %s", error->message);
}
static void
pk_transaction_dispose (GObject *object)
{
PkTransaction *transaction;
g_return_if_fail (PK_IS_TRANSACTION (object));
transaction = PK_TRANSACTION (object);
if (transaction->priv->waiting_for_auth) {
g_cancellable_cancel (transaction->priv->cancellable);
pk_transaction_error_code_emit (transaction, PK_ERROR_ENUM_NOT_AUTHORIZED, "client did not authorize action");
pk_transaction_finished_emit (transaction, PK_EXIT_ENUM_FAILED, 0);
}
if (transaction->priv->registration_id > 0) {
if (transaction->priv->state != PK_TRANSACTION_STATE_UNKNOWN &&
transaction->priv->state != PK_TRANSACTION_STATE_ERROR &&
transaction->priv->state != PK_TRANSACTION_STATE_NEW)
g_assert (transaction->priv->emitted_finished);
g_dbus_connection_unregister_object (transaction->priv->connection,
transaction->priv->registration_id);
transaction->priv->registration_id = 0;
}
unschedule_progress_changed (transaction);
if (transaction->priv->connection != NULL) {
g_debug ("emitting destroy %s", transaction->priv->tid);
g_dbus_connection_emit_signal (transaction->priv->connection,
NULL,
transaction->priv->tid,
PK_DBUS_INTERFACE_TRANSACTION,
"Destroy",
NULL,
NULL);
}
G_OBJECT_CLASS (pk_transaction_parent_class)->dispose (object);
}
static void
pk_transaction_finalize (GObject *object)
{
PkTransaction *transaction;
g_return_if_fail (PK_IS_TRANSACTION (object));
transaction = PK_TRANSACTION (object);
if (transaction->priv->subject != NULL)
g_object_unref (transaction->priv->subject);
if (transaction->priv->watch_id > 0)
g_bus_unwatch_name (transaction->priv->watch_id);
g_free (transaction->priv->last_package_id);
g_free (transaction->priv->cached_package_id);
g_free (transaction->priv->cached_key_id);
g_strfreev (transaction->priv->cached_package_ids);
g_free (transaction->priv->cached_transaction_id);
g_free (transaction->priv->cached_directory);
g_strfreev (transaction->priv->cached_values);
g_free (transaction->priv->cached_repo_id);
g_free (transaction->priv->cached_parameter);
g_free (transaction->priv->cached_value);
g_free (transaction->priv->tid);
g_free (transaction->priv->sender);
g_free (transaction->priv->cmdline);
g_ptr_array_unref (transaction->priv->supported_content_types);
if (transaction->priv->connection != NULL)
g_object_unref (transaction->priv->connection);
if (transaction->priv->introspection != NULL)
g_dbus_node_info_unref (transaction->priv->introspection);
g_key_file_unref (transaction->priv->conf);
g_object_unref (transaction->priv->dbus);
if (transaction->priv->backend != NULL)
g_object_unref (transaction->priv->backend);
g_object_unref (transaction->priv->job);
g_object_unref (transaction->priv->transaction_db);
g_object_unref (transaction->priv->results);
if (transaction->priv->authority != NULL)
g_object_unref (transaction->priv->authority);
g_object_unref (transaction->priv->cancellable);
G_OBJECT_CLASS (pk_transaction_parent_class)->finalize (object);
}
PkTransaction *
pk_transaction_new (GKeyFile *conf, GDBusNodeInfo *introspection)
{
PkTransaction *transaction;
transaction = g_object_new (PK_TYPE_TRANSACTION, NULL);
transaction->priv->conf = g_key_file_ref (conf);
transaction->priv->job = pk_backend_job_new (conf);
transaction->priv->introspection = g_dbus_node_info_ref (introspection);
return PK_TRANSACTION (transaction);
}