blob: 7aa3d87aca36d1b48d66cf1cb69dd409a7abb7a0 [file] [log] [blame]
/*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuMain"
#include <config.h>
#include <stdio.h>
#include <glib/gi18n.h>
#ifdef HAVE_GUSB
#include <gusb.h>
#endif
#include <xmlb.h>
#include <fwupd.h>
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#endif
#include "fu-common.h"
#include "fu-device-private.h"
#include "fu-util-common.h"
#include "fu-device.h"
#include "fu-security-attr.h"
#include "fu-security-attrs.h"
#ifdef HAVE_SYSTEMD
#include "fu-systemd.h"
#endif
#define SYSTEMD_FWUPD_UNIT "fwupd.service"
#define SYSTEMD_SNAP_FWUPD_UNIT "snap.fwupd.fwupd.service"
const gchar *
fu_util_get_systemd_unit (void)
{
if (g_getenv ("SNAP") != NULL)
return SYSTEMD_SNAP_FWUPD_UNIT;
return SYSTEMD_FWUPD_UNIT;
}
gchar *
fu_util_term_format (const gchar *text, FuUtilTermColor fg_color)
{
return g_strdup_printf ("\033[%um\033[1m%s\033[0m", fg_color, text);
}
#ifdef HAVE_SYSTEMD
static const gchar *
fu_util_get_expected_command (const gchar *target)
{
if (g_strcmp0 (target, SYSTEMD_SNAP_FWUPD_UNIT) == 0)
return "fwupd.fwupdmgr";
return "fwupdmgr";
}
#endif
gboolean
fu_util_using_correct_daemon (GError **error)
{
#ifdef HAVE_SYSTEMD
g_autofree gchar *default_target = NULL;
g_autoptr(GError) error_local = NULL;
const gchar *target = fu_util_get_systemd_unit ();
default_target = fu_systemd_get_default_target (&error_local);
if (default_target == NULL) {
g_debug ("Systemd isn't accessible: %s\n", error_local->message);
return TRUE;
}
if (!fu_systemd_unit_check_exists (target, &error_local)) {
g_debug ("wrong target: %s\n", error_local->message);
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
/* TRANSLATORS: error message */
_("Mismatched daemon and client, use %s instead"),
fu_util_get_expected_command (target));
return FALSE;
}
#endif
return TRUE;
}
void
fu_util_print_data (const gchar *title, const gchar *msg)
{
gsize title_len;
g_auto(GStrv) lines = NULL;
if (msg == NULL)
return;
g_print ("%s:", title);
/* pad */
title_len = fu_common_strwidth (title) + 1;
lines = g_strsplit (msg, "\n", -1);
for (guint j = 0; lines[j] != NULL; j++) {
for (gsize i = title_len; i < 25; i++)
g_print (" ");
g_print ("%s\n", lines[j]);
title_len = 0;
}
}
guint
fu_util_prompt_for_number (guint maxnum)
{
gint retval;
guint answer = 0;
do {
char buffer[64];
/* swallow the \n at end of line too */
if (!fgets (buffer, sizeof (buffer), stdin))
break;
if (strlen (buffer) == sizeof (buffer) - 1)
continue;
/* get a number */
retval = sscanf (buffer, "%u", &answer);
/* positive */
if (retval == 1 && answer <= maxnum)
break;
/* TRANSLATORS: the user isn't reading the question */
g_print (_("Please enter a number from 0 to %u: "), maxnum);
} while (TRUE);
return answer;
}
gboolean
fu_util_prompt_for_boolean (gboolean def)
{
do {
char buffer[4];
if (!fgets (buffer, sizeof (buffer), stdin))
continue;
if (strlen (buffer) == sizeof (buffer) - 1)
continue;
if (g_strcmp0 (buffer, "\n") == 0)
return def;
buffer[0] = g_ascii_toupper (buffer[0]);
if (g_strcmp0 (buffer, "Y\n") == 0)
return TRUE;
if (g_strcmp0 (buffer, "N\n") == 0)
return FALSE;
} while (TRUE);
return FALSE;
}
static gboolean
fu_util_traverse_tree (GNode *n, gpointer data)
{
guint idx = g_node_depth (n) - 1;
g_autofree gchar *tmp = NULL;
g_auto(GStrv) split = NULL;
/* get split lines */
if (FWUPD_IS_DEVICE (n->data)) {
FwupdDevice *dev = FWUPD_DEVICE (n->data);
tmp = fu_util_device_to_string (dev, idx);
} else if (FWUPD_IS_REMOTE (n->data)) {
FwupdRemote *remote = FWUPD_REMOTE (n->data);
tmp = fu_util_remote_to_string (remote, idx);
} else if (FWUPD_IS_RELEASE (n->data)) {
FwupdRelease *release = FWUPD_RELEASE (n->data);
tmp = fu_util_release_to_string (release, idx);
g_debug ("%s", tmp);
}
/* root node */
if (n->data == NULL && g_getenv ("FWUPD_VERBOSE") == NULL) {
const gchar *str = data;
g_print ("%s\n│\n", str != NULL ? str : "○");
return FALSE;
}
if (n->parent == NULL)
return FALSE;
if (tmp == NULL)
return FALSE;
split = g_strsplit (tmp, "\n", -1);
for (guint i = 0; split[i] != NULL; i++) {
g_autoptr(GString) str = g_string_new (NULL);
/* header */
if (i == 0) {
if (g_node_next_sibling (n) == NULL)
g_string_prepend (str, "└─");
else
g_string_prepend (str, "├─");
/* properties */
} else {
g_string_prepend (str, n->children == NULL ? " " : " │");
g_string_prepend (str, g_node_next_sibling (n) == NULL ? " " : "│");
g_string_append (str, " ");
}
/* ancestors */
for (GNode *c = n->parent; c->parent != NULL; c = c->parent) {
if (g_node_next_sibling (c) != NULL || idx == 0) {
g_string_prepend (str, "│ ");
continue;
}
g_string_prepend (str, " ");
}
/* empty line */
if (split[i][0] == '\0') {
g_print ("%s\n", str->str);
continue;
}
/* dump to the console */
g_string_append (str, split[i] + (idx * 2));
g_print ("%s\n", str->str);
}
return FALSE;
}
void
fu_util_print_tree (GNode *n, gpointer data)
{
g_node_traverse (n, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
fu_util_traverse_tree, data);
}
static gboolean
fu_util_is_interesting_child (FwupdDevice *dev)
{
GPtrArray *children = fwupd_device_get_children (dev);
for (guint i = 0; i < children->len; i++) {
FwupdDevice *child = g_ptr_array_index (children, i);
if (fu_util_is_interesting_device (child))
return TRUE;
}
return FALSE;
}
gboolean
fu_util_is_interesting_device (FwupdDevice *dev)
{
if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
return TRUE;
if (fwupd_device_get_update_error (dev) != NULL)
return TRUE;
/* device not plugged in, get-details */
if (fwupd_device_get_flags (dev) == 0)
return TRUE;
if (fu_util_is_interesting_child (dev))
return TRUE;
return FALSE;
}
gchar *
fu_util_get_user_cache_path (const gchar *fn)
{
const gchar *root = g_get_user_cache_dir ();
g_autofree gchar *basename = g_path_get_basename (fn);
g_autofree gchar *cachedir_legacy = NULL;
/* if run from a systemd unit, use the cache directory set there */
if (g_getenv ("CACHE_DIRECTORY") != NULL)
root = g_getenv ("CACHE_DIRECTORY");
/* return the legacy path if it exists rather than renaming it to
* prevent problems when using old and new versions of fwupd */
cachedir_legacy = g_build_filename (root, "fwupdmgr", NULL);
if (g_file_test (cachedir_legacy, G_FILE_TEST_IS_DIR))
return g_build_filename (cachedir_legacy, basename, NULL);
return g_build_filename (root, "fwupd", basename, NULL);
}
gchar *
fu_util_get_versions (void)
{
GString *string = g_string_new ("");
g_string_append_printf (string, "client version:\t%s\n", SOURCE_VERSION);
g_string_append_printf (string,
"compile-time dependency versions\n");
#ifdef HAVE_GUSB
g_string_append_printf (string,
"\tgusb:\t%d.%d.%d\n",
G_USB_MAJOR_VERSION,
G_USB_MINOR_VERSION,
G_USB_MICRO_VERSION);
#endif
#ifdef EFIVAR_LIBRARY_VERSION
g_string_append_printf (string,
"\tefivar:\t%s",
EFIVAR_LIBRARY_VERSION);
#endif
return g_string_free (string, FALSE);
}
static gboolean
fu_util_update_shutdown (GError **error)
{
g_autoptr(GDBusConnection) connection = NULL;
g_autoptr(GVariant) val = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
if (connection == NULL)
return FALSE;
#ifdef HAVE_LOGIND
/* shutdown using logind */
val = g_dbus_connection_call_sync (connection,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"PowerOff",
g_variant_new ("(b)", TRUE),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
error);
#elif defined(HAVE_CONSOLEKIT)
/* shutdown using ConsoleKit */
val = g_dbus_connection_call_sync (connection,
"org.freedesktop.ConsoleKit",
"/org/freedesktop/ConsoleKit/Manager",
"org.freedesktop.ConsoleKit.Manager",
"Stop",
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
error);
#else
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"No supported backend compiled in to perform the operation.");
#endif
return val != NULL;
}
gboolean
fu_util_update_reboot (GError **error)
{
g_autoptr(GDBusConnection) connection = NULL;
g_autoptr(GVariant) val = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
if (connection == NULL)
return FALSE;
#ifdef HAVE_LOGIND
/* reboot using logind */
val = g_dbus_connection_call_sync (connection,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"Reboot",
g_variant_new ("(b)", TRUE),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
error);
#elif defined(HAVE_CONSOLEKIT)
/* reboot using ConsoleKit */
val = g_dbus_connection_call_sync (connection,
"org.freedesktop.ConsoleKit",
"/org/freedesktop/ConsoleKit/Manager",
"org.freedesktop.ConsoleKit.Manager",
"Restart",
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
error);
#else
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"No supported backend compiled in to perform the operation.");
#endif
return val != NULL;
}
gboolean
fu_util_prompt_warning (FwupdDevice *device, const gchar *machine, GError **error)
{
FwupdDeviceFlags flags;
g_autofree gchar *str = NULL;
/* device is already in bootloader mode */
flags = fwupd_device_get_flags (device);
if (flags & FWUPD_DEVICE_FLAG_IS_BOOTLOADER)
return TRUE;
/* device may reboot */
if ((flags & FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) == 0) {
/* TRANSLATORS: warn the user before updating, %1 is a device name */
str = g_strdup_printf (_("%s and all connected devices may not be usable while updating."),
fwupd_device_get_name (device));
/* device can get bricked */
} else if ((flags & FWUPD_DEVICE_FLAG_SELF_RECOVERY) == 0) {
/* external device */
if ((flags & FWUPD_DEVICE_FLAG_INTERNAL) == 0) {
/* TRANSLATORS: warn the user before updating, %1 is a device name */
str = g_strdup_printf (_("%s must remain connected for the duration of the update to avoid damage."),
fwupd_device_get_name (device));
} else if (flags & FWUPD_DEVICE_FLAG_REQUIRE_AC) {
/* TRANSLATORS: warn the user before updating, %1 is a machine name */
str = g_strdup_printf (_("%s must remain plugged into a power source for the duration of the update to avoid damage."),
machine);
}
}
if (str != NULL) {
g_print ("%s %s [Y|n]: ",
str,
/* TRANSLATORS: prompt to apply the update */
_("Continue with update?"));
if (!fu_util_prompt_for_boolean (TRUE)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Request canceled");
return FALSE;
}
}
return TRUE;
}
gboolean
fu_util_prompt_complete (FwupdDeviceFlags flags, gboolean prompt, GError **error)
{
if (flags & FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) {
if (prompt) {
g_print ("\n%s %s [y|N]: ",
/* TRANSLATORS: explain why we want to shutdown */
_("An update requires the system to shutdown to complete."),
/* TRANSLATORS: shutdown to apply the update */
_("Shutdown now?"));
if (!fu_util_prompt_for_boolean (FALSE))
return TRUE;
}
return fu_util_update_shutdown (error);
}
if (flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) {
if (prompt) {
g_print ("\n%s %s [y|N]: ",
/* TRANSLATORS: explain why we want to reboot */
_("An update requires a reboot to complete."),
/* TRANSLATORS: reboot to apply the update */
_("Restart now?"));
if (!fu_util_prompt_for_boolean (FALSE))
return TRUE;
}
return fu_util_update_reboot (error);
}
return TRUE;
}
static void
fu_util_cmd_free (FuUtilCmd *item)
{
g_free (item->name);
g_free (item->arguments);
g_free (item->description);
g_free (item);
}
GPtrArray *
fu_util_cmd_array_new (void)
{
return g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_cmd_free);
}
static gint
fu_util_cmd_sort_cb (FuUtilCmd **item1, FuUtilCmd **item2)
{
return g_strcmp0 ((*item1)->name, (*item2)->name);
}
void
fu_util_cmd_array_sort (GPtrArray *array)
{
g_ptr_array_sort (array, (GCompareFunc) fu_util_cmd_sort_cb);
}
void
fu_util_cmd_array_add (GPtrArray *array,
const gchar *name,
const gchar *arguments,
const gchar *description,
FuUtilCmdFunc callback)
{
g_auto(GStrv) names = NULL;
g_return_if_fail (name != NULL);
g_return_if_fail (description != NULL);
g_return_if_fail (callback != NULL);
/* add each one */
names = g_strsplit (name, ",", -1);
for (guint i = 0; names[i] != NULL; i++) {
FuUtilCmd *item = g_new0 (FuUtilCmd, 1);
item->name = g_strdup (names[i]);
if (i == 0) {
item->description = g_strdup (description);
} else {
/* TRANSLATORS: this is a command alias, e.g. 'get-devices' */
item->description = g_strdup_printf (_("Alias to %s"),
names[0]);
}
item->arguments = g_strdup (arguments);
item->callback = callback;
g_ptr_array_add (array, item);
}
}
gboolean
fu_util_cmd_array_run (GPtrArray *array,
FuUtilPrivate *priv,
const gchar *command,
gchar **values,
GError **error)
{
g_auto(GStrv) values_copy = g_new0 (gchar *, g_strv_length (values) + 1);
/* clear out bash completion sentinel */
for (guint i = 0; values[i] != NULL; i++) {
if (g_strcmp0 (values[i], "{") == 0)
break;
values_copy[i] = g_strdup (values[i]);
}
/* find command */
for (guint i = 0; i < array->len; i++) {
FuUtilCmd *item = g_ptr_array_index (array, i);
if (g_strcmp0 (item->name, command) == 0)
return item->callback (priv, values_copy, error);
}
/* not found */
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
/* TRANSLATORS: error message */
_("Command not found"));
return FALSE;
}
gchar *
fu_util_cmd_array_to_string (GPtrArray *array)
{
gsize len;
const gsize max_len = 35;
GString *string;
/* print each command */
string = g_string_new ("");
for (guint i = 0; i < array->len; i++) {
FuUtilCmd *item = g_ptr_array_index (array, i);
g_string_append (string, " ");
g_string_append (string, item->name);
len = fu_common_strwidth (item->name) + 2;
if (item->arguments != NULL) {
g_string_append (string, " ");
g_string_append (string, item->arguments);
len += fu_common_strwidth (item->arguments) + 1;
}
if (len < max_len) {
for (gsize j = len; j < max_len + 1; j++)
g_string_append_c (string, ' ');
g_string_append (string, item->description);
g_string_append_c (string, '\n');
} else {
g_string_append_c (string, '\n');
for (gsize j = 0; j < max_len + 1; j++)
g_string_append_c (string, ' ');
g_string_append (string, item->description);
g_string_append_c (string, '\n');
}
}
/* remove trailing newline */
if (string->len > 0)
g_string_set_size (string, string->len - 1);
return g_string_free (string, FALSE);
}
const gchar *
fu_util_branch_for_display (const gchar *branch)
{
if (branch == NULL) {
/* TRANSLATORS: this is the default branch name when unset */
return _("default");
}
return branch;
}
gchar *
fu_util_release_get_name (FwupdRelease *release)
{
const gchar *name = fwupd_release_get_name (release);
GPtrArray *cats = fwupd_release_get_categories (release);
for (guint i = 0; i < cats->len; i++) {
const gchar *cat = g_ptr_array_index (cats, i);
if (g_strcmp0 (cat, "X-Device") == 0) {
/* TRANSLATORS: a specific part of hardware,
* the first %s is the device name, e.g. 'Unifying Receiver` */
return g_strdup_printf (_("%s Device Update"), name);
}
if (g_strcmp0 (cat, "X-Configuration") == 0) {
/* TRANSLATORS: a specific part of hardware,
* the first %s is the device name, e.g. 'Secure Boot` */
return g_strdup_printf (_("%s Configuration Update"), name);
}
if (g_strcmp0 (cat, "X-System") == 0) {
/* TRANSLATORS: the entire system, e.g. all internal devices,
* the first %s is the device name, e.g. 'ThinkPad P50` */
return g_strdup_printf (_("%s System Update"), name);
}
if (g_strcmp0 (cat, "X-EmbeddedController") == 0) {
/* TRANSLATORS: the EC is typically the keyboard controller chip,
* the first %s is the device name, e.g. 'ThinkPad P50` */
return g_strdup_printf (_("%s Embedded Controller Update"), name);
}
if (g_strcmp0 (cat, "X-ManagementEngine") == 0) {
/* TRANSLATORS: ME stands for Management Engine, the Intel AMT thing,
* the first %s is the device name, e.g. 'ThinkPad P50` */
return g_strdup_printf (_("%s ME Update"), name);
}
if (g_strcmp0 (cat, "X-CorporateManagementEngine") == 0) {
/* TRANSLATORS: ME stands for Management Engine (with Intel AMT),
* where the first %s is the device name, e.g. 'ThinkPad P50` */
return g_strdup_printf (_("%s Corporate ME Update"), name);
}
if (g_strcmp0 (cat, "X-ConsumerManagementEngine") == 0) {
/* TRANSLATORS: ME stands for Management Engine, where
* the first %s is the device name, e.g. 'ThinkPad P50` */
return g_strdup_printf (_("%s Consumer ME Update"), name);
}
if (g_strcmp0 (cat, "X-Controller") == 0) {
/* TRANSLATORS: the controller is a device that has other devices
* plugged into it, for example ThunderBolt, FireWire or USB,
* the first %s is the device name, e.g. 'Intel ThunderBolt` */
return g_strdup_printf (_("%s Controller Update"), name);
}
if (g_strcmp0 (cat, "X-ThunderboltController") == 0) {
/* TRANSLATORS: the Thunderbolt controller is a device that
* has other high speed Thunderbolt devices plugged into it;
* the first %s is the system name, e.g. 'ThinkPad P50` */
return g_strdup_printf (_("%s Thunderbolt Controller Update"), name);
}
if (g_strcmp0 (cat, "X-CpuMicrocode") == 0) {
/* TRANSLATORS: the CPU microcode is firmware loaded onto the CPU
* at system bootup */
return g_strdup_printf (_("%s CPU Microcode Update"), name);
}
if (g_strcmp0 (cat, "X-Battery") == 0) {
/* TRANSLATORS: battery refers to the system power source */
return g_strdup_printf (_("%s Battery Update"), name);
}
if (g_strcmp0 (cat, "X-Camera") == 0) {
/* TRANSLATORS: camera can refer to the laptop internal
* camera in the bezel or external USB webcam */
return g_strdup_printf (_("%s Camera Update"), name);
}
if (g_strcmp0 (cat, "X-TPM") == 0) {
/* TRANSLATORS: TPM refers to a Trusted Platform Module */
return g_strdup_printf (_("%s TPM Update"), name);
}
if (g_strcmp0 (cat, "X-Touchpad") == 0) {
/* TRANSLATORS: TouchPad refers to a flat input device */
return g_strdup_printf (_("%s Touchpad Update"), name);
}
if (g_strcmp0 (cat, "X-Mouse") == 0) {
/* TRANSLATORS: Mouse refers to a handheld input device */
return g_strdup_printf (_("%s Mouse Update"), name);
}
if (g_strcmp0 (cat, "X-Keyboard") == 0) {
/* TRANSLATORS: Keyboard refers to an input device for typing */
return g_strdup_printf (_("%s Keyboard Update"), name);
}
}
/* TRANSLATORS: this is the fallback where we don't know if the release
* is updating the system, the device, or a device class, or something else --
* the first %s is the device name, e.g. 'ThinkPad P50` */
return g_strdup_printf (_("%s Update"), name);
}
static GPtrArray *
fu_util_strsplit_words (const gchar *text, guint line_len)
{
g_auto(GStrv) tokens = NULL;
g_autoptr(GPtrArray) lines = g_ptr_array_new ();
g_autoptr(GString) curline = g_string_new (NULL);
/* sanity check */
if (text == NULL || text[0] == '\0')
return NULL;
if (line_len == 0)
return NULL;
/* tokenize the string */
tokens = g_strsplit (text, " ", -1);
for (guint i = 0; tokens[i] != NULL; i++) {
/* current line plus new token is okay */
if (curline->len + fu_common_strwidth (tokens[i]) < line_len) {
g_string_append_printf (curline, "%s ", tokens[i]);
continue;
}
/* too long, so remove space, add newline and dump */
if (curline->len > 0)
g_string_truncate (curline, curline->len - 1);
g_ptr_array_add (lines, g_strdup (curline->str));
g_string_truncate (curline, 0);
g_string_append_printf (curline, "%s ", tokens[i]);
}
/* any incomplete line? */
if (curline->len > 0) {
g_string_truncate (curline, curline->len - 1);
g_ptr_array_add (lines, g_strdup (curline->str));
}
return g_steal_pointer (&lines);
}
static void
fu_util_warning_box_line (const gchar *start,
const gchar *text,
const gchar *end,
const gchar *padding,
guint width)
{
guint offset = 0;
if (start != NULL) {
offset += fu_common_strwidth (start);
g_print ("%s", start);
}
if (text != NULL) {
offset += fu_common_strwidth (text);
g_print ("%s", text);
}
if (end != NULL)
offset += fu_common_strwidth (end);
for (guint i = offset; i < width; i++)
g_print ("%s", padding);
if (end != NULL)
g_print ("%s\n", end);
}
void
fu_util_warning_box (const gchar *title, const gchar *body, guint width)
{
/* nothing to do */
if (title == NULL && body == NULL)
return;
/* header */
fu_util_warning_box_line ("╔", NULL, "╗", "═", width);
/* optional title */
if (title != NULL) {
g_autoptr(GPtrArray) lines = fu_util_strsplit_words (title, width - 4);
for (guint j = 0; j < lines->len; j++) {
const gchar *line = g_ptr_array_index (lines, j);
fu_util_warning_box_line ("║ ", line, " ║", " ", width);
}
}
/* join */
if (title != NULL && body != NULL)
fu_util_warning_box_line ("╠", NULL, "╣", "═", width);
/* optional body */
if (body != NULL) {
g_auto(GStrv) split = g_strsplit (body, "\n", -1);
for (guint i = 0; split[i] != NULL; i++) {
g_autoptr(GPtrArray) lines = fu_util_strsplit_words (split[i], width - 4);
if (lines == NULL)
continue;
for (guint j = 0; j < lines->len; j++) {
const gchar *line = g_ptr_array_index (lines, j);
fu_util_warning_box_line ("║ ", line, " ║", " ", width);
}
}
}
/* footer */
fu_util_warning_box_line ("╚", NULL, "╝", "═", width);
}
gboolean
fu_util_parse_filter_flags (const gchar *filter, FwupdDeviceFlags *include,
FwupdDeviceFlags *exclude, GError **error)
{
FwupdDeviceFlags tmp;
g_auto(GStrv) strv = g_strsplit (filter, ",", -1);
g_return_val_if_fail (include != NULL, FALSE);
g_return_val_if_fail (exclude != NULL, FALSE);
for (guint i = 0; strv[i] != NULL; i++) {
if (g_str_has_prefix (strv[i], "~")) {
tmp = fwupd_device_flag_from_string (strv[i] + 1);
if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Unknown device flag %s",
strv[i] + 1);
return FALSE;
}
if ((tmp & *include) > 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Filter %s already included",
fwupd_device_flag_to_string (tmp));
return FALSE;
}
if ((tmp & *exclude) > 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Filter %s already excluded",
fwupd_device_flag_to_string (tmp));
return FALSE;
}
*exclude |= tmp;
} else {
tmp = fwupd_device_flag_from_string (strv[i]);
if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Unknown device flag %s",
strv[i]);
return FALSE;
}
if ((tmp & *exclude) > 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Filter %s already excluded",
fwupd_device_flag_to_string (tmp));
return FALSE;
}
if ((tmp & *include) > 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Filter %s already included",
fwupd_device_flag_to_string (tmp));
return FALSE;
}
*include |= tmp;
}
}
return TRUE;
}
typedef struct {
guint cnt;
GString *str;
} FuUtilConvertHelper;
static gboolean
fu_util_convert_description_head_cb (XbNode *n, gpointer user_data)
{
FuUtilConvertHelper *helper = (FuUtilConvertHelper *) user_data;
helper->cnt++;
/* start */
if (g_strcmp0 (xb_node_get_element (n), "em") == 0) {
g_string_append (helper->str, "\033[3m");
} else if (g_strcmp0 (xb_node_get_element (n), "strong") == 0) {
g_string_append (helper->str, "\033[1m");
} else if (g_strcmp0 (xb_node_get_element (n), "code") == 0) {
g_string_append (helper->str, "`");
} else if (g_strcmp0 (xb_node_get_element (n), "li") == 0) {
g_string_append (helper->str, "• ");
} else if (g_strcmp0 (xb_node_get_element (n), "p") == 0 ||
g_strcmp0 (xb_node_get_element (n), "ul") == 0 ||
g_strcmp0 (xb_node_get_element (n), "ol") == 0) {
g_string_append (helper->str, "\n");
}
/* text */
if (xb_node_get_text (n) != NULL)
g_string_append (helper->str, xb_node_get_text (n));
return FALSE;
}
static gboolean
fu_util_convert_description_tail_cb (XbNode *n, gpointer user_data)
{
FuUtilConvertHelper *helper = (FuUtilConvertHelper *) user_data;
helper->cnt++;
/* end */
if (g_strcmp0 (xb_node_get_element (n), "em") == 0 ||
g_strcmp0 (xb_node_get_element (n), "strong") == 0) {
g_string_append (helper->str, "\033[0m");
} else if (g_strcmp0 (xb_node_get_element (n), "code") == 0) {
g_string_append (helper->str, "`");
} else if (g_strcmp0 (xb_node_get_element (n), "li") == 0) {
g_string_append (helper->str, "\n");
} else if (g_strcmp0 (xb_node_get_element (n), "p") == 0) {
g_string_append (helper->str, "\n");
}
/* tail */
if (xb_node_get_tail (n) != NULL)
g_string_append (helper->str, xb_node_get_tail (n));
return FALSE;
}
gchar *
fu_util_convert_description (const gchar *xml, GError **error)
{
g_autoptr(GString) str = g_string_new (NULL);
g_autoptr(XbNode) n = NULL;
g_autoptr(XbSilo) silo = NULL;
FuUtilConvertHelper helper = {
.cnt = 0,
.str = str,
};
/* parse XML */
silo = xb_silo_new_from_xml (xml, error);
if (silo == NULL)
return NULL;
/* convert to something we can show on the console */
n = xb_silo_get_root (silo);
xb_node_transmogrify (n,
fu_util_convert_description_head_cb,
fu_util_convert_description_tail_cb,
&helper);
/* success */
return fu_common_strstrip (str->str);
}
/**
* fu_util_time_to_str:
* @tmp: the time in seconds
*
* Converts a timestamp to a 'pretty' translated string
*
* Return value: (transfer full): A string
*
* Since: 1.3.7
**/
gchar *
fu_util_time_to_str (guint64 tmp)
{
g_return_val_if_fail (tmp != 0, NULL);
/* seconds */
if (tmp < 60) {
/* TRANSLATORS: duration in seconds */
return g_strdup_printf (ngettext ("%u second", "%u seconds",
(gint) tmp),
(guint) tmp);
}
/* minutes */
tmp /= 60;
if (tmp < 60) {
/* TRANSLATORS: duration in minutes */
return g_strdup_printf (ngettext ("%u minute", "%u minutes",
(gint) tmp),
(guint) tmp);
}
/* hours */
tmp /= 60;
if (tmp < 60) {
/* TRANSLATORS: duration in minutes */
return g_strdup_printf (ngettext ("%u hour", "%u hours",
(gint) tmp),
(guint) tmp);
}
/* days */
tmp /= 24;
/* TRANSLATORS: duration in days! */
return g_strdup_printf (ngettext ("%u day", "%u days",
(gint) tmp),
(guint) tmp);
}
static gchar *
fu_util_device_flag_to_string (guint64 device_flag)
{
if (device_flag == FWUPD_DEVICE_FLAG_NONE) {
return NULL;
}
if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) {
/* TRANSLATORS: Device cannot be removed easily*/
return _("Internal device");
}
if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE ||
device_flag == FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN) {
/* TRANSLATORS: Device is updatable in this or any other mode */
return _("Updatable");
}
if (device_flag == FWUPD_DEVICE_FLAG_ONLY_OFFLINE) {
/* TRANSLATORS: Update can only be done from offline mode */
return _("Update requires a reboot");
}
if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) {
/* TRANSLATORS: Must be plugged in to an outlet */
return _("System requires external power source");
}
if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) {
/* TRANSLATORS: Is locked and can be unlocked */
return _("Device is locked");
}
if (device_flag == FWUPD_DEVICE_FLAG_SUPPORTED) {
/* TRANSLATORS: Is found in current metadata */
return _("Supported on remote server");
}
if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) {
/* TRANSLATORS: Requires a bootloader mode to be manually enabled by the user */
return _("Requires a bootloader");
}
if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_REBOOT) {
/* TRANSLATORS: Requires a reboot to apply firmware or to reload hardware */
return _("Needs a reboot after installation");
}
if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) {
/* TRANSLATORS: Requires system shutdown to apply firmware */
return _("Needs shutdown after installation");
}
if (device_flag == FWUPD_DEVICE_FLAG_REPORTED) {
/* TRANSLATORS: Has been reported to a metadata server */
return _("Reported to remote server");
}
if (device_flag == FWUPD_DEVICE_FLAG_NOTIFIED) {
/* TRANSLATORS: User has been notified */
return _("User has been notified");
}
if (device_flag == FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) {
/* skip */
return NULL;
}
if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST) {
/* TRANSLATORS: Install composite firmware on the parent before the child */
return _("Install to parent device first");
}
if (device_flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) {
/* TRANSLATORS: Is currently in bootloader mode */
return _("Is in bootloader mode");
}
if (device_flag == FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) {
/* TRANSLATORS: The hardware is waiting to be replugged */
return _("Hardware is waiting to be replugged");
}
if (device_flag == FWUPD_DEVICE_FLAG_IGNORE_VALIDATION) {
/* TRANSLATORS: Ignore validation safety checks when flashing this device */
return _("Ignore validation safety checks");
}
if (device_flag == FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED) {
/* skip */
return NULL;
}
if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) {
/* TRANSLATORS: Device update needs to be separately activated */
return _("Device update needs activation");
}
if (device_flag == FWUPD_DEVICE_FLAG_HISTORICAL) {
/* skip */
return NULL;
}
if (device_flag == FWUPD_DEVICE_FLAG_WILL_DISAPPEAR) {
/* TRANSLATORS: Device will not return after update completes */
return _("Device will not re-appear after update completes");
}
if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY) {
/* TRANSLATORS: Device supports some form of checksum verification */
return _("Cryptographic hash verification is available");
}
if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE) {
/* skip */
return NULL;
}
if (device_flag == FWUPD_DEVICE_FLAG_DUAL_IMAGE) {
/* TRANSLATORS: Device supports a safety mechanism for flashing */
return _("Device stages updates");
}
if (device_flag == FWUPD_DEVICE_FLAG_SELF_RECOVERY) {
/* TRANSLATORS: Device supports a safety mechanism for flashing */
return _("Device can recover flash failures");
}
if (device_flag == FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) {
/* TRANSLATORS: Device remains usable during update */
return _("Device is usable for the duration of the update");
}
if (device_flag == FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED) {
/* TRANSLATORS: a version check is required for all firmware */
return _("Device firmware is required to have a version check");
}
if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES) {
/* TRANSLATORS: a version check is required for all firmware */
return _("Device is required to install all provided releases");
}
if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) {
/* TRANSLATORS: there is more than one supplier of the firmware */
return _("Device supports switching to a different branch of firmware");
}
if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) {
/* TRANSLATORS: save the old firmware to disk before installing the new one */
return _("Device will backup firmware before installing");
}
if (device_flag == FWUPD_DEVICE_FLAG_SKIPS_RESTART) {
/* skip */
return NULL;
}
if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) {
return NULL;
}
return NULL;
}
static const gchar *
fu_util_update_state_to_string (FwupdUpdateState update_state)
{
if (update_state == FWUPD_UPDATE_STATE_PENDING) {
/* TRANSLATORS: The update state of the specific device */
return _("Pending");
}
if (update_state == FWUPD_UPDATE_STATE_SUCCESS) {
/* TRANSLATORS: The update state of the specific device */
return _("Success");
}
if (update_state == FWUPD_UPDATE_STATE_FAILED) {
/* TRANSLATORS: The update state of the specific device */
return _("Failed");
}
if (update_state == FWUPD_UPDATE_STATE_FAILED_TRANSIENT) {
/* TRANSLATORS: The update state of the specific device */
return _("Transient failure");
}
if (update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT) {
/* TRANSLATORS: The update state of the specific device */
return _("Needs reboot");
}
return NULL;
}
gchar *
fu_util_device_to_string (FwupdDevice *dev, guint idt)
{
FwupdUpdateState state;
GPtrArray *guids = fwupd_device_get_guids (dev);
GPtrArray *vendor_ids = fwupd_device_get_vendor_ids (dev);
GPtrArray *instance_ids = fwupd_device_get_instance_ids (dev);
const gchar *tmp;
const gchar *tmp2;
guint64 flags = fwupd_device_get_flags (dev);
guint64 modified = fwupd_device_get_modified (dev);
g_autoptr(GHashTable) ids = NULL;
g_autoptr(GString) str = g_string_new (NULL);
/* some fields are intentionally not included and are only shown in --verbose */
if (g_getenv ("FWUPD_VERBOSE") != NULL) {
g_autofree gchar *debug_str = NULL;
debug_str = fwupd_device_to_string (dev);
g_debug ("%s", debug_str);
return NULL;
}
tmp = fwupd_device_get_name (dev);
if (tmp == NULL) {
/* TRANSLATORS: Name of hardware */
tmp = _("Unknown Device");
}
fu_common_string_append_kv (str, idt, tmp, NULL);
tmp = fwupd_device_get_id (dev);
if (tmp != NULL) {
/* TRANSLATORS: ID for hardware, typically a SHA1 sum */
fu_common_string_append_kv (str, idt + 1, _("Device ID"), tmp);
}
/* summary */
tmp = fwupd_device_get_summary (dev);
if (tmp != NULL) {
/* TRANSLATORS: one line summary of device */
fu_common_string_append_kv (str, idt + 1, _("Summary"), tmp);
}
/* description */
tmp = fwupd_device_get_description (dev);
if (tmp != NULL) {
g_autofree gchar *desc = NULL;
desc = fu_util_convert_description (tmp, NULL);
/* TRANSLATORS: multiline description of device */
fu_common_string_append_kv (str, idt + 1, _("Description"), desc);
}
/* versions */
tmp = fwupd_device_get_version (dev);
if (tmp != NULL) {
if (flags & FWUPD_DEVICE_FLAG_HISTORICAL) {
/* TRANSLATORS: version number of previous firmware */
fu_common_string_append_kv (str, idt + 1, _("Previous version"), tmp);
} else {
/* TRANSLATORS: version number of current firmware */
fu_common_string_append_kv (str, idt + 1, _("Current version"), tmp);
}
}
tmp = fwupd_device_get_version_lowest (dev);
if (tmp != NULL) {
/* TRANSLATORS: smallest version number installable on device */
fu_common_string_append_kv (str, idt + 1, _("Minimum Version"), tmp);
}
tmp = fwupd_device_get_version_bootloader (dev);
if (tmp != NULL) {
/* TRANSLATORS: firmware version of bootloader */
fu_common_string_append_kv (str, idt + 1, _("Bootloader Version"), tmp);
}
/* vendor */
tmp = fwupd_device_get_vendor (dev);
if (tmp != NULL && vendor_ids->len > 0) {
g_autofree gchar *strv = fu_common_strjoin_array ("|", vendor_ids);
g_autofree gchar *both = g_strdup_printf ("%s (%s)", tmp, strv);
/* TRANSLATORS: manufacturer of hardware */
fu_common_string_append_kv (str, idt + 1, _("Vendor"), both);
} else if (tmp != NULL) {
/* TRANSLATORS: manufacturer of hardware */
fu_common_string_append_kv (str, idt + 1, _("Vendor"), tmp);
} else if (vendor_ids->len > 0) {
g_autofree gchar *strv = fu_common_strjoin_array ("|", vendor_ids);
/* TRANSLATORS: manufacturer of hardware */
fu_common_string_append_kv (str, idt + 1, _("Vendor"), strv);
}
/* branch */
if (fwupd_device_get_branch (dev) != NULL) {
/* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */
fu_common_string_append_kv (str, idt + 1, _("Release Branch"),
fwupd_device_get_branch (dev));
}
/* install duration */
if (fwupd_device_get_install_duration (dev) > 0) {
g_autofree gchar *time = fu_util_time_to_str (fwupd_device_get_install_duration (dev));
/* TRANSLATORS: length of time the update takes to apply */
fu_common_string_append_kv (str, idt + 1, _("Install Duration"), time);
}
/* serial # */
tmp = fwupd_device_get_serial (dev);
if (tmp != NULL) {
/* TRANSLATORS: serial number of hardware */
fu_common_string_append_kv (str, idt + 1, _("Serial Number"), tmp);
}
/* update state */
state = fwupd_device_get_update_state (dev);
if (state != FWUPD_UPDATE_STATE_UNKNOWN) {
/* TRANSLATORS: hardware state, e.g. "pending" */
fu_common_string_append_kv (str, idt + 1, _("Update State"),
fu_util_update_state_to_string (state));
if (state == FWUPD_UPDATE_STATE_SUCCESS) {
tmp = fwupd_device_get_update_message (dev);
if (tmp != NULL) {
/* TRANSLATORS: helpful messages from last update */
fu_common_string_append_kv (str, idt + 1, _("Update Message"), tmp);
}
}
}
tmp = fwupd_device_get_update_error (dev);
if (tmp != NULL) {
/* TRANSLATORS: error message from last update attempt */
fu_common_string_append_kv (str, idt + 1, _("Update Error"), tmp);
}
/* modified date: for history devices */
if (modified > 0) {
g_autoptr(GDateTime) date = NULL;
g_autofree gchar *time_str = NULL;
date = g_date_time_new_from_unix_utc (modified);
time_str = g_date_time_format (date, "%F %R");
/* TRANSLATORS: the original time/date the device was modified */
fu_common_string_append_kv (str, idt +1, _("Last modified"), time_str);
}
/* all GUIDs for this hardware, with IDs if available */
ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
for (guint i = 0; i < instance_ids->len; i++) {
const gchar *instance_id = g_ptr_array_index (instance_ids, i);
g_hash_table_insert (ids,
fwupd_guid_hash_string (instance_id),
g_strdup (instance_id));
}
for (guint i = 0; i < guids->len; i++) {
const gchar *guid = g_ptr_array_index (guids, i);
const gchar *instance_id = g_hash_table_lookup (ids, guid);
g_autofree gchar *guid_src = NULL;
/* instance IDs are only available as root */
if (instance_id == NULL) {
guid_src = g_strdup (guid);
} else {
guid_src = g_strdup_printf ("%s ← %s", guid, instance_id);
}
if (i == 0) {
/* TRANSLATORS: global ID common to all similar hardware */
fu_common_string_append_kv (str, idt + 1, ngettext ("GUID", "GUIDs", guids->len), guid_src);
} else {
fu_common_string_append_kv (str, idt + 1, "", guid_src);
}
}
/* TRANSLATORS: description of device ability */
tmp = _("Device Flags");
for (guint i = 0; i < 64; i++) {
if ((flags & ((guint64) 1 << i)) == 0)
continue;
tmp2 = fu_util_device_flag_to_string ((guint64) 1 << i);
if (tmp2 == NULL)
continue;
/* header */
if (tmp != NULL) {
g_autofree gchar *bullet = NULL;
bullet = g_strdup_printf ("• %s", tmp2);
fu_common_string_append_kv (str, idt + 1, tmp, bullet);
tmp = NULL;
} else {
g_autofree gchar *bullet = NULL;
bullet = g_strdup_printf ("• %s", tmp2);
fu_common_string_append_kv (str, idt + 1, "", bullet);
}
}
return g_string_free (g_steal_pointer (&str), FALSE);
}
const gchar *
fu_util_plugin_flag_to_string (FwupdPluginFlags plugin_flag)
{
if (plugin_flag == FWUPD_PLUGIN_FLAG_UNKNOWN)
return NULL;
if (plugin_flag == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE)
return NULL;
if (plugin_flag == FWUPD_PLUGIN_FLAG_USER_WARNING)
return NULL;
if (plugin_flag == FWUPD_PLUGIN_FLAG_REQUIRE_HWID)
return NULL;
if (plugin_flag == FWUPD_PLUGIN_FLAG_NONE) {
/* TRANSLATORS: Plugin is active and in use */
return _("Enabled");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_DISABLED) {
/* TRANSLATORS: Plugin is inactive and not used */
return _("Disabled");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_NO_HARDWARE) {
/* TRANSLATORS: not required for this system */
return _("Required hardware was not found");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_LEGACY_BIOS) {
/* TRANSLATORS: system is not booted in UEFI mode */
return _("UEFI firmware can not be updated in legacy BIOS mode");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED) {
/* TRANSLATORS: capsule updates are an optional BIOS feature */
return _("UEFI capsule updates not available or enabled in firmware setup");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED) {
/* TRANSLATORS: user needs to run a command */
return _("Firmware updates disabled; run 'fwupdmgr unlock' to enable");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) {
/* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */
return _("Required efivarfs filesystem was not found");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND) {
/* TRANSLATORS: partition refers to something on disk, again, hey Arch users */
return _("UEFI ESP partition not detected or configured");
}
if (plugin_flag == FWUPD_PLUGIN_FLAG_FAILED_OPEN) {
/* TRANSLATORS: Failed to open plugin, hey Arch users */
return _("Plugin dependencies missing");
}
/* fall back for unknown types */
return fwupd_plugin_flag_to_string (plugin_flag);
}
static gchar *
fu_util_plugin_flag_to_cli_text (FwupdPluginFlags plugin_flag)
{
switch (plugin_flag) {
case FWUPD_PLUGIN_FLAG_UNKNOWN:
case FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE:
case FWUPD_PLUGIN_FLAG_USER_WARNING:
case FWUPD_PLUGIN_FLAG_REQUIRE_HWID:
return NULL;
case FWUPD_PLUGIN_FLAG_NONE:
return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag),
FU_UTIL_CLI_COLOR_GREEN);
case FWUPD_PLUGIN_FLAG_DISABLED:
case FWUPD_PLUGIN_FLAG_NO_HARDWARE:
return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag),
FU_UTIL_CLI_COLOR_BLACK);
case FWUPD_PLUGIN_FLAG_LEGACY_BIOS:
case FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED:
case FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED:
case FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED:
case FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND:
return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag),
FU_UTIL_TERM_COLOR_RED);
default:
break;
}
/* fall back for unknown types */
return g_strdup (fwupd_plugin_flag_to_string (plugin_flag));
}
gchar *
fu_util_plugin_to_string (FwupdPlugin *plugin, guint idt)
{
GString *str = g_string_new (NULL);
const gchar *hdr;
guint64 flags = fwupd_plugin_get_flags (plugin);
fu_common_string_append_kv (str, idt, fwupd_plugin_get_name (plugin), NULL);
/* TRANSLATORS: description of plugin state, e.g. disabled */
hdr = _("Flags");
if (flags == 0x0) {
const gchar *tmp = fu_util_plugin_flag_to_cli_text (flags);
g_autofree gchar *li = g_strdup_printf ("• %s", tmp);
fu_common_string_append_kv (str, idt + 1, hdr, li);
} else {
for (guint i = 0; i < 64; i++) {
g_autofree gchar *li = NULL;
g_autofree gchar *tmp = NULL;
if ((flags & ((guint64) 1 << i)) == 0)
continue;
tmp = fu_util_plugin_flag_to_cli_text ((guint64) 1 << i);
if (tmp == NULL)
continue;
li = g_strdup_printf ("• %s", tmp);
fu_common_string_append_kv (str, idt + 1, hdr, li);
/* clear header */
hdr = "";
}
}
return g_string_free (str, FALSE);
}
static const gchar *
fu_util_license_to_string (const gchar *license)
{
if (license == NULL) {
/* TRANSLATORS: we don't know the license of the update */
return _("Unknown");
}
if (g_strcmp0 (license, "LicenseRef-proprietary") == 0 ||
g_strcmp0 (license, "proprietary") == 0) {
/* TRANSLATORS: a non-free software license */
return _("Proprietary");
}
return license;
}
static const gchar *
fu_util_release_urgency_to_string (FwupdReleaseUrgency release_urgency)
{
if (release_urgency == FWUPD_RELEASE_URGENCY_LOW) {
/* TRANSLATORS: the release urgency */
return _("Low");
}
if (release_urgency == FWUPD_RELEASE_URGENCY_MEDIUM) {
/* TRANSLATORS: the release urgency */
return _("Medium");
}
if (release_urgency == FWUPD_RELEASE_URGENCY_HIGH) {
/* TRANSLATORS: the release urgency */
return _("High");
}
if (release_urgency == FWUPD_RELEASE_URGENCY_CRITICAL) {
/* TRANSLATORS: the release urgency */
return _("Critical");
}
/* TRANSLATORS: unknown release urgency */
return _("Unknown");
}
gchar *
fu_util_release_to_string (FwupdRelease *rel, guint idt)
{
GPtrArray *issues = fwupd_release_get_issues (rel);
GString *str = g_string_new (NULL);
guint64 flags = fwupd_release_get_flags (rel);
g_autoptr(GString) flags_str = g_string_new (NULL);
g_return_val_if_fail (FWUPD_IS_RELEASE (rel), NULL);
fu_common_string_append_kv (str, idt, fwupd_release_get_name (rel), NULL);
/* TRANSLATORS: version number of new firmware */
fu_common_string_append_kv (str, idt + 1 , _("New version"),
fwupd_release_get_version (rel));
if (fwupd_release_get_remote_id (rel) != NULL) {
/* TRANSLATORS: the server the file is coming from */
fu_common_string_append_kv (str, idt + 1, _("Remote ID"),
fwupd_release_get_remote_id (rel));
}
if (fwupd_release_get_branch (rel) != NULL) {
/* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */
fu_common_string_append_kv (str, idt + 1, _("Branch"),
fwupd_release_get_branch (rel));
}
if (fwupd_release_get_summary (rel) != NULL) {
/* TRANSLATORS: one line summary of device */
fu_common_string_append_kv (str, idt + 1, _("Summary"),
fwupd_release_get_summary (rel));
}
if (fwupd_release_get_name_variant_suffix (rel) != NULL) {
/* TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') */
fu_common_string_append_kv (str, idt + 1, _("Variant"),
fwupd_release_get_name_variant_suffix (rel));
}
/* TRANSLATORS: e.g. GPLv2+, Proprietary etc */
fu_common_string_append_kv (str, idt + 1, _("License"),
fu_util_license_to_string (fwupd_release_get_license (rel)));
if (fwupd_release_get_size (rel) != 0) {
g_autofree gchar *tmp = NULL;
tmp = g_format_size (fwupd_release_get_size (rel));
/* TRANSLATORS: file size of the download */
fu_common_string_append_kv (str, idt + 1, _("Size"), tmp);
}
if (fwupd_release_get_created (rel) != 0) {
gint64 value = (gint64) fwupd_release_get_created (rel);
g_autoptr(GDateTime) date = g_date_time_new_from_unix_utc (value);
g_autofree gchar *tmp = g_date_time_format (date, "%F");
/* TRANSLATORS: when the update was built */
fu_common_string_append_kv (str, idt + 1, _("Created"), tmp);
}
if (fwupd_release_get_urgency (rel) != FWUPD_RELEASE_URGENCY_UNKNOWN) {
FwupdReleaseUrgency tmp = fwupd_release_get_urgency (rel);
/* TRANSLATORS: how important the release is */
fu_common_string_append_kv (str, idt + 1, _("Urgency"),
fu_util_release_urgency_to_string (tmp));
}
if (fwupd_release_get_details_url (rel) != NULL) {
/* TRANSLATORS: more details about the update link */
fu_common_string_append_kv (str, idt + 1, _("Details"),
fwupd_release_get_details_url (rel));
}
if (fwupd_release_get_source_url (rel) != NULL) {
/* TRANSLATORS: source (as in code) link */
fu_common_string_append_kv (str, idt + 1, _("Source"),
fwupd_release_get_source_url (rel));
}
if (fwupd_release_get_vendor (rel) != NULL) {
/* TRANSLATORS: manufacturer of hardware */
fu_common_string_append_kv (str, idt + 1, _("Vendor"),
fwupd_release_get_vendor (rel));
}
if (fwupd_release_get_install_duration (rel) != 0) {
g_autofree gchar *tmp = fu_util_time_to_str (fwupd_release_get_install_duration (rel));
/* TRANSLATORS: length of time the update takes to apply */
fu_common_string_append_kv (str, idt + 1, _("Duration"), tmp);
}
if (fwupd_release_get_update_message (rel) != NULL) {
/* TRANSLATORS: helpful messages for the update */
fu_common_string_append_kv (str, idt + 1, _("Update Message"),
fwupd_release_get_update_message (rel));
}
for (guint i = 0; i < 64; i++) {
if ((flags & ((guint64) 1 << i)) == 0)
continue;
g_string_append_printf (flags_str, "%s|",
fwupd_release_flag_to_string ((guint64) 1 << i));
}
if (flags_str->len > 0) {
g_string_truncate (flags_str, flags_str->len - 1);
/* TRANSLATORS: release properties */
fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str);
}
if (fwupd_release_get_description (rel) != NULL) {
g_autofree gchar *desc = NULL;
desc = fu_util_convert_description (fwupd_release_get_description (rel), NULL);
/* TRANSLATORS: multiline description of device */
fu_common_string_append_kv (str, idt + 1, _("Description"), desc);
}
for (guint i = 0; i < issues->len; i++) {
const gchar *issue = g_ptr_array_index (issues, i);
if (i == 0) {
/* TRANSLATORS: issue fixed with the release, e.g. CVE */
fu_common_string_append_kv (str, idt + 1, ngettext ("Issue", "Issues", issues->len), issue);
} else {
fu_common_string_append_kv (str, idt + 1, "", issue);
}
}
return g_string_free (str, FALSE);
}
gchar *
fu_util_remote_to_string (FwupdRemote *remote, guint idt)
{
GString *str = g_string_new (NULL);
FwupdRemoteKind kind = fwupd_remote_get_kind (remote);
FwupdKeyringKind keyring_kind = fwupd_remote_get_keyring_kind (remote);
const gchar *tmp;
gint priority;
g_return_val_if_fail (FWUPD_IS_REMOTE (remote), NULL);
fu_common_string_append_kv (str, idt,
fwupd_remote_get_title (remote), NULL);
/* TRANSLATORS: remote identifier, e.g. lvfs-testing */
fu_common_string_append_kv (str, idt + 1, _("Remote ID"),
fwupd_remote_get_id (remote));
/* TRANSLATORS: remote type, e.g. remote or local */
fu_common_string_append_kv (str, idt + 1, _("Type"),
fwupd_remote_kind_to_string (kind));
/* TRANSLATORS: keyring type, e.g. GPG or PKCS7 */
if (keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) {
fu_common_string_append_kv (str, idt + 1, _("Keyring"),
fwupd_keyring_kind_to_string (keyring_kind));
}
/* TRANSLATORS: if the remote is enabled */
fu_common_string_append_kv (str, idt + 1, _("Enabled"),
fwupd_remote_get_enabled (remote) ? "true" : "false");
tmp = fwupd_remote_get_checksum (remote);
if (tmp != NULL) {
/* TRANSLATORS: remote checksum */
fu_common_string_append_kv (str, idt + 1, _("Checksum"), tmp);
}
/* optional parameters */
if (kind == FWUPD_REMOTE_KIND_DOWNLOAD &&
fwupd_remote_get_age (remote) > 0 &&
fwupd_remote_get_age (remote) != G_MAXUINT64) {
const gchar *unit = "s";
gdouble age = fwupd_remote_get_age (remote);
g_autofree gchar *age_str = NULL;
if (age > 60) {
age /= 60.f;
unit = "m";
}
if (age > 60) {
age /= 60.f;
unit = "h";
}
if (age > 24) {
age /= 24.f;
unit = "d";
}
if (age > 7) {
age /= 7.f;
unit = "w";
}
age_str = g_strdup_printf ("%.2f%s", age, unit);
/* TRANSLATORS: the age of the metadata */
fu_common_string_append_kv (str, idt + 1, _("Age"), age_str);
}
priority = fwupd_remote_get_priority (remote);
if (priority != 0) {
g_autofree gchar *priority_str = NULL;
priority_str = g_strdup_printf ("%i", priority);
/* TRANSLATORS: the numeric priority */
fu_common_string_append_kv (str, idt + 1, _("Priority"), priority_str);
}
tmp = fwupd_remote_get_username (remote);
if (tmp != NULL) {
/* TRANSLATORS: remote filename base */
fu_common_string_append_kv (str, idt + 1, _("Username"), tmp);
}
tmp = fwupd_remote_get_password (remote);
if (tmp != NULL) {
g_autofree gchar *hidden = g_strnfill (fu_common_strwidth (tmp), '*');
/* TRANSLATORS: remote filename base */
fu_common_string_append_kv (str, idt + 1, _("Password"), hidden);
}
tmp = fwupd_remote_get_filename_cache (remote);
if (tmp != NULL) {
/* TRANSLATORS: filename of the local file */
fu_common_string_append_kv (str, idt + 1, _("Filename"), tmp);
}
tmp = fwupd_remote_get_filename_cache_sig (remote);
if (tmp != NULL) {
/* TRANSLATORS: filename of the local file */
fu_common_string_append_kv (str, idt + 1, _("Filename Signature"), tmp);
}
tmp = fwupd_remote_get_metadata_uri (remote);
if (tmp != NULL) {
/* TRANSLATORS: remote URI */
fu_common_string_append_kv (str, idt + 1, _("Metadata URI"), tmp);
}
tmp = fwupd_remote_get_metadata_uri_sig (remote);
if (tmp != NULL) {
/* TRANSLATORS: remote URI */
fu_common_string_append_kv (str, idt + 1, _("Metadata Signature"), tmp);
}
tmp = fwupd_remote_get_firmware_base_uri (remote);
if (tmp != NULL) {
/* TRANSLATORS: remote URI */
fu_common_string_append_kv (str, idt + 1, _("Firmware Base URI"), tmp);
}
tmp = fwupd_remote_get_report_uri (remote);
if (tmp != NULL) {
/* TRANSLATORS: URI to send success/failure reports */
fu_common_string_append_kv (str, idt + 1, _("Report URI"), tmp);
/* TRANSLATORS: Boolean value to automatically send reports */
fu_common_string_append_kv (str, idt + 1, _("Automatic Reporting"),
fwupd_remote_get_automatic_reports (remote) ? "true" : "false");
}
return g_string_free (str, FALSE);
}
static void
fu_security_attr_append_str (FwupdSecurityAttr *attr, GString *str, FuSecurityAttrToStringFlags flags)
{
g_autofree gchar *name = NULL;
/* hide obsoletes by default */
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED) &&
(flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES) == 0)
return;
name = fu_security_attr_get_name (attr);
if (name == NULL)
name = g_strdup (fwupd_security_attr_get_appstream_id (attr));
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) {
g_string_append (str, "✦ ");
} else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) {
g_string_append (str, "✔ ");
} else {
g_string_append (str, "✘ ");
}
g_string_append_printf (str, "%s:", name);
for (guint i = fu_common_strwidth (name); i < 30; i++)
g_string_append (str, " ");
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) {
g_string_append_printf (str, "\033[37m\033[1m%s\033[0m", fu_security_attr_get_result (attr));
} else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) {
g_string_append_printf (str, "\033[32m\033[1m%s\033[0m", fu_security_attr_get_result (attr));
} else {
g_string_append_printf (str, "\033[31m\033[1m%s\033[0m", fu_security_attr_get_result (attr));
}
if ((flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS) > 0 &&
fwupd_security_attr_get_url (attr) != NULL) {
g_string_append_printf (str, ": %s",
fwupd_security_attr_get_url (attr));
}
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) {
/* TRANSLATORS: this is shown as a suffix for obsoleted tests */
g_string_append_printf (str, " %s", _("(obsoleted)"));
}
g_string_append_printf (str, "\n");
}
gchar *
fu_util_security_attrs_to_string (GPtrArray *attrs, FuSecurityAttrToStringFlags strflags)
{
FwupdSecurityAttrFlags flags = FWUPD_SECURITY_ATTR_FLAG_NONE;
const FwupdSecurityAttrFlags hpi_suffixes[] = {
FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE,
FWUPD_SECURITY_ATTR_FLAG_NONE,
};
GString *str = g_string_new (NULL);
gboolean low_help = FALSE;
gboolean runtime_help = FALSE;
gboolean pcr0_help = FALSE;
for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) {
gboolean has_header = FALSE;
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
if (fwupd_security_attr_get_level (attr) != j)
continue;
if (!has_header) {
g_string_append_printf (str, "\n\033[1mHSI-%u\033[0m\n", j);
has_header = TRUE;
}
fu_security_attr_append_str (attr, str, strflags);
/* make sure they have at least HSI-1 */
if (j < FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT &&
!fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
low_help = TRUE;
/* check for PCR0 not matching */
if (g_strcmp0 (fwupd_security_attr_get_appstream_id (attr),
FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0 &&
fwupd_security_attr_get_result (attr) == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID)
pcr0_help = TRUE;
}
}
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
flags |= fwupd_security_attr_get_flags (attr);
}
for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) {
if (flags & hpi_suffixes[j]) {
g_string_append_printf (str, "\n\033[1m%s -%s\033[0m\n",
/* TRANSLATORS: this is the HSI suffix */
_("Runtime Suffix"),
fwupd_security_attr_flag_to_suffix (hpi_suffixes[j]));
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
if (!fwupd_security_attr_has_flag (attr, hpi_suffixes[j]))
continue;
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) &&
!fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
runtime_help = TRUE;
fu_security_attr_append_str (attr, str, strflags);
}
}
}
if (low_help) {
g_string_append_printf (str, "\n%s\n » %s\n",
/* TRANSLATORS: this is instructions on how to improve the HSI security level */
_("This system has a low HSI security level."),
"https://github.com/fwupd/fwupd/wiki/Low-host-security-level");
}
if (runtime_help) {
g_string_append_printf (str, "\n%s\n » %s\n",
/* TRANSLATORS: this is instructions on how to improve the HSI suffix */
_("This system has HSI runtime issues."),
"https://github.com/fwupd/fwupd/wiki/Host-security-ID-runtime-issues");
}
if (pcr0_help) {
g_string_append_printf (str, "\n%s\n » %s\n",
/* TRANSLATORS: this is more background on a security measurement problem */
_("The TPM PCR0 differs from reconstruction."),
"https://github.com/fwupd/fwupd/wiki/TPM-PCR0-differs-from-reconstruction");
}
return g_string_free (str, FALSE);
}
gboolean
fu_util_send_report (FwupdClient *client,
const gchar *report_uri,
const gchar *data,
const gchar *sig,
gchar **uri, /* (nullable) (out) */
GError **error)
{
const gchar *server_msg = NULL;
JsonNode *json_root;
JsonObject *json_object;
g_autofree gchar *str = NULL;
g_autoptr(GBytes) upload_response = NULL;
g_autoptr(JsonParser) json_parser = NULL;
/* POST request */
upload_response = fwupd_client_upload_bytes (client, report_uri, data, sig,
FWUPD_CLIENT_UPLOAD_FLAG_NONE,
NULL, error);
if (upload_response == NULL)
return FALSE;
/* server returned nothing, and probably exploded in a ball of flames */
if (g_bytes_get_size (upload_response) == 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Failed to upload to %s",
report_uri);
return FALSE;
}
/* parse JSON reply */
json_parser = json_parser_new ();
str = g_strndup (g_bytes_get_data (upload_response, NULL),
g_bytes_get_size (upload_response));
if (!json_parser_load_from_data (json_parser, str, -1, error)) {
g_prefix_error (error, "Failed to parse JSON response from '%s': ", str);
return FALSE;
}
json_root = json_parser_get_root (json_parser);
if (json_root == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_PERMISSION_DENIED,
"JSON response was malformed: '%s'", str);
return FALSE;
}
json_object = json_node_get_object (json_root);
if (json_object == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_PERMISSION_DENIED,
"JSON response object was malformed: '%s'", str);
return FALSE;
}
/* get any optional server message */
if (json_object_has_member (json_object, "msg"))
server_msg = json_object_get_string_member (json_object, "msg");
/* server reported failed */
if (!json_object_get_boolean_member (json_object, "success")) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_PERMISSION_DENIED,
"Server rejected report: %s",
server_msg != NULL ? server_msg : "unspecified");
return FALSE;
}
/* server wanted us to see the message */
if (server_msg != NULL) {
g_debug ("server message: %s", server_msg);
if (g_strstr_len (server_msg, -1, "known issue") != NULL &&
json_object_has_member (json_object, "uri")) {
if (uri != NULL)
*uri = g_strdup (json_object_get_string_member (json_object, "uri"));
}
}
/* success */
return TRUE;
}
gint
fu_util_sort_devices_by_flags_cb (gconstpointer a, gconstpointer b)
{
FuDevice *dev_a = *((FuDevice **) a);
FuDevice *dev_b = *((FuDevice **) b);
if ((!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) &&
fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) ||
(!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) &&
fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED)))
return -1;
if ((fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) &&
!fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) ||
(fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) &&
!fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED)))
return 1;
return 0;
}
static gint
fu_util_device_order_compare (FuDevice *device1, FuDevice *device2)
{
if (fu_device_get_order (device1) < fu_device_get_order (device2))
return -1;
if (fu_device_get_order (device1) > fu_device_get_order (device2))
return 1;
return 0;
}
gint
fu_util_device_order_sort_cb (gconstpointer a, gconstpointer b)
{
FuDevice *device_a = *((FuDevice **) a);
FuDevice *device_b = *((FuDevice **) b);
return fu_util_device_order_compare (device_a, device_b);
}
gboolean
fu_util_switch_branch_warning (FwupdDevice *dev,
FwupdRelease *rel,
gboolean assume_yes,
GError **error)
{
const gchar *desc_markup = NULL;
g_autofree gchar *desc_plain = NULL;
g_autofree gchar *title = NULL;
g_autoptr(GString) desc_full = g_string_new (NULL);
/* warn the user if the vendor is different */
if (g_strcmp0 (fwupd_device_get_vendor (dev), fwupd_release_get_vendor (rel)) != 0) {
/* TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name */
g_string_append_printf (desc_full, _("The firmware from %s is not "
"supplied by %s, the hardware vendor."),
fwupd_release_get_vendor (rel),
fwupd_device_get_vendor (dev));
g_string_append (desc_full, "\n\n");
/* TRANSLATORS: %1 is the device vendor name */
g_string_append_printf (desc_full, _("Your hardware may be damaged using this firmware, "
"and installing this release may void any warranty "
"with %s."),
fwupd_device_get_vendor (dev));
g_string_append (desc_full, "\n\n");
}
/* from the <description> in the AppStream data */
desc_markup = fwupd_release_get_description (rel);
if (desc_markup == NULL)
return TRUE;
desc_plain = fu_util_convert_description (desc_markup, error);
if (desc_plain == NULL)
return FALSE;
g_string_append (desc_full, desc_plain);
/* TRANSLATORS: show and ask user to confirm --
* %1 is the old branch name, %2 is the new branch name */
title = g_strdup_printf (_("Switch branch from %s to %s?"),
fu_util_branch_for_display (fwupd_device_get_branch (dev)),
fu_util_branch_for_display (fwupd_release_get_branch (rel)));
fu_util_warning_box (title, desc_full->str, 80);
if (!assume_yes) {
/* ask for permission */
g_print ("\n%s [y|N]: ",
/* TRANSLATORS: should the branch be changed */
_("Do you understand the consequences of changing the firmware branch?"));
if (!fu_util_prompt_for_boolean (FALSE)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Declined branch switch");
return FALSE;
}
}
return TRUE;
}
void
fu_util_show_unsupported_warn (void)
{
#ifndef SUPPORTED_BUILD
g_autofree gchar *fmt = NULL;
/* TRANSLATORS: this is a prefix on the console */
fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_CLI_COLOR_YELLOW);
/* TRANSLATORS: unsupported build of the package */
g_printerr ("%s %s\n", fmt, _("This package has not been validated, it may not work properly."));
#endif
}
#ifdef HAVE_LIBCURL_7_62_0
G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup)
#endif
gboolean
fu_util_is_url (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
}