/* 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 .
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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"
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);
G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendCloudprint, gtk_print_backend_cloudprint, GTK_TYPE_PRINT_BACKEND)
void
g_io_module_load (GIOModule *module)
{
g_type_module_use (G_TYPE_MODULE (module));
gtk_print_backend_cloudprint_register_type (G_TYPE_MODULE (module));
gtk_cloudprint_account_register_type (G_TYPE_MODULE (module));
gtk_printer_cloudprint_register_type (G_TYPE_MODULE (module));
g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
GTK_TYPE_PRINT_BACKEND_CLOUDPRINT,
"cloudprint",
10);
}
void
g_io_module_unload (GIOModule *module)
{
}
char **
g_io_module_query (void)
{
char *eps[] = {
GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
NULL
};
return g_strdupv (eps);
}
/**
* 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.
*
* Returns: 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_class_finalize (GtkPrintBackendCloudprintClass *class)
{
}
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);
}