[gnome-software/wip/ubuntu] Refactor all snapd methods into gs-snapd.c



commit 9d5768d1eee4d35d8599eb5cb54513d18b583428
Author: Robert Ancell <robert ancell canonical com>
Date:   Fri Sep 30 12:39:22 2016 +1300

    Refactor all snapd methods into gs-snapd.c

 src/plugins/gs-plugin-snap.c      |  297 ++++-------------------
 src/plugins/gs-snapd.c            |  473 ++++++++++++++++++++++++++++++++++---
 src/plugins/gs-snapd.h            |   53 +++--
 src/plugins/gs-ubuntuone-dialog.c |   65 +-----
 4 files changed, 542 insertions(+), 346 deletions(-)
---
diff --git a/src/plugins/gs-plugin-snap.c b/src/plugins/gs-plugin-snap.c
index 83deeb2..c568e42 100644
--- a/src/plugins/gs-plugin-snap.c
+++ b/src/plugins/gs-plugin-snap.c
@@ -49,46 +49,6 @@ gs_plugin_initialize (GsPlugin *plugin)
        }
 }
 
-static JsonParser *
-parse_result (const gchar *response, const gchar *response_type, GError **error)
-{
-       g_autoptr(JsonParser) parser = NULL;
-       g_autoptr(GError) sub_error = 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, &sub_error)) {
-               g_set_error (error,
-                            GS_PLUGIN_ERROR,
-                            GS_PLUGIN_ERROR_FAILED,
-                            "Unable to parse snapd response: %s", sub_error->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
 refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_search)
 {
@@ -127,12 +87,8 @@ refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_sea
                g_autofree gchar *icon_response = NULL;
                gsize icon_response_length;
 
-               if (send_snapd_request ("GET", icon_url, NULL,
-                                       TRUE,
-                                       NULL, NULL,
-                                       TRUE,
-                                       NULL, NULL,
-                                       NULL, NULL, NULL, &icon_response, &icon_response_length, NULL, NULL)) 
{
+               icon_response = gs_snapd_get_resource (icon_url, &icon_response_length, NULL, NULL);
+               if (icon_response != NULL) {
                        g_autoptr(GdkPixbufLoader) loader = NULL;
 
                        loader = gdk_pixbuf_loader_new ();
@@ -185,58 +141,19 @@ refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package, gboolean from_sea
 static gboolean
 get_apps (GsPlugin *plugin, gchar **search_terms, GList **list, AppFilterFunc filter_func, gpointer 
user_data, GCancellable *cancellable, GError **error)
 {
-       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;
+       g_autoptr(JsonArray) result = NULL;
        GList *snaps;
        GList *i;
 
        /* Get all the apps */
-       query_fields = g_ptr_array_new_with_free_func (g_free);
-       if (search_terms != NULL) {
-               g_autofree gchar *query = NULL;
-               query = g_strjoinv ("+", search_terms);
-               g_ptr_array_add (query_fields, g_strdup_printf ("q=%s", query));
-               path = g_string_new ("/v2/find");
-       }
+       if (search_terms != NULL)
+               result = gs_snapd_find (search_terms, cancellable, error);
        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 (!send_snapd_request ("GET", path->str, NULL,
-                                TRUE,
-                                NULL, NULL,
-                                TRUE,
-                                NULL, NULL,
-                                &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);
-               return FALSE;
-       }
-
-       parser = parse_result (response, response_type, error);
-       if (parser == NULL)
+               result = gs_snapd_list (cancellable, error);
+       if (result == 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 (i = snaps; i != NULL; i = i->next) {
                JsonObject *package = json_node_get_object (i->data);
                g_autoptr(GsApp) app = NULL;
@@ -255,7 +172,6 @@ get_apps (GsPlugin *plugin, gchar **search_terms, GList **list, AppFilterFunc fi
                refine_app (plugin, app, package, TRUE);
                gs_plugin_add_app (list, app);
        }
-
        g_list_free (snaps);
 
        return TRUE;
@@ -264,43 +180,14 @@ get_apps (GsPlugin *plugin, gchar **search_terms, GList **list, AppFilterFunc fi
 static gboolean
 get_app (GsPlugin *plugin, GsApp *app, GCancellable *cancellable, GError **error)
 {
-       guint status_code;
-       g_autofree gchar *path = NULL, *reason_phrase = NULL, *response_type = NULL, *response = NULL;
-       g_autoptr(JsonParser) parser = NULL;
-       JsonObject *root, *result;
-
-       path = g_strdup_printf ("/v2/snaps/%s", gs_app_get_id (app));
-       if (!send_snapd_request ("GET", path, NULL,
-                                TRUE,
-                                NULL, NULL,
-                                TRUE,
-                                NULL, NULL,
-                                &status_code, &reason_phrase, &response_type, &response, NULL, cancellable, 
error))
-               return FALSE;
-
-       if (status_code == SOUP_STATUS_NOT_FOUND)
-               return TRUE;
+       g_autoptr(JsonObject) result = 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 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_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));
+       result = gs_snapd_list_one (gs_app_get_id (app), cancellable, error);
+       if (result == NULL)
                return FALSE;
-       }
+       // Hack to handle not found case
+       if (json_object_get_size (result) == 0)
+               return TRUE;
 
        refine_app (plugin, app, result, FALSE);
 
@@ -361,121 +248,39 @@ gs_plugin_refine (GsPlugin *plugin,
        return TRUE;
 }
 
-static gboolean
-send_package_action (GsPlugin *plugin, GsApp *app, const char *id, const gchar *action, GCancellable 
*cancellable, GError **error)
+typedef struct
+{
+       GsPlugin *plugin;
+       GsApp *app;
+} ProgressData;
+
+static void
+progress_cb (JsonObject *result, gpointer user_data)
 {
-       g_autofree gchar *content = NULL, *path = NULL;
-       guint status_code;
-       g_autofree gchar *reason_phrase = NULL, *response_type = NULL, *response = NULL, *status = NULL;
-       g_autoptr(JsonParser) parser = NULL;
-       JsonObject *root, *result, *task, *progress;
+       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;
-       g_autofree gchar *macaroon = NULL;
-       g_auto(GStrv) discharges = NULL;
-
-       content = g_strdup_printf ("{\"action\": \"%s\"}", action);
-       path = g_strdup_printf ("/v2/snaps/%s", id);
-       if (!send_snapd_request ("POST", path, content,
-                                TRUE,
-                                NULL, NULL,
-                                TRUE,
-                                &macaroon, &discharges,
-                                &status_code, &reason_phrase, &response_type, &response, NULL, cancellable, 
error))
-               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, *status_response_type = NULL, 
*status_response = NULL;
-                       g_autoptr(JsonParser) status_parser = NULL;
-
-                       /* Wait for a little bit before polling */
-                       g_usleep (100 * 1000);
-
-                       if (!send_snapd_request ("GET", resource_path, NULL,
-                                                TRUE,
-                                                macaroon, discharges,
-                                                TRUE,
-                                                NULL, NULL,
-                                                &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);
-
-                       done = 0;
-                       total = 0;
-
-                       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");
+       tasks = json_object_get_array_member (result, "tasks");
+       task_list = json_array_get_elements (tasks);
 
-                               done += task_done;
-                               total += task_total;
-                       }
+       for (l = task_list; l != NULL; l = l->next) {
+               JsonObject *task, *progress;
+               gint64 task_done, task_total;
 
-                       gs_plugin_progress_update (plugin, app, 100 * done / 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");
 
-                       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
@@ -484,20 +289,21 @@ gs_plugin_app_install (GsPlugin *plugin,
                       GCancellable *cancellable,
                       GError **error)
 {
-       gboolean result;
+       ProgressData data;
 
        /* We can only install apps we know of */
        if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
                return TRUE;
 
        gs_app_set_state (app, AS_APP_STATE_INSTALLING);
-       result = send_package_action (plugin, app, gs_app_get_id (app), "install", cancellable, error);
-       if (result)
-               gs_app_set_state (app, AS_APP_STATE_INSTALLED);
-       else
+       data.plugin = plugin;
+       data.app = app;
+       if (!gs_snapd_install (gs_app_get_id (app), progress_cb, &data, cancellable, error)) {
                gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
-
-       return result;
+               return FALSE;
+       }
+       gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+       return TRUE;
 }
 
 gboolean
@@ -539,18 +345,19 @@ gs_plugin_app_remove (GsPlugin *plugin,
                      GCancellable *cancellable,
                      GError **error)
 {
-       gboolean result;
+       ProgressData data;
 
        /* We can only remove apps we know of */
        if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
                return TRUE;
 
        gs_app_set_state (app, AS_APP_STATE_REMOVING);
-       result = send_package_action (plugin, app, gs_app_get_id (app), "remove", cancellable, error);
-       if (result)
-               gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
-       else
+       data.plugin = plugin;
+       data.app = app;
+       if (!gs_snapd_remove (gs_app_get_id (app), progress_cb, &data, cancellable, error)) {
                gs_app_set_state (app, AS_APP_STATE_INSTALLED);
-
-       return result;
+               return FALSE;
+       }
+       gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+       return TRUE;
 }
diff --git a/src/plugins/gs-snapd.c b/src/plugins/gs-snapd.c
index bbeadec..2dd7853 100644
--- a/src/plugins/gs-snapd.c
+++ b/src/plugins/gs-snapd.c
@@ -91,23 +91,23 @@ read_from_snapd (GSocket *socket,
        return TRUE;
 }
 
-gboolean
-send_snapd_request (const gchar  *method,
-                   const gchar  *path,
-                   const gchar  *content,
-                   gboolean      authenticate,
-                   const gchar  *macaroon_,
-                   gchar       **discharges_,
-                   gboolean      retry_after_login,
-                   gchar       **out_macaroon,
-                   gchar      ***out_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,
+             gboolean      authenticate,
+             const gchar  *macaroon_,
+             gchar       **discharges_,
+             gboolean      retry_after_login,
+             gchar       **out_macaroon,
+             gchar      ***out_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;
@@ -212,20 +212,20 @@ send_snapd_request (const gchar  *method,
                        return FALSE;
                }
 
-               ret = send_snapd_request (method,
-                                         path,
-                                         content,
-                                         TRUE,
-                                         macaroon, discharges,
-                                         FALSE,
-                                         NULL, NULL,
-                                         status_code,
-                                         reason_phrase,
-                                         response_type,
-                                         response,
-                                         response_length,
-                                         cancellable,
-                                         error);
+               ret = send_request (method,
+                                   path,
+                                   content,
+                                   TRUE,
+                                   macaroon, discharges,
+                                   FALSE,
+                                   NULL, NULL,
+                                   status_code,
+                                   reason_phrase,
+                                   response_type,
+                                   response,
+                                   response_length,
+                                   cancellable,
+                                   error);
 
                if (ret && out_macaroon != NULL) {
                        *out_macaroon = g_steal_pointer (&macaroon);
@@ -303,3 +303,414 @@ send_snapd_request (const gchar  *method,
 
        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);
+}
+
+gchar *
+gs_snapd_login (const gchar *username, const gchar *password, const gchar *otp,
+               guint *status_code,
+               GCancellable *cancellable, GError **error)
+{
+       g_autofree gchar *escaped_username = NULL;
+       g_autofree gchar *escaped_password = NULL;
+       g_autofree gchar *escaped_otp = NULL;
+       g_autofree gchar *content = NULL;
+       g_autofree gchar *response = NULL;
+
+       escaped_username = g_strescape (username, NULL);
+       escaped_password = g_strescape (password, NULL);
+
+       if (otp != NULL) {
+               escaped_otp = g_strescape (otp, NULL);
+
+               content = g_strdup_printf ("{"
+                                          "  \"username\" : \"%s\","
+                                          "  \"password\" : \"%s\","
+                                          "  \"otp\" : \"%s\""
+                                          "}",
+                                          escaped_username,
+                                          escaped_password,
+                                          escaped_otp);
+       } else {
+               content = g_strdup_printf ("{"
+                                          "  \"username\" : \"%s\","
+                                          "  \"password\" : \"%s\""
+                                          "}",
+                                          escaped_username,
+                                          escaped_password);
+       }
+
+       if (!send_request ("POST",
+                          "/v2/login",
+                          content,
+                          FALSE, NULL, NULL,
+                          FALSE, NULL, NULL,
+                          status_code,
+                          NULL,
+                          NULL,
+                          &response,
+                          NULL,
+                          NULL,
+                          error))
+               return NULL;
+
+       return g_steal_pointer (&response);
+}
+
+JsonObject *
+gs_snapd_list_one (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,
+                          TRUE, NULL, NULL,
+                          TRUE, NULL, NULL,
+                          &status_code, &reason_phrase,
+                          &response_type, &response, NULL,
+                          cancellable, error))
+               return NULL;
+
+       // Hack to not generate an error if not found
+       if (status_code == SOUP_STATUS_NOT_FOUND)
+               return json_object_new ();
+
+       if (status_code != SOUP_STATUS_OK) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "snapd returned status code %u: %s",
+                            status_code, reason_phrase);
+               return NULL;
+       }
+
+       parser = parse_result (response, response_type, error);
+       if (parser == NULL)
+               return NULL;
+       root = json_node_get_object (json_parser_get_root (parser));
+       result = json_object_get_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 (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,
+                          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;
+       }
+
+       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 (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,
+                          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;
+       }
+
+       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,
+                          TRUE, macaroon, discharges,
+                          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;
+       }
+
+       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 result");
+               return NULL;
+       }
+
+       return json_object_ref (result);
+}
+
+static gboolean
+send_package_action (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 *macaroon = NULL;
+       g_auto(GStrv) discharges = NULL;
+       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,
+                          TRUE, NULL, NULL,
+                          TRUE, &macaroon, &discharges,
+                          &status_code, &reason_phrase,
+                          &response_type, &response, NULL,
+                          cancellable, error))
+               return FALSE;
+
+       if (status_code != SOUP_STATUS_ACCEPTED) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "snapd returned status code %u: %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;
+       }
+
+       return TRUE;
+}
+
+gboolean
+gs_snapd_install (const gchar *name,
+                 GsSnapdProgressCallback callback, gpointer user_data,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+       return send_package_action (name, "install", callback, user_data, cancellable, error);
+}
+
+gboolean
+gs_snapd_remove (const gchar *name,
+                GsSnapdProgressCallback callback, gpointer user_data,
+                GCancellable *cancellable, GError **error)
+{
+       return send_package_action (name, "remove", callback, user_data, cancellable, error);
+}
+
+gchar *
+gs_snapd_get_resource (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,
+                          TRUE, NULL, NULL,
+                          TRUE, NULL, NULL,
+                          &status_code, &reason_phrase,
+                          NULL, &data, data_length,
+                          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;
+       }
+
+       return g_steal_pointer (&data);
+}
diff --git a/src/plugins/gs-snapd.h b/src/plugins/gs-snapd.h
index 7c4439f..d93b756 100644
--- a/src/plugins/gs-snapd.h
+++ b/src/plugins/gs-snapd.h
@@ -23,24 +23,45 @@
 #define __GS_SNAPD_H__
 
 #include <gio/gio.h>
+#include <json-glib/json-glib.h>
+
+typedef void (*GsSnapdProgressCallback) (JsonObject *object, gpointer user_data);
 
 gboolean gs_snapd_exists       (void);
 
-gboolean send_snapd_request (const gchar  *method,
-                            const gchar  *path,
-                            const gchar  *content,
-                            gboolean      authenticate,
-                            const gchar  *macaroon,
-                            gchar       **discharges,
-                            gboolean      retry_after_login,
-                            gchar       **out_macaroon,
-                            gchar      ***out_discharges,
-                            guint        *status_code,
-                            gchar       **reason_phrase,
-                            gchar       **response_type,
-                            gchar       **response,
-                            gsize        *response_length,
-                            GCancellable *cancellable,
-                            GError      **error);
+gchar *gs_snapd_login          (const gchar    *username,
+                                const gchar    *password,
+                                const gchar    *otp,
+                                guint          *status_code,
+                                GCancellable   *cancellable,
+                                GError         **error);
+
+JsonObject *gs_snapd_list_one  (const gchar    *name,
+                                GCancellable   *cancellable,
+                                GError         **error);
+
+JsonArray *gs_snapd_list       (GCancellable   *cancellable,
+                                GError         **error);
+
+JsonArray *gs_snapd_find       (gchar          **values,
+                                GCancellable   *cancellable,
+                                GError         **error);
+
+gboolean gs_snapd_install      (const gchar    *name,
+                                GsSnapdProgressCallback callback,
+                                gpointer        user_data,
+                                GCancellable   *cancellable,
+                                GError         **error);
+
+gboolean gs_snapd_remove       (const gchar    *name,
+                                GsSnapdProgressCallback callback,
+                                gpointer        user_data,
+                                GCancellable   *cancellable,
+                                GError         **error);
+
+gchar *gs_snapd_get_resource   (const gchar    *path,
+                                gsize          *data_length,
+                                GCancellable   *cancellable,
+                                GError         **error);
 
 #endif /* __GS_SNAPD_H__ */
diff --git a/src/plugins/gs-ubuntuone-dialog.c b/src/plugins/gs-ubuntuone-dialog.c
index 0650dc2..5d77db0 100644
--- a/src/plugins/gs-ubuntuone-dialog.c
+++ b/src/plugins/gs-ubuntuone-dialog.c
@@ -301,10 +301,7 @@ receive_login_response_cb (GsUbuntuoneDialog *self,
 static void
 check_snapd_response (GsUbuntuoneDialog *self,
                      guint              status_code,
-                     gchar             *reason_phrase,
-                     gchar             *response_type,
-                     gchar             *response,
-                     gsize              response_length)
+                     gchar             *response)
 {
        g_autoptr(GVariant) variant = NULL;
        g_autoptr(GVariant) result = NULL;
@@ -380,16 +377,7 @@ err:
 static void
 send_login_request (GsUbuntuoneDialog *self)
 {
-       g_autofree gchar *content = NULL;
-       g_autofree gchar *username = NULL;
-       g_autofree gchar *password = NULL;
-       g_autofree gchar *otp = NULL;
        GVariant *request;
-       guint status_code;
-       g_autofree gchar *reason_phrase = NULL;
-       g_autofree gchar *response_type = NULL;
-       g_autofree gchar *response = NULL;
-       gsize response_length;
        g_autoptr(GError) error = NULL;
 
        gtk_widget_set_sensitive (self->cancel_button, FALSE);
@@ -405,51 +393,20 @@ send_login_request (GsUbuntuoneDialog *self)
        show_status (self, _("Signing in…"), FALSE);
 
        if (self->get_macaroon) {
-               username = g_strescape (gtk_entry_get_text (GTK_ENTRY (self->email_entry)), NULL);
-               password = g_strescape (gtk_entry_get_text (GTK_ENTRY (self->password_entry)), NULL);
-
-               if (gtk_entry_get_text_length (GTK_ENTRY (self->passcode_entry)) > 0) {
-                       otp = g_strescape (gtk_entry_get_text (GTK_ENTRY (self->passcode_entry)), NULL);
-
-                       content = g_strdup_printf ("{"
-                                                  "  \"username\" : \"%s\","
-                                                  "  \"password\" : \"%s\","
-                                                  "  \"otp\" : \"%s\""
-                                                  "}",
-                                                  username,
-                                                  password,
-                                                  otp);
-               } else {
-                       content = g_strdup_printf ("{"
-                                                  "  \"username\" : \"%s\","
-                                                  "  \"password\" : \"%s\""
-                                                  "}",
-                                                  username,
-                                                  password);
-               }
-
-               if (send_snapd_request (SOUP_METHOD_POST,
-                                       "/v2/login",
-                                       content,
-                                       FALSE,
-                                       NULL, NULL,
-                                       FALSE,
-                                       NULL, NULL,
-                                       &status_code,
-                                       &reason_phrase,
-                                       &response_type,
-                                       &response,
-                                       &response_length,
-                                       NULL,
-                                       &error)) {
+               g_autofree gchar *response = NULL;
+               guint status_code;
+
+               response = gs_snapd_login (gtk_entry_get_text (GTK_ENTRY (self->email_entry)),
+                                          gtk_entry_get_text (GTK_ENTRY (self->password_entry)),
+                                          gtk_entry_get_text (GTK_ENTRY (self->passcode_entry)),
+                                          &status_code,
+                                          NULL, &error);
+               if (response != NULL) {
                        reenable_widgets (self);
 
                        check_snapd_response (self,
                                              status_code,
-                                             reason_phrase,
-                                             response_type,
-                                             response,
-                                             response_length);
+                                             response);
                } else {
                        g_warning ("could not send request: %s", error->message);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]