[gnome-software/wip/ubuntu-3-20] snap: Backport fixes from master
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/ubuntu-3-20] snap: Backport fixes from master
- Date: Thu, 15 Jun 2017 23:58:27 +0000 (UTC)
commit e04b2784d03a2ac400693880bde456e3dadaab5f
Author: Robert Ancell <robert ancell canonical com>
Date: Fri Jun 16 11:57:13 2017 +1200
snap: Backport fixes from master
Fixes the following:
- Refactor refining code to be more efficient and reliable
- Cache icons
- Set screenshot dimensions
- Set install date
- Use gs_plugin_add_popular to show featured snaps
src/plugins/gs-plugin-snap.c | 469 +++++++++++++++++++++++++++---------------
src/plugins/gs-snapd.c | 165 ++++++++++-----
src/plugins/gs-snapd.h | 10 +-
3 files changed, 424 insertions(+), 220 deletions(-)
---
diff --git a/src/plugins/gs-plugin-snap.c b/src/plugins/gs-plugin-snap.c
index db2aefd..71940b5 100644
--- a/src/plugins/gs-plugin-snap.c
+++ b/src/plugins/gs-plugin-snap.c
@@ -27,6 +27,7 @@
#include "gs-ubuntuone.h"
struct GsPluginPrivate {
+ GHashTable *store_snaps;
};
typedef gboolean (*AppFilterFunc)(const gchar *id, JsonObject *object, gpointer data);
@@ -48,131 +49,74 @@ gs_plugin_initialize (GsPlugin *plugin)
gs_plugin_get_name ());
gs_plugin_set_enabled (plugin, FALSE);
}
+
+ plugin->priv->store_snaps = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) json_object_unref);
}
-static void
-refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_search, GCancellable
*cancellable)
+static gboolean
+gs_plugin_snap_set_app_pixbuf_from_data (GsApp *app, const gchar *buf, gsize count, GError **error)
{
- const gchar *status, *icon_url, *launch_name = NULL;
- g_autoptr(GdkPixbuf) icon_pixbuf = NULL;
- gint64 size = -1;
-
- status = json_object_get_string_member (package, "status");
- if (g_strcmp0 (status, "installed") == 0 || g_strcmp0 (status, "active") == 0) {
- const gchar *update_available;
-
- update_available = json_object_has_member (package, "update_available") ?
- json_object_get_string_member (package, "update_available") : NULL;
- if (update_available)
- gs_app_set_state (app, AS_APP_STATE_UPDATABLE);
- else {
- if (gs_app_get_state (app) == AS_APP_STATE_AVAILABLE)
- gs_app_set_state (app, AS_APP_STATE_UNKNOWN);
- gs_app_set_state (app, AS_APP_STATE_INSTALLED);
- }
- size = json_object_get_int_member (package, "installed-size");
- } else if (g_strcmp0 (status, "not installed") == 0 || g_strcmp0 (status, "available") == 0) {
- gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
- size = json_object_get_int_member (package, "download-size");
- }
- else if (g_strcmp0 (status, "removed") == 0) {
- // A removed app is only available if it can be downloaded (it might have been sideloaded)
- size = json_object_get_int_member (package, "download-size");
- if (size > 0)
- gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+ g_autoptr(GdkPixbufLoader) loader = NULL;
+ g_autoptr(GError) error_local = NULL;
+
+ loader = gdk_pixbuf_loader_new ();
+ if (!gdk_pixbuf_loader_write (loader, buf, count, &error_local)) {
+ g_debug ("icon_data[%" G_GSIZE_FORMAT "]=%s", count, buf);
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "Failed to write: %s",
+ error_local->message);
+ return FALSE;
}
-
- gs_app_set_name (app, GS_APP_QUALITY_HIGHEST,
- json_object_get_string_member (package, "name"));
- gs_app_set_summary (app, GS_APP_QUALITY_HIGHEST,
- json_object_get_string_member (package, "summary"));
- gs_app_set_description (app, GS_APP_QUALITY_HIGHEST,
- json_object_get_string_member (package, "description"));
- gs_app_set_version (app, json_object_get_string_member (package, "version"));
- if (size > 0)
- gs_app_set_size (app, size);
- gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE);
- icon_url = json_object_get_string_member (package, "icon");
- if (g_str_has_prefix (icon_url, "/")) {
- g_autofree gchar *icon_data = NULL;
- gsize icon_data_length;
- g_autoptr(GError) error = NULL;
-
- icon_data = gs_snapd_get_resource (icon_url, &icon_data_length, cancellable, &error);
- if (icon_data != NULL) {
- g_autoptr(GdkPixbufLoader) loader = NULL;
-
- loader = gdk_pixbuf_loader_new ();
- gdk_pixbuf_loader_write (loader,
- (guchar *) icon_data,
- icon_data_length,
- NULL);
- gdk_pixbuf_loader_close (loader, NULL);
- icon_pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
- }
- else
- g_printerr ("Failed to get icon: %s\n", error->message);
+ if (!gdk_pixbuf_loader_close (loader, &error_local)) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "Failed to close: %s",
+ error_local->message);
+ return FALSE;
}
- else {
- g_autoptr(SoupMessage) message = NULL;
- g_autoptr(GdkPixbufLoader) loader = NULL;
+ gs_app_set_pixbuf (app, gdk_pixbuf_loader_get_pixbuf (loader));
+ return TRUE;
+}
- message = soup_message_new (SOUP_METHOD_GET, icon_url);
- if (message != NULL) {
- soup_session_send_message (plugin->soup_session, message);
- loader = gdk_pixbuf_loader_new ();
- gdk_pixbuf_loader_write (loader,
- (guint8 *) message->response_body->data,
- message->response_body->length,
- NULL);
- gdk_pixbuf_loader_close (loader, NULL);
- icon_pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
- }
- }
+static JsonArray *
+find_snaps (GsPlugin *plugin, const gchar *section, gboolean match_name, const gchar *query, GCancellable
*cancellable, GError **error)
+{
+ g_autoptr(JsonArray) snaps = NULL;
+ guint i;
- if (icon_pixbuf) {
- gs_app_set_pixbuf (app, icon_pixbuf);
- } else {
- g_autoptr(AsIcon) icon = NULL;
+ snaps = gs_snapd_find (section, match_name, query, cancellable, error);
+ if (snaps == NULL)
+ return NULL;
- icon = as_icon_new ();
- as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
- as_icon_set_name (icon, "package-x-generic");
- gs_app_set_icon (app, icon);
+ /* cache results */
+ for (i = 0; i < json_array_get_length (snaps); i++) {
+ JsonObject *snap = json_array_get_object_element (snaps, i);
+ g_hash_table_insert (plugin->priv->store_snaps, g_strdup (json_object_get_string_member
(snap, "name")), json_object_ref (snap));
}
- if (json_object_has_member (package, "screenshots") && gs_app_get_screenshots (app)->len <= 0) {
- JsonArray *screenshots;
- guint i;
-
- screenshots = json_object_get_array_member (package, "screenshots");
- for (i = 0; i < json_array_get_length (screenshots); i++) {
- JsonObject *screenshot = json_array_get_object_element (screenshots, i);
- g_autoptr(AsScreenshot) ss = NULL;
- g_autoptr(AsImage) image = NULL;
-
- ss = as_screenshot_new ();
- as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_NORMAL);
- image = as_image_new ();
- as_image_set_url (image, json_object_get_string_member (screenshot, "url"));
- as_image_set_kind (image, AS_IMAGE_KIND_SOURCE);
- as_screenshot_add_image (ss, image);
- gs_app_add_screenshot (app, ss);
- }
- }
+ return g_steal_pointer (&snaps);
+}
- if (!from_search) {
- JsonArray *apps;
+static GsApp *
+snap_to_app (GsPlugin *plugin, JsonObject *snap)
+{
+ GsApp *app;
- apps = json_object_get_array_member (package, "apps");
- if (apps && json_array_get_length (apps) > 0)
- launch_name = json_object_get_string_member (json_array_get_object_element (apps, 0),
"name");
+ /* create a unique ID for deduplication, TODO: branch? */
+ app = gs_app_new (json_object_get_string_member (snap, "name"));
+ gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
+ gs_app_set_management_plugin (app, "snap");
+ gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
+ gs_app_set_name (app, GS_APP_QUALITY_HIGHEST, json_object_get_string_member (snap, "name"));
+ gs_app_set_origin (app, _("Ubuntu Snappy Store")); // FIXME: Not necessarily from the snap store...
+ if (gs_plugin_check_distro_id (plugin, "ubuntu"))
+ gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE);
- if (launch_name)
- gs_app_set_metadata (app, "snap::launch-name", launch_name);
- else
- gs_app_add_quirk (app, AS_APP_QUIRK_NOT_LAUNCHABLE);
- }
+ return app;
}
gboolean
@@ -195,16 +139,12 @@ gs_plugin_url_to_app (GsPlugin *plugin,
/* create app */
path = gs_utils_get_url_path (url);
- snaps = gs_snapd_find_name (path, cancellable, NULL);
+ snaps = find_snaps (plugin, NULL, TRUE, 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_plugin_add_app (list, app);
+ gs_plugin_add_app (list, snap_to_app (plugin, snap));
return TRUE;
}
@@ -212,6 +152,28 @@ gs_plugin_url_to_app (GsPlugin *plugin,
void
gs_plugin_destroy (GsPlugin *plugin)
{
+ g_hash_table_unref (plugin->priv->store_snaps);
+}
+
+gboolean
+gs_plugin_add_popular (GsPlugin *plugin,
+ GList **list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(JsonArray) snaps = NULL;
+ guint i;
+
+ snaps = find_snaps (plugin, "featured", FALSE, NULL, cancellable, error);
+ if (snaps == NULL)
+ return FALSE;
+
+ for (i = 0; i < json_array_get_length (snaps); i++) {
+ JsonObject *snap = json_array_get_object_element (snaps, i);
+ gs_plugin_add_app (list, snap_to_app (plugin, snap));
+ }
+
+ return TRUE;
}
gboolean
@@ -220,30 +182,22 @@ gs_plugin_add_installed (GsPlugin *plugin,
GCancellable *cancellable,
GError **error)
{
- g_autoptr(JsonArray) result = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
guint i;
- result = gs_snapd_list (cancellable, error);
- if (result == NULL)
+ snaps = gs_snapd_list (cancellable, error);
+ if (snaps == NULL)
return FALSE;
- for (i = 0; i < json_array_get_length (result); i++) {
- JsonObject *package = json_array_get_object_element (result, i);
- g_autoptr(GsApp) app = NULL;
- const gchar *status, *name;
+ for (i = 0; i < json_array_get_length (snaps); i++) {
+ JsonObject *snap = json_array_get_object_element (snaps, i);
+ const gchar *status;
- status = json_object_get_string_member (package, "status");
+ status = json_object_get_string_member (snap, "status");
if (g_strcmp0 (status, "active") != 0)
continue;
- name = json_object_get_string_member (package, "name");
- app = gs_app_new (name);
- gs_app_set_management_plugin (app, "snap");
- gs_app_set_origin (app, _("Ubuntu Snappy Store")); // FIXME: Not necessarily from the snap
store...
- gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
- gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
- refine_app (plugin, app, package, TRUE, cancellable);
- gs_plugin_add_app (list, app);
+ gs_plugin_add_app (list, snap_to_app (plugin, snap));
}
return TRUE;
@@ -256,26 +210,225 @@ gs_plugin_add_search (GsPlugin *plugin,
GCancellable *cancellable,
GError **error)
{
- g_autoptr(JsonArray) result = NULL;
+ g_autofree gchar *query = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
guint i;
- result = gs_snapd_find (values, cancellable, error);
- if (result == NULL)
+ query = g_strjoinv (" ", values);
+ snaps = find_snaps (plugin, NULL, FALSE, query, cancellable, error);
+ if (snaps == NULL)
return FALSE;
- for (i = 0; i < json_array_get_length (result); i++) {
- JsonObject *package = json_array_get_object_element (result, i);
- g_autoptr(GsApp) app = NULL;
- const gchar *name;
-
- name = json_object_get_string_member (package, "name");
- app = gs_app_new (name);
- gs_app_set_management_plugin (app, "snap");
- gs_app_set_origin (app, _("Ubuntu Snappy Store")); // FIXME: Not necessarily from the snap
store...
- gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
- gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
- refine_app (plugin, app, package, TRUE, cancellable);
- gs_plugin_add_app (list, app);
+ for (i = 0; i < json_array_get_length (snaps); i++) {
+ JsonObject *snap = json_array_get_object_element (snaps, i);
+ gs_plugin_add_app (list, snap_to_app (plugin, snap));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+load_icon (GsPlugin *plugin, GsApp *app, const gchar *icon_url, GCancellable *cancellable, GError **error)
+{
+ if (icon_url == NULL || g_strcmp0 (icon_url, "") == 0) {
+ g_autoptr(AsIcon) icon = as_icon_new ();
+ as_icon_set_kind (icon, AS_ICON_KIND_STOCK);
+ as_icon_set_name (icon, "package-x-generic");
+ gs_app_set_icon (app, icon);
+ return TRUE;
+ }
+
+ /* icon is optional, either loaded from snapd or from a URL */
+ if (g_str_has_prefix (icon_url, "/")) {
+ g_autofree gchar *icon_data = NULL;
+ gsize icon_data_length;
+
+ icon_data = gs_snapd_get_resource (icon_url, &icon_data_length, cancellable, error);
+ if (icon_data == NULL)
+ return FALSE;
+
+ if (!gs_plugin_snap_set_app_pixbuf_from_data (app,
+ icon_data, icon_data_length,
+ error)) {
+ g_prefix_error (error, "Failed to load %s: ", icon_url);
+ return FALSE;
+ }
+ } else {
+ g_autofree gchar *basename_tmp = NULL;
+ g_autofree gchar *hash = NULL;
+ g_autofree gchar *basename = NULL;
+ g_autofree gchar *cache_dir = NULL;
+ g_autofree gchar *cache_fn = NULL;
+ g_autoptr(SoupMessage) message = NULL;
+ g_autoptr(GdkPixbufLoader) loader = NULL;
+ g_autoptr(GError) local_error = NULL;
+
+ /* attempt to load from cache */
+ basename_tmp = g_path_get_basename (icon_url);
+ hash = g_compute_checksum_for_string (G_CHECKSUM_SHA1, icon_url, -1);
+ basename = g_strdup_printf ("%s-%s", hash, basename_tmp);
+ cache_dir = gs_utils_get_cachedir ("snap-icons", error);
+ cache_fn = g_build_filename (cache_dir, basename, NULL);
+ if (cache_fn == NULL)
+ return FALSE;
+ if (g_file_test (cache_fn, G_FILE_TEST_EXISTS)) {
+ g_autofree gchar *data = NULL;
+ gsize data_len;
+
+ if (g_file_get_contents (cache_fn, &data, &data_len, &local_error) &&
+ gs_plugin_snap_set_app_pixbuf_from_data (app,
+ data, data_len,
+ error))
+ return TRUE;
+
+ g_warning ("Failed to load cached icon: %s", local_error->message);
+ }
+
+ /* load from URL */
+ message = soup_message_new (SOUP_METHOD_GET, icon_url);
+ if (message == NULL) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_NOT_SUPPORTED,
+ "Failed to parse icon URL: %s",
+ icon_url);
+ return FALSE;
+ }
+ soup_session_send_message (plugin->soup_session, message);
+ if (!gs_plugin_snap_set_app_pixbuf_from_data (app,
+ (const gchar *) message->response_body->data,
+ message->response_body->length,
+ error)) {
+ g_prefix_error (error, "Failed to load %s: ", icon_url);
+ return FALSE;
+ }
+
+ /* write to cache */
+ if (!g_file_set_contents (cache_fn, message->response_body->data,
message->response_body->length, &local_error))
+ g_warning ("Failed to save icon to cache: %s", local_error->message);
+ }
+
+ return TRUE;
+}
+
+static JsonObject *
+get_store_snap (GsPlugin *plugin, const gchar *name, GCancellable *cancellable, GError **error)
+{
+ JsonObject *snap = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
+
+ /* use cached version if available */
+ snap = g_hash_table_lookup (plugin->priv->store_snaps, name);
+ if (snap != NULL)
+ return json_object_ref (snap);
+
+ snaps = find_snaps (plugin, NULL, TRUE, name, cancellable, error);
+ if (snaps == NULL || json_array_get_length (snaps) < 1)
+ return NULL;
+
+ return json_object_ref (json_array_get_object_element (snaps, 0));
+}
+
+static gboolean
+gs_plugin_refine_app (GsPlugin *plugin,
+ GsApp *app,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *id, *icon_url = NULL;
+ g_autoptr(JsonObject) local_snap = NULL;
+ g_autoptr(JsonObject) store_snap = NULL;
+
+ /* not us */
+ if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
+ return TRUE;
+
+ id = gs_app_get_id (app);
+ if (id == NULL)
+ id = gs_app_get_source_default (app);
+
+ /* get information from installed snaps */
+ local_snap = gs_snapd_list_one (id, cancellable, NULL);
+ if (local_snap != NULL) {
+ JsonArray *apps;
+ g_autoptr(GDateTime) install_date = NULL;
+ const gchar *launch_name = NULL;
+
+ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
+ gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, json_object_get_string_member (local_snap,
"name"));
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, json_object_get_string_member (local_snap,
"summary"));
+ gs_app_set_description (app, GS_APP_QUALITY_NORMAL, json_object_get_string_member
(local_snap, "description"));
+ gs_app_set_version (app, json_object_get_string_member (local_snap, "version"));
+ if (json_object_has_member (local_snap, "installed-size"))
+ gs_app_set_size (app, json_object_get_int_member (local_snap, "installed-size"));
+ if (json_object_has_member (local_snap, "install-date"))
+ install_date = gs_snapd_parse_date (json_object_get_string_member (local_snap,
"install-date"));
+ if (install_date != NULL)
+ gs_app_set_install_date (app, g_date_time_to_unix (install_date));
+ icon_url = json_object_get_string_member (local_snap, "icon");
+ if (g_strcmp0 (icon_url, "") == 0)
+ icon_url = NULL;
+
+ apps = json_object_get_array_member (local_snap, "apps");
+ if (apps && json_array_get_length (apps) > 0)
+ launch_name = json_object_get_string_member (json_array_get_object_element (apps, 0),
"name");
+
+ if (launch_name)
+ gs_app_set_metadata (app, "snap::launch-name", launch_name);
+ else
+ gs_app_add_quirk (app, AS_APP_QUIRK_NOT_LAUNCHABLE);
+ }
+
+ /* get information from snap store */
+ store_snap = get_store_snap (plugin, id, cancellable, NULL);
+ if (store_snap != NULL) {
+ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
+ gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, json_object_get_string_member (store_snap,
"name"));
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, json_object_get_string_member (store_snap,
"summary"));
+ gs_app_set_description (app, GS_APP_QUALITY_NORMAL, json_object_get_string_member
(store_snap, "description"));
+ gs_app_set_version (app, json_object_get_string_member (store_snap, "version"));
+ if (gs_app_get_size (app) == GS_APP_SIZE_UNKNOWN && json_object_has_member (store_snap,
"download-size"))
+ gs_app_set_size (app, json_object_get_int_member (store_snap, "download-size"));
+ if (icon_url == NULL) {
+ icon_url = json_object_get_string_member (store_snap, "icon");
+ if (g_strcmp0 (icon_url, "") == 0)
+ icon_url = NULL;
+ }
+
+ if (json_object_has_member (store_snap, "screenshots") && gs_app_get_screenshots (app)->len
== 0) {
+ JsonArray *screenshots;
+ guint i;
+
+ screenshots = json_object_get_array_member (store_snap, "screenshots");
+ for (i = 0; i < json_array_get_length (screenshots); i++) {
+ JsonObject *screenshot = json_array_get_object_element (screenshots, i);
+ g_autoptr(AsScreenshot) ss = NULL;
+ g_autoptr(AsImage) image = NULL;
+
+ ss = as_screenshot_new ();
+ as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_NORMAL);
+ image = as_image_new ();
+ as_image_set_url (image, json_object_get_string_member (screenshot, "url"));
+ as_image_set_kind (image, AS_IMAGE_KIND_SOURCE);
+ if (json_object_has_member (screenshot, "width"))
+ as_image_set_width (image, json_object_get_int_member (screenshot,
"width"));
+ if (json_object_has_member (screenshot, "height"))
+ as_image_set_height (image, json_object_get_int_member (screenshot,
"height"));
+ as_screenshot_add_image (ss, image);
+ gs_app_add_screenshot (app, ss);
+ }
+ }
+ }
+
+ /* load icon if requested */
+ if (gs_app_get_pixbuf (app) == NULL && gs_app_get_icon (app) == NULL) {
+ if (!load_icon (plugin, app, icon_url, cancellable, error))
+ return FALSE;
}
return TRUE;
@@ -292,15 +445,10 @@ gs_plugin_refine (GsPlugin *plugin,
for (link = *list; link; link = link->next) {
GsApp *app = link->data;
- g_autoptr(JsonObject) result = NULL;
+ g_autoptr(GError) local_error = NULL;
- if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
- continue;
-
- result = gs_snapd_list_one (gs_app_get_id (app), cancellable, NULL);
- if (result == NULL)
- continue;
- refine_app (plugin, app, result, FALSE, cancellable);
+ if (!gs_plugin_refine_app (plugin, app, flags, cancellable, &local_error))
+ g_warning ("Failed to refine snap: %s", local_error->message);
}
return TRUE;
@@ -394,7 +542,7 @@ is_graphical (GsApp *app, GCancellable *cancellable)
if (interface == NULL)
continue;
- if (strcmp (interface, "unity7") == 0 || strcmp (interface, "x11") == 0 || strcmp (interface,
"mir") == 0)
+ if (g_strcmp0 (interface, "unity7") == 0 || g_strcmp0 (interface, "x11") == 0 || g_strcmp0
(interface, "mir") == 0)
return TRUE;
}
@@ -412,7 +560,6 @@ gs_plugin_launch (GsPlugin *plugin,
GAppInfoCreateFlags flags = G_APP_INFO_CREATE_NONE;
g_autoptr(GAppInfo) info = NULL;
-
/* We can only launch apps we know of */
if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
return TRUE;
@@ -421,7 +568,7 @@ gs_plugin_launch (GsPlugin *plugin,
if (!launch_name)
return TRUE;
- if (strcmp (launch_name, gs_app_get_id (app)) == 0)
+ if (g_strcmp0 (launch_name, gs_app_get_id (app)) == 0)
binary_name = g_strdup_printf ("/snap/bin/%s", launch_name);
else
binary_name = g_strdup_printf ("/snap/bin/%s.%s", gs_app_get_id (app), launch_name);
diff --git a/src/plugins/gs-snapd.c b/src/plugins/gs-snapd.c
index 0a1d042..7e561a2 100644
--- a/src/plugins/gs-snapd.c
+++ b/src/plugins/gs-snapd.c
@@ -436,12 +436,10 @@ gs_snapd_list (GCancellable *cancellable, GError **error)
}
JsonArray *
-gs_snapd_find (gchar **values,
+gs_snapd_find (const gchar *section, gboolean match_name, const gchar *query,
GCancellable *cancellable, GError **error)
{
g_autoptr(GString) path = NULL;
- g_autofree gchar *query = NULL;
- g_autofree gchar *escaped = NULL;
guint status_code;
g_autofree gchar *reason_phrase = NULL;
g_autofree gchar *response_type = NULL;
@@ -450,60 +448,23 @@ gs_snapd_find (gchar **values,
JsonObject *root;
JsonArray *result;
- path = g_string_new ("/v2/find?q=");
- query = g_strjoinv (" ", values);
- escaped = soup_uri_encode (query, NULL);
- g_string_append (path, escaped);
- if (!send_request ("GET", path->str, NULL,
- TRUE, NULL, NULL,
- TRUE, NULL, NULL,
- &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;
+ path = g_string_new ("/v2/find?");
+ if (section != NULL) {
+ g_string_append_printf (path, "section=%s", section);
}
+ if (query != NULL) {
+ g_autofree gchar *escaped = 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;
+ escaped = soup_uri_encode (query, NULL);
+ if (section != NULL)
+ g_string_append (path, "&");
+ if (match_name)
+ g_string_append (path, "name=");
+ else
+ g_string_append (path, "q=");
+ g_string_append (path, escaped);
}
-
- return json_array_ref (result);
-}
-
-JsonArray *
-gs_snapd_find_name (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,
+ if (!send_request ("GET", path->str, NULL,
TRUE, NULL, NULL,
TRUE, NULL, NULL,
&status_code, &reason_phrase,
@@ -752,3 +713,99 @@ gs_snapd_get_resource (const gchar *path,
return g_steal_pointer (&data);
}
+
+static gboolean
+parse_date (const gchar *date_string, gint *year, gint *month, gint *day)
+{
+ /* Example: 2016-05-17 */
+ if (strchr (date_string, '-') != NULL) {
+ g_auto(GStrv) tokens = NULL;
+
+ tokens = g_strsplit (date_string, "-", -1);
+ if (g_strv_length (tokens) != 3)
+ return FALSE;
+
+ *year = atoi (tokens[0]);
+ *month = atoi (tokens[1]);
+ *day = atoi (tokens[2]);
+
+ return TRUE;
+ }
+ /* Example: 20160517 */
+ else if (strlen (date_string) == 8) {
+ // FIXME: Implement
+ return FALSE;
+ }
+ else
+ return FALSE;
+}
+
+static gboolean
+parse_time (const gchar *time_string, gint *hour, gint *minute, gdouble *seconds)
+{
+ /* Example: 09:36:53.682 or 09:36:53 or 09:36 */
+ if (strchr (time_string, ':') != NULL) {
+ g_auto(GStrv) tokens = NULL;
+
+ tokens = g_strsplit (time_string, ":", 3);
+ *hour = atoi (tokens[0]);
+ if (tokens[1] == NULL)
+ return FALSE;
+ *minute = atoi (tokens[1]);
+ if (tokens[2] != NULL)
+ *seconds = g_ascii_strtod (tokens[2], NULL);
+ else
+ *seconds = 0.0;
+
+ return TRUE;
+ }
+ /* Example: 093653.682 or 093653 or 0936 */
+ else {
+ // FIXME: Implement
+ return FALSE;
+ }
+}
+
+static gboolean
+is_timezone_prefix (gchar c)
+{
+ return c == '+' || c == '-' || c == 'Z';
+}
+
+GDateTime *
+gs_snapd_parse_date (const gchar *value)
+{
+ g_auto(GStrv) tokens = NULL;
+ g_autoptr(GTimeZone) timezone = NULL;
+ gint year = 0, month = 0, day = 0, hour = 0, minute = 0;
+ gdouble seconds = 0.0;
+
+ if (value == NULL)
+ return NULL;
+
+ /* Example: 2016-05-17T09:36:53+12:00 */
+ tokens = g_strsplit (value, "T", 2);
+ if (!parse_date (tokens[0], &year, &month, &day))
+ return NULL;
+ if (tokens[1] != NULL) {
+ gchar *timezone_start;
+
+ /* Timezone is either Z (UTC) +hh:mm or -hh:mm */
+ timezone_start = tokens[1];
+ while (*timezone_start != '\0' && !is_timezone_prefix (*timezone_start))
+ timezone_start++;
+ if (*timezone_start != '\0')
+ timezone = g_time_zone_new (timezone_start);
+
+ /* Strip off timezone */
+ *timezone_start = '\0';
+
+ if (!parse_time (tokens[1], &hour, &minute, &seconds))
+ return NULL;
+ }
+
+ if (timezone == NULL)
+ timezone = g_time_zone_new_local ();
+
+ return g_date_time_new (timezone, year, month, day, hour, minute, seconds);
+}
diff --git a/src/plugins/gs-snapd.h b/src/plugins/gs-snapd.h
index 366af72..9017f93 100644
--- a/src/plugins/gs-snapd.h
+++ b/src/plugins/gs-snapd.h
@@ -36,11 +36,9 @@ JsonObject *gs_snapd_list_one (const gchar *name,
JsonArray *gs_snapd_list (GCancellable *cancellable,
GError **error);
-JsonArray *gs_snapd_find (gchar **values,
- GCancellable *cancellable,
- GError **error);
-
-JsonArray *gs_snapd_find_name (const gchar *name,
+JsonArray *gs_snapd_find (const gchar *section,
+ gboolean match_name,
+ const gchar *query,
GCancellable *cancellable,
GError **error);
@@ -64,4 +62,6 @@ gchar *gs_snapd_get_resource (const gchar *path,
GCancellable *cancellable,
GError **error);
+GDateTime *gs_snapd_parse_date (const gchar *value);
+
#endif /* __GS_SNAPD_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]