mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-05 22:47:32 +00:00
Show translated firmware release notes when provided
Send the users locale to the daemon so that it can be used to prefer the localized update text over the default en_US version. $ LANG=fr_FR.UTF8 fwupdmgr get-details test.cab ... └─ACME Plan 9: Nouvelle version: 0.0.5 Licence: Propriétaire Urgence: Faible Fournisseur: ACME Ltd. Description: Cette version stable corrige des bugs. I decided to send the locale to the daemon rather than change the `Description` to return GVariant to `a{ss}` as we also probably want to support things like localized summary and URLs too in the future.
This commit is contained in:
parent
3c8339c9eb
commit
70f9124545
@ -34,6 +34,9 @@
|
||||
#include "fwupd-request-private.h"
|
||||
#include "fwupd-security-attr-private.h"
|
||||
|
||||
static void
|
||||
fwupd_client_fixup_dbus_error(GError *error);
|
||||
|
||||
typedef GObject *(*FwupdClientObjectNewFunc)(void);
|
||||
|
||||
#define FWUPD_CLIENT_DBUS_PROXY_TIMEOUT 180000 /* ms */
|
||||
@ -66,6 +69,7 @@ typedef struct {
|
||||
GDBusProxy *proxy;
|
||||
GProxyResolver *proxy_resolver;
|
||||
gchar *user_agent;
|
||||
GHashTable *hints; /* str:str */
|
||||
#ifdef SOUP_SESSION_COMPAT
|
||||
GObject *soup_session;
|
||||
GModule *soup_module; /* we leak this */
|
||||
@ -594,12 +598,40 @@ fwupd_client_curl_new(FwupdClient *self, GError **error)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
fwupd_client_set_hints_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) {
|
||||
/* new libfwupd and old daemon, just swallow the error */
|
||||
if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
|
||||
g_debug("ignoring %s", error->message);
|
||||
g_task_return_boolean(task, TRUE);
|
||||
return;
|
||||
}
|
||||
fwupd_client_fixup_dbus_error(error);
|
||||
g_task_return_error(task, g_steal_pointer(&error));
|
||||
return;
|
||||
}
|
||||
|
||||
/* success */
|
||||
g_task_return_boolean(task, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
fwupd_client_connect_get_proxy_cb(GObject *source, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
g_autoptr(GTask) task = G_TASK(user_data);
|
||||
GVariantBuilder builder;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
FwupdClient *self = g_task_get_source_object(task);
|
||||
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
||||
GCancellable *cancellable = g_task_get_cancellable(task);
|
||||
g_autoptr(GDBusProxy) proxy = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GVariant) val = NULL;
|
||||
@ -653,8 +685,21 @@ fwupd_client_connect_get_proxy_cb(GObject *source, GAsyncResult *res, gpointer u
|
||||
if (val7 != NULL)
|
||||
fwupd_client_set_host_security_id(self, g_variant_get_string(val7, NULL));
|
||||
|
||||
/* success */
|
||||
g_task_return_boolean(task, TRUE);
|
||||
/* build client hints */
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE_DICTIONARY);
|
||||
g_hash_table_iter_init(&iter, priv->hints);
|
||||
while (g_hash_table_iter_next(&iter, &key, &value))
|
||||
g_variant_builder_add(&builder, "{ss}", (const gchar *)key, (const gchar *)value);
|
||||
|
||||
/* only supported on fwupd >= 1.7.1 */
|
||||
g_dbus_proxy_call(priv->proxy,
|
||||
"SetHints",
|
||||
g_variant_new("(a{ss})", &builder),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
FWUPD_CLIENT_DBUS_PROXY_TIMEOUT,
|
||||
cancellable,
|
||||
fwupd_client_set_hints_cb,
|
||||
g_steal_pointer(&task));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4794,6 +4839,27 @@ fwupd_client_upload_bytes_finish(FwupdClient *self, GAsyncResult *res, GError **
|
||||
return g_task_propagate_pointer(G_TASK(res), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* fwupd_client_add_hint:
|
||||
* @self: a #FwupdClient
|
||||
* @key: the key, e.g. `locale`
|
||||
* @value: (nullable): the value @key should be set
|
||||
*
|
||||
* Sets optional hints from the client that may affect the list of devices.
|
||||
*
|
||||
* Since: 1.7.1
|
||||
**/
|
||||
void
|
||||
fwupd_client_add_hint(FwupdClient *self, const gchar *key, const gchar *value)
|
||||
{
|
||||
FwupdClientPrivate *priv = GET_PRIVATE(self);
|
||||
|
||||
g_return_if_fail(FWUPD_IS_CLIENT(self));
|
||||
g_return_if_fail(key != NULL);
|
||||
|
||||
g_hash_table_insert(priv->hints, g_strdup(key), g_strdup(value));
|
||||
}
|
||||
|
||||
#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 */
|
||||
@ -5177,6 +5243,10 @@ fwupd_client_init(FwupdClient *self)
|
||||
priv->idle_sources =
|
||||
g_ptr_array_new_with_free_func((GDestroyNotify)fwupd_client_context_helper_free);
|
||||
priv->proxy_resolver = g_proxy_resolver_get_default();
|
||||
priv->hints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
||||
|
||||
/* we get this one for free */
|
||||
fwupd_client_add_hint(self, "locale", g_getenv("LANG"));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -5191,6 +5261,7 @@ fwupd_client_finalize(GObject *object)
|
||||
g_free(priv->host_product);
|
||||
g_free(priv->host_machine_id);
|
||||
g_free(priv->host_security_id);
|
||||
g_hash_table_unref(priv->hints);
|
||||
g_mutex_clear(&priv->idle_mutex);
|
||||
if (priv->idle_id != 0)
|
||||
g_source_remove(priv->idle_id);
|
||||
|
@ -478,5 +478,7 @@ fwupd_client_upload_bytes_finish(FwupdClient *self,
|
||||
GError **error) G_GNUC_WARN_UNUSED_RESULT;
|
||||
gboolean
|
||||
fwupd_client_ensure_networking(FwupdClient *self, GError **error) G_GNUC_WARN_UNUSED_RESULT;
|
||||
void
|
||||
fwupd_client_add_hint(FwupdClient *self, const gchar *key, const gchar *value);
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -709,3 +709,9 @@ LIBFWUPD_1.7.0 {
|
||||
fwupd_security_attr_has_guid;
|
||||
local: *;
|
||||
} LIBFWUPD_1.6.2;
|
||||
|
||||
LIBFWUPD_1.7.1 {
|
||||
global:
|
||||
fwupd_client_add_hint;
|
||||
local: *;
|
||||
} LIBFWUPD_1.7.0;
|
||||
|
@ -15,6 +15,7 @@ struct _FuEngineRequest {
|
||||
FuEngineRequestKind kind;
|
||||
FwupdFeatureFlags feature_flags;
|
||||
FwupdDeviceFlags device_flags;
|
||||
gchar *locale;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuEngineRequest, fu_engine_request, G_TYPE_OBJECT)
|
||||
@ -26,6 +27,13 @@ fu_engine_request_get_feature_flags(FuEngineRequest *self)
|
||||
return self->feature_flags;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
fu_engine_request_get_locale(FuEngineRequest *self)
|
||||
{
|
||||
g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), NULL);
|
||||
return self->locale;
|
||||
}
|
||||
|
||||
FuEngineRequestKind
|
||||
fu_engine_request_get_kind(FuEngineRequest *self)
|
||||
{
|
||||
@ -40,6 +48,17 @@ fu_engine_request_set_feature_flags(FuEngineRequest *self, FwupdFeatureFlags fea
|
||||
self->feature_flags = feature_flags;
|
||||
}
|
||||
|
||||
void
|
||||
fu_engine_request_set_locale(FuEngineRequest *self, const gchar *locale)
|
||||
{
|
||||
g_return_if_fail(FU_IS_ENGINE_REQUEST(self));
|
||||
self->locale = g_strdup(locale);
|
||||
|
||||
/* remove the UTF8 suffix as it is not present in the XML */
|
||||
if (self->locale != NULL)
|
||||
g_strdelimit(self->locale, ".", '\0');
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_engine_request_has_feature_flag(FuEngineRequest *self, FwupdFeatureFlags feature_flag)
|
||||
{
|
||||
|
@ -25,6 +25,10 @@ FwupdFeatureFlags
|
||||
fu_engine_request_get_feature_flags(FuEngineRequest *self);
|
||||
void
|
||||
fu_engine_request_set_feature_flags(FuEngineRequest *self, FwupdFeatureFlags feature_flags);
|
||||
const gchar *
|
||||
fu_engine_request_get_locale(FuEngineRequest *self);
|
||||
void
|
||||
fu_engine_request_set_locale(FuEngineRequest *self, const gchar *locale);
|
||||
gboolean
|
||||
fu_engine_request_has_feature_flag(FuEngineRequest *self, FwupdFeatureFlags feature_flag);
|
||||
FwupdDeviceFlags
|
||||
|
@ -452,6 +452,25 @@ fu_engine_set_release_from_artifact(FuEngine *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
fu_engine_request_get_localized_xpath(FuEngineRequest *request, const gchar *element)
|
||||
{
|
||||
GString *xpath = g_string_new(element);
|
||||
const gchar *locale = NULL;
|
||||
|
||||
/* optional; not set in tests */
|
||||
if (request != NULL)
|
||||
locale = fu_engine_request_get_locale(request);
|
||||
|
||||
/* prefer the users locale if set */
|
||||
if (locale != NULL) {
|
||||
g_autofree gchar *xpath_locale = NULL;
|
||||
xpath_locale = g_strdup_printf("%s[@xml:lang='%s']|", element, locale);
|
||||
g_string_prepend(xpath, xpath_locale);
|
||||
}
|
||||
return g_string_free(xpath, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_engine_set_release_from_appstream(FuEngine *self,
|
||||
FuEngineRequest *request,
|
||||
@ -465,6 +484,7 @@ fu_engine_set_release_from_appstream(FuEngine *self,
|
||||
const gchar *tmp;
|
||||
const gchar *remote_id;
|
||||
guint64 tmp64;
|
||||
g_autofree gchar *description_xpath = NULL;
|
||||
g_autofree gchar *version_rel = NULL;
|
||||
g_autoptr(GPtrArray) cats = NULL;
|
||||
g_autoptr(GPtrArray) issues = NULL;
|
||||
@ -516,7 +536,8 @@ fu_engine_set_release_from_appstream(FuEngine *self,
|
||||
if (!fu_engine_set_release_from_artifact(self, rel, remote, artifact, error))
|
||||
return FALSE;
|
||||
}
|
||||
description = xb_node_query_first(release, "description", NULL);
|
||||
description_xpath = fu_engine_request_get_localized_xpath(request, "description");
|
||||
description = xb_node_query_first(release, description_xpath, NULL);
|
||||
if (description != NULL) {
|
||||
g_autofree gchar *xml = NULL;
|
||||
g_autoptr(GString) str = NULL;
|
||||
@ -4209,6 +4230,7 @@ fu_engine_get_result_from_component(FuEngine *self,
|
||||
GError **error)
|
||||
{
|
||||
FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE;
|
||||
g_autofree gchar *description_xpath = NULL;
|
||||
g_autoptr(FuInstallTask) task = NULL;
|
||||
g_autoptr(FuDevice) dev = NULL;
|
||||
g_autoptr(FwupdRelease) rel = NULL;
|
||||
@ -4300,7 +4322,8 @@ fu_engine_get_result_from_component(FuEngine *self,
|
||||
}
|
||||
|
||||
/* create a result with all the metadata in */
|
||||
description = xb_node_query_first(component, "description", NULL);
|
||||
description_xpath = fu_engine_request_get_localized_xpath(request, "description");
|
||||
description = xb_node_query_first(component, description_xpath, NULL);
|
||||
if (description != NULL) {
|
||||
g_autofree gchar *xml = NULL;
|
||||
xml = xb_node_export(description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL);
|
||||
|
@ -58,13 +58,18 @@ typedef enum {
|
||||
FU_MAIN_MACHINE_KIND_CONTAINER,
|
||||
} FuMainMachineKind;
|
||||
|
||||
typedef struct {
|
||||
FwupdFeatureFlags feature_flags;
|
||||
GHashTable *hints; /* str:str */
|
||||
} FuSenderItem;
|
||||
|
||||
typedef struct {
|
||||
GDBusConnection *connection;
|
||||
GDBusNodeInfo *introspection_daemon;
|
||||
GDBusProxy *proxy_uid;
|
||||
GMainLoop *loop;
|
||||
GFileMonitor *argv0_monitor;
|
||||
GHashTable *sender_features; /* sender:FwupdFeatureFlags */
|
||||
GHashTable *sender_items; /* sender:FuSenderItem */
|
||||
#if GLIB_CHECK_VERSION(2, 63, 3)
|
||||
GMemoryMonitor *memory_monitor;
|
||||
#endif
|
||||
@ -235,7 +240,7 @@ fu_main_engine_percentage_changed_cb(FuEngine *engine, guint percentage, FuMainP
|
||||
static FuEngineRequest *
|
||||
fu_main_create_request(FuMainPrivate *priv, const gchar *sender, GError **error)
|
||||
{
|
||||
FwupdFeatureFlags *feature_flags;
|
||||
FuSenderItem *sender_item;
|
||||
FwupdDeviceFlags device_flags = FWUPD_DEVICE_FLAG_NONE;
|
||||
uid_t calling_uid = 0;
|
||||
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
|
||||
@ -243,10 +248,14 @@ fu_main_create_request(FuMainPrivate *priv, const gchar *sender, GError **error)
|
||||
|
||||
g_return_val_if_fail(sender != NULL, NULL);
|
||||
|
||||
/* did the client set the list of supported feature */
|
||||
feature_flags = g_hash_table_lookup(priv->sender_features, sender);
|
||||
if (feature_flags != NULL)
|
||||
fu_engine_request_set_feature_flags(request, *feature_flags);
|
||||
/* did the client set the list of supported features or any hints */
|
||||
sender_item = g_hash_table_lookup(priv->sender_items, sender);
|
||||
if (sender_item != NULL) {
|
||||
const gchar *locale = g_hash_table_lookup(sender_item->hints, "locale");
|
||||
if (locale != NULL)
|
||||
fu_engine_request_set_locale(request, locale);
|
||||
fu_engine_request_set_feature_flags(request, sender_item->feature_flags);
|
||||
}
|
||||
|
||||
/* are we root and therefore trusted? */
|
||||
value = g_dbus_proxy_call_sync(priv->proxy_uid,
|
||||
@ -906,6 +915,19 @@ fu_main_install_with_helper(FuMainAuthHelper *helper_ref, GError **error)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static FuSenderItem *
|
||||
fu_main_ensure_sender_item(FuMainPrivate *priv, const gchar *sender)
|
||||
{
|
||||
FuSenderItem *sender_item;
|
||||
sender_item = g_hash_table_lookup(priv->sender_items, sender);
|
||||
if (sender_item == NULL) {
|
||||
sender_item = g_new0(FuSenderItem, 1);
|
||||
sender_item->hints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
||||
g_hash_table_insert(priv->sender_items, g_strdup(sender), sender_item);
|
||||
}
|
||||
return sender_item;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_main_device_id_valid(const gchar *device_id, GError **error)
|
||||
{
|
||||
@ -1510,23 +1532,37 @@ fu_main_daemon_method_call(GDBusConnection *connection,
|
||||
return;
|
||||
}
|
||||
if (g_strcmp0(method_name, "SetFeatureFlags") == 0) {
|
||||
FwupdFeatureFlags feature_flags;
|
||||
FuSenderItem *sender_item;
|
||||
guint64 feature_flags_u64 = 0;
|
||||
|
||||
g_variant_get(parameters, "(t)", &feature_flags_u64);
|
||||
g_debug("Called %s(%" G_GUINT64_FORMAT ")", method_name, feature_flags_u64);
|
||||
|
||||
/* old flags for the same sender will be automatically destroyed */
|
||||
feature_flags = feature_flags_u64;
|
||||
g_hash_table_insert(priv->sender_features,
|
||||
g_strdup(sender),
|
||||
#if GLIB_CHECK_VERSION(2, 67, 4)
|
||||
g_memdup2(&feature_flags, sizeof(feature_flags)));
|
||||
#else
|
||||
g_memdup(&feature_flags, sizeof(feature_flags)));
|
||||
#endif
|
||||
sender_item = fu_main_ensure_sender_item(priv, sender);
|
||||
sender_item->feature_flags = feature_flags_u64;
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
return;
|
||||
}
|
||||
if (g_strcmp0(method_name, "SetHints") == 0) {
|
||||
FuSenderItem *sender_item;
|
||||
const gchar *prop_key;
|
||||
const gchar *prop_value;
|
||||
g_autoptr(GVariantIter) iter = NULL;
|
||||
|
||||
g_variant_get(parameters, "(a{ss})", &iter);
|
||||
g_debug("Called %s()", method_name);
|
||||
sender_item = fu_main_ensure_sender_item(priv, sender);
|
||||
while (g_variant_iter_next(iter, "{&s&s}", &prop_key, &prop_value)) {
|
||||
g_debug("got hint %s=%s", prop_key, prop_value);
|
||||
g_hash_table_insert(sender_item->hints,
|
||||
g_strdup(prop_key),
|
||||
g_strdup(prop_value));
|
||||
}
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_strcmp0(method_name, "Install") == 0) {
|
||||
GVariant *prop_value;
|
||||
const gchar *device_id = NULL;
|
||||
@ -1845,7 +1881,7 @@ fu_main_is_container(void)
|
||||
static void
|
||||
fu_main_private_free(FuMainPrivate *priv)
|
||||
{
|
||||
g_hash_table_unref(priv->sender_features);
|
||||
g_hash_table_unref(priv->sender_items);
|
||||
if (priv->loop != NULL)
|
||||
g_main_loop_unref(priv->loop);
|
||||
if (priv->owner_id > 0)
|
||||
@ -1878,6 +1914,13 @@ fu_main_private_free(FuMainPrivate *priv)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainPrivate, fu_main_private_free)
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
static void
|
||||
fu_main_sender_item_free(FuSenderItem *sender_item)
|
||||
{
|
||||
g_hash_table_unref(sender_item->hints);
|
||||
g_free(sender_item);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
@ -1926,7 +1969,10 @@ main(int argc, char *argv[])
|
||||
|
||||
/* create new objects */
|
||||
priv = g_new0(FuMainPrivate, 1);
|
||||
priv->sender_features = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
||||
priv->sender_items = g_hash_table_new_full(g_str_hash,
|
||||
g_str_equal,
|
||||
g_free,
|
||||
(GDestroyNotify)fu_main_sender_item_free);
|
||||
priv->loop = g_main_loop_new(NULL, FALSE);
|
||||
|
||||
/* load engine */
|
||||
|
@ -307,6 +307,25 @@
|
||||
</arg>
|
||||
</method>
|
||||
|
||||
<!--***********************************************************-->
|
||||
<method name='SetHints'>
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
Sets optional hints from the client that may affect the list of devices.
|
||||
A typical hint might be <doc:tt>locale</doc:tt> and unknown hints should be ignored.
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
<arg type='a{ss}' name='hints' direction='in'>
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>An array of string key values.</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
</method>
|
||||
|
||||
<!--***********************************************************-->
|
||||
<method name='Install'>
|
||||
<doc:doc>
|
||||
|
Loading…
Reference in New Issue
Block a user