fwupd/libfwupd/fwupd-client.c
Richard Hughes 76cafb29b7 Fix a critical warning when downloading files
The call to g_proxy_resolver_get_default() is (transfer none) and so unref'ing
it causes GLib to get very upset. Also, move it to be per-FwupdClient rather
than per-request as it can be expensive to call in the PAC-runner case.
2021-04-30 11:19:09 +01:00

5109 lines
148 KiB
C

/*
* Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <glib-object.h>
#include <gio/gio.h>
#include <gmodule.h>
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#endif
#ifdef HAVE_GIO_UNIX
#include <gio/gunixfdlist.h>
#endif
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "fwupd-client-private.h"
#include "fwupd-client-sync.h"
#include "fwupd-common-private.h"
#include "fwupd-deprecated.h"
#include "fwupd-enums.h"
#include "fwupd-error.h"
#include "fwupd-device-private.h"
#include "fwupd-plugin-private.h"
#include "fwupd-security-attr-private.h"
#include "fwupd-release-private.h"
#include "fwupd-remote-private.h"
typedef GObject *(*FwupdClientObjectNewFunc) (void);
/**
* SECTION:fwupd-client
* @short_description: a way of interfacing with the daemon
*
* An object that allows client code to call the daemon methods synchronously.
*
* See also: #FwupdDevice
*/
static void fwupd_client_finalize (GObject *object);
typedef struct {
GMainContext *main_ctx;
FwupdStatus status;
gboolean tainted;
gboolean interactive;
guint percentage;
GMutex idle_mutex; /* for @idle_id and @idle_sources */
guint idle_id;
GPtrArray *idle_sources; /* element-type FwupdClientContextHelper */
gchar *daemon_version;
gchar *host_product;
gchar *host_machine_id;
gchar *host_security_id;
GMutex proxy_mutex; /* for @proxy */
GDBusProxy *proxy;
GProxyResolver *proxy_resolver;
gchar *user_agent;
#ifdef SOUP_SESSION_COMPAT
GObject *soup_session;
GModule *soup_module; /* we leak this */
#endif
} FwupdClientPrivate;
#ifdef HAVE_LIBCURL
typedef struct {
GPtrArray *urls;
CURL *curl;
curl_mime *mime;
struct curl_slist *headers;
} FwupdCurlHelper;
#endif
enum {
SIGNAL_CHANGED,
SIGNAL_STATUS_CHANGED,
SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED,
SIGNAL_DEVICE_CHANGED,
SIGNAL_LAST
};
enum {
PROP_0,
PROP_STATUS,
PROP_PERCENTAGE,
PROP_DAEMON_VERSION,
PROP_TAINTED,
PROP_SOUP_SESSION, /* compat ABI, do not use! */
PROP_HOST_PRODUCT,
PROP_HOST_MACHINE_ID,
PROP_HOST_SECURITY_ID,
PROP_INTERACTIVE,
PROP_LAST
};
static guint signals [SIGNAL_LAST] = { 0 };
G_DEFINE_TYPE_WITH_PRIVATE (FwupdClient, fwupd_client, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fwupd_client_get_instance_private (o))
#ifdef HAVE_LIBCURL_7_62_0
G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup)
#endif
#ifdef HAVE_LIBCURL
static void
fwupd_client_curl_helper_free (FwupdCurlHelper *helper)
{
if (helper->curl != NULL)
curl_easy_cleanup (helper->curl);
if (helper->mime != NULL)
curl_mime_free (helper->mime);
if (helper->headers != NULL)
curl_slist_free_all (helper->headers);
if (helper->urls != NULL)
g_ptr_array_unref (helper->urls);
g_free (helper);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdCurlHelper, fwupd_client_curl_helper_free)
#endif
typedef struct {
FwupdClient *self;
gchar *property_name;
guint signal_id;
FwupdDevice *device;
} FwupdClientContextHelper;
static void
fwupd_client_context_helper_free (FwupdClientContextHelper *helper)
{
g_clear_object (&helper->device);
g_object_unref (helper->self);
g_free (helper->property_name);
g_free (helper);
}
/* always executed in the main context given by priv->main_ctx */
static void
fwupd_client_context_object_notify (FwupdClient *self, const gchar *property_name)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (g_main_context_is_owner (priv->main_ctx));
/* property */
g_object_notify (G_OBJECT (self), property_name);
/* legacy signal name */
if (g_strcmp0 (property_name, "status") == 0)
g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, priv->status);
}
/* emits all pending context helpers in the correct GMainContext */
static gboolean
fwupd_client_context_idle_cb (gpointer user_data)
{
FwupdClient *self = FWUPD_CLIENT (user_data);
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->idle_mutex);
g_assert (locker != NULL);
for (guint i = 0; i < priv->idle_sources->len; i++) {
FwupdClientContextHelper *helper = g_ptr_array_index (priv->idle_sources, i);
/* property */
if (helper->property_name != NULL)
fwupd_client_context_object_notify (self, helper->property_name);
/* device signal */
if (helper->signal_id !=0 && helper->device != NULL)
g_signal_emit (self, signals[helper->signal_id], 0, helper->device);
}
/* all done */
g_ptr_array_set_size (priv->idle_sources, 0);
priv->idle_id = 0;
return G_SOURCE_REMOVE;
}
static void
fwupd_client_context_helper (FwupdClient *self, FwupdClientContextHelper *helper)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->idle_mutex);
g_assert (locker != NULL);
/* no source already attached to the context */
if (priv->idle_id == 0) {
g_autoptr(GSource) source = g_idle_source_new ();
g_source_set_callback (source, fwupd_client_context_idle_cb, self, NULL);
priv->idle_id = g_source_attach (g_steal_pointer (&source), priv->main_ctx);
}
/* run in the correct GMainContext and thread */
g_ptr_array_add (priv->idle_sources, helper);
}
/* run callback in the correct thread */
static void
fwupd_client_object_notify (FwupdClient *self, const gchar *property_name)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
FwupdClientContextHelper *helper = NULL;
/* shortcut */
if (g_main_context_is_owner (priv->main_ctx)) {
fwupd_client_context_object_notify (self, property_name);
return;
}
/* run in the correct GMainContext and thread */
helper = g_new0 (FwupdClientContextHelper, 1);
helper->self = g_object_ref (self);
helper->property_name = g_strdup (property_name);
fwupd_client_context_helper (self, helper);
}
/* run callback in the correct thread */
static void
fwupd_client_signal_emit_device (FwupdClient *self, guint signal_id, FwupdDevice *device)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
FwupdClientContextHelper *helper = NULL;
/* shortcut */
if (g_main_context_is_owner (priv->main_ctx)) {
g_signal_emit (self, signals[signal_id], 0, device);
return;
}
/* run in the correct GMainContext and thread */
helper = g_new0 (FwupdClientContextHelper, 1);
helper->self = g_object_ref (self);
helper->signal_id = signal_id;
helper->device = g_object_ref (device);
fwupd_client_context_helper (self, helper);
}
static void
fwupd_client_set_host_product (FwupdClient *self, const gchar *host_product)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
/* not changed */
if (g_strcmp0 (priv->host_product, host_product) == 0)
return;
g_free (priv->host_product);
priv->host_product = g_strdup (host_product);
fwupd_client_object_notify (self, "host-product");
}
static void
fwupd_client_set_host_machine_id (FwupdClient *self, const gchar *host_machine_id)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
/* not changed */
if (g_strcmp0 (priv->host_machine_id, host_machine_id) == 0)
return;
g_free (priv->host_machine_id);
priv->host_machine_id = g_strdup (host_machine_id);
fwupd_client_object_notify (self, "host-machine-id");
}
static void
fwupd_client_set_host_security_id (FwupdClient *self, const gchar *host_security_id)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
/* not changed */
if (g_strcmp0 (priv->host_security_id, host_security_id) == 0)
return;
g_free (priv->host_security_id);
priv->host_security_id = g_strdup (host_security_id);
fwupd_client_object_notify (self, "host-security-id");
}
static void
fwupd_client_set_daemon_version (FwupdClient *self, const gchar *daemon_version)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
/* not changed */
if (g_strcmp0 (priv->daemon_version, daemon_version) == 0)
return;
g_free (priv->daemon_version);
priv->daemon_version = g_strdup (daemon_version);
fwupd_client_object_notify (self, "daemon-version");
}
static void
fwupd_client_set_status (FwupdClient *self, FwupdStatus status)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
if (priv->status == status)
return;
priv->status = status;
g_debug ("Emitting ::status-changed() [%s]",
fwupd_status_to_string (priv->status));
fwupd_client_object_notify (self, "status");
}
static void
fwupd_client_set_percentage (FwupdClient *self, guint percentage)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
if (priv->percentage == percentage)
return;
priv->percentage = percentage;
fwupd_client_object_notify (self, "percentage");
}
static void
fwupd_client_properties_changed_cb (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GVariantDict) dict = NULL;
/* print to the console */
dict = g_variant_dict_new (changed_properties);
if (g_variant_dict_contains (dict, "Status")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "Status");
if (val != NULL)
fwupd_client_set_status (self, g_variant_get_uint32 (val));
}
if (g_variant_dict_contains (dict, "Tainted")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "Tainted");
if (val != NULL) {
priv->tainted = g_variant_get_boolean (val);
fwupd_client_object_notify (self, "tainted");
}
}
if (g_variant_dict_contains (dict, "Interactive")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "Interactive");
if (val != NULL) {
priv->interactive = g_variant_get_boolean (val);
fwupd_client_object_notify (self, "interactive");
}
}
if (g_variant_dict_contains (dict, "Percentage")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "Percentage");
if (val != NULL)
fwupd_client_set_percentage (self, g_variant_get_uint32 (val));
}
if (g_variant_dict_contains (dict, "DaemonVersion")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "DaemonVersion");
if (val != NULL)
fwupd_client_set_daemon_version (self, g_variant_get_string (val, NULL));
}
if (g_variant_dict_contains (dict, "HostProduct")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "HostProduct");
if (val != NULL)
fwupd_client_set_host_product (self, g_variant_get_string (val, NULL));
}
if (g_variant_dict_contains (dict, "HostMachineId")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "HostMachineId");
if (val != NULL)
fwupd_client_set_host_machine_id (self, g_variant_get_string (val, NULL));
}
if (g_variant_dict_contains (dict, "HostSecurityId")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "HostSecurityId");
if (val != NULL)
fwupd_client_set_host_security_id (self, g_variant_get_string (val, NULL));
}
}
static void
fwupd_client_signal_cb (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
FwupdClient *self)
{
g_autoptr(FwupdDevice) dev = NULL;
if (g_strcmp0 (signal_name, "Changed") == 0) {
g_debug ("Emitting ::changed()");
g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
return;
}
if (g_strcmp0 (signal_name, "DeviceAdded") == 0) {
dev = fwupd_device_from_variant (parameters);
g_debug ("Emitting ::device-added(%s)",
fwupd_device_get_id (dev));
fwupd_client_signal_emit_device (self, SIGNAL_DEVICE_ADDED, dev);
return;
}
if (g_strcmp0 (signal_name, "DeviceRemoved") == 0) {
dev = fwupd_device_from_variant (parameters);
g_debug ("Emitting ::device-removed(%s)",
fwupd_device_get_id (dev));
fwupd_client_signal_emit_device (self, SIGNAL_DEVICE_REMOVED, dev);
return;
}
if (g_strcmp0 (signal_name, "DeviceChanged") == 0) {
dev = fwupd_device_from_variant (parameters);
g_debug ("Emitting ::device-changed(%s)",
fwupd_device_get_id (dev));
fwupd_client_signal_emit_device (self, SIGNAL_DEVICE_CHANGED, dev);
return;
}
g_debug ("Unknown signal name '%s' from %s", signal_name, sender_name);
}
/**
* fwupd_client_get_main_context:
* @self: A #FwupdClient
*
* Gets the internal #GMainContext to use for synchronous methods.
* By default the value is set a new #GMainContext.
*
* Returns: (transfer full): the #GMainContext
*
* Since: 1.5.3
**/
GMainContext *
fwupd_client_get_main_context (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
if (priv->main_ctx != NULL)
return g_main_context_ref (priv->main_ctx);
return g_main_context_new ();
}
/**
* fwupd_client_set_main_context:
* @self: A #FwupdClient
* @main_ctx: (nullable): #GMainContext or %NULL to use the global default main context
*
* Sets the internal #GMainContext to use for returning progress signals.
*
* Since: 1.5.3
**/
void
fwupd_client_set_main_context (FwupdClient *self, GMainContext *main_ctx)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
if (main_ctx == priv->main_ctx)
return;
g_clear_pointer (&priv->main_ctx, g_main_context_unref);
if (main_ctx != NULL)
priv->main_ctx = g_main_context_ref (main_ctx);
}
/**
* fwupd_client_ensure_networking:
* @self: A #FwupdClient
* @error: (nullable): optional return location for an error
*
* Sets up the client networking support ready for use. Most other download and
* upload methods call this automatically, and do you only need to call this if
* the session is being used outside the #FwupdClient.
*
* Returns: %TRUE for success
*
* Since: 1.4.5
**/
gboolean
fwupd_client_ensure_networking (FwupdClient *self, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* check the user agent is sane */
if (priv->user_agent == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"user agent unset");
return FALSE;
}
if (g_strstr_len (priv->user_agent, -1, "fwupd/") == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"user agent unsuitable; fwupd version required");
return FALSE;
}
#ifdef SOUP_SESSION_COMPAT
if (priv->soup_session != NULL) {
g_object_set (priv->soup_session,
"user-agent", priv->user_agent,
NULL);
}
#endif
return TRUE;
}
#ifdef HAVE_LIBCURL
static int
fwupd_client_progress_callback_cb (void *clientp,
curl_off_t dltotal,
curl_off_t dlnow,
curl_off_t ultotal,
curl_off_t ulnow)
{
FwupdClient *self = FWUPD_CLIENT (clientp);
/* calculate percentage */
if (dltotal > 0 && dlnow >= 0 && dlnow <= dltotal) {
guint percentage = (guint) ((100 * dlnow) / dltotal);
g_debug ("download progress: %u%%", percentage);
fwupd_client_set_percentage (self, percentage);
} else if (ultotal > 0 && ulnow >= 0 && ulnow <= ultotal) {
guint percentage = (guint) ((100 * ulnow) / ultotal);
g_debug ("upload progress: %u%%", percentage);
fwupd_client_set_percentage (self, percentage);
}
return 0;
}
static void
fwupd_client_curl_helper_set_proxy (FwupdClient *self,
FwupdCurlHelper *helper,
const gchar *url)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_auto(GStrv) proxies = NULL;
g_autoptr(GError) error_local = NULL;
proxies = g_proxy_resolver_lookup (priv->proxy_resolver, url,
NULL, &error_local);
if (proxies == NULL) {
g_warning ("failed to lookup proxy for %s: %s",
url, error_local->message);
return;
}
if (g_strcmp0 (proxies[0], "direct://") != 0)
curl_easy_setopt (helper->curl, CURLOPT_PROXY, proxies[0]);
}
static FwupdCurlHelper *
fwupd_client_curl_new (FwupdClient *self, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(FwupdCurlHelper) helper = g_new0 (FwupdCurlHelper, 1);
/* check the user agent is sane */
if (!fwupd_client_ensure_networking (self, error))
return NULL;
/* create the session */
helper->curl = curl_easy_init ();
if (helper->curl == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to setup networking");
return NULL;
}
if (g_getenv ("FWUPD_CURL_VERBOSE") != NULL)
curl_easy_setopt (helper->curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt (helper->curl, CURLOPT_XFERINFOFUNCTION, fwupd_client_progress_callback_cb);
curl_easy_setopt (helper->curl, CURLOPT_XFERINFODATA, self);
curl_easy_setopt (helper->curl, CURLOPT_USERAGENT, priv->user_agent);
curl_easy_setopt (helper->curl, CURLOPT_CONNECTTIMEOUT, 60L);
curl_easy_setopt (helper->curl, CURLOPT_NOPROGRESS, 0L);
/* relax the SSL checks for broken corporate proxies */
if (g_getenv ("DISABLE_SSL_STRICT") != NULL)
curl_easy_setopt (helper->curl, CURLOPT_SSL_VERIFYPEER, 0L);
/* this disables the double-compression of the firmware.xml.gz file */
curl_easy_setopt (helper->curl, CURLOPT_HTTP_CONTENT_DECODING, 0L);
return g_steal_pointer (&helper);
}
#endif
static void
fwupd_client_connect_get_proxy_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
FwupdClient *self = g_task_get_source_object (task);
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GDBusProxy) proxy = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
g_autoptr(GVariant) val2 = NULL;
g_autoptr(GVariant) val3 = NULL;
g_autoptr(GVariant) val4 = NULL;
g_autoptr(GVariant) val5 = NULL;
g_autoptr(GVariant) val6 = NULL;
g_autoptr(GVariant) val7 = NULL;
g_autoptr(GMutexLocker) locker = NULL;
proxy = g_dbus_proxy_new_finish (res, &error);
if (proxy == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* another thread did this for us */
locker = g_mutex_locker_new (&priv->proxy_mutex);
if (priv->proxy != NULL) {
g_task_return_boolean (task, TRUE);
return;
}
priv->proxy = g_steal_pointer (&proxy);
/* connect signals, etc. */
g_signal_connect (priv->proxy, "g-properties-changed",
G_CALLBACK (fwupd_client_properties_changed_cb), self);
g_signal_connect (priv->proxy, "g-signal",
G_CALLBACK (fwupd_client_signal_cb), self);
val = g_dbus_proxy_get_cached_property (priv->proxy, "DaemonVersion");
if (val != NULL)
fwupd_client_set_daemon_version (self, g_variant_get_string (val, NULL));
val2 = g_dbus_proxy_get_cached_property (priv->proxy, "Tainted");
if (val2 != NULL)
priv->tainted = g_variant_get_boolean (val2);
val3 = g_dbus_proxy_get_cached_property (priv->proxy, "Status");
if (val3 != NULL)
fwupd_client_set_status (self, g_variant_get_uint32 (val3));
val4 = g_dbus_proxy_get_cached_property (priv->proxy, "Interactive");
if (val4 != NULL)
priv->interactive = g_variant_get_boolean (val4);
val5 = g_dbus_proxy_get_cached_property (priv->proxy, "HostProduct");
if (val5 != NULL)
fwupd_client_set_host_product (self, g_variant_get_string (val5, NULL));
val6 = g_dbus_proxy_get_cached_property (priv->proxy, "HostMachineId");
if (val6 != NULL)
fwupd_client_set_host_machine_id (self, g_variant_get_string (val6, NULL));
val7 = g_dbus_proxy_get_cached_property (priv->proxy, "HostSecurityId");
if (val7 != NULL)
fwupd_client_set_host_security_id (self, g_variant_get_string (val7, NULL));
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_connect_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Sets up the client ready for use. This is probably the first method you call
* when wanting to use libfwupd in an asynchronous manner.
*
* Other methods such as fwupd_client_get_devices_async() should only be called
* after fwupd_client_connect_finish() has been called without an error.
*
* Since: 1.5.0
**/
void
fwupd_client_connect_async (FwupdClient *self, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->proxy_mutex);
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_assert (locker != NULL);
/* nothing to do */
if (priv->proxy != NULL) {
g_task_return_boolean (task, TRUE);
return;
}
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
FWUPD_DBUS_SERVICE,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
cancellable,
fwupd_client_connect_get_proxy_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_connect_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_connect_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_connect_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_fixup_dbus_error (GError *error)
{
g_autofree gchar *name = NULL;
g_return_if_fail (error != NULL);
/* is a remote error? */
if (!g_dbus_error_is_remote_error (error))
return;
/* parse the remote error */
name = g_dbus_error_get_remote_error (error);
if (g_str_has_prefix (name, FWUPD_DBUS_INTERFACE)) {
error->domain = FWUPD_ERROR;
error->code = fwupd_error_from_string (name);
} else if (g_error_matches (error,
G_DBUS_ERROR,
G_DBUS_ERROR_SERVICE_UNKNOWN)) {
error->domain = FWUPD_ERROR;
error->code = FWUPD_ERROR_NOT_SUPPORTED;
} else if (g_error_matches (error,
G_IO_ERROR,
G_IO_ERROR_DBUS_ERROR)) {
error->domain = FWUPD_ERROR;
error->code = FWUPD_ERROR_NOT_SUPPORTED;
} else {
error->domain = FWUPD_ERROR;
error->code = FWUPD_ERROR_INTERNAL;
}
g_dbus_error_strip_remote_error (error);
}
static void
fwupd_client_get_host_security_attrs_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_security_attr_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_host_security_attrs_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the host security attributes from the daemon.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_host_security_attrs_async (FwupdClient *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetHostSecurityAttrs",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_host_security_attrs_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_host_security_attrs_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_host_security_attrs_async().
*
* Returns: (element-type FwupdSecurityAttr) (transfer container): attributes
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_host_security_attrs_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static GHashTable *
fwupd_report_metadata_hash_from_variant (GVariant *value)
{
GHashTable *hash;
gsize sz;
g_autoptr(GVariant) untuple = NULL;
hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
untuple = g_variant_get_child_value (value, 0);
sz = g_variant_n_children (untuple);
for (guint i = 0; i < sz; i++) {
g_autoptr(GVariant) data = NULL;
const gchar *key = NULL;
const gchar *val = NULL;
data = g_variant_get_child_value (untuple, i);
g_variant_get (data, "{&s&s}", &key, &val);
g_hash_table_insert (hash, g_strdup (key), g_strdup (val));
}
return hash;
}
static void
fwupd_client_get_report_metadata_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_report_metadata_hash_from_variant (val),
(GDestroyNotify) g_hash_table_unref);
}
/**
* fwupd_client_get_report_metadata_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the report metadata from the daemon.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_report_metadata_async (FwupdClient *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetReportMetadata",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_report_metadata_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_report_metadata_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_report_metadata_async().
*
* Returns: (transfer container): attributes
*
* Since: 1.5.0
**/
GHashTable *
fwupd_client_get_report_metadata_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_devices_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_device_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_devices_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the devices registered with the daemon.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_devices_async (FwupdClient *self, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetDevices",
NULL, G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_devices_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_devices_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_devices_async().
*
* Returns: (element-type FwupdDevice) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_devices_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_plugins_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_plugin_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_plugins_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the plugins being used by the daemon.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_plugins_async (FwupdClient *self, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetPlugins",
NULL, G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_plugins_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_plugins_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_plugins_async().
*
* Returns: (element-type FwupdDevice) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_plugins_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_history_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_device_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_history_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the history.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_history_async (FwupdClient *self, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetHistory",
NULL, G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_history_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_history_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_history_async().
*
* Returns: (element-type FwupdDevice) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_history_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_device_by_id_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
const gchar *device_id = g_task_get_task_data (task);
devices = fwupd_client_get_devices_finish (FWUPD_CLIENT (source), res, &error);
if (devices == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* find the device by ID (client side) */
for (guint i = 0; i < devices->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices, i);
if (g_strcmp0 (fwupd_device_get_id (dev), device_id) == 0) {
g_task_return_pointer (task,
g_object_ref (dev),
(GDestroyNotify) g_object_unref);
return;
}
}
/* failed */
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"failed to find %s", device_id);
}
/**
* fwupd_client_get_device_by_id_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets a device by it's device ID.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_device_by_id_async (FwupdClient *self, const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_task_set_task_data (task, g_strdup (device_id), g_free);
fwupd_client_get_devices_async (self, cancellable,
fwupd_client_get_device_by_id_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_device_by_id_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_device_by_id_async().
*
* Returns: (transfer full): a #FwupdDevice, or %NULL for failure
*
* Since: 1.5.0
**/
FwupdDevice *
fwupd_client_get_device_by_id_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_devices_by_guid_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) devices_tmp = NULL;
const gchar *guid = g_task_get_task_data (task);
/* get all the devices */
devices_tmp = fwupd_client_get_devices_finish (FWUPD_CLIENT (source), res, &error);
if (devices_tmp == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* find the devices by GUID (client side) */
devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
for (guint i = 0; i < devices_tmp->len; i++) {
FwupdDevice *dev_tmp = g_ptr_array_index (devices_tmp, i);
if (fwupd_device_has_guid (dev_tmp, guid))
g_ptr_array_add (devices, g_object_ref (dev_tmp));
}
/* nothing */
if (devices->len == 0) {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"failed to find any device providing %s", guid);
return;
}
/* success */
g_task_return_pointer (task,
g_steal_pointer (&devices),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_devices_by_guid_async:
* @self: A #FwupdClient
* @guid: the GUID, e.g. `e22c4520-43dc-5bb3-8245-5787fead9b63`
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets any devices that provide a specific GUID. An error is returned if no
* devices contains this GUID.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_devices_by_guid_async (FwupdClient *self, const gchar *guid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (guid != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_task_set_task_data (task, g_strdup (guid), g_free);
fwupd_client_get_devices_async (self, cancellable,
fwupd_client_get_devices_by_guid_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_devices_by_guid_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_devices_by_guid_async().
*
* Returns: (element-type FwupdRelease) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_devices_by_guid_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_releases_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_release_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_releases_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the releases for a specific device
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_releases_async (FwupdClient *self, const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetReleases",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_releases_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_releases_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_releases_async().
*
* Returns: (element-type FwupdRelease) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_releases_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_downgrades_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_release_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_downgrades_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the downgrades for a specific device.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_downgrades_async (FwupdClient *self, const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetDowngrades",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_downgrades_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_downgrades_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_downgrades_async().
*
* Returns: (element-type FwupdRelease) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_downgrades_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_upgrades_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_release_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_upgrades_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets all the upgrades for a specific device.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_upgrades_async (FwupdClient *self, const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetUpgrades",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_upgrades_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_upgrades_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_upgrades_async().
*
* Returns: (element-type FwupdRelease) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_upgrades_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_modify_config_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_modify_config_async:
* @self: A #FwupdClient
* @key: key, e.g. `DisabledPlugins`
* @value: value, e.g. `*`
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Modifies a daemon config option.
* The daemon will only respond to this request with proper permissions
*
* Since: 1.5.0
**/
void
fwupd_client_modify_config_async (FwupdClient *self,
const gchar *key,
const gchar *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (key != NULL);
g_return_if_fail (value != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "ModifyConfig",
g_variant_new ("(ss)", key, value),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_modify_config_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_modify_config_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_modify_config_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_modify_config_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_activate_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_activate_async:
* @self: A #FwupdClient
* @device_id: a device
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Activates up a device, which normally means the device switches to a new
* firmware version. This should only be called when data loss cannot occur.
*
* Since: 1.5.0
**/
void
fwupd_client_activate_async (FwupdClient *self,
const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "Activate",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_activate_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_activate_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_activate_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_activate_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_verify_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_verify_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Verify a specific device.
*
* Since: 1.5.0
**/
void
fwupd_client_verify_async (FwupdClient *self,
const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "Verify",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_verify_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_verify_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_verify_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_verify_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_verify_update_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_verify_update_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Update the verification record for a specific device.
*
* Since: 1.5.0
**/
void
fwupd_client_verify_update_async (FwupdClient *self,
const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "VerifyUpdate",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_verify_update_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_verify_update_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_verify_update_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_verify_update_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_unlock_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_unlock_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Unlocks a specific device so firmware can be read or wrote.
*
* Since: 1.5.0
**/
void
fwupd_client_unlock_async (FwupdClient *self,
const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "Unlock",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_unlock_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_unlock_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_unlock_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_unlock_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_clear_results_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_clear_results_async:
* @self: A #FwupdClient
* @device_id: a device
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Clears the results for a specific device.
*
* Since: 1.5.0
**/
void
fwupd_client_clear_results_async (FwupdClient *self,
const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "ClearResults",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_clear_results_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_clear_results_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_clear_results_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_clear_results_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_get_results_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_device_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_results_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets the results of a previous firmware update for a specific device.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_results_async (FwupdClient *self, const gchar *device_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetResults",
g_variant_new ("(s)", device_id),
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_results_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_results_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_results_async().
*
* Returns: (transfer full): a #FwupdDevice, or %NULL for failure
*
* Since: 1.5.0
**/
FwupdDevice *
fwupd_client_get_results_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
#ifdef HAVE_GIO_UNIX
static void
fwupd_client_install_stream_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GDBusMessage) msg = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
msg = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source),
res, &error);
if (msg == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
if (g_dbus_message_to_gerror (msg, &error)) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
void
fwupd_client_install_stream_async (FwupdClient *self,
const gchar *device_id,
GUnixInputStream *istr,
const gchar *filename_hint,
FwupdInstallFlags install_flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
GVariantBuilder builder;
g_autoptr(GDBusMessage) request = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
/* set options */
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&builder, "{sv}",
"reason", g_variant_new_string ("user-action"));
if (filename_hint != NULL) {
g_variant_builder_add (&builder, "{sv}",
"filename", g_variant_new_string (filename_hint));
}
if (install_flags & FWUPD_INSTALL_FLAG_OFFLINE) {
g_variant_builder_add (&builder, "{sv}",
"offline", g_variant_new_boolean (TRUE));
}
if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
g_variant_builder_add (&builder, "{sv}",
"allow-older", g_variant_new_boolean (TRUE));
}
if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
g_variant_builder_add (&builder, "{sv}",
"allow-reinstall", g_variant_new_boolean (TRUE));
}
if (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) {
g_variant_builder_add (&builder, "{sv}",
"allow-branch-switch", g_variant_new_boolean (TRUE));
}
if (install_flags & FWUPD_INSTALL_FLAG_FORCE) {
g_variant_builder_add (&builder, "{sv}",
"force", g_variant_new_boolean (TRUE));
}
if (install_flags & FWUPD_INSTALL_FLAG_IGNORE_POWER) {
g_variant_builder_add (&builder, "{sv}",
"ignore-power", g_variant_new_boolean (TRUE));
}
if (install_flags & FWUPD_INSTALL_FLAG_NO_HISTORY) {
g_variant_builder_add (&builder, "{sv}",
"no-history", g_variant_new_boolean (TRUE));
}
/* set out of band file descriptor */
fd_list = g_unix_fd_list_new ();
g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (istr), NULL);
request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"Install");
g_dbus_message_set_unix_fd_list (request, fd_list);
/* call into daemon */
g_dbus_message_set_body (request, g_variant_new ("(sha{sv})",
device_id,
g_unix_input_stream_get_fd (istr),
&builder));
g_dbus_connection_send_message_with_reply (g_dbus_proxy_get_connection (priv->proxy),
request,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
G_MAXINT,
NULL,
cancellable,
fwupd_client_install_stream_cb,
g_steal_pointer (&task));
}
#endif
/**
* fwupd_client_install_bytes_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @bytes: #GBytes
* @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Install firmware onto a specific device.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.0
**/
void
fwupd_client_install_bytes_async (FwupdClient *self,
const gchar *device_id,
GBytes *bytes,
FwupdInstallFlags install_flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
#ifdef HAVE_GIO_UNIX
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GError) error = NULL;
g_autoptr(GUnixInputStream) istr = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* move to a thread if this ever takes more than a few ms */
istr = fwupd_unix_input_stream_from_bytes (bytes, &error);
if (istr == NULL) {
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* call into daemon */
fwupd_client_install_stream_async (self, device_id, istr, NULL,
install_flags, cancellable,
callback, callback_data);
#else
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Not supported as <glib-unix.h> is unavailable");
#endif
}
/**
* fwupd_client_install_bytes_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_install_bytes_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_install_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
/**
* fwupd_client_install_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @filename: the filename to install
* @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Install firmware onto a specific device.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.0
**/
void
fwupd_client_install_async (FwupdClient *self,
const gchar *device_id,
const gchar *filename,
FwupdInstallFlags install_flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
#ifdef HAVE_GIO_UNIX
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GError) error = NULL;
g_autoptr(GUnixInputStream) istr = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (filename != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* move to a thread if this ever takes more than a few ms */
istr = fwupd_unix_input_stream_from_fn (filename, &error);
if (istr == NULL) {
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* call into daemon */
fwupd_client_install_stream_async (self, device_id, istr, NULL,
install_flags, cancellable,
callback, callback_data);
#else
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Not supported as <glib-unix.h> is unavailable");
#endif
}
/**
* fwupd_client_install_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_install_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_install_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
typedef struct {
FwupdDevice *device;
FwupdRelease *release;
FwupdInstallFlags install_flags;
FwupdClientDownloadFlags download_flags;
} FwupdClientInstallReleaseData;
static void
fwupd_client_install_release_data_free (FwupdClientInstallReleaseData *data)
{
g_object_unref (data->device);
g_object_unref (data->release);
g_free (data);
}
static void
fwupd_client_install_release_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
if (!fwupd_client_install_release_finish (FWUPD_CLIENT (source), res, &error)) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
static void
fwupd_client_install_release_bytes_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
if (!fwupd_client_install_bytes_finish (FWUPD_CLIENT (source), res, &error)) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
static void
fwupd_client_install_release_download_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GBytes) blob = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
FwupdClientInstallReleaseData *data = g_task_get_task_data (task);
GChecksumType checksum_type;
GCancellable *cancellable = g_task_get_cancellable (task);
const gchar *checksum_expected;
g_autofree gchar *checksum_actual = NULL;
blob = fwupd_client_download_bytes_finish (FWUPD_CLIENT (source), res, &error);
if (blob == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* verify checksum */
checksum_expected = fwupd_checksum_get_best (fwupd_release_get_checksums (data->release));
checksum_type = fwupd_checksum_guess_kind (checksum_expected);
checksum_actual = g_compute_checksum_for_bytes (checksum_type, blob);
if (g_strcmp0 (checksum_expected, checksum_actual) != 0) {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"checksum invalid, expected %s got %s",
checksum_expected, checksum_actual);
return;
}
/* if the device specifies ONLY_OFFLINE automatically set this flag */
if (fwupd_device_has_flag (data->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE))
data->install_flags |= FWUPD_INSTALL_FLAG_OFFLINE;
fwupd_client_install_bytes_async (FWUPD_CLIENT (source),
fwupd_device_get_id (data->device), blob,
data->install_flags, cancellable,
fwupd_client_install_release_bytes_cb,
g_steal_pointer (&task));
}
static gboolean
fwupd_client_is_url_http (const gchar *perhaps_url)
{
#ifdef HAVE_LIBCURL_7_62_0
g_autoptr(CURLU) h = curl_url ();
return curl_url_set (h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK;
#else
return g_str_has_prefix (perhaps_url, "http://") ||
g_str_has_prefix (perhaps_url, "https://");
#endif
}
static gboolean
fwupd_client_is_url_ipfs (const gchar *perhaps_url)
{
return g_str_has_prefix (perhaps_url, "ipfs://") ||
g_str_has_prefix (perhaps_url, "ipns://");
}
static void
fwupd_client_install_release_remote_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
GPtrArray *locations;
const gchar *uri_tmp;
g_autofree gchar *fn = NULL;
g_autoptr(FwupdRemote) remote = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GPtrArray) uris_built = g_ptr_array_new_with_free_func (g_free);
FwupdClientInstallReleaseData *data = g_task_get_task_data (task);
GCancellable *cancellable = g_task_get_cancellable (task);
/* if a remote-id was specified, the remote has to exist */
remote = fwupd_client_get_remote_by_id_finish (FWUPD_CLIENT (source), res, &error);
if (remote == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* get the default release only until other parts of fwupd can cope */
locations = fwupd_release_get_locations (data->release);
if (locations->len == 0) {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"release missing URI");
return;
}
uri_tmp = g_ptr_array_index (locations, 0);
/* local and directory remotes may have the firmware already */
if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
!fwupd_client_is_url_http (uri_tmp)) {
const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
g_autofree gchar *path = g_path_get_dirname (fn_cache);
fn = g_build_filename (path, uri_tmp, NULL);
} else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
fn = g_strdup (uri_tmp + 7);
}
/* install with flags chosen by the user */
if (fn != NULL) {
fwupd_client_install_async (FWUPD_CLIENT (source),
fwupd_device_get_id (data->device),
fn, data->install_flags,
cancellable,
fwupd_client_install_release_cb,
g_steal_pointer (&task));
return;
}
/* remote file */
for (guint i = 0; i < locations->len; i++) {
uri_tmp = g_ptr_array_index (locations, i);
if (fwupd_client_is_url_ipfs (uri_tmp)) {
g_ptr_array_add (uris_built, g_strdup (uri_tmp));
} else if (fwupd_client_is_url_http (uri_tmp)) {
g_autofree gchar *uri_str = NULL;
uri_str = fwupd_remote_build_firmware_uri (remote,
uri_tmp,
&error);
if (uri_str == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_ptr_array_add (uris_built, g_steal_pointer (&uri_str));
}
}
/* download file */
fwupd_client_download_bytes2_async (FWUPD_CLIENT (source),
uris_built,
data->download_flags,
cancellable,
fwupd_client_install_release_download_cb,
g_steal_pointer (&task));
}
#ifdef HAVE_LIBCURL
static GPtrArray *
fwupd_client_filter_locations (GPtrArray *locations,
FwupdClientDownloadFlags download_flags,
GError **error)
{
g_autoptr(GPtrArray) uris_filtered = g_ptr_array_new_with_free_func (g_free);
g_return_val_if_fail (locations != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
for (guint i = 0; i < locations->len; i++) {
const gchar *uri = g_ptr_array_index (locations, i);
if ((download_flags & FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_IPFS) > 0 &&
!fwupd_client_is_url_ipfs (uri))
continue;
g_ptr_array_add (uris_filtered, g_strdup (uri));
}
if (uris_filtered->len == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no valid release URIs");
return NULL;
}
return g_steal_pointer (&uris_filtered);
}
#endif
/**
* fwupd_client_install_release2_async:
* @self: A #FwupdClient
* @device: A #FwupdDevice
* @release: A #FwupdRelease
* @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
* @download_flags: the #FwupdClientDownloadFlags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_DISABLE_IPFS
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Installs a new release on a device, downloading the firmware if required.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.6
**/
void
fwupd_client_install_release2_async (FwupdClient *self,
FwupdDevice *device,
FwupdRelease *release,
FwupdInstallFlags install_flags,
FwupdClientDownloadFlags download_flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
FwupdClientInstallReleaseData *data;
const gchar *remote_id;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (FWUPD_IS_DEVICE (device));
g_return_if_fail (FWUPD_IS_RELEASE (release));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
data = g_new0 (FwupdClientInstallReleaseData, 1);
data->device = g_object_ref (device);
data->release = g_object_ref (release);
data->download_flags = download_flags;
data->install_flags = install_flags;
g_task_set_task_data (task, data, (GDestroyNotify) fwupd_client_install_release_data_free);
/* work out what remote-specific URI fields this should use */
remote_id = fwupd_release_get_remote_id (release);
if (remote_id == NULL) {
fwupd_client_download_bytes2_async (self,
fwupd_release_get_locations (release),
download_flags,
cancellable,
fwupd_client_install_release_download_cb,
g_steal_pointer (&task));
return;
}
/* if a remote-id was specified, the remote has to exist */
fwupd_client_get_remote_by_id_async (self, remote_id, cancellable,
fwupd_client_install_release_remote_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_install_release_async:
* @self: A #FwupdClient
* @device: A #FwupdDevice
* @release: A #FwupdRelease
* @install_flags: the #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_REINSTALL
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Installs a new release on a device, downloading the firmware if required.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.0
* Deprecated: 1.5.6
**/
void
fwupd_client_install_release_async (FwupdClient *self,
FwupdDevice *device,
FwupdRelease *release,
FwupdInstallFlags install_flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
return fwupd_client_install_release2_async (self, device, release, install_flags,
FWUPD_CLIENT_DOWNLOAD_FLAG_NONE,
cancellable, callback, callback_data);
}
/**
* fwupd_client_install_release_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_install_release_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_install_release_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
#ifdef HAVE_GIO_UNIX
static void
fwupd_client_get_details_stream_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GDBusMessage) msg = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
msg = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source),
res, &error);
if (msg == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
if (g_dbus_message_to_gerror (msg, &error)) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_device_array_from_variant (g_dbus_message_get_body (msg)),
(GDestroyNotify) g_ptr_array_unref);
}
void
fwupd_client_get_details_stream_async (FwupdClient *self,
GUnixInputStream *istr,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
gint fd = g_unix_input_stream_get_fd (istr);
g_autoptr(GDBusMessage) request = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
/* set out of band file descriptor */
fd_list = g_unix_fd_list_new ();
g_unix_fd_list_append (fd_list, fd, NULL);
request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"GetDetails");
g_dbus_message_set_unix_fd_list (request, fd_list);
/* call into daemon */
g_dbus_message_set_body (request, g_variant_new ("(h)", fd));
g_dbus_connection_send_message_with_reply (g_dbus_proxy_get_connection (priv->proxy),
request,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
G_MAXINT,
NULL,
cancellable,
fwupd_client_get_details_stream_cb,
g_steal_pointer (&task));
}
#endif
/**
* fwupd_client_get_details_bytes_async:
* @self: A #FwupdClient
* @bytes: a #GBytes for the firmware, e.g. `firmware.cab`
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets details about a specific firmware file.
*
* Since: 1.5.0
**/
void
fwupd_client_get_details_bytes_async (FwupdClient *self,
GBytes *bytes,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
#ifdef HAVE_GIO_UNIX
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GError) error = NULL;
g_autoptr(GUnixInputStream) istr = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* move to a thread if this ever takes more than a few ms */
istr = fwupd_unix_input_stream_from_bytes (bytes, &error);
if (istr == NULL) {
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* call into daemon */
fwupd_client_get_details_stream_async (self, istr, cancellable,
callback, callback_data);
#else
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Not supported as <glib-unix.h> is unavailable");
#endif
}
/**
* fwupd_client_get_details_bytes_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_details_bytes_async().
*
* Returns: (transfer container) (element-type FwupdDevice): an array of results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_details_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
/**
* fwupd_client_get_percentage:
* @self: A #FwupdClient
*
* Gets the last returned percentage value.
*
* Returns: a percentage, or 0 for unknown.
*
* Since: 0.7.3
**/
guint
fwupd_client_get_percentage (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), 0);
return priv->percentage;
}
/**
* fwupd_client_get_daemon_version:
* @self: A #FwupdClient
*
* Gets the daemon version number.
*
* Returns: a string, or %NULL for unknown.
*
* Since: 0.9.6
**/
const gchar *
fwupd_client_get_daemon_version (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
return priv->daemon_version;
}
/**
* fwupd_client_get_host_product:
* @self: A #FwupdClient
*
* Gets the string that represents the host running fwupd
*
* Returns: a string, or %NULL for unknown.
*
* Since: 1.3.1
**/
const gchar *
fwupd_client_get_host_product (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
return priv->host_product;
}
/**
* fwupd_client_get_host_machine_id:
* @self: A #FwupdClient
*
* Gets the string that represents the host machine ID
*
* Returns: a string, or %NULL for unknown.
*
* Since: 1.3.2
**/
const gchar *
fwupd_client_get_host_machine_id (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
return priv->host_machine_id;
}
/**
* fwupd_client_get_host_security_id:
* @self: A #FwupdClient
*
* Gets the string that represents the host machine ID
*
* Returns: a string, or %NULL for unknown.
*
* Since: 1.5.0
**/
const gchar *
fwupd_client_get_host_security_id (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
return priv->host_security_id;
}
/**
* fwupd_client_get_status:
* @self: A #FwupdClient
*
* Gets the last returned status value.
*
* Returns: a #FwupdStatus, or %FWUPD_STATUS_UNKNOWN for unknown.
*
* Since: 0.7.3
**/
FwupdStatus
fwupd_client_get_status (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FWUPD_STATUS_UNKNOWN);
return priv->status;
}
/**
* fwupd_client_get_tainted:
* @self: A #FwupdClient
*
* Gets if the daemon has been tainted by 3rd party code.
*
* Returns: %TRUE if the daemon is unsupported
*
* Since: 1.2.4
**/
gboolean
fwupd_client_get_tainted (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
return priv->tainted;
}
/**
* fwupd_client_get_daemon_interactive:
* @self: A #FwupdClient
*
* Gets if the daemon is running in an interactive terminal.
*
* Returns: %TRUE if the daemon is running in an interactive terminal
*
* Since: 1.3.4
**/
gboolean
fwupd_client_get_daemon_interactive (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
return priv->interactive;
}
#ifdef HAVE_GIO_UNIX
static void
fwupd_client_update_metadata_stream_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GDBusMessage) msg = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
msg = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source),
res, &error);
if (msg == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
if (g_dbus_message_to_gerror (msg, &error)) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
void
fwupd_client_update_metadata_stream_async (FwupdClient *self,
const gchar *remote_id,
GUnixInputStream *istr,
GUnixInputStream *istr_sig,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GDBusMessage) request = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
/* set out of band file descriptor */
fd_list = g_unix_fd_list_new ();
g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (istr), NULL);
g_unix_fd_list_append (fd_list, g_unix_input_stream_get_fd (istr_sig), NULL);
request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"UpdateMetadata");
g_dbus_message_set_unix_fd_list (request, fd_list);
/* call into daemon */
g_dbus_message_set_body (request, g_variant_new ("(shh)",
remote_id,
g_unix_input_stream_get_fd (istr),
g_unix_input_stream_get_fd (istr_sig)));
g_dbus_connection_send_message_with_reply (g_dbus_proxy_get_connection (priv->proxy),
request,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
G_MAXINT,
NULL,
cancellable,
fwupd_client_update_metadata_stream_cb,
g_steal_pointer (&task));
}
#endif
/**
* fwupd_client_update_metadata_bytes_async:
* @self: A #FwupdClient
* @remote_id: remote ID, e.g. `lvfs-testing`
* @metadata: XML metadata data
* @signature: signature data
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Updates the metadata. This allows a session process to download the metadata
* and metadata signing file to be passed into the daemon to be checked and
* parsed.
*
* The @remote_id allows the firmware to be tagged so that the remote can be
* matched when the firmware is downloaded.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.0
**/
void
fwupd_client_update_metadata_bytes_async (FwupdClient *self,
const gchar *remote_id,
GBytes *metadata,
GBytes *signature,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
#ifdef HAVE_GIO_UNIX
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GError) error = NULL;
g_autoptr(GUnixInputStream) istr = NULL;
g_autoptr(GUnixInputStream) istr_sig = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (remote_id != NULL);
g_return_if_fail (metadata != NULL);
g_return_if_fail (signature != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* move to a thread if this ever takes more than a few ms */
istr = fwupd_unix_input_stream_from_bytes (metadata, &error);
if (istr == NULL) {
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
istr_sig = fwupd_unix_input_stream_from_bytes (signature, &error);
if (istr_sig == NULL) {
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* call into daemon */
fwupd_client_update_metadata_stream_async (self, remote_id, istr, istr_sig,
cancellable,
callback, callback_data);
#else
g_autoptr(GTask) task = g_task_new (self, cancellable, callback, callback_data);
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Not supported as <glib-unix.h> is unavailable");
#endif
}
/**
* fwupd_client_update_metadata_bytes_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_update_metadata_bytes_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_update_metadata_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
typedef struct {
FwupdRemote *remote;
GBytes *signature;
GBytes *metadata;
} FwupdClientRefreshRemoteData;
static void
fwupd_client_refresh_remote_data_free (FwupdClientRefreshRemoteData *data)
{
if (data->signature != NULL)
g_bytes_unref (data->signature);
if (data->metadata != NULL)
g_bytes_unref (data->metadata);
g_object_unref (data->remote);
g_free (data);
}
static void
fwupd_client_refresh_remote_update_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
/* save metadata */
if (!fwupd_client_update_metadata_bytes_finish (FWUPD_CLIENT (source), res, &error)) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
static void
fwupd_client_refresh_remote_metadata_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GBytes) bytes = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
FwupdClientRefreshRemoteData *data = g_task_get_task_data (task);
FwupdClient *self = g_task_get_source_object (task);
GCancellable *cancellable = g_task_get_cancellable (task);
/* save metadata */
bytes = fwupd_client_download_bytes_finish (FWUPD_CLIENT (source), res, &error);
if (bytes == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
data->metadata = g_steal_pointer (&bytes);
/* send all this to fwupd */
fwupd_client_update_metadata_bytes_async (self,
fwupd_remote_get_id (data->remote),
data->metadata,
data->signature,
cancellable,
fwupd_client_refresh_remote_update_cb,
g_steal_pointer (&task));
}
static void
fwupd_client_refresh_remote_signature_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GBytes) bytes = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
FwupdClientRefreshRemoteData *data = g_task_get_task_data (task);
FwupdClient *self = g_task_get_source_object (task);
GCancellable *cancellable = g_task_get_cancellable (task);
GChecksumType checksum_kind;
g_autofree gchar *checksum = NULL;
/* save signature */
bytes = fwupd_client_download_bytes_finish (FWUPD_CLIENT (source), res, &error);
if (bytes == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
data->signature = g_steal_pointer (&bytes);
if (fwupd_remote_get_keyring_kind (data->remote) == FWUPD_KEYRING_KIND_JCAT) {
if (!fwupd_remote_load_signature_bytes (data->remote, data->signature, &error)) {
g_prefix_error (&error, "Failed to load signature: ");
g_task_return_error (task, g_steal_pointer (&error));
return;
}
}
/* is the signature checksum the same? */
checksum_kind = fwupd_checksum_guess_kind (fwupd_remote_get_checksum (data->remote));
checksum = g_compute_checksum_for_data (checksum_kind,
(const guchar *) g_bytes_get_data (data->signature, NULL),
g_bytes_get_size (data->signature));
if (g_strcmp0 (checksum, fwupd_remote_get_checksum (data->remote)) == 0) {
g_debug ("metadata signature of %s is unchanged, skipping",
fwupd_remote_get_id (data->remote));
g_task_return_boolean (task, TRUE);
return;
}
/* download metadata */
fwupd_client_download_bytes_async (self,
fwupd_remote_get_metadata_uri (data->remote),
FWUPD_CLIENT_DOWNLOAD_FLAG_NONE,
cancellable,
fwupd_client_refresh_remote_metadata_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_refresh_remote_async:
* @self: A #FwupdClient
* @remote: A #FwupdRemote
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Refreshes a remote by downloading new metadata.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.0
**/
void
fwupd_client_refresh_remote_async (FwupdClient *self,
FwupdRemote *remote,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientRefreshRemoteData *data;
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (FWUPD_IS_REMOTE (remote));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, callback_data);
data = g_new0 (FwupdClientRefreshRemoteData, 1);
data->remote = g_object_ref (remote);
g_task_set_task_data (task,
g_steal_pointer (&data),
(GDestroyNotify) fwupd_client_refresh_remote_data_free);
/* download signature */
fwupd_client_download_bytes_async (self,
fwupd_remote_get_metadata_uri_sig (remote),
FWUPD_CLIENT_DOWNLOAD_FLAG_NONE,
cancellable,
fwupd_client_refresh_remote_signature_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_refresh_remote_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_refresh_remote_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_refresh_remote_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_get_remotes_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_pointer (task,
fwupd_remote_array_from_variant (val),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_remotes_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets the list of remotes that have been configured for the system.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_remotes_async (FwupdClient *self, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetRemotes",
NULL, G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_remotes_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_remotes_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_remotes_async().
*
* Returns: (element-type FwupdRemote) (transfer container): results
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_remotes_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_get_approved_firmware_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_auto(GStrv) strv = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free);
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_variant_get (val, "(^as)", &strv);
for (guint i = 0; strv[i] != NULL; i++)
g_ptr_array_add (array, g_strdup (strv[i]));
/* success */
g_task_return_pointer (task,
g_steal_pointer (&array),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_approved_firmware_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets the list of approved firmware.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_approved_firmware_async (FwupdClient *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetApprovedFirmware",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_approved_firmware_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_approved_firmware_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_approved_firmware_async().
*
* Returns: (element-type utf8) (transfer container): checksums, or %NULL for error
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_approved_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_set_approved_firmware_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_set_approved_firmware_async:
* @self: A #FwupdClient
* @checksums: (element-type utf8): firmware checksums
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Sets the list of approved firmware.
*
* Since: 1.5.0
**/
void
fwupd_client_set_approved_firmware_async (FwupdClient *self,
GPtrArray *checksums,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_auto(GStrv) strv = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
strv = g_new0 (gchar *, checksums->len + 1);
for (guint i = 0; i < checksums->len; i++) {
const gchar *tmp = g_ptr_array_index (checksums, i);
strv[i] = g_strdup (tmp);
}
g_dbus_proxy_call (priv->proxy, "SetApprovedFirmware",
g_variant_new ("(^as)", strv),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_set_approved_firmware_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_set_approved_firmware_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_set_approved_firmware_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_set_approved_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_get_blocked_firmware_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_auto(GStrv) strv = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free);
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_variant_get (val, "(^as)", &strv);
for (guint i = 0; strv[i] != NULL; i++)
g_ptr_array_add (array, g_strdup (strv[i]));
/* success */
g_task_return_pointer (task,
g_steal_pointer (&array),
(GDestroyNotify) g_ptr_array_unref);
}
/**
* fwupd_client_get_blocked_firmware_async:
* @self: A #FwupdClient
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets the list of blocked firmware.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_get_blocked_firmware_async (FwupdClient *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "GetBlockedFirmware",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_get_blocked_firmware_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_blocked_firmware_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_blocked_firmware_async().
*
* Returns: (element-type utf8) (transfer container): checksums, or %NULL for error
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_blocked_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_set_blocked_firmware_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_set_blocked_firmware_async:
* @self: A #FwupdClient
* @checksums: (element-type utf8): firmware checksums
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Sets the list of blocked firmware.
*
* Since: 1.5.0
**/
void
fwupd_client_set_blocked_firmware_async (FwupdClient *self,
GPtrArray *checksums,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_auto(GStrv) strv = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
strv = g_new0 (gchar *, checksums->len + 1);
for (guint i = 0; i < checksums->len; i++) {
const gchar *tmp = g_ptr_array_index (checksums, i);
strv[i] = g_strdup (tmp);
}
g_dbus_proxy_call (priv->proxy, "SetBlockedFirmware",
g_variant_new ("(^as)", strv),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_set_blocked_firmware_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_set_blocked_firmware_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_set_blocked_firmware_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_set_blocked_firmware_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_set_feature_flags_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_set_feature_flags_async:
* @self: A #FwupdClient
* @feature_flags: #FwupdFeatureFlags, e.g. %FWUPD_FEATURE_FLAG_UPDATE_TEXT
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Sets the features the client supports. This allows firmware to depend on
* specific front-end features, for instance showing the user an image on
* how to detach the hardware.
*
* Since: 1.5.0
**/
void
fwupd_client_set_feature_flags_async (FwupdClient *self,
FwupdFeatureFlags feature_flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "SetFeatureFlags",
g_variant_new ("(t)", (guint64) feature_flags),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_set_feature_flags_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_set_feature_flags_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_set_feature_flags_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_set_feature_flags_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_self_sign_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
gchar *str = NULL;
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_variant_get (val, "(s)", &str);
g_task_return_pointer (task,
g_steal_pointer (&str),
(GDestroyNotify) g_free);
}
/**
* fwupd_client_self_sign_async:
* @self: A #FwupdClient
* @value: A string to sign, typically a JSON blob
* @flags: #FwupdSelfSignFlags, e.g. %FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Signs the data using the client self-signed certificate.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* Since: 1.5.0
**/
void
fwupd_client_self_sign_async (FwupdClient *self,
const gchar *value,
FwupdSelfSignFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
GVariantBuilder builder;
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (value != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* set options */
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
if (flags & FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP) {
g_variant_builder_add (&builder, "{sv}",
"add-timestamp", g_variant_new_boolean (TRUE));
}
if (flags & FWUPD_SELF_SIGN_FLAG_ADD_CERT) {
g_variant_builder_add (&builder, "{sv}",
"add-cert", g_variant_new_boolean (TRUE));
}
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy,
"SelfSign",
g_variant_new ("(sa{sv})", value, &builder),
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
fwupd_client_self_sign_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_self_sign_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_self_sign_async().
*
* Returns: a signature, or %NULL for failure
*
* Since: 1.5.0
**/
gchar *
fwupd_client_self_sign_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
static void
fwupd_client_modify_remote_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_modify_remote_async:
* @self: A #FwupdClient
* @remote_id: the remote ID, e.g. `lvfs-testing`
* @key: the key, e.g. `Enabled`
* @value: the key, e.g. `true`
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Modifies a system remote in a specific way.
*
* Since: 1.5.0
**/
void
fwupd_client_modify_remote_async (FwupdClient *self,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (remote_id != NULL);
g_return_if_fail (key != NULL);
g_return_if_fail (value != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "ModifyRemote",
g_variant_new ("(sss)", remote_id, key, value),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_modify_remote_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_modify_remote_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_modify_remote_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_modify_remote_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static void
fwupd_client_modify_device_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (val == NULL) {
fwupd_client_fixup_dbus_error (error);
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* success */
g_task_return_boolean (task, TRUE);
}
/**
* fwupd_client_modify_device_async:
* @self: A #FwupdClient
* @device_id: the device ID
* @key: the key, e.g. `Flags`
* @value: the key, e.g. `reported`
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Modifies a device in a specific way. Not all properties on the #FwupdDevice
* are settable by the client, and some may have other restrictions on @value.
*
* Since: 1.5.0
**/
void
fwupd_client_modify_device_async (FwupdClient *self,
const gchar *device_id,
const gchar *key,
const gchar *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (device_id != NULL);
g_return_if_fail (key != NULL);
g_return_if_fail (value != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_dbus_proxy_call (priv->proxy, "ModifyDevice",
g_variant_new ("(sss)", device_id, key, value),
G_DBUS_CALL_FLAGS_NONE, -1,
cancellable,
fwupd_client_modify_device_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_modify_device_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_modify_device_async().
*
* Returns: %TRUE for success
*
* Since: 1.5.0
**/
gboolean
fwupd_client_modify_device_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK(res), error);
}
static FwupdRemote *
fwupd_client_get_remote_by_id_noref (GPtrArray *remotes, const gchar *remote_id)
{
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote = g_ptr_array_index (remotes, i);
if (g_strcmp0 (remote_id, fwupd_remote_get_id (remote)) == 0)
return remote;
}
return NULL;
}
static void
fwupd_client_get_remote_by_id_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
FwupdRemote *remote_tmp;
g_autoptr(GTask) task = G_TASK (user_data);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) remotes = NULL;
const gchar *remote_id = g_task_get_task_data (task);
remotes = fwupd_client_get_remotes_finish (FWUPD_CLIENT (source), res, &error);
if (remotes == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
remote_tmp = fwupd_client_get_remote_by_id_noref (remotes, remote_id);
if (remote_tmp == NULL) {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"no remote '%s' found in search paths",
remote_id);
return;
}
/* success */
g_task_return_pointer (task,
g_object_ref (remote_tmp),
(GDestroyNotify) g_object_unref);
}
/**
* fwupd_client_get_remote_by_id_async:
* @self: A #FwupdClient
* @remote_id: the remote ID, e.g. `lvfs-testing`
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Gets a specific remote that has been configured for the system.
*
* Since: 1.5.0
**/
void
fwupd_client_get_remote_by_id_async (FwupdClient *self,
const gchar *remote_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (remote_id != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* call into daemon */
task = g_task_new (self, cancellable, callback, callback_data);
g_task_set_task_data (task, g_strdup (remote_id), g_free);
fwupd_client_get_remotes_async (self, cancellable,
fwupd_client_get_remote_by_id_cb,
g_steal_pointer (&task));
}
/**
* fwupd_client_get_remote_by_id_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_get_remote_by_id_async().
*
* Returns: (transfer full): a #FwupdRemote, or %NULL if not found
*
* Since: 1.5.0
**/
FwupdRemote *
fwupd_client_get_remote_by_id_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
/**
* fwupd_client_set_user_agent:
* @self: A #FwupdClient
* @user_agent: the user agent ID, e.g. `gnome-software/3.34.1`
*
* Manually sets the user agent that is used for downloading. The user agent
* should contain the runtime version of fwupd somewhere in the provided string.
*
* Since: 1.4.5
**/
void
fwupd_client_set_user_agent (FwupdClient *self, const gchar *user_agent)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (user_agent != NULL);
/* not changed */
if (g_strcmp0 (priv->user_agent, user_agent) == 0)
return;
g_free (priv->user_agent);
priv->user_agent = g_strdup (user_agent);
}
/**
* fwupd_client_get_user_agent:
* @self: A #FwupdClient
*
* Gets the string that represents the user agent that is used for
* uploading and downloading. The user agent will contain the runtime
* version of fwupd somewhere in the provided string.
*
* Returns: a string, or %NULL for unknown.
*
* Since: 1.5.2
**/
const gchar *
fwupd_client_get_user_agent (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
return priv->user_agent;
}
/**
* fwupd_client_set_user_agent_for_package:
* @self: A #FwupdClient
* @package_name: client program name, e.g. "gnome-software"
* @package_version: client program version, e.g. "3.28.1"
*
* Builds a user-agent to use for the download.
*
* Supplying harmless details to the server means it knows more about each
* client. This allows the web service to respond in a different way, for
* instance sending a different metadata file for old versions of fwupd, or
* returning an error for Solaris machines.
*
* Before freaking out about theoretical privacy implications, much more data
* than this is sent to each and every website you visit.
*
* Since: 1.4.5
**/
void
fwupd_client_set_user_agent_for_package (FwupdClient *self,
const gchar *package_name,
const gchar *package_version)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
GString *str = g_string_new (NULL);
g_autofree gchar *system = NULL;
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (package_name != NULL);
g_return_if_fail (package_version != NULL);
/* application name and version */
g_string_append_printf (str, "%s/%s", package_name, package_version);
/* system information */
system = fwupd_build_user_agent_system ();
if (system != NULL)
g_string_append_printf (str, " (%s)", system);
/* platform, which in our case is just fwupd */
if (g_strcmp0 (package_name, "fwupd") != 0)
g_string_append_printf (str, " fwupd/%s", priv->daemon_version);
/* success */
g_free (priv->user_agent);
priv->user_agent = g_string_free (str, FALSE);
}
#ifdef HAVE_LIBCURL
static size_t
fwupd_client_download_write_callback_cb (char *ptr, size_t size, size_t nmemb, void *userdata)
{
GByteArray *buf = (GByteArray *) userdata;
gsize realsize = size * nmemb;
g_byte_array_append (buf, (const guint8 *) ptr, realsize);
return realsize;
}
static GBytes *
fwupd_client_stream_read_bytes (GInputStream *stream, GError **error)
{
guint8 tmp[0x8000] = { 0x0 };
g_autoptr(GByteArray) buf = g_byte_array_new ();
/* read from stream in 32kB chunks */
while (TRUE) {
gssize sz;
sz = g_input_stream_read (stream, tmp, sizeof(tmp), NULL, error);
if (sz == 0)
break;
if (sz < 0)
return NULL;
g_byte_array_append (buf, tmp, sz);
}
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
}
static GBytes *
fwupd_client_download_ipfs (FwupdClient *self, const gchar *url, GError **error)
{
GInputStream *stream = NULL;
g_autofree gchar *path = NULL;
g_autoptr(GSubprocess) subprocess = NULL;
/* we get no detailed progress details */
fwupd_client_set_status (self, FWUPD_STATUS_DOWNLOADING);
fwupd_client_set_percentage (self, 0);
/* convert from URI to path */
if (g_str_has_prefix (url, "ipfs://")) {
path = g_strdup_printf ("/ipfs/%s", url + 7);
} else if (g_str_has_prefix (url, "ipns://")) {
path = g_strdup_printf ("/ipns/%s", url + 7);
} else {
path = g_strdup (url);
}
/* run sync */
subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE,
error, "ipfs", "cat", path, NULL);
if (subprocess == NULL)
return NULL;
if (!g_subprocess_wait_check (subprocess, NULL, error))
return NULL;
/* get raw stdout */
stream = g_subprocess_get_stdout_pipe (subprocess);
return fwupd_client_stream_read_bytes (stream, error);
}
static GBytes *
fwupd_client_download_http (FwupdClient *self,
CURL *curl,
const gchar *url,
GError **error)
{
CURLcode res;
gchar errbuf[CURL_ERROR_SIZE] = { '\0' };
g_autoptr(GByteArray) buf = g_byte_array_new ();
fwupd_client_set_status (self, FWUPD_STATUS_DOWNLOADING);
curl_easy_setopt (curl, CURLOPT_URL, url);
curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf);
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, fwupd_client_download_write_callback_cb);
curl_easy_setopt (curl, CURLOPT_WRITEDATA, buf);
res = curl_easy_perform (curl);
fwupd_client_set_status (self, FWUPD_STATUS_IDLE);
if (res != CURLE_OK) {
glong status_code = 0;
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status_code);
g_debug ("status-code was %ld", status_code);
if (status_code == 429) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to download due to server limit");
return NULL;
}
if (errbuf[0] != '\0') {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to download file: %s",
errbuf);
return NULL;
}
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to download file: %s",
curl_easy_strerror (res));
return NULL;
}
return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
}
static void
fwupd_client_download_bytes_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
FwupdClient *self = FWUPD_CLIENT (source_object);
FwupdCurlHelper *helper = g_task_get_task_data (task);
g_autoptr(GBytes) blob = NULL;
for (guint i = 0; i < helper->urls->len; i++) {
const gchar *url = g_ptr_array_index (helper->urls, i);
g_autoptr(GError) error = NULL;
g_debug ("downloading %s", url);
fwupd_client_curl_helper_set_proxy (self, helper, url);
if (fwupd_client_is_url_http (url)) {
blob = fwupd_client_download_http (self, helper->curl, url, &error);
if (blob != NULL)
break;
} else if (fwupd_client_is_url_ipfs (url)) {
blob = fwupd_client_download_ipfs (self, url, &error);
if (blob != NULL)
break;
} else {
g_set_error (&error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"not sure how to handle: %s", url);
}
if (i == helper->urls->len - 1) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
fwupd_client_set_percentage (self, 0);
fwupd_client_set_status (self, FWUPD_STATUS_IDLE);
g_debug ("failed to download %s: %s, trying next URI…",
url, error->message);
}
g_task_return_pointer (task,
g_steal_pointer (&blob),
(GDestroyNotify) g_bytes_unref);
}
#endif
/* private */
void
fwupd_client_download_bytes2_async (FwupdClient *self,
GPtrArray *urls,
FwupdClientDownloadFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
#ifdef HAVE_LIBCURL
g_autoptr(GError) error = NULL;
g_autoptr(FwupdCurlHelper) helper = NULL;
#endif
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (urls != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* ensure networking set up */
task = g_task_new (self, cancellable, callback, callback_data);
#ifdef HAVE_LIBCURL
helper = fwupd_client_curl_new (self, &error);
if (helper == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
helper->urls = fwupd_client_filter_locations (urls, flags, &error);
if (helper->urls == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_task_set_task_data (task, g_steal_pointer (&helper), (GDestroyNotify) fwupd_client_curl_helper_free);
/* download data */
g_task_run_in_thread (task, fwupd_client_download_bytes_thread_cb);
#else
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"no libcurl support");
#endif
}
/**
* fwupd_client_download_bytes_async:
* @self: A #FwupdClient
* @url: the remote URL
* @flags: #FwupdClientDownloadFlags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Downloads data from a remote server. The fwupd_client_set_user_agent() function
* should be called before this method is used.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.0
**/
void
fwupd_client_download_bytes_async (FwupdClient *self,
const gchar *url,
FwupdClientDownloadFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GPtrArray) urls = g_ptr_array_new_with_free_func (g_free);
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (url != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* just proxy */
g_ptr_array_add (urls, g_strdup (url));
fwupd_client_download_bytes2_async (self, urls, flags, cancellable,
callback, callback_data);
}
/**
* fwupd_client_download_bytes_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_download_bytes_async().
*
* Returns: (transfer full): downloaded data, or %NULL for error
*
* Since: 1.5.0
**/
GBytes *
fwupd_client_download_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
#ifdef HAVE_LIBCURL
static void
fwupd_client_upload_bytes_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
FwupdClient *self = FWUPD_CLIENT (source_object);
FwupdCurlHelper *helper = g_task_get_task_data (task);
CURLcode res;
gchar errbuf[CURL_ERROR_SIZE] = { '\0' };
g_autoptr(GByteArray) buf = g_byte_array_new ();
curl_easy_setopt (helper->curl, CURLOPT_ERRORBUFFER, errbuf);
curl_easy_setopt (helper->curl, CURLOPT_WRITEFUNCTION, fwupd_client_download_write_callback_cb);
curl_easy_setopt (helper->curl, CURLOPT_WRITEDATA, buf);
res = curl_easy_perform (helper->curl);
fwupd_client_set_status (self, FWUPD_STATUS_IDLE);
if (res != CURLE_OK) {
glong status_code = 0;
curl_easy_getinfo (helper->curl, CURLINFO_RESPONSE_CODE, &status_code);
g_debug ("status-code was %ld", status_code);
if (errbuf[0] != '\0') {
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to upload file: %s",
errbuf);
return;
}
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"failed to upload file: %s",
curl_easy_strerror (res));
return;
}
g_task_return_pointer (task,
g_byte_array_free_to_bytes (g_steal_pointer (&buf)),
(GDestroyNotify) g_bytes_unref);
}
#endif
/**
* fwupd_client_upload_bytes_async:
* @self: A #FwupdClient
* @url: the remote URL
* @payload: payload string
* @signature: (nullable): signature string
* @flags: #FwupdClientDownloadFlags, e.g. %FWUPD_CLIENT_DOWNLOAD_FLAG_NONE
* @cancellable: (nullable): optional #GCancellable
* @callback: the function to run on completion
* @callback_data: the data to pass to @callback
*
* Uploads data to a remote server. The fwupd_client_set_user_agent() function
* should be called before this method is used.
*
* You must have called fwupd_client_connect_async() on @self before using
* this method.
*
* NOTE: This method is thread-safe, but progress signals will be
* emitted in the global default main context, if not explicitly set with
* fwupd_client_set_main_context().
*
* Since: 1.5.0
**/
void
fwupd_client_upload_bytes_async (FwupdClient *self,
const gchar *url,
const gchar *payload,
const gchar *signature,
FwupdClientUploadFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer callback_data)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_autoptr(GTask) task = NULL;
#ifdef HAVE_LIBCURL
g_autoptr(FwupdCurlHelper) helper = NULL;
g_autoptr(GError) error = NULL;
#endif
g_return_if_fail (FWUPD_IS_CLIENT (self));
g_return_if_fail (url != NULL);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (priv->proxy != NULL);
/* ensure networking set up */
task = g_task_new (self, cancellable, callback, callback_data);
#ifdef HAVE_LIBCURL
helper = fwupd_client_curl_new (self, &error);
if (helper == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
/* build message */
if ((flags & FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART) > 0 ||
signature != NULL) {
curl_mimepart *part;
helper->mime = curl_mime_init (helper->curl);
curl_easy_setopt (helper->curl, CURLOPT_MIMEPOST, helper->mime);
part = curl_mime_addpart (helper->mime);
curl_mime_data (part, payload, CURL_ZERO_TERMINATED);
curl_mime_name (part, "payload");
if (signature != NULL) {
part = curl_mime_addpart (helper->mime);
curl_mime_data (part, signature, CURL_ZERO_TERMINATED);
curl_mime_name (part, "signature");
}
} else {
helper->headers = curl_slist_append (helper->headers, "Content-Type: text/plain");
curl_easy_setopt (helper->curl, CURLOPT_HTTPHEADER, helper->headers);
curl_easy_setopt (helper->curl, CURLOPT_POST, 1L);
curl_easy_setopt (helper->curl, CURLOPT_POSTFIELDSIZE, strlen (payload));
curl_easy_setopt (helper->curl, CURLOPT_COPYPOSTFIELDS, payload);
}
fwupd_client_set_status (self, FWUPD_STATUS_IDLE);
g_debug ("uploading to %s", url);
curl_easy_setopt (helper->curl, CURLOPT_URL, url);
g_task_set_task_data (task, g_steal_pointer (&helper), (GDestroyNotify) fwupd_client_curl_helper_free);
g_task_run_in_thread (task, fwupd_client_upload_bytes_thread_cb);
#else
g_task_return_new_error (task,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"no libcurl support");
#endif
}
/**
* fwupd_client_upload_bytes_finish:
* @self: A #FwupdClient
* @res: the #GAsyncResult
* @error: (nullable): optional return location for an error
*
* Gets the result of fwupd_client_upload_bytes_async().
*
* Returns: (transfer full): response data, or %NULL for error
*
* Since: 1.5.0
**/
GBytes *
fwupd_client_upload_bytes_finish (FwupdClient *self, GAsyncResult *res, GError **error)
{
g_return_val_if_fail (FWUPD_IS_CLIENT (self), NULL);
g_return_val_if_fail (g_task_is_valid (res, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK(res), error);
}
#ifdef SOUP_SESSION_COMPAT
/* this is bad; we dlopen libsoup-2.4.so.1 and get the gtype manually
* to avoid deps on both libcurl and libsoup whilst preserving ABI */
static void
fwupd_client_ensure_soup_session (FwupdClient *self)
{
FwupdClientObjectNewFunc func = NULL;
FwupdClientPrivate *priv = GET_PRIVATE (self);
GType soup_gtype;
/* already set up */
if (priv->soup_session != NULL)
return;
/* known GType, just create */
soup_gtype = g_type_from_name ("SoupSession");
if (soup_gtype != 0) {
priv->soup_session = g_object_new (soup_gtype, NULL);
return;
}
/* load the library at runtime, leaking the module */
if (priv->soup_module == NULL) {
g_autofree gchar *fn = NULL;
fn = g_build_filename (FWUPD_LIBDIR, "libsoup-2.4.so.1", NULL);
priv->soup_module = g_module_open (fn, G_MODULE_BIND_LAZY);
if (priv->soup_module == NULL) {
g_warning ("failed to find libsoup library");
return;
}
}
if (!g_module_symbol (priv->soup_module,
"soup_session_new",
(gpointer *) &func)) {
g_warning ("failed to find soup_session_get_type()");
g_module_close (priv->soup_module);
priv->soup_module = NULL;
return;
}
priv->soup_session = func ();
g_object_set (priv->soup_session, "timeout", (guint) 60, NULL);
}
#endif
static void
fwupd_client_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
FwupdClient *self = FWUPD_CLIENT (object);
FwupdClientPrivate *priv = GET_PRIVATE (self);
switch (prop_id) {
case PROP_STATUS:
g_value_set_uint (value, priv->status);
break;
case PROP_TAINTED:
g_value_set_boolean (value, priv->tainted);
break;
case PROP_SOUP_SESSION:
#ifdef SOUP_SESSION_COMPAT
fwupd_client_ensure_soup_session (self);
g_value_set_object (value, priv->soup_session);
#else
g_value_set_object (value, NULL);
#endif
break;
case PROP_PERCENTAGE:
g_value_set_uint (value, priv->percentage);
break;
case PROP_DAEMON_VERSION:
g_value_set_string (value, priv->daemon_version);
break;
case PROP_HOST_PRODUCT:
g_value_set_string (value, priv->host_product);
break;
case PROP_HOST_MACHINE_ID:
g_value_set_string (value, priv->host_machine_id);
break;
case PROP_HOST_SECURITY_ID:
g_value_set_string (value, priv->host_security_id);
break;
case PROP_INTERACTIVE:
g_value_set_boolean (value, priv->interactive);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fwupd_client_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
FwupdClient *self = FWUPD_CLIENT (object);
FwupdClientPrivate *priv = GET_PRIVATE (self);
switch (prop_id) {
case PROP_STATUS:
priv->status = g_value_get_uint (value);
break;
case PROP_PERCENTAGE:
priv->percentage = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fwupd_client_class_init (FwupdClientClass *klass)
{
GParamSpec *pspec;
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fwupd_client_finalize;
object_class->get_property = fwupd_client_get_property;
object_class->set_property = fwupd_client_set_property;
/**
* FwupdClient::changed:
* @self: the #FwupdClient instance that emitted the signal
*
* The ::changed signal is emitted when the daemon internal has
* changed, for instance when a device has been added or removed.
*
* Since: 0.7.0
**/
signals [SIGNAL_CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (FwupdClientClass, changed),
NULL, NULL, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* FwupdClient::state-changed:
* @self: the #FwupdClient instance that emitted the signal
* @status: the #FwupdStatus
*
* The ::state-changed signal is emitted when the daemon status has
* changed, e.g. going from %FWUPD_STATUS_IDLE to %FWUPD_STATUS_DEVICE_WRITE.
*
* Since: 0.7.0
**/
signals [SIGNAL_STATUS_CHANGED] =
g_signal_new ("status-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (FwupdClientClass, status_changed),
NULL, NULL, g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
/**
* FwupdClient::device-added:
* @self: the #FwupdClient instance that emitted the signal
* @result: the #FwupdDevice
*
* The ::device-added signal is emitted when a device has been
* added.
*
* Since: 0.7.1
**/
signals [SIGNAL_DEVICE_ADDED] =
g_signal_new ("device-added",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (FwupdClientClass, device_added),
NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 1, FWUPD_TYPE_DEVICE);
/**
* FwupdClient::device-removed:
* @self: the #FwupdClient instance that emitted the signal
* @result: the #FwupdDevice
*
* The ::device-removed signal is emitted when a device has been
* removed.
*
* Since: 0.7.1
**/
signals [SIGNAL_DEVICE_REMOVED] =
g_signal_new ("device-removed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (FwupdClientClass, device_removed),
NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 1, FWUPD_TYPE_DEVICE);
/**
* FwupdClient::device-changed:
* @self: the #FwupdClient instance that emitted the signal
* @result: the #FwupdDevice
*
* The ::device-changed signal is emitted when a device has been
* changed in some way, e.g. the version number is updated.
*
* Since: 0.7.1
**/
signals [SIGNAL_DEVICE_CHANGED] =
g_signal_new ("device-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (FwupdClientClass, device_changed),
NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 1, FWUPD_TYPE_DEVICE);
/**
* FwupdClient:status:
*
* The last-reported status of the daemon.
*
* Since: 0.7.0
*/
pspec = g_param_spec_uint ("status", NULL, NULL,
0, FWUPD_STATUS_LAST, FWUPD_STATUS_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_STATUS, pspec);
/**
* FwupdClient:tainted:
*
* If the daemon is tainted by 3rd party code.
*
* Since: 1.2.4
*/
pspec = g_param_spec_boolean ("tainted", NULL, NULL, FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_TAINTED, pspec);
/**
* FwupdClient:interactive:
*
* If the daemon is running in an interactive terminal
*
* Since: 1.3.4
*/
pspec = g_param_spec_boolean ("interactive", NULL, NULL, FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_INTERACTIVE, pspec);
/**
* FwupdClient:percentage:
*
* The last-reported percentage of the daemon.
*
* Since: 0.7.3
*/
pspec = g_param_spec_uint ("percentage", NULL, NULL,
0, 100, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_PERCENTAGE, pspec);
/**
* FwupdClient:daemon-version:
*
* The daemon version number.
*
* Since: 0.9.6
*/
pspec = g_param_spec_string ("daemon-version", NULL, NULL,
NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_DAEMON_VERSION, pspec);
/**
* FwupdClient:soup-session:
*
* The libsoup session, now unused.
*
* Since: 1.4.5
*/
pspec = g_param_spec_object ("soup-session", NULL, NULL, G_TYPE_OBJECT,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_SOUP_SESSION, pspec);
/**
* FwupdClient:host-product:
*
* The host product string
*
* Since: 1.3.1
*/
pspec = g_param_spec_string ("host-product", NULL, NULL,
NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_HOST_PRODUCT, pspec);
/**
* FwupdClient:host-machine-id:
*
* The host machine-id string
*
* Since: 1.3.2
*/
pspec = g_param_spec_string ("host-machine-id", NULL, NULL,
NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_HOST_MACHINE_ID, pspec);
/**
* FwupdClient:host-security-id:
*
* The host machine-id string
*
* Since: 1.5.0
*/
pspec = g_param_spec_string ("host-security-id", NULL, NULL,
NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_HOST_SECURITY_ID, pspec);
}
static void
fwupd_client_init (FwupdClient *self)
{
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_mutex_init (&priv->proxy_mutex);
g_mutex_init (&priv->idle_mutex);
priv->idle_sources = g_ptr_array_new_with_free_func ((GDestroyNotify) fwupd_client_context_helper_free);
priv->proxy_resolver = g_proxy_resolver_get_default ();
}
static void
fwupd_client_finalize (GObject *object)
{
FwupdClient *self = FWUPD_CLIENT (object);
FwupdClientPrivate *priv = GET_PRIVATE (self);
g_clear_pointer (&priv->main_ctx, g_main_context_unref);
g_free (priv->user_agent);
g_free (priv->daemon_version);
g_free (priv->host_product);
g_free (priv->host_machine_id);
g_free (priv->host_security_id);
g_mutex_clear (&priv->idle_mutex);
if (priv->idle_id != 0)
g_source_remove (priv->idle_id);
g_ptr_array_unref (priv->idle_sources);
g_mutex_clear (&priv->proxy_mutex);
if (priv->proxy != NULL)
g_object_unref (priv->proxy);
#ifdef SOUP_SESSION_COMPAT
if (priv->soup_session != NULL)
g_object_unref (priv->soup_session);
#endif
G_OBJECT_CLASS (fwupd_client_parent_class)->finalize (object);
}
/**
* fwupd_client_new:
*
* Creates a new client.
*
* Returns: a new #FwupdClient
*
* Since: 0.7.0
**/
FwupdClient *
fwupd_client_new (void)
{
FwupdClient *self;
self = g_object_new (FWUPD_TYPE_CLIENT, NULL);
return FWUPD_CLIENT (self);
}