summaryrefslogtreecommitdiff
path: root/modules/printbackends/cloudprint
diff options
context:
space:
mode:
Diffstat (limited to 'modules/printbackends/cloudprint')
-rw-r--r--modules/printbackends/cloudprint/Makefile.am44
-rw-r--r--modules/printbackends/cloudprint/gtkcloudprintaccount.c664
-rw-r--r--modules/printbackends/cloudprint/gtkcloudprintaccount.h74
-rw-r--r--modules/printbackends/cloudprint/gtkprintbackendcloudprint.c1053
-rw-r--r--modules/printbackends/cloudprint/gtkprintbackendcloudprint.h40
-rw-r--r--modules/printbackends/cloudprint/gtkprintercloudprint.c231
-rw-r--r--modules/printbackends/cloudprint/gtkprintercloudprint.h45
7 files changed, 2151 insertions, 0 deletions
diff --git a/modules/printbackends/cloudprint/Makefile.am b/modules/printbackends/cloudprint/Makefile.am
new file mode 100644
index 0000000000..6408d4cbbf
--- /dev/null
+++ b/modules/printbackends/cloudprint/Makefile.am
@@ -0,0 +1,44 @@
+include $(top_srcdir)/Makefile.decl
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+backenddir = $(libdir)/gtk-3.0/$(GTK_BINARY_VERSION)/printbackends
+
+backend_LTLIBRARIES = libprintbackend-cloudprint.la
+
+libprintbackend_cloudprint_la_SOURCES = \
+ gtkprintbackendcloudprint.h \
+ gtkprintbackendcloudprint.c \
+ gtkprintercloudprint.h \
+ gtkprintercloudprint.c \
+ gtkcloudprintaccount.h \
+ gtkcloudprintaccount.c
+
+libprintbackend_cloudprint_la_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/gtk \
+ -I$(top_builddir)/gtk \
+ -I$(top_srcdir)/gdk \
+ -I$(top_builddir)/gdk \
+ -DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED \
+ $(AM_CPPFLAGS)
+
+libprintbackend_cloudprint_la_CFLAGS = \
+ $(GTK_DEP_CFLAGS) \
+ $(GTK_DEBUG_FLAGS) \
+ $(REST_CFLAGS) \
+ $(JSON_GLIB_CFLAGS) \
+ $(AM_CFLAGS)
+
+libprintbackend_cloudprint_la_LDFLAGS = \
+ -avoid-version -module $(no_undefined)
+
+libprintbackend_cloudprint_la_LIBADD = \
+ $(top_builddir)/gtk/libgtk-3.la \
+ $(REST_LIBS) \
+ $(JSON_GLIB_LIBS) \
+ $(GTK_DEP_LIBS)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/printbackends/cloudprint/gtkcloudprintaccount.c b/modules/printbackends/cloudprint/gtkcloudprintaccount.c
new file mode 100644
index 0000000000..34f724cc9c
--- /dev/null
+++ b/modules/printbackends/cloudprint/gtkcloudprintaccount.c
@@ -0,0 +1,664 @@
+/* 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.
+ *
+ * Return value: 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;
+ GError *error = NULL;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: (%p) 'printer' REST call for "
+ "printer id %s", account, printerid));
+
+ 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);
+
+ 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;
+}
diff --git a/modules/printbackends/cloudprint/gtkcloudprintaccount.h b/modules/printbackends/cloudprint/gtkcloudprintaccount.h
new file mode 100644
index 0000000000..ef788743d3
--- /dev/null
+++ b/modules/printbackends/cloudprint/gtkcloudprintaccount.h
@@ -0,0 +1,74 @@
+/* gtkcloudprintaccount.h: 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/>.
+ */
+
+#ifndef __GTK_CLOUDPRINT_ACCOUNT_H__
+#define __GTK_CLOUDPRINT_ACCOUNT_H__
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "gtkprintbackendcloudprint.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CLOUDPRINT_ACCOUNT (gtk_cloudprint_account_get_type ())
+#define GTK_CLOUDPRINT_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccount))
+#define GTK_IS_CLOUDPRINT_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT))
+
+typedef struct _GtkPrinterCloudprint GtkPrinterCloudprint;
+typedef struct _GtkCloudprintAccount GtkCloudprintAccount;
+
+void gtk_cloudprint_account_register_type (GTypeModule *module);
+GtkCloudprintAccount *gtk_cloudprint_account_new (const gchar *id,
+ const gchar *path,
+ const gchar *presentation_identity);
+GType gtk_cloudprint_account_get_type (void) G_GNUC_CONST;
+
+void gtk_cloudprint_account_search (GtkCloudprintAccount *account,
+ GDBusConnection *connection,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+JsonNode *gtk_cloudprint_account_search_finish (GtkCloudprintAccount *account,
+ GAsyncResult *result,
+ GError **error);
+
+void gtk_cloudprint_account_printer (GtkCloudprintAccount *account,
+ const gchar *printerid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+JsonObject *gtk_cloudprint_account_printer_finish (GtkCloudprintAccount *account,
+ GAsyncResult *result,
+ GError **error);
+
+void gtk_cloudprint_account_submit (GtkCloudprintAccount *account,
+ GtkPrinterCloudprint *printer,
+ GMappedFile *file,
+ const gchar *title,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+JsonObject *gtk_cloudprint_account_submit_finish (GtkCloudprintAccount *account,
+ GAsyncResult *result,
+ GError **error);
+
+const gchar *gtk_cloudprint_account_get_presentation_identity (GtkCloudprintAccount *account);
+
+G_END_DECLS
+
+#endif /* __GTK_CLOUDPRINT_ACCOUNT_H__ */
diff --git a/modules/printbackends/cloudprint/gtkprintbackendcloudprint.c b/modules/printbackends/cloudprint/gtkprintbackendcloudprint.c
new file mode 100644
index 0000000000..c032691ce0
--- /dev/null
+++ b/modules/printbackends/cloudprint/gtkprintbackendcloudprint.c
@@ -0,0 +1,1053 @@
+/* gtkprintbackendcloudprint.c: Google Cloud Print implementation of
+ * GtkPrintBackend
+ * 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 <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <cairo.h>
+#include <cairo-pdf.h>
+#include <cairo-ps.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtkprintbackend.h>
+#include <gtk/gtkunixprint.h>
+#include <gtk/gtkprinter-private.h>
+
+#include "gtkprintbackendcloudprint.h"
+#include "gtkcloudprintaccount.h"
+#include "gtkprintercloudprint.h"
+
+typedef struct _GtkPrintBackendCloudprintClass GtkPrintBackendCloudprintClass;
+
+#define GTK_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass))
+#define GTK_IS_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT))
+#define GTK_PRINT_BACKEND_CLOUDPRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass))
+
+#define _STREAM_MAX_CHUNK_SIZE 8192
+
+#define ONLINE_ACCOUNTS_PATH "/org/gnome/OnlineAccounts"
+#define OBJECT_MANAGER_IFACE "org.freedesktop.DBus.ObjectManager"
+
+static GType print_backend_cloudprint_type = 0;
+
+struct _GtkPrintBackendCloudprintClass
+{
+ GtkPrintBackendClass parent_class;
+};
+
+struct _GtkPrintBackendCloudprint
+{
+ GtkPrintBackend parent_instance;
+ GCancellable *cancellable;
+ guint accounts_searching;
+};
+
+struct
+{
+ gchar *id;
+ gchar *path;
+ gchar *presentation_identity;
+} typedef TGOAAccount;
+
+static GObjectClass *backend_parent_class;
+static void gtk_print_backend_cloudprint_class_init (GtkPrintBackendCloudprintClass *class);
+static void gtk_print_backend_cloudprint_init (GtkPrintBackendCloudprint *impl);
+static void gtk_print_backend_cloudprint_finalize (GObject *object);
+static void cloudprint_printer_get_settings_from_options (GtkPrinter *printer,
+ GtkPrinterOptionSet *options,
+ GtkPrintSettings *settings);
+static GtkPrinterOptionSet *cloudprint_printer_get_options (GtkPrinter *printer,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup,
+ GtkPrintCapabilities capabilities);
+static void cloudprint_printer_prepare_for_print (GtkPrinter *printer,
+ GtkPrintJob *print_job,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup);
+static void cloudprint_request_printer_list (GtkPrintBackend *print_backend);
+static void gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend,
+ GtkPrintJob *job,
+ GIOChannel *data_io,
+ GtkPrintJobCompleteFunc callback,
+ gpointer user_data,
+ GDestroyNotify dnotify);
+static cairo_surface_t * cloudprint_printer_create_cairo_surface (GtkPrinter *printer,
+ GtkPrintSettings *settings,
+ gdouble width,
+ gdouble height,
+ GIOChannel *cache_io);
+static void cloudprint_printer_request_details (GtkPrinter *printer);
+TGOAAccount * t_goa_account_copy (TGOAAccount *account);
+void t_goa_account_free (gpointer data);
+
+
+
+static void
+gtk_print_backend_cloudprint_register_type (GTypeModule *module)
+{
+ const GTypeInfo print_backend_cloudprint_info =
+ {
+ sizeof (GtkPrintBackendCloudprintClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_print_backend_cloudprint_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkPrintBackendCloudprint),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_print_backend_cloudprint_init,
+ };
+
+ print_backend_cloudprint_type = g_type_module_register_type (module,
+ GTK_TYPE_PRINT_BACKEND,
+ "GtkPrintBackendCloudprint",
+ &print_backend_cloudprint_info, 0);
+}
+
+G_MODULE_EXPORT void
+pb_module_init (GTypeModule *module)
+{
+ gtk_print_backend_cloudprint_register_type (module);
+ gtk_cloudprint_account_register_type (module);
+ gtk_printer_cloudprint_register_type (module);
+}
+
+G_MODULE_EXPORT void
+pb_module_exit (void)
+{
+
+}
+
+G_MODULE_EXPORT GtkPrintBackend *
+pb_module_create (void)
+{
+ return gtk_print_backend_cloudprint_new ();
+}
+
+/*
+ * GtkPrintBackendCloudprint
+ */
+GType
+gtk_print_backend_cloudprint_get_type (void)
+{
+ return print_backend_cloudprint_type;
+}
+
+/**
+ * gtk_print_backend_cloudprint_new:
+ *
+ * Creates a new #GtkPrintBackendCloudprint
+ * object. #GtkPrintBackendCloudprint implements the #GtkPrintBackend
+ * interface using REST API calls to the Google Cloud Print service.
+ *
+ * Return value: the new #GtkPrintBackendCloudprint object
+ **/
+GtkPrintBackend *
+gtk_print_backend_cloudprint_new (void)
+{
+ return g_object_new (GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, NULL);
+}
+
+static void
+gtk_print_backend_cloudprint_class_init (GtkPrintBackendCloudprintClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (klass);
+
+ backend_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = gtk_print_backend_cloudprint_finalize;
+
+ backend_class->request_printer_list = cloudprint_request_printer_list;
+ backend_class->print_stream = gtk_print_backend_cloudprint_print_stream;
+ backend_class->printer_create_cairo_surface = cloudprint_printer_create_cairo_surface;
+ backend_class->printer_get_options = cloudprint_printer_get_options;
+ backend_class->printer_get_settings_from_options = cloudprint_printer_get_settings_from_options;
+ backend_class->printer_prepare_for_print = cloudprint_printer_prepare_for_print;
+ backend_class->printer_request_details = cloudprint_printer_request_details;
+}
+
+static void
+gtk_print_backend_cloudprint_init (GtkPrintBackendCloudprint *backend)
+{
+ backend->cancellable = g_cancellable_new ();
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: +GtkPrintBackendCloudprint(%p)\n",
+ backend));
+}
+
+static void
+gtk_print_backend_cloudprint_finalize (GObject *object)
+{
+ GtkPrintBackendCloudprint *backend;
+
+ backend = GTK_PRINT_BACKEND_CLOUDPRINT (object);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: -GtkPrintBackendCloudprint(%p)\n",
+ backend));
+
+ g_cancellable_cancel (backend->cancellable);
+ g_clear_object (&(backend->cancellable));
+
+ backend_parent_class->finalize (object);
+}
+
+static cairo_status_t
+_cairo_write (void *closure,
+ const unsigned char *data,
+ unsigned int length)
+{
+ GIOChannel *io = (GIOChannel *)closure;
+ gsize written;
+ GError *error;
+
+ error = NULL;
+
+ while (length > 0)
+ {
+ g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error);
+
+ if (error != NULL)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: Error writing to temp file, %s\n", error->message));
+
+ g_error_free (error);
+ return CAIRO_STATUS_WRITE_ERROR;
+ }
+
+ data += written;
+ length -= written;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_surface_t *
+cloudprint_printer_create_cairo_surface (GtkPrinter *printer,
+ GtkPrintSettings *settings,
+ gdouble width,
+ gdouble height,
+ GIOChannel *cache_io)
+{
+ cairo_surface_t *surface;
+
+ surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height);
+
+ cairo_surface_set_fallback_resolution (surface,
+ 2.0 * gtk_print_settings_get_printer_lpi (settings),
+ 2.0 * gtk_print_settings_get_printer_lpi (settings));
+
+ return surface;
+}
+
+typedef struct {
+ GtkPrintBackend *backend;
+ GtkPrintJobCompleteFunc callback;
+ GtkPrintJob *job;
+ GIOChannel *target_io;
+ gpointer user_data;
+ GDestroyNotify dnotify;
+ gchar *path;
+
+ /* Base64 encoding state */
+ gint b64state;
+ gint b64save;
+} _PrintStreamData;
+
+static void
+cloudprint_submit_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source);
+ _PrintStreamData *ps = (_PrintStreamData *) user_data;
+ JsonObject *result;
+ GError *error = NULL;
+ gboolean success = FALSE;
+
+ result = gtk_cloudprint_account_submit_finish (account, res, &error);
+ g_object_unref (account);
+ if (result == NULL)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: submit REST reply: %s\n",
+ error->message));
+ goto done;
+ }
+
+ json_object_unref (result);
+ success = TRUE;
+
+ done:
+ if (ps->callback != NULL)
+ ps->callback (ps->job, ps->user_data, error);
+
+ if (ps->dnotify != NULL)
+ ps->dnotify (ps->user_data);
+
+ gtk_print_job_set_status (ps->job,
+ (success ?
+ GTK_PRINT_STATUS_FINISHED :
+ GTK_PRINT_STATUS_FINISHED_ABORTED));
+
+ g_clear_object (&(ps->job));
+ g_clear_object (&(ps->backend));
+ g_clear_pointer (&error, g_error_free);
+
+ g_free (ps->path);
+ g_free (ps);
+}
+
+static void
+cloudprint_print_cb (GtkPrintBackendCloudprint *print_backend,
+ GError *cb_error,
+ gpointer user_data)
+{
+ _PrintStreamData *ps = (_PrintStreamData *) user_data;
+ gsize encodedlen;
+ gchar encoded[4]; /* Up to 4 bytes are needed to finish encoding */
+ GError *error = NULL;
+
+ encodedlen = g_base64_encode_close (FALSE,
+ encoded,
+ &ps->b64state,
+ &ps->b64save);
+
+ if (encodedlen > 0)
+ g_io_channel_write_chars (ps->target_io,
+ encoded,
+ encodedlen,
+ NULL,
+ &error);
+
+ if (ps->target_io != NULL)
+ g_io_channel_unref (ps->target_io);
+
+ if (cb_error == NULL)
+ {
+ GMappedFile *map = g_mapped_file_new (ps->path, FALSE, &error);
+ GtkPrinter *printer = gtk_print_job_get_printer (ps->job);
+ GtkCloudprintAccount *account = NULL;
+
+ if (map == NULL)
+ {
+ GTK_NOTE (PRINTING,
+ g_printerr ("Cloud Print Backend: failed to map file: %s\n",
+ error->message));
+ g_error_free (error);
+ goto out;
+ }
+
+ g_object_get (printer,
+ "cloudprint-account", &account,
+ NULL);
+
+ g_warn_if_fail (account != NULL);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: submitting job\n"));
+ gtk_cloudprint_account_submit (account,
+ GTK_PRINTER_CLOUDPRINT (printer),
+ map,
+ gtk_print_job_get_title (ps->job),
+ print_backend->cancellable,
+ cloudprint_submit_cb,
+ ps);
+ }
+
+ out:
+ if (ps->path != NULL)
+ unlink (ps->path);
+
+ if (cb_error != NULL || error != NULL)
+ {
+ if (ps->callback != NULL)
+ ps->callback (ps->job, ps->user_data, error);
+
+ if (ps->dnotify != NULL)
+ ps->dnotify (ps->user_data);
+
+ gtk_print_job_set_status (ps->job,
+ GTK_PRINT_STATUS_FINISHED_ABORTED);
+
+ g_clear_object (&(ps->job));
+ g_free (ps->path);
+ g_free (ps);
+ }
+}
+
+static gboolean
+cloudprint_write (GIOChannel *source,
+ GIOCondition con,
+ gpointer user_data)
+{
+ gchar buf[_STREAM_MAX_CHUNK_SIZE];
+ /* Base64 converts 24 bits into 32 bits, so divide the number of
+ * bytes by 3 (rounding up) and multiply by 4. Also, if the previous
+ * call left a non-zero state we may need an extra 4 bytes. */
+ gchar encoded[(_STREAM_MAX_CHUNK_SIZE / 3 + 1) * 4 + 4];
+ gsize bytes_read;
+ GError *error = NULL;
+ GIOStatus read_status;
+ _PrintStreamData *ps = (_PrintStreamData *) user_data;
+
+ read_status =
+ g_io_channel_read_chars (source,
+ buf,
+ _STREAM_MAX_CHUNK_SIZE,
+ &bytes_read,
+ &error);
+
+ if (read_status != G_IO_STATUS_ERROR)
+ {
+ gsize encodedlen = g_base64_encode_step ((guchar *) buf,
+ bytes_read,
+ FALSE,
+ encoded,
+ &ps->b64state,
+ &ps->b64save);
+
+ g_io_channel_write_chars (ps->target_io,
+ encoded,
+ encodedlen,
+ NULL,
+ &error);
+ }
+
+ if (error != NULL || read_status == G_IO_STATUS_EOF)
+ {
+ cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (ps->backend),
+ error, user_data);
+
+ if (error != NULL)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: %s\n", error->message));
+
+ g_error_free (error);
+ }
+
+ return FALSE;
+ }
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: Writing %i byte chunk to tempfile\n", (int)bytes_read));
+
+ return TRUE;
+}
+
+static void
+gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend,
+ GtkPrintJob *job,
+ GIOChannel *data_io,
+ GtkPrintJobCompleteFunc callback,
+ gpointer user_data,
+ GDestroyNotify dnotify)
+{
+ const gchar *prefix = "data:application/pdf;base64,";
+ GError *internal_error = NULL;
+ _PrintStreamData *ps;
+ int tmpfd;
+
+ ps = g_new0 (_PrintStreamData, 1);
+ ps->callback = callback;
+ ps->user_data = user_data;
+ ps->dnotify = dnotify;
+ ps->job = g_object_ref (job);
+ ps->backend = g_object_ref (print_backend);
+ ps->path = g_strdup_printf ("%s/cloudprintXXXXXX.pdf.b64",
+ g_get_tmp_dir ());
+ ps->b64state = 0;
+ ps->b64save = 0;
+
+ internal_error = NULL;
+
+ if (ps->path == NULL)
+ goto error;
+
+ tmpfd = g_mkstemp (ps->path);
+ if (tmpfd == -1)
+ {
+ int err = errno;
+ internal_error = g_error_new (gtk_print_error_quark (),
+ GTK_PRINT_ERROR_INTERNAL_ERROR,
+ "Error creating temporary file: %s",
+ g_strerror (err));
+ goto error;
+ }
+
+ ps->target_io = g_io_channel_unix_new (tmpfd);
+
+ if (ps->target_io != NULL)
+ {
+ g_io_channel_set_close_on_unref (ps->target_io, TRUE);
+ g_io_channel_set_encoding (ps->target_io, NULL, &internal_error);
+ }
+
+ g_io_channel_write_chars (ps->target_io,
+ prefix,
+ strlen (prefix),
+ NULL,
+ &internal_error);
+
+error:
+ if (internal_error != NULL)
+ {
+ cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (print_backend),
+ internal_error, ps);
+
+ g_error_free (internal_error);
+ return;
+ }
+
+ g_io_add_watch (data_io,
+ G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
+ (GIOFunc) cloudprint_write,
+ ps);
+}
+
+TGOAAccount *
+t_goa_account_copy (TGOAAccount *account)
+{
+ TGOAAccount *result = NULL;
+
+ if (account != NULL)
+ {
+ result = g_new0 (TGOAAccount, 1);
+ result->id = g_strdup (account->id);
+ result->path = g_strdup (account->path);
+ result->presentation_identity = g_strdup (account->presentation_identity);
+ }
+
+ return result;
+}
+
+void
+t_goa_account_free (gpointer data)
+{
+ TGOAAccount *account = (TGOAAccount *) data;
+
+ if (account != NULL)
+ {
+ g_free (account->id);
+ g_free (account->path);
+ g_free (account->presentation_identity);
+ g_free (account);
+ }
+}
+
+static GList *
+get_accounts (GVariant *output)
+{
+ GVariant *objects;
+ GList *result = NULL;
+ gint i, j, k;
+
+ g_variant_get (output, "(@a{oa{sa{sv}}})",
+ &objects);
+
+ if (objects)
+ {
+ for (i = 0; i < g_variant_n_children (objects); i++)
+ {
+ const gchar *object_name;
+ GVariant *object_variant;
+
+ g_variant_get_child (objects, i, "{&o@a{sa{sv}}}",
+ &object_name,
+ &object_variant);
+
+ if (g_str_has_prefix (object_name, "/org/gnome/OnlineAccounts/Accounts/"))
+ {
+ for (j = 0; j < g_variant_n_children (object_variant); j++)
+ {
+ const gchar *service_name;
+ GVariant *service_variant;
+
+ g_variant_get_child (object_variant, j, "{&s@a{sv}}",
+ &service_name,
+ &service_variant);
+
+ if (g_str_has_prefix (service_name, "org.gnome.OnlineAccounts.Account"))
+ {
+ TGOAAccount *account;
+ gboolean printers_disabled = FALSE;
+ gchar *provider_type = NULL;
+
+ account = g_new0 (TGOAAccount, 1);
+
+ account->path = g_strdup (object_name);
+ for (k = 0; k < g_variant_n_children (service_variant); k++)
+ {
+ const gchar *property_name;
+ GVariant *property_variant;
+ GVariant *value;
+
+ g_variant_get_child (service_variant, k, "{&s@v}",
+ &property_name,
+ &property_variant);
+
+ g_variant_get (property_variant, "v",
+ &value);
+
+ if (g_strcmp0 (property_name, "Id") == 0)
+ account->id = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (property_name, "ProviderType") == 0)
+ provider_type = g_variant_dup_string (value, NULL);
+ else if (g_strcmp0 (property_name, "PrintersDisabled") == 0)
+ printers_disabled = g_variant_get_boolean (value);
+ else if (g_strcmp0 (property_name, "PresentationIdentity") == 0)
+ account->presentation_identity = g_variant_dup_string (value, NULL);
+
+ g_variant_unref (property_variant);
+ g_variant_unref (value);
+ }
+
+ if (!printers_disabled &&
+ g_strcmp0 (provider_type, "google") == 0 &&
+ account->presentation_identity != NULL)
+ result = g_list_append (result, account);
+ else
+ t_goa_account_free (account);
+
+ g_free (provider_type);
+ }
+
+ g_variant_unref (service_variant);
+ }
+ }
+
+ g_variant_unref (object_variant);
+ }
+
+ g_variant_unref (objects);
+ }
+
+ return result;
+}
+
+static void
+cloudprint_search_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source);
+ GtkPrintBackendCloudprint *backend = NULL;
+ JsonNode *node;
+ JsonArray *printers;
+ guint i;
+ GError *error = NULL;
+
+ node = gtk_cloudprint_account_search_finish (account, res, &error);
+ g_object_unref (account);
+ if (node == NULL)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: search failed: %s\n",
+ error->message));
+
+ if (error->domain != G_IO_ERROR ||
+ error->code != G_IO_ERROR_CANCELLED)
+ backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data);
+
+ g_error_free (error);
+ goto done;
+ }
+
+ backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data);
+ printers = json_node_get_array (node);
+ for (i = 0; i < json_array_get_length (printers); i++)
+ {
+ GtkPrinterCloudprint *printer;
+ JsonObject *json_printer = json_array_get_object_element (printers, i);
+ const char *name = NULL;
+ const char *id = NULL;
+ const char *type = NULL;
+ const char *desc = NULL;
+ const char *status = NULL;
+ gboolean is_virtual;
+
+ if (json_object_has_member (json_printer, "displayName"))
+ name = json_object_get_string_member (json_printer, "displayName");
+
+ if (json_object_has_member (json_printer, "id"))
+ id = json_object_get_string_member (json_printer, "id");
+
+ if (name == NULL || id == NULL)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: ignoring incomplete "
+ "printer description\n"));
+ continue;
+ }
+
+ if (json_object_has_member (json_printer, "type"))
+ type = json_object_get_string_member (json_printer, "type");
+
+ if (json_object_has_member (json_printer, "description"))
+ desc = json_object_get_string_member (json_printer, "description");
+
+ if (json_object_has_member (json_printer, "connectionStatus"))
+ status = json_object_get_string_member (json_printer,
+ "connectionStatus");
+
+ is_virtual = (type != NULL && !strcmp (type, "DOCS"));
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: Adding printer %s\n", name));
+
+ printer = gtk_printer_cloudprint_new (name,
+ is_virtual,
+ GTK_PRINT_BACKEND (backend),
+ account,
+ id);
+ gtk_printer_set_has_details (GTK_PRINTER (printer), FALSE);
+ gtk_printer_set_icon_name (GTK_PRINTER (printer), "printer");
+ gtk_printer_set_location (GTK_PRINTER (printer),
+ gtk_cloudprint_account_get_presentation_identity (account));
+
+ if (desc != NULL)
+ gtk_printer_set_description (GTK_PRINTER (printer), desc);
+
+ if (status != NULL)
+ {
+ if (!strcmp (status, "ONLINE"))
+ /* Translators: The printer status is online, i.e. it is
+ * ready to print. */
+ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Online"));
+ else if (!strcmp (status, "UNKNOWN"))
+ /* Translators: We don't know whether this printer is
+ * available to print to. */
+ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Unknown"));
+ else if (!strcmp (status, "OFFLINE"))
+ /* Translators: The printer is offline. */
+ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Offline"));
+ else if (!strcmp (status, "DORMANT"))
+ /* We shouldn't get here because the query omits dormant
+ * printers by default. */
+
+ /* Translators: Printer has been offline for a long time. */
+ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Dormant"));
+ }
+
+ gtk_printer_set_is_active (GTK_PRINTER (printer), TRUE);
+
+ gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend),
+ GTK_PRINTER (printer));
+ g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
+ "printer-added", GTK_PRINTER (printer));
+ g_object_unref (printer);
+ }
+
+ json_node_free (node);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: 'search' finished for account %p\n",
+ account));
+
+ done:
+ if (backend != NULL && --backend->accounts_searching == 0)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: 'search' finished for "
+ "all accounts\n"));
+
+ gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
+ }
+}
+
+static void
+cloudprint_get_managed_objects_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GtkPrintBackendCloudprint *backend;
+ GVariant *output;
+ GError *error = NULL;
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+
+ if (output != NULL)
+ {
+ TGOAAccount *goa_account;
+ GList *accounts = NULL;
+ GList *iter;
+ guint searching;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: got objects managed by goa\n"));
+
+ backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data);
+
+ accounts = get_accounts (output);
+ g_variant_unref (output);
+ searching = backend->accounts_searching = g_list_length (accounts);
+
+ for (iter = accounts; iter != NULL; iter = iter->next)
+ {
+ GtkCloudprintAccount *account;
+ goa_account = (TGOAAccount *) iter->data;
+ account = gtk_cloudprint_account_new (goa_account->id,
+ goa_account->path,
+ goa_account->presentation_identity);
+ if (account == NULL)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: error constructing "
+ "account object"));
+ backend->accounts_searching--;
+ searching--;
+ continue;
+ }
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: issuing 'search' for %p\n",
+ account));
+
+ gtk_cloudprint_account_search (account,
+ G_DBUS_CONNECTION (source),
+ backend->cancellable,
+ cloudprint_search_cb,
+ GTK_PRINT_BACKEND (backend));
+ }
+
+ if (searching == 0)
+ gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
+
+ g_list_free_full (accounts, t_goa_account_free);
+ }
+ else
+ {
+ if (error->domain != G_IO_ERROR ||
+ error->code != G_IO_ERROR_CANCELLED)
+ {
+ if (error->domain != G_DBUS_ERROR ||
+ (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN &&
+ error->code != G_DBUS_ERROR_UNKNOWN_METHOD))
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: failed to get objects managed by goa: %s\n",
+ error->message));
+ g_warning ("%s", error->message);
+ }
+
+ gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data));
+ }
+
+ g_error_free (error);
+ }
+
+ g_object_unref (source);
+}
+
+static void
+cloudprint_bus_get_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GtkPrintBackendCloudprint *backend;
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ connection = g_bus_get_finish (res, &error);
+
+ if (connection != NULL)
+ {
+ backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: got connection to session bus\n"));
+
+ g_dbus_connection_call (connection,
+ ONLINE_ACCOUNTS_BUS,
+ ONLINE_ACCOUNTS_PATH,
+ OBJECT_MANAGER_IFACE,
+ "GetManagedObjects",
+ NULL,
+ G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ backend->cancellable,
+ cloudprint_get_managed_objects_cb,
+ backend);
+ }
+ else
+ {
+ if (error->domain != G_IO_ERROR ||
+ error->code != G_IO_ERROR_CANCELLED)
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: failed getting session bus: %s\n",
+ error->message));
+ g_warning ("%s", error->message);
+
+ gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data));
+ }
+ g_error_free (error);
+ }
+}
+
+static void
+cloudprint_request_printer_list (GtkPrintBackend *print_backend)
+{
+ GtkPrintBackendCloudprint *backend = GTK_PRINT_BACKEND_CLOUDPRINT (print_backend);
+
+ g_cancellable_reset (backend->cancellable);
+ g_bus_get (G_BUS_TYPE_SESSION, backend->cancellable, cloudprint_bus_get_cb, backend);
+}
+
+static GtkPrinterOptionSet *
+cloudprint_printer_get_options (GtkPrinter *printer,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup,
+ GtkPrintCapabilities capabilities)
+{
+ GtkPrinterOptionSet *set;
+ GtkPrinterOption *option;
+ const gchar *n_up[] = { "1" };
+
+ set = gtk_printer_option_set_new ();
+
+ /* How many document pages to go onto one side of paper. */
+ option = gtk_printer_option_new ("gtk-n-up", _("Pages per _sheet:"), GTK_PRINTER_OPTION_TYPE_PICKONE);
+ gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
+ (char **) n_up, (char **) n_up /* FIXME i18n (localised digits)! */);
+ gtk_printer_option_set (option, "1");
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ return set;
+}
+
+static void
+cloudprint_printer_get_settings_from_options (GtkPrinter *printer,
+ GtkPrinterOptionSet *options,
+ GtkPrintSettings *settings)
+{
+}
+
+static void
+cloudprint_printer_prepare_for_print (GtkPrinter *printer,
+ GtkPrintJob *print_job,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup)
+{
+ gdouble scale;
+
+ gtk_print_job_set_pages (print_job, gtk_print_settings_get_print_pages (settings));
+ gtk_print_job_set_page_ranges (print_job, NULL, 0);
+
+ if (gtk_print_job_get_pages (print_job) == GTK_PRINT_PAGES_RANGES)
+ {
+ GtkPageRange *page_ranges;
+ gint num_page_ranges;
+ page_ranges = gtk_print_settings_get_page_ranges (settings, &num_page_ranges);
+ gtk_print_job_set_page_ranges (print_job, page_ranges, num_page_ranges);
+ }
+
+ gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
+ gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
+ gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings));
+
+ scale = gtk_print_settings_get_scale (settings);
+ if (scale != 100.0)
+ gtk_print_job_set_scale (print_job, scale/100.0);
+
+ gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings));
+ gtk_print_job_set_rotate (print_job, TRUE);
+}
+
+static void
+cloudprint_printer_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source);
+ GtkPrinter *printer = GTK_PRINTER (user_data);
+ JsonObject *result;
+ GError *error = NULL;
+ gboolean success = FALSE;
+
+ result = gtk_cloudprint_account_printer_finish (account, res, &error);
+ if (result != NULL)
+ {
+ /* Ignore capabilities for now. */
+ json_object_unref (result);
+ success = TRUE;
+ }
+ else
+ {
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: failure getting details: %s\n",
+ error->message));
+
+ if (error->domain == G_IO_ERROR &&
+ error->code == G_IO_ERROR_CANCELLED)
+ {
+ g_error_free (error);
+ return;
+ }
+
+ g_error_free (error);
+ }
+
+ gtk_printer_set_has_details (printer, success);
+ g_signal_emit_by_name (printer, "details-acquired", success);
+}
+
+static void
+cloudprint_printer_request_details (GtkPrinter *printer)
+{
+ GtkPrintBackendCloudprint *backend;
+ GtkCloudprintAccount *account = NULL;
+ gchar *printerid = NULL;
+
+ g_object_get (printer,
+ "cloudprint-account", &account,
+ "printer-id", &printerid,
+ NULL);
+
+ g_warn_if_fail (account != NULL);
+ g_warn_if_fail (printerid != NULL);
+
+ backend = GTK_PRINT_BACKEND_CLOUDPRINT (gtk_printer_get_backend (printer));
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: Getting details for printer id %s\n",
+ printerid));
+
+ gtk_cloudprint_account_printer (account,
+ printerid,
+ backend->cancellable,
+ cloudprint_printer_cb,
+ printer);
+ g_object_unref (account);
+ g_free (printerid);
+}
diff --git a/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h b/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h
new file mode 100644
index 0000000000..044ad4b32b
--- /dev/null
+++ b/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h
@@ -0,0 +1,40 @@
+/* gtkprintbackendcloudprint.h: Google Cloud Print implementation of
+ * GtkPrintBackend
+ * 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/>.
+ */
+
+#ifndef __GTK_PRINT_BACKEND_CLOUDPRINT_H__
+#define __GTK_PRINT_BACKEND_CLOUDPRINT_H__
+
+#include <glib-object.h>
+#include "gtkprintbackend.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINT_BACKEND_CLOUDPRINT (gtk_print_backend_cloudprint_get_type ())
+#define GTK_PRINT_BACKEND_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprint))
+#define GTK_IS_PRINT_BACKEND_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT))
+
+#define ONLINE_ACCOUNTS_BUS "org.gnome.OnlineAccounts"
+
+typedef struct _GtkPrintBackendCloudprint GtkPrintBackendCloudprint;
+
+GtkPrintBackend *gtk_print_backend_cloudprint_new (void);
+GType gtk_print_backend_cloudprint_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_PRINT_BACKEND_CLOUDPRINT_H__ */
diff --git a/modules/printbackends/cloudprint/gtkprintercloudprint.c b/modules/printbackends/cloudprint/gtkprintercloudprint.c
new file mode 100644
index 0000000000..96bb98ece3
--- /dev/null
+++ b/modules/printbackends/cloudprint/gtkprintercloudprint.c
@@ -0,0 +1,231 @@
+/* gtkprintercloudprint.c: Google Cloud Print -specific Printer class,
+ * GtkPrinterCloudprint
+ * 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 <glib/gi18n-lib.h>
+#include <gtk/gtkintl.h>
+
+#include "gtkprintercloudprint.h"
+#include "gtkcloudprintaccount.h"
+
+typedef struct _GtkPrinterCloudprintClass GtkPrinterCloudprintClass;
+
+#define GTK_PRINTER_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprintClass))
+#define GTK_IS_PRINTER_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CLOUDPRINT))
+#define GTK_PRINTER_CLOUDPRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprintClass))
+
+static GtkPrinterClass *gtk_printer_cloudprint_parent_class;
+static GType printer_cloudprint_type = 0;
+
+struct _GtkPrinterCloudprintClass
+{
+ GtkPrinterClass parent_class;
+};
+
+struct _GtkPrinterCloudprint
+{
+ GtkPrinter parent_instance;
+
+ GtkCloudprintAccount *account;
+ gchar *id;
+};
+
+enum {
+ PROP_0,
+ PROP_CLOUDPRINT_ACCOUNT,
+ PROP_PRINTER_ID
+};
+
+static void gtk_printer_cloudprint_class_init (GtkPrinterCloudprintClass *class);
+static void gtk_printer_cloudprint_init (GtkPrinterCloudprint *impl);
+static void gtk_printer_cloudprint_finalize (GObject *object);
+static void gtk_printer_cloudprint_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_printer_cloudprint_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+void
+gtk_printer_cloudprint_register_type (GTypeModule *module)
+{
+ const GTypeInfo printer_cloudprint_info =
+ {
+ sizeof (GtkPrinterCloudprintClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_printer_cloudprint_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkPrinterCloudprint),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_printer_cloudprint_init,
+ };
+
+ printer_cloudprint_type = g_type_module_register_type (module,
+ GTK_TYPE_PRINTER,
+ "GtkPrinterCloudprint",
+ &printer_cloudprint_info, 0);
+}
+
+/*
+ * GtkPrinterCloudprint
+ */
+GType
+gtk_printer_cloudprint_get_type (void)
+{
+ return printer_cloudprint_type;
+}
+
+/**
+ * gtk_printer_cloudprint_new:
+ *
+ * Creates a new #GtkPrinterCloudprint object. #GtkPrinterCloudprint
+ * implements the #GtkPrinter interface and stores a reference to the
+ * #GtkCloudprintAccount object and the printer-id to use
+ *
+ * Return value: the new #GtkPrinterCloudprint object
+ **/
+GtkPrinterCloudprint *
+gtk_printer_cloudprint_new (const char *name,
+ gboolean is_virtual,
+ GtkPrintBackend *backend,
+ GtkCloudprintAccount *account,
+ const gchar *id)
+{
+ return g_object_new (GTK_TYPE_PRINTER_CLOUDPRINT,
+ "name", name,
+ "backend", backend,
+ "is-virtual", is_virtual,
+ "accepts-pdf", TRUE,
+ "cloudprint-account", account,
+ "printer-id", id,
+ NULL);
+}
+
+static void
+gtk_printer_cloudprint_class_init (GtkPrinterCloudprintClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gtk_printer_cloudprint_parent_class = g_type_class_peek_parent (klass);
+ gobject_class->finalize = gtk_printer_cloudprint_finalize;
+ gobject_class->set_property = gtk_printer_cloudprint_set_property;
+ gobject_class->get_property = gtk_printer_cloudprint_get_property;
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass),
+ PROP_CLOUDPRINT_ACCOUNT,
+ g_param_spec_object ("cloudprint-account",
+ P_("Cloud Print account"),
+ P_("GtkCloudprintAccount instance"),
+ GTK_TYPE_CLOUDPRINT_ACCOUNT,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass),
+ PROP_PRINTER_ID,
+ g_param_spec_string ("printer-id",
+ P_("Printer ID"),
+ P_("Cloud Print printer ID"),
+ "",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gtk_printer_cloudprint_init (GtkPrinterCloudprint *printer)
+{
+ printer->account = NULL;
+ printer->id = NULL;
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: +GtkPrinterCloudprint(%p)\n",
+ printer));
+}
+
+static void
+gtk_printer_cloudprint_finalize (GObject *object)
+{
+ GtkPrinterCloudprint *printer;
+
+ printer = GTK_PRINTER_CLOUDPRINT (object);
+
+ GTK_NOTE (PRINTING,
+ g_print ("Cloud Print Backend: -GtkPrinterCloudprint(%p)\n",
+ printer));
+
+ if (printer->account != NULL)
+ g_object_unref (printer->account);
+
+ g_free (printer->id);
+
+ G_OBJECT_CLASS (gtk_printer_cloudprint_parent_class)->finalize (object);
+}
+
+static void
+gtk_printer_cloudprint_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkPrinterCloudprint *printer = GTK_PRINTER_CLOUDPRINT (object);
+
+ switch (prop_id)
+ {
+ case PROP_CLOUDPRINT_ACCOUNT:
+ printer->account = g_value_dup_object (value);
+ break;
+
+ case PROP_PRINTER_ID:
+ printer->id = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_printer_cloudprint_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkPrinterCloudprint *printer = GTK_PRINTER_CLOUDPRINT (object);
+
+ switch (prop_id)
+ {
+ case PROP_CLOUDPRINT_ACCOUNT:
+ g_value_set_object (value, printer->account);
+ break;
+
+ case PROP_PRINTER_ID:
+ g_value_set_string (value, printer->id);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
diff --git a/modules/printbackends/cloudprint/gtkprintercloudprint.h b/modules/printbackends/cloudprint/gtkprintercloudprint.h
new file mode 100644
index 0000000000..4b86cf0d6b
--- /dev/null
+++ b/modules/printbackends/cloudprint/gtkprintercloudprint.h
@@ -0,0 +1,45 @@
+/* gtkprintercloudprint.h: Google Cloud Print -specific Printer class
+ * GtkPrinterCloudprint
+ * 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/>.
+ */
+
+#ifndef __GTK_PRINTER_CLOUDPRINT_H__
+#define __GTK_PRINTER_CLOUDPRINT_H__
+
+#include <glib-object.h>
+#include <gtk/gtkprinter-private.h>
+
+#include "gtkcloudprintaccount.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINTER_CLOUDPRINT (gtk_printer_cloudprint_get_type ())
+#define GTK_PRINTER_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprint))
+#define GTK_IS_PRINTER_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CLOUDPRINT))
+
+typedef struct _GtkPrinterCloudprint GtkPrinterCloudprint;
+
+void gtk_printer_cloudprint_register_type (GTypeModule *module);
+GtkPrinterCloudprint *gtk_printer_cloudprint_new (const char *name,
+ gboolean is_virtual,
+ GtkPrintBackend *backend,
+ GtkCloudprintAccount *account,
+ const gchar *id);
+GType gtk_printer_cloudprint_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_PRINTER_CLOUDPRINT_H__ */