diff options
Diffstat (limited to 'modules/printbackends/cloudprint')
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__ */ |