[gnome-software/wip/hughsie/fwupd: 5/6] Support getting firmware updates from fwupd
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/hughsie/fwupd: 5/6] Support getting firmware updates from fwupd
- Date: Thu, 12 Mar 2015 11:46:57 +0000 (UTC)
commit 24bb35bab62167d3d85097f84c67e8b9a1278b78
Author: Richard Hughes <richard hughsie com>
Date: Thu Mar 5 14:57:47 2015 +0000
Support getting firmware updates from fwupd
src/gnome-software-local-file.desktop.in | 2 +-
src/gs-plugin-loader.c | 5 +
src/gs-plugin-loader.h | 1 +
src/gs-plugin.h | 4 +
src/gs-shell-details.c | 7 +-
src/gs-shell-updates.c | 37 ++-
src/gs-update-list.c | 26 +-
src/gs-update-list.h | 1 +
src/plugins/Makefile.am | 6 +
src/plugins/gs-plugin-fwupd.c | 743 ++++++++++++++++++++++++++++++
10 files changed, 827 insertions(+), 5 deletions(-)
---
diff --git a/src/gnome-software-local-file.desktop.in b/src/gnome-software-local-file.desktop.in
index 049c855..b77386c 100644
--- a/src/gnome-software-local-file.desktop.in
+++ b/src/gnome-software-local-file.desktop.in
@@ -8,4 +8,4 @@ Type=Application
Icon=system-software-install
StartupNotify=true
NoDisplay=true
-MimeType=application/x-rpm;application/x-redhat-package-manager;application/x-deb;application/x-app-package;
+MimeType=application/x-rpm;application/x-redhat-package-manager;application/x-deb;application/x-app-package;application/vnd.ms-cab-compressed;
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 6488d62..3c79762 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -2720,6 +2720,11 @@ gs_plugin_loader_app_action_async (GsPluginLoader *plugin_loader,
state->state_success = AS_APP_STATE_UNKNOWN;
state->state_failure = AS_APP_STATE_UNKNOWN;
break;
+ case GS_PLUGIN_LOADER_ACTION_UPGRADE:
+ state->function_name = "gs_plugin_app_upgrade";
+ state->state_success = AS_APP_STATE_UNKNOWN;
+ state->state_failure = AS_APP_STATE_UNKNOWN;
+ break;
default:
g_assert_not_reached ();
break;
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 1146e81..579d6d6 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -67,6 +67,7 @@ typedef enum {
GS_PLUGIN_LOADER_ACTION_INSTALL,
GS_PLUGIN_LOADER_ACTION_REMOVE,
GS_PLUGIN_LOADER_ACTION_SET_RATING,
+ GS_PLUGIN_LOADER_ACTION_UPGRADE,
GS_PLUGIN_LOADER_ACTION_LAST
} GsPluginLoaderAction;
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 94c5268..751d01a 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -247,6 +247,10 @@ gboolean gs_plugin_app_set_rating (GsPlugin *plugin,
GsApp *app,
GCancellable *cancellable,
GError **error);
+gboolean gs_plugin_app_upgrade (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_plugin_refresh (GsPlugin *plugin,
guint cache_age,
GsPluginRefreshFlags flags,
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 6e58097..eea8fd7 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -742,7 +742,12 @@ gs_shell_details_refresh_all (GsShellDetails *shell_details)
switch (gs_app_get_kind (priv->app)) {
case GS_APP_KIND_NORMAL:
case GS_APP_KIND_SYSTEM:
- gtk_widget_set_visible (priv->infobar_details_app_norepo, tmp == NULL && gs_app_get_state
(priv->app) == AS_APP_STATE_AVAILABLE_LOCAL);
+ if (gs_app_get_id_kind (priv->app) == AS_ID_KIND_FIRMWARE) {
+ gtk_widget_set_visible (priv->infobar_details_app_norepo, FALSE);
+ } else {
+ gtk_widget_set_visible (priv->infobar_details_app_norepo,
+ tmp == NULL && gs_app_get_state (priv->app) ==
AS_APP_STATE_AVAILABLE_LOCAL);
+ }
break;
default:
gtk_widget_set_visible (priv->infobar_details_app_norepo, FALSE);
diff --git a/src/gs-shell-updates.c b/src/gs-shell-updates.c
index 51407b1..314dee6 100644
--- a/src/gs-shell-updates.c
+++ b/src/gs-shell-updates.c
@@ -839,11 +839,46 @@ gs_offline_updates_cancel (void)
g_warning ("failed to cancel the offline update: %s", error->message);
}
+/**
+ * gs_shell_updates_upgrade_cb:
+ **/
+static void
+gs_shell_updates_upgrade_cb (GsPluginLoader *plugin_loader,
+ GAsyncResult *res,
+ GsShellUpdates *shell_updates)
+{
+ _cleanup_error_free_ GError *error = NULL;
+
+ /* get the results */
+ if (!gs_plugin_loader_app_action_finish (plugin_loader, res, &error))
+ g_warning ("Failed to upgrade: %s", error->message);
+}
+
static void
gs_shell_updates_button_update_all_cb (GtkButton *button,
- GsShellUpdates *updates)
+ GsShellUpdates *shell_updates)
{
+ GsApp *app;
+ GsShellUpdatesPrivate *priv = shell_updates->priv;
+ guint i;
_cleanup_error_free_ GError *error = NULL;
+ _cleanup_ptrarray_unref_ GPtrArray *apps = NULL;
+
+ /* any firmware updates? */
+ apps = gs_update_list_get_apps (GS_UPDATE_LIST (priv->list_box_updates));
+ for (i = 0; i < apps->len; i++) {
+ app = g_ptr_array_index (apps, i);
+ if (gs_app_get_id_kind (app) != AS_ID_KIND_FIRMWARE)
+ continue;
+ gs_plugin_loader_app_action_async (priv->plugin_loader,
+ app,
+ GS_PLUGIN_LOADER_ACTION_UPGRADE,
+ priv->cancellable,
+ (GAsyncReadyCallback) gs_shell_updates_upgrade_cb,
+ shell_updates);
+ }
+
+ /* normal packages */
if (!pk_offline_trigger (PK_OFFLINE_ACTION_REBOOT, NULL, &error)) {
g_warning ("failed to trigger an offline update: %s", error->message);
return;
diff --git a/src/gs-update-list.c b/src/gs-update-list.c
index 733ba78..a900bce 100644
--- a/src/gs-update-list.c
+++ b/src/gs-update-list.c
@@ -56,6 +56,23 @@ gs_update_list_add_app (GsUpdateList *update_list,
gtk_widget_show (app_row);
}
+GPtrArray *
+gs_update_list_get_apps (GsUpdateList *update_list)
+{
+ GList *l;
+ GPtrArray *apps;
+ GsAppRow *app_row;
+ _cleanup_list_free_ GList *children = NULL;
+
+ apps = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ children = gtk_container_get_children (GTK_CONTAINER (update_list));
+ for (l = children; l != NULL; l = l->next) {
+ app_row = GS_APP_ROW (l->data);
+ g_ptr_array_add (apps, g_object_ref (gs_app_row_get_app (app_row)));
+ }
+ return apps;
+}
+
static gboolean
is_addon_id_kind (GsApp *app)
{
@@ -65,6 +82,8 @@ is_addon_id_kind (GsApp *app)
return FALSE;
if (id_kind == AS_ID_KIND_WEB_APP)
return FALSE;
+ if (id_kind == AS_ID_KIND_FIRMWARE)
+ return FALSE;
return TRUE;
}
@@ -117,12 +136,15 @@ get_app_sort_key (GsApp *app)
/* sort desktop files, then addons */
switch (gs_app_get_id_kind (app)) {
- case AS_ID_KIND_DESKTOP:
+ case AS_ID_KIND_FIRMWARE:
g_string_append (key, "1:");
break;
- default:
+ case AS_ID_KIND_DESKTOP:
g_string_append (key, "2:");
break;
+ default:
+ g_string_append (key, "3:");
+ break;
}
/* sort by install date */
diff --git a/src/gs-update-list.h b/src/gs-update-list.h
index ff2611b..aa954aa 100644
--- a/src/gs-update-list.h
+++ b/src/gs-update-list.h
@@ -53,6 +53,7 @@ GType gs_update_list_get_type (void);
GtkWidget *gs_update_list_new (void);
void gs_update_list_add_app (GsUpdateList *update_list,
GsApp *app);
+GPtrArray *gs_update_list_get_apps (GsUpdateList *update_list);
G_END_DECLS
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index de60263..7608898 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -38,6 +38,7 @@ plugin_LTLIBRARIES = \
libgs_plugin_epiphany.la \
libgs_plugin_icons.la \
libgs_plugin_systemd-updates.la \
+ libgs_plugin_fwupd.la \
libgs_plugin_packagekit-refine.la \
libgs_plugin_packagekit-refresh.la \
libgs_plugin_packagekit-offline.la \
@@ -148,6 +149,11 @@ libgs_plugin_systemd_updates_la_LIBADD = $(GS_PLUGIN_LIBS) $(PACKAGEKIT_LIBS)
libgs_plugin_systemd_updates_la_LDFLAGS = -module -avoid-version
libgs_plugin_systemd_updates_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
+libgs_plugin_fwupd_la_SOURCES = gs-plugin-fwupd.c
+libgs_plugin_fwupd_la_LIBADD = $(GS_PLUGIN_LIBS) $(PACKAGEKIT_LIBS)
+libgs_plugin_fwupd_la_LDFLAGS = -module -avoid-version
+libgs_plugin_fwupd_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
+
libgs_plugin_packagekit_history_la_SOURCES = gs-plugin-packagekit-history.c
libgs_plugin_packagekit_history_la_LIBADD = $(GS_PLUGIN_LIBS)
libgs_plugin_packagekit_history_la_LDFLAGS = -module -avoid-version
diff --git a/src/plugins/gs-plugin-fwupd.c b/src/plugins/gs-plugin-fwupd.c
new file mode 100644
index 0000000..6b07d5e
--- /dev/null
+++ b/src/plugins/gs-plugin-fwupd.c
@@ -0,0 +1,743 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <appstream-glib.h>
+#include <fcntl.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <libsoup/soup.h>
+
+#include <gs-plugin.h>
+
+#include "gs-cleanup.h"
+
+#define FWUPD_DBUS_PATH "/"
+#define FWUPD_DBUS_SERVICE "org.freedesktop.fwupd"
+#define FWUPD_DBUS_INTERFACE "org.freedesktop.fwupd"
+
+struct GsPluginPrivate {
+ gsize done_init;
+ GDBusProxy *proxy;
+ GPtrArray *to_download;
+ AsStore *store;
+ GPtrArray *to_ignore;
+ SoupSession *session;
+ gchar *cachedir;
+};
+
+/**
+ * gs_plugin_fwupd_setup_networking:
+ */
+static gboolean
+gs_plugin_fwupd_setup_networking (GsPlugin *plugin, GError **error)
+{
+ /* already set up */
+ if (plugin->priv->session != NULL)
+ return TRUE;
+
+ /* set up a session */
+ plugin->priv->session = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT,
+ "gnome-software",
+ SOUP_SESSION_TIMEOUT, 5000,
+ NULL);
+ if (plugin->priv->session == NULL) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "%s: failed to setup networking",
+ plugin->name);
+ return FALSE;
+ }
+ soup_session_add_feature_by_type (plugin->priv->session,
+ SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
+ return TRUE;
+}
+
+/**
+ * gs_plugin_get_name:
+ */
+const gchar *
+gs_plugin_get_name (void)
+{
+ return "fwupd";
+}
+
+/**
+ * gs_plugin_initialize:
+ */
+void
+gs_plugin_initialize (GsPlugin *plugin)
+{
+ plugin->priv = GS_PLUGIN_GET_PRIVATE (GsPluginPrivate);
+ plugin->priv->store = as_store_new ();
+ plugin->priv->to_download = g_ptr_array_new_with_free_func (g_free);
+ plugin->priv->to_ignore = g_ptr_array_new_with_free_func (g_free);
+}
+
+/**
+ * gs_plugin_destroy:
+ */
+void
+gs_plugin_destroy (GsPlugin *plugin)
+{
+ g_free (plugin->priv->cachedir);
+ g_object_unref (plugin->priv->store);
+ g_ptr_array_unref (plugin->priv->to_download);
+ g_ptr_array_unref (plugin->priv->to_ignore);
+ if (plugin->priv->proxy != NULL)
+ g_object_unref (plugin->priv->proxy);
+ if (plugin->priv->session != NULL)
+ g_object_unref (plugin->priv->session);
+}
+
+/**
+ * gs_plugin_fwupd_changed_cb:
+ */
+static void
+gs_plugin_fwupd_changed_cb (GDBusProxy *proxy,
+ const gchar *sender_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ GsPlugin *plugin)
+{
+ if (g_strcmp0 (signal_name, "Changed") == 0)
+ gs_plugin_updates_changed (plugin);
+}
+
+/**
+ * gs_plugin_startup:
+ */
+static gboolean
+gs_plugin_startup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
+{
+ gint rc;
+ _cleanup_object_unref_ GDBusConnection *conn = NULL;
+ conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+ if (conn == NULL)
+ return FALSE;
+ plugin->priv->proxy = g_dbus_proxy_new_sync (conn,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ FWUPD_DBUS_SERVICE,
+ FWUPD_DBUS_PATH,
+ FWUPD_DBUS_INTERFACE,
+ NULL,
+ error);
+ if (plugin->priv->proxy == NULL)
+ return FALSE;
+ g_signal_connect (plugin->priv->proxy, "g-signal",
+ G_CALLBACK (gs_plugin_fwupd_changed_cb), plugin);
+
+ /* create the cache location */
+ plugin->priv->cachedir = g_build_filename (g_get_user_cache_dir (),
+ "gnome-software",
+ "firmware",
+ NULL);
+ rc = g_mkdir_with_parents (plugin->priv->cachedir, 0700);
+ if (rc != 0) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "Could not create firmware cache");
+ return FALSE;
+ }
+
+ /* only load firmware from the system */
+ as_store_add_filter (plugin->priv->store, AS_ID_KIND_FIRMWARE);
+ if (!as_store_load (plugin->priv->store,
+ AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM,
+ cancellable, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * gs_plugin_fwupd_add_required_location:
+ */
+static void
+gs_plugin_fwupd_add_required_location (GsPlugin *plugin, const gchar *location)
+{
+ const gchar *tmp;
+ guint i;
+ for (i = 0; i < plugin->priv->to_ignore->len; i++) {
+ tmp = g_ptr_array_index (plugin->priv->to_ignore, i);
+ if (g_strcmp0 (tmp, location) == 0)
+ return;
+ }
+ for (i = 0; i < plugin->priv->to_download->len; i++) {
+ tmp = g_ptr_array_index (plugin->priv->to_download, i);
+ if (g_strcmp0 (tmp, location) == 0)
+ return;
+ }
+ g_ptr_array_add (plugin->priv->to_download, g_strdup (location));
+}
+
+/**
+ * gs_plugin_fwupd_add_device:
+ */
+static gboolean
+gs_plugin_fwupd_add_device (GsPlugin *plugin,
+ const gchar *device_id,
+ const gchar *guid,
+ const gchar *version,
+ GList **list,
+ GError **error)
+{
+ AsApp *item;
+ AsRelease *rel;
+ GPtrArray *releases;
+ const gchar *tmp;
+ guint i;
+ _cleanup_free_ gchar *basename = NULL;
+ _cleanup_free_ gchar *filename_cache = NULL;
+ _cleanup_free_ gchar *update_location = NULL;
+ _cleanup_free_ gchar *update_version = NULL;
+ _cleanup_object_unref_ GsApp *app = NULL;
+ _cleanup_string_free_ GString *update_desc = NULL;
+
+ /* find the device */
+ item = as_store_get_app_by_id (plugin->priv->store, guid);
+ if (item == NULL) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "device id %s not found in metadata",
+ guid);
+ return FALSE;
+ }
+
+ /* are any releases newer than what we have here */
+ g_debug ("device id %s found in metadata", guid);
+ update_desc = g_string_new ("");
+ releases = as_app_get_releases (item);
+ for (i = 0; i < releases->len; i++) {
+ _cleanup_free_ gchar *md = NULL;
+
+ /* check if actually newer */
+ rel = g_ptr_array_index (releases, i);
+ if (as_utils_vercmp (as_release_get_version (rel), version) <= 0)
+ continue;
+
+ /* get the update text, if it exists */
+ if (update_version == NULL) {
+ tmp = as_release_get_version (rel);
+ if (g_strstr_len (tmp, -1, ".") != NULL) {
+ update_version = g_strdup (tmp);
+ } else {
+ GDateTime *dt;
+ dt = g_date_time_new_from_unix_utc (as_release_get_timestamp (rel));
+ update_version = g_strdup_printf ("Version %s (%i-%i-%i)",
+ tmp,
+ g_date_time_get_year (dt),
+ g_date_time_get_month (dt),
+ g_date_time_get_day_of_month (dt));
+ g_date_time_unref (dt);
+ }
+ update_location = g_strdup (as_release_get_location_default (rel));
+ }
+ tmp = as_release_get_description (rel, NULL);
+ if (tmp == NULL)
+ continue;
+ md = as_markup_convert (tmp, -1,
+ AS_MARKUP_CONVERT_FORMAT_MARKDOWN,
+ NULL);
+ if (md != NULL)
+ g_string_append_printf (update_desc, "%s\n", md);
+ }
+
+ /* no updates for this hardware */
+ if (update_version == NULL) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "no updates available");
+ return FALSE;
+ }
+
+ /* nowhere to download the update from */
+ if (update_location == NULL) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "no location available for firmware");
+ return FALSE;
+ }
+
+ /* does the firmware already exist in the cache? */
+ basename = g_path_get_basename (update_location);
+ filename_cache = g_build_filename (plugin->priv->cachedir, basename, NULL);
+ if (!g_file_test (filename_cache, G_FILE_TEST_EXISTS)) {
+ gs_plugin_fwupd_add_required_location (plugin, update_location);
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "%s does not yet exist, wait patiently",
+ filename_cache);
+ return FALSE;
+ }
+
+ /* remove trailing newline */
+ if (update_desc->len > 0)
+ g_string_truncate (update_desc, update_desc->len - 1);
+
+ /* actually addd the application */
+ app = gs_app_new (guid);
+ gs_app_set_management_plugin (app, "fwupd");
+ gs_app_set_state (app, AS_APP_STATE_UPDATABLE);
+ gs_app_set_update_details (app, update_desc->str);
+ gs_app_set_update_version (app, update_version);
+ gs_app_add_source_id (app, filename_cache);
+ gs_app_add_source (app, as_app_get_name (item, NULL));
+ gs_app_add_category (app, "System");
+ gs_app_set_kind (app, GS_APP_KIND_SYSTEM);
+ gs_app_set_metadata (app, "fwupd::DeviceID", device_id);
+ gs_plugin_add_app (list, app);
+ return TRUE;
+}
+
+/**
+ * gs_plugin_add_updates:
+ */
+gboolean
+gs_plugin_add_updates (GsPlugin *plugin,
+ GList **list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *id;
+ gboolean ret;
+ GVariantIter *iter_device;
+ _cleanup_variant_iter_free_ GVariantIter *iter = NULL;
+ _cleanup_variant_unref_ GVariant *val = NULL;
+
+ /* watch the file in case it comes or goes */
+ if (g_once_init_enter (&plugin->priv->done_init)) {
+ ret = gs_plugin_startup (plugin, cancellable, error);
+ g_once_init_leave (&plugin->priv->done_init, TRUE);
+ if (!ret)
+ return FALSE;
+ }
+
+ val = g_dbus_proxy_call_sync (plugin->priv->proxy,
+ "GetDevices",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+ if (val == NULL)
+ return FALSE;
+
+ /* parse */
+ g_variant_get (val, "(a{sa{sv}})", &iter);
+ while (g_variant_iter_next (iter, "{&sa{sv}}", &id, &iter_device)) {
+ GVariant *variant;
+ const gchar *key;
+ _cleanup_free_ gchar *guid = NULL;
+ _cleanup_free_ gchar *version = NULL;
+
+ while (g_variant_iter_next (iter_device, "{&sv}", &key, &variant)) {
+ g_debug ("%s has key %s", id, key);
+ if (g_strcmp0 (key, "Guid") == 0) {
+ guid = g_variant_dup_string (variant, NULL);
+ } else if (g_strcmp0 (key, "Version") == 0) {
+ version = g_variant_dup_string (variant, NULL);
+ }
+ g_variant_unref (variant);
+ }
+
+ /* we got all we needed */
+ if (guid != NULL && version != NULL) {
+ _cleanup_error_free_ GError *error_local = NULL;
+ if (!gs_plugin_fwupd_add_device (plugin,
+ id,
+ guid,
+ version,
+ list,
+ &error_local)) {
+ g_debug ("cannot add device %s: %s",
+ id, error_local->message);
+ }
+ }
+
+ g_variant_iter_free (iter_device);
+ }
+
+ return TRUE;
+}
+
+/**
+ * gs_plugin_refresh:
+ */
+gboolean
+gs_plugin_refresh (GsPlugin *plugin,
+ guint cache_age,
+ GsPluginRefreshFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *tmp;
+ guint i;
+
+ /* ensure networking is set up */
+ if (!gs_plugin_fwupd_setup_networking (plugin, error))
+ return FALSE;
+
+ /* download the files to the cachedir */
+ for (i = 0; i < plugin->priv->to_download->len; i++) {
+ guint status_code;
+ _cleanup_error_free_ GError *error_local = NULL;
+ _cleanup_free_ gchar *basename = NULL;
+ _cleanup_free_ gchar *filename_cache = NULL;
+ _cleanup_object_unref_ SoupMessage *msg = NULL;
+
+ tmp = g_ptr_array_index (plugin->priv->to_download, i);
+ basename = g_path_get_basename (tmp);
+ filename_cache = g_build_filename (plugin->priv->cachedir, basename, NULL);
+ g_debug ("downloading %s to %s", tmp, filename_cache);
+
+ /* set sync request */
+ msg = soup_message_new (SOUP_METHOD_GET, tmp);
+ status_code = soup_session_send_message (plugin->priv->session, msg);
+ if (status_code != SOUP_STATUS_OK) {
+ g_warning ("Failed to download %s, ignoring: %s",
+ tmp, soup_status_get_phrase (status_code));
+ g_ptr_array_remove (plugin->priv->to_download, (gpointer) tmp);
+ g_ptr_array_add (plugin->priv->to_ignore, g_strdup (tmp));
+ continue;
+ }
+
+ /* save binary file */
+ if (!g_file_set_contents (filename_cache,
+ msg->response_body->data,
+ msg->response_body->length,
+ &error_local)) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "Failed to save firmware: %s",
+ error_local->message);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gs_plugin_fwupd_upgrade:
+ */
+static gboolean
+gs_plugin_fwupd_upgrade (GsPlugin *plugin,
+ const gchar *filename,
+ const gchar *device_id,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *body;
+ GVariantBuilder builder;
+ gint fd;
+ gint retval;
+ _cleanup_object_unref_ GDBusConnection *conn = NULL;
+ _cleanup_object_unref_ GDBusMessage *message = NULL;
+ _cleanup_object_unref_ GDBusMessage *request = NULL;
+ _cleanup_object_unref_ GUnixFDList *fd_list = NULL;
+
+ conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+ if (conn == NULL)
+ return FALSE;
+
+ /* set options */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add (&builder, "{sv}",
+ "reason", g_variant_new_string ("system-update"));
+ g_variant_builder_add (&builder, "{sv}",
+ "filename", g_variant_new_string (filename));
+ g_variant_builder_add (&builder, "{sv}",
+ "offline", g_variant_new_boolean (TRUE));
+
+ /* open file */
+ fd = open (filename, O_RDONLY);
+ if (fd < 0) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "failed to open %s",
+ filename);
+ return FALSE;
+ }
+
+ /* set out of band file descriptor */
+ fd_list = g_unix_fd_list_new ();
+ retval = g_unix_fd_list_append (fd_list, fd, NULL);
+ g_assert (retval != -1);
+ request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE,
+ FWUPD_DBUS_PATH,
+ FWUPD_DBUS_INTERFACE,
+ "Update");
+ g_dbus_message_set_unix_fd_list (request, fd_list);
+
+ /* g_unix_fd_list_append did a dup() already */
+ close (fd);
+
+ /* send message */
+ body = g_variant_new ("(sha{sv})", device_id, fd > -1 ? 0 : -1, &builder);
+ g_dbus_message_set_body (request, body);
+ message = g_dbus_connection_send_message_with_reply_sync (conn,
+ request,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL,
+ error);
+ if (message == NULL) {
+ g_dbus_error_strip_remote_error (*error);
+ return FALSE;
+ }
+ if (g_dbus_message_to_gerror (message, error)) {
+ g_dbus_error_strip_remote_error (*error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gs_plugin_app_upgrade:
+ */
+gboolean
+gs_plugin_app_upgrade (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *device_id;
+ const gchar *filename;
+
+ filename = gs_app_get_source_id_default (app);
+ device_id = gs_app_get_metadata_item (app, "fwupd::DeviceID");
+ if (filename == NULL || device_id == NULL) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "not enough data for fwupd %s:%s",
+ filename, device_id);
+ return FALSE;
+ }
+ return gs_plugin_fwupd_upgrade (plugin,
+ filename,
+ device_id,
+ cancellable,
+ error);
+}
+
+/**
+ * gs_plugin_app_install:
+ */
+gboolean
+gs_plugin_app_install (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *filename;
+
+ filename = gs_app_get_source_id_default (app);
+ if (filename == NULL) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "not enough data for fwupd %s",
+ filename);
+ return FALSE;
+ }
+ return gs_plugin_fwupd_upgrade (plugin,
+ filename,
+ "*",
+ cancellable,
+ error);
+}
+
+/**
+ * gs_plugin_fwupd_content_type_matches:
+ */
+static gboolean
+gs_plugin_fwupd_content_type_matches (const gchar *filename,
+ gboolean *matches,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *tmp;
+ guint i;
+ _cleanup_object_unref_ GFile *file = NULL;
+ _cleanup_object_unref_ GFileInfo *info = NULL;
+ const gchar *mimetypes[] = {
+ "application/vnd.ms-cab-compressed",
+ NULL };
+
+ /* get content type */
+ file = g_file_new_for_path (filename);
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ cancellable,
+ error);
+ if (info == NULL)
+ return FALSE;
+ tmp = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+
+ /* match any */
+ *matches = FALSE;
+ for (i = 0; mimetypes[i] != NULL; i++) {
+ if (g_strcmp0 (tmp, mimetypes[i]) == 0) {
+ *matches = TRUE;
+ break;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * gs_plugin_filename_to_app:
+ */
+gboolean
+gs_plugin_filename_to_app (GsPlugin *plugin,
+ GList **list,
+ const gchar *filename,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *body;
+ GVariant *val;
+ GVariant *variant;
+ const gchar *key;
+ gboolean supported;
+ gint fd;
+ gint retval;
+ _cleanup_object_unref_ GDBusConnection *conn = NULL;
+ _cleanup_object_unref_ GDBusMessage *message = NULL;
+ _cleanup_object_unref_ GDBusMessage *request = NULL;
+ _cleanup_object_unref_ GsApp *app = NULL;
+ _cleanup_object_unref_ GUnixFDList *fd_list = NULL;
+ _cleanup_variant_iter_free_ GVariantIter *iter = NULL;
+
+ /* does this match any of the mimetypes we support */
+ if (!gs_plugin_fwupd_content_type_matches (filename,
+ &supported,
+ cancellable,
+ error))
+ return FALSE;
+ if (!supported)
+ return TRUE;
+
+ /* get request */
+ conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+ if (conn == NULL)
+ return FALSE;
+
+ /* open file */
+ fd = open (filename, O_RDONLY);
+ if (fd < 0) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "failed to open %s",
+ filename);
+ return FALSE;
+ }
+
+ /* set out of band file descriptor */
+ fd_list = g_unix_fd_list_new ();
+ retval = g_unix_fd_list_append (fd_list, fd, NULL);
+ g_assert (retval != -1);
+ 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);
+
+ /* g_unix_fd_list_append did a dup() already */
+ close (fd);
+
+ /* send message */
+ body = g_variant_new ("(h)", fd > -1 ? 0 : -1);
+ g_dbus_message_set_body (request, body);
+ message = g_dbus_connection_send_message_with_reply_sync (conn,
+ request,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL,
+ error);
+ if (message == NULL) {
+ g_dbus_error_strip_remote_error (*error);
+ return FALSE;
+ }
+ if (g_dbus_message_to_gerror (message, error)) {
+ g_dbus_error_strip_remote_error (*error);
+ return FALSE;
+ }
+
+ /* get results */
+ app = gs_app_new (NULL);
+ gs_app_set_management_plugin (app, "fwupd");
+ gs_app_set_kind (app, GS_APP_KIND_PACKAGE);
+ gs_app_set_state (app, AS_APP_STATE_AVAILABLE_LOCAL);
+ gs_app_add_source_id (app, filename);
+ gs_app_add_category (app, "System");
+ val = g_dbus_message_get_body (message);
+ g_variant_get (val, "(a{sv})", &iter);
+ while (g_variant_iter_next (iter, "{&sv}", &key, &variant)) {
+ if (g_strcmp0 (key, "Version") == 0) {
+ gs_app_set_version (app, g_variant_get_string (variant, NULL));
+ } else if (g_strcmp0 (key, "Vendor") == 0) {
+ gs_app_set_origin (app, g_variant_get_string (variant, NULL));
+ } else if (g_strcmp0 (key, "Guid") == 0) {
+ gs_app_set_id (app, g_variant_get_string (variant, NULL));
+ } else if (g_strcmp0 (key, "Name") == 0) {
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL,
+ g_variant_get_string (variant, NULL));
+ } else if (g_strcmp0 (key, "Summary") == 0) {
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL,
+ g_variant_get_string (variant, NULL));
+ } else if (g_strcmp0 (key, "Description") == 0) {
+ _cleanup_free_ gchar *tmp = NULL;
+ tmp = as_markup_convert (g_variant_get_string (variant, NULL), -1,
+ AS_MARKUP_CONVERT_FORMAT_SIMPLE, NULL);
+ if (tmp != NULL)
+ gs_app_set_description (app, GS_APP_QUALITY_HIGHEST, tmp);
+ } else if (g_strcmp0 (key, "UrlHomepage") == 0) {
+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE,
+ g_variant_get_string (variant, NULL));
+ } else if (g_strcmp0 (key, "License") == 0) {
+ gs_app_set_licence (app, g_variant_get_string (variant, NULL));
+ } else if (g_strcmp0 (key, "Size") == 0) {
+ gs_app_set_size (app, g_variant_get_uint64 (variant));
+ }
+ g_variant_unref (variant);
+ }
+
+ gs_plugin_add_app (list, app);
+ return TRUE;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]