[gnome-software/wip/rancell/apt2] Create an APT plugin for systems that aren't using PackageKit
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/rancell/apt2] Create an APT plugin for systems that aren't using PackageKit
- Date: Fri, 3 Feb 2017 02:57:19 +0000 (UTC)
commit 0bf3b796f3dee527f9516e71c8efa0f9d779082a
Author: Robert Ancell <robert ancell canonical com>
Date: Fri Feb 3 15:56:59 2017 +1300
Create an APT plugin for systems that aren't using PackageKit
configure.ac | 26 +
src/plugins/Makefile.am | 24 +
src/plugins/com.canonical.Unity.Launcher.xml | 15 +
src/plugins/gs-plugin-apt.cc | 829 ++++++++++++++++++++++++++
4 files changed, 894 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 57a12c2..d63e07b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,6 +11,7 @@ AC_CONFIG_MACRO_DIR([m4])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
AC_PROG_CC_C99
+AC_PROG_CXX
AC_PROG_INSTALL
LT_INIT
AM_PROG_CC_C_O
@@ -206,6 +207,30 @@ AS_IF([test "x$have_packagekit" = "xyes"], [
])
AM_CONDITIONAL(HAVE_PACKAGEKIT, test "$have_packagekit" != no)
+# libapt
+AC_ARG_ENABLE(apt,
+ [AS_HELP_STRING([--enable-apt],
+ [enable apt support [default=no]])],,
+ enable_apt=maybe)
+AS_IF([test "x$enable_apt" = "xyes"], [
+ AC_LANG_PUSH([C++])
+ AC_CHECK_HEADERS([apt-pkg/init.h],
+ [have_apt=yes],
+ [have_apt=no])
+ AC_LANG_POP
+], [
+ have_apt=no
+])
+
+AS_IF([test "x$have_apt" = "xyes"], [
+ AC_DEFINE(HAVE_APT,1,[Build apt support])
+], [
+ AS_IF([test "x$enable_apt" = "xyes"], [
+ AC_MSG_ERROR([apt support requested but 'libapt-pkg' was not found])
+ ])
+])
+AM_CONDITIONAL(HAVE_APT, test "$have_apt" != no)
+
# PolicyKit
AC_ARG_ENABLE(polkit,
[AS_HELP_STRING([--enable-polkit],
@@ -496,6 +521,7 @@ echo "
GNOME Shell ext. support: ${enable_shell_extensions}
ODRS support: ${enable_odrs}
Webapps support: ${enable_webapps}
+ APT support: ${have_apt}
Ubuntu Reviews support: ${enable_ubuntu_reviews}
Snap support: ${have_snap}
Ext. appstream support: ${enable_external_appstream}
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 6853865..31f942e 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -66,6 +66,11 @@ if HAVE_SNAP
plugin_LTLIBRARIES += libgs_plugin_snap.la
endif
+if HAVE_APT
+plugin_LTLIBRARIES += \
+ libgs_plugin_apt.la
+endif
+
if HAVE_PACKAGEKIT
plugin_LTLIBRARIES += \
libgs_plugin_systemd-updates.la \
@@ -263,6 +268,25 @@ libgs_plugin_steam_la_LDFLAGS = -module -avoid-version
libgs_plugin_steam_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
endif
+if HAVE_APT
+ubuntu-unity-launcher-proxy.c ubuntu-unity-launcher-proxy.h: com.canonical.Unity.Launcher.xml Makefile
+ $(AM_V_GEN) gdbus-codegen --interface-prefix com.canonical.Unity.Launcher \
+ --generate-c-code ubuntu-unity-launcher-proxy \
+ --c-namespace UbuntuUnity \
+ --annotate 'com.canonical.Unity.Launcher' \
+ org.gtk.GDBus.C.Name \
+ Launcher \
+ $<
+
+libgs_plugin_apt_la_SOURCES = \
+ ubuntu-unity-launcher-proxy.c \
+ ubuntu-unity-launcher-proxy.h \
+ gs-plugin-apt.cc
+libgs_plugin_apt_la_LIBADD = $(GS_PLUGIN_LIBS) -lapt-pkg
+libgs_plugin_apt_la_LDFLAGS = -module -avoid-version
+libgs_plugin_apt_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
+endif
+
libgs_plugin_repos_la_SOURCES = gs-plugin-repos.c
libgs_plugin_repos_la_LIBADD = $(GS_PLUGIN_LIBS) $(RPM_LIBS)
libgs_plugin_repos_la_LDFLAGS = -module -avoid-version
diff --git a/src/plugins/com.canonical.Unity.Launcher.xml b/src/plugins/com.canonical.Unity.Launcher.xml
new file mode 100644
index 0000000..b0d7915
--- /dev/null
+++ b/src/plugins/com.canonical.Unity.Launcher.xml
@@ -0,0 +1,15 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name='com.canonical.Unity.Launcher'>
+ <method name='AddLauncherItem'>
+ <arg type='s' name='appstream_app_id' direction='in'/>
+ <arg type='s' name='aptdaemon_task' direction='in'/>
+ </method>
+
+ <method name='UpdateLauncherIconFavoriteState'>
+ <arg type='s' name='icon_uri' direction='in'/>
+ <arg type='b' name='is_sticky' direction='in'/>
+ </method>
+ </interface>
+</node>
diff --git a/src/plugins/gs-plugin-apt.cc b/src/plugins/gs-plugin-apt.cc
new file mode 100644
index 0000000..87e55a9
--- /dev/null
+++ b/src/plugins/gs-plugin-apt.cc
@@ -0,0 +1,829 @@
+/* -*- 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 <apt-pkg/init.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/version.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <set>
+#include <vector>
+#include <fstream>
+#include <iomanip>
+#include <algorithm>
+#include <stdexcept>
+
+#include <gs-plugin.h>
+#include <gs-utils.h>
+
+#define LICENSE_URL "http://www.ubuntu.com/about/about-ubuntu/licensing"
+
+typedef struct {
+ gchar *name;
+ gchar *section;
+ gchar *architecture;
+ gchar *installed_version;
+ gchar *update_version;
+ gchar *origin;
+ gchar *release;
+ gchar *component;
+ gint installed_size;
+ gint package_size;
+} PackageInfo;
+
+
+#include "ubuntu-unity-launcher-proxy.h"
+
+struct GsPluginData {
+ gsize loaded;
+ GHashTable *package_info;
+ GList *installed_packages;
+ GList *updatable_packages;
+};
+
+static void
+free_package_info (gpointer data)
+{
+ PackageInfo *info = (PackageInfo *) data;
+ g_free (info->section);
+ g_free (info->installed_version);
+ g_free (info->update_version);
+ g_free (info->origin);
+ g_free (info->release);
+ g_free (info->component);
+ g_free (info);
+}
+
+void
+gs_plugin_initialize (GsPlugin *plugin)
+{
+ GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData));
+
+ priv->package_info = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ free_package_info);
+
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "appstream"); /* need pkgname */
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit-history");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit-offline");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit-origin");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit-proxy");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit-refine");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit-refresh");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "systemd-updates");
+}
+
+gboolean
+gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+
+ pkgInitConfig (*_config);
+ pkgInitSystem (*_config, _system);
+
+ /* success */
+ return TRUE;
+}
+
+gboolean
+gs_plugin_url_to_app (GsPlugin *plugin,
+ GsAppList *list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *scheme = NULL;
+
+ /* not us */
+ scheme = gs_utils_get_url_scheme (url);
+ if (g_strcmp0 (scheme, "apt") != 0)
+ return TRUE;
+
+ // FIXME
+
+ return TRUE;
+}
+
+void
+gs_plugin_destroy (GsPlugin *plugin)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ g_hash_table_unref (priv->package_info);
+ g_list_free (priv->installed_packages);
+ g_list_free (priv->updatable_packages);
+}
+
+static gboolean
+version_newer (const gchar *v0, const gchar *v1)
+{
+ return v0 ? _system->VS->CmpVersion(v0, v1) < 0 : TRUE;
+}
+
+static gboolean
+look_at_pkg (const pkgCache::PkgIterator &P,
+ pkgSourceList *list,
+ pkgPolicy *policy,
+ GsPlugin *plugin)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ pkgCache::VerIterator current = P.CurrentVer();
+ pkgCache::VerIterator candidate = policy->GetCandidateVer(P);
+ pkgCache::VerFileIterator VF;
+ FileFd PkgF;
+ pkgTagSection Tags;
+ gchar *name;
+
+ PackageInfo *info;
+
+ if (!candidate || !candidate.FileList ())
+ return false;
+
+ name = g_strdup (P.Name ());
+
+ info = (PackageInfo *) g_hash_table_lookup (priv->package_info, name);
+
+ if (!info)
+ {
+ info = g_new0 (PackageInfo, 1);
+ info->name = name;
+ g_hash_table_insert (priv->package_info, name, info);
+ }
+
+ for (VF = candidate.FileList (); VF.IsGood (); VF++) {
+ // see InRelease for the fields
+ if (VF.File ().Archive ())
+ info->release = g_strdup (VF.File ().Archive ());
+ if(VF.File ().Origin ())
+ info->origin = g_strdup (VF.File ().Origin ());
+ if(VF.File ().Component ())
+ info->component = g_strdup (VF.File ().Component ());
+ // also available: Codename, Label
+ break;
+ }
+
+
+ pkgCache::PkgFileIterator I = VF.File ();
+
+ if (I.IsOk () == false)
+ return _error->Error (("Package file %s is out of sync."),I.FileName ());
+
+ PkgF.Open (I.FileName (), FileFd::ReadOnly, FileFd::Extension);
+
+ pkgTagFile TagF (&PkgF);
+
+ if (TagF.Jump (Tags, current.FileList ()->Offset) == false)
+ return _error->Error ("Internal Error, Unable to parse a package record");
+
+ if (Tags.FindI ("Installed-Size") > 0)
+ info->installed_size = Tags.FindI ("Installed-Size")*1024;
+ else
+ info->installed_size = 0;
+
+ if (Tags.FindI ("Size") > 0)
+ info->package_size = Tags.FindI ("Size");
+ else
+ info->package_size = 0;
+
+ if (current)
+ info->installed_version = g_strdup (current.VerStr ());
+
+ info->section = g_strdup (candidate.Section ());
+ info->architecture = g_strdup (P.Arch ());
+ if (info->installed_version) {
+ info->update_version = g_strdup (candidate.VerStr ());
+ priv->installed_packages =
+ g_list_append (priv->installed_packages, info);
+ }
+
+ /* no upgrade */
+ if (g_strcmp0 (info->installed_version, info->update_version) == 0)
+ info->update_version = NULL;
+
+ if (info->update_version)
+ priv->updatable_packages =
+ g_list_append (priv->updatable_packages, info);
+
+ return TRUE;
+}
+
+static gboolean
+load_apt_db (GsPlugin *plugin, GError **error)
+{
+ pkgSourceList *list;
+ pkgPolicy *policy;
+ pkgCacheFile cachefile;
+ pkgCache *cache = cachefile.GetPkgCache();
+ pkgCache::PkgIterator P;
+
+ list = cachefile.GetSourceList();
+ policy = cachefile.GetPolicy();
+ if (cache == NULL || _error->PendingError()) {
+ _error->DumpErrors();
+ return FALSE;
+ }
+
+ for (pkgCache::GrpIterator grp = cache->GrpBegin(); grp != cache->GrpEnd(); grp++) {
+ P = grp.FindPreferredPkg();
+ if (P.end())
+ continue;
+ look_at_pkg (P, list, policy, plugin);
+ }
+
+ return TRUE;
+}
+
+static void
+get_changelog (GsPlugin *plugin, GsApp *app)
+{
+ g_autofree gchar *source_prefix = NULL,
+ *uri = NULL,
+ *changelog_prefix = NULL,
+ *binary_source = NULL,
+ *current_version = NULL,
+ *update_version = NULL;
+ g_autoptr(SoupMessage) msg = NULL;
+ guint status_code;
+ g_auto(GStrv) lines = NULL;
+ int i;
+ GString *details;
+
+ // Need to know the source and version to download changelog
+ binary_source = g_strdup (gs_app_get_source_default (app));
+ current_version = g_strdup (gs_app_get_version (app));
+ update_version = g_strdup (gs_app_get_update_version (app));
+ if (binary_source == NULL || update_version == NULL)
+ return;
+
+ if (g_str_has_prefix (binary_source, "lib"))
+ source_prefix = g_strdup_printf ("lib%c", binary_source[3]);
+ else
+ source_prefix = g_strdup_printf ("%c", binary_source[0]);
+ uri = g_strdup_printf ("http://changelogs.ubuntu.com/changelogs/binary/%s/%s/%s/changelog",
source_prefix, binary_source, update_version);
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+
+ status_code = soup_session_send_message (gs_plugin_get_soup_session (plugin), msg);
+ if (status_code != SOUP_STATUS_OK) {
+ g_warning ("Failed to get changelog for %s version %s from changelogs.ubuntu.com: %s",
binary_source, update_version, soup_status_get_phrase (status_code));
+ return;
+ }
+
+ // Extract changelog entries newer than our current version
+ lines = g_strsplit (msg->response_body->data, "\n", -1);
+ details = g_string_new ("");
+ for (i = 0; lines[i] != NULL; i++) {
+ gchar *line = lines[i];
+ const gchar *version_start, *version_end;
+ g_autofree gchar *v = NULL;
+
+ // First line is in the form "package (version) distribution(s); urgency=urgency"
+ version_start = strchr (line, '(');
+ version_end = strchr (line, ')');
+ if (line[0] == ' ' || version_start == NULL || version_end == NULL || version_end <
version_start)
+ continue;
+ v = g_strdup_printf ("%.*s", (int) (version_end - version_start - 1), version_start + 1);
+
+ // We're only interested in new versions
+ if (!version_newer (current_version, v))
+ break;
+
+ g_string_append_printf (details, "%s\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;
+ g_string_append_printf (details, "%s\n", lines[i]);
+ }
+ }
+
+ gs_app_set_update_details (app, details->str);
+ g_string_free (details, TRUE);
+}
+
+static gboolean
+is_official (PackageInfo *info)
+{
+ return g_strcmp0 (info->origin, "Ubuntu") == 0;
+}
+
+static gboolean
+is_open_source (PackageInfo *info)
+{
+ const gchar *open_source_components[] = { "main", "universe", NULL };
+
+ /* There's no valid apps in the libs section */
+ return info->component != NULL && g_strv_contains (open_source_components, info->component);
+}
+
+
+gboolean
+gs_plugin_refine (GsPlugin *plugin,
+ GList **list,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ GList *link;
+
+ /* Load database once */
+ if (g_once_init_enter (&priv->loaded)) {
+ gboolean ret;
+
+ ret = load_apt_db (plugin, error);
+ g_once_init_leave (&priv->loaded, TRUE);
+ if (!ret)
+ return FALSE;
+ }
+
+ for (link = *list; link; link = link->next) {
+ GsApp *app = (GsApp *) link->data;
+ PackageInfo *info;
+
+ if (gs_app_get_source_default (app) == NULL)
+ continue;
+
+ info = (PackageInfo *) g_hash_table_lookup (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_installed
(app) == 0) {
+ gs_app_set_size_installed (app, info->installed_size);
+ }
+ if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE) != 0 && gs_app_get_size_download
(app) == 0) {
+ gs_app_set_size_download (app, info->package_size);
+ }
+ 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 && is_official (info)) {
+ gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE);
+ }
+ if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE) != 0 && is_open_source(info)) {
+ gs_app_set_license (app, GS_APP_QUALITY_LOWEST, "@LicenseRef-free="
LICENSE_URL);
+ }
+ }
+
+ if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS) != 0) {
+ get_changelog (plugin, app);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+is_allowed_section (PackageInfo *info)
+{
+ const gchar *section_blacklist[] = { "libs", NULL };
+
+ /* There's no valid apps in the libs section */
+ return info->section == NULL || !g_strv_contains (section_blacklist, info->section);
+}
+
+static gchar *
+get_origin (PackageInfo *info)
+{
+ if (!info->origin)
+ return NULL;
+
+ g_autofree gchar *origin_lower = g_strdup (info->origin);
+ gchar *out;
+ for (int i = 0; origin_lower[i]; ++i)
+ origin_lower[i] = g_ascii_tolower (origin_lower[i]);
+
+ out = g_strdup_printf ("%s-%s-%s", origin_lower, info->release, info->component);
+
+ return out;
+}
+
+gboolean
+gs_plugin_add_installed (GsPlugin *plugin,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ GList *link;
+
+ /* Load database once */
+ if (g_once_init_enter (&priv->loaded)) {
+ gboolean ret;
+
+ ret = load_apt_db (plugin, error);
+ g_once_init_leave (&priv->loaded, TRUE);
+ if (!ret)
+ return FALSE;
+ }
+
+ for (link = priv->installed_packages; link; link = link->next) {
+ PackageInfo *info = (PackageInfo *) link->data;
+ g_autofree gchar *origin = get_origin (info);
+ g_autoptr(GsApp) app = NULL;
+
+ if (!is_allowed_section (info))
+ continue;
+
+ app = gs_app_new (NULL);
+ // 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_set_name (app, GS_APP_QUALITY_LOWEST, info->name);
+ gs_app_add_source (app, info->name);
+ gs_app_set_origin (app, origin);
+ gs_app_set_origin_ui (app, info->origin);
+ gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+ gs_app_list_add (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 = (TransactionData *) user_data;
+ const gchar *name;
+ g_autoptr(GVariant) value = NULL;
+
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sv)"))) {
+ g_variant_get (parameters, "(&sv)", &name, &value);
+ if (data->app && strcmp (name, "Progress") == 0)
+ gs_app_set_progress (data->app, g_variant_get_int32 (value));
+ } else {
+ g_warning ("Unknown parameters in %s.%s: %s", interface_name, signal_name,
g_variant_get_type_string (parameters));
+ }
+}
+
+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 = (TransactionData *) user_data;
+
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(s)")))
+ g_variant_get (parameters, "(s)", data->result);
+ else
+ g_warning ("Unknown parameters in %s.%s: %s", interface_name, signal_name,
g_variant_get_type_string (parameters));
+
+ 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 (g_strcmp0 (method, "InstallFile") == 0)
+ parameters = g_variant_new ("(sb)", gs_app_get_origin (app), TRUE);
+ else if (app != NULL) {
+ 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)
+ g_main_loop_run (loop);
+ g_dbus_connection_signal_unsubscribe (conn, property_signal);
+ g_dbus_connection_signal_unsubscribe (conn, finished_signal);
+ if (result == NULL)
+ return FALSE;
+
+ if (g_strcmp0 (transaction_result, "exit-success") != 0) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "apt transaction returned result %s", transaction_result);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+app_is_ours (GsApp *app)
+{
+ const gchar *management_plugin = gs_app_get_management_plugin (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 (management_plugin, "PackageKit") == 0 ||
+ g_strcmp0 (management_plugin, "dpkg") == 0;
+}
+
+gboolean
+gs_plugin_app_install (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = FALSE;
+
+ 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 (g_strcmp0 (gs_app_get_management_plugin (app), "PackageKit") == 0)
+ success = aptd_transaction (plugin, "InstallPackages", app, error);
+ else if (g_strcmp0 (gs_app_get_management_plugin (app), "dpkg") == 0)
+ success = aptd_transaction (plugin, "InstallFile", app, error);
+
+ if (success)
+ gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+ else
+ gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+
+ return success;
+}
+
+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)
+{
+ return aptd_transaction (plugin, "UpdateCache", NULL, error);
+}
+
+gboolean
+gs_plugin_add_updates (GsPlugin *plugin,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ GList *link;
+
+ /* Load database once */
+ if (g_once_init_enter (&priv->loaded)) {
+ gboolean ret;
+
+ ret = load_apt_db (plugin, error);
+ g_once_init_leave (&priv->loaded, TRUE);
+ if (!ret)
+ return FALSE;
+ }
+
+ for (link = priv->updatable_packages; link; link = link->next) {
+ PackageInfo *info = (PackageInfo *) link->data;
+ g_autoptr(GsApp) app = NULL;
+
+ if (!is_allowed_section (info))
+ continue;
+
+ app = gs_app_new (NULL);
+ // 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_set_name (app, GS_APP_QUALITY_LOWEST, info->name);
+ gs_app_set_kind (app, AS_APP_KIND_GENERIC);
+ gs_app_add_source (app, info->name);
+ gs_app_list_add (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);
+}
+
+static gboolean
+content_type_matches (const gchar *filename,
+ gboolean *matches,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *content_type;
+ g_autoptr(GFile) file = NULL;
+ g_autoptr(GFileInfo) info = NULL;
+ const gchar *supported_types[] = {
+ "application/vnd.debian.binary-package",
+ 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;
+
+ content_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+ *matches = g_strv_contains (supported_types, content_type);
+
+ return TRUE;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]