[gnome-software/wip/rancell/apt: 25/25] Merge branch 'master' into wip/rancell/apt



commit 4d7451e59f8b74feb37a252b2a52d91a3013c4f2
Merge: 3dcd623 ceb6b9d
Author: Robert Ancell <robert ancell canonical com>
Date:   Mon Feb 29 12:45:24 2016 +1300

    Merge branch 'master' into wip/rancell/apt

 configure.ac                                   |    4 +-
 data/org.gnome.software.gschema.xml            |    4 +-
 po/ca.po                                       |  999 +++++++++-------
 po/hu.po                                       | 1185 +++++++++++--------
 po/nb.po                                       | 1387 +++++++++++++--------
 po/pl.po                                       |  123 +-
 po/pt_BR.po                                    |  544 +++------
 po/sk.po                                       |  128 +-
 po/sv.po                                       | 1534 +++++++++++++++---------
 src/Makefile.am                                |   18 +-
 src/gnome-software-local-file.desktop.in       |    2 +-
 src/gs-app-addon-row.c                         |    2 +-
 src/gs-app-row.c                               |   25 +-
 src/gs-app.c                                   |  150 +--
 src/gs-app.h                                   |   33 +-
 src/gs-cmd.c                                   |    2 +-
 src/gs-page.c                                  |    2 +-
 src/gs-plugin-loader.c                         |  137 ++--
 src/gs-self-test.c                             |   17 +-
 src/gs-shell-details.c                         |   63 +-
 src/gs-shell-extras.c                          |    9 +-
 src/gs-shell-installed.c                       |   44 +-
 src/gs-shell-moderate.c                        |    3 +-
 src/gs-shell-search.c                          |    4 +-
 src/gs-sources-dialog.c                        |   17 +-
 src/gs-update-dialog.c                         |   10 +-
 src/gs-update-list.c                           |   20 +-
 src/gs-utils.c                                 |    4 +-
 src/plugins/Makefile.am                        |    5 +-
 src/plugins/gs-appstream.c                     |  568 +++++++++
 src/plugins/gs-appstream.h                     |   40 +
 src/plugins/gs-plugin-appstream.c              |  561 +---------
 src/plugins/gs-plugin-apt.c                    |    4 +-
 src/plugins/gs-plugin-dummy.c                  |   39 +-
 src/plugins/gs-plugin-epiphany.c               |    4 +-
 src/plugins/gs-plugin-fedora-distro-upgrades.c |    2 +-
 src/plugins/gs-plugin-fedora-provenance.c      |    6 +-
 src/plugins/gs-plugin-fwupd.c                  |   16 +-
 src/plugins/gs-plugin-limba.c                  |    4 +-
 src/plugins/gs-plugin-moduleset.c              |    4 +-
 src/plugins/gs-plugin-packagekit-history.c     |    4 +-
 src/plugins/gs-plugin-packagekit-offline.c     |    2 +-
 src/plugins/gs-plugin-packagekit-refine.c      |   20 +-
 src/plugins/gs-plugin-packagekit-refresh.c     |   12 +-
 src/plugins/gs-plugin-packagekit.c             |    6 +-
 src/plugins/gs-plugin-self-test.c              |    2 +-
 src/plugins/gs-plugin-systemd-updates.c        |    2 +-
 src/plugins/gs-plugin-xdg-app-reviews.c        |    4 +-
 src/plugins/gs-plugin-xdg-app.c                |  267 ++++-
 src/plugins/menu-spec-common.c                 |    1 +
 src/plugins/packagekit-common.c                |    2 +-
 51 files changed, 4620 insertions(+), 3425 deletions(-)
---
diff --cc src/plugins/gs-plugin-apt.c
index da08bf0,0000000..ffe5d52
mode 100644,000000..100644
--- a/src/plugins/gs-plugin-apt.c
+++ b/src/plugins/gs-plugin-apt.c
@@@ -1,824 -1,0 +1,824 @@@
 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 + *
 + * Copyright (C) 2016 Canonical Ltd
 + *
 + * 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 <stdlib.h>
 +#include <string.h>
 +#include <ctype.h>
 +
 +#include <gs-plugin.h>
 +#include <gs-utils.h>
 +
 +typedef struct {
 +      gchar *name;
 +      gchar *installed_version;
 +      gchar *update_version;
 +      gint installed_size;
 +      gboolean is_official;
 +      gboolean is_open_source;
 +} PackageInfo;
 +
 +
 +#include "ubuntu-unity-launcher-proxy.h"
 +
 +struct GsPluginPrivate {
 +      gsize            loaded;
 +      GHashTable      *package_info;
 +      GList           *installed_packages;
 +      GList           *updatable_packages;
 +};
 +
 +const gchar *
 +gs_plugin_get_name (void)
 +{
 +      return "apt";
 +}
 +
 +static void
 +free_package_info (gpointer data)
 +{
 +      PackageInfo *info = data;
 +      g_free (info->name);
 +      g_free (info->installed_version);
 +      g_free (info->update_version);
 +      g_free (info);
 +}
 +
 +void
 +gs_plugin_initialize (GsPlugin *plugin)
 +{
 +      plugin->priv = GS_PLUGIN_GET_PRIVATE (GsPluginPrivate);
 +      plugin->priv->package_info = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_package_info);
 +}
 +
 +void
 +gs_plugin_destroy (GsPlugin *plugin)
 +{
 +      g_hash_table_unref (plugin->priv->package_info);
 +      g_list_free (plugin->priv->installed_packages);
 +      g_list_free (plugin->priv->updatable_packages);
 +}
 +
 +// Ordering of symbols in dpkg ~, 0-9, A-Z, a-z, everything else (ASCII ordering)
 +static int
 +order (int c)
 +{
 +      if (isdigit(c))
 +              return 0;
 +      else if (isalpha(c))
 +              return c;
 +      else if (c == '~')
 +              return -1;
 +      else if (c)
 +              return c + 256;
 +      else
 +              return 0;
 +}
 +
 +// Check if this is a valid dpkg character ('-' terminates version for revision)
 +static gboolean
 +valid_char (char c)
 +{
 +      return c != '\0' && c != '-';
 +}
 +
 +static int
 +compare_version (const char *v0, const char *v1)
 +{
 +      while (valid_char (*v0) || valid_char (*v1))
 +      {
 +              int first_diff = 0;
 +
 +              // Compare non-digits based on ordering rules
 +              while ((valid_char (*v0) && !isdigit (*v0)) || (valid_char (*v1) && !isdigit (*v1))) {
 +                      int o0 = order (*v0);
 +                      int o1 = order (*v1);
 +
 +                      if (o0 != o1)
 +                              return o0 - o1;
 +
 +                      v0++;
 +                      v1++;
 +              }
 +
 +              // Skip leading zeroes
 +              while (*v0 == '0')
 +                      v0++;
 +              while (*v1 == '0')
 +                      v1++;
 +
 +              // Compare numbers - longest wins, otherwise compare first digit
 +              while (isdigit (*v0) && isdigit (*v1)) {
 +                      if (first_diff == 0)
 +                              first_diff = *v0 - *v1;
 +                      v0++;
 +                      v1++;
 +              }
 +              if (isdigit (*v0))
 +                      return 1;
 +              if (isdigit (*v1))
 +                      return -1;
 +              if (first_diff)
 +                      return first_diff;
 +      }
 +
 +      return 0;
 +}
 +
 +// Get the epoch, i.e. 1:2.3-4 -> '1'
 +static int
 +get_epoch (const gchar *version)
 +{
 +      if (strchr (version, ':') == NULL)
 +              return 0;
 +      return atoi (version);
 +}
 +
 +// Get the version after the epoch, i.e. 1:2.3-4 -> '2.3'
 +static const gchar *
 +get_version (const gchar *version)
 +{
 +      const gchar *v = strchr (version, ':');
 +      return v ? v : version;
 +}
 +
 +// Get the debian revision, i.e. 1:2.3-4 -> '4'
 +static const gchar *
 +get_revision (const gchar *version)
 +{
 +      const gchar *v = strchr (version, '-');
 +      return v ? v + 1: version + strlen (version);
 +}
 +
 +static int
 +compare_dpkg_version (const gchar *v0, const gchar *v1)
 +{
 +      int r;
 +
 +      r = get_epoch (v0) - get_epoch (v1);
 +      if (r != 0)
 +              return r;
 +
 +      r = compare_version (get_version (v0), get_version (v1));
 +      if (r != 0)
 +              return r;
 +
 +      return compare_version (get_revision (v0), get_revision (v1));
 +}
 +
 +static gboolean
 +version_newer (const gchar *v0, const gchar *v1)
 +{
 +      return v0 ? compare_dpkg_version (v0, v1) < 0 : TRUE;
 +}
 +
 +typedef gboolean (*PackageFileFunc) (const gchar *name, gsize name_length, const gchar *value, gsize 
value_length, gpointer user_data, GError **error);
 +
 +static void
 +skip_to_eol (gchar *data, gsize data_length, gsize *offset)
 +{
 +      while (*offset < data_length && data[*offset] != '\n')
 +              (*offset)++;
 +      if (*offset >= data_length)
 +              return;
 +      (*offset)++;
 +}
 +
 +static gboolean
 +parse_package_file_data (gchar *data, gsize data_length, PackageFileFunc callback, gpointer user_data, 
GError **error)
 +{
 +      gsize offset;
 +
 +      for (offset = 0; offset < data_length; ) {
 +              gsize name_start, name_end, name_length;
 +              gsize value_start, value_end, value_length;
 +
 +              // Entry divided by empty space
 +              if (data[offset] == '\n') {
 +                      offset++;
 +                      continue;
 +              }
 +
 +              // Line continuations start with a space
 +              if (data[offset] == ' ') {
 +                      skip_to_eol (data, data_length, &offset);
 +                      continue;
 +              }
 +
 +              // Find the field in the form "name: value"
 +              name_start = offset;
 +              name_end = name_start + 1;
 +              while (name_end < data_length && !(data[name_end] == ':' || data[name_end] == '\n'))
 +                      name_end++;
 +              if ((name_end + 1) >= data_length)
 +                      return TRUE;
 +              if (data[name_end] != ':') {
 +                      offset = name_end;
 +                      skip_to_eol (data, data_length, &offset);
 +              }
 +              value_start = name_end + 1;
 +              while (value_start < data_length && data[value_start] == ' ')
 +                      value_start++;
 +              value_end = value_start + 1;
 +              while (value_end < data_length && data[value_end] != '\n')
 +                      value_end++;
 +              if (value_end >= data_length)
 +                      return TRUE;
 +              name_length = name_end - name_start;
 +              value_length = value_end - value_start;
 +
 +              if (!callback (data + name_start, name_length, data + value_start, value_length, user_data, 
error))
 +                      return FALSE;
 +              offset = value_end + 1;
 +      }
 +
 +      return TRUE;
 +}
 +
 +static gboolean
 +parse_package_file (const gchar *filename, PackageFileFunc callback, gpointer user_data, GError **error)
 +{
 +      g_autoptr(GMappedFile) f = NULL;
 +
 +      f = g_mapped_file_new (filename, FALSE, NULL);
 +      if (f == NULL)
 +              return FALSE;
 +      return parse_package_file_data (g_mapped_file_get_contents (f), g_mapped_file_get_length (f), 
callback, user_data, error);
 +}
 +
 +typedef struct {
 +      GsPlugin *plugin;
 +      PackageInfo *current_info;
 +      gboolean current_installed;
 +      gboolean is_official;
 +      gboolean is_open_source;
 +} FieldData;
 +
 +static gboolean
 +field_cb (const gchar *name, gsize name_length, const gchar *value, gsize value_length, gpointer user_data, 
GError **error)
 +{
 +      FieldData *data = user_data;
 +
 +      if (strncmp (name, "Package", name_length) == 0) {
 +              gchar *id = g_strndup (value, value_length);
 +              data->current_info = g_hash_table_lookup (data->plugin->priv->package_info, id);
 +              if (data->current_info == NULL) {
 +                      data->current_info = g_slice_new0 (PackageInfo);
 +                      data->current_installed = FALSE;
 +                      data->current_info->name = id;
 +                      g_hash_table_insert (data->plugin->priv->package_info, data->current_info->name, 
data->current_info);
 +              } else
 +                      g_free (id);
 +
 +              // If an official source provides this package, then mark it as such.
 +              // NOTE: A PPA could re-use the name and make this not true
 +              if (data->is_official)
 +                      data->current_info->is_official = data->is_official;
 +              if (data->is_open_source)
 +                      data->current_info->is_open_source = data->is_open_source;
 +              return TRUE;
 +      }
 +
 +      if (data->current_info == NULL)
 +              return TRUE;
 +
 +      if (strncmp (name, "Status", name_length) == 0) {
 +              if (strncmp (value, "install ok installed", value_length) == 0) {
 +                      data->current_installed = TRUE;
 +                      data->plugin->priv->installed_packages = g_list_append 
(data->plugin->priv->installed_packages, data->current_info);
 +              }
 +      } else if (strncmp (name, "Installed-Size", name_length) == 0) {
 +              data->current_info->installed_size = atoi (value);
 +      } else if (strncmp (name, "Version", name_length) == 0) {
 +              gchar *version = g_strndup (value, value_length);
 +              if (data->current_installed) {
 +                      g_free (data->current_info->installed_version);
 +                      data->current_info->installed_version = version;
 +              } else if (version_newer (data->current_info->installed_version, version) && version_newer 
(data->current_info->update_version, version)) {
 +                      if (data->current_info->installed_version && data->current_info->update_version == 
NULL)
 +                              data->plugin->priv->updatable_packages = g_list_append 
(data->plugin->priv->updatable_packages, data->current_info);
 +                      g_free (data->current_info->update_version);
 +                      data->current_info->update_version = version;
 +              } else
 +                      g_free (version);
 +      }
 +
 +      return TRUE;
 +}
 +
 +static gboolean
 +load_db (GsPlugin *plugin, GError **error)
 +{
 +      GPtrArray *lists;
 +      GDir *dir;
 +      guint i;
 +      gboolean result = FALSE;
 +
 +      // Find the package lists to load
 +      lists = g_ptr_array_new ();
 +      g_ptr_array_set_free_func (lists, g_free);
 +      g_ptr_array_add (lists, g_strdup ("/var/lib/dpkg/status"));
 +      dir = g_dir_open ("/var/lib/apt/lists", 0, NULL);
 +      while (TRUE) {
 +              const gchar *name = g_dir_read_name (dir);
 +              if (name == NULL)
 +                      break;
 +              if (g_str_has_suffix (name, "_Packages"))
 +                      g_ptr_array_add (lists, g_build_filename ("/var/lib/apt/lists", name, NULL));
 +      }
 +      g_dir_close (dir);
 +
 +      for (i = 0; i < lists->len; i++) {
 +              const gchar *list = lists->pdata[i];
 +              FieldData data;
 +              data.is_official = g_str_has_prefix (list, "/var/lib/apt/lists/archive.ubuntu.com_");
 +              data.is_open_source = data.is_official && (strstr (list, "_main_") != NULL || strstr (list, 
"_universe_") != NULL);
 +              data.plugin = plugin;
 +              data.current_info = NULL;
 +              data.current_installed = FALSE;
 +              result = parse_package_file (list, field_cb, &data, error);
 +              if (!result)
 +                      goto done;
 +      }
 +
 +done:
 +      g_ptr_array_unref (lists);
 +      return result;
 +}
 +
 +static void
 +get_changelog (GsPlugin *plugin, GsApp *app)
 +{
 +      const gchar *source, *current_version, *version;
 +      g_autofree gchar *source_prefix = NULL, *uri = NULL, *changelog_prefix = NULL;
 +      g_autoptr(SoupMessage) msg = NULL;
 +      guint status_code;
 +      gchar **lines;
 +      int i;
 +      GString *details;
 +
 +      g_return_if_fail (gs_app_get_source_default (app) != NULL);
 +      g_return_if_fail (gs_app_get_update_version (app) != NULL);
 +
 +      source = gs_app_get_source_default (app);
 +      if (g_str_has_prefix (source, "lib"))
 +              source_prefix = g_strdup_printf ("lib%c", source[3]);
 +      else
 +              source_prefix = g_strdup_printf ("%c", source[0]);
 +      current_version = gs_app_get_version (app);
 +      version = gs_app_get_update_version (app);
 +      uri = g_strdup_printf ("http://changelogs.ubuntu.com/changelogs/binary/%s/%s/%s/changelog";, 
source_prefix, source, version);
 +      msg = soup_message_new (SOUP_METHOD_GET, uri);
 +
 +      status_code = soup_session_send_message (plugin->soup_session, msg);
 +      if (status_code != SOUP_STATUS_OK) {
 +              g_warning ("Failed to get changelog for %s version %s from changelogs.ubuntu.com: %s", 
source, version, soup_status_get_phrase (status_code));
 +              return;
 +      }
 +
 +      // Extract changelog entries newer than our current version
 +      changelog_prefix = g_strdup_printf ("%s (", source);
 +      lines = g_strsplit (msg->response_body->data, "\n", -1);
 +      details = g_string_new ("");
 +      for (i = 0; lines[i] != NULL; i++) {
 +              gchar *line = lines[i];
 +              int version_start, version_end;
 +              g_autofree gchar *v = NULL;
 +
 +              // First line is in the form "package (version) distribution(s); urgency=urgency"
 +              if (!g_str_has_prefix (line, changelog_prefix))
 +                      continue;
 +              version_start = strlen (changelog_prefix);
 +              for (version_end = version_start; line[version_end] != '\0' && line[version_end] != ')'; 
version_end++);
 +              v = g_strdup_printf ("%.*s", version_end - version_start, line + version_start);
 +
 +              // We're only interested in new versions
 +              if (!version_newer (current_version, v))
 +                      break;
 +
 +              g_string_append_printf (details, "# %s\n\n", v);
 +              for (i++; lines[i] != NULL; i++) {
 +                      // Last line is in the form " -- maintainer name <email address>  date"
 +                      if (g_str_has_prefix (lines[i], " -- "))
 +                              break;
 +                      if (g_str_has_prefix (lines[i], "  "))
 +                              g_string_append_printf (details, "%s\n\n", lines[i] + 2);
 +                      else
 +                              g_string_append_printf (details, "%s\n\n", lines[i]);
 +              }
 +      }
 +      g_strfreev (lines);
 +
 +      gs_app_set_update_details (app, details->str);
 +      g_string_free (details, TRUE);
 +}
 +
 +gboolean
 +gs_plugin_refine (GsPlugin *plugin,
 +                GList **list,
 +                GsPluginRefineFlags flags,
 +                GCancellable *cancellable,
 +                GError **error)
 +{
 +      GList *link;
 +
 +      /* Load database once */
 +      if (g_once_init_enter (&plugin->priv->loaded)) {
 +              gboolean ret;
 +
 +              ret = load_db (plugin, error);
 +              g_once_init_leave (&plugin->priv->loaded, TRUE);
 +              if (!ret)
 +                      return FALSE;
 +      }
 +
 +      for (link = *list; link; link = link->next) {
 +              GsApp *app = link->data;
 +              PackageInfo *info;
 +
 +              if (gs_app_get_source_default (app) == NULL)
 +                      continue;
 +
 +              info = g_hash_table_lookup (plugin->priv->package_info, gs_app_get_source_default (app));
 +              if (info != NULL) {
 +                      if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) {
 +                              if (info->installed_version != NULL) {
 +                                      if (info->update_version != NULL) {
 +                                              gs_app_set_state (app, AS_APP_STATE_UPDATABLE_LIVE);
 +                                      } else {
 +                                              gs_app_set_state (app, AS_APP_STATE_INSTALLED);
 +                                      }
 +                              } else {
 +                                      gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
 +                              }
 +                      }
 +                      if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE) != 0 && gs_app_get_size (app) == 0) 
{
 +                              gs_app_set_size (app, info->installed_size * 1024);
 +                      }
 +                      if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION) != 0) {
 +                              if (info->installed_version != NULL) {
 +                                      gs_app_set_version (app, info->installed_version);
 +                              }
 +                              if (info->update_version != NULL) {
 +                                      gs_app_set_update_version (app, info->update_version);
 +                              }
 +                      }
 +                      if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) != 0 && info->is_official) {
-                               gs_app_set_provenance (app, TRUE);
++                              gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE);
 +                      }
 +                      if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENCE) != 0 && info->is_open_source) {
-                               gs_app_set_licence (app, "@LicenseRef-ubuntu", GS_APP_QUALITY_HIGHEST);
++                              gs_app_set_licence (app, GS_APP_QUALITY_HIGHEST, "@LicenseRef-ubuntu");
 +                      }
 +              }
 +
 +              if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS) != 0) {
 +                      get_changelog (plugin, app);
 +              }
 +      }
 +
 +      return TRUE;
 +}
 +
 +gboolean
 +gs_plugin_add_installed (GsPlugin *plugin,
 +                       GList **list,
 +                       GCancellable *cancellable,
 +                       GError **error)
 +{
 +      GList *link;
 +
 +      /* Load database once */
 +      if (g_once_init_enter (&plugin->priv->loaded)) {
 +              gboolean ret;
 +
 +              ret = load_db (plugin, error);
 +              g_once_init_leave (&plugin->priv->loaded, TRUE);
 +              if (!ret)
 +                      return FALSE;
 +      }
 +
 +      for (link = plugin->priv->installed_packages; link; link = link->next) {
 +              PackageInfo *info = link->data;
 +              g_autoptr(GsApp) app = NULL;
 +
 +              app = gs_app_new (info->name);
 +              // FIXME: Since appstream marks all packages as owned by PackageKit and we are replacing 
PackageKit we need to accept those packages
 +              gs_app_set_management_plugin (app, "PackageKit");
 +              gs_app_add_source (app, info->name);
 +              gs_plugin_add_app (list, app);
 +      }
 +
 +      return TRUE;
 +}
 +
 +typedef struct
 +{
 +      GsPlugin *plugin;
 +      GMainLoop *loop;
 +      GsApp *app;
 +      gchar **result;
 +} TransactionData;
 +
 +static void
 +transaction_property_changed_cb (GDBusConnection *connection,
 +                               const gchar *sender_name,
 +                               const gchar *object_path,
 +                               const gchar *interface_name,
 +                               const gchar *signal_name,
 +                               GVariant *parameters,
 +                               gpointer user_data)
 +{
 +      TransactionData *data = user_data;
 +      const gchar *name;
 +      g_autoptr(GVariant) value = NULL;
 +
 +      g_variant_get (parameters, "(&sv)", &name, &value);
 +      if (data->app && strcmp (name, "Progress") == 0)
 +              gs_plugin_progress_update (data->plugin, data->app, g_variant_get_int32 (value));
 +}
 +
 +static void
 +transaction_finished_cb (GDBusConnection *connection,
 +                       const gchar *sender_name,
 +                       const gchar *object_path,
 +                       const gchar *interface_name,
 +                       const gchar *signal_name,
 +                       GVariant *parameters,
 +                       gpointer user_data)
 +{
 +      TransactionData *data = user_data;
 +
 +      g_variant_get (parameters, "(s)", data->result);
 +
 +      g_main_loop_quit (data->loop);
 +}
 +
 +static void
 +notify_unity_launcher (GsApp *app, const gchar *transaction_path)
 +{
 +      UbuntuUnityLauncher *launcher = NULL;
 +
 +      g_return_if_fail (GS_IS_APP (app));
 +      g_return_if_fail (transaction_path);
 +
 +      launcher = ubuntu_unity_launcher_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
 +              G_DBUS_PROXY_FLAGS_NONE,
 +              "com.canonical.Unity.Launcher",
 +              "/com/canonical/Unity/Launcher",
 +              NULL, NULL);
 +
 +      g_return_if_fail (launcher);
 +
 +      ubuntu_unity_launcher_call_add_launcher_item (launcher,
 +              gs_app_get_id (app),
 +              transaction_path,
 +              NULL, NULL, NULL);
 +
 +      g_object_unref (launcher);
 +}
 +
 +static gboolean
 +aptd_transaction (GsPlugin *plugin, const gchar *method, GsApp *app, GError **error)
 +{
 +      g_autoptr(GDBusConnection) conn = NULL;
 +      GVariant *parameters;
 +      g_autoptr(GVariant) result = NULL;
 +      g_autofree gchar *transaction_path = NULL, *transaction_result = NULL;
 +      g_autoptr(GMainLoop) loop = NULL;
 +      guint property_signal, finished_signal;
 +      TransactionData data;
 +
 +      conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
 +      if (conn == NULL)
 +              return FALSE;
 +
 +      if (app) {
 +              GVariantBuilder builder;
 +              g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")),
 +              g_variant_builder_add (&builder, "s", gs_app_get_source_default (app));
 +              parameters = g_variant_new ("(as)", &builder);
 +      }
 +      else
 +              parameters = g_variant_new ("()");
 +      result = g_dbus_connection_call_sync (conn,
 +                                            "org.debian.apt",
 +                                            "/org/debian/apt",
 +                                            "org.debian.apt",
 +                                            method,
 +                                            parameters,
 +                                            G_VARIANT_TYPE ("(s)"),
 +                                            G_DBUS_CALL_FLAGS_NONE,
 +                                            -1,
 +                                            NULL,
 +                                            error);
 +      if (result == NULL)
 +              return FALSE;
 +      g_variant_get (result, "(s)", &transaction_path);
 +      g_variant_unref (result);
 +
 +      if (!g_strcmp0(method, "InstallPackages"))
 +              notify_unity_launcher (app, transaction_path);
 +
 +      loop = g_main_loop_new (NULL, FALSE);
 +
 +      data.plugin = plugin;
 +      data.app = app;
 +      data.loop = loop;
 +      data.result = &transaction_result;
 +      property_signal = g_dbus_connection_signal_subscribe (conn,
 +                                                            "org.debian.apt",
 +                                                            "org.debian.apt.transaction",
 +                                                            "PropertyChanged",
 +                                                            transaction_path,
 +                                                            NULL,
 +                                                            G_DBUS_SIGNAL_FLAGS_NONE,
 +                                                            transaction_property_changed_cb,
 +                                                            &data,
 +                                                            NULL);
 +      finished_signal = g_dbus_connection_signal_subscribe (conn,
 +                                                            "org.debian.apt",
 +                                                            "org.debian.apt.transaction",
 +                                                            "Finished",
 +                                                            transaction_path,
 +                                                            NULL,
 +                                                            G_DBUS_SIGNAL_FLAGS_NONE,
 +                                                            transaction_finished_cb,
 +                                                            &data,
 +                                                            NULL);
 +      result = g_dbus_connection_call_sync (conn,
 +                                            "org.debian.apt",
 +                                            transaction_path,
 +                                            "org.debian.apt.transaction",
 +                                            "Run",
 +                                            g_variant_new ("()"),
 +                                            G_VARIANT_TYPE ("()"),
 +                                            G_DBUS_CALL_FLAGS_NONE,
 +                                            -1,
 +                                            NULL,
 +                                            error);
 +      if (result == NULL)
 +              return FALSE;
 +
 +      g_main_loop_run (loop);
 +      g_dbus_connection_signal_unsubscribe (conn, property_signal);
 +      g_dbus_connection_signal_unsubscribe (conn, finished_signal);
 +
 +      if (g_strcmp0 (transaction_result, "exit-success") != 0) {
 +              g_set_error (error,
 +                           GS_PLUGIN_ERROR,
 +                           GS_PLUGIN_ERROR_FAILED,
 +                           "apt trasaction returned result %s", transaction_result);
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +static gboolean
 +app_is_ours (GsApp *app)
 +{
 +      // FIXME: Since appstream marks all packages as owned by PackageKit and we are replacing PackageKit 
we need to accept those packages
 +      return g_strcmp0 (gs_app_get_management_plugin (app), "PackageKit") == 0;
 +}
 +
 +gboolean
 +gs_plugin_app_install (GsPlugin *plugin,
 +                     GsApp *app,
 +                     GCancellable *cancellable,
 +                     GError **error)
 +{
 +      if (!app_is_ours (app))
 +              return TRUE;
 +
 +      if (gs_app_get_source_default (app) == NULL)
 +              return TRUE;
 +
 +      gs_app_set_state (app, AS_APP_STATE_INSTALLING);
 +      if (aptd_transaction (plugin, "InstallPackages", app, error))
 +              gs_app_set_state (app, AS_APP_STATE_INSTALLED);
 +      else {
 +              gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +gboolean
 +gs_plugin_app_remove (GsPlugin *plugin,
 +                    GsApp *app,
 +                    GCancellable *cancellable,
 +                    GError **error)
 +{
 +      if (!app_is_ours (app))
 +              return TRUE;
 +
 +      if (gs_app_get_source_default (app) == NULL)
 +              return TRUE;
 +
 +      gs_app_set_state (app, AS_APP_STATE_REMOVING);
 +      if (aptd_transaction (plugin, "RemovePackages", app, error))
 +              gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
 +      else {
 +              gs_app_set_state (app, AS_APP_STATE_INSTALLED);
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +gboolean
 +gs_plugin_refresh (GsPlugin *plugin,
 +                 guint cache_age,
 +                 GsPluginRefreshFlags flags,
 +                 GCancellable *cancellable,
 +                 GError **error)
 +{
 +      if ((flags & GS_PLUGIN_REFRESH_FLAGS_UPDATES) == 0)
 +              return TRUE;
 +
 +      return aptd_transaction (plugin, "UpdateCache", NULL, error);
 +}
 +
 +gboolean
 +gs_plugin_add_updates (GsPlugin *plugin,
 +                       GList **list,
 +                       GCancellable *cancellable,
 +                       GError **error)
 +{
 +      GList *link;
 +
 +      /* Load database once */
 +      if (g_once_init_enter (&plugin->priv->loaded)) {
 +              gboolean ret;
 +
 +              ret = load_db (plugin, error);
 +              g_once_init_leave (&plugin->priv->loaded, TRUE);
 +              if (!ret)
 +                      return FALSE;
 +      }
 +
 +      for (link = plugin->priv->updatable_packages; link; link = link->next) {
 +              PackageInfo *info = link->data;
 +              g_autoptr(GsApp) app = NULL;
 +
 +              app = gs_app_new (info->name);
 +              // FIXME: Since appstream marks all packages as owned by PackageKit and we are replacing 
PackageKit we need to accept those packages
 +              gs_app_set_management_plugin (app, "PackageKit");
 +              gs_app_add_source (app, info->name);
 +              gs_plugin_add_app (list, app);
 +      }
 +
 +      return TRUE;
 +}
 +
 +gboolean
 +gs_plugin_app_update (GsPlugin *plugin,
 +                    GsApp *app,
 +                    GCancellable *cancellable,
 +                    GError **error)
 +{
 +      if (!app_is_ours (app))
 +              return TRUE;
 +
 +      gs_app_set_state (app, AS_APP_STATE_INSTALLING);
 +      if (aptd_transaction (plugin, "UpgradePackages", app, error))
 +              gs_app_set_state (app, AS_APP_STATE_INSTALLED);
 +      else {
 +              gs_app_set_state (app, AS_APP_STATE_UPDATABLE_LIVE);
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +gboolean
 +gs_plugin_launch (GsPlugin *plugin,
 +                GsApp *app,
 +                GCancellable *cancellable,
 +                GError **error)
 +{
 +      if (!app_is_ours (app))
 +              return TRUE;
 +
 +      return gs_plugin_app_launch (plugin, app, error);
 +}


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