gtk/modules/printbackends/cloudprint/gtkprintbackendcloudprint.c
Tim Waugh aaae0ca8ba 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
2014-02-28 11:45:03 +01:00

1054 lines
30 KiB
C

/* 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);
}