[gnome-software/wip/ubuntu-3-22] Support snap:// URL handling by backporting URL handling improvements from trunk
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/ubuntu-3-22] Support snap:// URL handling by backporting URL handling improvements from trunk
- Date: Mon, 13 Mar 2017 23:51:13 +0000 (UTC)
commit 3af36a47efc6d4bd6d1007ba4639d9b05460ae1b
Author: Richard Hughes <richard hughsie com>
Date: Thu Feb 2 10:11:54 2017 +0000
Support snap:// URL handling by backporting URL handling improvements from trunk
src/gs-application.c | 64 +++++--------
src/gs-cmd.c | 11 ++
src/gs-plugin-loader-sync.c | 50 ++++++++++
src/gs-plugin-loader-sync.h | 5 +
src/gs-plugin-loader.c | 190 +++++++++++++++++++++++++++++++++++++
src/gs-plugin-loader.h | 9 ++
src/gs-plugin-vfuncs.h | 24 +++++
src/gs-shell-details.c | 21 ++++
src/gs-shell-details.h | 2 +
src/gs-shell.c | 3 +
src/gs-utils.c | 61 ++++++++++++
src/gs-utils.h | 2 +
src/plugins/gs-plugin-appstream.c | 33 +++++++
src/plugins/gs-plugin-snap.c | 38 ++++++++
src/plugins/gs-snapd.c | 49 ++++++++++
src/plugins/gs-snapd.h | 6 +
16 files changed, 528 insertions(+), 40 deletions(-)
---
diff --git a/src/gs-application.c b/src/gs-application.c
index c079d38..cf426a5 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -629,6 +629,26 @@ details_pkg_activated (GSimpleAction *action,
}
static void
+details_url_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ GsApplication *app = GS_APPLICATION (data);
+ const gchar *url;
+ g_autoptr (GsApp) a = NULL;
+
+ initialize_ui_and_present_window (app, NULL);
+
+ g_variant_get (parameter, "(&s)", &url);
+
+ /* this is only used as a wrapper to transport the URL to
+ * the gs_shell_change_mode() function -- not in the GsAppList */
+ a = gs_app_new (NULL);
+ gs_app_set_metadata (a, "GnomeSoftware::from-url", url);
+ gs_shell_show_app (app->shell, a);
+}
+
+static void
filename_activated (GSimpleAction *action,
GVariant *parameter,
gpointer data)
@@ -729,6 +749,7 @@ static GActionEntry actions[] = {
{ "search", search_activated, "s", NULL, NULL },
{ "details", details_activated, "(ss)", NULL, NULL },
{ "details-pkg", details_pkg_activated, "s", NULL, NULL },
+ { "details-url", details_url_activated, "(s)", NULL, NULL },
{ "filename", filename_activated, "(s)", NULL, NULL },
{ "launch", launch_activated, "s", NULL, NULL },
{ "show-offline-update-error", show_offline_updates_error, NULL, NULL, NULL },
@@ -943,46 +964,9 @@ gs_application_open (GApplication *application,
for (i = 0; i < n_files; i++) {
g_autofree gchar *str = g_file_get_uri (files[i]);
- g_autoptr(SoupURI) uri = NULL;
-
- uri = soup_uri_new (str);
- if (!SOUP_URI_IS_VALID (uri))
- continue;
-
- if (g_strcmp0 (soup_uri_get_scheme (uri), "appstream") == 0) {
- const gchar *host = soup_uri_get_host (uri);
- const gchar *path = soup_uri_get_path (uri);
-
- /* appstream://foo -> scheme: appstream, host: foo, path: / */
- /* appstream:foo -> scheme: appstream, host: (empty string), path: /foo */
- if (host != NULL && (strlen (host) > 0))
- path = host;
-
- /* trim any leading slashes */
- while (*path == '/')
- path++;
-
- g_action_group_activate_action (G_ACTION_GROUP (app),
- "details",
- g_variant_new ("(ss)", path, ""));
- }
- if (g_strcmp0 (soup_uri_get_scheme (uri), "apt") == 0) {
- const gchar *host = soup_uri_get_host (uri);
- const gchar *path = soup_uri_get_path (uri);
-
- /* trim any leading slashes */
- while (*path == '/')
- path++;
-
- /* apt://foo -> scheme: apt, host: foo, path: / */
- /* apt:foo -> scheme: apt, host: (empty string), path: /foo */
- if (host != NULL && (strlen (host) > 0))
- path = host;
-
- g_action_group_activate_action (G_ACTION_GROUP (app),
- "details-pkg",
- g_variant_new_string (path));
- }
+ g_action_group_activate_action (G_ACTION_GROUP (app),
+ "details-url",
+ g_variant_new ("(s)", str));
}
}
diff --git a/src/gs-cmd.c b/src/gs-cmd.c
index a6a219d..20ed808 100644
--- a/src/gs-cmd.c
+++ b/src/gs-cmd.c
@@ -361,6 +361,17 @@ main (int argc, char **argv)
list = gs_app_list_new ();
gs_app_list_add (list, app);
}
+ } else if (argc == 3 && g_strcmp0 (argv[1], "url-to-app") == 0) {
+ app = gs_plugin_loader_url_to_app (plugin_loader,
+ argv[2],
+ refine_flags,
+ NULL,
+ &error);
+ if (app == NULL) {
+ ret = FALSE;
+ } else {
+ gs_app_list_add (list, app);
+ }
} else if (argc == 2 && g_strcmp0 (argv[1], "updates") == 0) {
for (i = 0; i < repeat; i++) {
if (list != NULL)
diff --git a/src/gs-plugin-loader-sync.c b/src/gs-plugin-loader-sync.c
index 5da526e..45bc1d4 100644
--- a/src/gs-plugin-loader-sync.c
+++ b/src/gs-plugin-loader-sync.c
@@ -703,4 +703,54 @@ gs_plugin_loader_file_to_app (GsPluginLoader *plugin_loader,
return helper.app;
}
+static void
+gs_plugin_loader_url_to_app_finish_sync (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+ GsPluginLoaderHelper *helper = (GsPluginLoaderHelper *) user_data;
+ helper->app = gs_plugin_loader_url_to_app_finish (plugin_loader,
+ res,
+ helper->error);
+ g_main_loop_quit (helper->loop);
+}
+
+/**
+ * gs_plugin_loader_url_to_app:
+ **/
+GsApp *
+gs_plugin_loader_url_to_app (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginLoaderHelper helper;
+
+ /* create temp object */
+ helper.app = NULL;
+ helper.context = g_main_context_new ();
+ helper.loop = g_main_loop_new (helper.context, FALSE);
+ helper.error = error;
+
+ g_main_context_push_thread_default (helper.context);
+
+ /* run async method */
+ gs_plugin_loader_url_to_app_async (plugin_loader,
+ url,
+ flags,
+ cancellable,
+ gs_plugin_loader_url_to_app_finish_sync,
+ &helper);
+ g_main_loop_run (helper.loop);
+
+ g_main_context_pop_thread_default (helper.context);
+
+ g_main_loop_unref (helper.loop);
+ g_main_context_unref (helper.context);
+
+ return helper.app;
+}
+
/* vim: set noexpandtab: */
diff --git a/src/gs-plugin-loader-sync.h b/src/gs-plugin-loader-sync.h
index 8fd2abf..79be4d4 100644
--- a/src/gs-plugin-loader-sync.h
+++ b/src/gs-plugin-loader-sync.h
@@ -102,6 +102,11 @@ GsApp *gs_plugin_loader_file_to_app (GsPluginLoader
*plugin_loader,
GsPluginRefineFlags flags,
GCancellable *cancellable,
GError **error);
+GsApp *gs_plugin_loader_url_to_app (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 367a80a..f04c627 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -135,6 +135,11 @@ typedef gboolean (*GsPluginFileToAppFunc) (GsPlugin *plugin,
GFile *file,
GCancellable *cancellable,
GError **error);
+typedef gboolean (*GsPluginUrlToAppFunc) (GsPlugin *plugin,
+ GsAppList *list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error);
typedef gboolean (*GsPluginUpdateFunc) (GsPlugin *plugin,
GsAppList *apps,
GCancellable *cancellable,
@@ -4534,6 +4539,191 @@ gs_plugin_loader_file_to_app_finish (GsPluginLoader *plugin_loader,
/******************************************************************************/
static void
+gs_plugin_loader_url_to_app_thread_cb (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ const gchar *function_name = "gs_plugin_url_to_app";
+ gboolean ret = TRUE;
+ GError *error = NULL;
+ guint i;
+ guint j;
+ GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
+ GsPlugin *plugin;
+ GsPluginUrlToAppFunc plugin_func = NULL;
+
+ /* run each plugin */
+ for (i = 0; i < priv->plugins->len; i++) {
+ g_autoptr(AsProfileTask) ptask = NULL;
+ g_autoptr(GError) error_local = NULL;
+ plugin = g_ptr_array_index (priv->plugins, i);
+ if (!gs_plugin_get_enabled (plugin))
+ continue;
+ if (g_task_return_error_if_cancelled (task))
+ return;
+ g_module_symbol (gs_plugin_get_module (plugin),
+ function_name,
+ (gpointer *) &plugin_func);
+ if (plugin_func == NULL)
+ continue;
+ ptask = as_profile_start (priv->profile,
+ "GsPlugin::%s(%s)",
+ gs_plugin_get_name (plugin),
+ function_name);
+ g_assert (ptask != NULL);
+ gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
+ ret = plugin_func (plugin, state->list, state->value,
+ cancellable, &error_local);
+ gs_plugin_loader_action_stop (plugin_loader, plugin);
+ if (!ret) {
+ /* badly behaved plugin */
+ if (error_local == NULL) {
+ g_critical ("%s did not set error for %s",
+ gs_plugin_get_name (plugin),
+ function_name);
+ continue;
+ }
+ g_warning ("failed to call %s on %s: %s",
+ function_name,
+ gs_plugin_get_name (plugin),
+ error_local->message);
+ continue;
+ }
+ gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
+ }
+
+ /* set the local file on any of the returned results */
+ for (j = 0; j < gs_app_list_length (state->list); j++) {
+ GsApp *app = gs_app_list_index (state->list, j);
+ if (gs_app_get_local_file (app) == NULL)
+ gs_app_set_local_file (app, state->file);
+ }
+
+ /* run refine() on each one */
+ ret = gs_plugin_loader_run_refine (plugin_loader,
+ function_name,
+ state->list,
+ state->flags,
+ cancellable,
+ &error);
+ if (!ret) {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ /* filter package list */
+ gs_app_list_filter (state->list, gs_plugin_loader_app_set_prio, plugin_loader);
+ gs_app_list_filter_duplicates (state->list, GS_APP_LIST_FILTER_FLAG_PRIORITY);
+
+ /* check the apps have an icon set */
+ for (j = 0; j < gs_app_list_length (state->list); j++) {
+ GsApp *app = gs_app_list_index (state->list, j);
+ if (_gs_app_get_icon_by_kind (app, AS_ICON_KIND_STOCK) == NULL &&
+ _gs_app_get_icon_by_kind (app, AS_ICON_KIND_LOCAL) == NULL &&
+ _gs_app_get_icon_by_kind (app, AS_ICON_KIND_CACHED) == NULL) {
+ g_autoptr(AsIcon) ic = as_icon_new ();
+ as_icon_set_kind (ic, AS_ICON_KIND_STOCK);
+ if (gs_app_get_kind (app) == AS_APP_KIND_SOURCE)
+ as_icon_set_name (ic, "x-package-repository");
+ else
+ as_icon_set_name (ic, "application-x-executable");
+ gs_app_add_icon (app, ic);
+ }
+ }
+
+ /* run refine() on each one again to pick up any icons */
+ ret = gs_plugin_loader_run_refine (plugin_loader,
+ function_name,
+ state->list,
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+ cancellable,
+ &error);
+ if (!ret) {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ /* success */
+ if (gs_app_list_length (state->list) != 1) {
+ g_task_return_new_error (task,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_NOT_SUPPORTED,
+ "no application was created for %s",
+ g_file_get_path (state->file));
+ return;
+ }
+ g_task_return_pointer (task, g_object_ref (gs_app_list_index (state->list, 0)), (GDestroyNotify)
g_object_unref);
+}
+
+/**
+ * gs_plugin_loader_url_to_app_async:
+ *
+ * This method calls all plugins that implement the gs_plugin_add_url_to_app()
+ * function. The plugins can either return #GsApp objects of kind
+ * %AS_APP_KIND_DESKTOP for bonafide applications, or #GsApp's of kind
+ * %AS_APP_KIND_GENERIC for packages that may or may not be applications.
+ *
+ * Once the list of updates is refined, some of the #GsApp's of kind
+ * %AS_APP_KIND_GENERIC will have been promoted to a kind of %AS_APP_KIND_DESKTOP,
+ * or if they are core applications.
+ *
+ * Files that are supported will have the GFile used to create them available
+ * from the gs_app_get_local_file() method.
+ **/
+void
+gs_plugin_loader_url_to_app_async (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginLoaderAsyncState *state;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ /* save state */
+ state = g_slice_new0 (GsPluginLoaderAsyncState);
+ state->flags = flags;
+ state->list = gs_app_list_new ();
+ state->value = g_strdup (url);
+
+ /* run in a thread */
+ task = g_task_new (plugin_loader, cancellable, callback, user_data);
+ g_task_set_task_data (task, state, (GDestroyNotify) gs_plugin_loader_free_async_state);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_run_in_thread (task, gs_plugin_loader_url_to_app_thread_cb);
+}
+
+/**
+ * gs_plugin_loader_url_to_app_finish:
+ *
+ * Return value: (element-type GsApp) (transfer full): An application, or %NULL
+ **/
+GsApp *
+gs_plugin_loader_url_to_app_finish (GsPluginLoader *plugin_loader,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
+ g_return_val_if_fail (G_IS_TASK (res), NULL);
+ g_return_val_if_fail (g_task_is_valid (res, plugin_loader), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+/******************************************************************************/
+
+/**
+ * gs_plugin_loader_update_thread_cb:
+ **/
+static void
gs_plugin_loader_update_thread_cb (GTask *task,
gpointer object,
gpointer task_data,
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index c43a2dd..d267a9d 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -160,6 +160,15 @@ void gs_plugin_loader_file_to_app_async (GsPluginLoader
*plugin_loader,
GsApp *gs_plugin_loader_file_to_app_finish (GsPluginLoader *plugin_loader,
GAsyncResult *res,
GError **error);
+void gs_plugin_loader_url_to_app_async (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags refine_flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GsApp *gs_plugin_loader_url_to_app_finish (GsPluginLoader *plugin_loader,
+ GAsyncResult *res,
+ GError **error);
void gs_plugin_loader_update_async (GsPluginLoader *plugin_loader,
GsAppList *apps,
GCancellable *cancellable,
diff --git a/src/gs-plugin-vfuncs.h b/src/gs-plugin-vfuncs.h
index ce6ad7e..bee56fb 100644
--- a/src/gs-plugin-vfuncs.h
+++ b/src/gs-plugin-vfuncs.h
@@ -838,6 +838,30 @@ gboolean gs_plugin_file_to_app (GsPlugin *plugin,
GError **error);
/**
+ * gs_plugin_url_to_app:
+ * @plugin: a #GsPlugin
+ * @list: a #GsAppList
+ * @url: a #URL, e.g. "apt://gimp"
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Converts a URL to a #GsApp. It's expected that only one plugin will
+ * match the scheme of @url and that a single #GsApp will be in the returned
+ * list. If no plugins can handle the file, the list will be empty.
+ *
+ * For example, the apt plugin can turn apt://gimp into a application.
+ *
+ * Plugins are expected to add new apps using gs_app_list_add().
+ *
+ * Returns: %TRUE for success or if not relevant
+ **/
+gboolean gs_plugin_url_to_app (GsPlugin *plugin,
+ GsAppList *list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error);
+
+/**
* gs_plugin_update:
* @plugin: a #GsPlugin
* @apps: a #GsAppList
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 14dd2a6..048a41c 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -1473,6 +1473,27 @@ gs_shell_details_set_local_file (GsShellDetails *self, GFile *file)
self);
}
+/**
+ * gs_shell_details_set_url:
+ **/
+void
+gs_shell_details_set_url (GsShellDetails *self, const gchar *url)
+{
+ gs_shell_details_set_state (self, GS_SHELL_DETAILS_STATE_LOADING);
+ gs_plugin_loader_url_to_app_async (self->plugin_loader,
+ url,
+ GS_PLUGIN_REFINE_FLAGS_DEFAULT |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS,
+ self->cancellable,
+ gs_shell_details_file_to_app_cb,
+ self);
+}
+
+/**
+ * gs_shell_details_load:
+ **/
static void
gs_shell_details_load (GsShellDetails *self)
{
diff --git a/src/gs-shell-details.h b/src/gs-shell-details.h
index 45da1d5..5ad1086 100644
--- a/src/gs-shell-details.h
+++ b/src/gs-shell-details.h
@@ -41,6 +41,8 @@ void gs_shell_details_set_app (GsShellDetails *self,
GsApp *app);
void gs_shell_details_set_local_file(GsShellDetails *self,
GFile *file);
+void gs_shell_details_set_url (GsShellDetails *self,
+ const gchar *url);
GsApp *gs_shell_details_get_app (GsShellDetails *self);
void gs_shell_details_setup (GsShellDetails *self,
GsShell *shell,
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 61d58bd..0a41c0a 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -320,6 +320,9 @@ gs_shell_change_mode (GsShell *shell,
if (gs_app_get_local_file (app) != NULL) {
gs_shell_details_set_local_file (priv->shell_details,
gs_app_get_local_file (app));
+ } else if (gs_app_get_metadata_item (app, "GnomeSoftware::from-url") != NULL) {
+ gs_shell_details_set_url (priv->shell_details,
+ gs_app_get_metadata_item (app,
"GnomeSoftware::from-url"));
} else {
gs_shell_details_set_app (priv->shell_details, data);
}
diff --git a/src/gs-utils.c b/src/gs-utils.c
index 58263d1..8c7f492 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -36,6 +36,7 @@
#include <fnmatch.h>
#include <math.h>
#include <glib/gstdio.h>
+#include <string.h>
#ifdef HAVE_POLKIT
#include <polkit/polkit.h>
@@ -503,4 +504,64 @@ gs_utils_get_wilson_rating (guint64 star1,
return (gint) ceil (val);
}
+/**
+ * gs_utils_get_url_scheme:
+ * @url: A URL, e.g. "appstream://gimp.desktop"
+ *
+ * Gets the scheme from the URL string.
+ *
+ * Returns: the URL scheme, e.g. "appstream"
+ */
+gchar *
+gs_utils_get_url_scheme (const gchar *url)
+{
+ g_autoptr(SoupURI) uri = NULL;
+
+ /* no data */
+ if (url == NULL)
+ return NULL;
+
+ /* create URI from URL */
+ uri = soup_uri_new (url);
+ if (!SOUP_URI_IS_VALID (uri))
+ return NULL;
+
+ /* success */
+ return g_strdup (soup_uri_get_scheme (uri));
+}
+
+/**
+ * gs_utils_get_url_path:
+ * @url: A URL, e.g. "appstream://gimp.desktop"
+ *
+ * Gets the path from the URL string, removing any leading slashes.
+ *
+ * Returns: the URL path, e.g. "gimp.desktop"
+ */
+gchar *
+gs_utils_get_url_path (const gchar *url)
+{
+ g_autoptr(SoupURI) uri = NULL;
+ const gchar *host;
+ const gchar *path;
+
+ uri = soup_uri_new (url);
+ if (!SOUP_URI_IS_VALID (uri))
+ return NULL;
+
+ /* foo://bar -> scheme: foo, host: bar, path: / */
+ /* foo:bar -> scheme: foo, host: (empty string), path: /bar */
+ host = soup_uri_get_host (uri);
+ path = soup_uri_get_path (uri);
+ if (host != NULL && (strlen (host) > 0))
+ path = host;
+
+ /* trim any leading slashes */
+ while (*path == '/')
+ path++;
+
+ /* success */
+ return g_strdup (path);
+}
+
/* vim: set noexpandtab: */
diff --git a/src/gs-utils.h b/src/gs-utils.h
index 0a9db60..0bfa5ab 100644
--- a/src/gs-utils.h
+++ b/src/gs-utils.h
@@ -70,6 +70,8 @@ gint gs_utils_get_wilson_rating (guint64 star1,
guint64 star3,
guint64 star4,
guint64 star5);
+gchar *gs_utils_get_url_scheme (const gchar *url);
+gchar *gs_utils_get_url_path (const gchar *url);
G_END_DECLS
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index d6ab82a..8e34f7f 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -323,6 +323,39 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
return TRUE;
}
+gboolean
+gs_plugin_url_to_app (GsPlugin *plugin,
+ GsAppList *list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ AsApp *item;
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *scheme = NULL;
+ g_autoptr(GsApp) app = NULL;
+
+ /* not us */
+ scheme = gs_utils_get_url_scheme (url);
+ if (g_strcmp0 (scheme, "appstream") != 0 && g_strcmp0 (scheme, "apt") != 0)
+ return TRUE;
+
+ /* create app */
+ path = gs_utils_get_url_path (url);
+ if (g_strcmp0 (scheme, "appstream") == 0)
+ item = as_store_get_app_by_id (priv->store, path);
+ else
+ item = as_store_get_app_by_pkgname (priv->store, path);
+ if (item == NULL)
+ return TRUE;
+ app = gs_appstream_create_app (plugin, item, error);
+ if (app == NULL)
+ return FALSE;
+ gs_app_list_add (list, app);
+ return TRUE;
+}
+
static gboolean
gs_plugin_refine_from_id (GsPlugin *plugin,
GsApp *app,
diff --git a/src/plugins/gs-plugin-snap.c b/src/plugins/gs-plugin-snap.c
index 7310c9f..3cf1cc5 100644
--- a/src/plugins/gs-plugin-snap.c
+++ b/src/plugins/gs-plugin-snap.c
@@ -224,6 +224,44 @@ refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_sea
}
}
+gboolean
+gs_plugin_url_to_app (GsPlugin *plugin,
+ GsAppList *list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *scheme = NULL;
+ g_autofree gchar *macaroon = NULL;
+ g_auto(GStrv) discharges = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
+ JsonObject *snap;
+ g_autofree gchar *path = NULL;
+ g_autoptr(GsApp) app = NULL;
+
+ /* not us */
+ scheme = gs_utils_get_url_scheme (url);
+ if (g_strcmp0 (scheme, "snap") != 0)
+ return TRUE;
+
+ get_macaroon (plugin, &macaroon, &discharges);
+
+ /* create app */
+ path = gs_utils_get_url_path (url);
+ snaps = gs_snapd_find_name (macaroon, discharges, path, cancellable, NULL);
+ if (snaps == NULL || json_array_get_length (snaps) < 1)
+ return TRUE;
+
+ snap = json_array_get_object_element (snaps, 0);
+ app = gs_app_new (json_object_get_string_member (snap, "name"));
+ gs_app_set_management_plugin (app, "snap");
+ gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
+ refine_app (plugin, app, snap, TRUE, cancellable);
+ gs_app_list_add (list, app);
+
+ return TRUE;
+}
+
void
gs_plugin_destroy (GsPlugin *plugin)
{
diff --git a/src/plugins/gs-snapd.c b/src/plugins/gs-snapd.c
index 4a75bb6..a16e192 100644
--- a/src/plugins/gs-snapd.c
+++ b/src/plugins/gs-snapd.c
@@ -486,6 +486,55 @@ gs_snapd_find (const gchar *macaroon, gchar **discharges,
return json_array_ref (result);
}
+JsonArray *
+gs_snapd_find_name (const gchar *macaroon, gchar **discharges,
+ const gchar *name,
+ GCancellable *cancellable, GError **error)
+{
+ g_autofree gchar *escaped = NULL;
+ g_autofree gchar *path = NULL;
+ guint status_code;
+ g_autofree gchar *reason_phrase = NULL;
+ g_autofree gchar *response_type = NULL;
+ g_autofree gchar *response = NULL;
+ g_autoptr(JsonParser) parser = NULL;
+ JsonObject *root;
+ JsonArray *result;
+
+ escaped = soup_uri_encode (name, NULL);
+ path = g_strdup_printf ("/v2/find?name=%s", escaped);
+ if (!send_request ("GET", path, NULL,
+ macaroon, discharges,
+ &status_code, &reason_phrase,
+ &response_type, &response, NULL,
+ cancellable, error))
+ return NULL;
+
+ if (status_code != SOUP_STATUS_OK) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd returned status code %u: %s",
+ status_code, reason_phrase);
+ return NULL;
+ }
+
+ parser = parse_result (response, response_type, error);
+ if (parser == NULL)
+ return NULL;
+ root = json_node_get_object (json_parser_get_root (parser));
+ result = json_object_get_array_member (root, "result");
+ if (result == NULL) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd returned no result");
+ return NULL;
+ }
+
+ return json_array_ref (result);
+}
+
JsonObject *
gs_snapd_get_interfaces (const gchar *macaroon, gchar **discharges, GCancellable *cancellable, GError
**error)
{
diff --git a/src/plugins/gs-snapd.h b/src/plugins/gs-snapd.h
index af28bd1..103bf56 100644
--- a/src/plugins/gs-snapd.h
+++ b/src/plugins/gs-snapd.h
@@ -51,6 +51,12 @@ JsonObject *gs_snapd_get_interfaces (const gchar *macaroon,
GCancellable *cancellable,
GError **error);
+JsonArray *gs_snapd_find_name (const gchar *macaroon,
+ gchar **discharges,
+ const gchar *name,
+ GCancellable *cancellable,
+ GError **error);
+
gboolean gs_snapd_install (const gchar *macaroon,
gchar **discharges,
const gchar *name,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]