From 0a7bc9731751da416cc74e08089a8d1e7562d1c7 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 11 Jan 2018 13:14:04 +0000 Subject: [PATCH] Add a 'report-history' command to fwupdmgr This shares your history with a reporting server, typically the LVFS. NOTE: no data is sent without the user opting-in, and the data sent is shown to the user before upload. --- src/fu-util.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/src/fu-util.c b/src/fu-util.c index 6046378f6..7beff254e 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -237,6 +237,26 @@ fu_util_prompt_for_number (guint maxnum) return answer; } +static 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 FwupdDevice * fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error) { @@ -569,6 +589,163 @@ fu_util_clear_history (FuUtilPrivate *priv, gchar **values, GError **error) return fu_history_remove_all (history, error); } +static gboolean +fu_util_report_history_for_uri (FuUtilPrivate *priv, + const gchar *report_uri, + GPtrArray *devices, + GError **error) +{ + guint status_code; + g_autofree gchar *data = NULL; + g_autoptr(SoupMessage) msg = NULL; + + /* convert to JSON */ + data = fwupd_build_history_report_json (devices, error); + if (data == NULL) + return FALSE; + + /* ask for permission */ + fu_util_print_data (_("Target"), report_uri); + fu_util_print_data (_("Payload"), data); + g_print ("%s [Y|n]: ", _("Proceed with upload?")); + if (!fu_util_prompt_for_boolean (TRUE)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED, + "User declined action"); + return FALSE; + } + + /* POST request */ + msg = soup_message_new (SOUP_METHOD_POST, report_uri); + soup_message_set_request (msg, "application/json; charset=utf-8", + SOUP_MEMORY_COPY, data, strlen (data)); + status_code = soup_session_send_message (priv->soup_session, msg); + g_debug ("server returned: %s", msg->response_body->data); + if (!SOUP_STATUS_IS_SUCCESSFUL (status_code)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Failed to upload to %s: %s", + report_uri, soup_status_get_phrase (status_code)); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GHashTable) remote_id_uri_map = NULL; + g_autoptr(GHashTable) report_map = NULL; + g_autoptr(GList) uris = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) remotes = NULL; + + /* set up networking */ + if (!fu_util_setup_networking (priv, error)) + return FALSE; + + /* create a map of RemoteID to RemoteURI */ + remotes = fwupd_client_get_remotes (priv->client, NULL, error); + if (remotes == NULL) + return FALSE; + remote_id_uri_map = g_hash_table_new (g_str_hash, g_str_equal); + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index (remotes, i); + if (fwupd_remote_get_id (remote) == NULL) + continue; + if (fwupd_remote_get_report_uri (remote) == NULL) + continue; + g_debug ("adding %s for %s", + fwupd_remote_get_report_uri (remote), + fwupd_remote_get_id (remote)); + g_hash_table_insert (remote_id_uri_map, + (gpointer) fwupd_remote_get_id (remote), + (gpointer) fwupd_remote_get_report_uri (remote)); + } + + /* get all devices from the history database, then filter them, + * adding to a hash map of report-ids */ + devices = fwupd_client_get_history (priv->client, NULL, error); + if (devices == NULL) + return FALSE; + report_map = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_ptr_array_unref); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devices, i); + FwupdRelease *rel = fwupd_device_get_release_default (dev); + const gchar *remote_id; + const gchar *remote_uri; + GPtrArray *devices_tmp; + + /* filter, if not forcing */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED)) + continue; + if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + } + + /* find the RemoteURI to use for the device */ + remote_id = fwupd_release_get_remote_id (rel); + if (remote_id == NULL) { + g_debug ("%s has no RemoteID", fwupd_device_get_id (dev)); + continue; + } + remote_uri = g_hash_table_lookup (remote_id_uri_map, remote_id); + if (remote_uri == NULL) { + g_debug ("%s has no RemoteURI", remote_id); + continue; + } + + /* add this to the hash map */ + devices_tmp = g_hash_table_lookup (report_map, remote_uri); + if (devices_tmp == NULL) { + devices_tmp = g_ptr_array_new (); + g_hash_table_insert (report_map, g_strdup (remote_uri), devices_tmp); + } + g_debug ("using %s for %s", remote_uri, fwupd_device_get_id (dev)); + g_ptr_array_add (devices_tmp, dev); + } + + /* nothing to report */ + if (g_hash_table_size (report_map) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No reports require uploading"); + return FALSE; + } + + /* process each uri */ + uris = g_hash_table_get_keys (report_map); + for (GList *l = uris; l != NULL; l = l->next) { + const gchar *uri = l->data; + GPtrArray *devices_tmp = g_hash_table_lookup (report_map, uri); + if (!fu_util_report_history_for_uri (priv, uri, devices_tmp, error)) + return FALSE; + } + + /* mark each device as reported */ + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devices, i); + if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED)) + continue; + if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + g_debug ("setting flag on %s", fwupd_device_get_id (dev)); + if (!fwupd_client_modify_device (priv->client, + fwupd_device_get_id (dev), + "Flags", "reported", + NULL, error)) + return FALSE; + } + + return TRUE; +} + static gboolean fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1716,6 +1893,12 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Erase all firmware update history"), fu_util_clear_history); + fu_util_add (priv->cmd_array, + "report-history", + NULL, + /* TRANSLATORS: command description */ + _("Share firmware history with the developers"), + fu_util_report_history); fu_util_add (priv->cmd_array, "install", "FILE [ID]",