[gtk+] New cloudprint GTK+ print module for Google Cloud Print.



commit aaae0ca8bac2f46400a3124a82a1ad06f4c735b0
Author: Tim Waugh <twaugh redhat com>
Date:   Wed Feb 26 16:05:44 2014 +0000

    New cloudprint GTK+ print module for Google Cloud Print.
    
    This is a web service provided by Google that allows people to
    share their printers (https://www.google.com/cloudprint/learn/).
    
    In addition to being able to print to printers shared on Google Cloud
    Print, there is an equivalent of "Print to file" in the form of "Save to
    Google Drive".
    
    The cloudprint module uses gnome-online-accounts to obtain the OAuth 2.0
    access token for the Google account.
    
    Currently it can discover available printers, get simple details about
    them such as display name and status, and submit jobs without any
    special options.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=723368

 configure.ac                                       |   32 +
 gtk/Makefile.am                                    |   16 +
 modules/printbackends/Makefile.am                  |    4 +
 modules/printbackends/cloudprint/Makefile.am       |   44 +
 .../cloudprint/gtkcloudprintaccount.c              |  664 ++++++++++++
 .../cloudprint/gtkcloudprintaccount.h              |   74 ++
 .../cloudprint/gtkprintbackendcloudprint.c         | 1053 ++++++++++++++++++++
 .../cloudprint/gtkprintbackendcloudprint.h         |   40 +
 .../cloudprint/gtkprintercloudprint.c              |  231 +++++
 .../cloudprint/gtkprintercloudprint.h              |   45 +
 10 files changed, 2203 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 42c26f1..1aa2a37 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1520,6 +1520,37 @@ else
   fi
 fi
 
+# Checks to see if we should compile with cloudprint backend for GTK+
+#
+
+AC_ARG_ENABLE(cloudprint,
+              [AS_HELP_STRING([--disable-cloudprint],
+                              [disable cloudprint print backend])],,
+              [enable_cloudprint=auto])
+
+if test "x$enable_cloudprint" = "xno"; then
+  AM_CONDITIONAL(HAVE_CLOUDPRINT, false)
+else
+  PKG_CHECK_MODULES(REST, [rest-0.7], have_rest=yes, have_rest=no)
+  PKG_CHECK_MODULES(JSON_GLIB, [json-glib-1.0], have_json_glib=yes, have_json_glib=no)
+  if test "x$have_rest" = "xyes" -a "x$have_json_glib" = "xyes"; then
+    PRINT_BACKENDS="$PRINT_BACKENDS cloudprint"
+  fi
+  AM_CONDITIONAL(HAVE_CLOUDPRINT, test "x$have_rest" = "xyes" -a "x$have_json_glib" = "xyes")
+fi
+
+if test "x$enable_cloudprint" = "xyes" -a "x$have_rest" = "xno"; then
+    AC_MSG_ERROR([
+*** rest not found.
+])
+fi
+
+if test "x$enable_cloudprint" = "xyes" -a "x$have_json_glib" = "xno"; then
+    AC_MSG_ERROR([
+*** json-glib not found.
+])
+fi
+
 AM_CONDITIONAL(HAVE_PAPI_CUPS, test "x$have_papi" = "xyes" -a "x$CUPS_CONFIG" != "xno")
 
 gtk_save_cppflags="$CPPFLAGS"
@@ -1859,6 +1890,7 @@ modules/engines/pixbuf/Makefile
 modules/input/Makefile
 modules/printbackends/Makefile
 modules/printbackends/cups/Makefile
+modules/printbackends/cloudprint/Makefile
 modules/printbackends/lpr/Makefile
 modules/printbackends/file/Makefile
 modules/printbackends/papi/Makefile
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 6df2a89..f8c0cb3 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -18,6 +18,21 @@ endif
 
 SUBDIRS = a11y native .
 
+if HAVE_CLOUDPRINT
+if HAVE_PAPI_CUPS
+GTK_PRINT_BACKENDS=file,papi,cups,cloudprint
+else
+if HAVE_CUPS
+GTK_PRINT_BACKENDS=file,cups,cloudprint
+else
+if HAVE_PAPI
+GTK_PRINT_BACKENDS=file,papi,cloudprint
+else
+GTK_PRINT_BACKENDS=file,lpr,cloudprint
+endif
+endif
+endif
+else
 if HAVE_PAPI_CUPS
 GTK_PRINT_BACKENDS=file,papi,cups
 else
@@ -31,6 +46,7 @@ GTK_PRINT_BACKENDS=file,lpr
 endif
 endif
 endif
+endif
 
 AM_CPPFLAGS =                                          \
        -DG_LOG_DOMAIN=\"Gtk\"                          \
diff --git a/modules/printbackends/Makefile.am b/modules/printbackends/Makefile.am
index 43468a6..7282a01 100644
--- a/modules/printbackends/Makefile.am
+++ b/modules/printbackends/Makefile.am
@@ -2,6 +2,10 @@ include $(top_srcdir)/Makefile.decl
 
 SUBDIRS = file lpr
 
+if HAVE_CLOUDPRINT
+SUBDIRS += cloudprint
+endif
+
 if HAVE_CUPS
 SUBDIRS += cups
 endif
diff --git a/modules/printbackends/cloudprint/Makefile.am b/modules/printbackends/cloudprint/Makefile.am
new file mode 100644
index 0000000..6408d4c
--- /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 0000000..34f724c
--- /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 0000000..ef78874
--- /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 0000000..c032691
--- /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 0000000..044ad4b
--- /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 0000000..96bb98e
--- /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 0000000..4b86cf0
--- /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__ */


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