[gnome-software/wip/hughsie/fwupd: 38/38] Support getting firmware updates from fwupd



commit ada2d64800e7d5bbdedd4524555abfdcda4555ea
Author: Richard Hughes <richard hughsie com>
Date:   Thu Mar 5 14:57:47 2015 +0000

    Support getting firmware updates from fwupd

 configure.ac                             |    9 +
 contrib/gnome-software.spec.in           |    1 +
 src/gnome-software-local-file.desktop.in |    2 +-
 src/gs-app.c                             |   16 +-
 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                  |   12 +
 src/plugins/gs-plugin-fwupd.c            |  904 ++++++++++++++++++++++++++++++
 src/plugins/gs-plugin-systemd-updates.c  |   18 +
 14 files changed, 1031 insertions(+), 12 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 589adfb..0772ec2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,15 @@ AC_ARG_ENABLE(dogtail,
               enable_dogtail=yes)
 AM_CONDITIONAL(ENABLE_DOGTAIL, test "$enable_dogtail" != no)
 
+# fwupd
+AC_ARG_ENABLE(firmware, AS_HELP_STRING([--enable-firmware],[enable firmware support]),
+              enable_firmware=$enableval,enable_firmware=yes)
+if test x$enable_firmware = xyes; then
+  PKG_CHECK_MODULES(FWUPD, fwupd)
+  AC_DEFINE(HAVE_FIRMWARE,1,[Build firmware support])
+fi
+AM_CONDITIONAL(HAVE_FIRMWARE, test x$enable_firmware = xyes)
+
 # this refers to the gnome-software plugin API version
 # this is not in any way related to a package or soname version
 GS_PLUGIN_API_VERSION=8
diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in
index d3149ea..6645dad 100644
--- a/contrib/gnome-software.spec.in
+++ b/contrib/gnome-software.spec.in
@@ -28,6 +28,7 @@ BuildRequires: libsoup-devel
 BuildRequires: gnome-desktop3-devel
 BuildRequires: gsettings-desktop-schemas-devel
 BuildRequires: libappstream-glib-devel
+BuildRequires: fwupd-devel
 
 # this is not a library version
 %define gs_plugin_version               7
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-app.c b/src/gs-app.c
index 1271def..4c17452 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -804,9 +804,18 @@ gs_app_get_pixbuf (GsApp *app)
 {
        g_return_val_if_fail (GS_IS_APP (app), NULL);
 
-       if (app->priv->pixbuf == NULL && gs_app_get_state (app) == AS_APP_STATE_AVAILABLE_LOCAL) {
-               const gchar *icon_name;
+       /* has an icon */
+       if (app->priv->pixbuf == NULL &&
+           app->priv->icon != NULL &&
+           as_icon_get_kind (app->priv->icon) == AS_ICON_KIND_STOCK) {
+               app->priv->pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                                             as_icon_get_name (app->priv->icon), 64,
+                                                             GTK_ICON_LOOKUP_USE_BUILTIN |
+                                                             GTK_ICON_LOOKUP_FORCE_SIZE,
+                                                             NULL);
 
+       } else if (app->priv->pixbuf == NULL && gs_app_get_state (app) == AS_APP_STATE_AVAILABLE_LOCAL) {
+               const gchar *icon_name;
                if (gs_app_get_kind (app) == GS_APP_KIND_SOURCE)
                        icon_name = "x-package-repository";
                else if (gs_app_is_addon_id_kind (app))
@@ -818,9 +827,8 @@ gs_app_get_pixbuf (GsApp *app)
                                                              GTK_ICON_LOOKUP_USE_BUILTIN |
                                                              GTK_ICON_LOOKUP_FORCE_SIZE,
                                                              NULL);
-       }
 
-       if (app->priv->pixbuf == NULL && gs_app_get_kind (app) == GS_APP_KIND_MISSING) {
+       } else if (app->priv->pixbuf == NULL && gs_app_get_kind (app) == GS_APP_KIND_MISSING) {
                app->priv->pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
                                                              "dialog-question-symbolic", 16,
                                                              GTK_ICON_LOOKUP_USE_BUILTIN |
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 6c30cd4..8fc3ccf 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -2581,6 +2581,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 5247ea0..c0f743b 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 810aabd..c1bc18f 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -239,6 +239,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 9a0c9ac..cace8e8 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -741,7 +741,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 092272b..ac05db0 100644
--- a/src/gs-shell-updates.c
+++ b/src/gs-shell-updates.c
@@ -835,18 +835,47 @@ 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_button_update_all_cb (GtkButton      *button,
-                                      GsShellUpdates *updates)
+gs_shell_updates_upgrade_cb (GsPluginLoader *plugin_loader,
+                            GAsyncResult *res,
+                            GsShellUpdates *shell_updates)
 {
        _cleanup_error_free_ GError *error = NULL;
-       if (!pk_offline_trigger (PK_OFFLINE_ACTION_REBOOT, NULL, &error)) {
-               g_warning ("failed to trigger an offline update: %s", error->message);
+
+       /* get the results */
+       if (!gs_plugin_loader_app_action_finish (plugin_loader, res, &error)) {
+               g_warning ("Failed to upgrade: %s", error->message);
                return;
        }
        gs_reboot (gs_offline_updates_cancel);
 }
 
+static void
+gs_shell_updates_button_update_all_cb (GtkButton      *button,
+                                      GsShellUpdates *shell_updates)
+{
+       GsApp *app;
+       GsShellUpdatesPrivate *priv = shell_updates->priv;
+       guint i;
+       _cleanup_error_free_ GError *error = NULL;
+       _cleanup_ptrarray_unref_ GPtrArray *apps = NULL;
+
+       /* do the upgrade action? */
+       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);
+               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);
+       }
+}
+
 /**
  * gs_shell_updates_get_properties_cb:
  **/
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 86cffc8..f01bd5c 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -9,6 +9,7 @@ AM_CPPFLAGS =                                           \
        $(PACKAGEKIT_CFLAGS)                            \
        $(SOUP_CFLAGS)                                  \
        $(SQLITE_CFLAGS)                                \
+       $(FWUPD_CFLAGS)                                 \
        -DBINDIR=\"$(bindir)\"                          \
        -DDATADIR=\"$(datadir)\"                        \
        -DGS_MODULESETDIR=\"$(datadir)/gnome-software/modulesets.d\" \
@@ -43,6 +44,10 @@ plugin_LTLIBRARIES =                                 \
        libgs_plugin_packagekit-history.la              \
        libgs_plugin_packagekit.la
 
+if HAVE_FIRMWARE
+plugin_LTLIBRARIES += libgs_plugin_fwupd.la
+endif
+
 libgs_plugin_dummy_la_SOURCES = gs-plugin-dummy.c
 libgs_plugin_dummy_la_LIBADD = $(GS_PLUGIN_LIBS)
 libgs_plugin_dummy_la_LDFLAGS = -module -avoid-version
@@ -142,6 +147,13 @@ 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)
 
+if HAVE_FIRMWARE
+libgs_plugin_fwupd_la_SOURCES = gs-plugin-fwupd.c
+libgs_plugin_fwupd_la_LIBADD = $(GS_PLUGIN_LIBS) $(FWUPD_LIBS)
+libgs_plugin_fwupd_la_LDFLAGS = -module -avoid-version
+libgs_plugin_fwupd_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
+endif
+
 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..68e9821
--- /dev/null
+++ b/src/plugins/gs-plugin-fwupd.c
@@ -0,0 +1,904 @@
+/* -*- 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 <fwupd.h>
+#include <appstream-glib.h>
+#include <fcntl.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <libsoup/soup.h>
+#include <glib/gstdio.h>
+
+#include <gs-plugin.h>
+
+#include "gs-cleanup.h"
+
+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_error_free_ GError *error_local = NULL;
+       _cleanup_object_unref_ GDBusConnection *conn = NULL;
+
+       /* register D-Bus errors */
+       fwupd_error_quark ();
+
+       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_local);
+       if (plugin->priv->proxy == NULL) {
+               g_warning ("Failed to start fwupd: %s", error_local->message);
+               return TRUE;
+       }
+       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_get_file_checksum:
+ */
+static gchar *
+gs_plugin_fwupd_get_file_checksum (const gchar *filename,
+                                  GChecksumType checksum_type,
+                                  GError **error)
+{
+       gsize len;
+       _cleanup_free_ gchar *data = NULL;
+
+       if (!g_file_get_contents (filename, &data, &len, error))
+               return NULL;
+       return g_compute_checksum_for_data (checksum_type, (const guchar *)data, len);
+}
+
+/**
+ * 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 *checksum = NULL;
+       _cleanup_free_ gchar *checksum2 = NULL;
+       _cleanup_free_ gchar *filename_cache = NULL;
+       _cleanup_free_ gchar *update_location = NULL;
+       _cleanup_free_ gchar *update_version = NULL;
+       _cleanup_object_unref_ AsIcon *icon = 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 checksum */
+               tmp = as_release_get_checksum (rel, G_CHECKSUM_SHA1);
+               if (tmp == NULL) {
+                       g_warning ("%s [%s] has no checksum, ignoring as unsafe",
+                                  as_app_get_id (item),
+                                  as_release_get_version (rel));
+                       continue;
+               }
+
+               /* get the update text, if it exists */
+               if (update_version == NULL) {
+                       checksum = g_strdup (tmp);
+                       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 ("0.0.%s-%04i%02i%02i",
+                                                                 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;
+       }
+
+       /* does the checksum match */
+       checksum2 = gs_plugin_fwupd_get_file_checksum (filename_cache,
+                                                      G_CHECKSUM_SHA1,
+                                                      error);
+       if (checksum2 == NULL)
+               return FALSE;
+       if (g_strcmp0 (checksum, checksum2) != 0) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "%s does not match checksum, expected %s, got %s",
+                            filename_cache, checksum, checksum2);
+               g_unlink (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_id_kind (app, AS_ID_KIND_FIRMWARE);
+       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_app_set_metadata (app, "DataDir::desktop-icon", "application-x-firmware");
+       gs_plugin_add_app (list, app);
+
+       /* create icon */
+       icon = as_icon_new ();
+       as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
+       as_icon_set_name (icon, "application-x-firmware", -1);
+       gs_app_set_icon (app, icon);
+
+       return TRUE;
+}
+
+/**
+ * gs_plugin_add_updates_historical:
+ */
+gboolean
+gs_plugin_add_updates_historical (GsPlugin *plugin,
+                                 GList **list,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       GVariant *variant;
+       const gchar *key;
+       gboolean ret;
+       _cleanup_error_free_ GError *error_local = NULL;
+       _cleanup_object_unref_ GsApp *app = NULL;
+       _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;
+       }
+
+       /* could not connect */
+       if (plugin->priv->proxy == NULL)
+               return TRUE;
+       val = g_dbus_proxy_call_sync (plugin->priv->proxy,
+                                     "GetResults",
+                                     g_variant_new ("(s)", FWUPD_DEVICE_ID_ANY),
+                                     G_DBUS_CALL_FLAGS_NONE,
+                                     -1,
+                                     NULL,
+                                     &error_local);
+       if (val == NULL) {
+               if (g_error_matches (error_local,
+                                    FWUPD_ERROR,
+                                    FWUPD_ERROR_NOTHING_TO_DO))
+                       return TRUE;
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_FAILED,
+                                    error_local->message);
+               return FALSE;
+       }
+
+       /* parse */
+       app = gs_app_new (NULL);
+       gs_app_set_management_plugin (app, "fwupd");
+       gs_app_set_state (app, AS_APP_STATE_UPDATABLE);
+       gs_app_set_kind (app, GS_APP_KIND_PACKAGE);
+       g_variant_get (val, "(a{sv})", &iter);
+       while (g_variant_iter_next (iter, "{&sv}", &key, &variant)) {
+               g_debug ("key %s", key);
+               if (g_strcmp0 (key, "Guid") == 0) {
+                       gs_app_set_id (app, g_variant_get_string (variant, NULL));
+                       continue;
+               }
+               if (g_strcmp0 (key, "VersionNew") == 0) {
+                       gs_app_set_update_version (app, g_variant_get_string (variant, NULL));
+                       continue;
+               }
+               if (g_strcmp0 (key, "Name") == 0) {
+                       gs_app_add_source (app, g_variant_get_string (variant, NULL));
+                       continue;
+               }
+               g_variant_unref (variant);
+       }
+       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_error_free_ GError *error_local = NULL;
+       _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;
+       }
+
+       /* could not connect */
+       if (plugin->priv->proxy == NULL)
+               return TRUE;
+       val = g_dbus_proxy_call_sync (plugin->priv->proxy,
+                                     "GetDevices",
+                                     NULL,
+                                     G_DBUS_CALL_FLAGS_NONE,
+                                     -1,
+                                     NULL,
+                                     &error_local);
+       if (val == NULL) {
+               if (g_error_matches (error_local,
+                                    FWUPD_ERROR,
+                                    FWUPD_ERROR_NOTHING_TO_DO))
+                       return TRUE;
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_FAILED,
+                                    error_local->message);
+               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_local2 = NULL;
+                       if (!gs_plugin_fwupd_add_device (plugin,
+                                                        id,
+                                                        guid,
+                                                        version,
+                                                        list,
+                                                        &error_local2)) {
+                               g_debug ("cannot add device %s: %s",
+                                        id, error_local2->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;
+       }
+       gs_app_set_state (app, AS_APP_STATE_INSTALLING);
+       if (!gs_plugin_fwupd_upgrade (plugin, filename, device_id,
+                                     cancellable, error))
+               return FALSE;
+       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+       return TRUE;
+}
+
+/**
+ * gs_plugin_app_install:
+ */
+gboolean
+gs_plugin_app_install (GsPlugin *plugin,
+                      GsApp *app,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       const gchar *filename;
+
+       /* only process this app if was created by this plugin */
+       if (g_strcmp0 (gs_app_get_management_plugin (app), "fwupd") != 0)
+               return TRUE;
+
+       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;
+       }
+       gs_app_set_state (app, AS_APP_STATE_INSTALLING);
+       if (!gs_plugin_fwupd_upgrade (plugin, filename, FWUPD_DEVICE_ID_ANY,
+                                     cancellable, error))
+               return FALSE;
+       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+       return TRUE;
+}
+
+/**
+ * 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_ AsIcon *icon = NULL;
+       _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_metadata (app, "DataDir::desktop-icon", "application-x-firmware");
+       gs_app_set_id_kind (app, AS_ID_KIND_FIRMWARE);
+       gs_app_set_management_plugin (app, "fwupd");
+       gs_app_set_kind (app, GS_APP_KIND_SYSTEM);
+       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);
+       }
+
+       /* create icon */
+       icon = as_icon_new ();
+       as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
+       as_icon_set_name (icon, "application-x-firmware", -1);
+       gs_app_set_icon (app, icon);
+
+       gs_plugin_add_app (list, app);
+       return TRUE;
+}
diff --git a/src/plugins/gs-plugin-systemd-updates.c b/src/plugins/gs-plugin-systemd-updates.c
index 449fe5b..e6a47ef 100644
--- a/src/plugins/gs-plugin-systemd-updates.c
+++ b/src/plugins/gs-plugin-systemd-updates.c
@@ -31,6 +31,7 @@
 struct GsPluginPrivate {
        GFileMonitor            *monitor;
        gsize                    done_init;
+       gboolean                 is_triggered;
 };
 
 /**
@@ -143,3 +144,20 @@ gs_plugin_add_updates (GsPlugin *plugin,
        }
        return TRUE;
 }
+
+/**
+ * gs_plugin_app_upgrade:
+ */
+gboolean
+gs_plugin_app_upgrade (GsPlugin *plugin,
+                      GsApp *app,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       if (plugin->priv->is_triggered)
+               return TRUE;
+       if (!pk_offline_trigger (PK_OFFLINE_ACTION_REBOOT, NULL, error))
+               return FALSE;
+       plugin->priv->is_triggered = TRUE;
+       return TRUE;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]