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



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]