[gnome-software] packagekit: Support apt:// URLs



commit 8c739ffd075f06225a0631c5e84545a08af1d77e
Author: Iain Lane <iain orangesquash org uk>
Date:   Mon Oct 23 16:57:26 2017 +0100

    packagekit: Support apt:// URLs
    
    When we receive an apt:// URL and we're runing a Debian-based distro,
    try to resolve it to a package using PK.

 lib/gs-os-release.c                                |   25 ++
 lib/gs-os-release.h                                |   19 +-
 plugins/packagekit/gs-plugin-packagekit-local.c    |   18 +-
 plugins/packagekit/gs-plugin-packagekit-refine.c   |  242 +-------------------
 plugins/packagekit/gs-plugin-packagekit-refresh.c  |   37 +---
 plugins/packagekit/gs-plugin-packagekit-upgrade.c  |   15 +-
 .../packagekit/gs-plugin-packagekit-url-to-app.c   |  122 ++++++++++
 plugins/packagekit/gs-plugin-packagekit.c          |   79 +------
 plugins/packagekit/meson.build                     |   16 ++
 plugins/packagekit/packagekit-common.c             |  218 ++++++++++++++++++
 plugins/packagekit/packagekit-common.h             |   37 +++-
 11 files changed, 446 insertions(+), 382 deletions(-)
---
diff --git a/lib/gs-os-release.c b/lib/gs-os-release.c
index 2c3b1cb..2aa598e 100644
--- a/lib/gs-os-release.c
+++ b/lib/gs-os-release.c
@@ -43,6 +43,7 @@ struct _GsOsRelease
        gchar                   *name;
        gchar                   *version;
        gchar                   *id;
+       gchar                   **id_like;
        gchar                   *version_id;
        gchar                   *pretty_name;
        gchar                   *cpe_name;
@@ -111,6 +112,10 @@ gs_os_release_initable_init (GInitable *initable,
                        os_release->id = g_strdup (tmp);
                        continue;
                }
+               if (g_strcmp0 (lines[i], "ID_LIKE") == 0) {
+                       os_release->id_like = g_strsplit (tmp, " ", 0);
+                       continue;
+               }
                if (g_strcmp0 (lines[i], "VERSION_ID") == 0) {
                        os_release->version_id = g_strdup (tmp);
                        continue;
@@ -187,6 +192,25 @@ gs_os_release_get_id (GsOsRelease *os_release)
 }
 
 /**
+ * gs_os_release_get_id_like:
+ * @os_release: A #GsOsRelease
+ *
+ * Gets the ID_LIKE from the os-release parser. This is a list of operating
+ * systems that are "closely related" to the local operating system, possibly
+ * by being a derivative distribution.
+ *
+ * Returns: a %NULL terminated list
+ *
+ * Since: 3.26.2
+ **/
+const gchar * const *
+gs_os_release_get_id_like (GsOsRelease *os_release)
+{
+       g_return_val_if_fail (GS_IS_OS_RELEASE (os_release), NULL);
+       return (const gchar * const *) os_release->id_like;
+}
+
+/**
  * gs_os_release_get_version_id:
  * @os_release: A #GsOsRelease
  *
@@ -278,6 +302,7 @@ gs_os_release_finalize (GObject *object)
        g_free (os_release->name);
        g_free (os_release->version);
        g_free (os_release->id);
+       g_strfreev (os_release->id_like);
        g_free (os_release->version_id);
        g_free (os_release->pretty_name);
        g_free (os_release->cpe_name);
diff --git a/lib/gs-os-release.h b/lib/gs-os-release.h
index 8da104a..ec68259 100644
--- a/lib/gs-os-release.h
+++ b/lib/gs-os-release.h
@@ -33,15 +33,16 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GsOsRelease, gs_os_release, GS, OS_RELEASE, GObject)
 
-GsOsRelease    *gs_os_release_new                      (GError         **error);
-const gchar    *gs_os_release_get_name                 (GsOsRelease    *os_release);
-const gchar    *gs_os_release_get_version              (GsOsRelease    *os_release);
-const gchar    *gs_os_release_get_id                   (GsOsRelease    *os_release);
-const gchar    *gs_os_release_get_version_id           (GsOsRelease    *os_release);
-const gchar    *gs_os_release_get_pretty_name          (GsOsRelease    *os_release);
-const gchar    *gs_os_release_get_cpe_name             (GsOsRelease    *os_release);
-const gchar    *gs_os_release_get_distro_codename      (GsOsRelease    *os_release);
-const gchar    *gs_os_release_get_home_url             (GsOsRelease    *os_release);
+GsOsRelease            *gs_os_release_new                      (GError         **error);
+const gchar            *gs_os_release_get_name                 (GsOsRelease    *os_release);
+const gchar            *gs_os_release_get_version              (GsOsRelease    *os_release);
+const gchar            *gs_os_release_get_id                   (GsOsRelease    *os_release);
+const gchar *    const *gs_os_release_get_id_like              (GsOsRelease    *os_release);
+const gchar            *gs_os_release_get_version_id           (GsOsRelease    *os_release);
+const gchar            *gs_os_release_get_pretty_name          (GsOsRelease    *os_release);
+const gchar            *gs_os_release_get_cpe_name             (GsOsRelease    *os_release);
+const gchar            *gs_os_release_get_distro_codename      (GsOsRelease    *os_release);
+const gchar            *gs_os_release_get_home_url             (GsOsRelease    *os_release);
 
 G_END_DECLS
 
diff --git a/plugins/packagekit/gs-plugin-packagekit-local.c b/plugins/packagekit/gs-plugin-packagekit-local.c
index 0840b12..f93d1dd 100644
--- a/plugins/packagekit/gs-plugin-packagekit-local.c
+++ b/plugins/packagekit/gs-plugin-packagekit-local.c
@@ -47,15 +47,10 @@ gs_plugin_destroy (GsPlugin *plugin)
        g_object_unref (priv->task);
 }
 
-typedef struct {
-       GsApp           *app;
-       GsPlugin        *plugin;
-} ProgressData;
-
 static void
-gs_plugin_packagekit_progress_cb (PkProgress *progress,
-                                 PkProgressType type,
-                                 gpointer user_data)
+gs_plugin_packagekit_local_progress_cb (PkProgress *progress,
+                                       PkProgressType type,
+                                       gpointer user_data)
 {
        ProgressData *data = (ProgressData *) user_data;
        GsPlugin *plugin = data->plugin;
@@ -83,7 +78,7 @@ gs_plugin_packagekit_refresh_guess_app_id (GsPlugin *plugin,
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        PkFiles *item;
-       ProgressData data;
+       ProgressData data = { 0 };
        guint i;
        guint j;
        gchar **fns;
@@ -99,7 +94,7 @@ gs_plugin_packagekit_refresh_guess_app_id (GsPlugin *plugin,
        results = pk_client_get_files_local (PK_CLIENT (priv->task),
                                             files,
                                             cancellable,
-                                            gs_plugin_packagekit_progress_cb, &data,
+                                            gs_plugin_packagekit_local_progress_cb, &data,
                                             error);
        if (!gs_plugin_packagekit_results_valid (results, error)) {
                gs_utils_error_add_unique_id (error, app);
@@ -145,7 +140,7 @@ gs_plugin_file_to_app (GsPlugin *plugin,
        GsPluginData *priv = gs_plugin_get_data (plugin);
        const gchar *package_id;
        PkDetails *item;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr (PkResults) results = NULL;
        g_autofree gchar *basename = NULL;
        g_autofree gchar *content_type = NULL;
@@ -170,7 +165,6 @@ gs_plugin_file_to_app (GsPlugin *plugin,
        if (!g_strv_contains (mimetypes, content_type))
                return TRUE;
 
-       data.app = NULL;
        data.plugin = plugin;
 
        /* get details */
diff --git a/plugins/packagekit/gs-plugin-packagekit-refine.c 
b/plugins/packagekit/gs-plugin-packagekit-refine.c
index 486f4b0..0c6f41b 100644
--- a/plugins/packagekit/gs-plugin-packagekit-refine.c
+++ b/plugins/packagekit/gs-plugin-packagekit-refine.c
@@ -88,145 +88,6 @@ gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app)
        }
 }
 
-typedef struct {
-       GsApp           *app;
-       GsPlugin        *plugin;
-       AsProfileTask   *ptask;
-       gchar           *profile_id;
-} ProgressData;
-
-static void
-gs_plugin_packagekit_progress_cb (PkProgress *progress,
-                                 PkProgressType type,
-                                 gpointer user_data)
-{
-       ProgressData *data = (ProgressData *) user_data;
-       GsPlugin *plugin = data->plugin;
-       GsPluginStatus plugin_status;
-       PkStatusEnum status;
-
-       if (type != PK_PROGRESS_TYPE_STATUS)
-               return;
-       g_object_get (progress,
-                     "status", &status,
-                     NULL);
-
-       /* profile */
-       if (status == PK_STATUS_ENUM_SETUP) {
-               data->ptask = as_profile_start (gs_plugin_get_profile (plugin),
-                                               "packagekit-refine::transaction[%s]",
-                                               data->profile_id);
-               /* this isn't awesome, but saves us handling it in the caller */
-               g_free (data->profile_id);
-               data->profile_id = NULL;
-       } else if (status == PK_STATUS_ENUM_FINISHED) {
-               g_clear_pointer (&data->ptask, as_profile_task_free);
-       }
-
-       plugin_status = packagekit_status_enum_to_plugin_status (status);
-       if (plugin_status != GS_PLUGIN_STATUS_UNKNOWN)
-               gs_plugin_status_update (plugin, data->app, plugin_status);
-}
-
-static void
-gs_plugin_packagekit_set_metadata_from_package (GsPlugin *plugin,
-                                                GsApp *app,
-                                                PkPackage *package)
-{
-       const gchar *data;
-
-       gs_app_set_management_plugin (app, "packagekit");
-       gs_app_add_source (app, pk_package_get_name (package));
-       gs_app_add_source_id (app, pk_package_get_id (package));
-
-       /* set origin */
-       if (gs_app_get_origin (app) == NULL) {
-               data = pk_package_get_data (package);
-               if (g_str_has_prefix (data, "installed:"))
-                       data += 10;
-               gs_app_set_origin (app, data);
-       }
-
-       /* set unavailable state */
-       if (pk_package_get_info (package) == PK_INFO_ENUM_UNAVAILABLE) {
-               gs_app_set_state (app, AS_APP_STATE_UNAVAILABLE);
-               gs_app_set_size_installed (app, GS_APP_SIZE_UNKNOWABLE);
-               gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE);
-       }
-       if (gs_app_get_version (app) == NULL)
-               gs_app_set_version (app,
-                       pk_package_get_version (package));
-       gs_app_set_name (app,
-                        GS_APP_QUALITY_LOWEST,
-                        pk_package_get_name (package));
-       gs_app_set_summary (app,
-                           GS_APP_QUALITY_LOWEST,
-                           pk_package_get_summary (package));
-}
-
-static void
-gs_plugin_packagekit_resolve_packages_app (GsPlugin *plugin,
-                                          GPtrArray *packages,
-                                          GsApp *app)
-{
-       GPtrArray *sources;
-       PkPackage *package;
-       const gchar *pkgname;
-       guint i, j;
-       guint number_available = 0;
-       guint number_installed = 0;
-
-       /* find any packages that match the package name */
-       number_installed = 0;
-       number_available = 0;
-       sources = gs_app_get_sources (app);
-       for (j = 0; j < sources->len; j++) {
-               pkgname = g_ptr_array_index (sources, j);
-               for (i = 0; i < packages->len; i++) {
-                       package = g_ptr_array_index (packages, i);
-                       if (g_strcmp0 (pk_package_get_name (package), pkgname) == 0) {
-                               gs_plugin_packagekit_set_metadata_from_package (plugin, app, package);
-                               switch (pk_package_get_info (package)) {
-                               case PK_INFO_ENUM_INSTALLED:
-                                       number_installed++;
-                                       break;
-                               case PK_INFO_ENUM_AVAILABLE:
-                                       number_available++;
-                                       break;
-                               case PK_INFO_ENUM_UNAVAILABLE:
-                                       number_available++;
-                                       break;
-                               default:
-                                       /* should we expect anything else? */
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       /* if *all* the source packages for the app are installed then the
-        * application is considered completely installed */
-       if (number_installed == sources->len && number_available == 0) {
-               if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
-                       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
-       } else if (number_installed + number_available == sources->len) {
-               /* if all the source packages are installed and all the rest
-                * of the packages are available then the app is available */
-               if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
-                       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
-       } else if (number_installed + number_available > sources->len) {
-               /* we have more packages returned than source packages */
-               gs_app_set_state (app, AS_APP_STATE_UNKNOWN);
-               gs_app_set_state (app, AS_APP_STATE_UPDATABLE);
-       } else if (number_installed + number_available < sources->len) {
-               g_autofree gchar *tmp = NULL;
-               /* we have less packages returned than source packages */
-               tmp = gs_app_to_string (app);
-               g_debug ("Failed to find all packages for:\n%s", tmp);
-               gs_app_set_state (app, AS_APP_STATE_UNAVAILABLE);
-       }
-}
-
 static gboolean
 gs_plugin_packagekit_resolve_packages (GsPlugin *plugin,
                                       GsAppList *list,
@@ -239,7 +100,7 @@ gs_plugin_packagekit_resolve_packages (GsPlugin *plugin,
        const gchar *pkgname;
        guint i;
        guint j;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
        g_autoptr(GPtrArray) package_ids = NULL;
        g_autoptr(GPtrArray) packages = NULL;
@@ -263,10 +124,7 @@ gs_plugin_packagekit_resolve_packages (GsPlugin *plugin,
                return TRUE;
        g_ptr_array_add (package_ids, NULL);
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
-       data.profile_id = NULL;
 
        /* resolve them all at once */
        results = pk_client_resolve (priv->client,
@@ -308,13 +166,12 @@ gs_plugin_packagekit_refine_from_desktop (GsPlugin *plugin,
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        const gchar *to_array[] = { NULL, NULL };
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
        g_autoptr(GPtrArray) packages = NULL;
 
        data.app = app;
        data.plugin = plugin;
-       data.ptask = NULL;
        data.profile_id = g_path_get_basename (filename);
 
        to_array[0] = filename;
@@ -381,7 +238,7 @@ gs_plugin_packagekit_refine_updatedetails (GsPlugin *plugin,
        GsApp *app;
        guint cnt = 0;
        PkUpdateDetail *update_detail;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autofree const gchar **package_ids = NULL;
        g_autoptr(PkResults) results = NULL;
        g_autoptr(GPtrArray) array = NULL;
@@ -398,10 +255,7 @@ gs_plugin_packagekit_refine_updatedetails (GsPlugin *plugin,
        if (cnt == 0)
                return TRUE;
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
-       data.profile_id = NULL;
 
        /* get any update details */
        results = pk_client_get_update_detail (priv->client,
@@ -437,83 +291,6 @@ gs_plugin_packagekit_refine_updatedetails (GsPlugin *plugin,
        return TRUE;
 }
 
-/*
- * gs_pk_compare_ids:
- *
- * Do not compare the repo. Some backends do not append the origin.
- */
-static gboolean
-gs_pk_compare_ids (const gchar *package_id1, const gchar *package_id2)
-{
-       gboolean ret;
-       g_auto(GStrv) split1 = NULL;
-       g_auto(GStrv) split2 = NULL;
-
-       split1 = pk_package_id_split (package_id1);
-       split2 = pk_package_id_split (package_id2);
-       ret = (g_strcmp0 (split1[PK_PACKAGE_ID_NAME],
-                         split2[PK_PACKAGE_ID_NAME]) == 0 &&
-              g_strcmp0 (split1[PK_PACKAGE_ID_VERSION],
-                         split2[PK_PACKAGE_ID_VERSION]) == 0 &&
-              g_strcmp0 (split1[PK_PACKAGE_ID_ARCH],
-                         split2[PK_PACKAGE_ID_ARCH]) == 0);
-       return ret;
-}
-
-static void
-gs_plugin_packagekit_refine_details_app (GsPlugin *plugin,
-                                        GPtrArray *array,
-                                        GsApp *app)
-{
-       GPtrArray *source_ids;
-       PkDetails *details;
-       const gchar *package_id;
-       guint i;
-       guint j;
-       guint64 size = 0;
-
-       source_ids = gs_app_get_source_ids (app);
-       for (j = 0; j < source_ids->len; j++) {
-               package_id = g_ptr_array_index (source_ids, j);
-               for (i = 0; i < array->len; i++) {
-                       g_autofree gchar *desc = NULL;
-                       /* right package? */
-                       details = g_ptr_array_index (array, i);
-                       if (!gs_pk_compare_ids (package_id,
-                                               pk_details_get_package_id (details))) {
-                               continue;
-                       }
-                       if (gs_app_get_license (app) == NULL) {
-                               g_autofree gchar *license_spdx = NULL;
-                               license_spdx = as_utils_license_to_spdx (pk_details_get_license (details));
-                               if (license_spdx != NULL) {
-                                       gs_app_set_license (app,
-                                                           GS_APP_QUALITY_LOWEST,
-                                                           license_spdx);
-                               }
-                       }
-                       if (gs_app_get_url (app, AS_URL_KIND_HOMEPAGE) == NULL) {
-                               gs_app_set_url (app,
-                                               AS_URL_KIND_HOMEPAGE,
-                                               pk_details_get_url (details));
-                       }
-                       size += pk_details_get_size (details);
-                       break;
-               }
-       }
-
-       /* the size is the size of all sources */
-       if (gs_app_is_installed (app)) {
-               gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE);
-               if (size > 0 && gs_app_get_size_installed (app) == 0)
-                       gs_app_set_size_installed (app, size);
-       } else {
-               gs_app_set_size_installed (app, GS_APP_SIZE_UNKNOWABLE);
-               if (size > 0 && gs_app_get_size_download (app) == 0)
-                       gs_app_set_size_download (app, size);
-       }
-}
-
 static gboolean
 gs_plugin_packagekit_refine_details2 (GsPlugin *plugin,
                                      GsAppList *list,
@@ -525,7 +302,7 @@ gs_plugin_packagekit_refine_details2 (GsPlugin *plugin,
        GsApp *app;
        const gchar *package_id;
        guint i, j;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(GPtrArray) array = NULL;
        g_autoptr(GPtrArray) package_ids = NULL;
        g_autoptr(PkResults) results = NULL;
@@ -541,9 +318,7 @@ gs_plugin_packagekit_refine_details2 (GsPlugin *plugin,
        }
        g_ptr_array_add (package_ids, NULL);
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
        data.profile_id = g_strjoinv (",", (gchar **) package_ids->pdata);
 
        /* get any details */
@@ -579,7 +354,7 @@ gs_plugin_packagekit_refine_update_urgency (GsPlugin *plugin,
        GsApp *app;
        const gchar *package_id;
        PkBitfield filter;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(AsProfileTask) ptask = NULL;
        g_autoptr(PkPackageSack) sack = NULL;
        g_autoptr(PkResults) results = NULL;
@@ -591,10 +366,7 @@ gs_plugin_packagekit_refine_update_urgency (GsPlugin *plugin,
        if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_SEVERITY) == 0)
                return TRUE;
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
-       data.profile_id = NULL;
 
        /* get the list of updates */
        filter = pk_bitfield_value (PK_FILTER_ENUM_NONE);
@@ -774,15 +546,13 @@ gs_plugin_packagekit_refine_distro_upgrade (GsPlugin *plugin,
        GsPluginData *priv = gs_plugin_get_data (plugin);
        guint i;
        GsApp *app2;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
        g_autoptr(GsAppList) list = NULL;
        guint cache_age_save;
 
        data.app = app;
        data.plugin = plugin;
-       data.ptask = NULL;
-       data.profile_id = NULL;
 
        /* ask PK to simulate upgrading the system */
        cache_age_save = pk_client_get_cache_age (priv->client);
diff --git a/plugins/packagekit/gs-plugin-packagekit-refresh.c 
b/plugins/packagekit/gs-plugin-packagekit-refresh.c
index 729102d..ec4b261 100644
--- a/plugins/packagekit/gs-plugin-packagekit-refresh.c
+++ b/plugins/packagekit/gs-plugin-packagekit-refresh.c
@@ -55,40 +55,6 @@ gs_plugin_destroy (GsPlugin *plugin)
        g_object_unref (priv->task);
 }
 
-typedef struct {
-       GsPlugin        *plugin;
-       AsProfileTask   *ptask;
-} ProgressData;
-
-static void
-gs_plugin_packagekit_progress_cb (PkProgress *progress,
-                                 PkProgressType type,
-                                 gpointer user_data)
-{
-       ProgressData *data = (ProgressData *) user_data;
-       GsPlugin *plugin = data->plugin;
-       GsPluginStatus plugin_status;
-       PkStatusEnum status;
-
-       if (type != PK_PROGRESS_TYPE_STATUS)
-               return;
-       g_object_get (progress,
-                     "status", &status,
-                     NULL);
-
-       /* profile */
-       if (status == PK_STATUS_ENUM_SETUP) {
-               data->ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
-                                                       "packagekit-refresh::transaction");
-       } else if (status == PK_STATUS_ENUM_FINISHED) {
-               g_clear_pointer (&data->ptask, as_profile_task_free);
-       }
-
-       plugin_status = packagekit_status_enum_to_plugin_status (status);
-       if (plugin_status != GS_PLUGIN_STATUS_UNKNOWN)
-               gs_plugin_status_update (plugin, NULL, plugin_status);
-}
-
 gboolean
 gs_plugin_refresh (GsPlugin *plugin,
                   guint cache_age,
@@ -97,7 +63,7 @@ gs_plugin_refresh (GsPlugin *plugin,
                   GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
 
        /* nothing to re-generate */
@@ -108,7 +74,6 @@ gs_plugin_refresh (GsPlugin *plugin,
        pk_client_set_background (PK_CLIENT (priv->task), cache_age > 0);
 
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* refresh the metadata */
        if (flags & GS_PLUGIN_REFRESH_FLAGS_METADATA ||
diff --git a/plugins/packagekit/gs-plugin-packagekit-upgrade.c 
b/plugins/packagekit/gs-plugin-packagekit-upgrade.c
index e119389..20c8209 100644
--- a/plugins/packagekit/gs-plugin-packagekit-upgrade.c
+++ b/plugins/packagekit/gs-plugin-packagekit-upgrade.c
@@ -48,15 +48,10 @@ gs_plugin_destroy (GsPlugin *plugin)
        g_object_unref (priv->task);
 }
 
-typedef struct {
-       GsApp           *app;
-       GsPlugin        *plugin;
-} ProgressData;
-
 static void
-gs_plugin_packagekit_progress_cb (PkProgress *progress,
-                                 PkProgressType type,
-                                 gpointer user_data)
+gs_plugin_packagekit_upgrade_progress_cb (PkProgress *progress,
+                                         PkProgressType type,
+                                         gpointer user_data)
 {
        ProgressData *data = (ProgressData *) user_data;
        GsPlugin *plugin = data->plugin;
@@ -80,7 +75,7 @@ gs_plugin_app_upgrade_download (GsPlugin *plugin,
                                GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
 
        /* only process this app if was created by this plugin */
@@ -100,7 +95,7 @@ gs_plugin_app_upgrade_download (GsPlugin *plugin,
                                               gs_app_get_version (app),
                                               PK_UPGRADE_KIND_ENUM_COMPLETE,
                                               cancellable,
-                                              gs_plugin_packagekit_progress_cb, &data,
+                                              gs_plugin_packagekit_upgrade_progress_cb, &data,
                                               error);
        if (!gs_plugin_packagekit_results_valid (results, error)) {
                gs_app_set_state_recover (app);
diff --git a/plugins/packagekit/gs-plugin-packagekit-url-to-app.c 
b/plugins/packagekit/gs-plugin-packagekit-url-to-app.c
new file mode 100644
index 0000000..067b08e
--- /dev/null
+++ b/plugins/packagekit/gs-plugin-packagekit-url-to-app.c
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 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 <packagekit-glib2/packagekit.h>
+#include <gnome-software.h>
+
+#include "packagekit-common.h"
+
+struct GsPluginData {
+       PkClient                *client;
+};
+
+void
+gs_plugin_initialize (GsPlugin *plugin)
+{
+       GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData));
+       priv->client = pk_client_new ();
+
+       pk_client_set_background (priv->client, FALSE);
+       pk_client_set_cache_age (priv->client, G_MAXUINT);
+}
+
+gboolean
+gs_plugin_url_to_app (GsPlugin *plugin,
+                     GsAppList *list,
+                     const gchar *url,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+
+       g_autofree gchar *scheme = NULL;
+       g_autofree gchar *path = NULL;
+       const gchar *id = NULL;
+       const gchar * const *id_like = NULL;
+       g_auto(GStrv) package_ids = NULL;
+       g_autoptr(PkResults) results = NULL;
+       g_autoptr(GsApp) app = NULL;
+       g_autoptr(GsOsRelease) os_release = NULL;
+       g_autoptr(GPtrArray) packages = NULL;
+       g_autoptr(GPtrArray) details = NULL;
+       ProgressData data = { 0 };
+
+       path = gs_utils_get_url_path (url);
+
+       data.app = app;
+       data.plugin = plugin;
+       data.profile_id = path;
+
+       /* only do this for apt:// on debian or debian-like distros */
+       os_release = gs_os_release_new (error);
+       if (os_release == NULL) {
+               g_prefix_error (error, "failed to determine OS information:");
+                return FALSE;
+       } else  {
+               id = gs_os_release_get_id (os_release);
+               id_like = gs_os_release_get_id_like (os_release);
+               scheme = gs_utils_get_url_scheme (url);
+               if (!(g_strcmp0 (scheme, "apt") == 0 &&
+                    (g_strcmp0 (id, "debian") == 0 ||
+                     g_strv_contains (id_like, "debian")))) {
+                       return TRUE;
+               }
+       }
+
+       app = gs_app_new (NULL);
+       gs_app_add_source (app, path);
+       gs_app_set_kind (app, AS_APP_KIND_GENERIC);
+
+       package_ids = g_new0 (gchar *, 2);
+       package_ids[0] = g_strdup (path);
+
+       results = pk_client_resolve (priv->client,
+                                    pk_bitfield_from_enums (PK_FILTER_ENUM_NEWEST, PK_FILTER_ENUM_ARCH, -1),
+                                    package_ids,
+                                    cancellable,
+                                    gs_plugin_packagekit_progress_cb, &data,
+                                    error);
+
+       if (!gs_plugin_packagekit_results_valid (results, error)) {
+               g_prefix_error (error, "failed to resolve package_ids: ");
+               return FALSE;
+       }
+
+       /* get results */
+       packages = pk_results_get_package_array (results);
+       details = pk_results_get_details_array (results);
+
+       if (packages->len >= 1) {
+               if (gs_app_get_local_file (app) != NULL)
+                       return TRUE;
+
+               gs_plugin_packagekit_resolve_packages_app (plugin, packages, app);
+               gs_plugin_packagekit_refine_details_app (plugin, details, app);
+
+               gs_app_list_add (list, app);
+       } else {
+               g_warning ("no results returned");
+       }
+
+       return TRUE;
+}
diff --git a/plugins/packagekit/gs-plugin-packagekit.c b/plugins/packagekit/gs-plugin-packagekit.c
index 7978ba1..26ec0da 100644
--- a/plugins/packagekit/gs-plugin-packagekit.c
+++ b/plugins/packagekit/gs-plugin-packagekit.c
@@ -56,53 +56,6 @@ gs_plugin_destroy (GsPlugin *plugin)
        g_object_unref (priv->task);
 }
 
-typedef struct {
-       GsApp           *app;
-       GsPlugin        *plugin;
-       AsProfileTask   *ptask;
-} ProgressData;
-
-static void
-gs_plugin_packagekit_progress_cb (PkProgress *progress,
-                                 PkProgressType type,
-                                 gpointer user_data)
-{
-       ProgressData *data = (ProgressData *) user_data;
-       GsPlugin *plugin = data->plugin;
-
-       if (type == PK_PROGRESS_TYPE_STATUS) {
-               GsPluginStatus plugin_status;
-               PkStatusEnum status;
-               g_object_get (progress,
-                             "status", &status,
-                             NULL);
-
-               /* profile */
-               if (status == PK_STATUS_ENUM_SETUP) {
-                       data->ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
-                                               "packagekit-refine::transaction");
-               } else if (status == PK_STATUS_ENUM_FINISHED) {
-                       g_clear_pointer (&data->ptask, as_profile_task_free);
-               }
-
-               plugin_status = packagekit_status_enum_to_plugin_status (status);
-               if (plugin_status != GS_PLUGIN_STATUS_UNKNOWN)
-                       gs_plugin_status_update (plugin, data->app, plugin_status);
-
-       } else if (type == PK_PROGRESS_TYPE_PERCENTAGE) {
-               gint percentage = pk_progress_get_percentage (progress);
-               if (data->app != NULL && percentage >= 0 && percentage <= 100)
-                       gs_app_set_progress (data->app, (guint) percentage);
-       }
-
-       /* Only go from TRUE to FALSE - it doesn't make sense for a package
-        * install to become uncancellable later on */
-       if (data->app != NULL && gs_app_get_allow_cancel (data->app)) {
-               gs_app_set_allow_cancel (data->app,
-                                        pk_progress_get_allow_cancel (progress));
-       }
-}
-
 static gboolean
 gs_plugin_add_sources_related (GsPlugin *plugin,
                               GHashTable *hash,
@@ -114,16 +67,14 @@ gs_plugin_add_sources_related (GsPlugin *plugin,
        GsApp *app;
        GsApp *app_tmp;
        PkBitfield filter;
-       ProgressData data;
+       ProgressData data = { 0 };
        const gchar *id;
        gboolean ret = TRUE;
        g_autoptr(GsAppList) installed = gs_app_list_new ();
        g_autoptr(PkResults) results = NULL;
        g_autoptr(AsProfileTask) ptask = NULL;
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
                                          "packagekit::add-sources-related");
@@ -174,16 +125,14 @@ gs_plugin_add_sources (GsPlugin *plugin,
        GsPluginData *priv = gs_plugin_get_data (plugin);
        PkBitfield filter;
        PkRepoDetail *rd;
-       ProgressData data;
+       ProgressData data = { 0 };
        const gchar *id;
        guint i;
        g_autoptr(GHashTable) hash = NULL;
        g_autoptr(PkResults) results = NULL;
        g_autoptr(GPtrArray) array = NULL;
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* ask PK for the repo details */
        filter = pk_bitfield_from_enums (PK_FILTER_ENUM_NOT_SOURCE,
@@ -232,12 +181,11 @@ gs_plugin_app_source_enable (GsPlugin *plugin,
                             GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
 
        data.app = app;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* do sync call */
        gs_plugin_status_update (plugin, app, GS_PLUGIN_STATUS_WAITING);
@@ -263,7 +211,7 @@ gs_plugin_app_install (GsPlugin *plugin,
        GsPluginData *priv = gs_plugin_get_data (plugin);
        GPtrArray *addons;
        GPtrArray *source_ids;
-       ProgressData data;
+       ProgressData data = { 0 };
        const gchar *package_id;
        guint i, j;
        g_autofree gchar *local_filename = NULL;
@@ -273,7 +221,6 @@ gs_plugin_app_install (GsPlugin *plugin,
 
        data.app = app;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* only process this app if was created by this plugin */
        if (g_strcmp0 (gs_app_get_management_plugin (app),
@@ -445,12 +392,11 @@ gs_plugin_app_source_disable (GsPlugin *plugin,
                              GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
 
        data.app = app;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* do sync call */
        gs_plugin_status_update (plugin, app, GS_PLUGIN_STATUS_WAITING);
@@ -474,13 +420,11 @@ gs_plugin_app_source_remove (GsPlugin *plugin,
                             GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(GError) error_local = NULL;
        g_autoptr(PkResults) results = NULL;
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* do sync call */
        gs_plugin_status_update (plugin, app, GS_PLUGIN_STATUS_WAITING);
@@ -510,7 +454,7 @@ gs_plugin_app_remove (GsPlugin *plugin,
        GsPluginData *priv = gs_plugin_get_data (plugin);
        const gchar *package_id;
        GPtrArray *source_ids;
-       ProgressData data;
+       ProgressData data = { 0 };
        guint i;
        guint cnt = 0;
        g_autoptr(PkResults) results = NULL;
@@ -518,7 +462,6 @@ gs_plugin_app_remove (GsPlugin *plugin,
 
        data.app = app;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* only process this app if was created by this plugin */
        if (g_strcmp0 (gs_app_get_management_plugin (app),
@@ -586,12 +529,10 @@ gs_plugin_add_search_files (GsPlugin *plugin,
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        PkBitfield filter;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* do sync call */
        gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_WAITING);
@@ -620,12 +561,10 @@ gs_plugin_add_search_what_provides (GsPlugin *plugin,
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
        PkBitfield filter;
-       ProgressData data;
+       ProgressData data = { 0 };
        g_autoptr(PkResults) results = NULL;
 
-       data.app = NULL;
        data.plugin = plugin;
-       data.ptask = NULL;
 
        /* do sync call */
        gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_WAITING);
diff --git a/plugins/packagekit/meson.build b/plugins/packagekit/meson.build
index ac049f2..b2cfc29 100644
--- a/plugins/packagekit/meson.build
+++ b/plugins/packagekit/meson.build
@@ -136,6 +136,22 @@ sources : 'gs-plugin-packagekit-proxy.c',
   c_args : cargs,
   dependencies : [ plugin_libs, packagekit ])
 
+shared_module(
+  'gs_plugin_packagekit-url-to-app',
+  sources : [
+    'gs-plugin-packagekit-url-to-app.c',
+    'packagekit-common.c',
+  ],
+  include_directories : [
+    include_directories('../..'),
+    include_directories('../../lib'),
+  ],
+  install : true,
+  install_dir: plugin_dir,
+  c_args : cargs,
+  dependencies : [ plugin_libs, packagekit ]
+)
+
 if get_option('enable-tests')
   cargs += ['-DTESTDATADIR="' + join_paths(meson.current_source_dir(), 'tests') + '"']
   e = executable(
diff --git a/plugins/packagekit/packagekit-common.c b/plugins/packagekit/packagekit-common.c
index 8295c98..4b53c79 100644
--- a/plugins/packagekit/packagekit-common.c
+++ b/plugins/packagekit/packagekit-common.c
@@ -291,3 +291,221 @@ gs_plugin_packagekit_add_results (GsPlugin *plugin,
        }
        return TRUE;
 }
+
+void
+gs_plugin_packagekit_resolve_packages_app (GsPlugin *plugin,
+                                          GPtrArray *packages,
+                                          GsApp *app)
+{
+       GPtrArray *sources;
+       PkPackage *package;
+       const gchar *pkgname;
+       guint i, j;
+       guint number_available = 0;
+       guint number_installed = 0;
+
+       /* find any packages that match the package name */
+       number_installed = 0;
+       number_available = 0;
+       sources = gs_app_get_sources (app);
+       for (j = 0; j < sources->len; j++) {
+               pkgname = g_ptr_array_index (sources, j);
+               for (i = 0; i < packages->len; i++) {
+                       package = g_ptr_array_index (packages, i);
+                       if (g_strcmp0 (pk_package_get_name (package), pkgname) == 0) {
+                               gs_plugin_packagekit_set_metadata_from_package (plugin, app, package);
+                               switch (pk_package_get_info (package)) {
+                               case PK_INFO_ENUM_INSTALLED:
+                                       number_installed++;
+                                       break;
+                               case PK_INFO_ENUM_AVAILABLE:
+                                       number_available++;
+                                       break;
+                               case PK_INFO_ENUM_UNAVAILABLE:
+                                       number_available++;
+                                       break;
+                               default:
+                                       /* should we expect anything else? */
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* if *all* the source packages for the app are installed then the
+        * application is considered completely installed */
+       if (number_installed == sources->len && number_available == 0) {
+               if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
+                       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+       } else if (number_installed + number_available == sources->len) {
+               /* if all the source packages are installed and all the rest
+                * of the packages are available then the app is available */
+               if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
+                       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+       } else if (number_installed + number_available > sources->len) {
+               /* we have more packages returned than source packages */
+               gs_app_set_state (app, AS_APP_STATE_UNKNOWN);
+               gs_app_set_state (app, AS_APP_STATE_UPDATABLE);
+       } else if (number_installed + number_available < sources->len) {
+               g_autofree gchar *tmp = NULL;
+               /* we have less packages returned than source packages */
+               tmp = gs_app_to_string (app);
+               g_debug ("Failed to find all packages for:\n%s", tmp);
+               gs_app_set_state (app, AS_APP_STATE_UNAVAILABLE);
+       }
+}
+
+void
+gs_plugin_packagekit_progress_cb (PkProgress *progress,
+                                 PkProgressType type,
+                                 gpointer user_data)
+{
+       ProgressData *data = (ProgressData *) user_data;
+       GsPlugin *plugin = data->plugin;
+
+       if (type == PK_PROGRESS_TYPE_STATUS) {
+               GsPluginStatus plugin_status;
+               PkStatusEnum status;
+               g_object_get (progress,
+                             "status", &status,
+                             NULL);
+
+               /* profile */
+               if (status == PK_STATUS_ENUM_SETUP) {
+                       data->ptask = as_profile_start_literal (gs_plugin_get_profile (plugin),
+                                               "packagekit-refine::transaction");
+               } else if (status == PK_STATUS_ENUM_FINISHED) {
+                       g_clear_pointer (&data->ptask, as_profile_task_free);
+               }
+
+               plugin_status = packagekit_status_enum_to_plugin_status (status);
+               if (plugin_status != GS_PLUGIN_STATUS_UNKNOWN)
+                       gs_plugin_status_update (plugin, data->app, plugin_status);
+
+       } else if (type == PK_PROGRESS_TYPE_PERCENTAGE) {
+               gint percentage = pk_progress_get_percentage (progress);
+               if (data->app != NULL && percentage >= 0 && percentage <= 100)
+                       gs_app_set_progress (data->app, (guint) percentage);
+       }
+
+       /* Only go from TRUE to FALSE - it doesn't make sense for a package
+        * install to become uncancellable later on */
+       if (data->app != NULL && gs_app_get_allow_cancel (data->app)) {
+               gs_app_set_allow_cancel (data->app,
+                                        pk_progress_get_allow_cancel (progress));
+       }
+}
+
+void
+gs_plugin_packagekit_set_metadata_from_package (GsPlugin *plugin,
+                                                GsApp *app,
+                                                PkPackage *package)
+{
+       const gchar *data;
+
+       gs_app_set_management_plugin (app, "packagekit");
+       gs_app_add_source (app, pk_package_get_name (package));
+       gs_app_add_source_id (app, pk_package_get_id (package));
+
+       /* set origin */
+       if (gs_app_get_origin (app) == NULL) {
+               data = pk_package_get_data (package);
+               if (g_str_has_prefix (data, "installed:"))
+                       data += 10;
+               gs_app_set_origin (app, data);
+       }
+
+       /* set unavailable state */
+       if (pk_package_get_info (package) == PK_INFO_ENUM_UNAVAILABLE) {
+               gs_app_set_state (app, AS_APP_STATE_UNAVAILABLE);
+               gs_app_set_size_installed (app, GS_APP_SIZE_UNKNOWABLE);
+               gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE);
+       }
+       if (gs_app_get_version (app) == NULL)
+               gs_app_set_version (app,
+                       pk_package_get_version (package));
+       gs_app_set_name (app,
+                        GS_APP_QUALITY_LOWEST,
+                        pk_package_get_name (package));
+       gs_app_set_summary (app,
+                           GS_APP_QUALITY_LOWEST,
+                           pk_package_get_summary (package));
+}
+
+/*
+ * gs_pk_compare_ids:
+ *
+ * Do not compare the repo. Some backends do not append the origin.
+ */
+static gboolean
+gs_pk_compare_ids (const gchar *package_id1, const gchar *package_id2)
+{
+       gboolean ret;
+       g_auto(GStrv) split1 = NULL;
+       g_auto(GStrv) split2 = NULL;
+
+       split1 = pk_package_id_split (package_id1);
+       split2 = pk_package_id_split (package_id2);
+       ret = (g_strcmp0 (split1[PK_PACKAGE_ID_NAME],
+                         split2[PK_PACKAGE_ID_NAME]) == 0 &&
+              g_strcmp0 (split1[PK_PACKAGE_ID_VERSION],
+                         split2[PK_PACKAGE_ID_VERSION]) == 0 &&
+              g_strcmp0 (split1[PK_PACKAGE_ID_ARCH],
+                         split2[PK_PACKAGE_ID_ARCH]) == 0);
+       return ret;
+}
+
+
+void
+gs_plugin_packagekit_refine_details_app (GsPlugin *plugin,
+                                        GPtrArray *array,
+                                        GsApp *app)
+{
+       GPtrArray *source_ids;
+       PkDetails *details;
+       const gchar *package_id;
+       guint i;
+       guint j;
+       guint64 size = 0;
+
+       source_ids = gs_app_get_source_ids (app);
+       for (j = 0; j < source_ids->len; j++) {
+               package_id = g_ptr_array_index (source_ids, j);
+               for (i = 0; i < array->len; i++) {
+                       g_autofree gchar *desc = NULL;
+                       /* right package? */
+                       details = g_ptr_array_index (array, i);
+                       if (!gs_pk_compare_ids (package_id,
+                                               pk_details_get_package_id (details))) {
+                               continue;
+                       }
+                       if (gs_app_get_license (app) == NULL) {
+                               g_autofree gchar *license_spdx = NULL;
+                               license_spdx = as_utils_license_to_spdx (pk_details_get_license (details));
+                               if (license_spdx != NULL) {
+                                       gs_app_set_license (app,
+                                                           GS_APP_QUALITY_LOWEST,
+                                                           license_spdx);
+                               }
+                       }
+                       if (gs_app_get_url (app, AS_URL_KIND_HOMEPAGE) == NULL) {
+                               gs_app_set_url (app,
+                                               AS_URL_KIND_HOMEPAGE,
+                                               pk_details_get_url (details));
+                       }
+                       size += pk_details_get_size (details);
+                       break;
+               }
+       }
+
+       /* the size is the size of all sources */
+       if (gs_app_is_installed (app)) {
+               gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE);
+               if (size > 0 && gs_app_get_size_installed (app) == 0)
+                       gs_app_set_size_installed (app, size);
+       } else {
+               gs_app_set_size_installed (app, GS_APP_SIZE_UNKNOWABLE);
+               if (size > 0 && gs_app_get_size_download (app) == 0)
+                       gs_app_set_size_download (app, size);
+       }
+}
diff --git a/plugins/packagekit/packagekit-common.h b/plugins/packagekit/packagekit-common.h
index 11e9714..68c71b2 100644
--- a/plugins/packagekit/packagekit-common.h
+++ b/plugins/packagekit/packagekit-common.h
@@ -29,15 +29,34 @@
 
 G_BEGIN_DECLS
 
-GsPluginStatus         packagekit_status_enum_to_plugin_status (PkStatusEnum    status);
-
-gboolean       gs_plugin_packagekit_add_results        (GsPlugin       *plugin,
-                                                        GsAppList      *list,
-                                                        PkResults      *results,
-                                                        GError         **error);
-gboolean       gs_plugin_packagekit_error_convert      (GError         **error);
-gboolean       gs_plugin_packagekit_results_valid      (PkResults      *results,
-                                                        GError         **error);
+typedef struct {
+       GsApp           *app;
+       GsPlugin        *plugin;
+       AsProfileTask   *ptask;
+       gchar           *profile_id;
+} ProgressData;
+
+GsPluginStatus         packagekit_status_enum_to_plugin_status         (PkStatusEnum    status);
+
+gboolean       gs_plugin_packagekit_add_results                (GsPlugin       *plugin,
+                                                                GsAppList      *list,
+                                                                PkResults      *results,
+                                                                GError         **error);
+gboolean       gs_plugin_packagekit_error_convert              (GError         **error);
+gboolean       gs_plugin_packagekit_results_valid              (PkResults      *results,
+                                                                GError         **error);
+void           gs_plugin_packagekit_progress_cb                (PkProgress     *progress,
+                                                                PkProgressType type,
+                                                                gpointer       user_data);
+void           gs_plugin_packagekit_resolve_packages_app       (GsPlugin *plugin,
+                                                                GPtrArray *packages,
+                                                                GsApp *app);
+void           gs_plugin_packagekit_set_metadata_from_package  (GsPlugin *plugin,
+                                                                GsApp *app,
+                                                                PkPackage *package);
+void           gs_plugin_packagekit_refine_details_app         (GsPlugin *plugin,
+                                                                GPtrArray *array,
+                                                                GsApp *app);
 
 G_END_DECLS
 



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