summaryrefslogtreecommitdiff
path: root/modules/printbackends/gtkcloudprintaccount.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/printbackends/gtkcloudprintaccount.c')
-rw-r--r--modules/printbackends/gtkcloudprintaccount.c662
1 files changed, 662 insertions, 0 deletions
diff --git a/modules/printbackends/gtkcloudprintaccount.c b/modules/printbackends/gtkcloudprintaccount.c
new file mode 100644
index 0000000000..ee65a18287
--- /dev/null
+++ b/modules/printbackends/gtkcloudprintaccount.c
@@ -0,0 +1,662 @@
+/* gtkcloudprintaccount.c: Google Cloud Print account class
+ * Copyright (C) 2014, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <rest/oauth2-proxy.h>
+#include <rest/rest-proxy.h>
+#include <rest/rest-proxy-call.h>
+#include <json-glib/json-glib.h>
+
+#include <gtk/gtkunixprint.h>
+#include "gtkcloudprintaccount.h"
+#include "gtkprintercloudprint.h"
+
+#define CLOUDPRINT_PROXY "GTK+"
+
+#define ACCOUNT_IFACE "org.gnome.OnlineAccounts.Account"
+#define O_AUTH2_BASED_IFACE "org.gnome.OnlineAccounts.OAuth2Based"
+
+#define GTK_CLOUDPRINT_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccountClass))
+#define GTK_IS_CLOUDPRINT_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLOUDPRINT_ACCOUNT))
+#define GTK_CLOUDPRINT_ACCOUNT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccountClass))
+
+static GObjectClass *gtk_cloudprint_account_parent_class;
+static GType gtk_cloudprint_account_type = 0;
+
+typedef struct _GtkCloudprintAccountClass GtkCloudprintAccountClass;
+
+struct _GtkCloudprintAccountClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GtkCloudprintAccount
+{
+ GObject parent_instance;
+
+ gchar *printer_id;
+ gchar *goa_path;
+ gchar *presentation_identity;
+ RestProxy *rest_proxy;
+ gchar *oauth2_access_token;
+};
+
+static void gtk_cloudprint_account_class_init (GtkCloudprintAccountClass *class);
+static void gtk_cloudprint_account_init (GtkCloudprintAccount *impl);
+static void gtk_cloudprint_account_finalize (GObject *object);
+
+void
+gtk_cloudprint_account_register_type (GTypeModule *module)
+{
+ const GTypeInfo cloudprint_account_info =
+ {
+ sizeof (GtkCloudprintAccountClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_cloudprint_account_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkCloudprintAccount),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_cloudprint_account_init,
+ };
+
+ gtk_cloudprint_account_type = g_type_module_register_type (module,
+ G_TYPE_OBJECT,
+ "GtkCloudprintAccount",
+ &cloudprint_account_info, 0);
+}
+
+/*
+ * GtkCloudprintAccount
+ */
+GType
+gtk_cloudprint_account_get_type (void)
+{
+ return gtk_cloudprint_account_type;
+}
+
+/**
+ * gtk_cloudprint_account_new:
+ *
+ * Creates a new #GtkCloudprintAccount object, representing a Google
+ * Cloud Print account and its state data.
+ *
+ * Returns: the new #GtkCloudprintAccount object
+ **/
+GtkCloudprintAccount *
+gtk_cloudprint_account_new (const gchar *id,
+ const gchar *path,
+ const gchar *presentation_identity)
+{
+ GtkCloudprintAccount *account = g_object_new (GTK_TYPE_CLOUDPRINT_ACCOUNT,
+ NULL);
+ account->printer_id = g_strdup (id);
+ account->goa_path = g_strdup (path);
+ account->presentation_identity = g_strdup (presentation_identity);
+ return account;
+}
+
+static void
+gtk_cloudprint_account_class_init (GtkCloudprintAccountClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gtk_cloudprint_account_parent_class = g_type_class_peek_parent (klass);
+ gobject_class->finalize = gtk_cloudprint_account_finalize;
+}
+
+static void
+gtk_cloudprint_account_init (GtkCloudprintAccount *account)
+{
+ account->printer_id = NULL;
+ account->goa_path = NULL;
+ account->presentation_identity = NULL;
+ account->rest_proxy = NULL;
+ account->oauth2_access_token = NULL;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: +GtkCloudprintAccount(%p)\n",
+ account));
+}
+
+static void
+gtk_cloudprint_account_finalize (GObject *object)
+{
+ GtkCloudprintAccount *account;
+
+ account = GTK_CLOUDPRINT_ACCOUNT (object);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: -GtkCloudprintAccount(%p)\n",
+ account));
+
+ g_clear_object (&(account->rest_proxy));
+ g_clear_pointer (&(account->printer_id), g_free);
+ g_clear_pointer (&(account->goa_path), g_free);
+ g_clear_pointer (&(account->presentation_identity), g_free);
+ g_clear_pointer (&(account->oauth2_access_token), g_free);
+
+ G_OBJECT_CLASS (gtk_cloudprint_account_parent_class)->finalize (object);
+}
+
+static JsonParser *
+cloudprint_json_parse (RestProxyCall *call, JsonObject **result, GError **error)
+{
+ JsonParser *json_parser = json_parser_new ();
+ JsonNode *root;
+ JsonObject *json_object;
+ gboolean success = FALSE;
+
+ if (!json_parser_load_from_data (json_parser,
+ rest_proxy_call_get_payload (call),
+ rest_proxy_call_get_payload_length (call),
+ error))
+ {
+ g_object_unref (json_parser);
+ return NULL;
+ }
+
+ root = json_parser_get_root (json_parser);
+ if (JSON_NODE_TYPE (root) != JSON_NODE_OBJECT)
+ {
+ if (error != NULL)
+ *error = g_error_new_literal (gtk_print_error_quark (),
+ GTK_PRINT_ERROR_INTERNAL_ERROR,
+ "Bad reply");
+
+ g_object_unref (json_parser);
+ return NULL;
+ }
+
+ json_object = json_node_get_object (root);
+ if (json_object_has_member (json_object, "success"))
+ success = json_object_get_boolean_member (json_object, "success");
+
+ if (!success)
+ {
+ const gchar *message = "(no message)";
+
+ if (json_object_has_member (json_object, "message"))
+ message = json_object_get_string_member (json_object, "message");
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: unsuccessful submit: %s\n",
+ message));
+
+ if (error != NULL)
+ *error = g_error_new_literal (gtk_print_error_quark (),
+ GTK_PRINT_ERROR_INTERNAL_ERROR,
+ message);
+
+ g_object_unref (json_parser);
+ return NULL;
+ }
+
+ if (result != NULL)
+ *result = json_node_dup_object (root);
+
+ return json_parser;
+}
+
+static void
+gtk_cloudprint_account_search_rest_call_cb (RestProxyCall *call,
+ const GError *cb_error,
+ GObject *weak_object,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GtkCloudprintAccount *account = g_task_get_task_data (task);
+ JsonParser *json_parser = NULL;
+ JsonObject *result;
+ JsonNode *printers = NULL;
+ GError *error = NULL;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) 'search' REST call "
+ "returned\n", account));
+
+ if (cb_error != NULL)
+ {
+ error = g_error_copy (cb_error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (g_task_return_error_if_cancelled (task))
+ {
+ g_object_unref (task);
+ return;
+ }
+
+ if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_object_unref (json_parser);
+
+ if (json_object_has_member (result, "printers"))
+ printers = json_object_dup_member (result, "printers");
+
+ json_object_unref (result);
+ if (printers == NULL)
+ {
+ g_task_return_new_error (task,
+ gtk_print_error_quark (),
+ GTK_PRINT_ERROR_INTERNAL_ERROR,
+ "Bad reply to 'search' request");
+ return;
+ }
+
+ g_task_return_pointer (task,
+ printers,
+ (GDestroyNotify) json_node_free);
+ g_object_unref (task);
+}
+
+static void
+gtk_cloudprint_account_got_oauth2_access_token_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GtkCloudprintAccount *account = g_task_get_task_data (task);
+ RestProxyCall *call;
+ RestProxy *rest;
+ GVariant *output;
+ gint expires_in = 0;
+ GError *error = NULL;
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
+ result,
+ &error);
+ g_object_unref (source);
+
+ if (output == NULL)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_variant_get (output, "(si)",
+ &account->oauth2_access_token,
+ &expires_in);
+ g_variant_unref (output);
+
+ rest = oauth2_proxy_new_with_token (account->printer_id,
+ account->oauth2_access_token,
+ "https://accounts.google.com/o/oauth2/token",
+ "https://www.google.com/cloudprint/",
+ FALSE);
+
+ if (rest == NULL)
+ {
+ g_task_return_new_error (task,
+ gtk_print_error_quark (),
+ GTK_PRINT_ERROR_INTERNAL_ERROR,
+ "REST proxy creation failed");
+ g_object_unref (task);
+ return;
+ }
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) 'search' REST call\n",
+ account));
+
+ account->rest_proxy = g_object_ref (rest);
+
+ call = rest_proxy_new_call (REST_PROXY (rest));
+ g_object_unref (rest);
+ rest_proxy_call_set_function (call, "search");
+ rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY);
+ rest_proxy_call_add_param (call, "connection_status", "ALL");
+ if (!rest_proxy_call_async (call,
+ gtk_cloudprint_account_search_rest_call_cb,
+ NULL,
+ task,
+ &error))
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ }
+
+ g_object_unref (call);
+}
+
+static void
+gtk_cloudprint_account_ensure_credentials_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GtkCloudprintAccount *account = g_task_get_task_data (task);
+ GVariant *output;
+ gint expires_in = 0;
+ GError *error = NULL;
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
+ result,
+ &error);
+
+ if (output == NULL)
+ {
+ g_object_unref (source);
+ if (error->domain != G_DBUS_ERROR ||
+ (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN &&
+ error->code != G_DBUS_ERROR_UNKNOWN_METHOD))
+ g_task_return_error (task, error);
+ else
+ /* Return an empty list. */
+ g_task_return_pointer (task,
+ json_node_new (JSON_NODE_ARRAY),
+ (GDestroyNotify) json_node_free);
+
+ g_object_unref (task);
+ return;
+ }
+
+ g_variant_get (output, "(i)",
+ &expires_in);
+ g_variant_unref (output);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) getting access token\n",
+ account));
+
+ g_dbus_connection_call (G_DBUS_CONNECTION (source),
+ ONLINE_ACCOUNTS_BUS,
+ account->goa_path,
+ O_AUTH2_BASED_IFACE,
+ "GetAccessToken",
+ NULL,
+ G_VARIANT_TYPE ("(si)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ g_task_get_cancellable (task),
+ gtk_cloudprint_account_got_oauth2_access_token_cb,
+ task);
+}
+
+void
+gtk_cloudprint_account_search (GtkCloudprintAccount *account,
+ GDBusConnection *dbus_connection,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task = g_task_new (G_OBJECT (account),
+ cancellable,
+ callback,
+ user_data);
+ g_task_set_task_data (task,
+ g_object_ref (account),
+ (GDestroyNotify) g_object_unref);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) ensuring credentials\n",
+ account));
+
+ g_dbus_connection_call (g_object_ref (dbus_connection),
+ ONLINE_ACCOUNTS_BUS,
+ account->goa_path,
+ ACCOUNT_IFACE,
+ "EnsureCredentials",
+ NULL,
+ G_VARIANT_TYPE ("(i)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ gtk_cloudprint_account_ensure_credentials_cb,
+ task);
+}
+
+JsonNode *
+gtk_cloudprint_account_search_finish (GtkCloudprintAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, account), NULL);
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+gtk_cloudprint_account_printer_rest_call_cb (RestProxyCall *call,
+ const GError *cb_error,
+ GObject *weak_object,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GtkCloudprintAccount *account = g_task_get_task_data (task);
+ JsonParser *json_parser = NULL;
+ JsonObject *result;
+ GError *error = NULL;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) 'printer' REST call "
+ "returned\n", account));
+
+ if (cb_error != NULL)
+ {
+ error = g_error_copy (cb_error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (g_task_return_error_if_cancelled (task))
+ {
+ g_object_unref (task);
+ return;
+ }
+
+ if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_object_unref (json_parser);
+ g_task_return_pointer (task,
+ result,
+ (GDestroyNotify) json_object_unref);
+ g_object_unref (task);
+}
+
+void
+gtk_cloudprint_account_printer (GtkCloudprintAccount *account,
+ const gchar *printerid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ RestProxyCall *call;
+ GTask *task;
+ GError *error = NULL;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) 'printer' REST call for "
+ "printer id %s", account, printerid));
+
+ task = g_task_new (G_OBJECT (account), cancellable, callback, user_data);
+
+ g_task_set_task_data (task,
+ g_object_ref (account),
+ (GDestroyNotify) g_object_unref);
+
+ call = rest_proxy_new_call (REST_PROXY (account->rest_proxy));
+ rest_proxy_call_set_function (call, "printer");
+ rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY);
+ rest_proxy_call_add_param (call, "printerid", printerid);
+ if (!rest_proxy_call_async (call,
+ gtk_cloudprint_account_printer_rest_call_cb,
+ NULL,
+ task,
+ &error))
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ }
+
+ g_object_unref (call);
+}
+
+JsonObject *
+gtk_cloudprint_account_printer_finish (GtkCloudprintAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, account), NULL);
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+gtk_cloudprint_account_submit_rest_call_cb (RestProxyCall *call,
+ const GError *cb_error,
+ GObject *weak_object,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GtkCloudprintAccount *account = g_task_get_task_data (task);
+ JsonParser *json_parser = NULL;
+ JsonObject *result;
+ GError *error = NULL;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) 'submit' REST call "
+ "returned\n", account));
+
+ if (cb_error != NULL)
+ {
+ error = g_error_copy (cb_error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (g_task_return_error_if_cancelled (task))
+ {
+ g_object_unref (task);
+ return;
+ }
+
+ if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_object_unref (json_parser);
+ g_task_return_pointer (task,
+ result,
+ (GDestroyNotify) json_object_unref);
+ g_object_unref (task);
+}
+
+void
+gtk_cloudprint_account_submit (GtkCloudprintAccount *account,
+ GtkPrinterCloudprint *printer,
+ GMappedFile *file,
+ const gchar *title,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ RestProxyCall *call;
+ gchar *printerid = NULL;
+ RestParam *param;
+ GError *error = NULL;
+ gchar *auth;
+
+ g_object_get (printer,
+ "printer-id", &printerid,
+ NULL);
+
+ g_warn_if_fail (printerid != NULL);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) 'submit' REST call for "
+ "printer id %s\n", account, printerid));
+
+ task = g_task_new (G_OBJECT (account),
+ cancellable,
+ callback,
+ user_data);
+
+ g_task_set_task_data (task,
+ g_object_ref (account),
+ (GDestroyNotify) g_object_unref);
+
+ call = rest_proxy_new_call (REST_PROXY (account->rest_proxy));
+ rest_proxy_call_set_method (call, "POST");
+ rest_proxy_call_set_function (call, "submit");
+
+ auth = g_strdup_printf ("Bearer %s", account->oauth2_access_token);
+ rest_proxy_call_add_header (call, "Authorization", auth);
+ g_free (auth);
+ rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY);
+
+ rest_proxy_call_add_param (call, "printerid", printerid);
+ g_free (printerid);
+
+ rest_proxy_call_add_param (call, "contentType", "dataUrl");
+ rest_proxy_call_add_param (call, "title", title);
+ param = rest_param_new_with_owner ("content",
+ g_mapped_file_get_contents (file),
+ g_mapped_file_get_length (file),
+ "dataUrl",
+ NULL,
+ file,
+ (GDestroyNotify) g_mapped_file_unref);
+ rest_proxy_call_add_param_full (call, param);
+
+ if (!rest_proxy_call_async (call,
+ gtk_cloudprint_account_submit_rest_call_cb,
+ NULL,
+ task,
+ &error))
+ {
+ g_task_return_error (task, error);
+ g_object_unref (call);
+ g_object_unref (task);
+ return;
+ }
+
+ g_object_unref (call);
+}
+
+JsonObject *
+gtk_cloudprint_account_submit_finish (GtkCloudprintAccount *account,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, account), NULL);
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+const gchar *
+gtk_cloudprint_account_get_presentation_identity (GtkCloudprintAccount *account)
+{
+ return account->presentation_identity;
+}