[gnome-software/wip/rancell/paid: 393/393] Merge branch 'master' into wip/rancell/paid
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/rancell/paid: 393/393] Merge branch 'master' into wip/rancell/paid
- Date: Tue, 8 Nov 2016 02:52:20 +0000 (UTC)
commit 34aa3583f6583d91046f2b19aaa075625a6653b9
Merge: f1cebf3 9d61723
Author: Robert Ancell <robert ancell canonical com>
Date: Tue Nov 8 14:16:10 2016 +1300
Merge branch 'master' into wip/rancell/paid
RELEASE | 6 +-
configure.ac | 119 +-
contrib/gnome-software.spec.in | 26 +-
data/Makefile.am | 13 +-
data/appdata/org.gnome.Software.appdata.xml.in | 226 +-
...org.gnome.software.external-appstream.policy.in | 20 +
data/org.gnome.software.gschema.xml | 42 +-
data/tests/example.flatpakrepo | 1 +
doc/api/Makefile.am | 2 +
doc/api/gnome-software-docs.xml | 2 +
doc/api/gnome-software.types | 1 +
po/LINGUAS | 2 +
po/POTFILES.in | 2 +
po/ar.po | 4104 ++++++++++++++------
po/ca.po | 382 ++-
po/cs.po | 2928 ++++++++------
po/da.po | 3271 +++++++++-------
po/de.po | 3593 +++++++++++------
po/en_GB.po | 3644 ++++++++++++++---
po/eo.po | 4104 +++++++++++++-------
po/es.po | 1555 +++++---
po/fa.po | 2954 ++++++++++++++
po/fi.po | 3144 ++++++++++------
po/fr.po | 738 ++--
po/fur.po | 1547 +++++---
po/gl.po | 234 +-
po/he.po | 3946 ++++++++++++-------
po/hr.po | 2972 ++++++++++++++
po/hu.po | 3122 +++++++--------
po/it.po | 2574 +++++++------
po/ja.po | 3859 ++++++++++++-------
po/kk.po | 1301 ++++---
po/ko.po | 2554 +++++++------
po/lt.po | 1736 +++++----
po/lv.po | 3424 +++++++++++------
po/nb.po | 2961 ++++++++------
po/nl.po | 3748 ++++++++++++------
po/oc.po | 3099 +++++++++-------
po/pl.po | 2658 +++++++------
po/pt_BR.po | 2765 ++++++++------
po/ru.po | 3127 +++++++++------
po/sk.po | 1248 ++++---
po/sl.po | 1650 +++++---
po/sr.po | 3171 ++++++++++------
po/sr latin po | 3236 ++++++++++------
po/sv.po | 3150 ++++++++++------
po/uk.po | 3368 +++++++++++------
po/zh_TW.po | 652 ++--
src/Makefile.am | 19 +-
src/gd-notification.c | 875 +++++
src/gd-notification.h | 67 +
src/gnome-software-local-file.desktop.in | 2 +-
src/gnome-software-service.desktop.in | 1 +
src/gnome-software.ui | 230 +-
src/gs-app-list-private.h | 2 +
src/gs-app-list.c | 207 +-
src/gs-app-list.h | 4 +
src/gs-app-private.h | 5 +-
src/gs-app-row.c | 15 +-
src/gs-app.c | 704 +++-
src/gs-app.h | 22 +-
src/gs-application.c | 95 +-
src/gs-auth-dialog.c | 6 +-
src/gs-auth.c | 3 +-
src/gs-auth.h | 18 -
src/gs-category.c | 44 +
src/gs-cmd.c | 12 +-
src/gs-common.c | 387 +--
src/gs-common.h | 12 +-
src/gs-content-rating.c | 569 +++
src/gs-content-rating.h | 62 +
src/gs-dbus-helper.c | 2 +-
src/gs-folders.c | 12 +-
src/gs-main.c | 1 +
src/gs-os-release.c | 14 +
src/gs-page.c | 287 +-
src/gs-payment-method.c | 144 -
src/gs-payment-method.h | 49 -
src/gs-plugin-event.c | 323 ++
src/gs-plugin-event.h | 83 +
src/gs-plugin-loader-sync.c | 6 +-
src/gs-plugin-loader-sync.h | 6 +-
src/gs-plugin-loader.c | 943 ++++--
src/gs-plugin-loader.h | 55 +-
src/gs-plugin-private.h | 4 +
src/gs-plugin-types.h | 309 ++
src/gs-plugin-vfuncs.h | 91 +-
src/gs-plugin.c | 426 ++-
src/gs-plugin.h | 200 +-
src/gs-purchase-dialog.c | 28 -
src/gs-purchase-dialog.h | 6 -
src/gs-review-dialog.c | 4 +-
src/gs-review-row.c | 32 +-
src/gs-review-row.h | 2 +-
src/gs-screenshot-image.c | 5 +-
src/gs-self-test.c | 279 ++-
src/gs-shell-category.c | 11 +-
src/gs-shell-details.c | 313 ++-
src/gs-shell-details.h | 1 -
src/gs-shell-details.ui | 93 +
src/gs-shell-extras.c | 71 +-
src/gs-shell-installed.c | 35 +-
src/gs-shell-moderate.c | 15 +-
src/gs-shell-overview.c | 219 +-
src/gs-shell-overview.ui | 374 ++-
src/gs-shell-search-provider.c | 4 +-
src/gs-shell-search.c | 45 +-
src/gs-shell-updates.c | 112 +-
src/gs-shell.c | 880 ++++-
src/gs-shell.h | 4 +-
src/gs-sources-dialog-row.c | 92 +-
src/gs-sources-dialog-row.h | 7 +
src/gs-sources-dialog-row.ui | 67 +-
src/gs-sources-dialog.c | 299 ++-
src/gs-sources-dialog.ui | 25 +-
src/gs-summary-tile.c | 10 -
src/gs-update-dialog.c | 4 +-
src/gs-update-monitor.c | 225 +-
src/gs-update-monitor.h | 2 -
src/gs-upgrade-banner.ui | 1 -
src/gs-utils.c | 303 ++-
src/gs-utils.h | 8 +
src/gtk-style-hc.css | 4 +-
src/gtk-style.css | 17 +
src/plugins/Makefile.am | 53 +-
src/plugins/gnome-software-install-appstream.in | 9 +
src/plugins/gs-appstream.c | 503 +++-
src/plugins/gs-appstream.h | 32 +
src/plugins/gs-desktop-common.c | 5 +-
src/plugins/gs-desktop-common.h | 2 +-
src/plugins/gs-flatpak-symlinks.c | 190 +-
src/plugins/gs-flatpak-symlinks.h | 2 +-
src/plugins/gs-flatpak.c | 1200 +++++-
src/plugins/gs-flatpak.h | 42 +-
src/plugins/gs-plugin-appstream.c | 416 +--
src/plugins/gs-plugin-desktop-menu-path.c | 4 +-
src/plugins/gs-plugin-dpkg.c | 6 +-
src/plugins/gs-plugin-dummy.c | 92 +-
src/plugins/gs-plugin-epiphany.c | 20 +-
src/plugins/gs-plugin-external-appstream.c | 238 ++
src/plugins/gs-plugin-fedora-distro-upgrades.c | 42 +-
src/plugins/gs-plugin-flatpak-system.c | 117 +-
src/plugins/gs-plugin-flatpak-user.c | 99 +-
src/plugins/gs-plugin-fwupd.c | 433 ++-
src/plugins/gs-plugin-hardcoded-featured.c | 2 +-
src/plugins/gs-plugin-icons.c | 55 +-
src/plugins/gs-plugin-key-colors.c | 4 +-
src/plugins/gs-plugin-limba.c | 2 +
src/plugins/gs-plugin-modalias.c | 163 +
src/plugins/gs-plugin-odrs.c | 110 +-
src/plugins/gs-plugin-ostree.c | 8 +-
src/plugins/gs-plugin-packagekit-history.c | 2 +-
src/plugins/gs-plugin-packagekit-local.c | 10 +-
src/plugins/gs-plugin-packagekit-refine.c | 52 +-
src/plugins/gs-plugin-packagekit-refresh.c | 2 +-
src/plugins/gs-plugin-packagekit-upgrade.c | 14 +-
src/plugins/gs-plugin-packagekit.c | 14 +-
src/plugins/gs-plugin-repos.c | 12 +-
src/plugins/gs-plugin-rpm.c | 2 +-
src/plugins/gs-plugin-shell-extensions.c | 77 +-
src/plugins/gs-plugin-snap.c | 733 +---
src/plugins/gs-plugin-steam.c | 60 +-
src/plugins/gs-plugin-systemd-updates.c | 38 +-
src/plugins/gs-plugin-ubuntu-reviews.c | 68 +-
src/plugins/gs-plugin-ubuntuone.c | 16 +-
src/plugins/gs-snapd.c | 492 +++-
src/plugins/gs-snapd.h | 80 +-
src/plugins/packagekit-common.c | 24 +-
src/plugins/packagekit-common.h | 2 +-
169 files changed, 71724 insertions(+), 36717 deletions(-)
---
diff --cc src/Makefile.am
index 333cada,2a2f3d3..5b40af7
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@@ -111,8 -113,6 +114,7 @@@ gnome_software_cmd_SOURCES =
gs-app.c \
gs-app-list.c \
gs-auth.c \
+ gs-price.c \
- gs-review.c \
gs-cmd.c \
gs-common.c \
gs-debug.c \
@@@ -194,18 -199,15 +201,19 @@@ gnome_software_SOURCES =
gs-os-release.h \
gs-page.c \
gs-page.h \
- gs-payment-method.c \
- gs-payment-method.h \
gs-plugin.c \
gs-plugin.h \
+ gs-plugin-event.c \
+ gs-plugin-event.h \
gs-plugin-private.h \
+ gs-plugin-types.h \
gs-plugin-vfuncs.h \
+ gs-price.c \
+ gs-price.h \
gs-progress-button.c \
gs-progress-button.h \
+ gs-purchase-dialog.c \
+ gs-purchase-dialog.h \
gs-removal-dialog.c \
gs-removal-dialog.h \
gs-review-bar.c \
diff --cc src/gs-app.c
index 5713824,a87228b..380d43a
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@@ -383,13 -408,6 +410,13 @@@ gs_app_to_string (GsApp *app
gs_app_kv_lpad (str, "origin-ui", app->origin_ui);
if (app->origin_hostname != NULL && app->origin_hostname[0] != '\0')
gs_app_kv_lpad (str, "origin-hostname", app->origin_hostname);
+ for (i = 0; i < app->prices->len; i++) {
+ GsPrice *price = g_ptr_array_index (app->prices, i);
+ g_autofree gchar *key = NULL, *text = NULL;
- key = g_strdup_printf ("price-%02i", i);
++ key = g_strdup_printf ("price-%02u", i);
+ text = gs_price_to_string (price);
+ gs_app_kv_lpad (str, key, text);
+ }
if (app->rating != -1)
gs_app_kv_printf (str, "rating", "%i", app->rating);
if (app->review_ratings != NULL) {
@@@ -2877,8 -3360,8 +3405,9 @@@ gs_app_dispose (GObject *object
g_clear_pointer (&app->history, g_ptr_array_unref);
g_clear_pointer (&app->related, g_ptr_array_unref);
g_clear_pointer (&app->screenshots, g_ptr_array_unref);
+ g_clear_pointer (&app->prices, g_ptr_array_unref);
g_clear_pointer (&app->reviews, g_ptr_array_unref);
+ g_clear_pointer (&app->provides, g_ptr_array_unref);
g_clear_pointer (&app->icons, g_ptr_array_unref);
G_OBJECT_CLASS (gs_app_parent_class)->dispose (object);
@@@ -3037,8 -3525,8 +3571,9 @@@ gs_app_init (GsApp *app
app->related = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->history = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->screenshots = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ app->prices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->reviews = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ app->provides = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->icons = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->metadata = g_hash_table_new_full (g_str_hash,
g_str_equal,
diff --cc src/gs-page.c
index ce08ea7,d0c7b27..196d594
--- a/src/gs-page.c
+++ b/src/gs-page.c
@@@ -29,8 -29,7 +29,9 @@@
#include "gs-shell.h"
#include "gs-common.h"
#include "gs-auth-dialog.h"
+ #include "gs-screenshot-image.h"
+#include "gs-price.h"
+#include "gs-purchase-dialog.h"
typedef struct
{
@@@ -63,33 -70,6 +72,29 @@@ gs_page_helper_free (GsPageHelper *help
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsPageHelper, gs_page_helper_free);
static void
+gs_page_app_purchased_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GsPageHelper) helper = (GsPageHelper *) user_data;
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+ GsPage *page = helper->page;
+ GsPagePrivate *priv = gs_page_get_instance_private (page);
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+
+ ret = gs_plugin_loader_app_action_finish (plugin_loader,
+ res,
+ &error);
+ if (!ret) {
+ g_warning ("failed to purchase %s: %s",
+ gs_app_get_id (helper->app),
+ error->message);
- gs_app_notify_failed_modal (helper->app,
- gs_shell_get_window (priv->shell),
- GS_PLUGIN_LOADER_ACTION_PURCHASE,
- error);
+ return;
+ }
+}
+
+static void
gs_page_app_installed_cb (GObject *source,
GAsyncResult *res,
gpointer user_data);
@@@ -320,137 -265,7 +290,57 @@@ gs_page_set_header_end_widget (GsPage *
g_set_object (&priv->header_end_widget, widget);
}
+static void
+gs_page_payment_methods_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data);
+
+static void
- gs_page_payment_methods_authenticate_cb (GtkDialog *dialog,
- GtkResponseType response_type,
- GsPageHelper *helper)
- {
- GsPagePrivate *priv = gs_page_get_instance_private (helper->page);
-
- /* unmap the dialog */
- gtk_widget_destroy (GTK_WIDGET (dialog));
-
- if (response_type != GTK_RESPONSE_OK) {
- gs_page_helper_free (helper);
- return;
- }
- gs_plugin_loader_get_payment_methods_async (priv->plugin_loader,
- helper->cancellable,
- gs_page_payment_methods_cb,
- helper);
- }
-
- static void
+gs_page_purchase_app_response_cb (GtkDialog *dialog,
+ gint response,
+ GsPageHelper *helper)
+{
+ GsPagePrivate *priv = gs_page_get_instance_private (helper->page);
+ GPtrArray *prices;
+
+ /* not agreed */
+ if (response != GTK_RESPONSE_OK) {
+ gs_page_helper_free (helper);
+ return;
+ }
+ g_debug ("purchase %s", gs_app_get_id (helper->app));
+ prices = gs_app_get_prices (helper->app);
+ gs_plugin_loader_app_purchase_async (priv->plugin_loader,
+ helper->app,
+ g_ptr_array_index (prices, 0), // FIXME: User should pick price,
check if no prices
+ helper->cancellable,
+ gs_page_app_purchased_cb,
+ helper);
+}
+
- static void
- gs_page_payment_methods_cb (GObject *source,
- GAsyncResult *res,
- gpointer user_data)
++void
++gs_page_purchase_app (GsPage *page, GsApp *app, GCancellable *cancellable)
+{
- g_autoptr(GsPageHelper) helper = (GsPageHelper *) user_data;
- GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
- GsPage *page = helper->page;
+ GsPagePrivate *priv = gs_page_get_instance_private (page);
- g_autoptr(GPtrArray) payment_methods = NULL;
- g_autoptr(GError) error = NULL;
++ GsPageHelper *helper;
+ GtkWidget *dialog;
+
- payment_methods = gs_plugin_loader_get_payment_methods_finish (plugin_loader,
- res,
- &error);
- if (g_error_matches (error,
- G_IO_ERROR,
- G_IO_ERROR_CANCELLED)) {
- g_debug ("%s", error->message);
- return;
- }
- if (payment_methods == NULL) {
- /* try to authenticate then retry */
- if (g_error_matches (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_AUTH_REQUIRED)) {
- g_autoptr(GError) error_local = NULL;
- GtkWidget *auth_dialog;
- auth_dialog = gs_auth_dialog_new (priv->plugin_loader,
- helper->app,
- gs_utils_get_error_value (error),
- &error_local);
- if (auth_dialog == NULL) {
- g_warning ("%s", error_local->message);
- return;
- }
- gs_shell_modal_dialog_present (priv->shell, GTK_DIALOG (auth_dialog));
- g_signal_connect (auth_dialog, "response",
- G_CALLBACK (gs_page_payment_methods_authenticate_cb),
- g_steal_pointer (&helper));
- return;
- }
-
- g_warning ("failed to get payment methods: %s",
- error->message);
- gs_app_notify_failed_modal (helper->app,
- gs_shell_get_window (priv->shell),
- GS_PLUGIN_LOADER_ACTION_PURCHASE,
- error);
- return;
- }
++ helper = g_slice_new0 (GsPageHelper);
++ helper->app = g_object_ref (app);
++ helper->page = g_object_ref (page);
++ helper->cancellable = g_object_ref (cancellable);
+
+ /* ask for confirmation */
+ dialog = gs_purchase_dialog_new ();
+ gs_purchase_dialog_set_app (GS_PURCHASE_DIALOG (dialog), helper->app);
- gs_purchase_dialog_set_payment_methods (GS_PURCHASE_DIALOG (dialog), payment_methods);
+
+ /* handle this async */
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gs_page_purchase_app_response_cb), g_steal_pointer (&helper));
+ gs_shell_modal_dialog_present (priv->shell, GTK_DIALOG (dialog));
+}
+
void
- gs_page_purchase_app (GsPage *page, GsApp *app, GCancellable *cancellable)
- {
- GsPagePrivate *priv = gs_page_get_instance_private (page);
- GsPageHelper *helper;
-
- helper = g_slice_new0 (GsPageHelper);
- helper->app = g_object_ref (app);
- helper->page = g_object_ref (page);
- helper->cancellable = g_object_ref (cancellable);
-
- /* load payment methods */
- gs_plugin_loader_get_payment_methods_async (priv->plugin_loader,
- helper->cancellable,
- gs_page_payment_methods_cb,
- helper);
- }
-
- void
gs_page_install_app (GsPage *page, GsApp *app, GCancellable *cancellable)
{
GsPagePrivate *priv = gs_page_get_instance_private (page);
diff --cc src/gs-plugin-loader.c
index 9377fde,125715c..4edbd8e
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@@ -107,15 -120,6 +120,11 @@@ typedef gboolean (*GsPluginAuthFunc)
GsAuth *auth,
GCancellable *cancellable,
GError **error);
- typedef gboolean (*GsPluginPaymentMethodFunc) (GsPlugin *plugin,
- GPtrArray *payment_methods,
- GCancellable *cancellable,
- GError **error);
+typedef gboolean (*GsPluginPurchaseFunc) (GsPlugin *plugin,
+ GsApp *app,
+ GsPrice *price,
+ GCancellable *cancellable,
+ GError **error);
typedef gboolean (*GsPluginRefineFunc) (GsPlugin *plugin,
GsAppList *list,
GsPluginRefineFlags flags,
@@@ -156,8 -166,7 +171,9 @@@ typedef struct
GsApp *app;
AsReview *review;
GsAuth *auth;
+ GsPluginAction action;
+ GPtrArray *payment_methods;
+ GsPrice *price;
} GsPluginLoaderAsyncState;
static void
@@@ -2845,143 -3112,6 +3123,71 @@@ gs_plugin_loader_review_action_thread_c
g_task_return_boolean (task, TRUE);
}
+static void
- gs_plugin_loader_add_payment_methods_thread_cb (GTask *task,
- gpointer object,
- gpointer task_data,
- GCancellable *cancellable)
- {
- GError *error = NULL;
- GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
- GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
- GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
- GsPlugin *plugin;
- GsPluginPaymentMethodFunc plugin_func = NULL;
- gboolean anything_ran = FALSE;
- gboolean exists;
- gboolean ret;
- guint i;
-
- /* run each plugin */
- for (i = 0; i < priv->plugins->len; i++) {
- g_autoptr(AsProfileTask) ptask = NULL;
- g_autoptr(GError) error_local = NULL;
-
- plugin = g_ptr_array_index (priv->plugins, i);
- if (!gs_plugin_get_enabled (plugin))
- continue;
- if (g_cancellable_set_error_if_cancelled (cancellable, &error))
- g_task_return_error (task, error);
-
- exists = g_module_symbol (gs_plugin_get_module (plugin),
- state->function_name,
- (gpointer *) &plugin_func);
- if (!exists)
- continue;
- ptask = as_profile_start (priv->profile,
- "GsPlugin::%s(%s)",
- gs_plugin_get_name (plugin),
- state->function_name);
- gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
- ret = plugin_func (plugin, state->payment_methods,
- cancellable, &error_local);
- gs_plugin_loader_action_stop (plugin_loader, plugin);
- if (!ret) {
- /* abort early to allow main thread to process */
- if (gs_plugin_loader_is_auth_error (error_local)) {
- g_task_return_error (task, error_local);
- error_local = NULL;
- return;
- }
-
- g_warning ("failed to call %s on %s: %s",
- state->function_name,
- gs_plugin_get_name (plugin),
- error_local->message);
- continue;
- }
- anything_ran = TRUE;
- gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
- }
-
- /* nothing ran */
- if (!anything_ran) {
- g_set_error (&error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_NOT_SUPPORTED,
- "no plugin could handle %s",
- state->function_name);
- g_task_return_error (task, error);
- }
-
- g_task_return_pointer (task, g_object_ref (state->payment_methods), g_object_unref);
- }
-
- static void
+gs_plugin_loader_app_purchase_thread_cb (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ GsPlugin *plugin;
+ GsPluginPurchaseFunc plugin_func = NULL;
+ gboolean anything_ran = FALSE;
+ gboolean exists;
+ gboolean ret;
+ guint i;
+
+ /* run each plugin */
+ for (i = 0; i < priv->plugins->len; i++) {
+ g_autoptr(AsProfileTask) ptask = NULL;
+ g_autoptr(GError) error_local = NULL;
+
+ plugin = g_ptr_array_index (priv->plugins, i);
+ if (!gs_plugin_get_enabled (plugin))
+ continue;
+ if (g_cancellable_set_error_if_cancelled (cancellable, &error))
+ g_task_return_error (task, error);
+
+ exists = g_module_symbol (gs_plugin_get_module (plugin),
+ state->function_name,
+ (gpointer *) &plugin_func);
+ if (!exists)
+ continue;
+ ptask = as_profile_start (priv->profile,
+ "GsPlugin::%s(%s)",
+ gs_plugin_get_name (plugin),
+ state->function_name);
+ gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
+ ret = plugin_func (plugin, state->app, state->price,
+ cancellable, &error_local);
+ gs_plugin_loader_action_stop (plugin_loader, plugin);
+ if (!ret) {
+ g_warning ("failed to call %s on %s: %s",
+ state->function_name,
+ gs_plugin_get_name (plugin),
+ error_local->message);
+ continue;
+ }
+ anything_ran = TRUE;
+ gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
+ }
+
+ /* nothing ran */
+ if (!anything_ran) {
+ g_set_error (&error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_NOT_SUPPORTED,
+ "no plugin could handle %s",
+ state->function_name);
+ g_task_return_error (task, error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
static gboolean
load_install_queue (GsPluginLoader *plugin_loader, GError **error)
{
@@@ -3220,83 -3356,6 +3432,39 @@@ gs_plugin_loader_app_action_async (GsPl
g_task_run_in_thread (task, gs_plugin_loader_app_action_thread_cb);
}
+/**
- * gs_plugin_loader_get_payment_methods_async:
- **/
- void
- gs_plugin_loader_get_payment_methods_async (GsPluginLoader *plugin_loader,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
- {
- GsPluginLoaderAsyncState *state;
- g_autoptr(GTask) task = NULL;
-
- g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
- g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
-
- /* save state */
- state = g_slice_new0 (GsPluginLoaderAsyncState);
- state->payment_methods = g_ptr_array_new_with_free_func (g_object_unref);
- state->function_name = "gs_plugin_add_payment_methods";
-
- /* run in a thread */
- task = g_task_new (plugin_loader, cancellable, callback, user_data);
- g_task_set_task_data (task, state, (GDestroyNotify) gs_plugin_loader_free_async_state);
- g_task_run_in_thread (task, gs_plugin_loader_add_payment_methods_thread_cb);
- }
-
- /**
- * gs_plugin_loader_get_payment_methods_finish:
- *
- * Return value: (element-type GsPaymentMethod) (transfer full): A list of payment methods.
- **/
- GPtrArray *
- gs_plugin_loader_get_payment_methods_finish (GsPluginLoader *plugin_loader,
- GAsyncResult *res,
- GError **error)
- {
- g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
- g_return_val_if_fail (G_IS_TASK (res), NULL);
- g_return_val_if_fail (g_task_is_valid (res, plugin_loader), NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-
- return g_task_propagate_pointer (G_TASK (res), error);
- }
-
- /**
+ * gs_plugin_loader_app_purchase_async:
+ **/
+void
+gs_plugin_loader_app_purchase_async (GsPluginLoader *plugin_loader,
+ GsApp *app,
+ GsPrice *price,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginLoaderAsyncState *state;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+ g_return_if_fail (GS_IS_APP (app));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ /* save state */
+ state = g_slice_new0 (GsPluginLoaderAsyncState);
+ state->app = g_object_ref (app);
+ state->price = g_object_ref (price);
+ state->function_name = "gs_plugin_app_purchase";
+
+ /* run in a thread */
+ task = g_task_new (plugin_loader, cancellable, callback, user_data);
+ g_task_set_task_data (task, state, (GDestroyNotify) gs_plugin_loader_free_async_state);
+ g_task_run_in_thread (task, gs_plugin_loader_app_purchase_thread_cb);
+}
+
+/**
+ * gs_plugin_loader_review_action_async:
+ **/
void
gs_plugin_loader_review_action_async (GsPluginLoader *plugin_loader,
GsApp *app,
diff --cc src/gs-plugin-types.h
index 0000000,b4f6d42..7fc5526
mode 000000,100644..100644
--- a/src/gs-plugin-types.h
+++ b/src/gs-plugin-types.h
@@@ -1,0 -1,307 +1,309 @@@
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012-2016 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+ #ifndef __GS_PLUGIN_TYPES_H
+ #define __GS_PLUGIN_TYPES_H
+
+ #include <glib-object.h>
+
+ G_BEGIN_DECLS
+
+ /**
+ * GsPluginStatus:
+ * @GS_PLUGIN_STATUS_UNKNOWN: Unknown status
+ * @GS_PLUGIN_STATUS_WAITING: Waiting
+ * @GS_PLUGIN_STATUS_FINISHED: Finished
+ * @GS_PLUGIN_STATUS_SETUP: Setup in progress
+ * @GS_PLUGIN_STATUS_DOWNLOADING: Downloading in progress
+ * @GS_PLUGIN_STATUS_QUERYING: Querying in progress
+ * @GS_PLUGIN_STATUS_INSTALLING: Installing in progress
+ * @GS_PLUGIN_STATUS_REMOVING: Removing in progress
+ *
+ * The ststus of the plugin.
+ **/
+ typedef enum {
+ GS_PLUGIN_STATUS_UNKNOWN,
+ GS_PLUGIN_STATUS_WAITING,
+ GS_PLUGIN_STATUS_FINISHED,
+ GS_PLUGIN_STATUS_SETUP,
+ GS_PLUGIN_STATUS_DOWNLOADING,
+ GS_PLUGIN_STATUS_QUERYING,
+ GS_PLUGIN_STATUS_INSTALLING,
+ GS_PLUGIN_STATUS_REMOVING,
+ /*< private >*/
+ GS_PLUGIN_STATUS_LAST
+ } GsPluginStatus;
+
+ /**
+ * GsPluginFlags:
+ * @GS_PLUGIN_FLAGS_NONE: No flags set
+ * @GS_PLUGIN_FLAGS_RUNNING_SELF: The plugin is running
+ * @GS_PLUGIN_FLAGS_RUNNING_OTHER: Another plugin is running
+ * @GS_PLUGIN_FLAGS_EXCLUSIVE: An exclusive action is running
+ * @GS_PLUGIN_FLAGS_RECENT: This plugin recently ran
+ * @GS_PLUGIN_FLAGS_GLOBAL_CACHE: Use the global app cache
+ *
+ * The flags for the plugin at this point in time.
+ **/
+ #define GS_PLUGIN_FLAGS_NONE (0u)
+ #define GS_PLUGIN_FLAGS_RUNNING_SELF (1u << 0)
+ #define GS_PLUGIN_FLAGS_RUNNING_OTHER (1u << 1)
+ #define GS_PLUGIN_FLAGS_EXCLUSIVE (1u << 2)
+ #define GS_PLUGIN_FLAGS_RECENT (1u << 3)
+ #define GS_PLUGIN_FLAGS_GLOBAL_CACHE (1u << 4)
+ typedef guint64 GsPluginFlags;
+
+ /**
+ * GsPluginError:
+ * @GS_PLUGIN_ERROR_FAILED: Generic failure
+ * @GS_PLUGIN_ERROR_NOT_SUPPORTED: Action not supported
+ * @GS_PLUGIN_ERROR_CANCELLED: Action was cancelled
+ * @GS_PLUGIN_ERROR_NO_NETWORK: No network connection available
+ * @GS_PLUGIN_ERROR_NO_SECURITY: Security policy forbid action
+ * @GS_PLUGIN_ERROR_NO_SPACE: No disk space to allow action
+ * @GS_PLUGIN_ERROR_AUTH_REQUIRED: Authentication was required
+ * @GS_PLUGIN_ERROR_AUTH_INVALID: Provided authentication was invalid
+ * @GS_PLUGIN_ERROR_PIN_REQUIRED: PIN required for authentication
+ * @GS_PLUGIN_ERROR_ACCOUNT_SUSPENDED: User account has been suspended
+ * @GS_PLUGIN_ERROR_ACCOUNT_DEACTIVATED: User account has been deactivated
+ * @GS_PLUGIN_ERROR_PLUGIN_DEPSOLVE_FAILED: The plugins installed are incompatible
+ * @GS_PLUGIN_ERROR_DOWNLOAD_FAILED: The download action failed
+ * @GS_PLUGIN_ERROR_WRITE_FAILED: The save-to-disk failed
+ * @GS_PLUGIN_ERROR_INVALID_FORMAT: The data format is invalid
+ * @GS_PLUGIN_ERROR_DELETE_FAILED: The delete action failed
+ *
+ * The failure error types.
+ **/
+ typedef enum {
+ GS_PLUGIN_ERROR_FAILED,
+ GS_PLUGIN_ERROR_NOT_SUPPORTED,
+ GS_PLUGIN_ERROR_CANCELLED,
+ GS_PLUGIN_ERROR_NO_NETWORK,
+ GS_PLUGIN_ERROR_NO_SECURITY,
+ GS_PLUGIN_ERROR_NO_SPACE,
+ GS_PLUGIN_ERROR_AUTH_REQUIRED,
+ GS_PLUGIN_ERROR_AUTH_INVALID,
+ GS_PLUGIN_ERROR_PIN_REQUIRED,
+ GS_PLUGIN_ERROR_ACCOUNT_SUSPENDED,
+ GS_PLUGIN_ERROR_ACCOUNT_DEACTIVATED,
+ GS_PLUGIN_ERROR_PLUGIN_DEPSOLVE_FAILED,
+ GS_PLUGIN_ERROR_DOWNLOAD_FAILED,
+ GS_PLUGIN_ERROR_WRITE_FAILED,
+ GS_PLUGIN_ERROR_INVALID_FORMAT,
+ GS_PLUGIN_ERROR_DELETE_FAILED,
+ /*< private >*/
+ GS_PLUGIN_ERROR_LAST
+ } GsPluginError;
+
+ /**
+ * GsPluginRefineFlags:
+ * @GS_PLUGIN_REFINE_FLAGS_DEFAULT: No explicit flags set
+ * @GS_PLUGIN_REFINE_FLAGS_USE_HISTORY: Get the historical view
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE: Require the license
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_URL: Require the URL
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION: Require the long description
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE: Require the installed and download sizes
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING: Require the rating
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION: Require the version
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_HISTORY: Require the history
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_SETUP_ACTION: Require enough to install or remove the package
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS: Require update details
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN: Require the origin
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED: Require related packages
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_MENU_PATH: Require the menu path
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_ADDONS: Require available addons
+ * @GS_PLUGIN_REFINE_FLAGS_ALLOW_PACKAGES: Allow packages to be returned
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_SEVERITY: Require update severity
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPGRADE_REMOVED: Require distro upgrades
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE: Require the provenance
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS: Require user-reviews
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS: Require user-ratings
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_KEY_COLORS: Require the key colors
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON: Require the icon to be loaded
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_PERMISSIONS: Require the needed permissions
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME: Require the origin hostname
+ * @GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_UI: Require the origin for UI
+ *
+ * The refine flags.
+ **/
+ #define GS_PLUGIN_REFINE_FLAGS_DEFAULT (0u)
+ #define GS_PLUGIN_REFINE_FLAGS_USE_HISTORY (1u << 0)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE (1u << 1)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_URL (1u << 2)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION (1u << 3)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE (1u << 4)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING (1u << 5)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION (1u << 6)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_HISTORY (1u << 7)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_SETUP_ACTION (1u << 8)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS (1u << 9)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN (1u << 10)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED (1u << 11)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_MENU_PATH (1u << 12)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_ADDONS (1u << 13)
+ #define GS_PLUGIN_REFINE_FLAGS_ALLOW_PACKAGES (1u << 14)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_SEVERITY (1u << 15)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPGRADE_REMOVED (1u << 16)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE (1u << 17)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS (1u << 18)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS (1u << 19)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_KEY_COLORS (1u << 20)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON (1u << 21)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_PERMISSIONS (1u << 22)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME (1u << 23)
+ #define GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_UI (1u << 24)
+ typedef guint64 GsPluginRefineFlags;
+
+ /**
+ * GsPluginRefreshFlags:
+ * @GS_PLUGIN_REFRESH_FLAGS_NONE: Generate new metadata if possible
+ * @GS_PLUGIN_REFRESH_FLAGS_METADATA: Download new metadata
+ * @GS_PLUGIN_REFRESH_FLAGS_PAYLOAD: Download any pending payload
+ * @GS_PLUGIN_REFRESH_FLAGS_INTERACTIVE: Running by user request
+ *
+ * The flags used for refresh. Regeneration and downloading is only
+ * done if the cache is older than the %cache_age.
+ *
+ * The %GS_PLUGIN_REFRESH_FLAGS_METADATA can be used to make sure
+ * there's enough metadata to start the application.
+ * The %GS_PLUGIN_REFRESH_FLAGS_PAYLOAD flag should only be used when
+ * the session is idle and bandwidth is unmetered as the amount of data
+ * and IO may be large.
+ **/
+ typedef enum {
+ GS_PLUGIN_REFRESH_FLAGS_NONE = 0,
+ GS_PLUGIN_REFRESH_FLAGS_METADATA = 1 << 0,
+ GS_PLUGIN_REFRESH_FLAGS_PAYLOAD = 1 << 1,
+ GS_PLUGIN_REFRESH_FLAGS_INTERACTIVE = 1 << 2,
+ /*< private >*/
+ GS_PLUGIN_REFRESH_FLAGS_LAST
+ } GsPluginRefreshFlags;
+
+ /**
+ * GsPluginRule:
+ * @GS_PLUGIN_RULE_CONFLICTS: The plugin conflicts with another
+ * @GS_PLUGIN_RULE_RUN_AFTER: Order the plugin after another
+ * @GS_PLUGIN_RULE_RUN_BEFORE: Order the plugin before another
+ * @GS_PLUGIN_RULE_BETTER_THAN: Results are better than another
+ *
+ * The rules used for ordering plugins.
+ * Plugins are expected to add rules in gs_plugin_initialize().
+ **/
+ typedef enum {
+ GS_PLUGIN_RULE_CONFLICTS,
+ GS_PLUGIN_RULE_RUN_AFTER,
+ GS_PLUGIN_RULE_RUN_BEFORE,
+ GS_PLUGIN_RULE_BETTER_THAN,
+ /*< private >*/
+ GS_PLUGIN_RULE_LAST
+ } GsPluginRule;
+
+ /**
+ * GsPluginAction:
+ * @GS_PLUGIN_ACTION_UNKNOWN: Action is unknown
+ * @GS_PLUGIN_ACTION_SETUP: Plugin setup (internal)
+ * @GS_PLUGIN_ACTION_INSTALL: Install an application
+ * @GS_PLUGIN_ACTION_REMOVE: Remove an application
+ * @GS_PLUGIN_ACTION_UPDATE: Update an application
+ * @GS_PLUGIN_ACTION_SET_RATING: Set rating on an application
+ * @GS_PLUGIN_ACTION_UPGRADE_DOWNLOAD: Download a distro upgrade
+ * @GS_PLUGIN_ACTION_UPGRADE_TRIGGER: Trigger a distro upgrade
+ * @GS_PLUGIN_ACTION_LAUNCH: Launch an application
+ * @GS_PLUGIN_ACTION_UPDATE_CANCEL: Cancel the update
+ * @GS_PLUGIN_ACTION_ADD_SHORTCUT: Add a shortcut to an application
+ * @GS_PLUGIN_ACTION_REMOVE_SHORTCUT: Remove a shortcut to an application
+ * @GS_PLUGIN_ACTION_REVIEW_SUBMIT: Submit a new review
+ * @GS_PLUGIN_ACTION_REVIEW_UPVOTE: Upvote an existing review
+ * @GS_PLUGIN_ACTION_REVIEW_DOWNVOTE: Downvote an existing review
+ * @GS_PLUGIN_ACTION_REVIEW_REPORT: Report an existing review
+ * @GS_PLUGIN_ACTION_REVIEW_REMOVE: Remove a review written by the user
+ * @GS_PLUGIN_ACTION_REVIEW_DISMISS: Dismiss (ignore) a review when moderating
+ * @GS_PLUGIN_ACTION_GET_UPDATES: Get the list of updates
+ * @GS_PLUGIN_ACTION_GET_DISTRO_UPDATES: Get the list of distro updates
+ * @GS_PLUGIN_ACTION_GET_UNVOTED_REVIEWS: Get the list of moderatable reviews
+ * @GS_PLUGIN_ACTION_GET_SOURCES: Get the list of sources
+ * @GS_PLUGIN_ACTION_GET_INSTALLED: Get the list of installed applications
+ * @GS_PLUGIN_ACTION_GET_POPULAR: Get the list of popular applications
+ * @GS_PLUGIN_ACTION_GET_FEATURED: Get the list of featured applications
+ * @GS_PLUGIN_ACTION_SEARCH: Get the search results for a query
+ * @GS_PLUGIN_ACTION_SEARCH_FILES: Get the search results for a file query
+ * @GS_PLUGIN_ACTION_SEARCH_PROVIDES: Get the search results for a provide query
+ * @GS_PLUGIN_ACTION_GET_CATEGORIES: Get the list of categories
+ * @GS_PLUGIN_ACTION_GET_CATEGORY_APPS: Get the apps for a specific category
+ * @GS_PLUGIN_ACTION_REFINE: Refine the application
+ * @GS_PLUGIN_ACTION_REFRESH: Refresh all the sources
+ * @GS_PLUGIN_ACTION_FILE_TO_APP: Convert the file to an application
+ * @GS_PLUGIN_ACTION_AUTH_LOGIN: Authentication login action
+ * @GS_PLUGIN_ACTION_AUTH_LOGOUT: Authentication logout action
+ * @GS_PLUGIN_ACTION_AUTH_REGISTER: Authentication register action
+ * @GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD: Authentication lost password action
++ * @GS_PLUGIN_ACTION_PURCHASE: Purchase an app
+ *
+ * The plugin action.
+ **/
+ typedef enum {
+ GS_PLUGIN_ACTION_UNKNOWN,
+ GS_PLUGIN_ACTION_SETUP,
+ GS_PLUGIN_ACTION_INSTALL,
+ GS_PLUGIN_ACTION_REMOVE,
+ GS_PLUGIN_ACTION_UPDATE,
+ GS_PLUGIN_ACTION_SET_RATING,
+ GS_PLUGIN_ACTION_UPGRADE_DOWNLOAD,
+ GS_PLUGIN_ACTION_UPGRADE_TRIGGER,
+ GS_PLUGIN_ACTION_LAUNCH,
+ GS_PLUGIN_ACTION_UPDATE_CANCEL,
+ GS_PLUGIN_ACTION_ADD_SHORTCUT,
+ GS_PLUGIN_ACTION_REMOVE_SHORTCUT,
+ GS_PLUGIN_ACTION_REVIEW_SUBMIT,
+ GS_PLUGIN_ACTION_REVIEW_UPVOTE,
+ GS_PLUGIN_ACTION_REVIEW_DOWNVOTE,
+ GS_PLUGIN_ACTION_REVIEW_REPORT,
+ GS_PLUGIN_ACTION_REVIEW_REMOVE,
+ GS_PLUGIN_ACTION_REVIEW_DISMISS,
+ GS_PLUGIN_ACTION_GET_UPDATES,
+ GS_PLUGIN_ACTION_GET_DISTRO_UPDATES,
+ GS_PLUGIN_ACTION_GET_UNVOTED_REVIEWS,
+ GS_PLUGIN_ACTION_GET_SOURCES,
+ GS_PLUGIN_ACTION_GET_INSTALLED,
+ GS_PLUGIN_ACTION_GET_POPULAR,
+ GS_PLUGIN_ACTION_GET_FEATURED,
+ GS_PLUGIN_ACTION_SEARCH,
+ GS_PLUGIN_ACTION_SEARCH_FILES,
+ GS_PLUGIN_ACTION_SEARCH_PROVIDES,
+ GS_PLUGIN_ACTION_GET_CATEGORIES,
+ GS_PLUGIN_ACTION_GET_CATEGORY_APPS,
+ GS_PLUGIN_ACTION_REFINE,
+ GS_PLUGIN_ACTION_REFRESH,
+ GS_PLUGIN_ACTION_FILE_TO_APP,
+ GS_PLUGIN_ACTION_AUTH_LOGIN,
+ GS_PLUGIN_ACTION_AUTH_LOGOUT,
+ GS_PLUGIN_ACTION_AUTH_REGISTER,
+ GS_PLUGIN_ACTION_AUTH_LOST_PASSWORD,
++ GS_PLUGIN_ACTION_PURCHASE,
+ /*< private >*/
+ GS_PLUGIN_ACTION_LAST
+ } GsPluginAction;
+
+ G_END_DECLS
+
+ #endif /* __GS_PLUGIN_TYPES_H */
+
+ /* vim: set noexpandtab: */
diff --cc src/gs-plugin-vfuncs.h
index 5fef056,713d351..91bf3ed
--- a/src/gs-plugin-vfuncs.h
+++ b/src/gs-plugin-vfuncs.h
@@@ -40,8 -40,6 +40,7 @@@
#include "gs-app.h"
#include "gs-app-list.h"
#include "gs-category.h"
+#include "gs-price.h"
- #include "gs-payment-method.h"
G_BEGIN_DECLS
@@@ -499,45 -542,6 +543,27 @@@ gboolean gs_plugin_update_cancel (GsP
GError **error);
/**
- * gs_plugin_add_payment_methods:
- * @plugin: a #GsPlugin
- * @payment_methods: (element-type GsPaymentMethod): a #GPtrArray
- * @cancellable: a #GCancellable, or %NULL
- * @error: a #GError, or %NULL
- *
- * Get the available payment methods for purchasing apps.
- *
- * Returns: %TRUE for success or if not relevant
- **/
- gboolean gs_plugin_add_payment_methods (GsPlugin *plugin,
- GPtrArray *payment_methods,
- GCancellable *cancellable,
- GError **error);
-
- /**
+ * gs_plugin_app_purchase:
+ * @plugin: a #GsPlugin
+ * @app: a #GsApp
+ * @price: a #GsPrice
- * @payment_method: (allow-none): a #GsPaymentMethod, or %NULL
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Purchase the application.
+ *
+ * NOTE: Once the action is complete, the plugin must set the new state of @app
+ * to %AS_APP_STATE_AVAILABLE.
+ *
+ * Returns: %TRUE for success or if not relevant
+ **/
+gboolean gs_plugin_app_purchase (GsPlugin *plugin,
+ GsApp *app,
+ GsPrice *price,
- GsPaymentMethod *payment_method,
+ GCancellable *cancellable,
+ GError **error);
+
+/**
* gs_plugin_app_install:
* @plugin: a #GsPlugin
* @app: a #GsApp
diff --cc src/gs-plugin.h
index 3400959,648bfcd..70690fe
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@@ -32,7 -32,8 +32,9 @@@
#include "gs-app-list.h"
#include "gs-auth.h"
#include "gs-category.h"
+ #include "gs-plugin-event.h"
+ #include "gs-plugin-types.h"
+#include "gs-price.h"
G_BEGIN_DECLS
diff --cc src/gs-purchase-dialog.c
index 58d799f,0000000..a9bbbcd
mode 100644,000000..100644
--- a/src/gs-purchase-dialog.c
+++ b/src/gs-purchase-dialog.c
@@@ -1,107 -1,0 +1,79 @@@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-purchase-dialog.h"
+
+struct _GsPurchaseDialog
+{
+ GtkDialog parent_instance;
+
+ GtkWidget *label_title;
+ GtkWidget *combo_payment_method;
+ GtkListStore *model_payment_method;
+};
+
+G_DEFINE_TYPE (GsPurchaseDialog, gs_purchase_dialog, GTK_TYPE_DIALOG)
+
+void
+gs_purchase_dialog_set_app (GsPurchaseDialog *dialog, GsApp *app)
+{
+ g_return_if_fail (GS_IS_PURCHASE_DIALOG (dialog));
+}
+
- void
- gs_purchase_dialog_set_payment_methods (GsPurchaseDialog *dialog, GPtrArray *payment_methods)
- {
- guint i;
-
- g_return_if_fail (GS_IS_PURCHASE_DIALOG (dialog));
-
- gtk_list_store_clear (dialog->model_payment_method);
- for (i = 0; i < payment_methods->len; i++) {
- GtkTreeIter iter;
- GsPaymentMethod *method = payment_methods->pdata[i];
-
- gtk_list_store_append (dialog->model_payment_method, &iter);
- gtk_list_store_set (dialog->model_payment_method, &iter,
- 0, gs_payment_method_get_description (method),
- 1, method,
- -1);
- }
- }
-
-
+GsPrice *
+gs_purchase_dialog_get_price (GsPurchaseDialog *dialog)
+{
+ g_return_val_if_fail (GS_IS_PURCHASE_DIALOG (dialog), NULL);
+ return NULL;
+}
+
- GsPaymentMethod *
- gs_purchase_dialog_get_payment_method (GsPurchaseDialog *dialog)
- {
- g_return_val_if_fail (GS_IS_PURCHASE_DIALOG (dialog), NULL);
- return NULL;
- }
-
+static void
+gs_purchase_dialog_init (GsPurchaseDialog *dialog)
+{
+ gtk_widget_init_template (GTK_WIDGET (dialog));
+}
+
+static void
+gs_purchase_dialog_class_init (GsPurchaseDialogClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/Software/gs-purchase-dialog.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GsPurchaseDialog, label_title);
+ gtk_widget_class_bind_template_child (widget_class, GsPurchaseDialog, combo_payment_method);
+ gtk_widget_class_bind_template_child (widget_class, GsPurchaseDialog, model_payment_method);
+}
+
+GtkWidget *
+gs_purchase_dialog_new (void)
+{
+ return GTK_WIDGET (g_object_new (GS_TYPE_PURCHASE_DIALOG,
+ "use-header-bar", TRUE,
+ NULL));
+}
+
+/* vim: set noexpandtab: */
diff --cc src/gs-purchase-dialog.h
index 9ef63a1,0000000..fb3714e
mode 100644,000000..100644
--- a/src/gs-purchase-dialog.h
+++ b/src/gs-purchase-dialog.h
@@@ -1,53 -1,0 +1,47 @@@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_PURCHASE_DIALOG_H
+#define GS_PURCHASE_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#include "gs-app.h"
+#include "gs-price.h"
- #include "gs-payment-method.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PURCHASE_DIALOG (gs_purchase_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsPurchaseDialog, gs_purchase_dialog, GS, PURCHASE_DIALOG, GtkDialog)
+
+GtkWidget *gs_purchase_dialog_new (void);
+
+void gs_purchase_dialog_set_app (GsPurchaseDialog *dialog,
+ GsApp *app);
+
- void gs_purchase_dialog_set_payment_methods (GsPurchaseDialog *dialog,
- GPtrArray *payment_methods);
-
+GsPrice *gs_purchase_dialog_get_price (GsPurchaseDialog *dialog);
+
- GsPaymentMethod *gs_purchase_dialog_get_payment_method (GsPurchaseDialog *dialog);
-
+G_END_DECLS
+
+#endif /* GS_PURCHASE_DIALOG_H */
+
+/* vim: set noexpandtab: */
diff --cc src/plugins/gs-plugin-dummy.c
index a9290f1,80cf496..994f158
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@@ -651,43 -658,6 +691,24 @@@ gs_plugin_update_cancel (GsPlugin *plug
return TRUE;
}
+/**
- * gs_plugin_add_payment_methods:
- */
- gboolean
- gs_plugin_add_payment_methods (GsPlugin *plugin,
- GPtrArray *payment_methods,
- GCancellable *cancellable,
- GError **error)
- {
- GsPaymentMethod *method;
- g_debug ("Adding payment methods");
- method = gs_payment_method_new ();
- gs_payment_method_set_description (method, "Test Payment Method");
- gs_payment_method_add_metadata (method, "card-number", "0000 0000 0000 0000");
- g_ptr_array_add (payment_methods, method);
- return TRUE;
- }
-
- /**
+ * gs_plugin_app_purchase:
+ */
+gboolean
+gs_plugin_app_purchase (GsPlugin *plugin,
+ GsApp *app,
+ GsPrice *price,
- GsPaymentMethod *payment_method,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_debug ("Purchasing app");
+ gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+ return TRUE;
+}
+
+/**
+ * gs_plugin_review_submit:
+ */
gboolean
gs_plugin_review_submit (GsPlugin *plugin,
GsApp *app,
diff --cc src/plugins/gs-plugin-snap.c
index 80d2877,7310c9f..3b4edc7
--- a/src/plugins/gs-plugin-snap.c
+++ b/src/plugins/gs-plugin-snap.c
@@@ -672,71 -363,6 +380,35 @@@ progress_cb (JsonObject *result, gpoint
}
gboolean
+gs_plugin_app_purchase (GsPlugin *plugin,
+ GsApp *app,
+ GsPrice *price,
- GsPaymentMethod *payment_method,
+ GCancellable *cancellable,
+ GError **error)
+{
- g_autoptr(JsonBuilder) builder = NULL;
- g_autofree gchar *value = NULL;
- g_autoptr(JsonNode) json_root = NULL;
- g_autoptr(JsonGenerator) json_generator = NULL;
- g_autofree gchar *data = NULL;
- guint status_code;
- g_autofree gchar *reason_phrase = NULL;
- g_autofree gchar *response_type = NULL;
- g_autofree gchar *response = NULL;
++ g_autofree gchar *macaroon = NULL;
++ g_auto(GStrv) discharges = NULL;
+
+ /* We can only buy apps we know of */
+ if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
+ return TRUE;
+
- builder = json_builder_new ();
- json_builder_begin_object (builder);
- json_builder_set_member_name (builder, "snap-id");
- json_builder_add_string_value (builder, gs_app_get_metadata_item (app, "snap::id"));
- json_builder_set_member_name (builder, "snap-name");
- json_builder_add_string_value (builder, gs_app_get_id (app));
- json_builder_set_member_name (builder, "snap-price");
- value = g_strdup_printf ("%f", gs_price_get_amount (price));
- json_builder_add_string_value (builder, value);
- json_builder_set_member_name (builder, "currency");
- json_builder_add_string_value (builder, gs_price_get_currency (price));
- json_builder_end_object (builder);
-
- json_root = json_builder_get_root (builder);
- json_generator = json_generator_new ();
- json_generator_set_pretty (json_generator, TRUE);
- json_generator_set_root (json_generator, json_root);
- data = json_generator_to_data (json_generator, NULL);
- if (data == NULL) {
- g_set_error_literal (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "Failed to generate JSON request");
- return FALSE;
- }
-
- if (!gs_snapd_request ("POST", "/v2/buy", data,
- NULL, NULL,
- &status_code, &reason_phrase,
- &response_type, &response, NULL,
- cancellable, error))
- return FALSE;
-
- if (status_code != SOUP_STATUS_OK) {
- // FIXME
- return FALSE;
- }
++ get_macaroon (plugin, &macaroon, &discharges);
+
+ gs_app_set_state (app, AS_APP_STATE_PURCHASING);
++ if (!gs_snapd_buy (macaroon, discharges,
++ gs_app_get_metadata_item (app, "snap::id"),
++ gs_price_get_amount (price),
++ gs_price_get_currency (price),
++ cancellable, error))
++ gs_app_set_state_recover (app);
++ return FALSE;
+ gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
++
+ return TRUE;
+}
+
+gboolean
gs_plugin_app_install (GsPlugin *plugin,
GsApp *app,
GCancellable *cancellable,
diff --cc src/plugins/gs-snapd.c
index a7e2cb8,4a75bb6..c0ce1ff
--- a/src/plugins/gs-snapd.c
+++ b/src/plugins/gs-snapd.c
@@@ -338,41 -336,327 +336,380 @@@ parse_result (const gchar *response, co
if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) {
g_set_error_literal (error,
GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
+ GS_PLUGIN_ERROR_INVALID_FORMAT,
"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_INVALID_FORMAT,
+ "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_INVALID_FORMAT,
+ "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 %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 (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 %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);
+ }
+
+ JsonObject *
+ gs_snapd_get_interfaces (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;
+ JsonObject *result;
+
+ if (!send_request ("GET", "/v2/interfaces", 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;
}
+
+ 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);
+ }
+
++gboolean
++gs_snapd_buy (const gchar *macaroon, gchar **discharges, const gchar *id, gdouble price, const gchar
*currency, GCancellable *cancellable, GError **error)
++{
++ g_autoptr(JsonBuilder) builder = NULL;
++ g_autofree gchar *value = NULL;
++ g_autoptr(JsonNode) json_root = NULL;
++ g_autoptr(JsonGenerator) json_generator = NULL;
++ g_autofree gchar *data = NULL;
++ guint status_code;
++ g_autofree gchar *reason_phrase = NULL;
++
++ builder = json_builder_new ();
++ json_builder_begin_object (builder);
++ json_builder_set_member_name (builder, "snap-id");
++ json_builder_add_string_value (builder, id);
++ json_builder_set_member_name (builder, "price");
++ json_builder_add_double_value (builder, price);
++ json_builder_set_member_name (builder, "currency");
++ json_builder_add_string_value (builder, currency);
++ json_builder_end_object (builder);
++
++ json_root = json_builder_get_root (builder);
++ json_generator = json_generator_new ();
++ json_generator_set_pretty (json_generator, TRUE);
++ json_generator_set_root (json_generator, json_root);
++ data = json_generator_to_data (json_generator, NULL);
++ if (data == NULL) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
- "snapd response does not contain a \"result\" field");
++ "Failed to generate JSON request");
++ return FALSE;
++ }
++
++ if (!send_request ("POST", "/v2/buy", NULL,
++ macaroon, discharges,
++ &status_code, &reason_phrase,
++ NULL, NULL, 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 %u: %s",
++ status_code, reason_phrase);
+ 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)
+ static JsonObject *
+ get_changes (const gchar *macaroon, gchar **discharges,
+ const gchar *change_id,
+ GCancellable *cancellable, GError **error)
{
- g_autoptr(JsonObject) result = 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, *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 %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);
+ }
- if (!gs_snapd_parse_result (response_type, response, &result, error))
+ 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 (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;
+ 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;
+ }
+
+ 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_NOT_SUPPORTED,
+ "snapd operation finished with status %s", status);
+ return FALSE;
+ }
return TRUE;
}
diff --cc src/plugins/gs-snapd.h
index 7c0eb9c,af28bd1..f2bcaa8
--- a/src/plugins/gs-snapd.h
+++ b/src/plugins/gs-snapd.h
@@@ -25,31 -25,53 +25,61 @@@
#include <gio/gio.h>
#include <json-glib/json-glib.h>
- gboolean gs_snapd_exists (void);
-
- 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);
-
- gboolean gs_snapd_parse_result (const gchar *response_type,
- const gchar *response,
- JsonObject **result,
- GError **error);
-
- gboolean gs_snapd_parse_error (const gchar *response_type,
- const gchar *response,
- gchar **message,
- gchar **kind,
- GError **error);
+ typedef void (*GsSnapdProgressCallback) (JsonObject *object, gpointer user_data);
+ gboolean gs_snapd_exists (void);
+
+ 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,
+ GCancellable *cancellable,
+ GError **error);
+
+ JsonArray *gs_snapd_find (const gchar *macaroon,
+ gchar **discharges,
+ gchar **values,
+ GCancellable *cancellable,
+ GError **error);
+
+ JsonObject *gs_snapd_get_interfaces (const gchar *macaroon,
+ gchar **discharges,
+ GCancellable *cancellable,
+ GError **error);
+
++gboolean gs_snapd_buy (const gchar *macaroon,
++ gchar **discharges,
++ const gchar *id,
++ gdouble price,
++ const gchar *currency,
++ GCancellable *cancellable,
++ GError **error);
++
+ 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]