[gnome-software/wip/ubuntu-xenial] Refactor snapd code
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/ubuntu-xenial] Refactor snapd code
- Date: Mon, 12 Sep 2016 04:34:05 +0000 (UTC)
commit 6e23f9c1179adefff53b5e4291f983e2b99977c9
Author: Robert Ancell <robert ancell canonical com>
Date: Mon Sep 12 16:32:52 2016 +1200
Refactor snapd code
src/plugins/gs-plugin-snap.c | 438 ++++++++++--------------------------------
src/plugins/gs-snapd.c | 372 +++++++++++++++++++++++++++++++----
src/plugins/gs-snapd.h | 50 ++++--
3 files changed, 466 insertions(+), 394 deletions(-)
---
diff --git a/src/plugins/gs-plugin-snap.c b/src/plugins/gs-plugin-snap.c
index 0b37484..9c1c452 100644
--- a/src/plugins/gs-plugin-snap.c
+++ b/src/plugins/gs-plugin-snap.c
@@ -70,47 +70,6 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
return TRUE;
}
-static JsonParser *
-parse_result (const gchar *response, const gchar *response_type, GError **error)
-{
- g_autoptr(JsonParser) parser = NULL;
- g_autoptr(GError) error_local = NULL;
-
- if (response_type == NULL) {
- g_set_error_literal (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd returned no content type");
- return NULL;
- }
- if (g_strcmp0 (response_type, "application/json") != 0) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd returned unexpected content type %s", response_type);
- return NULL;
- }
-
- parser = json_parser_new ();
- if (!json_parser_load_from_data (parser, response, -1, &error_local)) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "Unable to parse snapd response: %s",
- error_local->message);
- return NULL;
- }
- if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) {
- g_set_error_literal (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd response does is not a valid JSON object");
- return NULL;
- }
-
- return g_object_ref (parser);
-}
-
static void
get_macaroon (GsPlugin *plugin, gchar **macaroon, gchar ***discharges)
{
@@ -184,26 +143,24 @@ refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_sea
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_response = NULL;
- gsize icon_response_length;
-
- if (gs_snapd_request ("GET", icon_url, NULL,
- macaroon, discharges,
- NULL, NULL,
- NULL, &icon_response, &icon_response_length,
- cancellable, NULL)) {
+ 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_response,
- icon_response_length,
+ (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\n");
+ g_printerr ("Failed to get icon: %s\n", error->message);
}
else {
g_autoptr(SoupMessage) message = NULL;
@@ -247,192 +204,85 @@ refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_sea
}
}
-static gboolean
-get_apps (GsPlugin *plugin,
- const gchar *sources,
- gchar **search_terms,
- GList **list,
- AppFilterFunc filter_func,
- gpointer user_data,
- GCancellable *cancellable,
- GError **error)
+void
+gs_plugin_destroy (GsPlugin *plugin)
+{
+ g_clear_object (&plugin->priv->auth);
+}
+
+gboolean
+gs_plugin_add_installed (GsPlugin *plugin,
+ GList **list,
+ GCancellable *cancellable,
+ GError **error)
{
g_autofree gchar *macaroon = NULL;
g_auto(GStrv) discharges = NULL;
- guint status_code;
- GPtrArray *query_fields;
- g_autoptr (GString) path = NULL;
- g_autofree gchar *reason_phrase = NULL, *response_type = NULL, *response = NULL;
- g_autoptr(JsonParser) parser = NULL;
- JsonObject *root;
- JsonArray *result;
- GList *snaps;
- GList *l;
+ g_autoptr(JsonArray) result = NULL;
+ guint i;
get_macaroon (plugin, &macaroon, &discharges);
-
- /* Get all the apps */
- query_fields = g_ptr_array_new_with_free_func (g_free);
- if (sources != NULL) {
- g_autofree gchar *escaped;
- escaped = soup_uri_encode (sources, NULL);
- g_ptr_array_add (query_fields, g_strdup_printf ("sources=%s", escaped));
- }
- if (search_terms != NULL && search_terms[0] != NULL) {
- g_autoptr (GString) query = NULL;
- g_autofree gchar *escaped = NULL;
- gint i;
-
- query = g_string_new ("q=");
- escaped = soup_uri_encode (search_terms[0], NULL);
- g_string_append (query, escaped);
- for (i = 1; search_terms[i] != NULL; i++) {
- g_autofree gchar *e = soup_uri_encode (search_terms[0], NULL);
- g_string_append_printf (query, "+%s", e);
- }
- g_ptr_array_add (query_fields, g_strdup (query->str));
- path = g_string_new ("/v2/find");
- }
- else
- path = g_string_new ("/v2/snaps");
- g_ptr_array_add (query_fields, NULL);
- if (query_fields->len > 1) {
- g_autofree gchar *fields = NULL;
- g_string_append (path, "?");
- fields = g_strjoinv ("&", (gchar **) query_fields->pdata);
- g_string_append (path, fields);
- }
- g_ptr_array_free (query_fields, TRUE);
- if (!gs_snapd_request ("GET", path->str, NULL,
- macaroon, discharges,
- &status_code, &reason_phrase,
- &response_type, &response, NULL,
- cancellable, error))
- return FALSE;
-
- if (status_code != SOUP_STATUS_OK) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd returned status code %d: %s",
- status_code, reason_phrase);
+ result = gs_snapd_list (macaroon, discharges, cancellable, error);
+ if (result == NULL)
return FALSE;
- }
- parser = parse_result (response, response_type, error);
- if (parser == NULL)
- return FALSE;
-
- root = json_node_get_object (json_parser_get_root (parser));
- result = json_object_get_array_member (root, "result");
- snaps = json_array_get_elements (result);
-
- for (l = snaps; l != NULL; l = l->next) {
- JsonObject *package = json_node_get_object (l->data);
+ 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 *id;
-
- id = json_object_get_string_member (package, "name");
+ const gchar *status, *name;
- if (filter_func != NULL && !filter_func (id, package, user_data))
+ status = json_object_get_string_member (package, "status");
+ if (g_strcmp0 (status, "active") != 0)
continue;
- app = gs_app_new (id);
+ 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, _("Snap Store"));
+ gs_app_set_origin (app, _("Snap 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);
}
- g_list_free (snaps);
-
return TRUE;
}
-static gboolean
-get_app (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error)
+gboolean
+gs_plugin_add_search (GsPlugin *plugin,
+ gchar **values,
+ GList **list,
+ GCancellable *cancellable,
+ GError **error)
{
g_autofree gchar *macaroon = NULL;
g_auto(GStrv) discharges = NULL;
- guint status_code;
- g_autofree gchar *path = NULL;
- g_autofree gchar *reason_phrase = NULL;
- g_autofree gchar *response = NULL;
- g_autofree gchar *response_type = NULL;
- g_autoptr(JsonParser) parser = NULL;
- JsonObject *root, *result;
+ g_autoptr(JsonArray) result = NULL;
+ guint i;
get_macaroon (plugin, &macaroon, &discharges);
-
- path = g_strdup_printf ("/v2/snaps/%s", gs_app_get_id (app));
- if (!gs_snapd_request ("GET", path, NULL,
- macaroon, discharges,
- &status_code, &reason_phrase,
- &response_type, &response, NULL,
- cancellable, error))
+ result = gs_snapd_find (macaroon, discharges, values, cancellable, error);
+ if (result == NULL)
return FALSE;
- if (status_code != SOUP_STATUS_OK) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd returned status code %d: %s",
- status_code, reason_phrase);
- 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;
- parser = parse_result (response, response_type, error);
- if (parser == NULL)
- return FALSE;
- root = json_node_get_object (json_parser_get_root (parser));
- result = json_object_get_object_member (root, "result");
- if (result == NULL) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd returned no results for %s", gs_app_get_id (app));
- return FALSE;
+ 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, _("Snap 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);
}
- refine_app (plugin, app, result, FALSE, cancellable);
-
return TRUE;
}
-void
-gs_plugin_destroy (GsPlugin *plugin)
-{
- g_clear_object (&plugin->priv->auth);
-}
-
-static gboolean
-is_active (const gchar *id, JsonObject *object, gpointer data)
-{
- const gchar *status = json_object_get_string_member (object, "status");
- return g_strcmp0 (status, "active") == 0;
-}
-
-gboolean
-gs_plugin_add_installed (GsPlugin *plugin,
- GList **list,
- GCancellable *cancellable,
- GError **error)
-{
- return get_apps (plugin, "local", NULL, list, is_active, NULL, cancellable, error);
-}
-
-gboolean
-gs_plugin_add_search (GsPlugin *plugin,
- gchar **values,
- GList **list,
- GCancellable *cancellable,
- GError **error)
-{
- return get_apps (plugin, NULL, values, list, NULL, values, cancellable, error);
-}
-
gboolean
gs_plugin_refine (GsPlugin *plugin,
GList **list,
@@ -440,156 +290,61 @@ gs_plugin_refine (GsPlugin *plugin,
GCancellable *cancellable,
GError **error)
{
+ g_autofree gchar *macaroon = NULL;
+ g_auto(GStrv) discharges = NULL;
GList *link;
+ get_macaroon (plugin, &macaroon, &discharges);
+
for (link = *list; link; link = link->next) {
GsApp *app = link->data;
+ g_autoptr(JsonObject) result = NULL;
if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
continue;
- // Get info from snapd
- if (!get_app (plugin, app, cancellable, error))
+ result = gs_snapd_list_one (macaroon, discharges, gs_app_get_id (app), cancellable, error);
+ if (result == NULL)
return FALSE;
+ refine_app (plugin, app, result, FALSE, cancellable);
}
return TRUE;
}
-static gboolean
-send_package_action (GsPlugin *plugin,
- GsApp *app,
- const gchar *id,
- const gchar *action,
- GCancellable *cancellable,
- GError **error)
+typedef struct
{
- g_autofree gchar *macaroon = NULL;
- g_auto(GStrv) discharges = NULL;
- g_autofree gchar *content = NULL, *path = NULL;
- guint status_code;
- g_autofree gchar *reason_phrase = NULL;
- g_autofree gchar *response_type = NULL;
- g_autofree gchar *response = NULL;
- g_autofree gchar *status = NULL;
- g_autoptr(JsonParser) parser = NULL;
- JsonObject *root, *result, *task, *progress;
+ GsPlugin *plugin;
+ GsApp *app;
+} ProgressData;
+
+static void
+progress_cb (JsonObject *result, gpointer user_data)
+{
+ ProgressData *data = user_data;
JsonArray *tasks;
GList *task_list, *l;
- gint64 done, total, task_done, task_total;
- const gchar *resource_path;
- const gchar *type;
- const gchar *change_id;
-
- get_macaroon (plugin, &macaroon, &discharges);
-
- content = g_strdup_printf ("{\"action\": \"%s\"}", action);
- path = g_strdup_printf ("/v2/snaps/%s", id);
- if (!gs_snapd_request ("POST", path, content,
- macaroon, discharges,
- &status_code, &reason_phrase,
- &response_type, &response, NULL,
- cancellable, error))
- return FALSE;
-
- if (status_code == SOUP_STATUS_UNAUTHORIZED) {
- g_set_error_literal (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_AUTH_REQUIRED,
- "Requires authentication with @snapd");
- return FALSE;
- }
+ gint64 done = 0, total = 0;
- if (status_code != SOUP_STATUS_ACCEPTED) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd returned status code %d: %s",
- status_code, reason_phrase);
- return FALSE;
- }
-
- parser = parse_result (response, response_type, error);
- if (parser == NULL)
- return FALSE;
-
- root = json_node_get_object (json_parser_get_root (parser));
- type = json_object_get_string_member (root, "type");
-
- if (g_strcmp0 (type, "async") == 0) {
- change_id = json_object_get_string_member (root, "change");
- resource_path = g_strdup_printf ("/v2/changes/%s", change_id);
-
- while (TRUE) {
- g_autofree gchar *status_reason_phrase = NULL;
- g_autofree gchar *status_response_type = NULL;
- g_autofree gchar *status_response = NULL;
- g_autoptr(JsonParser) status_parser = NULL;
-
- /* Wait for a little bit before polling */
- g_usleep (100 * 1000);
-
- if (!gs_snapd_request ("GET", resource_path, NULL,
- macaroon, discharges,
- &status_code, &status_reason_phrase,
- &status_response_type, &status_response, NULL,
- cancellable, error)) {
- return FALSE;
- }
-
- if (status_code != SOUP_STATUS_OK) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd returned status code %d: %s",
- status_code, status_reason_phrase);
- return FALSE;
- }
-
- status_parser = parse_result (status_response, status_response_type, error);
- if (status_parser == NULL)
- return FALSE;
-
- root = json_node_get_object (json_parser_get_root (status_parser));
- result = json_object_get_object_member (root, "result");
-
- g_free (status);
- status = g_strdup (json_object_get_string_member (result, "status"));
-
- if (g_strcmp0 (status, "Done") == 0)
- break;
-
- tasks = json_object_get_array_member (result, "tasks");
- task_list = json_array_get_elements (tasks);
+ tasks = json_object_get_array_member (result, "tasks");
+ task_list = json_array_get_elements (tasks);
- done = 0;
- total = 0;
+ for (l = task_list; l != NULL; l = l->next) {
+ JsonObject *task, *progress;
+ gint64 task_done, task_total;
- for (l = task_list; l != NULL; l = l->next) {
- task = json_node_get_object (l->data);
- progress = json_object_get_object_member (task, "progress");
- task_done = json_object_get_int_member (progress, "done");
- task_total = json_object_get_int_member (progress, "total");
+ task = json_node_get_object (l->data);
+ progress = json_object_get_object_member (task, "progress");
+ task_done = json_object_get_int_member (progress, "done");
+ task_total = json_object_get_int_member (progress, "total");
- done += task_done;
- total += task_total;
- }
-
- gs_plugin_progress_update (plugin, app, 100 * done / total);
-
- g_list_free (task_list);
- }
+ done += task_done;
+ total += task_total;
}
- if (g_strcmp0 (status, "Done") != 0) {
- g_set_error (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd operation finished with status %s", status);
- return FALSE;
- }
+ gs_plugin_progress_update (data->plugin, data->app, 100 * done / total);
- return TRUE;
+ g_list_free (task_list);
}
gboolean
@@ -598,15 +353,21 @@ gs_plugin_app_install (GsPlugin *plugin,
GCancellable *cancellable,
GError **error)
{
- gboolean ret;
+ g_autofree gchar *macaroon = NULL;
+ g_auto(GStrv) discharges = NULL;
+ ProgressData data;
/* We can only install apps we know of */
if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
return TRUE;
+ get_macaroon (plugin, &macaroon, &discharges);
+
gs_app_set_state (app, AS_APP_STATE_INSTALLING);
- ret = send_package_action (plugin, app, gs_app_get_id (app), "install", cancellable, error);
- if (!ret) {
+ get_macaroon (plugin, &macaroon, &discharges);
+ data.plugin = plugin;
+ data.app = app;
+ if (!gs_snapd_install (macaroon, discharges, gs_app_get_id (app), progress_cb, &data, cancellable,
error)) {
gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
return FALSE;
}
@@ -652,15 +413,20 @@ gs_plugin_app_remove (GsPlugin *plugin,
GCancellable *cancellable,
GError **error)
{
- gboolean ret;
+ g_autofree gchar *macaroon = NULL;
+ g_auto(GStrv) discharges = NULL;
+ ProgressData data;
/* We can only remove apps we know of */
if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
return TRUE;
+ get_macaroon (plugin, &macaroon, &discharges);
+
gs_app_set_state (app, AS_APP_STATE_REMOVING);
- ret = send_package_action (plugin, app, gs_app_get_id (app), "remove", cancellable, error);
- if (!ret) {
+ data.plugin = plugin;
+ data.app = app;
+ if (!gs_snapd_remove (macaroon, discharges, gs_app_get_id (app), progress_cb, &data, cancellable,
error)) {
gs_app_set_state (app, AS_APP_STATE_INSTALLED);
return FALSE;
}
diff --git a/src/plugins/gs-snapd.c b/src/plugins/gs-snapd.c
index e158fc9..be7b2e1 100644
--- a/src/plugins/gs-snapd.c
+++ b/src/plugins/gs-snapd.c
@@ -91,19 +91,19 @@ read_from_snapd (GSocket *socket,
return TRUE;
}
-gboolean
-gs_snapd_request (const gchar *method,
- const gchar *path,
- const gchar *content,
- const gchar *macaroon,
- gchar **discharges,
- guint *status_code,
- gchar **reason_phrase,
- gchar **response_type,
- gchar **response,
- gsize *response_length,
- GCancellable *cancellable,
- GError **error)
+static gboolean
+send_request (const gchar *method,
+ const gchar *path,
+ const gchar *content,
+ const gchar *macaroon,
+ gchar **discharges,
+ guint *status_code,
+ gchar **reason_phrase,
+ gchar **response_type,
+ gchar **response,
+ gsize *response_length,
+ GCancellable *cancellable,
+ GError **error)
{
g_autoptr (GSocket) socket = NULL;
g_autoptr (GString) request = NULL;
@@ -302,29 +302,25 @@ gs_snapd_request (const gchar *method,
return TRUE;
}
-gboolean
-gs_snapd_parse_result (const gchar *response_type,
- const gchar *response,
- JsonObject **result,
- GError **error)
+static JsonParser *
+parse_result (const gchar *response, const gchar *response_type, GError **error)
{
g_autoptr(JsonParser) parser = NULL;
g_autoptr(GError) error_local = NULL;
- JsonObject *root;
if (response_type == NULL) {
g_set_error_literal (error,
GS_PLUGIN_ERROR,
GS_PLUGIN_ERROR_FAILED,
"snapd returned no content type");
- return FALSE;
+ return NULL;
}
if (g_strcmp0 (response_type, "application/json") != 0) {
g_set_error (error,
GS_PLUGIN_ERROR,
GS_PLUGIN_ERROR_FAILED,
"snapd returned unexpected content type %s", response_type);
- return FALSE;
+ return NULL;
}
parser = json_parser_new ();
@@ -334,46 +330,338 @@ gs_snapd_parse_result (const gchar *response_type,
GS_PLUGIN_ERROR_FAILED,
"Unable to parse snapd response: %s",
error_local->message);
- return FALSE;
+ return NULL;
}
-
if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) {
g_set_error_literal (error,
GS_PLUGIN_ERROR,
GS_PLUGIN_ERROR_FAILED,
"snapd response does is not a valid JSON object");
- return FALSE;
+ return NULL;
+ }
+
+ return g_object_ref (parser);
+}
+
+JsonObject *
+gs_snapd_list_one (const gchar *macaroon, gchar **discharges,
+ const gchar *name,
+ GCancellable *cancellable, GError **error)
+{
+ 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, *result;
+
+ path = g_strdup_printf ("/v2/snaps/%s", name);
+ 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 %d: %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_object_member (root, "result");
+ if (result == NULL) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd returned no results for %s", name);
+ return NULL;
+ }
+
+ return json_object_ref (result);
+}
+
+JsonArray *
+gs_snapd_list (const gchar *macaroon, gchar **discharges,
+ GCancellable *cancellable, GError **error)
+{
+ 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;
+
+ if (!send_request ("GET", "/v2/snaps", 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 %d: %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);
+}
+
+JsonArray *
+gs_snapd_find (const gchar *macaroon, gchar **discharges,
+ gchar **values,
+ 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;
+ g_autofree gchar *response = NULL;
+ g_autoptr(JsonParser) parser = NULL;
+ 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 %d: %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);
+}
+
+static JsonObject *
+get_changes (const gchar *macaroon, gchar **discharges,
+ const gchar *change_id,
+ GCancellable *cancellable, GError **error)
+{
+ 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, *result;
+
+ path = g_strdup_printf ("/v2/changes/%s", change_id);
+ 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 %d: %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));
- if (!json_object_has_member (root, "result")) {
+ result = json_object_get_object_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_object_ref (result);
+}
+
+static gboolean
+send_package_action (const gchar *macaroon,
+ gchar **discharges,
+ const gchar *name,
+ const gchar *action,
+ GsSnapdProgressCallback callback,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *content = NULL, *path = NULL;
+ guint status_code;
+ g_autofree gchar *reason_phrase = NULL;
+ g_autofree gchar *response_type = NULL;
+ g_autofree gchar *response = NULL;
+ g_autofree gchar *status = NULL;
+ g_autoptr(JsonParser) parser = NULL;
+ JsonObject *root, *result;
+ const gchar *type;
+
+ content = g_strdup_printf ("{\"action\": \"%s\"}", action);
+ path = g_strdup_printf ("/v2/snaps/%s", name);
+ if (!send_request ("POST", path, content,
+ macaroon, discharges,
+ &status_code, &reason_phrase,
+ &response_type, &response, NULL,
+ cancellable, error))
+ return FALSE;
+
+ if (status_code == SOUP_STATUS_UNAUTHORIZED) {
g_set_error_literal (error,
GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "snapd response does not contain a \"result\" field");
+ GS_PLUGIN_ERROR_AUTH_REQUIRED,
+ "Requires authentication with @snapd");
+ return FALSE;
+ }
+
+ if (status_code != SOUP_STATUS_ACCEPTED) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd returned status code %d: %s",
+ status_code, reason_phrase);
+ return FALSE;
+ }
+
+ parser = parse_result (response, response_type, error);
+ if (parser == NULL)
+ return FALSE;
+
+ root = json_node_get_object (json_parser_get_root (parser));
+ type = json_object_get_string_member (root, "type");
+
+ if (g_strcmp0 (type, "async") == 0) {
+ const gchar *change_id;
+
+ change_id = json_object_get_string_member (root, "change");
+
+ while (TRUE) {
+ /* Wait for a little bit before polling */
+ g_usleep (100 * 1000);
+
+ result = get_changes (macaroon, discharges, change_id, cancellable, error);
+ if (result == NULL)
+ return FALSE;
+
+ status = g_strdup (json_object_get_string_member (result, "status"));
+
+ if (g_strcmp0 (status, "Done") == 0)
+ break;
+
+ callback (result, user_data);
+ }
+ }
+
+ if (g_strcmp0 (status, "Done") != 0) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd operation finished with status %s", status);
return FALSE;
}
- if (result != NULL)
- *result = json_object_ref (json_object_get_object_member (root, "result"));
return TRUE;
}
gboolean
-gs_snapd_parse_error (const gchar *response_type,
- const gchar *response,
- gchar **message,
- gchar **kind,
- GError **error)
+gs_snapd_install (const gchar *macaroon, gchar **discharges,
+ const gchar *name,
+ GsSnapdProgressCallback callback, gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
{
- g_autoptr(JsonObject) result = NULL;
+ return send_package_action (macaroon, discharges, name, "install", callback, user_data, cancellable,
error);
+}
- if (!gs_snapd_parse_result (response_type, response, &result, error))
- return FALSE;
+gboolean
+gs_snapd_remove (const gchar *macaroon, gchar **discharges,
+ const gchar *name,
+ GsSnapdProgressCallback callback, gpointer user_data,
+ GCancellable *cancellable, GError **error)
+{
+ return send_package_action (macaroon, discharges, name, "remove", callback, user_data, cancellable,
error);
+}
- if (message != NULL)
- *message = g_strdup (json_object_get_string_member (result, "message"));
- if (kind != NULL)
- *kind = json_object_has_member (result, "kind") ? g_strdup (json_object_get_string_member
(result, "kind")) : NULL;
+gchar *
+gs_snapd_get_resource (const gchar *macaroon, gchar **discharges,
+ const gchar *path,
+ gsize *data_length,
+ GCancellable *cancellable, GError **error)
+{
+ guint status_code;
+ g_autofree gchar *reason_phrase = NULL;
+ g_autofree gchar *response_type = NULL;
+ g_autofree gchar *data = NULL;
+
+ if (!send_request ("GET", path, NULL,
+ macaroon, discharges,
+ &status_code, &reason_phrase,
+ NULL, &data, data_length,
+ cancellable, error))
+ return NULL;
- return TRUE;
+ if (status_code != SOUP_STATUS_OK) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd returned status code %d: %s",
+ status_code, reason_phrase);
+ return NULL;
+ }
+
+ return g_steal_pointer (&data);
}
diff --git a/src/plugins/gs-snapd.h b/src/plugins/gs-snapd.h
index 419253f..90a2c7d 100644
--- a/src/plugins/gs-snapd.h
+++ b/src/plugins/gs-snapd.h
@@ -25,30 +25,48 @@
#include <gio/gio.h>
#include <json-glib/json-glib.h>
+typedef void (*GsSnapdProgressCallback) (JsonObject *object, gpointer user_data);
+
gboolean gs_snapd_exists (void);
-gboolean gs_snapd_request (const gchar *method,
- const gchar *path,
- const gchar *content,
- const gchar *macaroon,
+JsonObject *gs_snapd_list_one (const gchar *macaroon,
+ gchar **discharges,
+ const gchar *name,
+ GCancellable *cancellable,
+ GError **error);
+
+JsonArray *gs_snapd_list (const gchar *macaroon,
gchar **discharges,
- guint *status_code,
- gchar **reason_phrase,
- gchar **response_type,
- gchar **response,
- gsize *response_length,
GCancellable *cancellable,
GError **error);
-gboolean gs_snapd_parse_result (const gchar *response_type,
- const gchar *response,
- JsonObject **result,
+JsonArray *gs_snapd_find (const gchar *macaroon,
+ gchar **discharges,
+ gchar **values,
+ GCancellable *cancellable,
GError **error);
-gboolean gs_snapd_parse_error (const gchar *response_type,
- const gchar *response,
- gchar **message,
- gchar **kind,
+gboolean gs_snapd_install (const gchar *macaroon,
+ gchar **discharges,
+ const gchar *name,
+ GsSnapdProgressCallback callback,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean gs_snapd_remove (const gchar *macaroon,
+ gchar **discharges,
+ const gchar *name,
+ GsSnapdProgressCallback callback,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
+
+gchar *gs_snapd_get_resource (const gchar *macaroon,
+ gchar **discharges,
+ const gchar *path,
+ gsize *data_length,
+ GCancellable *cancellable,
GError **error);
#endif /* __GS_SNAPD_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]