[gnome-software/gnome-3-24] snap: Backport fixes from master
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/gnome-3-24] snap: Backport fixes from master
- Date: Thu, 15 Jun 2017 04:50:21 +0000 (UTC)
commit 60730fa88aab4be392885e0d0e9c5000192c6bb1
Author: Robert Ancell <robert ancell canonical com>
Date: Thu Jun 15 16:48:56 2017 +1200
snap: Backport fixes from master
Fixes the following:
- Refactor refining code to be more efficient and reliable
- Cache icons
- Mark snaps as sandboxed
- Set screenshot dimensions
- Set install date
- Set developer name
- Use gs_plugin_add_popular to show featured snaps
plugins/snap/gs-plugin-snap.c | 484 ++++++++++++++++++++++++++---------------
plugins/snap/gs-snapd.c | 167 ++++++++++-----
plugins/snap/gs-snapd.h | 12 +-
3 files changed, 427 insertions(+), 236 deletions(-)
---
diff --git a/plugins/snap/gs-plugin-snap.c b/plugins/snap/gs-plugin-snap.c
index bda2554..2578a83 100644
--- a/plugins/snap/gs-plugin-snap.c
+++ b/plugins/snap/gs-plugin-snap.c
@@ -29,6 +29,7 @@
struct GsPluginData {
GsAuth *auth;
+ GHashTable *store_snaps;
};
void
@@ -42,6 +43,9 @@ gs_plugin_initialize (GsPlugin *plugin)
gs_plugin_set_enabled (plugin, FALSE);
}
+ priv->store_snaps = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) json_object_unref);
+
priv->auth = gs_auth_new ("snapd");
gs_auth_set_provider_name (priv->auth, "Snap Store");
gs_auth_set_provider_schema (priv->auth, "com.ubuntu.UbuntuOne.GnomeSoftware");
@@ -100,137 +104,77 @@ get_macaroon (GsPlugin *plugin, gchar **macaroon, gchar ***discharges)
g_variant_get (macaroon_variant, "(s^as)", macaroon, discharges);
}
-static void
-refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_search, GCancellable
*cancellable)
-{
- g_autofree gchar *macaroon = NULL;
- g_auto(GStrv) discharges = NULL;
- const gchar *status, *confinement, *icon_url, *launch_name = NULL;
- g_autoptr(GdkPixbuf) icon_pixbuf = NULL;
- gint64 size = -1;
- get_macaroon (plugin, &macaroon, &discharges);
+static gboolean
+gs_plugin_snap_set_app_pixbuf_from_data (GsApp *app, const gchar *buf, gsize count, GError **error)
+{
+ g_autoptr(GdkPixbufLoader) loader = NULL;
+ g_autoptr(GError) error_local = NULL;
- gs_app_set_kind (app, AS_APP_KIND_DESKTOP);
- 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);
- }
- } else if (g_strcmp0 (status, "not installed") == 0 || g_strcmp0 (status, "available") == 0) {
- gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
- }
- 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 (json_object_has_member (package, "installed-size")) {
- size = json_object_get_int_member (package, "installed-size");
- if (size > 0)
- gs_app_set_size_installed (app, (guint64) size);
- }
- if (json_object_has_member (package, "download-size")) {
- size = json_object_get_int_member (package, "download-size");
- if (size > 0)
- gs_app_set_size_download (app, (guint64) size);
+ 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;
}
- if (gs_plugin_check_distro_id (plugin, "ubuntu"))
- gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE);
-
- confinement = json_object_get_string_member (package, "confinement");
- if (g_strcmp0 (confinement, "strict") == 0)
- gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED);
-
- 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 (macaroon, discharges, 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 (gs_plugin_get_soup_session (plugin), message);
- loader = gdk_pixbuf_loader_new ();
- gdk_pixbuf_loader_write (loader,
- (guint8 *) message->response_body->data,
- (gsize) 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)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ g_autofree gchar *macaroon = NULL;
+ g_auto(GStrv) discharges = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
+ guint i;
- if (icon_pixbuf) {
- gs_app_set_pixbuf (app, icon_pixbuf);
- } else {
- 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_add_icon (app, icon);
+ get_macaroon (plugin, &macaroon, &discharges);
+ snaps = gs_snapd_find (macaroon, discharges, section, match_name, query, cancellable, error);
+ if (snaps == NULL)
+ return NULL;
+
+ /* 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 (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_scope (app, AS_APP_SCOPE_SYSTEM);
+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_SNAP);
+ 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"));
+ if (gs_plugin_check_distro_id (plugin, "ubuntu"))
+ gs_app_add_quirk (app, AS_APP_QUIRK_PROVENANCE);
+ if (g_strcmp0 (json_object_get_string_member (snap, "confinement"), "strict") == 0)
+ gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED);
- 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
@@ -241,8 +185,6 @@ gs_plugin_url_to_app (GsPlugin *plugin,
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;
@@ -255,19 +197,12 @@ gs_plugin_url_to_app (GsPlugin *plugin,
/* create app */
path = gs_utils_get_url_path (url);
- get_macaroon (plugin, &macaroon, &discharges);
- snaps = gs_snapd_find_name (macaroon, discharges, 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_scope (app, AS_APP_SCOPE_SYSTEM);
- gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_SNAP);
- 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);
+ gs_app_list_add (list, snap_to_app (plugin, snap));
return TRUE;
}
@@ -277,6 +212,28 @@ gs_plugin_destroy (GsPlugin *plugin)
{
GsPluginData *priv = gs_plugin_get_data (plugin);
g_clear_object (&priv->auth);
+ g_hash_table_unref (priv->store_snaps);
+}
+
+gboolean
+gs_plugin_add_popular (GsPlugin *plugin,
+ GsAppList *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_app_list_add (list, snap_to_app (plugin, snap));
+ }
+
+ return TRUE;
}
gboolean
@@ -287,32 +244,23 @@ gs_plugin_add_installed (GsPlugin *plugin,
{
g_autofree gchar *macaroon = NULL;
g_auto(GStrv) discharges = NULL;
- g_autoptr(JsonArray) result = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
guint i;
get_macaroon (plugin, &macaroon, &discharges);
- result = gs_snapd_list (macaroon, discharges, cancellable, error);
- if (result == NULL)
+ snaps = gs_snapd_list (macaroon, discharges, 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;
- /* create a unique ID for deduplication, TODO: branch? */
- name = json_object_get_string_member (package, "name");
- app = gs_app_new (name);
- gs_app_set_scope (app, AS_APP_SCOPE_SYSTEM);
- gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_SNAP);
- gs_app_set_management_plugin (app, "snap");
- gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
- refine_app (plugin, app, package, TRUE, cancellable);
- gs_app_list_add (list, app);
+ gs_app_list_add (list, snap_to_app (plugin, snap));
}
return TRUE;
@@ -325,35 +273,127 @@ gs_plugin_add_search (GsPlugin *plugin,
GCancellable *cancellable,
GError **error)
{
- g_autofree gchar *macaroon = NULL;
- g_auto(GStrv) discharges = NULL;
- g_autoptr(JsonArray) result = NULL;
+ g_autofree gchar *query = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
guint i;
- get_macaroon (plugin, &macaroon, &discharges);
- result = gs_snapd_find (macaroon, discharges, 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;
-
- /* create a unique ID for deduplication, TODO: branch? */
- name = json_object_get_string_member (package, "name");
- app = gs_app_new (name);
- gs_app_set_scope (app, AS_APP_SCOPE_SYSTEM);
- gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_SNAP);
- gs_app_set_management_plugin (app, "snap");
- gs_app_add_quirk (app, AS_APP_QUIRK_NOT_REVIEWABLE);
- refine_app (plugin, app, package, TRUE, cancellable);
- gs_app_list_add (list, app);
+ for (i = 0; i < json_array_get_length (snaps); i++) {
+ JsonObject *snap = json_array_get_object_element (snaps, i);
+ gs_app_list_add (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_add_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 *macaroon = NULL;
+ g_auto(GStrv) discharges = NULL;
+ g_autofree gchar *icon_data = NULL;
+ gsize icon_data_length;
+
+ get_macaroon (plugin, &macaroon, &discharges);
+ icon_data = gs_snapd_get_resource (macaroon, discharges, 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_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_fn = gs_utils_get_cache_filename ("snap-icons", basename,
GS_UTILS_CACHE_FLAG_WRITEABLE, error);
+ 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 (gs_plugin_get_soup_session (plugin), 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)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ JsonObject *snap = NULL;
+ g_autoptr(JsonArray) snaps = NULL;
+
+ /* use cached version if available */
+ snap = g_hash_table_lookup (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));
+}
+
gboolean
gs_plugin_refine_app (GsPlugin *plugin,
GsApp *app,
@@ -363,8 +403,9 @@ gs_plugin_refine_app (GsPlugin *plugin,
{
g_autofree gchar *macaroon = NULL;
g_auto(GStrv) discharges = NULL;
- g_autoptr(JsonObject) result = NULL;
- const gchar *id;
+ 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)
@@ -375,10 +416,105 @@ gs_plugin_refine_app (GsPlugin *plugin,
id = gs_app_get_id (app);
if (id == NULL)
id = gs_app_get_source_default (app);
- result = gs_snapd_list_one (macaroon, discharges, id, cancellable, error);
- if (result == NULL)
- return FALSE;
- refine_app (plugin, app, result, FALSE, cancellable);
+
+ /* get information from installed snaps */
+ local_snap = gs_snapd_list_one (macaroon, discharges, 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_installed (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));
+ gs_app_set_developer_name (app, json_object_get_string_member (local_snap, "developer"));
+ 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) {
+ const gchar *screenshot_url = 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 (json_object_has_member (store_snap, "download-size"))
+ gs_app_set_size_download (app, json_object_get_int_member (store_snap,
"download-size"));
+ gs_app_set_developer_name (app, json_object_get_string_member (store_snap, "developer"));
+ 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);
+
+ /* fall back to the screenshot */
+ if (screenshot_url == NULL)
+ screenshot_url = json_object_get_string_member (screenshot, "url");
+ }
+ }
+
+ /* use some heuristics to guess the application origin */
+ if (gs_app_get_origin_hostname (app) == NULL) {
+ if (icon_url != NULL && !g_str_has_prefix (icon_url, "/"))
+ gs_app_set_origin_hostname (app, icon_url);
+ else if (screenshot_url != NULL)
+ gs_app_set_origin_hostname (app, screenshot_url);
+ }
+ }
+
+ /* load icon if requested */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON) {
+ if (!load_icon (plugin, app, icon_url, cancellable, error))
+ return FALSE;
+ }
return TRUE;
}
diff --git a/plugins/snap/gs-snapd.c b/plugins/snap/gs-snapd.c
index a6c4307..0b3ffe9 100644
--- a/plugins/snap/gs-snapd.c
+++ b/plugins/snap/gs-snapd.c
@@ -430,12 +430,10 @@ gs_snapd_list (const gchar *macaroon, gchar **discharges,
JsonArray *
gs_snapd_find (const gchar *macaroon, gchar **discharges,
- gchar **values,
+ const gchar *section, gboolean match_name, 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;
@@ -444,60 +442,23 @@ gs_snapd_find (const gchar *macaroon, gchar **discharges,
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,
- 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;
+ path = g_string_new ("/v2/find?");
+ if (section != NULL) {
+ g_string_append_printf (path, "section=%s", section);
}
-
- 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;
+ if (query != NULL) {
+ g_autofree gchar *escaped = 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 *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,
+ if (!send_request ("GET", path->str, NULL,
macaroon, discharges,
&status_code, &reason_phrase,
&response_type, &response, NULL,
@@ -752,3 +713,99 @@ gs_snapd_get_resource (const gchar *macaroon, gchar **discharges,
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/plugins/snap/gs-snapd.h b/plugins/snap/gs-snapd.h
index fb038a2..dc78db3 100644
--- a/plugins/snap/gs-snapd.h
+++ b/plugins/snap/gs-snapd.h
@@ -42,13 +42,9 @@ JsonArray *gs_snapd_list (const gchar *macaroon,
JsonArray *gs_snapd_find (const gchar *macaroon,
gchar **discharges,
- gchar **values,
- GCancellable *cancellable,
- GError **error);
-
-JsonArray *gs_snapd_find_name (const gchar *macaroon,
- gchar **discharges,
- const gchar *name,
+ const gchar *section,
+ gboolean match_name,
+ gchar *query,
GCancellable *cancellable,
GError **error);
@@ -80,4 +76,6 @@ gchar *gs_snapd_get_resource (const gchar *macaroon,
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]