[gnome-software/wip/rancell/ubuntu-3-20-rebase: 9/17] 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/rancell/ubuntu-3-20-rebase: 9/17] Support snap:// URL handling by backporting URL handling improvements from trunk
- Date: Sat, 17 Jun 2017 01:18:34 +0000 (UTC)
commit 272ec0fc88662a5ee50750ab295aab5c5d68c384
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 | 42 ++++++----
src/gs-cmd.c | 11 +++
src/gs-plugin-loader-sync.c | 50 ++++++++++++
src/gs-plugin-loader-sync.h | 5 +
src/gs-plugin-loader.c | 155 +++++++++++++++++++++++++++++++++++++
src/gs-plugin-loader.h | 9 ++
src/gs-plugin.h | 10 +++
src/gs-shell-details.c | 18 ++++
src/gs-shell-details.h | 2 +
src/gs-shell.c | 9 ++-
src/gs-utils.c | 60 ++++++++++++++
src/gs-utils.h | 3 +
src/plugins/gs-plugin-appstream.c | 40 ++++++++++
13 files changed, 395 insertions(+), 19 deletions(-)
---
diff --git a/src/gs-application.c b/src/gs-application.c
index 6b10e01..50c8520 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -597,6 +597,26 @@ details_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);
+
+ 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)
@@ -688,6 +708,8 @@ static GActionEntry actions[] = {
{ "set-mode", set_mode_activated, "s", NULL, NULL },
{ "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 },
@@ -820,23 +842,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 *path = soup_uri_get_path (uri);
-
- /* trim any leading slashes */
- while (*path == '/')
- path++;
-
- g_action_group_activate_action (G_ACTION_GROUP (app),
- "details",
- g_variant_new ("(ss)", 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 c169d33..4c61952 100644
--- a/src/gs-cmd.c
+++ b/src/gs-cmd.c
@@ -324,6 +324,17 @@ main (int argc, char **argv)
} else {
gs_plugin_add_app (&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_plugin_add_app (&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 254b8b9..e2fed01 100644
--- a/src/gs-plugin-loader-sync.c
+++ b/src/gs-plugin-loader-sync.c
@@ -664,4 +664,54 @@ gs_plugin_loader_filename_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 b1b88ec..158c81d 100644
--- a/src/gs-plugin-loader-sync.h
+++ b/src/gs-plugin-loader-sync.h
@@ -91,6 +91,11 @@ GsApp *gs_plugin_loader_filename_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 656cf2a..a648a6a 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -3752,6 +3752,161 @@ gs_plugin_loader_filename_to_app_finish (GsPluginLoader *plugin_loader,
/******************************************************************************/
/**
+ * gs_plugin_loader_url_to_app_thread_cb:
+ **/
+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;
+ GList *l;
+ GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
+ GsPlugin *plugin;
+ GsPluginUrlToAppFunc plugin_func = NULL;
+ guint i;
+
+ /* 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 (!plugin->enabled)
+ continue;
+ ret = g_task_return_error_if_cancelled (task);
+ if (ret)
+ return;
+ ret = g_module_symbol (plugin->module,
+ function_name,
+ (gpointer *) &plugin_func);
+ if (!ret)
+ continue;
+ ptask = as_profile_start (priv->profile,
+ "GsPlugin::%s(%s)",
+ plugin->name,
+ function_name);
+ ret = plugin_func (plugin, &state->list, state->value,
+ cancellable, &error_local);
+ if (!ret) {
+ g_warning ("failed to call %s on %s: %s",
+ function_name, plugin->name,
+ error_local->message);
+ continue;
+ }
+ gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
+ }
+
+ /* dedupe applications we already know about */
+ gs_plugin_loader_list_dedupe (plugin_loader, state->list);
+
+ /* set the local file on any of the returned results */
+ for (l = state->list; l != NULL; l = l->next) {
+ GsApp *app = GS_APP (l->data);
+ 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_plugin_list_filter_duplicates (&state->list);
+ if (state->list == NULL) {
+ g_task_return_new_error (task,
+ GS_PLUGIN_LOADER_ERROR,
+ GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
+ "no url_to_app results to show");
+ return;
+ }
+
+ /* success */
+ if (g_list_length (state->list) != 1) {
+ g_task_return_new_error (task,
+ GS_PLUGIN_LOADER_ERROR,
+ GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
+ "no application was created for %s",
+ state->value);
+ return;
+ }
+ g_task_return_pointer (task, g_object_ref (state->list->data), (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->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_offline_update_thread_cb:
**/
static void
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 2365cb0..1a3a3d2 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -180,6 +180,15 @@ void gs_plugin_loader_filename_to_app_async (GsPluginLoader
*plugin_loader,
GsApp *gs_plugin_loader_filename_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_offline_update_async (GsPluginLoader *plugin_loader,
GList *apps,
GCancellable *cancellable,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 4c40680..f49d1eb 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -165,6 +165,11 @@ typedef gboolean (*GsPluginFilenameToAppFunc) (GsPlugin *plugin,
const gchar *filename,
GCancellable *cancellable,
GError **error);
+typedef gboolean (*GsPluginUrlToAppFunc) (GsPlugin *plugin,
+ GList **list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error);
typedef gboolean (*GsPluginOfflineUpdateFunc) (GsPlugin *plugin,
GList *apps,
GCancellable *cancellable,
@@ -332,6 +337,11 @@ gboolean gs_plugin_filename_to_app (GsPlugin *plugin,
const gchar *filename,
GCancellable *cancellable,
GError **error);
+gboolean gs_plugin_url_to_app (GsPlugin *plugin,
+ GList **list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_plugin_offline_update (GsPlugin *plugin,
GList *apps,
GCancellable *cancellable,
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 1d59993..af097c8 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -1244,6 +1244,24 @@ gs_shell_details_set_filename (GsShellDetails *self, const gchar *filename)
}
/**
+ * 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
diff --git a/src/gs-shell-details.h b/src/gs-shell-details.h
index 3ffd72a..cbe78a8 100644
--- a/src/gs-shell-details.h
+++ b/src/gs-shell-details.h
@@ -42,6 +42,8 @@ void gs_shell_details_set_app (GsShellDetails *self,
GsApp *app);
void gs_shell_details_set_filename (GsShellDetails *self,
const gchar *filename);
+void gs_shell_details_set_url (GsShellDetails *self,
+ const gchar *url);
GsApp *gs_shell_details_get_app (GsShellDetails *self);
void gs_shell_details_reload (GsShellDetails *self);
void gs_shell_details_setup (GsShellDetails *self,
diff --git a/src/gs-shell.c b/src/gs-shell.c
index bd2deb0..154b4c0 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -297,8 +297,13 @@ gs_shell_change_mode (GsShell *shell,
new_page = GS_PAGE (priv->shell_updates);
break;
case GS_SHELL_MODE_DETAILS:
- if (app != NULL)
- gs_shell_details_set_app (priv->shell_details, app);
+ if (app != NULL) {
+ 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, app);
+ }
if (data != NULL)
gs_shell_details_set_filename (priv->shell_details, data);
new_page = GS_PAGE (priv->shell_details);
diff --git a/src/gs-utils.c b/src/gs-utils.c
index 4cd1799..86f99af 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -517,4 +517,64 @@ gs_utils_get_content_type (const gchar *filename,
return g_strdup (tmp);
}
+/**
+ * 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 561054c..6882063 100644
--- a/src/gs-utils.h
+++ b/src/gs-utils.h
@@ -65,6 +65,9 @@ gchar *gs_utils_get_cachedir (const gchar *kind,
gchar *gs_utils_get_user_hash (GError **error);
GPermission *gs_utils_get_permission (const gchar *id);
+gchar *gs_utils_get_url_scheme (const gchar *url);
+gchar *gs_utils_get_url_path (const gchar *url);
+
G_END_DECLS
#endif /* __GS_UTILS_H */
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index 14f7db2..357e390 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -246,6 +246,46 @@ gs_plugin_appstream_startup (GsPlugin *plugin, GError **error)
}
/**
+ * gs_plugin_url_to_app:
+ */
+gboolean
+gs_plugin_url_to_app (GsPlugin *plugin,
+ GList **list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error)
+{
+ AsApp *item;
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *scheme = NULL;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&plugin->priv->store_mutex);
+
+ /* not us */
+ scheme = gs_utils_get_url_scheme (url);
+ if (g_strcmp0 (scheme, "appstream") != 0 && g_strcmp0 (scheme, "apt") != 0)
+ return TRUE;
+
+ /* load XML files */
+ if (!gs_plugin_appstream_startup (plugin, error))
+ return FALSE;
+
+ /* create app */
+ path = gs_utils_get_url_path (url);
+ if (g_strcmp0 (scheme, "appstream") == 0)
+ item = as_store_get_app_by_id (plugin->priv->store, path);
+ else
+ item = as_store_get_app_by_pkgname (plugin->priv->store, path);
+ if (item == NULL)
+ return TRUE;
+ app = gs_app_new (as_app_get_id (item));
+ if (!gs_appstream_refine_app (plugin, app, item, error))
+ return FALSE;
+ gs_plugin_add_app (list, app);
+ return TRUE;
+}
+
+/**
* gs_plugin_refine_from_id:
*/
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]