Consolidate print backends into one directory
authorMatthias Clasen <mclasen@redhat.com>
Sun, 11 Mar 2018 16:18:16 +0000 (12:18 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Sun, 11 Mar 2018 16:38:35 +0000 (12:38 -0400)
This will let us use GTK_PATH to load them uninstalled, which
is useful for in-tree tests.

41 files changed:
modules/printbackends/cloudprint/gtkcloudprintaccount.c [deleted file]
modules/printbackends/cloudprint/gtkcloudprintaccount.h [deleted file]
modules/printbackends/cloudprint/gtkprintbackendcloudprint.c [deleted file]
modules/printbackends/cloudprint/gtkprintbackendcloudprint.h [deleted file]
modules/printbackends/cloudprint/gtkprintercloudprint.c [deleted file]
modules/printbackends/cloudprint/gtkprintercloudprint.h [deleted file]
modules/printbackends/cloudprint/meson.build [deleted file]
modules/printbackends/cups/gtkcupssecretsutils.c [deleted file]
modules/printbackends/cups/gtkcupssecretsutils.h [deleted file]
modules/printbackends/cups/gtkcupsutils.c [deleted file]
modules/printbackends/cups/gtkcupsutils.h [deleted file]
modules/printbackends/cups/gtkprintbackendcups.c [deleted file]
modules/printbackends/cups/gtkprintbackendcups.h [deleted file]
modules/printbackends/cups/gtkprintercups.c [deleted file]
modules/printbackends/cups/gtkprintercups.h [deleted file]
modules/printbackends/cups/meson.build [deleted file]
modules/printbackends/file/gtkprintbackendfile.c [deleted file]
modules/printbackends/file/gtkprintbackendfile.h [deleted file]
modules/printbackends/file/meson.build [deleted file]
modules/printbackends/gtkcloudprintaccount.c [new file with mode: 0644]
modules/printbackends/gtkcloudprintaccount.h [new file with mode: 0644]
modules/printbackends/gtkcupssecretsutils.c [new file with mode: 0644]
modules/printbackends/gtkcupssecretsutils.h [new file with mode: 0644]
modules/printbackends/gtkcupsutils.c [new file with mode: 0644]
modules/printbackends/gtkcupsutils.h [new file with mode: 0644]
modules/printbackends/gtkprintbackendcloudprint.c [new file with mode: 0644]
modules/printbackends/gtkprintbackendcloudprint.h [new file with mode: 0644]
modules/printbackends/gtkprintbackendcups.c [new file with mode: 0644]
modules/printbackends/gtkprintbackendcups.h [new file with mode: 0644]
modules/printbackends/gtkprintbackendfile.c [new file with mode: 0644]
modules/printbackends/gtkprintbackendfile.h [new file with mode: 0644]
modules/printbackends/gtkprintbackendlpr.c [new file with mode: 0644]
modules/printbackends/gtkprintbackendlpr.h [new file with mode: 0644]
modules/printbackends/gtkprintercloudprint.c [new file with mode: 0644]
modules/printbackends/gtkprintercloudprint.h [new file with mode: 0644]
modules/printbackends/gtkprintercups.c [new file with mode: 0644]
modules/printbackends/gtkprintercups.h [new file with mode: 0644]
modules/printbackends/lpr/gtkprintbackendlpr.c [deleted file]
modules/printbackends/lpr/gtkprintbackendlpr.h [deleted file]
modules/printbackends/lpr/meson.build [deleted file]
modules/printbackends/meson.build

diff --git a/modules/printbackends/cloudprint/gtkcloudprintaccount.c b/modules/printbackends/cloudprint/gtkcloudprintaccount.c
deleted file mode 100644 (file)
index ee65a18..0000000
+++ /dev/null
@@ -1,662 +0,0 @@
-/* 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.
- *
- * Returns: 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;
-  GTask *task;
-  GError *error = NULL;
-
-  GTK_NOTE (PRINTING,
-           g_print ("Cloud Print Backend: (%p) 'printer' REST call for "
-                    "printer id %s", 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_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
deleted file mode 100644 (file)
index ef78874..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/* 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
deleted file mode 100644 (file)
index b556034..0000000
+++ /dev/null
@@ -1,1036 +0,0 @@
-/* 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"
-
-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);
-}
diff --git a/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h b/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h
deleted file mode 100644 (file)
index 044ad4b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/* 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
deleted file mode 100644 (file)
index 4f299fd..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/* 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
- *
- * Returns: 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
deleted file mode 100644 (file)
index 564fb15..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/* 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))
-
-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__ */
diff --git a/modules/printbackends/cloudprint/meson.build b/modules/printbackends/cloudprint/meson.build
deleted file mode 100644 (file)
index f5217cb..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-shared_module('printbackend-cloudprint',
-              'gtkprintbackendcloudprint.c',
-              'gtkprintercloudprint.c',
-              'gtkcloudprintaccount.c',
-              c_args: [
-                '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
-                '-DGTK_DISABLE_DEPRECATION_WARNINGS',
-              ],
-              dependencies: [ libgtk_dep, rest_dep, json_glib_dep ],
-              install_dir: printbackends_install_dir,
-              install : true)
diff --git a/modules/printbackends/cups/gtkcupssecretsutils.c b/modules/printbackends/cups/gtkcupssecretsutils.c
deleted file mode 100644 (file)
index 925f7d5..0000000
+++ /dev/null
@@ -1,1041 +0,0 @@
-/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords
- * Copyright (C) 2014, Intevation GmbH
- *
- * 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 <glib.h>
-#include <gio/gio.h>
-#include <string.h>
-
-#include <gtk/gtk.h>
-
-#include "gtkcupssecretsutils.h"
-
-#define SECRETS_BUS              "org.freedesktop.secrets"
-#define SECRETS_IFACE(interface) "org.freedesktop.Secret."interface
-#define SECRETS_PATH             "/org/freedesktop/secrets"
-#define SECRETS_TIMEOUT          5000
-
-typedef enum
-{
-  SECRETS_SERVICE_ACTION_QUERY,
-  SECRETS_SERVICE_ACTION_STORE
-} SecretsServiceAction;
-
-typedef struct
-{
-  GDBusConnection       *dbus_connection;
-  SecretsServiceAction   action;
-  gchar                **auth_info,
-                       **auth_info_labels,
-                       **auth_info_required,
-                        *printer_uri,
-                        *session_path,
-                        *collection_path;
-  GDBusProxy            *item_proxy;
-  guint                  prompt_subscription;
-} SecretsServiceData;
-
-/**
- * create_attributes:
- * @printer_uri: URI for the printer
- * @additional_labels: Optional labels for additional attributes
- * @additional_attrs: Optional additional attributes
- *
- * Creates a GVariant dictionary with key / value pairs that
- * can be used to identify a secret item.
- *
- * Returns: A GVariant dictionary of string pairs or NULL on error.
- */
-static GVariant *
-create_attributes (const gchar  *printer_uri,
-                   gchar       **additional_attrs,
-                   gchar       **additional_labels)
-{
-  GVariantBuilder *attr_builder = NULL;
-  GVariant        *ret = NULL;
-
-  if (printer_uri == NULL)
-    {
-      GTK_NOTE (PRINTING,
-                g_print ("create_attributes called with invalid parameters.\n"));
-      return NULL;
-    }
-
-  attr_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY);
-  /* The printer uri is the main identifying part */
-  g_variant_builder_add (attr_builder, "{ss}", "uri", printer_uri);
-
-  if (additional_labels != NULL)
-    {
-      int i;
-      for (i = 0; additional_labels[i] != NULL; i++)
-        {
-          g_variant_builder_add (attr_builder, "{ss}",
-                                 additional_labels[i],
-                                 additional_attrs[i]);
-        }
-    }
-
-  ret = g_variant_builder_end (attr_builder);
-  g_variant_builder_unref (attr_builder);
-
-  return ret;
-}
-
-static void
-get_secret_cb (GObject      *source_object,
-               GAsyncResult *res,
-               gpointer      user_data)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-  GError             *error = NULL;
-  GVariant           *output,
-                     *attributes;
-  gchar             **auth_info = NULL,
-                     *key = NULL,
-                     *value = NULL;
-  GVariantIter       *iter = NULL;
-  guint               i, required_len;
-  gint                pw_field = -1;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  output = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
-                                     res,
-                                     &error);
-  if (output == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  attributes = g_dbus_proxy_get_cached_property (task_data->item_proxy,
-                                                 "Attributes");
-  if (attributes == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("Failed to lookup attributes.\n"));
-      g_variant_unref (output);
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  /* Iterate over the attributes to fill the auth info */
-  g_variant_get (attributes, "a{ss}", &iter);
-
-  auth_info = g_new0 (gchar *,
-                      g_strv_length (task_data->auth_info_required) + 1);
-
-  while (g_variant_iter_loop (iter, "{ss}", &key, &value))
-    {
-      /* Match attributes with required auth info */
-      for (i = 0; task_data->auth_info_required[i] != NULL; i++)
-        {
-          if ((strcmp (key, "user") == 0 ||
-               strcmp (key, "username") == 0) &&
-              strcmp (task_data->auth_info_required[i],
-                      "username") == 0)
-            {
-              auth_info[i] = g_strdup (value);
-            }
-          else if (strcmp (key, "domain") == 0 &&
-                   strcmp (task_data->auth_info_required[i], "domain") == 0)
-            {
-              auth_info[i] = g_strdup (value);
-            }
-          else if ((strcmp (key, "hostname") == 0 ||
-                    strcmp (key, "server") == 0 ) &&
-                   strcmp (task_data->auth_info_required[i], "hostname") == 0)
-            {
-              auth_info[i] = g_strdup (value);
-            }
-          else if (strcmp (task_data->auth_info_required[i], "password") == 0)
-            {
-              pw_field = i;
-            }
-        }
-    }
-
-  if (pw_field == -1)
-    {
-      /* should not happen... */
-      GTK_NOTE (PRINTING, g_print ("No password required?.\n"));
-      g_variant_unref (output);
-      goto fail;
-    }
-  else
-    {
-      GVariant      *secret,
-                    *s_value;
-      gconstpointer  ba_passwd = NULL;
-      gsize          len = 0;
-
-      secret = g_variant_get_child_value (output, 0);
-      g_variant_unref (output);
-      if (secret == NULL || g_variant_n_children (secret) != 4)
-        {
-          GTK_NOTE (PRINTING, g_print ("Get secret response invalid.\n"));
-          if (secret != NULL)
-            g_variant_unref (secret);
-          goto fail;
-        }
-      s_value = g_variant_get_child_value (secret, 2);
-      ba_passwd = g_variant_get_fixed_array (s_value,
-                                             &len,
-                                             sizeof (guchar));
-
-      g_variant_unref (secret);
-
-      if (ba_passwd == NULL)
-        {
-          GTK_NOTE (PRINTING, g_print ("Invalid / no secret found.\n"));
-          g_variant_unref (s_value);
-          goto fail;
-        }
-
-      auth_info[pw_field] = g_strndup (ba_passwd, len);
-      g_variant_unref (s_value);
-    }
-
-  for (i = 0; task_data->auth_info_required[i] != NULL; i++)
-    {
-      if (auth_info[i] == NULL)
-        {
-          /* Error out if we did not find everything */
-          GTK_NOTE (PRINTING, g_print ("Failed to lookup required attribute: %s.\n",
-                                       task_data->auth_info_required[i]));
-          goto fail;
-        }
-    }
-
-  g_task_return_pointer (task, auth_info, NULL);
-  return;
-
-fail:
-  /* Error out */
-  GTK_NOTE (PRINTING, g_print ("Failed to lookup secret.\n"));
-  required_len = g_strv_length (task_data->auth_info_required);
-  for (i = 0; i < required_len; i++)
-    {
-      /* Not all fields of auth_info are neccessarily written so we can not
-         use strfreev here */
-      g_free (auth_info[i]);
-    }
-  g_free (auth_info);
-  g_task_return_pointer (task, NULL, NULL);
-}
-
-static void
-create_item_cb (GObject      *source_object,
-                GAsyncResult *res,
-                gpointer      user_data)
-{
-  GTask    *task;
-  GError   *error = NULL;
-  GVariant *output;
-  gchar    *item = NULL;
-
-  task = user_data;
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  g_variant_get (output, "(&o&o)", &item, NULL);
-  if (item != NULL && strlen (item) > 1)
-    {
-      GTK_NOTE (PRINTING, g_print ("Successfully stored auth info.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-  g_variant_unref (output);
-}
-
-static void
-do_store_auth_info (GTask *task)
-{
-  GVariant            *attributes = NULL,
-                      *properties = NULL,
-                      *secret = NULL;
-  gchar              **additional_attrs = NULL,
-                     **additional_labels = NULL,
-                      *password = NULL;
-  SecretsServiceData  *task_data = g_task_get_task_data (task);
-  guint                i,
-                       length,
-                       additional_count = 0;
-  GVariantBuilder     *prop_builder = NULL;
-
-  length = g_strv_length (task_data->auth_info_labels);
-
-  additional_attrs = g_new0 (gchar *, length + 1);
-  additional_labels = g_new0 (gchar *, length + 1);
-  /* The labels user and server are chosen to be compatible with
-     the attributes used by system-config-printer */
-  for (i = 0; task_data->auth_info_labels[i] != NULL; i++)
-    {
-      if (g_strcmp0 (task_data->auth_info_labels[i], "username") == 0)
-        {
-          additional_attrs[additional_count] = task_data->auth_info[i];
-          additional_labels[additional_count++] = "user";
-        }
-      else if (g_strcmp0 (task_data->auth_info_labels[i], "hostname") == 0)
-        {
-          additional_attrs[additional_count] = task_data->auth_info[i];
-          additional_labels[additional_count++] = "server";
-        }
-      else if (g_strcmp0 (task_data->auth_info_labels[i], "password") == 0)
-        {
-          password = task_data->auth_info[i];
-        }
-    }
-
-  attributes = create_attributes (task_data->printer_uri,
-                                  additional_attrs,
-                                  additional_labels);
-  g_free (additional_labels);
-  g_free (additional_attrs);
-  if (attributes == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  if (password == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("No secret to store.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  prop_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY);
-
-  g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Label"),
-                         g_variant_new_string (task_data->printer_uri));
-  g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Attributes"),
-                         attributes);
-
-  properties = g_variant_builder_end (prop_builder);
-
-  g_variant_builder_unref (prop_builder);
-
-  secret = g_variant_new ("(oay@ays)",
-                          task_data->session_path,
-                          NULL,
-                          g_variant_new_bytestring (password),
-                          "text/plain");
-
-  g_dbus_connection_call (task_data->dbus_connection,
-                          SECRETS_BUS,
-                          task_data->collection_path,
-                          SECRETS_IFACE ("Collection"),
-                          "CreateItem",
-                          g_variant_new ("(@a{sv}@(oayays)b)",
-                                         properties,
-                                         secret,
-                                         TRUE),
-                          G_VARIANT_TYPE ("(oo)"),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          SECRETS_TIMEOUT,
-                          g_task_get_cancellable (task),
-                          create_item_cb,
-                          task);
-}
-
-static void
-prompt_completed_cb (GDBusConnection *connection,
-                     const gchar     *sender_name,
-                     const gchar     *object_path,
-                     const gchar     *interface_name,
-                     const gchar     *signal_name,
-                     GVariant        *parameters,
-                     gpointer         user_data)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-  GVariant           *dismissed;
-  gboolean            is_dismissed = TRUE;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  g_dbus_connection_signal_unsubscribe (task_data->dbus_connection,
-                                        task_data->prompt_subscription);
-  task_data->prompt_subscription = 0;
-
-  dismissed = g_variant_get_child_value (parameters, 0);
-
-  if (dismissed == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("Invalid prompt signal.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  g_variant_get (dismissed, "b", &is_dismissed);
-  g_variant_unref (dismissed);
-
-  if (is_dismissed)
-    {
-      GTK_NOTE (PRINTING, g_print ("Collection unlock dismissed.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  /* Prompt successfull, proceed to get or store secret */
-  switch (task_data->action)
-    {
-      case SECRETS_SERVICE_ACTION_STORE:
-        do_store_auth_info (task);
-        break;
-
-      case SECRETS_SERVICE_ACTION_QUERY:
-        g_dbus_proxy_call (task_data->item_proxy,
-                           "GetSecret",
-                           g_variant_new ("(o)",
-                                          task_data->session_path),
-                           G_DBUS_CALL_FLAGS_NONE,
-                           SECRETS_TIMEOUT,
-                           g_task_get_cancellable (task),
-                           get_secret_cb,
-                           task);
-        break;
-    }
-}
-
-static void
-prompt_cb (GObject      *source_object,
-           GAsyncResult *res,
-           gpointer      user_data)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-  GError             *error = NULL;
-  GVariant           *output;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  g_variant_unref (output);
-
-  /* Connect to the prompt's completed signal */
-  task_data->prompt_subscription =
-    g_dbus_connection_signal_subscribe (task_data->dbus_connection,
-                                        NULL,
-                                        SECRETS_IFACE ("Prompt"),
-                                        "Completed",
-                                        NULL,
-                                        NULL,
-                                        G_DBUS_SIGNAL_FLAGS_NONE,
-                                        prompt_completed_cb,
-                                        task,
-                                        NULL);
-}
-
-static void
-unlock_collection_cb (GObject      *source_object,
-                      GAsyncResult *res,
-                      gpointer      user_data)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-  GError             *error = NULL;
-  GVariant           *output;
-  const gchar        *prompt_path;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  g_variant_get (output, "(@ao&o)", NULL, &prompt_path);
-
-  if (prompt_path != NULL && strlen (prompt_path) > 1)
-    {
-      g_dbus_connection_call (task_data->dbus_connection,
-                              SECRETS_BUS,
-                              prompt_path,
-                              SECRETS_IFACE ("Prompt"),
-                              "Prompt",
-                              g_variant_new ("(s)", "0"),
-                              G_VARIANT_TYPE ("()"),
-                              G_DBUS_CALL_FLAGS_NONE,
-                              SECRETS_TIMEOUT,
-                              g_task_get_cancellable (task),
-                              prompt_cb,
-                              task);
-    }
-  else
-    {
-      switch (task_data->action)
-        {
-          case SECRETS_SERVICE_ACTION_STORE:
-            do_store_auth_info (task);
-            break;
-
-          case SECRETS_SERVICE_ACTION_QUERY:
-            /* Prompt successfull proceed to get secret */
-            g_dbus_proxy_call (task_data->item_proxy,
-                               "GetSecret",
-                               g_variant_new ("(o)",
-                                              task_data->session_path),
-                               G_DBUS_CALL_FLAGS_NONE,
-                               SECRETS_TIMEOUT,
-                               g_task_get_cancellable (task),
-                               get_secret_cb,
-                               task);
-            break;
-        }
-    }
-  g_variant_unref (output);
-}
-
-static void
-unlock_read_alias_cb (GObject      *source_object,
-                      GAsyncResult *res,
-                      gpointer      user_data)
-{
-  GTask *task;
-  SecretsServiceData *task_data;
-  GError *error = NULL;
-  GVariant *output, *subresult;
-  gsize path_len = 0;
-  const gchar *collection_path;
-  const gchar *to_unlock[2];
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  subresult = g_variant_get_child_value (output, 0);
-  g_variant_unref (output);
-
-  if (subresult == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("Invalid ReadAlias response.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  collection_path = g_variant_get_string (subresult, &path_len);
-  to_unlock[0] = collection_path;
-  to_unlock[1] = NULL;
-
-  task_data->collection_path = g_strdup (collection_path);
-
-  g_dbus_connection_call (task_data->dbus_connection,
-                          SECRETS_BUS,
-                          SECRETS_PATH,
-                          SECRETS_IFACE ("Service"),
-                          "Unlock",
-                          g_variant_new ("(^ao)", to_unlock),
-                          G_VARIANT_TYPE ("(aoo)"),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          SECRETS_TIMEOUT,
-                          g_task_get_cancellable (task),
-                          unlock_collection_cb,
-                          task);
-
-  g_variant_unref (subresult);
-}
-
-static void
-item_proxy_cb (GObject      *source_object,
-               GAsyncResult *res,
-               gpointer      user_data)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-  GError             *error = NULL;
-  GDBusProxy         *item_proxy;
-  GVariant           *locked;
-  gboolean            is_locked;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  item_proxy = g_dbus_proxy_new_finish (res,
-                                        &error);
-  if (item_proxy == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  task_data->item_proxy = item_proxy;
-
-  locked = g_dbus_proxy_get_cached_property (item_proxy, "Locked");
-
-  if (locked == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("Failed to look up \"Locked\" property on item.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  g_variant_get (locked, "b", &is_locked);
-  g_variant_unref (locked);
-
-  if (is_locked)
-    {
-      /* Go down the unlock -> lookup path */
-      g_dbus_connection_call (task_data->dbus_connection,
-                              SECRETS_BUS,
-                              SECRETS_PATH,
-                              SECRETS_IFACE ("Service"),
-                              "ReadAlias",
-                              g_variant_new ("(s)", "default"),
-                              G_VARIANT_TYPE ("(o)"),
-                              G_DBUS_CALL_FLAGS_NONE,
-                              SECRETS_TIMEOUT,
-                              g_task_get_cancellable (task),
-                              unlock_read_alias_cb,
-                              task);
-      return;
-    }
-
-  /* Unlocked proceed to get or store secret */
-  switch (task_data->action)
-    {
-      case SECRETS_SERVICE_ACTION_STORE:
-        do_store_auth_info (task);
-        break;
-
-      case SECRETS_SERVICE_ACTION_QUERY:
-        g_dbus_proxy_call (item_proxy,
-                           "GetSecret",
-                           g_variant_new ("(o)",
-                                          task_data->session_path),
-                           G_DBUS_CALL_FLAGS_NONE,
-                           SECRETS_TIMEOUT,
-                           g_task_get_cancellable (task),
-                           get_secret_cb,
-                           task);
-        break;
-    }
-}
-
-static void
-search_items_cb (GObject      *source_object,
-                 GAsyncResult *res,
-                 gpointer      user_data)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-  GError             *error = NULL;
-  GVariant           *output;
-  gsize               array_cnt,
-                      i;
-  gboolean            found_item = FALSE;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  array_cnt = g_variant_n_children (output);
-
-  for (i = 0; i < array_cnt; i++)
-    {
-      GVariant * const   item_paths = g_variant_get_child_value (output, i);
-      const gchar      **items = NULL;
-
-      if (item_paths == NULL)
-        {
-          GTK_NOTE (PRINTING,
-                    g_print ("SearchItems returned invalid result.\n"));
-          continue;
-        }
-
-      items = g_variant_get_objv (item_paths, NULL);
-
-      if (*items == NULL)
-        {
-          g_variant_unref (item_paths);
-          g_free ((gpointer) items);
-          continue;
-        }
-
-      /* Access the first found item. */
-      found_item = TRUE;
-      g_dbus_proxy_new (task_data->dbus_connection,
-                        G_DBUS_PROXY_FLAGS_NONE,
-                        NULL,
-                        SECRETS_BUS,
-                        *items,
-                        SECRETS_IFACE ("Item"),
-                        g_task_get_cancellable (task),
-                        item_proxy_cb,
-                        task);
-      g_free ((gpointer) items);
-      g_variant_unref (item_paths);
-      break;
-    }
-  g_variant_unref (output);
-
-  if (!found_item)
-    {
-      GTK_NOTE (PRINTING, g_print ("No match found in secrets service.\n"));
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-}
-
-static void
-open_session_cb (GObject      *source_object,
-                 GAsyncResult *res,
-                 gpointer      user_data)
-{
-  GTask              *task;
-  GVariant           *output,
-                     *session_variant;
-  SecretsServiceData *task_data;
-  GError             *error = NULL;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  session_variant = g_variant_get_child_value (output, 1);
-
-  if (session_variant == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("Invalid session path response.\n"));
-      g_variant_unref (output);
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  task_data->session_path = g_variant_dup_string (session_variant, NULL);
-
-  if (task_data->session_path == NULL)
-    {
-      GTK_NOTE (PRINTING, g_print ("Invalid session path string value.\n"));
-      g_variant_unref (session_variant);
-      g_variant_unref (output);
-      g_task_return_pointer (task, NULL, NULL);
-      return;
-    }
-
-  g_variant_unref (session_variant);
-  g_variant_unref (output);
-
-  switch (task_data->action)
-    {
-      case SECRETS_SERVICE_ACTION_QUERY:
-        {
-          /* Search for the secret item */
-          GVariant *secrets_attrs;
-
-          secrets_attrs = create_attributes (task_data->printer_uri, NULL, NULL);
-          if (secrets_attrs == NULL)
-            {
-              GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n"));
-              g_task_return_pointer (task, NULL, NULL);
-              return;
-            }
-
-          g_dbus_connection_call (task_data->dbus_connection,
-                                  SECRETS_BUS,
-                                  SECRETS_PATH,
-                                  SECRETS_IFACE ("Service"),
-                                  "SearchItems",
-                                  g_variant_new ("(@a{ss})", secrets_attrs),
-                                  G_VARIANT_TYPE ("(aoao)"),
-                                  G_DBUS_CALL_FLAGS_NONE,
-                                  SECRETS_TIMEOUT,
-                                  g_task_get_cancellable (task),
-                                  search_items_cb,
-                                  task);
-          break;
-        }
-
-      case SECRETS_SERVICE_ACTION_STORE:
-        {
-          /* Look up / unlock the default collection for storing */
-          g_dbus_connection_call (task_data->dbus_connection,
-                                  SECRETS_BUS,
-                                  SECRETS_PATH,
-                                  SECRETS_IFACE ("Service"),
-                                  "ReadAlias",
-                                  g_variant_new ("(s)", "default"),
-                                  G_VARIANT_TYPE ("(o)"),
-                                  G_DBUS_CALL_FLAGS_NONE,
-                                  SECRETS_TIMEOUT,
-                                  g_task_get_cancellable (task),
-                                  unlock_read_alias_cb,
-                                  task);
-          break;
-        }
-    }
-}
-
-static void
-get_connection_cb (GObject      *source_object,
-                   GAsyncResult *res,
-                   gpointer      user_data)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-  GError             *error = NULL;
-
-  task = user_data;
-  task_data = g_task_get_task_data (task);
-
-  task_data->dbus_connection = g_bus_get_finish (res, &error);
-  if (task_data->dbus_connection == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  /* Now open a session */
-  g_dbus_connection_call (task_data->dbus_connection,
-                          SECRETS_BUS,
-                          SECRETS_PATH,
-                          SECRETS_IFACE ("Service"),
-                          "OpenSession",
-                          g_variant_new ("(sv)", "plain",
-                                         g_variant_new_string ("")),
-                          G_VARIANT_TYPE ("(vo)"),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          SECRETS_TIMEOUT,
-                          g_task_get_cancellable (task),
-                          open_session_cb,
-                          task);
-}
-
-/**
- * gtk_cups_secrets_service_watch:
- * @appeared: The callback to call when the service interface appears
- * @vanished: The callback to call when the service interface vanishes
- * @user_data: A reference to the watching printbackend
- *
- * Registers a watch for the secrets service interface.
- *
- * Returns: The watcher id
- */
-guint
-gtk_cups_secrets_service_watch (GBusNameAppearedCallback appeared,
-                                GBusNameVanishedCallback vanished,
-                                gpointer                 user_data)
-{
-  return g_bus_watch_name (G_BUS_TYPE_SESSION,
-                           SECRETS_BUS,
-                           G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
-                           appeared,
-                           vanished,
-                           user_data,
-                           NULL);
-}
-
-void
-cleanup_task_data (gpointer data)
-{
-  gint                i;
-  SecretsServiceData *task_data = data;
-
-  g_free (task_data->collection_path);
-  g_strfreev (task_data->auth_info_labels);
-  g_strfreev (task_data->auth_info_required);
-  g_free (task_data->printer_uri);
-
-  if (task_data->auth_info != NULL)
-    {
-      for (i = 0; task_data->auth_info[i] != NULL; i++)
-        {
-          memset (task_data->auth_info[i], 0, strlen (task_data->auth_info[i]));
-          g_clear_pointer (&task_data->auth_info[i], g_free);
-        }
-      g_clear_pointer (&task_data->auth_info, g_free);
-    }
-
-  if (task_data->prompt_subscription != 0)
-    {
-      g_dbus_connection_signal_unsubscribe (task_data->dbus_connection,
-                                            task_data->prompt_subscription);
-      task_data->prompt_subscription = 0;
-    }
-
-  if (task_data->session_path != NULL)
-    {
-      g_dbus_connection_call (task_data->dbus_connection,
-                              SECRETS_BUS,
-                              task_data->session_path,
-                              SECRETS_IFACE ("Session"),
-                              "Close",
-                              NULL,
-                              G_VARIANT_TYPE ("()"),
-                              G_DBUS_CALL_FLAGS_NONE,
-                              SECRETS_TIMEOUT,
-                              NULL,
-                              NULL,
-                              NULL);
-    }
-
-  g_clear_object (&task_data->dbus_connection);
-  g_clear_pointer (&task_data->session_path, g_free);
-  g_clear_object (&task_data->item_proxy);
-}
-
-/**
- * gtk_cups_secrets_service_query_task:
- * @source_object: Source object for this task
- * @cancellable: Cancellable to cancel this task
- * @callback: Callback to call once the query is finished
- * @user_data: The user_data passed to the callback
- * @printer_uri: URI of the printer
- * @auth_info_required: Info required for authentication
- *
- * Checks if a secrets service as described by the secrets-service standard
- * is available and if so it tries to find the authentication info in the
- * default collection of the service.
- *
- * This is the entry point to a chain of async calls to open a session,
- * search the secret, unlock the collection (if necessary) and finally
- * to lookup the secret.
- *
- * See: http://standards.freedesktop.org/secret-service/ for documentation
- * of the used API.
- */
-void
-gtk_cups_secrets_service_query_task (gpointer              source_object,
-                                     GCancellable         *cancellable,
-                                     GAsyncReadyCallback   callback,
-                                     gpointer              user_data,
-                                     const gchar          *printer_uri,
-                                     gchar               **auth_info_required)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-
-  task_data = g_new0 (SecretsServiceData, 1);
-  task_data->action = SECRETS_SERVICE_ACTION_QUERY;
-  task_data->printer_uri = g_strdup (printer_uri);
-  task_data->auth_info_required = g_strdupv (auth_info_required);
-
-  task = g_task_new (source_object, cancellable, callback, user_data);
-
-  g_task_set_task_data (task, task_data, cleanup_task_data);
-
-  g_bus_get (G_BUS_TYPE_SESSION, cancellable,
-             get_connection_cb, task);
-}
-
-static void
-store_done_cb (GObject      *source_object,
-               GAsyncResult *res,
-               gpointer      user_data)
-{
-  GTask  *task = (GTask *) res;
-  GError *error = NULL;
-
-  g_task_propagate_pointer (task, &error);
-
-  if (error != NULL)
-    {
-      GTK_NOTE (PRINTING,
-                g_print ("Failed to store auth info: %s\n", error->message));
-      g_error_free (error);
-    }
-
-  g_object_unref (task);
-  GTK_NOTE (PRINTING,
-            g_print ("gtk_cups_secrets_service_store finished.\n"));
-}
-
-/**
- * gtk_cups_secrets_service_store:
- * @auth_info: Auth info that should be stored
- * @auth_info_labels: The keys to use for the auth info
- * @printer_uri: URI of the printer
- *
- * Tries to store the auth_info in a secrets service.
- */
-void
-gtk_cups_secrets_service_store (gchar       **auth_info,
-                                gchar       **auth_info_labels,
-                                const gchar  *printer_uri)
-{
-  GTask              *task;
-  SecretsServiceData *task_data;
-
-  if (auth_info == NULL || auth_info_labels == NULL || printer_uri == NULL)
-    {
-      GTK_NOTE (PRINTING,
-                g_print ("Invalid call to gtk_cups_secrets_service_store.\n"));
-      return;
-    }
-
-  task_data = g_new0 (SecretsServiceData, 1);
-  task_data->action = SECRETS_SERVICE_ACTION_STORE;
-  task_data->printer_uri = g_strdup (printer_uri);
-  task_data->auth_info = g_strdupv (auth_info);
-  task_data->auth_info_labels = g_strdupv (auth_info_labels);
-
-  task = g_task_new (NULL, NULL, store_done_cb, NULL);
-
-  g_task_set_task_data (task, task_data, cleanup_task_data);
-
-  g_bus_get (G_BUS_TYPE_SESSION, NULL,
-             get_connection_cb, task);
-}
diff --git a/modules/printbackends/cups/gtkcupssecretsutils.h b/modules/printbackends/cups/gtkcupssecretsutils.h
deleted file mode 100644 (file)
index 1a0424a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords
- * Copyright (C) 2014 Intevation GmbH
- *
- * 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_SECRETS_UTILS_H__
-#define __GTK_SECRETS_UTILS_H__
-
-#include <glib.h>
-
-#include "gtkcupsutils.h"
-
-G_BEGIN_DECLS
-
-void  gtk_cups_secrets_service_query_task (gpointer                   source_object,
-                                           GCancellable              *cancellable,
-                                           GAsyncReadyCallback        callback,
-                                           gpointer                   user_data,
-                                           const gchar               *printer_uri,
-                                           gchar                    **auth_info_required);
-guint gtk_cups_secrets_service_watch      (GBusNameAppearedCallback   appeared,
-                                           GBusNameVanishedCallback   vanished,
-                                           gpointer                   user_data);
-void  gtk_cups_secrets_service_store      (gchar                    **auth_info,
-                                           gchar                    **auth_info_labels,
-                                           const gchar               *printer_uri);
-
-G_END_DECLS
-
-#endif /* __GTK_SECRETS_UTILS_H__ */
diff --git a/modules/printbackends/cups/gtkcupsutils.c b/modules/printbackends/cups/gtkcupsutils.c
deleted file mode 100644 (file)
index 54239ca..0000000
+++ /dev/null
@@ -1,1663 +0,0 @@
-/* GTK - The GIMP Toolkit
- * gtkcupsutils.h: Statemachine implementation of POST and GET 
- * cups calls which can be used to create a non-blocking cups API
- * Copyright (C) 2006, 2007 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 <gtk/gtk.h>
-#include "gtkcupsutils.h"
-
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <time.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-
-typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
-
-static void _connect            (GtkCupsRequest *request);
-static void _post_send          (GtkCupsRequest *request);
-static void _post_write_request (GtkCupsRequest *request);
-static void _post_write_data    (GtkCupsRequest *request);
-static void _post_check         (GtkCupsRequest *request);
-static void _post_auth          (GtkCupsRequest *request);
-static void _post_read_response (GtkCupsRequest *request);
-
-static void _get_send           (GtkCupsRequest *request);
-static void _get_check          (GtkCupsRequest *request);
-static void _get_auth           (GtkCupsRequest *request);
-static void _get_read_data      (GtkCupsRequest *request);
-
-struct _GtkCupsResult
-{
-  gchar *error_msg;
-  ipp_t *ipp_response;
-  GtkCupsErrorType error_type;
-
-  /* some error types like HTTP_ERROR have a status and a code */
-  int error_status;            
-  int error_code;
-
-  guint is_error : 1;
-  guint is_ipp_response : 1;
-};
-
-
-#define _GTK_CUPS_MAX_ATTEMPTS 10 
-#define _GTK_CUPS_MAX_CHUNK_SIZE 8192
-
-static GtkCupsRequestStateFunc post_states[] = {
-  _connect,
-  _post_send,
-  _post_write_request,
-  _post_write_data,
-  _post_check,
-  _post_auth,
-  _post_read_response
-};
-
-static GtkCupsRequestStateFunc get_states[] = {
-  _connect,
-  _get_send,
-  _get_check,
-  _get_auth,
-  _get_read_data
-};
-
-#ifndef HAVE_CUPS_API_1_6
-#define ippSetOperation(ipp_request, ipp_op_id) ipp_request->request.op.operation_id = ipp_op_id
-#define ippSetRequestId(ipp_request, ipp_rq_id) ipp_request->request.op.request_id = ipp_rq_id
-#define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state
-#define ippGetString(attr, index, foo) attr->values[index].string.text
-#define ippGetCount(attr) attr->num_values
-
-int
-ippSetVersion (ipp_t *ipp,
-               int    major,
-               int    minor)
-{
-  if (!ipp || major < 0 || minor < 0)
-    return 0;
-
-  ipp->request.any.version[0] = major;
-  ipp->request.any.version[1] = minor;
-
-  return 1;
-}
-#endif
-
-static void
-gtk_cups_result_set_error (GtkCupsResult    *result,
-                           GtkCupsErrorType  error_type,
-                           int               error_status,
-                           int               error_code, 
-                           const char       *error_msg,
-                          ...)
-{
-  va_list args;
-
-  result->is_ipp_response = FALSE;
-  result->is_error = TRUE;
-  result->error_type = error_type;
-  result->error_status = error_status;
-  result->error_code = error_code;
-
-  va_start (args, error_msg);
-  result->error_msg = g_strdup_vprintf (error_msg, args);
-  va_end (args);
-}
-
-GtkCupsRequest *
-gtk_cups_request_new_with_username (http_t             *connection,
-                                    GtkCupsRequestType  req_type, 
-                                    gint                operation_id,
-                                    GIOChannel         *data_io,
-                                    const char         *server,
-                                    const char         *resource,
-                                    const char         *username)
-{
-  GtkCupsRequest *request;
-  cups_lang_t *language;
-  
-  request = g_new0 (GtkCupsRequest, 1);
-  request->result = g_new0 (GtkCupsResult, 1);
-
-  request->result->error_msg = NULL;
-  request->result->ipp_response = NULL;
-
-  request->result->is_error = FALSE;
-  request->result->is_ipp_response = FALSE;
-
-  request->type = req_type;
-  request->state = GTK_CUPS_REQUEST_START;
-
-  request->password_state = GTK_CUPS_PASSWORD_NONE;
-
-   if (server)
-    request->server = g_strdup (server);
-  else
-    request->server = g_strdup (cupsServer ());
-
-
-  if (resource)
-    request->resource = g_strdup (resource);
-  else
-    request->resource = g_strdup ("/");
-  if (connection != NULL)
-    {
-      request->http = connection;
-      request->own_http = FALSE;
-    }
-  else
-    {
-      request->http = NULL;
-      request->http = httpConnectEncrypt (request->server, 
-                                          ippPort (), 
-                                          cupsEncryption ());
-
-      if (request->http)
-        httpBlocking (request->http, 0);
-        
-      request->own_http = TRUE;
-    }
-
-  request->last_status = HTTP_CONTINUE;
-
-  request->attempts = 0;
-  request->data_io = data_io;
-
-  request->ipp_request = ippNew ();
-  ippSetOperation (request->ipp_request, operation_id);
-  ippSetRequestId (request->ipp_request, 1);
-
-  language = cupsLangDefault ();
-
-  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-                                   "attributes-charset", 
-                                   NULL, "utf-8");
-       
-  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-                                   "attributes-natural-language", 
-                                   NULL, language->language);
-
-  if (username != NULL)
-    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
-                                     "requesting-user-name",
-                                     NULL, username);
-  else
-    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
-                                     "requesting-user-name",
-                                     NULL, cupsUser ());
-
-  request->auth_info_required = NULL;
-  request->auth_info = NULL;
-  request->need_auth_info = FALSE;
-
-  cupsLangFree (language);
-
-  return request;
-}
-
-GtkCupsRequest *
-gtk_cups_request_new (http_t             *connection,
-                      GtkCupsRequestType  req_type, 
-                      gint                operation_id,
-                      GIOChannel         *data_io,
-                      const char         *server,
-                      const char         *resource)
-{
-  return gtk_cups_request_new_with_username (connection,
-                                             req_type,
-                                             operation_id,
-                                             data_io,
-                                             server,
-                                             resource,
-                                             NULL);
-}
-
-static void
-gtk_cups_result_free (GtkCupsResult *result)
-{
-  g_free (result->error_msg);
-
-  if (result->ipp_response)
-    ippDelete (result->ipp_response);
-
-  g_free (result);
-}
-
-void
-gtk_cups_request_free (GtkCupsRequest *request)
-{
-  if (request->own_http)
-    {
-      if (request->http)
-        httpClose (request->http);
-    }
-  
-  if (request->ipp_request)
-    ippDelete (request->ipp_request);
-
-  g_free (request->server);
-  g_free (request->resource);
-  if (request->password != NULL)
-    {
-      memset (request->password, 0, strlen (request->password));
-      g_free (request->password);
-    }
-
-  g_free (request->username);
-  g_strfreev (request->auth_info_required);
-
-  gtk_cups_result_free (request->result);
-
-  g_free (request);
-}
-
-gboolean 
-gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
-{
-  if (connect_only && request->state != GTK_CUPS_REQUEST_START)
-    return FALSE;
-
-  do
-    {
-      if (request->type == GTK_CUPS_POST)
-        post_states[request->state] (request);
-      else if (request->type == GTK_CUPS_GET)
-        get_states[request->state] (request);
-
-      if (gtk_cups_result_is_error (request->result))
-        request->state = GTK_CUPS_REQUEST_DONE;
-
-      if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
-          request->state != GTK_CUPS_REQUEST_DONE)
-        {
-          /* TODO: should add a status or error code for too many failed attempts */
-          gtk_cups_result_set_error (request->result,
-                                     GTK_CUPS_ERROR_GENERAL,
-                                     0,
-                                     0,
-                                     "Too many failed attempts");
-
-          request->state = GTK_CUPS_REQUEST_DONE;
-        }
-
-      if (request->state == GTK_CUPS_REQUEST_DONE)
-        {
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-          return TRUE;
-        }
-    }
-  /* We need to recheck using httpCheck if the poll_state is read, because
-   * Cups has an internal read buffer. And if this buffer is filled, we may
-   * never get a poll event again. */
-  while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
-
-  return FALSE;
-}
-
-GtkCupsPollState 
-gtk_cups_request_get_poll_state (GtkCupsRequest *request)
-{
-  return request->poll_state;
-}
-
-
-
-GtkCupsResult *
-gtk_cups_request_get_result (GtkCupsRequest *request)
-{
-  return request->result;
-}
-
-void            
-gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
-                                 ipp_tag_t       group,
-                                 ipp_tag_t       tag,
-                                 const char     *name,
-                                 const char     *charset,
-                                 const char     *value)
-{
-  ippAddString (request->ipp_request,
-                group,
-                tag,
-                name,
-                charset,
-                value);
-}
-
-void            
-gtk_cups_request_ipp_add_strings (GtkCupsRequest    *request,
-                                 ipp_tag_t          group,
-                                 ipp_tag_t          tag,
-                                 const char        *name,
-                                 int                num_values,
-                                 const char        *charset,
-                                 const char *const *values)
-{
-  ippAddStrings (request->ipp_request,
-                group,
-                tag,
-                name,
-                num_values,
-                charset,
-                values);
-}
-
-const char *
-gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
-                                 ipp_tag_t       tag,
-                                 const char     *name)
-{
-  ipp_attribute_t *attribute = NULL;
-
-  if (request != NULL && request->ipp_request != NULL)
-    attribute = ippFindAttribute (request->ipp_request,
-                                  name,
-                                  tag);
-
-  if (attribute != NULL && ippGetCount (attribute) > 0)
-      return ippGetString (attribute, 0, NULL);
-  else
-    return NULL;
-}
-
-
-typedef struct
-{
-  const char   *name;
-  ipp_tag_t    value_tag;
-} ipp_option_t;
-
-static const ipp_option_t ipp_options[] = {
-  { "blackplot",               IPP_TAG_BOOLEAN },
-  { "brightness",              IPP_TAG_INTEGER },
-  { "columns",                 IPP_TAG_INTEGER },
-  { "copies",                  IPP_TAG_INTEGER },
-  { "finishings",              IPP_TAG_ENUM },
-  { "fitplot",                 IPP_TAG_BOOLEAN },
-  { "gamma",                   IPP_TAG_INTEGER },
-  { "hue",                     IPP_TAG_INTEGER },
-  { "job-k-limit",             IPP_TAG_INTEGER },
-  { "job-page-limit",          IPP_TAG_INTEGER },
-  { "job-priority",            IPP_TAG_INTEGER },
-  { "job-quota-period",                IPP_TAG_INTEGER },
-  { "landscape",               IPP_TAG_BOOLEAN },
-  { "media",                   IPP_TAG_KEYWORD },
-  { "mirror",                  IPP_TAG_BOOLEAN },
-  { "natural-scaling",         IPP_TAG_INTEGER },
-  { "number-up",               IPP_TAG_INTEGER },
-  { "orientation-requested",   IPP_TAG_ENUM },
-  { "page-bottom",             IPP_TAG_INTEGER },
-  { "page-left",               IPP_TAG_INTEGER },
-  { "page-ranges",             IPP_TAG_RANGE },
-  { "page-right",              IPP_TAG_INTEGER },
-  { "page-top",                        IPP_TAG_INTEGER },
-  { "penwidth",                        IPP_TAG_INTEGER },
-  { "ppi",                     IPP_TAG_INTEGER },
-  { "prettyprint",             IPP_TAG_BOOLEAN },
-  { "printer-resolution",      IPP_TAG_RESOLUTION },
-  { "print-quality",           IPP_TAG_ENUM },
-  { "saturation",              IPP_TAG_INTEGER },
-  { "scaling",                 IPP_TAG_INTEGER },
-  { "sides",                   IPP_TAG_KEYWORD },
-  { "wrap",                    IPP_TAG_BOOLEAN },
-  { "number-up-layout",                IPP_TAG_INTEGER }
-};
-
-
-static ipp_tag_t
-_find_option_tag (const gchar *option)
-{
-  int lower_bound, upper_bound, num_options;
-  int current_option;
-  ipp_tag_t result;
-
-  result = IPP_TAG_ZERO;
-
-  lower_bound = 0;
-  upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
-  
-  while (1)
-    {
-      int match;
-      current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
-
-      match = strcasecmp (option, ipp_options[current_option].name);
-      if (match == 0)
-        {
-         result = ipp_options[current_option].value_tag;
-         return result;
-       }
-      else if (match < 0)
-        {
-          upper_bound = current_option - 1;
-       }
-      else
-        {
-          lower_bound = current_option + 1;
-       }
-
-      if (upper_bound == lower_bound && upper_bound == current_option)
-        return result;
-
-      if (upper_bound < 0)
-        return result;
-
-      if (lower_bound > num_options)
-        return result;
-
-      if (upper_bound < lower_bound)
-        return result;
-    }
-}
-
-/*
- * Note that this function uses IPP_TAG_JOB, so it is
- * only suitable for IPP Group 2 attributes.
- * See RFC 2911.
- */
-void
-gtk_cups_request_encode_option (GtkCupsRequest *request,
-                                const gchar    *option,
-                               const gchar    *value)
-{
-  ipp_tag_t option_tag;
-
-  g_return_if_fail (option != NULL);
-  g_return_if_fail (value != NULL);
-
-  option_tag = _find_option_tag (option);
-
-  if (option_tag == IPP_TAG_ZERO)
-    {
-      option_tag = IPP_TAG_NAME;
-      if (strcasecmp (value, "true") == 0 ||
-          strcasecmp (value, "false") == 0)
-        {
-          option_tag = IPP_TAG_BOOLEAN;
-        }
-    }
-        
-  switch (option_tag)
-    {
-      case IPP_TAG_INTEGER:
-      case IPP_TAG_ENUM:
-        ippAddInteger (request->ipp_request,
-                       IPP_TAG_JOB,
-                       option_tag,
-                       option,
-                       strtol (value, NULL, 0));
-        break;
-
-      case IPP_TAG_BOOLEAN:
-        {
-          char b;
-          
-          if (strcasecmp (value, "true") == 0 ||
-             strcasecmp (value, "on") == 0 ||
-             strcasecmp (value, "yes") == 0) 
-           b = 1;
-         else
-            b = 0;
-
-          ippAddBoolean (request->ipp_request,
-                         IPP_TAG_JOB,
-                         option,
-                         b);
-        
-          break;
-        }
-        
-      case IPP_TAG_RANGE:
-        {
-          char *s;
-          int lower;
-          int upper;
-          
-          if (*value == '-')
-           {
-             lower = 1;
-             s = (char *)value;
-           }
-         else
-           lower = strtol (value, &s, 0);
-
-         if (*s == '-')
-           {
-             if (s[1])
-               upper = strtol (s + 1, NULL, 0);
-             else
-               upper = 2147483647;
-            }
-         else
-           upper = lower;
-         
-          ippAddRange (request->ipp_request,
-                       IPP_TAG_JOB,
-                       option,
-                       lower,
-                       upper);
-
-          break;
-        }
-
-      case IPP_TAG_RESOLUTION:
-        {
-          char *s;
-          int xres;
-          int yres;
-          ipp_res_t units;
-          
-          xres = strtol (value, &s, 0);
-
-         if (*s == 'x')
-           yres = strtol (s + 1, &s, 0);
-         else
-           yres = xres;
-
-         if (strcasecmp (s, "dpc") == 0)
-            units = IPP_RES_PER_CM;
-          else
-            units = IPP_RES_PER_INCH;
-          
-          ippAddResolution (request->ipp_request,
-                            IPP_TAG_JOB,
-                            option,
-                            units,
-                            xres,
-                            yres);
-
-          break;
-        }
-
-      default:
-        {
-          char *values;
-          char *s;
-          int in_quotes;
-          char *next;
-          GPtrArray *strings;
-          
-          values = g_strdup (value);
-          strings = NULL;
-         in_quotes = 0;
-
-          for (s = values, next = s; *s != '\0'; s++)
-            {
-              if (in_quotes != 2 && *s == '\'')
-                {
-                  /* skip quoted value */
-                  if (in_quotes == 0)
-                    in_quotes = 1;
-                  else
-                    in_quotes = 0;
-                }
-              else if (in_quotes != 1 && *s == '\"')
-                {
-                  /* skip quoted value */
-                  if (in_quotes == 0)
-                    in_quotes = 2;
-                  else
-                    in_quotes = 0;
-                }
-              else if (in_quotes == 0 && *s == ',')
-                {
-                  /* found delimiter, add to value array */
-                  *s = '\0';
-                  if (strings == NULL)
-                    strings = g_ptr_array_new ();
-                  g_ptr_array_add (strings, next);
-                  next = s + 1;
-                }
-              else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
-                {
-                  /* skip escaped character */
-                  s++;
-                }
-            }
-          
-          if (strings == NULL)
-            {
-              /* single value */
-              ippAddString (request->ipp_request,
-                            IPP_TAG_JOB,
-                            option_tag,
-                            option,
-                            NULL,
-                            value);
-            }
-          else
-            {
-              /* multiple values */
-              
-              /* add last value */
-              g_ptr_array_add (strings, next);
-              
-              ippAddStrings (request->ipp_request,
-                             IPP_TAG_JOB,
-                             option_tag,
-                             option,
-                             strings->len,
-                             NULL,
-                             (const char **) strings->pdata);
-              g_ptr_array_free (strings, TRUE);
-            }
-
-          g_free (values);
-        }
-
-        break;
-    }
-}
-                               
-void
-gtk_cups_request_set_ipp_version (GtkCupsRequest     *request,
-                                 gint                major,
-                                 gint                minor)
-{
-  ippSetVersion (request->ipp_request, major, minor);
-}
-
-static void
-_connect (GtkCupsRequest *request)
-{
-  request->poll_state = GTK_CUPS_HTTP_IDLE;
-  request->bytes_received = 0;
-
-  if (request->http == NULL)
-    {
-      request->http = httpConnectEncrypt (request->server, 
-                                          ippPort (), 
-                                          cupsEncryption ());
-
-      if (request->http == NULL)
-        request->attempts++;
-
-      if (request->http)
-        httpBlocking (request->http, 0);
-        
-      request->own_http = TRUE;
-    }
-  else
-    {
-      request->attempts = 0;
-      request->state++;
-
-      /* we always write to the socket after we get
-         the connection */
-      request->poll_state = GTK_CUPS_HTTP_WRITE;
-    }
-}
-
-static void 
-_post_send (GtkCupsRequest *request)
-{
-  gchar length[255];
-  struct stat data_info;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  request->poll_state = GTK_CUPS_HTTP_WRITE;
-
-  if (request->data_io != NULL)
-    {
-      fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
-      sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
-    }
-  else
-    sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
-       
-  httpClearFields (request->http);
-  httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
-  httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
-#ifdef HAVE_HTTPGETAUTHSTRING
-  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
-#else
-#ifdef HAVE_HTTP_AUTHSTRING
-  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
-#endif
-#endif
-
-  if (httpPost (request->http, request->resource))
-    {
-      if (httpReconnect (request->http))
-        {
-          request->state = GTK_CUPS_POST_DONE;
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-
-          /* TODO: should add a status or error code for failed post */
-          gtk_cups_result_set_error (request->result,
-                                     GTK_CUPS_ERROR_GENERAL,
-                                     0,
-                                     0,
-                                     "Failed Post");
-        }
-
-      request->attempts++;
-      return;    
-    }
-        
-    request->attempts = 0;
-
-    request->state = GTK_CUPS_POST_WRITE_REQUEST;
-    ippSetState (request->ipp_request, IPP_IDLE);
-}
-
-static void 
-_post_write_request (GtkCupsRequest *request)
-{
-  ipp_state_t ipp_status;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  request->poll_state = GTK_CUPS_HTTP_WRITE;
-  
-  ipp_status = ippWrite (request->http, request->ipp_request);
-
-  if (ipp_status == IPP_ERROR)
-    {
-      int cups_error = cupsLastError ();
-      request->state = GTK_CUPS_POST_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-      gtk_cups_result_set_error (request->result, 
-                                 GTK_CUPS_ERROR_IPP,
-                                 ipp_status,
-                                 cups_error,
-                                 "%s", 
-                                 ippErrorString (cups_error));
-      return;
-    }
-
-  if (ipp_status == IPP_DATA)
-    {
-      if (request->data_io != NULL)
-        request->state = GTK_CUPS_POST_WRITE_DATA;
-      else
-        {
-          request->state = GTK_CUPS_POST_CHECK;
-          request->poll_state = GTK_CUPS_HTTP_READ;
-       }
-    }
-}
-
-static void 
-_post_write_data (GtkCupsRequest *request)
-{
-  gsize bytes;
-  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
-  http_status_t http_status;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  request->poll_state = GTK_CUPS_HTTP_WRITE;
-  
-  if (httpCheck (request->http))
-    http_status = httpUpdate (request->http);
-  else
-    http_status = request->last_status;
-
-  request->last_status = http_status;
-
-
-  if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
-    {
-      GIOStatus io_status;
-      GError *error;
-
-      error = NULL;
-
-      /* send data */
-      io_status =
-        g_io_channel_read_chars (request->data_io, 
-                                buffer, 
-                                _GTK_CUPS_MAX_CHUNK_SIZE,
-                                &bytes,
-                                &error);
-
-      if (io_status == G_IO_STATUS_ERROR)
-        {
-          request->state = GTK_CUPS_POST_DONE;
-         request->poll_state = GTK_CUPS_HTTP_IDLE;
-     
-          gtk_cups_result_set_error (request->result,
-                                     GTK_CUPS_ERROR_IO,
-                                     io_status,
-                                     error->code, 
-                                     "Error reading from cache file: %s",
-                                     error->message);
-
-         g_error_free (error);
-          return;
-       }
-      else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
-        {
-          request->state = GTK_CUPS_POST_CHECK;
-         request->poll_state = GTK_CUPS_HTTP_READ;
-
-          request->attempts = 0;
-          return;
-        }
-
-
-      if (httpWrite2 (request->http, buffer, bytes) < bytes)
-        {
-          int http_errno;
-
-          http_errno = httpError (request->http);
-
-          request->state = GTK_CUPS_POST_DONE;
-         request->poll_state = GTK_CUPS_HTTP_IDLE;
-     
-          gtk_cups_result_set_error (request->result,
-                                     GTK_CUPS_ERROR_HTTP,
-                                     http_status,
-                                     http_errno, 
-                                     "Error writing to socket in Post %s", 
-                                     g_strerror (http_errno));
-          return;
-        }
-    }
-  else if (http_status == HTTP_UNAUTHORIZED)
-    {
-      request->state = GTK_CUPS_POST_CHECK;
-      request->poll_state = GTK_CUPS_HTTP_READ;
-
-      request->attempts = 0;
-      return;
-    }
-  else
-    {
-      request->attempts++;
-    }
-}
-
-static void
-_post_auth (GtkCupsRequest *request)
-{
-  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
-    {
-      if (request->password == NULL)
-        {
-          request->state = GTK_CUPS_POST_DONE;
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-
-          gtk_cups_result_set_error (request->result, 
-                                     GTK_CUPS_ERROR_AUTH,
-                                     0,
-                                     1,
-                                     "Canceled by user");
-        }
-      else
-        request->state = GTK_CUPS_POST_CHECK;
-    }
-}
-
-static void
-_get_auth (GtkCupsRequest *request)
-{
-  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
-    {
-      if (request->password == NULL)
-        {
-          request->state = GTK_CUPS_GET_DONE;
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-
-          gtk_cups_result_set_error (request->result, 
-                                     GTK_CUPS_ERROR_AUTH,
-                                     0,
-                                     1,
-                                     "Canceled by user");
-        }
-      else
-        request->state = GTK_CUPS_GET_CHECK;
-    }
-}
-
-/* Very ugly hack: cups has a stupid synchronous password callback 
- * that doesn't even take the request or user data parameters, so 
- * we have to use a static variable to pass the password to it.
- * Not threadsafe !
- * The callback sets cups_password to NULL to signal that the 
- * password has been used.
- */
-static char *cups_password = NULL;
-static char *cups_username = NULL;
-
-static const char *
-passwordCB (const char *prompt)
-{
-  char *pwd = cups_password;
-  cups_password = NULL;
-
-  cupsSetUser (cups_username);
-
-  return pwd;
-}
-
-static void 
-_post_check (GtkCupsRequest *request)
-{
-  http_status_t http_status;
-
-  http_status = request->last_status;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
-
-  request->poll_state = GTK_CUPS_HTTP_READ;
-
-  if (http_status == HTTP_CONTINUE)
-    {
-      goto again; 
-    }
-  else if (http_status == HTTP_UNAUTHORIZED)
-    {
-      int auth_result = -1;
-      httpFlush (request->http);
-
-      if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
-        {
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-          request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
-          request->state = GTK_CUPS_POST_AUTH;
-          request->need_password = TRUE;
-
-          return;
-        }
-
-      /* Negotiate */
-      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
-        {
-          auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
-        }
-      /* Basic, BasicDigest, Digest and PeerCred */
-      else
-        {
-          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
-            {
-              cups_username = request->username;
-              cupsSetPasswordCB (passwordCB);
-
-              /* This call success for PeerCred authentication */
-              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
-
-              if (auth_result != 0)
-                {
-                  /* move to AUTH state to let the backend 
-                   * ask for a password
-                   */ 
-                  request->poll_state = GTK_CUPS_HTTP_IDLE;
-                  request->state = GTK_CUPS_POST_AUTH;
-                  request->need_password = TRUE;
-
-                  return;
-                }
-            }
-          else
-            {
-              cups_password = request->password;
-              cups_username = request->username;
-
-              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
-
-              if (cups_password != NULL)
-                return;
-
-              if (request->password != NULL)
-                {
-                  memset (request->password, 0, strlen (request->password));
-                  g_free (request->password);
-                  request->password = NULL;
-                }
-
-              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
-            }
-        }
-
-      if (auth_result ||
-          httpReconnect (request->http))
-        {
-          /* if the password has been used, reset password_state
-           * so that we ask for a new one next time around
-           */ 
-          if (cups_password == NULL)
-            request->password_state = GTK_CUPS_PASSWORD_NONE;
-
-          request->state = GTK_CUPS_POST_DONE;
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-          gtk_cups_result_set_error (request->result, 
-                                     GTK_CUPS_ERROR_AUTH,
-                                     0,
-                                     0,
-                                     "Not authorized");
-          return;
-        }
-      
-      if (request->data_io != NULL)
-        g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
-
-      request->state = GTK_CUPS_POST_CONNECT;
-      request->poll_state = GTK_CUPS_HTTP_WRITE;
-    }
-  else if (http_status == HTTP_ERROR)
-    {
-      int error = httpError (request->http);
-#ifdef G_OS_WIN32
-      if (error != WSAENETDOWN && error != WSAENETUNREACH)
-#else
-      if (error != ENETDOWN && error != ENETUNREACH)     
-#endif /* G_OS_WIN32 */
-        {
-          request->attempts++;
-          goto again;
-        }
-      else
-        {
-          request->state = GTK_CUPS_POST_DONE;
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-     
-          gtk_cups_result_set_error (request->result,
-                                     GTK_CUPS_ERROR_HTTP,
-                                     http_status,
-                                     error, 
-                                     "Unknown HTTP error");
-
-          return;
-        }
-    }
-  else if (http_status == HTTP_UPGRADE_REQUIRED)
-    {
-      /* Flush any error message... */
-      httpFlush (request->http);
-
-      cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
-      request->state = GTK_CUPS_POST_CONNECT;
-
-      /* Reconnect... */
-      httpReconnect (request->http);
-
-      /* Upgrade with encryption... */
-      httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
-      request->attempts++;
-      goto again;
-    }
-  else if (http_status != HTTP_OK)
-    {
-      int http_errno;
-
-      http_errno = httpError (request->http);
-
-      if (http_errno == EPIPE)
-        request->state = GTK_CUPS_POST_CONNECT;
-      else
-        {
-          request->state = GTK_CUPS_POST_DONE;
-          gtk_cups_result_set_error (request->result,
-                                     GTK_CUPS_ERROR_HTTP,
-                                     http_status,
-                                     http_errno, 
-                                     "HTTP Error in POST %s", 
-                                     g_strerror (http_errno));
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-          httpFlush (request->http); 
-          return;
-        }
-
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-      request->last_status = HTTP_CONTINUE;
-
-      httpFlush (request->http);
-      if (request->own_http)
-        httpClose (request->http);
-      request->http = NULL;
-
-      return;
-    }
-  else
-    {
-      request->state = GTK_CUPS_POST_READ_RESPONSE;
-      return;
-    }
-
- again:
-  http_status = HTTP_CONTINUE;
-
-  if (httpCheck (request->http))
-    http_status = httpUpdate (request->http);
-
-  request->last_status = http_status;
-}
-
-static void 
-_post_read_response (GtkCupsRequest *request)
-{
-  ipp_state_t ipp_status;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  request->poll_state = GTK_CUPS_HTTP_READ;
-
-  if (request->result->ipp_response == NULL)
-    request->result->ipp_response = ippNew();
-
-  ipp_status = ippRead (request->http, 
-                        request->result->ipp_response);
-
-  if (ipp_status == IPP_ERROR)
-    {
-      int ipp_error = cupsLastError ();
-      gtk_cups_result_set_error (request->result,  
-                                 GTK_CUPS_ERROR_IPP,
-                                 ipp_status,
-                                 ipp_error,
-                                 "%s",
-                                 ippErrorString (ipp_error));
-      
-      ippDelete (request->result->ipp_response);
-      request->result->ipp_response = NULL;
-
-      request->state = GTK_CUPS_POST_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-    }
-  else if (ipp_status == IPP_DATA)
-    {
-      request->state = GTK_CUPS_POST_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-    }
-}
-
-static void 
-_get_send (GtkCupsRequest *request)
-{
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  request->poll_state = GTK_CUPS_HTTP_WRITE;
-
-  if (request->data_io == NULL)
-    {
-      gtk_cups_result_set_error (request->result,
-                                 GTK_CUPS_ERROR_IO,
-                                 G_IO_STATUS_ERROR,
-                                 G_IO_CHANNEL_ERROR_FAILED, 
-                                 "Get requires an open io channel");
-
-      request->state = GTK_CUPS_GET_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-
-      return;
-    }
-
-  httpClearFields (request->http);
-#ifdef HAVE_HTTPGETAUTHSTRING
-  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
-#else
-#ifdef HAVE_HTTP_AUTHSTRING
-  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
-#endif
-#endif
-
-  if (httpGet (request->http, request->resource))
-    {
-      if (httpReconnect (request->http))
-        {
-          request->state = GTK_CUPS_GET_DONE;
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-        
-          /* TODO: should add a status or error code for failed GET */ 
-          gtk_cups_result_set_error (request->result, 
-                                     GTK_CUPS_ERROR_GENERAL,
-                                     0,
-                                     0,
-                                     "Failed Get");
-        }
-
-      request->attempts++;
-      return;    
-    }
-
-  if (httpCheck (request->http))
-    request->last_status = httpUpdate (request->http);
-        
-  request->attempts = 0;
-
-  request->state = GTK_CUPS_GET_CHECK;
-  request->poll_state = GTK_CUPS_HTTP_READ;
-  
-  ippSetState (request->ipp_request, IPP_IDLE);
-}
-
-static void 
-_get_check (GtkCupsRequest *request)
-{
-  http_status_t http_status;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  http_status = request->last_status;
-
-  request->poll_state = GTK_CUPS_HTTP_READ;
-
-  if (http_status == HTTP_CONTINUE)
-    {
-      goto again; 
-    }
-  else if (http_status == HTTP_UNAUTHORIZED)
-    {
-      int auth_result = -1;
-      httpFlush (request->http);
-
-      if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
-        {
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-          request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
-          request->state = GTK_CUPS_GET_AUTH;
-          request->need_password = TRUE;
-
-          return;
-        }
-
-      /* Negotiate */
-      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
-        {
-          auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
-        }
-      /* Basic, BasicDigest, Digest and PeerCred */
-      else
-        {
-          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
-            {
-              cups_username = request->username;
-              cupsSetPasswordCB (passwordCB);
-
-              /* This call success for PeerCred authentication */
-              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
-
-              if (auth_result != 0)
-                {
-                  /* move to AUTH state to let the backend
-                   * ask for a password
-                   */
-                  request->poll_state = GTK_CUPS_HTTP_IDLE;
-                  request->state = GTK_CUPS_GET_AUTH;
-                  request->need_password = TRUE;
-
-                  return;
-                }
-            }
-          else
-            {
-              cups_password = request->password;
-              cups_username = request->username;
-
-              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
-
-              if (cups_password != NULL)
-                return;
-
-              if (request->password != NULL)
-                {
-                  memset (request->password, 0, strlen (request->password));
-                  g_free (request->password);
-                  request->password = NULL;
-                }
-
-              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
-            }
-        }
-
-      if (auth_result ||
-          httpReconnect (request->http))
-        {
-          /* if the password has been used, reset password_state
-           * so that we ask for a new one next time around
-           */
-          if (cups_password == NULL)
-            request->password_state = GTK_CUPS_PASSWORD_NONE;
-
-          request->state = GTK_CUPS_GET_DONE;
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-          gtk_cups_result_set_error (request->result, 
-                                     GTK_CUPS_ERROR_AUTH,
-                                     0,
-                                     0,
-                                     "Not authorized");
-          return;
-        }
-
-      request->state = GTK_CUPS_GET_CONNECT;
-      request->last_status = HTTP_CONTINUE;
-
-     return;
-    }
-  else if (http_status == HTTP_UPGRADE_REQUIRED)
-    {
-      /* Flush any error message... */
-      httpFlush (request->http);
-
-      cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
-      request->state = GTK_CUPS_GET_CONNECT;
-
-      /* Reconnect... */
-      httpReconnect (request->http);
-
-      /* Upgrade with encryption... */
-      httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
-      request->attempts++;
-      goto again;
-    }
-  else if (http_status != HTTP_OK)
-    {
-      int http_errno;
-
-      http_errno = httpError (request->http);
-
-      if (http_errno == EPIPE)
-        request->state = GTK_CUPS_GET_CONNECT;
-      else
-        {
-          request->state = GTK_CUPS_GET_DONE;
-          gtk_cups_result_set_error (request->result,
-                                     GTK_CUPS_ERROR_HTTP,
-                                     http_status,
-                                     http_errno, 
-                                     "HTTP Error in GET %s", 
-                                     g_strerror (http_errno));
-          request->poll_state = GTK_CUPS_HTTP_IDLE;
-          httpFlush (request->http);
-
-          return;
-        }
-
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-      request->last_status = HTTP_CONTINUE;
-
-      httpFlush (request->http);
-      if (request->own_http)
-        httpClose (request->http);
-      request->http = NULL;
-
-      return;
-    }
-  else
-    {
-      request->state = GTK_CUPS_GET_READ_DATA;
-      return;
-    }
-
- again:
-  http_status = HTTP_CONTINUE;
-
-  if (httpCheck (request->http))
-    http_status = httpUpdate (request->http);
-
-  request->last_status = http_status;
-
-}
-
-static void 
-_get_read_data (GtkCupsRequest *request)
-{
-  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
-  gsize bytes;
-  gsize bytes_written;
-  GIOStatus io_status;
-  GError *error;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  error = NULL;
-
-  request->poll_state = GTK_CUPS_HTTP_READ;
-
-  bytes = httpRead2 (request->http, buffer, sizeof (buffer));
-  request->bytes_received += bytes;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
-
-  io_status =
-    g_io_channel_write_chars (request->data_io, 
-                              buffer, 
-                             bytes, 
-                             &bytes_written,
-                             &error);
-
-  if (io_status == G_IO_STATUS_ERROR)
-    {
-      request->state = GTK_CUPS_GET_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-    
-      gtk_cups_result_set_error (request->result,
-                                 GTK_CUPS_ERROR_IO,
-                                 io_status,
-                                 error->code, 
-                                 error->message);
-      g_error_free (error);
-    }
-
-  /* Stop if we do not expect any more data or EOF was received. */
-  if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
-    {
-      request->state = GTK_CUPS_GET_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-
-      return;
-    }
-}
-
-gboolean
-gtk_cups_request_is_done (GtkCupsRequest *request)
-{
-  return (request->state == GTK_CUPS_REQUEST_DONE);
-}
-
-gboolean
-gtk_cups_result_is_error (GtkCupsResult *result)
-{
-  return result->is_error;
-}
-
-ipp_t *
-gtk_cups_result_get_response (GtkCupsResult *result)
-{
-  return result->ipp_response;
-}
-
-GtkCupsErrorType
-gtk_cups_result_get_error_type (GtkCupsResult *result)
-{
-  return result->error_type;
-}
-
-int
-gtk_cups_result_get_error_status (GtkCupsResult *result)
-{
-  return result->error_status;
-}
-
-int
-gtk_cups_result_get_error_code (GtkCupsResult *result)
-{
-  return result->error_code;
-}
-
-const char *
-gtk_cups_result_get_error_string (GtkCupsResult *result)
-{
-  return result->error_msg; 
-}
-
-/* This function allocates new instance of GtkCupsConnectionTest() and creates
- * a socket for communication with a CUPS server 'server'.
- */
-GtkCupsConnectionTest *
-gtk_cups_connection_test_new (const char *server,
-                              const int   port)
-{
-  GtkCupsConnectionTest *result = NULL;
-  gchar                 *port_str = NULL;
-
-  result = g_new (GtkCupsConnectionTest, 1);
-
-  if (port >= 0)
-    port_str = g_strdup_printf ("%d", port);
-  else
-    port_str = g_strdup_printf ("%d", ippPort ());
-
-  if (server != NULL)
-    result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
-  else
-    result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
-
-  g_free (port_str);
-
-  result->socket = -1;
-  result->current_addr = NULL;
-  result->last_wrong_addr = NULL;
-  result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
-
-  result->at_init = gtk_cups_connection_test_get_state (result);
-
-  return result;
-}
-
-
-/* A non-blocking test whether it is possible to connect to a CUPS server specified
- * inside of GtkCupsConnectionTest structure.
- *  - you need to check it more then once.
- * The connection is closed after a successful connection.
- */
-GtkCupsConnectionState 
-gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
-{
-  GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
-  http_addrlist_t       *iter;
-  gint                   error_code;
-  gint                   flags;
-  gint                   code;
-
-  if (test == NULL)
-    return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
-
-  if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
-    {
-      test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
-      return GTK_CUPS_CONNECTION_AVAILABLE;
-    }
-  else
-    {
-      if (test->socket == -1)
-        {
-          if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
-            iter = test->last_wrong_addr->next;
-          else
-            {
-              test->last_wrong_addr = NULL;
-              iter = test->addrlist;
-            }
-
-          while (iter)
-            {
-              test->socket = socket (iter->addr.addr.sa_family,
-                                     SOCK_STREAM,
-                                     0);
-
-              if (test->socket >= 0)
-                {
-                  flags = fcntl (test->socket, F_GETFL);
-
-                  if (flags != -1)
-                    flags |= O_NONBLOCK;
-
-                  fcntl (test->socket, F_SETFL, flags);
-              
-                  test->current_addr = iter;
-              
-                  break;
-                }
-               iter = iter->next;
-            }
-        }
-
-      if (test->socket >= 0)
-        {
-          code = connect (test->socket,
-                          &test->current_addr->addr.addr,
-                          httpAddrLength (&test->current_addr->addr));
-
-          error_code = errno;
-
-          if (code == 0 || error_code == EISCONN)
-            {
-              close (test->socket);
-              test->socket = -1;
-              test->current_addr = NULL;
-              result = GTK_CUPS_CONNECTION_AVAILABLE;
-            }
-          else
-            {
-              if (error_code == EALREADY || error_code == EINPROGRESS)
-                result = GTK_CUPS_CONNECTION_IN_PROGRESS;
-              else
-                {
-                  close (test->socket);
-                  test->socket = -1;
-                  test->last_wrong_addr = test->current_addr;
-                  result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
-                }
-            }
-         }
-
-      return result;
-    }
-}
-
-/* This function frees memory used by the GtkCupsConnectionTest structure.
- */
-void 
-gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
-{
-  if (test == NULL)
-    return;
-
-  test->current_addr = NULL;
-  test->last_wrong_addr = NULL;
-  httpAddrFreeList (test->addrlist);
-  if (test->socket != -1)
-    {
-      close (test->socket);
-      test->socket = -1;
-    }
-  g_free (test);
-}
diff --git a/modules/printbackends/cups/gtkcupsutils.h b/modules/printbackends/cups/gtkcupsutils.h
deleted file mode 100644 (file)
index 500bb4f..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/* gtkcupsutils.h 
- * Copyright (C) 2006 John (J5) Palmieri <johnp@redhat.com>
- *
- * 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_CUPS_UTILS_H__
-#define __GTK_CUPS_UTILS_H__
-
-#include <glib.h>
-#include <cups/cups.h>
-#include <cups/language.h>
-#include <cups/http.h>
-#include <cups/ipp.h>
-
-G_BEGIN_DECLS
-
-typedef struct _GtkCupsRequest        GtkCupsRequest;
-typedef struct _GtkCupsResult         GtkCupsResult;
-typedef struct _GtkCupsConnectionTest GtkCupsConnectionTest;
-
-typedef enum
-{
-  GTK_CUPS_ERROR_HTTP,
-  GTK_CUPS_ERROR_IPP,
-  GTK_CUPS_ERROR_IO,
-  GTK_CUPS_ERROR_AUTH,
-  GTK_CUPS_ERROR_GENERAL
-} GtkCupsErrorType;
-
-typedef enum
-{
-  GTK_CUPS_POST,
-  GTK_CUPS_GET
-} GtkCupsRequestType;
-
-
-/** 
- * Direction we should be polling the http socket on.
- * We are either reading or writting at each state.
- * This makes it easy for mainloops to connect to poll.
- */
-typedef enum
-{
-  GTK_CUPS_HTTP_IDLE,
-  GTK_CUPS_HTTP_READ,
-  GTK_CUPS_HTTP_WRITE
-} GtkCupsPollState;
-
-typedef enum
-{
-  GTK_CUPS_CONNECTION_AVAILABLE,
-  GTK_CUPS_CONNECTION_NOT_AVAILABLE,
-  GTK_CUPS_CONNECTION_IN_PROGRESS  
-} GtkCupsConnectionState;
-
-typedef enum
-{
-  GTK_CUPS_PASSWORD_NONE,
-  GTK_CUPS_PASSWORD_REQUESTED,
-  GTK_CUPS_PASSWORD_HAS,
-  GTK_CUPS_PASSWORD_APPLIED,
-  GTK_CUPS_PASSWORD_NOT_VALID
-} GtkCupsPasswordState;
-
-struct _GtkCupsRequest 
-{
-  GtkCupsRequestType type;
-
-  http_t *http;
-  http_status_t last_status;
-  ipp_t *ipp_request;
-
-  gchar *server;
-  gchar *resource;
-  GIOChannel *data_io;
-  gint attempts;
-
-  GtkCupsResult *result;
-
-  gint state;
-  GtkCupsPollState poll_state;
-  guint64 bytes_received;
-
-  gchar *password;
-  gchar *username;
-
-  gint own_http : 1;
-  gint need_password : 1;
-  gint need_auth_info : 1;
-  gchar **auth_info_required;
-  gchar **auth_info;
-  GtkCupsPasswordState password_state;
-};
-
-struct _GtkCupsConnectionTest
-{
-  GtkCupsConnectionState at_init;
-  http_addrlist_t       *addrlist;
-  http_addrlist_t       *current_addr;
-  http_addrlist_t       *last_wrong_addr;
-  gint                   socket;
-};
-
-#define GTK_CUPS_REQUEST_START 0
-#define GTK_CUPS_REQUEST_DONE 500
-
-/* POST states */
-enum 
-{
-  GTK_CUPS_POST_CONNECT = GTK_CUPS_REQUEST_START,
-  GTK_CUPS_POST_SEND,
-  GTK_CUPS_POST_WRITE_REQUEST,
-  GTK_CUPS_POST_WRITE_DATA,
-  GTK_CUPS_POST_CHECK,
-  GTK_CUPS_POST_AUTH,
-  GTK_CUPS_POST_READ_RESPONSE,
-  GTK_CUPS_POST_DONE = GTK_CUPS_REQUEST_DONE
-};
-
-/* GET states */
-enum
-{
-  GTK_CUPS_GET_CONNECT = GTK_CUPS_REQUEST_START,
-  GTK_CUPS_GET_SEND,
-  GTK_CUPS_GET_CHECK,
-  GTK_CUPS_GET_AUTH,
-  GTK_CUPS_GET_READ_DATA,
-  GTK_CUPS_GET_DONE = GTK_CUPS_REQUEST_DONE
-};
-
-GtkCupsRequest        * gtk_cups_request_new_with_username (http_t             *connection,
-                                                           GtkCupsRequestType  req_type,
-                                                           gint                operation_id,
-                                                           GIOChannel         *data_io,
-                                                           const char         *server,
-                                                           const char         *resource,
-                                                           const char         *username);
-GtkCupsRequest        * gtk_cups_request_new               (http_t             *connection,
-                                                           GtkCupsRequestType  req_type,
-                                                           gint                operation_id,
-                                                           GIOChannel         *data_io,
-                                                           const char         *server,
-                                                           const char         *resource);
-void                    gtk_cups_request_ipp_add_string    (GtkCupsRequest     *request,
-                                                           ipp_tag_t           group,
-                                                           ipp_tag_t           tag,
-                                                           const char         *name,
-                                                           const char         *charset,
-                                                           const char         *value);
-void                    gtk_cups_request_ipp_add_strings   (GtkCupsRequest     *request,
-                                                           ipp_tag_t           group,
-                                                           ipp_tag_t           tag,
-                                                           const char         *name,
-                                                           int                 num_values,
-                                                           const char         *charset,
-                                                           const char * const *values);
-const char            * gtk_cups_request_ipp_get_string    (GtkCupsRequest     *request,
-                                                           ipp_tag_t           tag,
-                                                           const char         *name);
-gboolean                gtk_cups_request_read_write        (GtkCupsRequest     *request,
-                                                            gboolean            connect_only);
-GtkCupsPollState        gtk_cups_request_get_poll_state    (GtkCupsRequest     *request);
-void                    gtk_cups_request_free              (GtkCupsRequest     *request);
-GtkCupsResult         * gtk_cups_request_get_result        (GtkCupsRequest     *request);
-gboolean                gtk_cups_request_is_done           (GtkCupsRequest     *request);
-void                    gtk_cups_request_encode_option     (GtkCupsRequest     *request,
-                                                           const gchar        *option,
-                                                           const gchar        *value);
-void                    gtk_cups_request_set_ipp_version   (GtkCupsRequest     *request,
-                                                           gint                major,
-                                                           gint                minor);
-gboolean                gtk_cups_result_is_error           (GtkCupsResult      *result);
-ipp_t                 * gtk_cups_result_get_response       (GtkCupsResult      *result);
-GtkCupsErrorType        gtk_cups_result_get_error_type     (GtkCupsResult      *result);
-int                     gtk_cups_result_get_error_status   (GtkCupsResult      *result);
-int                     gtk_cups_result_get_error_code     (GtkCupsResult      *result);
-const char            * gtk_cups_result_get_error_string   (GtkCupsResult      *result);
-GtkCupsConnectionTest * gtk_cups_connection_test_new       (const char         *server,
-                                                            const int           port);
-GtkCupsConnectionState  gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test);
-void                    gtk_cups_connection_test_free      (GtkCupsConnectionTest *test);
-
-G_END_DECLS
-#endif 
diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c
deleted file mode 100644 (file)
index 09e18b1..0000000
+++ /dev/null
@@ -1,6745 +0,0 @@
-/* GTK - The GIMP Toolkit
- * gtkprintbackendcups.h: Default implementation of GtkPrintBackend
- * for the Common Unix Print System (CUPS)
- * Copyright (C) 2006, 2007 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 <ctype.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <time.h>
-/* Cups 1.6 deprecates ppdFindAttr(), ppdFindCustomOption(),
- * ppdFirstCustomParam(), and ppdNextCustomParam() among others. This
- * turns off the warning so that it will compile.
- */
-#ifdef HAVE_CUPS_API_1_6
-# define _PPD_DEPRECATED
-#endif
-
-#include <cups/cups.h>
-#include <cups/language.h>
-#include <cups/http.h>
-#include <cups/ipp.h>
-#include <errno.h>
-#include <cairo.h>
-#include <cairo-pdf.h>
-#include <cairo-ps.h>
-
-#include <glib/gstdio.h>
-#include <glib/gi18n-lib.h>
-#include <gmodule.h>
-
-#include <gtk/gtk.h>
-#include <gtk/gtkprintbackend.h>
-#include <gtk/gtkunixprint.h>
-#include <gtk/gtkprinter-private.h>
-
-#include "gtkprintbackendcups.h"
-#include "gtkprintercups.h"
-
-#include "gtkcupsutils.h"
-#include "gtkcupssecretsutils.h"
-
-#include <gtkprintutils.h>
-
-#ifdef HAVE_COLORD
-#include <colord.h>
-#endif
-
-typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
-
-#define GTK_PRINT_BACKEND_CUPS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
-#define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
-#define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
-
-#define _CUPS_MAX_ATTEMPTS 10
-#define _CUPS_MAX_CHUNK_SIZE 8192
-
-#ifdef HAVE_CUPS_API_1_6
-#define AVAHI_IF_UNSPEC -1
-#define AVAHI_PROTO_INET 0
-#define AVAHI_PROTO_INET6 1
-#define AVAHI_PROTO_UNSPEC -1
-
-#define AVAHI_BUS "org.freedesktop.Avahi"
-#define AVAHI_SERVER_IFACE "org.freedesktop.Avahi.Server"
-#define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser"
-#define AVAHI_SERVICE_RESOLVER_IFACE "org.freedesktop.Avahi.ServiceResolver"
-#endif
-
-/* define this to see warnings about ignored ppd options */
-#undef PRINT_IGNORED_OPTIONS
-
-#define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
-#define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].string.text;}
-
-typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
-                                                   GtkCupsResult   *result,
-                                                   gpointer         user_data);
-
-typedef enum
-{
-  DISPATCH_SETUP,
-  DISPATCH_REQUEST,
-  DISPATCH_SEND,
-  DISPATCH_CHECK,
-  DISPATCH_READ,
-  DISPATCH_ERROR
-} GtkPrintCupsDispatchState;
-
-typedef struct
-{
-  GSource source;
-
-  http_t *http;
-  GtkCupsRequest *request;
-  GtkCupsPollState poll_state;
-  GPollFD *data_poll;
-  GtkPrintBackendCups *backend;
-  GtkPrintCupsResponseCallbackFunc callback;
-  gpointer                         callback_data;
-
-} GtkPrintCupsDispatchWatch;
-
-struct _GtkPrintBackendCupsClass
-{
-  GtkPrintBackendClass parent_class;
-};
-
-struct _GtkPrintBackendCups
-{
-  GtkPrintBackend parent_instance;
-
-  char *default_printer;
-
-  guint list_printers_poll;
-  guint list_printers_pending : 1;
-  gint  list_printers_attempts;
-  guint got_default_printer   : 1;
-  guint default_printer_poll;
-  GtkCupsConnectionTest *cups_connection_test;
-  gint  reading_ppds;
-
-  GList      *requests;
-  GHashTable *auth;
-  gchar      *username;
-  gboolean    authentication_lock;
-#ifdef HAVE_COLORD
-  CdClient   *colord_client;
-#endif
-#ifdef HAVE_CUPS_API_1_6
-  GDBusConnection *dbus_connection;
-  gchar           *avahi_default_printer;
-  guint            avahi_service_browser_subscription_id;
-  guint            avahi_service_browser_subscription_ids[2];
-  gchar           *avahi_service_browser_paths[2];
-  GCancellable    *avahi_cancellable;
-#endif
-  gboolean      secrets_service_available;
-  guint         secrets_service_watch_id;
-  GCancellable *secrets_service_cancellable;
-};
-
-static GObjectClass *backend_parent_class;
-
-static void                 gtk_print_backend_cups_class_init      (GtkPrintBackendCupsClass          *class);
-static void                 gtk_print_backend_cups_init            (GtkPrintBackendCups               *impl);
-static void                 gtk_print_backend_cups_finalize        (GObject                           *object);
-static void                 gtk_print_backend_cups_dispose         (GObject                           *object);
-static void                 cups_get_printer_list                  (GtkPrintBackend                   *print_backend);
-static void                 cups_get_default_printer               (GtkPrintBackendCups               *print_backend);
-static void                 cups_get_local_default_printer         (GtkPrintBackendCups               *print_backend);
-static void                 cups_request_execute                   (GtkPrintBackendCups               *print_backend,
-                                                                   GtkCupsRequest                    *request,
-                                                                   GtkPrintCupsResponseCallbackFunc   callback,
-                                                                   gpointer                           user_data,
-                                                                   GDestroyNotify                     notify);
-static void                 cups_printer_get_settings_from_options (GtkPrinter                        *printer,
-                                                                   GtkPrinterOptionSet               *options,
-                                                                   GtkPrintSettings                  *settings);
-static gboolean             cups_printer_mark_conflicts            (GtkPrinter                        *printer,
-                                                                   GtkPrinterOptionSet               *options);
-static GtkPrinterOptionSet *cups_printer_get_options               (GtkPrinter                        *printer,
-                                                                   GtkPrintSettings                  *settings,
-                                                                   GtkPageSetup                      *page_setup,
-                                                                    GtkPrintCapabilities               capabilities);
-static void                 cups_printer_prepare_for_print         (GtkPrinter                        *printer,
-                                                                   GtkPrintJob                       *print_job,
-                                                                   GtkPrintSettings                  *settings,
-                                                                   GtkPageSetup                      *page_setup);
-static GList *              cups_printer_list_papers               (GtkPrinter                        *printer);
-static GtkPageSetup *       cups_printer_get_default_page_size     (GtkPrinter                        *printer);
-static void                 cups_printer_request_details           (GtkPrinter                        *printer);
-static gboolean             cups_request_default_printer           (GtkPrintBackendCups               *print_backend);
-static gboolean             cups_request_ppd                       (GtkPrinter                        *printer);
-static gboolean             cups_printer_get_hard_margins          (GtkPrinter                        *printer,
-                                                                   gdouble                           *top,
-                                                                   gdouble                           *bottom,
-                                                                   gdouble                           *left,
-                                                                   gdouble                           *right);
-static GtkPrintCapabilities cups_printer_get_capabilities          (GtkPrinter                        *printer);
-static void                 set_option_from_settings               (GtkPrinterOption                  *option,
-                                                                   GtkPrintSettings                  *setting);
-static void                 cups_begin_polling_info                (GtkPrintBackendCups               *print_backend,
-                                                                   GtkPrintJob                       *job,
-                                                                   int                                job_id);
-static gboolean             cups_job_info_poll_timeout             (gpointer                           user_data);
-static void                 gtk_print_backend_cups_print_stream    (GtkPrintBackend                   *backend,
-                                                                   GtkPrintJob                       *job,
-                                                                   GIOChannel                        *data_io,
-                                                                   GtkPrintJobCompleteFunc            callback,
-                                                                   gpointer                           user_data,
-                                                                   GDestroyNotify                     dnotify);
-static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter                        *printer,
-                                                                   GtkPrintSettings                  *settings,
-                                                                   gdouble                            width,
-                                                                   gdouble                            height,
-                                                                   GIOChannel                        *cache_io);
-
-static void                 gtk_print_backend_cups_set_password    (GtkPrintBackend                   *backend,
-                                                                    gchar                            **auth_info_required,
-                                                                    gchar                            **auth_info,
-                                                                    gboolean                           store_auth_info);
-
-void                        overwrite_and_free                      (gpointer                          data);
-static gboolean             is_address_local                        (const gchar                      *address);
-static gboolean             request_auth_info                       (gpointer                          data);
-static void                 lookup_auth_info                        (gpointer                          data);
-
-#ifdef HAVE_CUPS_API_1_6
-static void                 avahi_request_printer_list              (GtkPrintBackendCups              *cups_backend);
-#endif
-
-static void                 secrets_service_appeared_cb             (GDBusConnection *connection,
-                                                                     const gchar *name,
-                                                                     const gchar *name_owner,
-                                                                     gpointer user_data);
-static void                 secrets_service_vanished_cb             (GDBusConnection *connection,
-                                                                     const gchar *name,
-                                                                     gpointer user_data);
-
-G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendCups, gtk_print_backend_cups, GTK_TYPE_PRINT_BACKEND)
-
-void
-g_io_module_load (GIOModule *module)
-{
-  g_type_module_use (G_TYPE_MODULE (module));
-
-  gtk_print_backend_cups_register_type (G_TYPE_MODULE (module));
-  gtk_printer_cups_register_type (G_TYPE_MODULE (module));
-
-  g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
-                                  GTK_TYPE_PRINT_BACKEND_CUPS,
-                                  "cups",
-                                  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);
-}
-
-/* CUPS 1.6 Getter/Setter Functions CUPS 1.6 makes private most of the
- * IPP structures and enforces access via new getter functions, which
- * are unfortunately not available in earlier versions. We define
- * below those getter functions as macros for use when building
- * against earlier CUPS versions.
- */
-#ifndef HAVE_CUPS_API_1_6
-#define ippGetOperation(ipp_request) ipp_request->request.op.operation_id
-#define ippGet:Integer(attr, index) attr->values[index].integer
-#define ippGetBoolean(attr, index) attr->values[index].boolean
-#define ippGetString(attr, index, foo) attr->values[index].string.text
-#define ippGetValueTag(attr) attr->value_tag
-#define ippGetName(attr) attr->name
-#define ippGetCount(attr) attr->num_values
-#define ippGetGroupTag(attr) attr->group_tag
-#define ippGetCollection(attr, index) attr->values[index].collection
-
-static int
-ippGetRange (ipp_attribute_t *attr,
-             int element,
-             int *upper)
-{
-  *upper = attr->values[element].range.upper;
-  return (attr->values[element].range.lower);
-}
-
-static ipp_attribute_t *
-ippFirstAttribute (ipp_t *ipp)
-{
-  if (!ipp)
-    return (NULL);
-
-  return (ipp->current = ipp->attrs);
-}
-
-static ipp_attribute_t *
-ippNextAttribute (ipp_t *ipp)
-{
-  if (!ipp || !ipp->current)
-    return (NULL);
-
-  return (ipp->current = ipp->current->next);
-}
-#endif
-
-/*
- * GtkPrintBackendCups
- */
-
-/**
- * gtk_print_backend_cups_new:
- *
- * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
- * implements the #GtkPrintBackend interface with direct access to
- * the filesystem using Unix/Linux API calls
- *
- * Returns: the new #GtkPrintBackendCups object
- */
-GtkPrintBackend *
-gtk_print_backend_cups_new (void)
-{
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
-
-  return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
-}
-
-static void
-gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
-{
-  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
-
-  backend_parent_class = g_type_class_peek_parent (class);
-
-  gobject_class->finalize = gtk_print_backend_cups_finalize;
-  gobject_class->dispose = gtk_print_backend_cups_dispose;
-
-  backend_class->request_printer_list = cups_get_printer_list;
-  backend_class->print_stream = gtk_print_backend_cups_print_stream;
-  backend_class->printer_request_details = cups_printer_request_details;
-  backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
-  backend_class->printer_get_options = cups_printer_get_options;
-  backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
-  backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
-  backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
-  backend_class->printer_list_papers = cups_printer_list_papers;
-  backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
-  backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
-  backend_class->printer_get_capabilities = cups_printer_get_capabilities;
-  backend_class->set_password = gtk_print_backend_cups_set_password;
-}
-
-static void
-gtk_print_backend_cups_class_finalize (GtkPrintBackendCupsClass *class)
-{
-}
-
-static gboolean
-option_is_ipp_option (GtkPrinterOption *option)
-{
-  gpointer data = g_object_get_data (G_OBJECT (option), "is-ipp-option");
-
-  if (data != NULL)
-    return GPOINTER_TO_UINT (data) != 0;
-  else
-    return FALSE;
-}
-
-static void
-option_set_is_ipp_option (GtkPrinterOption *option,
-                          gboolean          is_ipp_option)
-{
-  g_object_set_data (G_OBJECT (option),
-                     "is-ipp-option",
-                     GUINT_TO_POINTER (is_ipp_option ? 1 : 0));
-}
-
-static cairo_status_t
-_cairo_write_to_cups (void                *closure,
-                      const unsigned char *data,
-                      unsigned int         length)
-{
-  GIOChannel *io = (GIOChannel *)closure;
-  gsize written;
-  GError *error;
-
-  error = NULL;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length));
-
-  while (length > 0)
-    {
-      g_io_channel_write_chars (io, (gchar *)data, length, &written, &error);
-
-      if (error != NULL)
-       {
-         GTK_NOTE (PRINTING,
-                    g_print ("CUPS Backend: Error writing to temp file, %s\n",
-                             error->message));
-
-          g_error_free (error);
-         return CAIRO_STATUS_WRITE_ERROR;
-       }
-
-      GTK_NOTE (PRINTING,
-                g_print ("CUPS Backend: Wrote %"G_GSIZE_FORMAT" bytes to temp file\n", written));
-
-      data += written;
-      length -= written;
-    }
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_surface_t *
-cups_printer_create_cairo_surface (GtkPrinter       *printer,
-                                  GtkPrintSettings *settings,
-                                  gdouble           width,
-                                  gdouble           height,
-                                  GIOChannel       *cache_io)
-{
-  cairo_surface_t *surface;
-  ppd_file_t      *ppd_file = NULL;
-  ppd_attr_t      *ppd_attr = NULL;
-  ppd_attr_t      *ppd_attr_res = NULL;
-  ppd_attr_t      *ppd_attr_screen_freq = NULL;
-  ppd_attr_t      *ppd_attr_res_screen_freq = NULL;
-  gchar           *res_string = NULL;
-  gint             level = 2;
-
-  if (gtk_printer_accepts_pdf (printer))
-    surface = cairo_pdf_surface_create_for_stream (_cairo_write_to_cups, cache_io, width, height);
-  else
-    surface = cairo_ps_surface_create_for_stream  (_cairo_write_to_cups, cache_io, width, height);
-
-  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
-
-  if (ppd_file != NULL)
-    {
-      ppd_attr = ppdFindAttr (ppd_file, "LanguageLevel", NULL);
-
-      if (ppd_attr != NULL)
-        level = atoi (ppd_attr->value);
-
-      if (gtk_print_settings_get_resolution (settings) == 0)
-        {
-          ppd_attr_res = ppdFindAttr (ppd_file, "DefaultResolution", NULL);
-
-          if (ppd_attr_res != NULL)
-            {
-              int res, res_x, res_y;
-
-              if (sscanf (ppd_attr_res->value, "%dx%ddpi", &res_x, &res_y) == 2)
-                {
-                  if (res_x > 0 && res_y > 0)
-                    gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
-                }
-              else if (sscanf (ppd_attr_res->value, "%ddpi", &res) == 1)
-                {
-                  if (res > 0)
-                    gtk_print_settings_set_resolution (settings, res);
-                }
-            }
-        }
-
-      res_string = g_strdup_printf ("%ddpi",
-                                    gtk_print_settings_get_resolution (settings));
-      ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
-      g_free (res_string);
-
-      if (ppd_attr_res_screen_freq == NULL)
-        {
-          res_string = g_strdup_printf ("%dx%ddpi",
-                                        gtk_print_settings_get_resolution_x (settings),
-                                        gtk_print_settings_get_resolution_y (settings));
-          ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
-          g_free (res_string);
-        }
-
-      ppd_attr_screen_freq = ppdFindAttr (ppd_file, "ScreenFreq", NULL);
-
-      if (ppd_attr_res_screen_freq != NULL && atof (ppd_attr_res_screen_freq->value) > 0.0)
-        gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_res_screen_freq->value));
-      else if (ppd_attr_screen_freq != NULL && atof (ppd_attr_screen_freq->value) > 0.0)
-        gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_screen_freq->value));
-    }
-
-  if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_PS)
-    {
-      if (level == 2)
-        cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
-
-      if (level == 3)
-        cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_3);
-    }
-
-  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 {
-  GtkPrintJobCompleteFunc callback;
-  GtkPrintJob *job;
-  gpointer user_data;
-  GDestroyNotify dnotify;
-  http_t *http;
-} CupsPrintStreamData;
-
-static void
-cups_free_print_stream_data (CupsPrintStreamData *data)
-{
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  if (data->dnotify)
-    data->dnotify (data->user_data);
-  g_object_unref (data->job);
-  if (data->http != NULL)
-    httpClose (data->http);
-  g_free (data);
-}
-
-static void
-cups_print_cb (GtkPrintBackendCups *print_backend,
-               GtkCupsResult       *result,
-               gpointer             user_data)
-{
-  GError *error = NULL;
-  CupsPrintStreamData *ps = user_data;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  if (gtk_cups_result_is_error (result))
-    error = g_error_new_literal (gtk_print_error_quark (),
-                                 GTK_PRINT_ERROR_INTERNAL_ERROR,
-                                 gtk_cups_result_get_error_string (result));
-
-  if (ps->callback)
-    ps->callback (ps->job, ps->user_data, error);
-
-  if (error == NULL)
-    {
-      int job_id = 0;
-      ipp_attribute_t *attr;           /* IPP job-id attribute */
-      ipp_t *response = gtk_cups_result_get_response (result);
-
-      if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
-       job_id = ippGetInteger (attr, 0);
-
-      if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
-       gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
-      else
-       {
-         gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
-         cups_begin_polling_info (print_backend, ps->job, job_id);
-       }
-    }
-  else
-    gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
-
-
-  if (error)
-    g_error_free (error);
-}
-
-typedef struct {
-  GtkCupsRequest *request;
-  GtkPrinterCups *printer;
-} CupsOptionsData;
-
-static void
-add_cups_options (const gchar *key,
-                 const gchar *value,
-                 gpointer     user_data)
-{
-  CupsOptionsData *data = (CupsOptionsData *) user_data;
-  GtkCupsRequest *request = data->request;
-  GtkPrinterCups *printer = data->printer;
-  gboolean custom_value = FALSE;
-  gchar *new_value = NULL;
-  gint i;
-
-  if (!key || !value)
-    return;
-
-  if (!g_str_has_prefix (key, "cups-"))
-    return;
-
-  if (strcmp (value, "gtk-ignore-value") == 0)
-    return;
-
-  key = key + strlen ("cups-");
-
-  if (printer && printer->ppd_file)
-    {
-      ppd_coption_t *coption;
-      gboolean       found = FALSE;
-      gboolean       custom_values_enabled = FALSE;
-
-      coption = ppdFindCustomOption (printer->ppd_file, key);
-      if (coption && coption->option)
-        {
-          for (i = 0; i < coption->option->num_choices; i++)
-            {
-              /* Are custom values enabled ? */
-              if (g_str_equal (coption->option->choices[i].choice, "Custom"))
-                custom_values_enabled = TRUE;
-
-              /* Is the value among available choices ? */
-              if (g_str_equal (coption->option->choices[i].choice, value))
-                found = TRUE;
-            }
-
-          if (custom_values_enabled && !found)
-            custom_value = TRUE;
-        }
-    }
-
-  /* Add "Custom." prefix to custom values if not already added. */
-  if (custom_value && !g_str_has_prefix (value, "Custom."))
-    {
-      new_value = g_strdup_printf ("Custom.%s", value);
-      gtk_cups_request_encode_option (request, key, new_value);
-      g_free (new_value);
-    }
-  else
-    gtk_cups_request_encode_option (request, key, value);
-}
-
-static void
-gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
-                                     GtkPrintJob             *job,
-                                    GIOChannel              *data_io,
-                                    GtkPrintJobCompleteFunc  callback,
-                                    gpointer                 user_data,
-                                    GDestroyNotify           dnotify)
-{
-  GtkPrinterCups *cups_printer;
-  CupsPrintStreamData *ps;
-  CupsOptionsData *options_data;
-  GtkCupsRequest *request = NULL;
-  GtkPrintSettings *settings;
-  const gchar *title;
-  char  printer_absolute_uri[HTTP_MAX_URI];
-  http_t *http = NULL;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
-  settings = gtk_print_job_get_settings (job);
-
-#ifdef HAVE_CUPS_API_1_6
-  if (cups_printer->avahi_browsed)
-    {
-      http = httpConnect (cups_printer->hostname, cups_printer->port);
-      if (http)
-        {
-          request = gtk_cups_request_new_with_username (http,
-                                                        GTK_CUPS_POST,
-                                                        IPP_PRINT_JOB,
-                                                        data_io,
-                                                        cups_printer->hostname,
-                                                        cups_printer->device_uri,
-                                                        GTK_PRINT_BACKEND_CUPS (print_backend)->username);
-          g_snprintf (printer_absolute_uri, HTTP_MAX_URI, "%s", cups_printer->printer_uri);
-        }
-      else
-        {
-          GError *error = NULL;
-
-          GTK_NOTE (PRINTING,
-                    g_warning ("CUPS Backend: Error connecting to %s:%d",
-                               cups_printer->hostname,
-                               cups_printer->port));
-
-          error = g_error_new (gtk_print_error_quark (),
-                               GTK_CUPS_ERROR_GENERAL,
-                               "Error connecting to %s",
-                               cups_printer->hostname);
-
-          gtk_print_job_set_status (job, GTK_PRINT_STATUS_FINISHED_ABORTED);
-
-          if (callback)
-            {
-              callback (job, user_data, error);
-            }
-
-          g_clear_error (&error);
-
-          return;
-        }
-    }
-  else
-#endif
-    {
-      request = gtk_cups_request_new_with_username (NULL,
-                                                    GTK_CUPS_POST,
-                                                    IPP_PRINT_JOB,
-                                                    data_io,
-                                                    NULL,
-                                                    cups_printer->device_uri,
-                                                    GTK_PRINT_BACKEND_CUPS (print_backend)->username);
-
-      httpAssembleURIf (HTTP_URI_CODING_ALL,
-                        printer_absolute_uri,
-                        sizeof (printer_absolute_uri),
-                        "ipp",
-                        NULL,
-                        "localhost",
-                        ippPort (),
-                        "/printers/%s",
-                        gtk_printer_get_name (gtk_print_job_get_printer (job)));
-    }
-
-  gtk_cups_request_set_ipp_version (request,
-                                    cups_printer->ipp_version_major,
-                                    cups_printer->ipp_version_minor);
-
-  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
-                                   IPP_TAG_URI, "printer-uri",
-                                   NULL, printer_absolute_uri);
-
-  title = gtk_print_job_get_title (job);
-  if (title) {
-    char *title_truncated = NULL;
-    size_t title_bytes = strlen (title);
-
-    if (title_bytes >= IPP_MAX_NAME)
-      {
-        gchar *end;
-
-        end = g_utf8_find_prev_char (title, title + IPP_MAX_NAME - 1);
-        title_truncated = g_utf8_substring (title,
-                                            0,
-                                            g_utf8_pointer_to_offset (title, end));
-      }
-
-    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
-                                     IPP_TAG_NAME, "job-name",
-                                     NULL,
-                                     title_truncated ? title_truncated : title);
-    g_free (title_truncated);
-  }
-
-  options_data = g_new0 (CupsOptionsData, 1);
-  options_data->request = request;
-  options_data->printer = cups_printer;
-  gtk_print_settings_foreach (settings, add_cups_options, options_data);
-  g_free (options_data);
-
-  ps = g_new0 (CupsPrintStreamData, 1);
-  ps->callback = callback;
-  ps->user_data = user_data;
-  ps->dnotify = dnotify;
-  ps->job = g_object_ref (job);
-  ps->http = http;
-
-  request->need_auth_info = FALSE;
-  request->auth_info_required = NULL;
-
-  /* Check if auth_info_required is set and if it should be handled.
-   * The cups libraries handle the ticket exchange for "negotiate". */
-  if (cups_printer->auth_info_required != NULL &&
-      g_strv_length (cups_printer->auth_info_required) == 1 &&
-      g_strcmp0 (cups_printer->auth_info_required[0], "negotiate") == 0)
-    {
-      GTK_NOTE (PRINTING,
-                g_print ("CUPS Backend: Ignoring auth-info-required \"%s\"\n",
-                         cups_printer->auth_info_required[0]));
-    }
-  else if (cups_printer->auth_info_required != NULL)
-    {
-      request->need_auth_info = TRUE;
-      request->auth_info_required = g_strdupv (cups_printer->auth_info_required);
-    }
-
-  cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
-                        request,
-                        (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
-                        ps,
-                        (GDestroyNotify)cups_free_print_stream_data);
-}
-
-void overwrite_and_free (gpointer data)
-{
-  gchar *password = (gchar *) data;
-
-  if (password != NULL)
-    {
-      memset (password, 0, strlen (password));
-      g_free (password);
-    }
-}
-
-static void
-gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
-{
-#ifdef HAVE_CUPS_API_1_6
-  gint i;
-#endif
-
-  backend_cups->list_printers_poll = FALSE;
-  backend_cups->got_default_printer = FALSE;
-  backend_cups->list_printers_pending = FALSE;
-  backend_cups->list_printers_attempts = 0;
-  backend_cups->reading_ppds = 0;
-
-  backend_cups->requests = NULL;
-  backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free);
-  backend_cups->authentication_lock = FALSE;
-
-  backend_cups->default_printer_poll = 0;
-  backend_cups->cups_connection_test = NULL;
-
-  backend_cups->username = NULL;
-
-#ifdef HAVE_COLORD
-  backend_cups->colord_client = cd_client_new ();
-#endif
-
-#ifdef HAVE_CUPS_API_1_6
-  backend_cups->dbus_connection = NULL;
-  backend_cups->avahi_default_printer = NULL;
-  backend_cups->avahi_service_browser_subscription_id = 0;
-  for (i = 0; i < 2; i++)
-    {
-      backend_cups->avahi_service_browser_paths[i] = NULL;
-      backend_cups->avahi_service_browser_subscription_ids[i] = 0;
-    }
-#endif
-
-  cups_get_local_default_printer (backend_cups);
-
-  backend_cups->secrets_service_available = FALSE;
-  backend_cups->secrets_service_cancellable = g_cancellable_new ();
-  backend_cups->secrets_service_watch_id =
-    gtk_cups_secrets_service_watch (secrets_service_appeared_cb,
-                                    secrets_service_vanished_cb,
-                                    backend_cups);
-}
-
-static void
-gtk_print_backend_cups_finalize (GObject *object)
-{
-  GtkPrintBackendCups *backend_cups;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: finalizing CUPS backend module\n"));
-
-  backend_cups = GTK_PRINT_BACKEND_CUPS (object);
-
-  g_free (backend_cups->default_printer);
-  backend_cups->default_printer = NULL;
-
-  gtk_cups_connection_test_free (backend_cups->cups_connection_test);
-  backend_cups->cups_connection_test = NULL;
-
-  g_hash_table_destroy (backend_cups->auth);
-
-  g_free (backend_cups->username);
-
-#ifdef HAVE_COLORD
-  g_object_unref (backend_cups->colord_client);
-#endif
-
-#ifdef HAVE_CUPS_API_1_6
-  g_clear_object (&backend_cups->avahi_cancellable);
-  g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
-  g_clear_object (&backend_cups->dbus_connection);
-#endif
-
-  g_clear_object (&backend_cups->secrets_service_cancellable);
-  if (backend_cups->secrets_service_watch_id != 0)
-    {
-      g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
-    }
-
-  backend_parent_class->finalize (object);
-}
-
-static void
-gtk_print_backend_cups_dispose (GObject *object)
-{
-  GtkPrintBackendCups *backend_cups;
-#ifdef HAVE_CUPS_API_1_6
-  gint                 i;
-#endif
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  backend_cups = GTK_PRINT_BACKEND_CUPS (object);
-
-  if (backend_cups->list_printers_poll > 0)
-    g_source_remove (backend_cups->list_printers_poll);
-  backend_cups->list_printers_poll = 0;
-  backend_cups->list_printers_attempts = 0;
-
-  if (backend_cups->default_printer_poll > 0)
-    g_source_remove (backend_cups->default_printer_poll);
-  backend_cups->default_printer_poll = 0;
-
-#ifdef HAVE_CUPS_API_1_6
-  g_cancellable_cancel (backend_cups->avahi_cancellable);
-
-  for (i = 0; i < 2; i++)
-    {
-      if (backend_cups->avahi_service_browser_subscription_ids[i] > 0)
-        {
-          g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
-                                                backend_cups->avahi_service_browser_subscription_ids[i]);
-          backend_cups->avahi_service_browser_subscription_ids[i] = 0;
-        }
-
-      if (backend_cups->avahi_service_browser_paths[i])
-        {
-          g_dbus_connection_call (backend_cups->dbus_connection,
-                                  AVAHI_BUS,
-                                  backend_cups->avahi_service_browser_paths[i],
-                                  AVAHI_SERVICE_BROWSER_IFACE,
-                                  "Free",
-                                  NULL,
-                                  NULL,
-                                  G_DBUS_CALL_FLAGS_NONE,
-                                  -1,
-                                  NULL,
-                                  NULL,
-                                  NULL);
-          g_clear_pointer (&backend_cups->avahi_service_browser_paths[i], g_free);
-        }
-    }
-
-  if (backend_cups->avahi_service_browser_subscription_id > 0)
-    {
-      g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
-                                            backend_cups->avahi_service_browser_subscription_id);
-      backend_cups->avahi_service_browser_subscription_id = 0;
-    }
-#endif
-
-  backend_parent_class->dispose (object);
-}
-
-static gboolean
-is_address_local (const gchar *address)
-{
-  if (address[0] == '/' ||
-      strcmp (address, "127.0.0.1") == 0 ||
-      strcmp (address, "[::1]") == 0)
-    return TRUE;
-  else
-    return FALSE;
-}
-
-static void
-gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
-                                     gchar           **auth_info_required,
-                                     gchar           **auth_info,
-                                     gboolean          store_auth_info)
-{
-  GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
-  GList *l;
-  char   dispatch_hostname[HTTP_MAX_URI];
-  gchar *username = NULL;
-  gchar *hostname = NULL;
-  gchar *password = NULL;
-  gint   length;
-  gint   i;
-
-  length = g_strv_length (auth_info_required);
-
-  if (auth_info != NULL)
-    for (i = 0; i < length; i++)
-      {
-        if (g_strcmp0 (auth_info_required[i], "username") == 0)
-          username = g_strdup (auth_info[i]);
-        else if (g_strcmp0 (auth_info_required[i], "hostname") == 0)
-          hostname = g_strdup (auth_info[i]);
-        else if (g_strcmp0 (auth_info_required[i], "password") == 0)
-          password = g_strdup (auth_info[i]);
-      }
-
-  if (hostname != NULL && username != NULL && password != NULL)
-    {
-      gchar *key = g_strconcat (username, "@", hostname, NULL);
-      g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
-      GTK_NOTE (PRINTING,
-                g_print ("CUPS backend: caching password for %s\n", key));
-    }
-
-  g_free (cups_backend->username);
-  cups_backend->username = g_strdup (username);
-
-
-  for (l = cups_backend->requests; l; l = l->next)
-    {
-      GtkPrintCupsDispatchWatch *dispatch = l->data;
-
-      httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname));
-      if (is_address_local (dispatch_hostname))
-        strcpy (dispatch_hostname, "localhost");
-
-      if (dispatch->request->need_auth_info)
-        {
-          if (auth_info != NULL)
-            {
-              dispatch->request->auth_info = g_new0 (gchar *, length + 1);
-              for (i = 0; i < length; i++)
-                dispatch->request->auth_info[i] = g_strdup (auth_info[i]);
-            }
-          /* Save the password if the user requested it */
-          if (password != NULL && store_auth_info)
-            {
-              const gchar *printer_uri =
-                  gtk_cups_request_ipp_get_string (dispatch->request,
-                                                   IPP_TAG_URI,
-                                                   "printer-uri");
-
-              gtk_cups_secrets_service_store (auth_info, auth_info_required,
-                                              printer_uri);
-            }
-          dispatch->backend->authentication_lock = FALSE;
-          dispatch->request->need_auth_info = FALSE;
-        }
-      else if (dispatch->request->password_state == GTK_CUPS_PASSWORD_REQUESTED || auth_info == NULL)
-        {
-          overwrite_and_free (dispatch->request->password);
-          dispatch->request->password = g_strdup (password);
-          g_free (dispatch->request->username);
-          dispatch->request->username = g_strdup (username);
-          dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
-          dispatch->backend->authentication_lock = FALSE;
-        }
-    }
-}
-
-static gboolean
-request_password (gpointer data)
-{
-  GtkPrintCupsDispatchWatch *dispatch = data;
-  const gchar               *username;
-  gchar                     *password;
-  gchar                     *prompt = NULL;
-  gchar                     *key = NULL;
-  char                       hostname[HTTP_MAX_URI];
-  gchar                    **auth_info_required;
-  gchar                    **auth_info_default;
-  gchar                    **auth_info_display;
-  gboolean                  *auth_info_visible;
-  gint                       length = 3;
-  gint                       i;
-
-  if (dispatch->backend->authentication_lock)
-    return G_SOURCE_REMOVE;
-
-  httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
-  if (is_address_local (hostname))
-    strcpy (hostname, "localhost");
-
-  if (dispatch->backend->username != NULL)
-    username = dispatch->backend->username;
-  else
-    username = cupsUser ();
-
-  auth_info_required = g_new0 (gchar*, length + 1);
-  auth_info_required[0] = g_strdup ("hostname");
-  auth_info_required[1] = g_strdup ("username");
-  auth_info_required[2] = g_strdup ("password");
-
-  auth_info_default = g_new0 (gchar*, length + 1);
-  auth_info_default[0] = g_strdup (hostname);
-  auth_info_default[1] = g_strdup (username);
-
-  auth_info_display = g_new0 (gchar*, length + 1);
-  auth_info_display[1] = g_strdup (_("Username:"));
-  auth_info_display[2] = g_strdup (_("Password:"));
-
-  auth_info_visible = g_new0 (gboolean, length + 1);
-  auth_info_visible[1] = TRUE;
-
-  key = g_strconcat (username, "@", hostname, NULL);
-  password = g_hash_table_lookup (dispatch->backend->auth, key);
-
-  if (password && dispatch->request->password_state != GTK_CUPS_PASSWORD_NOT_VALID)
-    {
-      GTK_NOTE (PRINTING,
-                g_print ("CUPS backend: using stored password for %s\n", key));
-
-      overwrite_and_free (dispatch->request->password);
-      dispatch->request->password = g_strdup (password);
-      g_free (dispatch->request->username);
-      dispatch->request->username = g_strdup (username);
-      dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
-    }
-  else
-    {
-      const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
-      const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
-      char *printer_name = NULL;
-
-      if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
-        printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
-
-      if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID)
-        g_hash_table_remove (dispatch->backend->auth, key);
-
-      dispatch->request->password_state = GTK_CUPS_PASSWORD_REQUESTED;
-
-      dispatch->backend->authentication_lock = TRUE;
-
-      switch (ippGetOperation (dispatch->request->ipp_request))
-        {
-          case IPP_PRINT_JOB:
-            if (job_title != NULL && printer_name != NULL)
-              prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
-            else
-              prompt = g_strdup_printf ( _("Authentication is required to print a document on %s"), hostname);
-            break;
-          case IPP_GET_JOB_ATTRIBUTES:
-            if (job_title != NULL)
-              prompt = g_strdup_printf ( _("Authentication is required to get attributes of job “%s”"), job_title);
-            else
-              prompt = g_strdup ( _("Authentication is required to get attributes of a job"));
-            break;
-          case IPP_GET_PRINTER_ATTRIBUTES:
-            if (printer_name != NULL)
-              prompt = g_strdup_printf ( _("Authentication is required to get attributes of printer %s"), printer_name);
-            else
-              prompt = g_strdup ( _("Authentication is required to get attributes of a printer"));
-            break;
-          case CUPS_GET_DEFAULT:
-            prompt = g_strdup_printf ( _("Authentication is required to get default printer of %s"), hostname);
-            break;
-          case CUPS_GET_PRINTERS:
-            prompt = g_strdup_printf ( _("Authentication is required to get printers from %s"), hostname);
-            break;
-          default:
-            /* work around gcc warning about 0 not being a value for this enum */
-            if (ippGetOperation (dispatch->request->ipp_request) == 0)
-              prompt = g_strdup_printf ( _("Authentication is required to get a file from %s"), hostname);
-            else
-              prompt = g_strdup_printf ( _("Authentication is required on %s"), hostname);
-            break;
-        }
-
-      g_free (printer_name);
-
-      g_signal_emit_by_name (dispatch->backend, "request-password",
-                             auth_info_required, auth_info_default,
-                             auth_info_display, auth_info_visible, prompt,
-                             FALSE); /* Cups password is only cached not stored. */
-
-      g_free (prompt);
-    }
-
-  for (i = 0; i < length; i++)
-    {
-      g_free (auth_info_required[i]);
-      g_free (auth_info_default[i]);
-      g_free (auth_info_display[i]);
-    }
-
-  g_free (auth_info_required);
-  g_free (auth_info_default);
-  g_free (auth_info_display);
-  g_free (auth_info_visible);
-  g_free (key);
-
-  return G_SOURCE_REMOVE;
-}
-
-static void
-cups_dispatch_add_poll (GSource *source)
-{
-  GtkPrintCupsDispatchWatch *dispatch;
-  GtkCupsPollState poll_state;
-
-  dispatch = (GtkPrintCupsDispatchWatch *) source;
-
-  poll_state = gtk_cups_request_get_poll_state (dispatch->request);
-
-  /* Remove the old source if the poll state changed. */
-  if (poll_state != dispatch->poll_state && dispatch->data_poll != NULL)
-    {
-      g_source_remove_poll (source, dispatch->data_poll);
-      g_free (dispatch->data_poll);
-      dispatch->data_poll = NULL;
-    }
-
-  if (dispatch->request->http != NULL)
-    {
-      if (dispatch->data_poll == NULL)
-        {
-         dispatch->data_poll = g_new0 (GPollFD, 1);
-         dispatch->poll_state = poll_state;
-
-         if (poll_state == GTK_CUPS_HTTP_READ)
-           dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
-         else if (poll_state == GTK_CUPS_HTTP_WRITE)
-           dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
-         else
-           dispatch->data_poll->events = 0;
-
-          dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
-          g_source_add_poll (source, dispatch->data_poll);
-        }
-    }
-}
-
-static gboolean
-check_auth_info (gpointer user_data)
-{
-  GtkPrintCupsDispatchWatch *dispatch;
-  dispatch = (GtkPrintCupsDispatchWatch *) user_data;
-
-  if (!dispatch->request->need_auth_info)
-    {
-      if (dispatch->request->auth_info == NULL)
-        {
-          dispatch->callback (GTK_PRINT_BACKEND (dispatch->backend),
-                              gtk_cups_request_get_result (dispatch->request),
-                              dispatch->callback_data);
-          g_source_destroy ((GSource *) dispatch);
-        }
-      else
-        {
-          gint length;
-          gint i;
-
-          length = g_strv_length (dispatch->request->auth_info_required);
-
-          gtk_cups_request_ipp_add_strings (dispatch->request,
-                                            IPP_TAG_JOB,
-                                            IPP_TAG_TEXT,
-                                            "auth-info",
-                                            length,
-                                            NULL,
-                                            (const char * const *) dispatch->request->auth_info);
-
-          g_source_attach ((GSource *) dispatch, NULL);
-          g_source_unref ((GSource *) dispatch);
-
-          for (i = 0; i < length; i++)
-            overwrite_and_free (dispatch->request->auth_info[i]);
-          g_free (dispatch->request->auth_info);
-          dispatch->request->auth_info = NULL;
-        }
-
-      return G_SOURCE_REMOVE;
-    }
-
-  return G_SOURCE_CONTINUE;
-}
-
-static void
-lookup_auth_info_cb (GObject      *source_object,
-                     GAsyncResult *res,
-                     gpointer      user_data)
-{
-  GTask                      *task;
-  GtkPrintCupsDispatchWatch  *dispatch;
-  gchar                     **auth_info;
-  GError                     *error = NULL;
-  gint                        i;
-
-  task = (GTask *) res;
-  dispatch = user_data;
-  auth_info = g_task_propagate_pointer (task, &error);
-
-  if (auth_info == NULL)
-    {
-      if (error != NULL)
-        {
-          GTK_NOTE (PRINTING,
-                    g_print ("Failed to look up auth info: %s\n", error->message));
-          g_error_free (error);
-        }
-      else
-        {
-          /* Error note should have been shown by the function causing this */
-          GTK_NOTE (PRINTING, g_print ("Failed to look up auth info.\n"));
-        }
-      dispatch->backend->authentication_lock = FALSE;
-      g_object_unref (task);
-      request_auth_info (dispatch);
-      return;
-    }
-
-  gtk_print_backend_cups_set_password (GTK_PRINT_BACKEND (dispatch->backend),
-                                       dispatch->request->auth_info_required, auth_info,
-                                       FALSE);
-  for (i = 0; auth_info[i] != NULL; i++)
-    {
-      overwrite_and_free (auth_info[i]);
-      auth_info[i] = NULL;
-    }
-  g_clear_pointer (auth_info, g_free);
-
-  g_object_unref (task);
-}
-
-static void
-lookup_auth_info (gpointer user_data)
-{
-  GtkPrintCupsDispatchWatch  *dispatch;
-  gsize                       length,
-                              i;
-  gboolean                    need_secret_auth_info = FALSE;
-  const gchar                *printer_uri;
-
-  dispatch = user_data;
-
-  if (dispatch->backend->authentication_lock)
-    return;
-
-  length = g_strv_length (dispatch->request->auth_info_required);
-
-  for (i = 0; i < length; i++)
-    {
-      if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
-        {
-          need_secret_auth_info = TRUE;
-          break;
-        }
-    }
-
-  g_idle_add (check_auth_info, user_data);
-
-  if (dispatch->backend->secrets_service_available && need_secret_auth_info)
-    {
-      dispatch->backend->authentication_lock = TRUE;
-      printer_uri = gtk_cups_request_ipp_get_string (dispatch->request,
-                                                     IPP_TAG_URI,
-                                                     "printer-uri");
-      gtk_cups_secrets_service_query_task (dispatch->backend,
-                                           dispatch->backend->secrets_service_cancellable,
-                                           lookup_auth_info_cb,
-                                           dispatch,
-                                           printer_uri,
-                                           dispatch->request->auth_info_required);
-      return;
-    }
-
-  request_auth_info (user_data);
-}
-
-static gboolean
-request_auth_info (gpointer user_data)
-{
-  GtkPrintCupsDispatchWatch  *dispatch;
-  const char                 *job_title;
-  const char                 *printer_uri;
-  gchar                      *prompt = NULL;
-  char                       *printer_name = NULL;
-  gint                        length;
-  gint                        i;
-  gboolean                   *auth_info_visible = NULL;
-  gchar                     **auth_info_default = NULL;
-  gchar                     **auth_info_display = NULL;
-
-  dispatch = (GtkPrintCupsDispatchWatch *) user_data;
-
-  if (dispatch->backend->authentication_lock)
-    return FALSE;
-
-  job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
-  printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
-  length = g_strv_length (dispatch->request->auth_info_required);
-
-  auth_info_visible = g_new0 (gboolean, length);
-  auth_info_default = g_new0 (gchar *, length + 1);
-  auth_info_display = g_new0 (gchar *, length + 1);
-
-  for (i = 0; i < length; i++)
-    {
-      if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0)
-        {
-          auth_info_display[i] = g_strdup (_("Domain:"));
-          auth_info_default[i] = g_strdup ("WORKGROUP");
-          auth_info_visible[i] = TRUE;
-        }
-      else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0)
-        {
-          auth_info_display[i] = g_strdup (_("Username:"));
-          if (dispatch->backend->username != NULL)
-            auth_info_default[i] = g_strdup (dispatch->backend->username);
-          else
-            auth_info_default[i] = g_strdup (cupsUser ());
-          auth_info_visible[i] = TRUE;
-        }
-      else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
-        {
-          auth_info_display[i] = g_strdup (_("Password:"));
-          auth_info_visible[i] = FALSE;
-        }
-    }
-
-  if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
-    printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
-
-  dispatch->backend->authentication_lock = TRUE;
-
-  if (job_title != NULL)
-    {
-      if (printer_name != NULL)
-        prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
-      else
-        prompt = g_strdup_printf ( _("Authentication is required to print document “%s”"), job_title);
-    }
-  else
-    {
-      if (printer_name != NULL)
-        prompt = g_strdup_printf ( _("Authentication is required to print this document on printer %s"), printer_name);
-      else
-        prompt = g_strdup ( _("Authentication is required to print this document"));
-    }
-
-  g_signal_emit_by_name (dispatch->backend, "request-password",
-                         dispatch->request->auth_info_required,
-                         auth_info_default,
-                         auth_info_display,
-                         auth_info_visible,
-                         prompt,
-                         dispatch->backend->secrets_service_available);
-
-  for (i = 0; i < length; i++)
-    {
-      g_free (auth_info_default[i]);
-      g_free (auth_info_display[i]);
-    }
-
-  g_free (auth_info_default);
-  g_free (auth_info_display);
-  g_free (printer_name);
-  g_free (prompt);
-
-  return FALSE;
-}
-
-static gboolean
-cups_dispatch_watch_check (GSource *source)
-{
-  GtkPrintCupsDispatchWatch *dispatch;
-  GtkCupsPollState poll_state;
-  gboolean result;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
-
-  dispatch = (GtkPrintCupsDispatchWatch *) source;
-
-  poll_state = gtk_cups_request_get_poll_state (dispatch->request);
-
-  if (poll_state != GTK_CUPS_HTTP_IDLE && !dispatch->request->need_password)
-    if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
-       return FALSE;
-
-  result = gtk_cups_request_read_write (dispatch->request, FALSE);
-  if (result && dispatch->data_poll != NULL)
-    {
-      g_source_remove_poll (source, dispatch->data_poll);
-      g_free (dispatch->data_poll);
-      dispatch->data_poll = NULL;
-    }
-
-  if (dispatch->request->need_password && dispatch->request->password_state != GTK_CUPS_PASSWORD_REQUESTED)
-    {
-      dispatch->request->need_password = FALSE;
-      g_idle_add (request_password, dispatch);
-      result = FALSE;
-    }
-
-  return result;
-}
-
-static gboolean
-cups_dispatch_watch_prepare (GSource *source,
-                            gint    *timeout_)
-{
-  GtkPrintCupsDispatchWatch *dispatch;
-  gboolean result;
-
-  dispatch = (GtkPrintCupsDispatchWatch *) source;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
-
-  *timeout_ = -1;
-
-  result = gtk_cups_request_read_write (dispatch->request, TRUE);
-
-  cups_dispatch_add_poll (source);
-
-  return result;
-}
-
-static gboolean
-cups_dispatch_watch_dispatch (GSource     *source,
-                             GSourceFunc  callback,
-                             gpointer     user_data)
-{
-  GtkPrintCupsDispatchWatch *dispatch;
-  GtkPrintCupsResponseCallbackFunc ep_callback;
-  GtkCupsResult *result;
-
-  g_assert (callback != NULL);
-
-  ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
-
-  dispatch = (GtkPrintCupsDispatchWatch *) source;
-
-  result = gtk_cups_request_get_result (dispatch->request);
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
-
-  if (gtk_cups_result_is_error (result))
-    {
-      GTK_NOTE (PRINTING,
-                g_print("Error result: %s (type %i, status %i, code %i)\n",
-                        gtk_cups_result_get_error_string (result),
-                        gtk_cups_result_get_error_type (result),
-                        gtk_cups_result_get_error_status (result),
-                        gtk_cups_result_get_error_code (result)));
-     }
-
-  ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
-
-  return FALSE;
-}
-
-static void
-cups_dispatch_watch_finalize (GSource *source)
-{
-  GtkPrintCupsDispatchWatch *dispatch;
-  GtkCupsResult *result;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
-
-  dispatch = (GtkPrintCupsDispatchWatch *) source;
-
-  result = gtk_cups_request_get_result (dispatch->request);
-  if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH)
-    {
-      const gchar *username;
-      gchar        hostname[HTTP_MAX_URI];
-      gchar       *key;
-
-      httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
-      if (is_address_local (hostname))
-        strcpy (hostname, "localhost");
-
-      if (dispatch->backend->username != NULL)
-        username = dispatch->backend->username;
-      else
-        username = cupsUser ();
-
-      key = g_strconcat (username, "@", hostname, NULL);
-      GTK_NOTE (PRINTING,
-                g_print ("CUPS backend: removing stored password for %s\n", key));
-      g_hash_table_remove (dispatch->backend->auth, key);
-      g_free (key);
-
-      if (dispatch->backend)
-        dispatch->backend->authentication_lock = FALSE;
-    }
-
-  gtk_cups_request_free (dispatch->request);
-
-  if (dispatch->backend)
-    {
-      /* We need to unref this at idle time, because it might be the
-       * last reference to this module causing the code to be
-       * unloaded (including this particular function!)
-       * Update: Doing this at idle caused a deadlock taking the
-       * mainloop context lock while being in a GSource callout for
-       * multithreaded apps. So, for now we just disable unloading
-       * of print backends. See _gtk_print_backend_create for the
-       * disabling.
-       */
-
-      dispatch->backend->requests = g_list_remove (dispatch->backend->requests, dispatch);
-
-
-      g_object_unref (dispatch->backend);
-      dispatch->backend = NULL;
-    }
-
-  if (dispatch->data_poll)
-    {
-      g_source_remove_poll (source, dispatch->data_poll);
-      g_free (dispatch->data_poll);
-      dispatch->data_poll = NULL;
-    }
-}
-
-static GSourceFuncs _cups_dispatch_watch_funcs = {
-  cups_dispatch_watch_prepare,
-  cups_dispatch_watch_check,
-  cups_dispatch_watch_dispatch,
-  cups_dispatch_watch_finalize
-};
-
-
-static void
-cups_request_execute (GtkPrintBackendCups              *print_backend,
-                      GtkCupsRequest                   *request,
-                      GtkPrintCupsResponseCallbackFunc  callback,
-                      gpointer                          user_data,
-                      GDestroyNotify                    notify)
-{
-  GtkPrintCupsDispatchWatch *dispatch;
-
-  dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs,
-                                                         sizeof (GtkPrintCupsDispatchWatch));
-  g_source_set_name (&dispatch->source, "GTK+ CUPS backend");
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
-
-  dispatch->request = request;
-  dispatch->backend = g_object_ref (print_backend);
-  dispatch->poll_state = GTK_CUPS_HTTP_IDLE;
-  dispatch->data_poll = NULL;
-  dispatch->callback = NULL;
-  dispatch->callback_data = NULL;
-
-  print_backend->requests = g_list_prepend (print_backend->requests, dispatch);
-
-  g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
-
-  if (request->need_auth_info)
-    {
-      dispatch->callback = callback;
-      dispatch->callback_data = user_data;
-      lookup_auth_info (dispatch);
-    }
-  else
-    {
-      g_source_attach ((GSource *) dispatch, NULL);
-      g_source_unref ((GSource *) dispatch);
-    }
-}
-
-typedef struct {
-  GtkPrintBackendCups *print_backend;
-  GtkPrintJob *job;
-  int job_id;
-  int counter;
-} CupsJobPollData;
-
-static void
-job_object_died        (gpointer  user_data,
-                GObject  *where_the_object_was)
-{
-  CupsJobPollData *data = user_data;
-  data->job = NULL;
-}
-
-static void
-cups_job_poll_data_free (CupsJobPollData *data)
-{
-  if (data->job)
-    g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
-
-  g_free (data);
-}
-
-static void
-cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
-                         GtkCupsResult       *result,
-                         gpointer             user_data)
-{
-  CupsJobPollData *data = user_data;
-  ipp_attribute_t *attr;
-  ipp_t *response;
-  int state;
-  gboolean done;
-
-  if (data->job == NULL)
-    {
-      cups_job_poll_data_free (data);
-      return;
-    }
-
-  data->counter++;
-
-  response = gtk_cups_result_get_response (result);
-
-  state = 0;
-
-#ifdef HAVE_CUPS_API_1_6
-  attr = ippFindAttribute (response, "job-state", IPP_TAG_ENUM);
-  state = ippGetInteger (attr, 0);
-#else
-  for (attr = response->attrs; attr != NULL; attr = attr->next)
-    {
-      if (!attr->name)
-        continue;
-
-      _CUPS_MAP_ATTR_INT (attr, state, "job-state");
-    }
-#endif
-
-  done = FALSE;
-  switch (state)
-    {
-    case IPP_JOB_PENDING:
-    case IPP_JOB_HELD:
-    case IPP_JOB_STOPPED:
-      gtk_print_job_set_status (data->job,
-                               GTK_PRINT_STATUS_PENDING);
-      break;
-    case IPP_JOB_PROCESSING:
-      gtk_print_job_set_status (data->job,
-                               GTK_PRINT_STATUS_PRINTING);
-      break;
-    default:
-    case IPP_JOB_CANCELLED:
-    case IPP_JOB_ABORTED:
-      gtk_print_job_set_status (data->job,
-                               GTK_PRINT_STATUS_FINISHED_ABORTED);
-      done = TRUE;
-      break;
-    case 0:
-    case IPP_JOB_COMPLETED:
-      gtk_print_job_set_status (data->job,
-                               GTK_PRINT_STATUS_FINISHED);
-      done = TRUE;
-      break;
-    }
-
-  if (!done && data->job != NULL)
-    {
-      guint32 timeout;
-      guint id;
-
-      if (data->counter < 5)
-       timeout = 100;
-      else if (data->counter < 10)
-       timeout = 500;
-      else
-       timeout = 1000;
-
-      id = g_timeout_add (timeout, cups_job_info_poll_timeout, data);
-      g_source_set_name_by_id (id, "[gtk+] cups_job_info_poll_timeout");
-    }
-  else
-    cups_job_poll_data_free (data);
-}
-
-static void
-cups_request_job_info (CupsJobPollData *data)
-{
-  GtkCupsRequest *request;
-  gchar *job_uri;
-
-  request = gtk_cups_request_new_with_username (NULL,
-                                                GTK_CUPS_POST,
-                                                IPP_GET_JOB_ATTRIBUTES,
-                                                NULL,
-                                                NULL,
-                                                NULL,
-                                                data->print_backend->username);
-
-  job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
-  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
-                                   "job-uri", NULL, job_uri);
-  g_free (job_uri);
-
-  cups_request_execute (data->print_backend,
-                        request,
-                        (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
-                        data,
-                        NULL);
-}
-
-static gboolean
-cups_job_info_poll_timeout (gpointer user_data)
-{
-  CupsJobPollData *data = user_data;
-
-  if (data->job == NULL)
-    cups_job_poll_data_free (data);
-  else
-    cups_request_job_info (data);
-
-  return G_SOURCE_REMOVE;
-}
-
-static void
-cups_begin_polling_info (GtkPrintBackendCups *print_backend,
-                        GtkPrintJob         *job,
-                        gint                 job_id)
-{
-  CupsJobPollData *data;
-
-  data = g_new0 (CupsJobPollData, 1);
-
-  data->print_backend = print_backend;
-  data->job = job;
-  data->job_id = job_id;
-  data->counter = 0;
-
-  g_object_weak_ref (G_OBJECT (job), job_object_died, data);
-
-  cups_request_job_info (data);
-}
-
-static void
-mark_printer_inactive (GtkPrinter      *printer,
-                       GtkPrintBackend *backend)
-{
-  gtk_printer_set_is_active (printer, FALSE);
-  g_signal_emit_by_name (backend, "printer-removed", printer);
-}
-
-static gint
-find_printer (GtkPrinter  *printer,
-             const gchar *find_name)
-{
-  const gchar *printer_name;
-
-  printer_name = gtk_printer_get_name (printer);
-  return g_ascii_strcasecmp (printer_name, find_name);
-}
-/* Printer messages we're interested in */
-static const char * const printer_messages[] =
-  {
-    "toner-low",
-    "toner-empty",
-    "developer-low",
-    "developer-empty",
-    "marker-supply-low",
-    "marker-supply-empty",
-    "cover-open",
-    "door-open",
-    "media-low",
-    "media-empty",
-    "offline",
-    "other"
-  };
-
-/* Attributes we're interested in for printers */
-static const char * const printer_attrs[] =
-  {
-    "printer-name",
-    "printer-uri-supported",
-    "member-uris",
-    "printer-location",
-    "printer-info",
-    "printer-state-message",
-    "printer-state-reasons",
-    "printer-state",
-    "queued-job-count",
-    "printer-is-accepting-jobs",
-    "job-sheets-supported",
-    "job-sheets-default",
-    "printer-type",
-    "auth-info-required",
-    "number-up-default",
-    "ipp-versions-supported",
-    "multiple-document-handling-supported",
-    "copies-supported",
-    "number-up-supported",
-    "device-uri"
-  };
-
-/* Attributes we're interested in for printers without PPD */
-static const char * const printer_attrs_detailed[] =
-  {
-    "printer-name",
-    "printer-uri-supported",
-    "member-uris",
-    "printer-location",
-    "printer-info",
-    "printer-state-message",
-    "printer-state-reasons",
-    "printer-state",
-    "queued-job-count",
-    "printer-is-accepting-jobs",
-    "job-sheets-supported",
-    "job-sheets-default",
-    "printer-type",
-    "auth-info-required",
-    "number-up-default",
-    "ipp-versions-supported",
-    "multiple-document-handling-supported",
-    "copies-supported",
-    "number-up-supported",
-    "media-col-default",
-    "media-col-supported",
-    "media-default",
-    "media-size-supported",
-    "media-supported",
-    "media-left-margin-supported",
-    "media-right-margin-supported",
-    "media-bottom-margin-supported",
-    "media-top-margin-supported",
-    "sides-default",
-    "sides-supported",
-    "output-bin-default",
-    "output-bin-supported",
-  };
-
-typedef enum
-  {
-    GTK_PRINTER_STATE_LEVEL_NONE = 0,
-    GTK_PRINTER_STATE_LEVEL_INFO = 1,
-    GTK_PRINTER_STATE_LEVEL_WARNING = 2,
-    GTK_PRINTER_STATE_LEVEL_ERROR = 3
-  } PrinterStateLevel;
-
-typedef struct
-{
-  float x_dimension;
-  float y_dimension;
-} MediaSize;
-
-typedef struct
-{
-  const gchar *printer_name;
-  const gchar *printer_uri;
-  const gchar *member_uris;
-  const gchar *location;
-  const gchar *description;
-  gchar *state_msg;
-  const gchar *reason_msg;
-  PrinterStateLevel reason_level;
-  gint state;
-  gint job_count;
-  gboolean is_paused;
-  gboolean is_accepting_jobs;
-  const gchar *default_cover_before;
-  const gchar *default_cover_after;
-  gboolean default_printer;
-  gboolean got_printer_type;
-  gboolean remote_printer;
-#ifdef HAVE_CUPS_API_1_6
-  gboolean avahi_printer;
-#endif
-  gchar  **auth_info_required;
-  gint     default_number_up;
-  guchar   ipp_version_major;
-  guchar   ipp_version_minor;
-  gboolean supports_copies;
-  gboolean supports_collate;
-  gboolean supports_number_up;
-  gchar    *media_default;
-  GList    *media_supported;
-  GList    *media_size_supported;
-  float     media_bottom_margin_default;
-  float     media_top_margin_default;
-  float     media_left_margin_default;
-  float     media_right_margin_default;
-  gboolean  media_margin_default_set;
-  gchar    *sides_default;
-  GList    *sides_supported;
-  char    **covers;
-  int       number_of_covers;
-  gchar    *output_bin_default;
-  GList    *output_bin_supported;
-  gchar    *original_device_uri;
-} PrinterSetupInfo;
-
-static void
-printer_setup_info_free (PrinterSetupInfo *info)
-{
-  g_free (info->original_device_uri);
-  g_free (info->state_msg);
-  g_strfreev (info->covers);
-  g_slice_free (PrinterSetupInfo, info);
-}
-
-static void
-get_ipp_version (const char *ipp_version_string,
-                 guchar     *ipp_version_major,
-                 guchar     *ipp_version_minor)
-{
-  gchar **ipp_version_strv;
-  gchar  *endptr;
-
-  *ipp_version_major = 1;
-  *ipp_version_minor = 1;
-
-  if (ipp_version_string)
-    {
-      ipp_version_strv = g_strsplit (ipp_version_string, ".", 0);
-
-      if (ipp_version_strv)
-        {
-          if (g_strv_length (ipp_version_strv) == 2)
-            {
-              *ipp_version_major = (guchar) g_ascii_strtoull (ipp_version_strv[0], &endptr, 10);
-              if (endptr == ipp_version_strv[0])
-                *ipp_version_major = 1;
-
-              *ipp_version_minor = (guchar) g_ascii_strtoull (ipp_version_strv[1], &endptr, 10);
-              if (endptr == ipp_version_strv[1])
-                *ipp_version_minor = 1;
-            }
-
-          g_strfreev (ipp_version_strv);
-        }
-    }
-}
-
-static void
-get_server_ipp_version (guchar *ipp_version_major,
-                        guchar *ipp_version_minor)
-{
-  *ipp_version_major = 1;
-  *ipp_version_minor = 1;
-
-  if (IPP_VERSION && strlen (IPP_VERSION) == 2)
-    {
-      *ipp_version_major = (unsigned char) IPP_VERSION[0];
-      *ipp_version_minor = (unsigned char) IPP_VERSION[1];
-    }
-}
-
-static gint
-ipp_version_cmp (guchar ipp_version_major1,
-                 guchar ipp_version_minor1,
-                 guchar ipp_version_major2,
-                 guchar ipp_version_minor2)
-{
-  if (ipp_version_major1 == ipp_version_major2 &&
-      ipp_version_minor1 == ipp_version_minor2)
-    {
-      return 0;
-    }
-  else if (ipp_version_major1 < ipp_version_major2 ||
-           (ipp_version_major1 == ipp_version_major2 &&
-            ipp_version_minor1 < ipp_version_minor2))
-    {
-      return -1;
-    }
-  else
-    {
-      return 1;
-    }
-}
-
-static void
-cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
-                              ipp_attribute_t *attr,
-                              PrinterSetupInfo *info)
-{
-  gint i, j;
-  if (strcmp (ippGetName (attr), "printer-name") == 0 &&
-      ippGetValueTag (attr) == IPP_TAG_NAME)
-    info->printer_name = ippGetString (attr, 0, NULL);
-  else if (strcmp (ippGetName (attr), "printer-uri-supported") == 0 &&
-          ippGetValueTag (attr) == IPP_TAG_URI)
-    info->printer_uri = ippGetString (attr, 0, NULL);
-  else if (strcmp (ippGetName (attr), "member-uris") == 0 &&
-          ippGetValueTag (attr) == IPP_TAG_URI)
-    info->member_uris = ippGetString (attr, 0, NULL);
-  else if (strcmp (ippGetName (attr), "printer-location") == 0)
-    info->location = ippGetString (attr, 0, NULL);
-  else if (strcmp (ippGetName (attr), "printer-info") == 0)
-    info->description = ippGetString (attr, 0, NULL);
-  else if (strcmp (ippGetName (attr), "printer-state-message") == 0)
-    info->state_msg = g_strdup (ippGetString (attr, 0, NULL));
-  else if (strcmp (ippGetName (attr), "printer-state-reasons") == 0)
-    /* Store most important reason to reason_msg and set
-       its importance at printer_state_reason_level */
-    {
-      for (i = 0; i < ippGetCount (attr); i++)
-       {
-         if (strcmp (ippGetString (attr, i, NULL), "none") != 0)
-           {
-             gboolean interested_in = FALSE;
-             /* Sets is_paused flag for paused printer. */
-             if (strcmp (ippGetString (attr, i, NULL), "paused") == 0)
-               {
-                 info->is_paused = TRUE;
-               }
-
-             for (j = 0; j < G_N_ELEMENTS (printer_messages); j++)
-               if (strncmp (ippGetString (attr, i, NULL), printer_messages[j],
-                            strlen (printer_messages[j])) == 0)
-                 {
-                   interested_in = TRUE;
-                   break;
-                 }
-
-             if (interested_in)
-               {
-                 if (g_str_has_suffix (ippGetString (attr, i, NULL), "-report"))
-                   {
-                     if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_INFO)
-                       {
-                         info->reason_msg = ippGetString (attr, i, NULL);
-                         info->reason_level = GTK_PRINTER_STATE_LEVEL_INFO;
-                       }
-                   }
-                 else if (g_str_has_suffix (ippGetString (attr, i, NULL), "-warning"))
-                   {
-                     if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_WARNING)
-                       {
-                         info->reason_msg = ippGetString (attr, i, NULL);
-                         info->reason_level = GTK_PRINTER_STATE_LEVEL_WARNING;
-                       }
-                   }
-                 else  /* It is error in the case of no suffix. */
-                   {
-                     info->reason_msg = ippGetString (attr, i, NULL);
-                     info->reason_level = GTK_PRINTER_STATE_LEVEL_ERROR;
-                   }
-               }
-           }
-       }
-    }
-  else if (strcmp (ippGetName (attr), "printer-state") == 0)
-    info->state = ippGetInteger (attr, 0);
-  else if (strcmp (ippGetName (attr), "queued-job-count") == 0)
-    info->job_count = ippGetInteger (attr, 0);
-  else if (strcmp (ippGetName (attr), "printer-is-accepting-jobs") == 0)
-    {
-      if (ippGetBoolean (attr, 0) == 1)
-       info->is_accepting_jobs = TRUE;
-      else
-       info->is_accepting_jobs = FALSE;
-    }
-  else if (strcmp (ippGetName (attr), "job-sheets-supported") == 0)
-    {
-      info->number_of_covers = ippGetCount (attr);
-      info->covers = g_new (char *, info->number_of_covers + 1);
-      for (i = 0; i < info->number_of_covers; i++)
-        info->covers[i] = g_strdup (ippGetString (attr, i, NULL));
-      info->covers[info->number_of_covers] = NULL;
-    }
-  else if (strcmp (ippGetName (attr), "job-sheets-default") == 0)
-    {
-      if (ippGetCount (attr) == 2)
-       {
-         info->default_cover_before = ippGetString (attr, 0, NULL);
-         info->default_cover_after = ippGetString (attr, 1, NULL);
-       }
-    }
-  else if (strcmp (ippGetName (attr), "printer-type") == 0)
-    {
-      info->got_printer_type = TRUE;
-      if (ippGetInteger (attr, 0) & 0x00020000)
-       info->default_printer = TRUE;
-      else
-       info->default_printer = FALSE;
-
-      if (ippGetInteger (attr, 0) & 0x00000002)
-       info->remote_printer = TRUE;
-      else
-       info->remote_printer = FALSE;
-    }
-  else if (strcmp (ippGetName (attr), "auth-info-required") == 0)
-    {
-      if (strcmp (ippGetString (attr, 0, NULL), "none") != 0)
-       {
-         info->auth_info_required = g_new0 (gchar *, ippGetCount (attr) + 1);
-         for (i = 0; i < ippGetCount (attr); i++)
-           info->auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL));
-       }
-    }
-  else if (strcmp (ippGetName (attr), "number-up-default") == 0)
-    {
-      info->default_number_up = ippGetInteger (attr, 0);
-    }
-  else if (g_strcmp0 (ippGetName (attr), "ipp-versions-supported") == 0)
-    {
-      guchar server_ipp_version_major;
-      guchar server_ipp_version_minor;
-      guchar ipp_version_major;
-      guchar ipp_version_minor;
-
-      get_server_ipp_version (&server_ipp_version_major,
-                              &server_ipp_version_minor);
-
-      for (i = 0; i < ippGetCount (attr); i++)
-        {
-          get_ipp_version (ippGetString (attr, i, NULL),
-                           &ipp_version_major,
-                           &ipp_version_minor);
-
-          if (ipp_version_cmp (ipp_version_major,
-                               ipp_version_minor,
-                               info->ipp_version_major,
-                               info->ipp_version_minor) > 0 &&
-              ipp_version_cmp (ipp_version_major,
-                               ipp_version_minor,
-                               server_ipp_version_major,
-                               server_ipp_version_minor) <= 0)
-            {
-              info->ipp_version_major = ipp_version_major;
-              info->ipp_version_minor = ipp_version_minor;
-            }
-        }
-    }
-  else if (g_strcmp0 (ippGetName (attr), "number-up-supported") == 0)
-    {
-      if (ippGetCount (attr) == 6)
-        {
-          info->supports_number_up = TRUE;
-        }
-    }
-  else if (g_strcmp0 (ippGetName (attr), "copies-supported") == 0)
-    {
-      int upper = 1;
-
-      ippGetRange (attr, 0, &upper);
-      if (upper > 1)
-        {
-          info->supports_copies = TRUE;
-        }
-    }
-  else if (g_strcmp0 (ippGetName (attr), "multiple-document-handling-supported") == 0)
-    {
-      for (i = 0; i < ippGetCount (attr); i++)
-        {
-          if (g_strcmp0 (ippGetString (attr, i, NULL), "separate-documents-collated-copies") == 0)
-            {
-              info->supports_collate = TRUE;
-            }
-        }
-    }
-  else if (g_strcmp0 (ippGetName (attr), "sides-default") == 0)
-    {
-      info->sides_default = g_strdup (ippGetString (attr, 0, NULL));
-    }
-  else if (g_strcmp0 (ippGetName (attr), "sides-supported") == 0)
-    {
-      for (i = 0; i < ippGetCount (attr); i++)
-        info->sides_supported = g_list_prepend (info->sides_supported, g_strdup (ippGetString (attr, i, NULL)));
-
-      info->sides_supported = g_list_reverse (info->sides_supported);
-    }
-  else if (g_strcmp0 (ippGetName (attr), "media-default") == 0)
-    {
-      if (ippGetValueTag (attr) == IPP_TAG_KEYWORD ||
-          ippGetValueTag (attr) == IPP_TAG_NAME)
-        info->media_default = g_strdup (ippGetString (attr, 0, NULL));
-    }
-  else if (g_strcmp0 (ippGetName (attr), "media-col-default") == 0)
-    {
-      ipp_attribute_t *iter;
-      ipp_t           *col;
-      gint             num_of_margins = 0;
-
-      for (i = 0; i < ippGetCount (attr); i++)
-        {
-          col = ippGetCollection (attr, i);
-          for (iter = ippFirstAttribute (col); iter != NULL; iter = ippNextAttribute (col))
-            {
-              switch (ippGetValueTag (iter))
-                {
-                  case IPP_TAG_INTEGER:
-                    if (g_strcmp0 (ippGetName (iter), "media-bottom-margin") == 0)
-                      {
-                        info->media_bottom_margin_default = ippGetInteger (iter, 0) / 100.0;
-                        num_of_margins++;
-                      }
-                    else if (g_strcmp0 (ippGetName (iter), "media-top-margin") == 0)
-                      {
-                        info->media_top_margin_default = ippGetInteger (iter, 0) / 100.0;
-                        num_of_margins++;
-                      }
-                    else if (g_strcmp0 (ippGetName (iter), "media-left-margin") == 0)
-                      {
-                        info->media_left_margin_default = ippGetInteger (iter, 0) / 100.0;
-                        num_of_margins++;
-                      }
-                    else if (g_strcmp0 (ippGetName (iter), "media-right-margin") == 0)
-                      {
-                        info->media_right_margin_default = ippGetInteger (iter, 0) / 100.0;
-                        num_of_margins++;
-                      }
-                    break;
-
-                  default:
-                    break;
-                }
-            }
-        }
-
-      if (num_of_margins == 4)
-        info->media_margin_default_set = TRUE;
-    }
-  else if (g_strcmp0 (ippGetName (attr), "media-supported") == 0)
-    {
-      for (i = 0; i < ippGetCount (attr); i++)
-        info->media_supported = g_list_prepend (info->media_supported, g_strdup (ippGetString (attr, i, NULL)));
-
-      info->media_supported = g_list_reverse (info->media_supported);
-    }
-  else if (g_strcmp0 (ippGetName (attr), "media-size-supported") == 0)
-    {
-      ipp_attribute_t *iter;
-      MediaSize       *media_size;
-      gboolean         number_of_dimensions;
-      ipp_t           *media_size_collection;
-
-      for (i = 0; i < ippGetCount (attr); i++)
-        {
-          media_size_collection = ippGetCollection (attr, i);
-          media_size = g_new0 (MediaSize, 1);
-          number_of_dimensions = 0;
-
-          for (iter = ippFirstAttribute (media_size_collection);
-               iter != NULL;
-               iter = ippNextAttribute (media_size_collection))
-            {
-              if (g_strcmp0 (ippGetName (iter), "x-dimension") == 0 &&
-                  ippGetValueTag (iter) == IPP_TAG_INTEGER)
-                {
-                  media_size->x_dimension = ippGetInteger (iter, 0) / 100.0;
-                  number_of_dimensions++;
-                }
-              else if (g_strcmp0 (ippGetName (iter), "y-dimension") == 0 &&
-                  ippGetValueTag (iter) == IPP_TAG_INTEGER)
-                {
-                  media_size->y_dimension = ippGetInteger (iter, 0) / 100.0;
-                  number_of_dimensions++;
-                }
-            }
-
-          if (number_of_dimensions == 2)
-            info->media_size_supported = g_list_prepend (info->media_size_supported, media_size);
-          else
-            g_free (media_size);
-        }
-
-      info->media_size_supported = g_list_reverse (info->media_size_supported);
-    }
-  else if (g_strcmp0 (ippGetName (attr), "output-bin-default") == 0)
-    {
-      info->output_bin_default = g_strdup (ippGetString (attr, 0, NULL));
-    }
-  else if (g_strcmp0 (ippGetName (attr), "output-bin-supported") == 0)
-    {
-      for (i = 0; i < ippGetCount (attr); i++)
-        info->output_bin_supported = g_list_prepend (info->output_bin_supported, g_strdup (ippGetString (attr, i, NULL)));
-
-      info->output_bin_supported = g_list_reverse (info->output_bin_supported);
-    }
-  else if (g_strcmp0 (ippGetName (attr), "device-uri") == 0)
-    {
-      info->original_device_uri = g_strdup (ippGetString (attr, 0, NULL));
-    }
-  else
-    {
-      GTK_NOTE (PRINTING,
-               g_print ("CUPS Backend: Attribute %s ignored\n", ippGetName (attr)));
-    }
-}
-
-static GtkPrinter*
-cups_create_printer (GtkPrintBackendCups *cups_backend,
-                    PrinterSetupInfo *info)
-{
-  GtkPrinterCups *cups_printer;
-  GtkPrinter *printer;
-  GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
-  char uri[HTTP_MAX_URI];      /* Printer URI */
-  char method[HTTP_MAX_URI];   /* Method/scheme name */
-  char username[HTTP_MAX_URI]; /* Username:password */
-  char hostname[HTTP_MAX_URI]; /* Hostname */
-  char resource[HTTP_MAX_URI]; /* Resource name */
-  int  port;                   /* Port number */
-  char *cups_server;            /* CUPS server */
-
-#ifdef HAVE_COLORD
-#ifdef HAVE_CUPS_API_1_6
-  if (info->avahi_printer)
-    cups_printer = gtk_printer_cups_new (info->printer_name,
-                                        backend,
-                                        NULL);
-  else
-#endif
-    cups_printer = gtk_printer_cups_new (info->printer_name,
-                                        backend,
-                                        cups_backend->colord_client);
-#else
-  cups_printer = gtk_printer_cups_new (info->printer_name, backend, NULL);
-#endif
-
-  cups_printer->device_uri = g_strdup_printf ("/printers/%s",
-                                             info->printer_name);
-
-  /* Check to see if we are looking at a class */
-  if (info->member_uris)
-    {
-      cups_printer->printer_uri = g_strdup (info->member_uris);
-      /* TODO if member_uris is a class we need to recursivly find a printer */
-      GTK_NOTE (PRINTING,
-               g_print ("CUPS Backend: Found class with printer %s\n",
-                        info->member_uris));
-    }
-  else
-    {
-      cups_printer->printer_uri = g_strdup (info->printer_uri);
-      GTK_NOTE (PRINTING,
-               g_print ("CUPS Backend: Found printer %s\n",
-                        info->printer_uri));
-    }
-
-  httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri,
-                  method, sizeof (method),
-                  username, sizeof (username),
-                  hostname, sizeof (hostname),
-                  &port,
-                  resource, sizeof (resource));
-
-  if (strncmp (resource, "/printers/", 10) == 0)
-    {
-      cups_printer->ppd_name = g_strdup (resource + 10);
-      GTK_NOTE (PRINTING,
-               g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, info->printer_name));
-    }
-
-  gethostname (uri, sizeof (uri));
-  cups_server = g_strdup (cupsServer());
-
-  if (strcasecmp (uri, hostname) == 0)
-    strcpy (hostname, "localhost");
-
-  /* if the cups server is local and listening at a unix domain socket
-   * then use the socket connection
-   */
-  if ((strstr (hostname, "localhost") != NULL) &&
-      (cups_server[0] == '/'))
-    strcpy (hostname, cups_server);
-
-  g_free (cups_server);
-
-  cups_printer->default_cover_before = g_strdup (info->default_cover_before);
-  cups_printer->default_cover_after = g_strdup (info->default_cover_after);
-  cups_printer->original_device_uri = g_strdup (info->original_device_uri);
-
-  if (info->default_number_up > 0)
-    cups_printer->default_number_up = info->default_number_up;
-
-  cups_printer->hostname = g_strdup (hostname);
-  cups_printer->port = port;
-
-  cups_printer->auth_info_required = g_strdupv (info->auth_info_required);
-  g_strfreev (info->auth_info_required);
-
-  printer = GTK_PRINTER (cups_printer);
-
-  if (cups_backend->default_printer != NULL &&
-      strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
-    gtk_printer_set_is_default (printer, TRUE);
-
-#ifdef HAVE_CUPS_API_1_6
-  cups_printer->avahi_browsed = info->avahi_printer;
-#endif
-
-  gtk_print_backend_add_printer (backend, printer);
-  return printer;
-}
-
-static void
-set_printer_icon_name_from_info (GtkPrinter       *printer,
-                                 PrinterSetupInfo *info)
-{
-  /* Set printer icon according to importance
-     (none, report, warning, error - report is omitted). */
-  if (info->reason_level == GTK_PRINTER_STATE_LEVEL_ERROR)
-    gtk_printer_set_icon_name (printer, "printer-error");
-  else if (info->reason_level == GTK_PRINTER_STATE_LEVEL_WARNING)
-    gtk_printer_set_icon_name (printer, "printer-warning");
-  else if (gtk_printer_is_paused (printer))
-    gtk_printer_set_icon_name (printer, "printer-paused");
-  else
-    gtk_printer_set_icon_name (printer, "printer");
-}
-
-static gchar *
-get_reason_msg_desc (guint i,
-                     const gchar *printer_name)
-{
-  gchar *reason_msg_desc;
-
-  /* The numbers must match the indices in the printer_messages array */
-  switch (i)
-    {
-      case 0:
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on toner."),
-                                           printer_name);
-        break;
-      case 1:
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” has no toner left."),
-                                           printer_name);
-        break;
-      case 2:
-        /* Translators: "Developer" like on photo development context */
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on developer."),
-                                           printer_name);
-        break;
-      case 3:
-        /* Translators: "Developer" like on photo development context */
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of developer."),
-                                           printer_name);
-        break;
-      case 4:
-        /* Translators: "marker" is one color bin of the printer */
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on at least one marker supply."),
-                                           printer_name);
-        break;
-      case 5:
-        /* Translators: "marker" is one color bin of the printer */
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of at least one marker supply."),
-                                           printer_name);
-        break;
-      case 6:
-        reason_msg_desc = g_strdup_printf (_("The cover is open on printer “%s”."),
-                                           printer_name);
-        break;
-      case 7:
-        reason_msg_desc = g_strdup_printf (_("The door is open on printer “%s”."),
-                                           printer_name);
-        break;
-      case 8:
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on paper."),
-                                           printer_name);
-        break;
-      case 9:
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of paper."),
-                                           printer_name);
-        break;
-      case 10:
-        reason_msg_desc = g_strdup_printf (_("Printer “%s” is currently offline."),
-                                           printer_name);
-        break;
-      case 11:
-        reason_msg_desc = g_strdup_printf (_("There is a problem on printer “%s”."),
-                                           printer_name);
-        break;
-      default:
-        g_assert_not_reached ();
-    }
-
-  return reason_msg_desc;
-}
-
-static void
-set_info_state_message (PrinterSetupInfo *info)
-{
-  gint i;
-
-  if (info->state_msg == NULL || strlen (info->state_msg) == 0)
-    {
-      gchar *tmp_msg2 = NULL;
-      if (info->is_paused && !info->is_accepting_jobs)
-        /* Translators: this is a printer status. */
-        tmp_msg2 = g_strdup ( _("Paused; Rejecting Jobs"));
-      if (info->is_paused && info->is_accepting_jobs)
-        /* Translators: this is a printer status. */
-        tmp_msg2 = g_strdup ( _("Paused"));
-      if (!info->is_paused && !info->is_accepting_jobs)
-        /* Translators: this is a printer status. */
-        tmp_msg2 = g_strdup ( _("Rejecting Jobs"));
-
-      if (tmp_msg2 != NULL)
-        {
-          g_free (info->state_msg);
-          info->state_msg = tmp_msg2;
-        }
-    }
-
-  /* Set description of the reason and combine it with printer-state-message. */
-  if (info->reason_msg)
-    {
-      gchar *reason_msg_desc = NULL;
-      gboolean found = FALSE;
-
-      for (i = 0; i < G_N_ELEMENTS (printer_messages); i++)
-        {
-          if (strncmp (info->reason_msg, printer_messages[i],
-                       strlen (printer_messages[i])) == 0)
-            {
-              reason_msg_desc = get_reason_msg_desc (i, info->printer_name);
-              found = TRUE;
-              break;
-            }
-        }
-
-      if (!found)
-        info->reason_level = GTK_PRINTER_STATE_LEVEL_NONE;
-
-      if (info->reason_level >= GTK_PRINTER_STATE_LEVEL_WARNING)
-        {
-          if (info->state_msg == NULL || info->state_msg[0] == '\0')
-            {
-              g_free (info->state_msg);
-              info->state_msg = reason_msg_desc;
-              reason_msg_desc = NULL;
-            }
-          else
-            {
-              gchar *tmp_msg = NULL;
-              /* Translators: this string connects multiple printer states together. */
-              tmp_msg = g_strjoin ( _("; "), info->state_msg,
-                                   reason_msg_desc, NULL);
-              g_free (info->state_msg);
-              info->state_msg = tmp_msg;
-            }
-        }
-
-      g_free (reason_msg_desc);
-    }
-}
-
-static void
-set_default_printer (GtkPrintBackendCups *cups_backend,
-                     const gchar         *default_printer_name)
-{
-  cups_backend->default_printer = g_strdup (default_printer_name);
-  cups_backend->got_default_printer = TRUE;
-
-  if (cups_backend->default_printer != NULL)
-    {
-      GtkPrinter *default_printer = NULL;
-      default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
-                                                        cups_backend->default_printer);
-      if (default_printer != NULL)
-        {
-          gtk_printer_set_is_default (default_printer, TRUE);
-          g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
-                                 "printer-status-changed", default_printer);
-        }
-    }
-}
-
-#ifdef HAVE_CUPS_API_1_6
-static void
-cups_request_printer_info_cb (GtkPrintBackendCups *cups_backend,
-                              GtkCupsResult       *result,
-                              gpointer             user_data)
-{
-  PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
-  GtkPrintBackend  *backend = GTK_PRINT_BACKEND (cups_backend);
-  ipp_attribute_t  *attr;
-  GtkPrinter       *printer;
-  gboolean          status_changed = FALSE;
-  ipp_t            *response;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  if (gtk_cups_result_is_error (result))
-    {
-      GTK_NOTE (PRINTING,
-                g_warning ("CUPS Backend: Error getting printer info: %s %d %d",
-                           gtk_cups_result_get_error_string (result),
-                           gtk_cups_result_get_error_type (result),
-                           gtk_cups_result_get_error_code (result)));
-
-      goto done;
-    }
-
-  response = gtk_cups_result_get_response (result);
-  attr = ippFirstAttribute (response);
-  while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
-    attr = ippNextAttribute (response);
-
-  if (attr)
-    {
-      while (attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
-        {
-          cups_printer_handle_attribute (cups_backend, attr, info);
-          attr = ippNextAttribute (response);
-        }
-
-      if (info->printer_name && info->printer_uri)
-        {
-          set_info_state_message (info);
-
-          printer = gtk_print_backend_find_printer (backend, info->printer_name);
-          if (printer != NULL)
-            g_object_ref (printer);
-          else
-            goto done;
-
-          if (info->got_printer_type &&
-              info->default_printer &&
-              cups_backend->avahi_default_printer == NULL)
-            cups_backend->avahi_default_printer = g_strdup (info->printer_name);
-
-          gtk_printer_set_is_paused (printer, info->is_paused);
-          gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
-
-          GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
-          GTK_PRINTER_CUPS (printer)->state = info->state;
-          GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
-          GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
-          GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
-          GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
-          GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
-          GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
-          GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
-          status_changed = gtk_printer_set_job_count (printer, info->job_count);
-          status_changed |= gtk_printer_set_location (printer, info->location);
-          status_changed |= gtk_printer_set_description (printer, info->description);
-          status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
-          status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
-
-          set_printer_icon_name_from_info (printer, info);
-
-          GTK_PRINTER_CUPS (printer)->media_default = info->media_default;
-          GTK_PRINTER_CUPS (printer)->media_supported = info->media_supported;
-          GTK_PRINTER_CUPS (printer)->media_size_supported = info->media_size_supported;
-          if (info->media_margin_default_set)
-            {
-              GTK_PRINTER_CUPS (printer)->media_margin_default_set = TRUE;
-              GTK_PRINTER_CUPS (printer)->media_bottom_margin_default = info->media_bottom_margin_default;
-              GTK_PRINTER_CUPS (printer)->media_top_margin_default = info->media_top_margin_default;
-              GTK_PRINTER_CUPS (printer)->media_left_margin_default = info->media_left_margin_default;
-              GTK_PRINTER_CUPS (printer)->media_right_margin_default = info->media_right_margin_default;
-            }
-          GTK_PRINTER_CUPS (printer)->sides_default = info->sides_default;
-          GTK_PRINTER_CUPS (printer)->sides_supported = info->sides_supported;
-          GTK_PRINTER_CUPS (printer)->output_bin_default = info->output_bin_default;
-          GTK_PRINTER_CUPS (printer)->output_bin_supported = info->output_bin_supported;
-
-          gtk_printer_set_has_details (printer, TRUE);
-          g_signal_emit_by_name (printer, "details-acquired", TRUE);
-
-          if (status_changed)
-            g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
-                                   "printer-status-changed", printer);
-
-          /* The ref is held by GtkPrintBackend, in add_printer() */
-          g_object_unref (printer);
-        }
-    }
-
-done:
-  if (!cups_backend->got_default_printer &&
-      gtk_print_backend_printer_list_is_done (backend) &&
-      cups_backend->avahi_default_printer != NULL)
-    {
-      set_default_printer (cups_backend, cups_backend->avahi_default_printer);
-    }
-
-  printer_setup_info_free (info);
-}
-
-static void
-cups_request_printer_info (const gchar         *printer_uri,
-                           const gchar         *host,
-                           gint                 port,
-                           GtkPrintBackendCups *backend)
-{
-  GtkCupsRequest *request;
-  http_t         *http;
-
-  http = httpConnect (host, port);
-  if (http)
-    {
-      request = gtk_cups_request_new_with_username (http,
-                                                    GTK_CUPS_POST,
-                                                    IPP_GET_PRINTER_ATTRIBUTES,
-                                                    NULL,
-                                                    NULL,
-                                                    NULL,
-                                                    backend->username);
-
-      gtk_cups_request_set_ipp_version (request, 1, 1);
-
-      gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
-                                       "printer-uri", NULL, printer_uri);
-
-      gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                                        "requested-attributes", G_N_ELEMENTS (printer_attrs_detailed),
-                                        NULL, printer_attrs_detailed);
-
-      cups_request_execute (backend,
-                            request,
-                            (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
-                            http,
-                            (GDestroyNotify) httpClose);
-    }
-}
-
-typedef struct
-{
-  gchar               *printer_uri;
-  gchar               *location;
-  gchar               *host;
-  gint                 port;
-  gchar               *printer_name;
-  gchar               *name;
-  gboolean             got_printer_type;
-  guint                printer_type;
-  gboolean             got_printer_state;
-  guint                printer_state;
-  gchar               *type;
-  gchar               *domain;
-  gchar               *UUID;
-  GtkPrintBackendCups *backend;
-} AvahiConnectionTestData;
-
-static GtkPrinter *
-find_printer_by_uuid (GtkPrintBackendCups *backend,
-                      const gchar         *UUID)
-{
-  GtkPrinterCups *printer;
-  GtkPrinter     *result = NULL;
-  GList          *printers;
-  GList          *iter;
-  gchar          *printer_uuid;
-
-  printers = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
-  for (iter = printers; iter != NULL; iter = iter->next)
-    {
-      printer = GTK_PRINTER_CUPS (iter->data);
-      if (printer->original_device_uri != NULL)
-        {
-          printer_uuid = g_strrstr (printer->original_device_uri, "uuid=");
-          if (printer_uuid != NULL && strlen (printer_uuid) >= 41)
-            {
-              printer_uuid += 5;
-              printer_uuid = g_strndup (printer_uuid, 36);
-
-#if GLIB_CHECK_VERSION(2, 52, 0)
-              if (g_uuid_string_is_valid (printer_uuid))
-#endif
-                {
-                  if (g_strcmp0 (printer_uuid, UUID) == 0)
-                    {
-                      result = GTK_PRINTER (printer);
-                      g_free (printer_uuid);
-                      break;
-                    }
-                }
-
-              g_free (printer_uuid);
-            }
-        }
-    }
-
-  g_list_free (printers);
-
-  return result;
-}
-
-/*
- *  Create new GtkPrinter from informations included in TXT records.
- */
-static void
-create_cups_printer_from_avahi_data (AvahiConnectionTestData *data)
-{
-  PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
-  GtkPrinter       *printer;
-
-  info->avahi_printer = TRUE;
-  info->printer_name = data->printer_name;
-  info->printer_uri = data->printer_uri;
-
-  if (data->got_printer_state)
-    {
-      info->state = data->printer_state;
-      info->is_paused = info->state == IPP_PRINTER_STOPPED;
-    }
-
-  info->got_printer_type = data->got_printer_type;
-  if (data->got_printer_type)
-    {
-      if (data->printer_type & CUPS_PRINTER_DEFAULT)
-        info->default_printer = TRUE;
-      else
-        info->default_printer = FALSE;
-
-      if (data->printer_type & CUPS_PRINTER_REMOTE)
-        info->remote_printer = TRUE;
-      else
-        info->remote_printer = FALSE;
-
-      if (data->printer_type & CUPS_PRINTER_REJECTING)
-        info->is_accepting_jobs = FALSE;
-      else
-        info->is_accepting_jobs = TRUE;
-
-      if (info->default_printer &&
-          data->backend->avahi_default_printer == NULL)
-        data->backend->avahi_default_printer = g_strdup (info->printer_name);
-    }
-
-  set_info_state_message (info);
-
-  printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (data->backend), data->printer_name);
-
-  if (printer == NULL && data->UUID != NULL)
-    printer = find_printer_by_uuid (data->backend, data->UUID);
-
-  if (printer == NULL)
-    {
-      printer = cups_create_printer (data->backend, info);
-
-      if (data->got_printer_type)
-        {
-          gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
-          GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
-
-          if (info->default_printer &&
-              data->backend->avahi_default_printer == NULL)
-            data->backend->avahi_default_printer = g_strdup (info->printer_name);
-        }
-
-      if (data->got_printer_state)
-        GTK_PRINTER_CUPS (printer)->state = info->state;
-
-      GTK_PRINTER_CUPS (printer)->avahi_name = g_strdup (data->name);
-      GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (data->type);
-      GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (data->domain);
-      g_free (GTK_PRINTER_CUPS (printer)->hostname);
-      GTK_PRINTER_CUPS (printer)->hostname = g_strdup (data->host);
-      GTK_PRINTER_CUPS (printer)->port = data->port;
-      gtk_printer_set_location (printer, data->location);
-      gtk_printer_set_state_message (printer, info->state_msg);
-
-      set_printer_icon_name_from_info (printer, info);
-
-      if (!gtk_printer_is_active (printer))
-        gtk_printer_set_is_active (printer, TRUE);
-
-      g_signal_emit_by_name (data->backend, "printer-added", printer);
-      gtk_printer_set_is_new (printer, FALSE);
-      g_signal_emit_by_name (data->backend, "printer-list-changed");
-
-      if (!data->backend->got_default_printer &&
-          gtk_print_backend_printer_list_is_done (GTK_PRINT_BACKEND (data->backend)) &&
-          data->backend->avahi_default_printer != NULL)
-        set_default_printer (data->backend, data->backend->avahi_default_printer);
-
-      /* The ref is held by GtkPrintBackend, in add_printer() */
-      g_object_unref (printer);
-    }
-
-  printer_setup_info_free (info);
-}
-
-static void
-avahi_connection_test_cb (GObject      *source_object,
-                          GAsyncResult *res,
-                          gpointer      user_data)
-{
-  AvahiConnectionTestData *data = (AvahiConnectionTestData *) user_data;
-  GSocketConnection       *connection;
-
-  connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
-                                                       res,
-                                                       NULL);
-  g_object_unref (source_object);
-
-  if (connection != NULL)
-    {
-      g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
-      g_object_unref (connection);
-
-      create_cups_printer_from_avahi_data (data);
-    }
-
-  g_free (data->printer_uri);
-  g_free (data->location);
-  g_free (data->host);
-  g_free (data->printer_name);
-  g_free (data->name);
-  g_free (data->type);
-  g_free (data->domain);
-  g_free (data);
-}
-
-gboolean
-avahi_txt_get_key_value_pair (const gchar  *entry,
-                              gchar       **key,
-                              gchar       **value)
-{
-  const gchar *equal_sign;
-
-  *key = NULL;
-  *value = NULL;
-
-  if (entry != NULL)
-    {
-      /* See RFC 6763 section 6.3 */
-      equal_sign = strstr (entry, "=");
-
-      if (equal_sign != NULL)
-        {
-          *key = g_strndup (entry, equal_sign - entry);
-          *value = g_strdup (equal_sign + 1);
-
-          return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-static void
-avahi_service_resolver_cb (GObject      *source_object,
-                           GAsyncResult *res,
-                           gpointer      user_data)
-{
-  AvahiConnectionTestData *data;
-  GtkPrintBackendCups     *backend;
-  const gchar             *name;
-  const gchar             *host;
-  const gchar             *type;
-  const gchar             *domain;
-  const gchar             *address;
-  const gchar             *protocol_string;
-  GVariant                *output;
-  GVariant                *txt;
-  GVariant                *child;
-  guint32                  flags;
-  guint16                  port;
-  GError                  *error = NULL;
-  gchar                   *queue_name = NULL;
-  gchar                   *tmp;
-  gchar                   *printer_name;
-  gchar                   *endptr;
-  gchar                   *key;
-  gchar                   *value;
-  gsize                    length;
-  gint                     interface;
-  gint                     protocol;
-  gint                     aprotocol;
-  gint                     i;
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output)
-    {
-      backend = GTK_PRINT_BACKEND_CUPS (user_data);
-
-      g_variant_get (output, "(ii&s&s&s&si&sq@aayu)",
-                     &interface,
-                     &protocol,
-                     &name,
-                     &type,
-                     &domain,
-                     &host,
-                     &aprotocol,
-                     &address,
-                     &port,
-                     &txt,
-                     &flags);
-
-      data = g_new0 (AvahiConnectionTestData, 1);
-
-      for (i = 0; i < g_variant_n_children (txt); i++)
-        {
-          child = g_variant_get_child_value (txt, i);
-
-          length = g_variant_get_size (child);
-          if (length > 0)
-            {
-              tmp = g_strndup (g_variant_get_data (child), length);
-              g_variant_unref (child);
-
-              if (!avahi_txt_get_key_value_pair (tmp, &key, &value))
-                {
-                  g_free (tmp);
-                  continue;
-                }
-
-              if (g_strcmp0 (key, "rp") == 0)
-                {
-                  queue_name = g_strdup (value);
-
-                  printer_name = g_strrstr (queue_name, "/");
-                  if (printer_name != NULL)
-                    data->printer_name = g_strdup (printer_name + 1);
-                  else
-                    data->printer_name = g_strdup (queue_name);
-                }
-              else if (g_strcmp0 (key, "note") == 0)
-                {
-                  data->location = g_strdup (value);
-                }
-              else if (g_strcmp0 (key, "printer-type") == 0)
-                {
-                  endptr = NULL;
-                  data->printer_type = g_ascii_strtoull (value, &endptr, 16);
-                  if (data->printer_type != 0 || endptr != value)
-                    data->got_printer_type = TRUE;
-                }
-              else if (g_strcmp0 (key, "printer-state") == 0)
-                {
-                  endptr = NULL;
-                  data->printer_state = g_ascii_strtoull (value, &endptr, 10);
-                  if (data->printer_state != 0 || endptr != value)
-                    data->got_printer_state = TRUE;
-                }
-              else if (g_strcmp0 (key, "UUID") == 0)
-                {
-                  if (*value != '\0')
-                    data->UUID = g_strdup (value);
-                }
-
-              g_clear_pointer (&key, g_free);
-              g_clear_pointer (&value, g_free);
-              g_free (tmp);
-            }
-          else
-            {
-              g_variant_unref (child);
-            }
-        }
-
-      if (queue_name)
-        {
-          if (g_strcmp0 (type, "_ipp._tcp") == 0)
-            protocol_string = "ipp";
-          else
-            protocol_string = "ipps";
-
-          if (aprotocol == AVAHI_PROTO_INET6)
-            data->printer_uri = g_strdup_printf ("%s://[%s]:%u/%s", protocol_string, address, port, queue_name);
-          else
-            data->printer_uri = g_strdup_printf ("%s://%s:%u/%s", protocol_string, address, port, queue_name);
-
-          data->host = g_strdup (address);
-          data->port = port;
-
-          data->name = g_strdup (name);
-          data->type = g_strdup (type);
-          data->domain = g_strdup (domain);
-          data->backend = backend;
-
-          /* It can happen that the address is not reachable */
-          g_socket_client_connect_to_host_async (g_socket_client_new (),
-                                                 address,
-                                                 port,
-                                                 backend->avahi_cancellable,
-                                                 avahi_connection_test_cb,
-                                                 data);
-          g_free (queue_name);
-        }
-      else
-        {
-          g_free (data->printer_name);
-          g_free (data->location);
-          g_free (data);
-        }
-
-      g_variant_unref (txt);
-      g_variant_unref (output);
-    }
-  else
-    {
-      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        g_warning ("%s", error->message);
-      g_error_free (error);
-    }
-}
-
-static void
-avahi_service_browser_signal_handler (GDBusConnection *connection,
-                                      const gchar     *sender_name,
-                                      const gchar     *object_path,
-                                      const gchar     *interface_name,
-                                      const gchar     *signal_name,
-                                      GVariant        *parameters,
-                                      gpointer         user_data)
-{
-  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
-  gchar               *name;
-  gchar               *type;
-  gchar               *domain;
-  guint                flags;
-  gint                 interface;
-  gint                 protocol;
-
-  if (g_strcmp0 (signal_name, "ItemNew") == 0)
-    {
-      g_variant_get (parameters, "(ii&s&s&su)",
-                     &interface,
-                     &protocol,
-                     &name,
-                     &type,
-                     &domain,
-                     &flags);
-
-      if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
-          g_strcmp0 (type, "_ipps._tcp") == 0)
-        {
-          g_dbus_connection_call (backend->dbus_connection,
-                                  AVAHI_BUS,
-                                  "/",
-                                  AVAHI_SERVER_IFACE,
-                                  "ResolveService",
-                                  g_variant_new ("(iisssiu)",
-                                                 interface,
-                                                 protocol,
-                                                 name,
-                                                 type,
-                                                 domain,
-                                                 AVAHI_PROTO_UNSPEC,
-                                                 0),
-                                  G_VARIANT_TYPE ("(iissssisqaayu)"),
-                                  G_DBUS_CALL_FLAGS_NONE,
-                                  -1,
-                                  backend->avahi_cancellable,
-                                  avahi_service_resolver_cb,
-                                  user_data);
-        }
-    }
-  else if (g_strcmp0 (signal_name, "ItemRemove") == 0)
-    {
-      g_variant_get (parameters, "(ii&s&s&su)",
-                     &interface,
-                     &protocol,
-                     &name,
-                     &type,
-                     &domain,
-                     &flags);
-
-      if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
-          g_strcmp0 (type, "_ipps._tcp") == 0)
-        {
-          GtkPrinterCups *printer;
-          GList          *list;
-          GList          *iter;
-
-          list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
-          for (iter = list; iter; iter = iter->next)
-            {
-              printer = GTK_PRINTER_CUPS (iter->data);
-              if (g_strcmp0 (printer->avahi_name, name) == 0 &&
-                  g_strcmp0 (printer->avahi_type, type) == 0 &&
-                  g_strcmp0 (printer->avahi_domain, domain) == 0)
-                {
-                  if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
-                                 backend->avahi_default_printer) == 0)
-                    g_clear_pointer (&backend->avahi_default_printer, g_free);
-
-                  g_signal_emit_by_name (backend, "printer-removed", printer);
-                  gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
-                                                    GTK_PRINTER (printer));
-                  g_signal_emit_by_name (backend, "printer-list-changed");
-                  break;
-                }
-            }
-
-          g_list_free (list);
-        }
-    }
-}
-
-static void
-avahi_service_browser_new_cb (GObject      *source_object,
-                              GAsyncResult *res,
-                              gpointer      user_data)
-{
-  GtkPrintBackendCups *cups_backend;
-  GVariant            *output;
-  GError              *error = NULL;
-  gint                 i;
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  if (output)
-    {
-      cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
-      i = cups_backend->avahi_service_browser_paths[0] ? 1 : 0;
-
-      g_variant_get (output, "(o)", &cups_backend->avahi_service_browser_paths[i]);
-
-      cups_backend->avahi_service_browser_subscription_ids[i] =
-        g_dbus_connection_signal_subscribe (cups_backend->dbus_connection,
-                                            NULL,
-                                            AVAHI_SERVICE_BROWSER_IFACE,
-                                            NULL,
-                                            cups_backend->avahi_service_browser_paths[i],
-                                            NULL,
-                                            G_DBUS_SIGNAL_FLAGS_NONE,
-                                            avahi_service_browser_signal_handler,
-                                            user_data,
-                                            NULL);
-
-      /*
-       * The general subscription for all service browsers is not needed
-       * now because we are already subscribed to service browsers
-       * specific to _ipp._tcp and _ipps._tcp services.
-       */
-      if (cups_backend->avahi_service_browser_paths[0] &&
-          cups_backend->avahi_service_browser_paths[1] &&
-          cups_backend->avahi_service_browser_subscription_id > 0)
-        {
-          g_dbus_connection_signal_unsubscribe (cups_backend->dbus_connection,
-                                                cups_backend->avahi_service_browser_subscription_id);
-          cups_backend->avahi_service_browser_subscription_id = 0;
-        }
-
-      g_variant_unref (output);
-    }
-  else
-    {
-      /*
-       * The creation of ServiceBrowser fails with G_IO_ERROR_DBUS_ERROR
-       * if Avahi is disabled.
-       */
-      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) &&
-          !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        g_warning ("%s", error->message);
-      g_error_free (error);
-    }
-}
-
-static void
-avahi_create_browsers (GObject      *source_object,
-                       GAsyncResult *res,
-                       gpointer      user_data)
-{
-  GDBusConnection     *dbus_connection;
-  GtkPrintBackendCups *cups_backend;
-  GError              *error = NULL;
-
-  dbus_connection = g_bus_get_finish (res, &error);
-  if (!dbus_connection)
-    {
-      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        g_warning ("Couldn't connect to D-Bus system bus, %s", error->message);
-
-      g_error_free (error);
-      return;
-    }
-
-  cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
-  cups_backend->dbus_connection = dbus_connection;
-
-  /*
-   * We need to subscribe to signals of service browser before
-   * we actually create it because it starts to emit them right
-   * after its creation.
-   */
-  cups_backend->avahi_service_browser_subscription_id =
-    g_dbus_connection_signal_subscribe  (cups_backend->dbus_connection,
-                                         NULL,
-                                         AVAHI_SERVICE_BROWSER_IFACE,
-                                         NULL,
-                                         NULL,
-                                         NULL,
-                                         G_DBUS_SIGNAL_FLAGS_NONE,
-                                         avahi_service_browser_signal_handler,
-                                         cups_backend,
-                                         NULL);
-
-  /*
-   * Create service browsers for _ipp._tcp and _ipps._tcp services.
-   */
-  g_dbus_connection_call (cups_backend->dbus_connection,
-                          AVAHI_BUS,
-                          "/",
-                          AVAHI_SERVER_IFACE,
-                          "ServiceBrowserNew",
-                          g_variant_new ("(iissu)",
-                                         AVAHI_IF_UNSPEC,
-                                         AVAHI_PROTO_UNSPEC,
-                                         "_ipp._tcp",
-                                         "",
-                                         0),
-                          G_VARIANT_TYPE ("(o)"),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          -1,
-                          cups_backend->avahi_cancellable,
-                          avahi_service_browser_new_cb,
-                          cups_backend);
-
-  g_dbus_connection_call (cups_backend->dbus_connection,
-                          AVAHI_BUS,
-                          "/",
-                          AVAHI_SERVER_IFACE,
-                          "ServiceBrowserNew",
-                          g_variant_new ("(iissu)",
-                                         AVAHI_IF_UNSPEC,
-                                         AVAHI_PROTO_UNSPEC,
-                                         "_ipps._tcp",
-                                         "",
-                                         0),
-                          G_VARIANT_TYPE ("(o)"),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          -1,
-                          cups_backend->avahi_cancellable,
-                          avahi_service_browser_new_cb,
-                          cups_backend);
-}
-
-static void
-avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
-{
-  cups_backend->avahi_cancellable = g_cancellable_new ();
-  g_bus_get (G_BUS_TYPE_SYSTEM, cups_backend->avahi_cancellable, avahi_create_browsers, cups_backend);
-}
-#endif
-
-static void
-cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
-                              GtkCupsResult       *result,
-                              gpointer             user_data)
-{
-  GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
-  ipp_attribute_t *attr;
-  ipp_t *response;
-  gboolean list_has_changed;
-  GList *removed_printer_checklist;
-  gchar *remote_default_printer = NULL;
-  GList *iter;
-
-  list_has_changed = FALSE;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  cups_backend->list_printers_pending = FALSE;
-
-  if (gtk_cups_result_is_error (result))
-    {
-      GTK_NOTE (PRINTING,
-                g_warning ("CUPS Backend: Error getting printer list: %s %d %d",
-                           gtk_cups_result_get_error_string (result),
-                           gtk_cups_result_get_error_type (result),
-                           gtk_cups_result_get_error_code (result)));
-
-      if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
-          gtk_cups_result_get_error_code (result) == 1)
-        {
-          /* Canceled by user, stop popping up more password dialogs */
-          if (cups_backend->list_printers_poll > 0)
-            g_source_remove (cups_backend->list_printers_poll);
-          cups_backend->list_printers_poll = 0;
-          cups_backend->list_printers_attempts = 0;
-        }
-
-      goto done;
-    }
-
-  /* Gather the names of the printers in the current queue
-   * so we may check to see if they were removed
-   */
-  removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
-
-  response = gtk_cups_result_get_response (result);
-#ifdef HAVE_CUPS_API_1_6
-  for (attr = ippFirstAttribute (response); attr != NULL;
-       attr = ippNextAttribute (response))
-    {
-      GtkPrinter *printer;
-      gboolean status_changed = FALSE;
-      GList *node;
-      PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
-
-      /* Skip leading attributes until we hit a printer...
-       */
-      while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
-        attr = ippNextAttribute (response);
-
-      if (attr == NULL)
-        break;
-      while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
-      {
-       cups_printer_handle_attribute (cups_backend, attr, info);
-        attr = ippNextAttribute (response);
-      }
-#else
-  for (attr = response->attrs; attr != NULL; attr = attr->next)
-    {
-      GtkPrinter *printer;
-      gboolean status_changed = FALSE;
-      GList *node;
-      PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
-      info->default_number_up = 1;
-
-      /* Skip leading attributes until we hit a printer...
-       */
-      while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
-        attr = attr->next;
-
-      if (attr == NULL)
-        break;
-      while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
-      {
-       cups_printer_handle_attribute (cups_backend, attr, info);
-        attr = attr->next;
-      }
-#endif
-
-      if (info->printer_name == NULL ||
-         (info->printer_uri == NULL && info->member_uris == NULL))
-      {
-        if (attr == NULL)
-         break;
-       else
-          continue;
-      }
-
-      if (info->got_printer_type)
-        {
-          if (info->default_printer && !cups_backend->got_default_printer)
-            {
-              if (!info->remote_printer)
-                {
-                  cups_backend->got_default_printer = TRUE;
-                  cups_backend->default_printer = g_strdup (info->printer_name);
-                }
-              else
-                {
-                  if (remote_default_printer == NULL)
-                    remote_default_printer = g_strdup (info->printer_name);
-                }
-            }
-        }
-      else
-        {
-          if (!cups_backend->got_default_printer)
-            cups_get_default_printer (cups_backend);
-        }
-
-      /* remove name from checklist if it was found */
-      node = g_list_find_custom (removed_printer_checklist,
-                                info->printer_name,
-                                (GCompareFunc) find_printer);
-      removed_printer_checklist = g_list_delete_link (removed_printer_checklist,
-                                                     node);
-
-      printer = gtk_print_backend_find_printer (backend, info->printer_name);
-      if (!printer)
-       {
-         printer = cups_create_printer (cups_backend, info);
-         list_has_changed = TRUE;
-       }
-
-      else
-       g_object_ref (printer);
-
-      GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
-
-      gtk_printer_set_is_paused (printer, info->is_paused);
-      gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
-
-      if (!gtk_printer_is_active (printer))
-        {
-         gtk_printer_set_is_active (printer, TRUE);
-         gtk_printer_set_is_new (printer, TRUE);
-          list_has_changed = TRUE;
-        }
-
-      if (gtk_printer_is_new (printer))
-        {
-         g_signal_emit_by_name (backend, "printer-added", printer);
-
-         gtk_printer_set_is_new (printer, FALSE);
-        }
-
-      GTK_PRINTER_CUPS (printer)->state = info->state;
-      GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
-      GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
-      GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
-      GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
-      GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
-      GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
-      GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
-      status_changed = gtk_printer_set_job_count (printer, info->job_count);
-      status_changed |= gtk_printer_set_location (printer, info->location);
-      status_changed |= gtk_printer_set_description (printer,
-                                                    info->description);
-
-      set_info_state_message (info);
-
-      status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
-      status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
-
-      set_printer_icon_name_from_info (printer, info);
-
-      if (status_changed)
-        g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
-                               "printer-status-changed", printer);
-
-      /* The ref is held by GtkPrintBackend, in add_printer() */
-      g_object_unref (printer);
-      printer_setup_info_free (info);
-
-      if (attr == NULL)
-        break;
-    }
-
-  /* look at the removed printers checklist and mark any printer
-     as inactive if it is in the list, emitting a printer_removed signal */
-  if (removed_printer_checklist != NULL)
-    {
-      for (iter = removed_printer_checklist; iter; iter = iter->next)
-        {
-#ifdef HAVE_CUPS_API_1_6
-          if (!GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
-#endif
-            {
-              mark_printer_inactive (GTK_PRINTER (iter->data), backend);
-              list_has_changed = TRUE;
-            }
-        }
-
-      g_list_free (removed_printer_checklist);
-    }
-
-done:
-  if (list_has_changed)
-    g_signal_emit_by_name (backend, "printer-list-changed");
-
-  gtk_print_backend_set_list_done (backend);
-
-  if (!cups_backend->got_default_printer && remote_default_printer != NULL)
-    {
-      set_default_printer (cups_backend, remote_default_printer);
-      g_free (remote_default_printer);
-    }
-
-#ifdef HAVE_CUPS_API_1_6
-  if (!cups_backend->got_default_printer && cups_backend->avahi_default_printer != NULL)
-    {
-      set_default_printer (cups_backend, cups_backend->avahi_default_printer);
-    }
-#endif
-}
-
-static void
-update_backend_status (GtkPrintBackendCups    *cups_backend,
-                       GtkCupsConnectionState  state)
-{
-  switch (state)
-    {
-    case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
-      g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
-      break;
-    case GTK_CUPS_CONNECTION_AVAILABLE:
-      g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
-      break;
-    default: ;
-    }
-}
-
-static gboolean
-cups_request_printer_list (GtkPrintBackendCups *cups_backend)
-{
-  GtkCupsConnectionState state;
-  GtkCupsRequest *request;
-
-  if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
-    return TRUE;
-
-  state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
-  update_backend_status (cups_backend, state);
-
-  if (cups_backend->list_printers_attempts == 60)
-    {
-      cups_backend->list_printers_attempts = -1;
-      if (cups_backend->list_printers_poll > 0)
-        g_source_remove (cups_backend->list_printers_poll);
-      cups_backend->list_printers_poll = g_timeout_add (200, (GSourceFunc) cups_request_printer_list, cups_backend);
-      g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk+] cups_request_printer_list");
-    }
-  else if (cups_backend->list_printers_attempts != -1)
-    cups_backend->list_printers_attempts++;
-
-  if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
-    return TRUE;
-  else
-    if (cups_backend->list_printers_attempts > 0)
-      cups_backend->list_printers_attempts = 60;
-
-  cups_backend->list_printers_pending = TRUE;
-
-  request = gtk_cups_request_new_with_username (NULL,
-                                                GTK_CUPS_POST,
-                                                CUPS_GET_PRINTERS,
-                                                NULL,
-                                                NULL,
-                                                NULL,
-                                                cups_backend->username);
-
-  gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-                                   "requested-attributes", G_N_ELEMENTS (printer_attrs),
-                                   NULL, printer_attrs);
-
-  cups_request_execute (cups_backend,
-                        request,
-                        (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
-                       request,
-                       NULL);
-
-  return TRUE;
-}
-
-static void
-cups_get_printer_list (GtkPrintBackend *backend)
-{
-  GtkPrintBackendCups *cups_backend;
-
-  cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
-
-  if (cups_backend->cups_connection_test == NULL)
-    cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
-
-  if (cups_backend->list_printers_poll == 0)
-    {
-      if (cups_request_printer_list (cups_backend))
-        {
-          cups_backend->list_printers_poll = g_timeout_add (50, (GSourceFunc) cups_request_printer_list, backend);
-          g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk+] cups_request_printer_list");
-        }
-
-#ifdef HAVE_CUPS_API_1_6
-      avahi_request_printer_list (cups_backend);
-#endif
-    }
-}
-
-typedef struct {
-  GtkPrinterCups *printer;
-  GIOChannel *ppd_io;
-  http_t *http;
-} GetPPDData;
-
-static void
-get_ppd_data_free (GetPPDData *data)
-{
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-  httpClose (data->http);
-  g_io_channel_unref (data->ppd_io);
-  g_object_unref (data->printer);
-  g_free (data);
-}
-
-static void
-cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
-                     GtkCupsResult       *result,
-                     GetPPDData          *data)
-{
-  GtkPrinter *printer;
-  struct stat data_info;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  printer = GTK_PRINTER (data->printer);
-  GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
-  print_backend->reading_ppds--;
-
-#ifndef HAVE_CUPS_API_1_6
-  if (gtk_cups_result_is_error (result))
-    {
-      gboolean success = FALSE;
-
-      /* If we get a 404 then it is just a raw printer without a ppd
-         and not an error. */
-      if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
-          (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
-        {
-          gtk_printer_set_has_details (printer, TRUE);
-          success = TRUE;
-        }
-
-      g_signal_emit_by_name (printer, "details-acquired", success);
-
-      return;
-    }
-#endif
-
-  if (!gtk_cups_result_is_error (result))
-    {
-      /* let ppdOpenFd take over the ownership of the open file */
-      g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
-      data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
-      ppdLocalize (data->printer->ppd_file);
-      ppdMarkDefaults (data->printer->ppd_file);
-    }
-
-#ifdef HAVE_CUPS_API_1_6
-  fstat (g_io_channel_unix_get_fd (data->ppd_io), &data_info);
-  /*
-   * Standalone Avahi printers and raw printers don't have PPD files or have
-   * empty PPD files. Try to get printer details via IPP.
-   * Always do this for Avahi printers.
-   */
-  if (data_info.st_size == 0 ||
-      GTK_PRINTER_CUPS (printer)->avahi_browsed ||
-      (gtk_cups_result_is_error (result) &&
-       ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
-         (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))))
-    {
-      cups_request_printer_info (GTK_PRINTER_CUPS (printer)->printer_uri,
-                                 GTK_PRINTER_CUPS (printer)->hostname,
-                                 GTK_PRINTER_CUPS (printer)->port,
-                                 GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer)));
-
-      return;
-    }
-#endif
-
-  gtk_printer_set_has_details (printer, TRUE);
-  g_signal_emit_by_name (printer, "details-acquired", TRUE);
-}
-
-static gboolean
-cups_request_ppd (GtkPrinter *printer)
-{
-  GError *error;
-  GtkPrintBackend *print_backend;
-  GtkPrinterCups *cups_printer;
-  GtkCupsRequest *request;
-  char *ppd_filename = NULL;
-  gchar *resource;
-  http_t *http;
-  GetPPDData *data;
-  int fd;
-
-  cups_printer = GTK_PRINTER_CUPS (printer);
-
-  error = NULL;
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %s\n", G_STRFUNC));
-
-  if (cups_printer->remote
-#ifdef HAVE_CUPS_API_1_6
-      && !cups_printer->avahi_browsed
-#endif
-      )
-    {
-      GtkCupsConnectionState state;
-
-      state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
-
-      if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
-        {
-          if (cups_printer->get_remote_ppd_attempts == 60)
-            {
-              cups_printer->get_remote_ppd_attempts = -1;
-              if (cups_printer->get_remote_ppd_poll > 0)
-                g_source_remove (cups_printer->get_remote_ppd_poll);
-              cups_printer->get_remote_ppd_poll = g_timeout_add (200, (GSourceFunc) cups_request_ppd, printer);
-              g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk+] cups_request_ppd");
-            }
-          else if (cups_printer->get_remote_ppd_attempts != -1)
-            cups_printer->get_remote_ppd_attempts++;
-
-          return TRUE;
-        }
-
-      gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
-      cups_printer->remote_cups_connection_test = NULL;
-      cups_printer->get_remote_ppd_poll = 0;
-      cups_printer->get_remote_ppd_attempts = 0;
-
-      if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
-        {
-          g_signal_emit_by_name (printer, "details-acquired", FALSE);
-          return FALSE;
-        }
-    }
-
-  http = httpConnectEncrypt (cups_printer->hostname,
-                            cups_printer->port,
-                            cupsEncryption ());
-
-  data = g_new0 (GetPPDData, 1);
-
-  fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
-                        &ppd_filename,
-                        &error);
-
-#ifdef G_ENABLE_DEBUG
-  /* If we are debugging printing don't delete the tmp files */
-  if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
-    unlink (ppd_filename);
-#else
-  unlink (ppd_filename);
-#endif /* G_ENABLE_DEBUG */
-
-  if (error != NULL)
-    {
-      GTK_NOTE (PRINTING,
-                g_warning ("CUPS Backend: Failed to create temp file, %s\n",
-                           error->message));
-      g_error_free (error);
-      httpClose (http);
-      g_free (ppd_filename);
-      g_free (data);
-
-      g_signal_emit_by_name (printer, "details-acquired", FALSE);
-      return FALSE;
-    }
-
-  data->http = http;
-  fchmod (fd, S_IRUSR | S_IWUSR);
-  data->ppd_io = g_io_channel_unix_new (fd);
-  g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
-  g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
-
-  data->printer = (GtkPrinterCups *) g_object_ref (printer);
-
-  resource = g_strdup_printf ("/printers/%s.ppd",
-                              gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
-
-  print_backend = gtk_printer_get_backend (printer);
-
-  request = gtk_cups_request_new_with_username (data->http,
-                                                GTK_CUPS_GET,
-                                                0,
-                                                data->ppd_io,
-                                                cups_printer->hostname,
-                                                resource,
-                                                GTK_PRINT_BACKEND_CUPS (print_backend)->username);
-
-  gtk_cups_request_set_ipp_version (request,
-                                    cups_printer->ipp_version_major,
-                                    cups_printer->ipp_version_minor);
-
-  GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
-
-
-  cups_printer->reading_ppd = TRUE;
-  GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
-
-  cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
-                        request,
-                        (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
-                        data,
-                        (GDestroyNotify)get_ppd_data_free);
-
-  g_free (resource);
-  g_free (ppd_filename);
-
-  return FALSE;
-}
-
-/* Ordering matters for default preference */
-static const char *lpoptions_locations[] = {
-  "/etc/cups/lpoptions",
-  ".lpoptions",
-  ".cups/lpoptions"
-};
-
-static void
-cups_parse_user_default_printer (const char  *filename,
-                                 char       **printer_name)
-{
-  FILE *fp;
-  char line[1024], *lineptr, *defname = NULL;
-
-  if ((fp = g_fopen (filename, "r")) == NULL)
-    return;
-
-  while (fgets (line, sizeof (line), fp) != NULL)
-    {
-      if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
-        continue;
-
-      lineptr = line + 8;
-      while (isspace (*lineptr))
-        lineptr++;
-
-      if (!*lineptr)
-        continue;
-
-      defname = lineptr;
-      while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
-        lineptr++;
-
-      *lineptr = '\0';
-
-      g_free (*printer_name);
-
-      *printer_name = g_strdup (defname);
-    }
-
-  fclose (fp);
-}
-
-static void
-cups_get_user_default_printer (char **printer_name)
-{
-  int i;
-
-  for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
-    {
-      if (g_path_is_absolute (lpoptions_locations[i]))
-        {
-          cups_parse_user_default_printer (lpoptions_locations[i],
-                                           printer_name);
-        }
-      else
-        {
-          char *filename;
-
-          filename = g_build_filename (g_get_home_dir (),
-                                       lpoptions_locations[i], NULL);
-          cups_parse_user_default_printer (filename, printer_name);
-          g_free (filename);
-        }
-    }
-}
-
-static int
-cups_parse_user_options (const char     *filename,
-                         const char     *printer_name,
-                         int             num_options,
-                         cups_option_t **options)
-{
-  FILE *fp;
-  gchar line[1024], *lineptr, *name;
-
-  if ((fp = g_fopen (filename, "r")) == NULL)
-    return num_options;
-
-  while (fgets (line, sizeof (line), fp) != NULL)
-    {
-      if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
-        lineptr = line + 4;
-      else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
-        lineptr = line + 7;
-      else
-        continue;
-
-      /* Skip leading whitespace */
-      while (isspace (*lineptr))
-        lineptr++;
-
-      if (!*lineptr)
-        continue;
-
-      name = lineptr;
-      while (!isspace (*lineptr) && *lineptr)
-        {
-          lineptr++;
-        }
-
-      if (!*lineptr)
-        continue;
-
-      *lineptr++ = '\0';
-
-      if (strcasecmp (name, printer_name) != 0)
-          continue;
-
-      /* We found our printer, parse the options */
-      num_options = cupsParseOptions (lineptr, num_options, options);
-    }
-
-  fclose (fp);
-
-  return num_options;
-}
-
-static int
-cups_get_user_options (const char     *printer_name,
-                       int             num_options,
-                       cups_option_t **options)
-{
-  int i;
-
-  for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
-    {
-      if (g_path_is_absolute (lpoptions_locations[i]))
-        {
-           num_options = cups_parse_user_options (lpoptions_locations[i],
-                                                  printer_name,
-                                                  num_options,
-                                                  options);
-        }
-      else
-        {
-          char *filename;
-
-          filename = g_build_filename (g_get_home_dir (),
-                                       lpoptions_locations[i], NULL);
-          num_options = cups_parse_user_options (filename, printer_name,
-                                                 num_options, options);
-          g_free (filename);
-        }
-    }
-
-  return num_options;
-}
-
-/* This function requests default printer from a CUPS server in regular intervals.
- * In the case of unreachable CUPS server the request is repeated later.
- * The default printer is not requested in the case of previous success.
- */
-static void
-cups_get_default_printer (GtkPrintBackendCups *backend)
-{
-  GtkPrintBackendCups *cups_backend;
-
-  cups_backend = backend;
-
-  if (cups_backend->cups_connection_test == NULL)
-    cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
-
-  if (cups_backend->default_printer_poll == 0)
-    {
-      if (cups_request_default_printer (cups_backend))
-        {
-          cups_backend->default_printer_poll = g_timeout_add (200, (GSourceFunc) cups_request_default_printer, backend);
-          g_source_set_name_by_id (cups_backend->default_printer_poll, "[gtk+] cups_request_default_printer");
-        }
-    }
-}
-
-/* This function gets default printer from local settings.*/
-static void
-cups_get_local_default_printer (GtkPrintBackendCups *backend)
-{
-  const char *str;
-  char *name = NULL;
-
-  if ((str = g_getenv ("LPDEST")) != NULL)
-    {
-      backend->default_printer = g_strdup (str);
-      backend->got_default_printer = TRUE;
-      return;
-    }
-  else if ((str = g_getenv ("PRINTER")) != NULL &&
-          strcmp (str, "lp") != 0)
-    {
-      backend->default_printer = g_strdup (str);
-      backend->got_default_printer = TRUE;
-      return;
-    }
-
-  /* Figure out user setting for default printer */
-  cups_get_user_default_printer (&name);
-  if (name != NULL)
-    {
-      backend->default_printer = name;
-      backend->got_default_printer = TRUE;
-      return;
-    }
-}
-
-static void
-cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
-                                GtkCupsResult       *result,
-                                gpointer             user_data)
-{
-  ipp_t *response;
-  ipp_attribute_t *attr;
-  GtkPrinter *printer;
-
-  if (gtk_cups_result_is_error (result))
-    {
-      if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
-          gtk_cups_result_get_error_code (result) == 1)
-        {
-          /* Canceled by user, stop popping up more password dialogs */
-          if (print_backend->list_printers_poll > 0)
-            g_source_remove (print_backend->list_printers_poll);
-          print_backend->list_printers_poll = 0;
-        }
-
-      return;
-    }
-
-  response = gtk_cups_result_get_response (result);
-
-  if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
-      print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
-
-  print_backend->got_default_printer = TRUE;
-
-  if (print_backend->default_printer != NULL)
-    {
-      printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
-      if (printer != NULL)
-        {
-          gtk_printer_set_is_default (printer, TRUE);
-          g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
-        }
-    }
-
-  /* Make sure to kick off get_printers if we are polling it,
-   * as we could have blocked this reading the default printer
-   */
-  if (print_backend->list_printers_poll != 0)
-    cups_request_printer_list (print_backend);
-}
-
-static gboolean
-cups_request_default_printer (GtkPrintBackendCups *print_backend)
-{
-  GtkCupsConnectionState state;
-  GtkCupsRequest *request;
-
-  state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
-  update_backend_status (print_backend, state);
-
-  if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
-    return TRUE;
-
-  request = gtk_cups_request_new_with_username (NULL,
-                                                GTK_CUPS_POST,
-                                                CUPS_GET_DEFAULT,
-                                                NULL,
-                                                NULL,
-                                                NULL,
-                                                print_backend->username);
-
-  cups_request_execute (print_backend,
-                        request,
-                        (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
-                       g_object_ref (print_backend),
-                       g_object_unref);
-
-  return FALSE;
-}
-
-static void
-cups_printer_request_details (GtkPrinter *printer)
-{
-  GtkPrinterCups *cups_printer;
-
-  cups_printer = GTK_PRINTER_CUPS (printer);
-  if (!cups_printer->reading_ppd &&
-      gtk_printer_cups_get_ppd (cups_printer) == NULL)
-    {
-      if (cups_printer->remote
-#ifdef HAVE_CUPS_API_1_6
-          && !cups_printer->avahi_browsed
-#endif
-          )
-        {
-          if (cups_printer->get_remote_ppd_poll == 0)
-            {
-              cups_printer->remote_cups_connection_test =
-                gtk_cups_connection_test_new (cups_printer->hostname,
-                                              cups_printer->port);
-
-              if (cups_request_ppd (printer))
-                {
-                  cups_printer->get_remote_ppd_poll = g_timeout_add (50, (GSourceFunc) cups_request_ppd, printer);
-                  g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk+] cups_request_ppd");
-                }
-            }
-        }
-      else
-        cups_request_ppd (printer);
-    }
-}
-
-static char *
-ppd_text_to_utf8 (ppd_file_t *ppd_file,
-                 const char *text)
-{
-  const char *encoding = NULL;
-  char *res;
-
-  if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
-    {
-      return g_strdup (text);
-    }
-  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
-    {
-      encoding = "ISO-8859-1";
-    }
-  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
-    {
-      encoding = "ISO-8859-2";
-    }
-  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
-    {
-      encoding = "ISO-8859-5";
-    }
-  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
-    {
-      encoding = "SHIFT-JIS";
-    }
-  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
-    {
-      encoding = "MACINTOSH";
-    }
-  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
-    {
-      encoding = "WINDOWS-1252";
-    }
-  else
-    {
-      /* Fallback, try iso-8859-1... */
-      encoding = "ISO-8859-1";
-    }
-
-  res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
-
-  if (res == NULL)
-    {
-      GTK_NOTE (PRINTING,
-                g_warning ("CUPS Backend: Unable to convert PPD text\n"));
-      res = g_strdup ("???");
-    }
-
-  return res;
-}
-
-/* TODO: Add more translations for common settings here */
-
-static const struct {
-  const char *keyword;
-  const char *translation;
-} cups_option_translations[] = {
-  { "Duplex", NC_("printing option", "Two Sided") },
-  { "MediaType", NC_("printing option", "Paper Type") },
-  { "InputSlot", NC_("printing option", "Paper Source") },
-  { "OutputBin", NC_("printing option", "Output Tray") },
-  { "Resolution", NC_("printing option", "Resolution") },
-  { "PreFilter", NC_("printing option", "GhostScript pre-filtering") }
-};
-
-
-static const struct {
-  const char *keyword;
-  const char *choice;
-  const char *translation;
-} cups_choice_translations[] = {
-  { "Duplex", "None", NC_("printing option value", "One Sided") },
-  /* Translators: this is an option of "Two Sided" */
-  { "Duplex", "DuplexNoTumble", NC_("printing option value", "Long Edge (Standard)") },
-  /* Translators: this is an option of "Two Sided" */
-  { "Duplex", "DuplexTumble", NC_("printing option value", "Short Edge (Flip)") },
-  /* Translators: this is an option of "Paper Source" */
-  { "InputSlot", "Auto", NC_("printing option value", "Auto Select") },
-  /* Translators: this is an option of "Paper Source" */
-  { "InputSlot", "AutoSelect", NC_("printing option value", "Auto Select") },
-  /* Translators: this is an option of "Paper Source" */
-  { "InputSlot", "Default", NC_("printing option value", "Printer Default") },
-  /* Translators: this is an option of "Paper Source" */
-  { "InputSlot", "None", NC_("printing option value", "Printer Default") },
-  /* Translators: this is an option of "Paper Source" */
-  { "InputSlot", "PrinterDefault", NC_("printing option value", "Printer Default") },
-  /* Translators: this is an option of "Paper Source" */
-  { "InputSlot", "Unspecified", NC_("printing option value", "Auto Select") },
-  /* Translators: this is an option of "Resolution" */
-  { "Resolution", "default", NC_("printing option value", "Printer Default") },
-  /* Translators: this is an option of "GhostScript" */
-  { "PreFilter", "EmbedFonts", NC_("printing option value", "Embed GhostScript fonts only") },
-  /* Translators: this is an option of "GhostScript" */
-  { "PreFilter", "Level1", NC_("printing option value", "Convert to PS level 1") },
-  /* Translators: this is an option of "GhostScript" */
-  { "PreFilter", "Level2", NC_("printing option value", "Convert to PS level 2") },
-  /* Translators: this is an option of "GhostScript" */
-  { "PreFilter", "No", NC_("printing option value", "No pre-filtering") }
-};
-
-static const struct {
-  const char *name;
-  const char *translation;
-} cups_group_translations[] = {
-/* Translators: "Miscellaneous" is the label for a button, that opens
-   up an extra panel of settings in a print dialog. */
-  { "Miscellaneous", NC_("printing option group", "Miscellaneous") }
-};
-
-static const struct {
-  const char *ppd_keyword;
-  const char *name;
-} ppd_option_names[] = {
-  { "Duplex", "gtk-duplex" },
-  { "MediaType", "gtk-paper-type" },
-  { "InputSlot", "gtk-paper-source" },
-  { "OutputBin", "gtk-output-tray" }
-};
-
-static const struct {
-  const char *ipp_option_name;
-  const char *gtk_option_name;
-  const char *translation;
-} ipp_option_translations[] = {
-  { "sides", "gtk-duplex", NC_("printing option", "Two Sided") },
-  { "output-bin", "gtk-output-tray", NC_("printing option", "Output Tray") }
-};
-
-static const struct {
-  const char *ipp_option_name;
-  const char *ipp_choice;
-  const char *translation;
-} ipp_choice_translations[] = {
-  { "sides", "one-sided", NC_("sides", "One Sided") },
-  /* Translators: this is an option of "Two Sided" */
-  { "sides", "two-sided-long-edge", NC_("sides", "Long Edge (Standard)") },
-  /* Translators: this is an option of "Two Sided" */
-  { "sides", "two-sided-short-edge", NC_("sides", "Short Edge (Flip)") },
-
-  /* Translators: Top output bin */
-  { "output-bin", "top", NC_("output-bin", "Top Bin") },
-  /* Translators: Middle output bin */
-  { "output-bin", "middle", NC_("output-bin", "Middle Bin") },
-  /* Translators: Bottom output bin */
-  { "output-bin", "bottom", NC_("output-bin", "Bottom Bin") },
-  /* Translators: Side output bin */
-  { "output-bin", "side", NC_("output-bin", "Side Bin") },
-  /* Translators: Left output bin */
-  { "output-bin", "left", NC_("output-bin", "Left Bin") },
-  /* Translators: Right output bin */
-  { "output-bin", "right", NC_("output-bin", "Right Bin") },
-  /* Translators: Center output bin */
-  { "output-bin", "center", NC_("output-bin", "Center Bin") },
-  /* Translators: Rear output bin */
-  { "output-bin", "rear", NC_("output-bin", "Rear Bin") },
-  /* Translators: Output bin where one sided output is oriented in the face-up position */
-  { "output-bin", "face-up", NC_("output-bin", "Face Up Bin") },
-  /* Translators: Output bin where one sided output is oriented in the face-down position */
-  { "output-bin", "face-down", NC_("output-bin", "Face Down Bin") },
-  /* Translators: Large capacity output bin */
-  { "output-bin", "large-capacity", NC_("output-bin", "Large Capacity Bin") },
-  { NULL, NULL, NULL }
-};
-
-/*
- * Handles "format not a string literal" error
- * https://mail.gnome.org/archives/desktop-devel-list/2016-March/msg00075.html
- */
-static gchar *
-get_ipp_choice_translation_string (gint  index,
-                                  guint i)
-{
-  gchar *translation;
-
-  if (i < G_N_ELEMENTS (ipp_choice_translations))
-    translation = g_strdup (_(ipp_choice_translations[i].translation));
-  else
-    {
-      switch (i)
-        {
-          case 14:
-            /* Translators: Output stacker number %d */
-            translation = g_strdup_printf (C_("output-bin", "Stacker %d"), index);
-            break;
-          case 15:
-            /* Translators: Output mailbox number %d */
-            translation = g_strdup_printf (C_("output-bin", "Mailbox %d"), index);
-            break;
-          case 16:
-            /* Translators: Private mailbox */
-            translation = g_strdup (C_("output-bin", "My Mailbox"));
-            break;
-          case 17:
-            /* Translators: Output tray number %d */
-            translation = g_strdup_printf (C_("output-bin", "Tray %d"), index);
-            break;
-          default:
-            g_assert_not_reached ();
-        }
-    }
-
-  return translation;
-}
-
-static const struct {
-  const char *lpoption;
-  const char *name;
-} lpoption_names[] = {
-  { "number-up", "gtk-n-up" },
-  { "number-up-layout", "gtk-n-up-layout" },
-  { "job-billing", "gtk-billing-info" },
-  { "job-priority", "gtk-job-prio" }
-};
-
-/* keep sorted when changing */
-static const char *color_option_whitelist[] = {
-  "BRColorEnhancement",
-  "BRColorMatching",
-  "BRColorMatching",
-  "BRColorMode",
-  "BRGammaValue",
-  "BRImprovedGray",
-  "BlackSubstitution",
-  "ColorModel",
-  "HPCMYKInks",
-  "HPCSGraphics",
-  "HPCSImages",
-  "HPCSText",
-  "HPColorSmart",
-  "RPSBlackMode",
-  "RPSBlackOverPrint",
-  "Rcmyksimulation",
-};
-
-/* keep sorted when changing */
-static const char *color_group_whitelist[] = {
-  "ColorPage",
-  "FPColorWise1",
-  "FPColorWise2",
-  "FPColorWise3",
-  "FPColorWise4",
-  "FPColorWise5",
-  "HPColorOptionsPanel",
-};
-
-/* keep sorted when changing */
-static const char *image_quality_option_whitelist[] = {
-  "BRDocument",
-  "BRHalfTonePattern",
-  "BRNormalPrt",
-  "BRPrintQuality",
-  "BitsPerPixel",
-  "Darkness",
-  "Dithering",
-  "EconoMode",
-  "Economode",
-  "HPEconoMode",
-  "HPEdgeControl",
-  "HPGraphicsHalftone",
-  "HPHalftone",
-  "HPLJDensity",
-  "HPPhotoHalftone",
-  "OutputMode",
-  "REt",
-  "RPSBitsPerPixel",
-  "RPSDitherType",
-  "Resolution",
-  "ScreenLock",
-  "Smoothing",
-  "TonerSaveMode",
-  "UCRGCRForImage",
-};
-
-/* keep sorted when changing */
-static const char *image_quality_group_whitelist[] = {
-  "FPImageQuality1",
-  "FPImageQuality2",
-  "FPImageQuality3",
-  "ImageQualityPage",
-};
-
-/* keep sorted when changing */
-static const char * finishing_option_whitelist[] = {
-  "BindColor",
-  "BindEdge",
-  "BindType",
-  "BindWhen",
-  "Booklet",
-  "FoldType",
-  "FoldWhen",
-  "HPStaplerOptions",
-  "Jog",
-  "Slipsheet",
-  "Sorter",
-  "StapleLocation",
-  "StapleOrientation",
-  "StapleWhen",
-  "StapleX",
-  "StapleY",
-};
-
-/* keep sorted when changing */
-static const char *finishing_group_whitelist[] = {
-  "FPFinishing1",
-  "FPFinishing2",
-  "FPFinishing3",
-  "FPFinishing4",
-  "FinishingPage",
-  "HPFinishingPanel",
-};
-
-/* keep sorted when changing */
-static const char *cups_option_blacklist[] = {
-  "Collate",
-  "Copies",
-  "OutputOrder",
-  "PageRegion",
-  "PageSize",
-};
-
-static char *
-get_option_text (ppd_file_t   *ppd_file,
-                ppd_option_t *option)
-{
-  int i;
-  char *utf8;
-
-  for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
-    {
-      if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
-        return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
-                                       "printing option",
-                                       cups_option_translations[i].translation));
-    }
-
-  utf8 = ppd_text_to_utf8 (ppd_file, option->text);
-
-  /* Some ppd files have spaces in the text before the colon */
-  g_strchomp (utf8);
-
-  return utf8;
-}
-
-static char *
-get_choice_text (ppd_file_t   *ppd_file,
-                ppd_choice_t *choice)
-{
-  int i;
-  ppd_option_t *option = choice->option;
-  const char *keyword = option->keyword;
-
-  for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
-    {
-      if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
-         strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
-        return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
-                                       "printing option value",
-                                       cups_choice_translations[i].translation));
-    }
-  return ppd_text_to_utf8 (ppd_file, choice->text);
-}
-
-static gboolean
-group_has_option (ppd_group_t  *group,
-                 ppd_option_t *option)
-{
-  int i;
-
-  if (group == NULL)
-    return FALSE;
-
-  if (group->num_options > 0 &&
-      option >= group->options && option < group->options + group->num_options)
-    return TRUE;
-
-  for (i = 0; i < group->num_subgroups; i++)
-    {
-      if (group_has_option (&group->subgroups[i],option))
-       return TRUE;
-    }
-  return FALSE;
-}
-
-static void
-set_option_off (GtkPrinterOption *option)
-{
-  /* Any of these will do, _set only applies the value
-   * if its allowed of the option */
-  gtk_printer_option_set (option, "False");
-  gtk_printer_option_set (option, "Off");
-  gtk_printer_option_set (option, "None");
-}
-
-static gboolean
-value_is_off (const char *value)
-{
-  return  (strcasecmp (value, "None") == 0 ||
-          strcasecmp (value, "Off") == 0 ||
-          strcasecmp (value, "False") == 0);
-}
-
-static const char *
-ppd_group_name (ppd_group_t *group)
-{
-  return group->name;
-}
-
-static int
-available_choices (ppd_file_t     *ppd,
-                  ppd_option_t   *option,
-                  ppd_choice_t ***available,
-                  gboolean        keep_if_only_one_option)
-{
-  ppd_option_t *other_option;
-  int i, j;
-  gchar *conflicts;
-  ppd_const_t *constraint;
-  const char *choice, *other_choice;
-  ppd_option_t *option1, *option2;
-  ppd_group_t *installed_options;
-  int num_conflicts;
-  gboolean all_default;
-  int add_auto;
-
-  if (available)
-    *available = NULL;
-
-  conflicts = g_new0 (char, option->num_choices);
-
-  installed_options = NULL;
-  for (i = 0; i < ppd->num_groups; i++)
-    {
-      const char *name;
-
-      name = ppd_group_name (&ppd->groups[i]);
-      if (strcmp (name, "InstallableOptions") == 0)
-       {
-         installed_options = &ppd->groups[i];
-         break;
-       }
-    }
-
-  for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
-    {
-      option1 = ppdFindOption (ppd, constraint->option1);
-      if (option1 == NULL)
-       continue;
-
-      option2 = ppdFindOption (ppd, constraint->option2);
-      if (option2 == NULL)
-       continue;
-
-      if (option == option1)
-       {
-         choice = constraint->choice1;
-         other_option = option2;
-         other_choice = constraint->choice2;
-       }
-      else if (option == option2)
-       {
-         choice = constraint->choice2;
-         other_option = option1;
-         other_choice = constraint->choice1;
-       }
-      else
-       continue;
-
-      /* We only care of conflicts with installed_options and PageSize */
-      if (!group_has_option (installed_options, other_option) &&
-         (strcmp (other_option->keyword, "PageSize") != 0))
-       continue;
-
-      if (*other_choice == 0)
-       {
-         /* Conflict only if the installed option is not off */
-         if (value_is_off (other_option->defchoice))
-           continue;
-       }
-      /* Conflict if the installed option has the specified default */
-      else if (strcasecmp (other_choice, other_option->defchoice) != 0)
-       continue;
-
-      if (*choice == 0)
-       {
-         /* Conflict with all non-off choices */
-         for (j = 0; j < option->num_choices; j++)
-           {
-             if (!value_is_off (option->choices[j].choice))
-               conflicts[j] = 1;
-           }
-       }
-      else
-       {
-         for (j = 0; j < option->num_choices; j++)
-           {
-             if (strcasecmp (option->choices[j].choice, choice) == 0)
-               conflicts[j] = 1;
-           }
-       }
-    }
-
-  num_conflicts = 0;
-  all_default = TRUE;
-  for (j = 0; j < option->num_choices; j++)
-    {
-      if (conflicts[j])
-       num_conflicts++;
-      else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
-       all_default = FALSE;
-    }
-
-  if ((all_default && !keep_if_only_one_option) ||
-      (num_conflicts == option->num_choices))
-    {
-      g_free (conflicts);
-
-      return 0;
-    }
-
-  /* Some ppds don't have a "use printer default" option for
-   * InputSlot. This means you always have to select a particular slot,
-   * and you can't auto-pick source based on the paper size. To support
-   * this we always add an auto option if there isn't one already. If
-   * the user chooses the generated option we don't send any InputSlot
-   * value when printing. The way we detect existing auto-cases is based
-   * on feedback from Michael Sweet of cups fame.
-   */
-  add_auto = 0;
-  if (strcmp (option->keyword, "InputSlot") == 0)
-    {
-      gboolean found_auto = FALSE;
-      for (j = 0; j < option->num_choices; j++)
-       {
-         if (!conflicts[j])
-           {
-             if (strcmp (option->choices[j].choice, "Auto") == 0 ||
-                 strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
-                 strcmp (option->choices[j].choice, "Default") == 0 ||
-                 strcmp (option->choices[j].choice, "None") == 0 ||
-                 strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
-                 strcmp (option->choices[j].choice, "Unspecified") == 0 ||
-                 option->choices[j].code == NULL ||
-                 option->choices[j].code[0] == 0)
-               {
-                 found_auto = TRUE;
-                 break;
-               }
-           }
-       }
-
-      if (!found_auto)
-       add_auto = 1;
-    }
-
-  if (available)
-    {
-      *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
-
-      i = 0;
-      for (j = 0; j < option->num_choices; j++)
-       {
-         if (!conflicts[j])
-           (*available)[i++] = &option->choices[j];
-       }
-
-      if (add_auto)
-       (*available)[i++] = NULL;
-    }
-
-  g_free (conflicts);
-
-  return option->num_choices - num_conflicts + add_auto;
-}
-
-static GtkPrinterOption *
-create_pickone_option (ppd_file_t   *ppd_file,
-                      ppd_option_t *ppd_option,
-                      const gchar  *gtk_name)
-{
-  GtkPrinterOption *option;
-  ppd_choice_t **available;
-  char *label;
-  int n_choices;
-  int i;
-  ppd_coption_t *coption;
-
-  g_assert (ppd_option->ui == PPD_UI_PICKONE);
-
-  option = NULL;
-
-  n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
-  if (n_choices > 0)
-    {
-
-      /* right now only support one parameter per custom option
-       * if more than one print warning and only offer the default choices
-       */
-
-      label = get_option_text (ppd_file, ppd_option);
-
-      coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
-
-      if (coption)
-        {
-         ppd_cparam_t *cparam;
-
-          cparam = ppdFirstCustomParam (coption);
-
-          if (ppdNextCustomParam (coption) == NULL)
-           {
-              switch (cparam->type)
-               {
-                case PPD_CUSTOM_INT:
-                 option = gtk_printer_option_new (gtk_name, label,
-                                        GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
-                 break;
-                case PPD_CUSTOM_PASSCODE:
-                 option = gtk_printer_option_new (gtk_name, label,
-                                        GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
-                 break;
-                case PPD_CUSTOM_PASSWORD:
-                   option = gtk_printer_option_new (gtk_name, label,
-                                        GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
-                 break;
-               case PPD_CUSTOM_REAL:
-                   option = gtk_printer_option_new (gtk_name, label,
-                                        GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
-                 break;
-                case PPD_CUSTOM_STRING:
-                 option = gtk_printer_option_new (gtk_name, label,
-                                        GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
-                 break;
-#ifdef PRINT_IGNORED_OPTIONS
-                case PPD_CUSTOM_POINTS:
-                 g_warning ("CUPS Backend: PPD Custom Points Option not supported");
-                 break;
-                case PPD_CUSTOM_CURVE:
-                  g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
-                 break;
-                case PPD_CUSTOM_INVCURVE:
-                 g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
-                 break;
-#endif
-                default:
-                  break;
-               }
-           }
-#ifdef PRINT_IGNORED_OPTIONS
-         else
-           g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
-#endif
-       }
-
-      if (!option)
-        option = gtk_printer_option_new (gtk_name, label,
-                                        GTK_PRINTER_OPTION_TYPE_PICKONE);
-      g_free (label);
-
-      gtk_printer_option_allocate_choices (option, n_choices);
-      for (i = 0; i < n_choices; i++)
-       {
-         if (available[i] == NULL)
-           {
-             /* This was auto-added */
-             option->choices[i] = g_strdup ("gtk-ignore-value");
-             option->choices_display[i] = g_strdup (_("Printer Default"));
-           }
-         else
-           {
-             option->choices[i] = g_strdup (available[i]->choice);
-             option->choices_display[i] = get_choice_text (ppd_file, available[i]);
-           }
-       }
-
-      if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
-        {
-          if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
-            gtk_printer_option_set (option, ppd_option->defchoice + 7);
-          else
-            gtk_printer_option_set (option, ppd_option->defchoice);
-        }
-      else
-        {
-          gtk_printer_option_set (option, ppd_option->defchoice);
-        }
-    }
-#ifdef PRINT_IGNORED_OPTIONS
-  else
-    g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
-#endif
-  g_free (available);
-
-  return option;
-}
-
-static GtkPrinterOption *
-create_boolean_option (ppd_file_t   *ppd_file,
-                      ppd_option_t *ppd_option,
-                      const gchar  *gtk_name)
-{
-  GtkPrinterOption *option;
-  ppd_choice_t **available;
-  char *label;
-  int n_choices;
-
-  g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
-
-  option = NULL;
-
-  n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
-  if (n_choices == 2)
-    {
-      label = get_option_text (ppd_file, ppd_option);
-      option = gtk_printer_option_new (gtk_name, label,
-                                      GTK_PRINTER_OPTION_TYPE_BOOLEAN);
-      g_free (label);
-
-      gtk_printer_option_allocate_choices (option, 2);
-      option->choices[0] = g_strdup ("True");
-      option->choices_display[0] = g_strdup ("True");
-      option->choices[1] = g_strdup ("False");
-      option->choices_display[1] = g_strdup ("False");
-
-      gtk_printer_option_set (option, ppd_option->defchoice);
-    }
-#ifdef PRINT_IGNORED_OPTIONS
-  else
-    g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
-#endif
-  g_free (available);
-
-  return option;
-}
-
-static gchar *
-get_ppd_option_name (const gchar *keyword)
-{
-  int i;
-
-  for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
-    if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
-      return g_strdup (ppd_option_names[i].name);
-
-  return g_strdup_printf ("cups-%s", keyword);
-}
-
-static gchar *
-get_lpoption_name (const gchar *lpoption)
-{
-  int i;
-
-  for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
-    if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
-      return g_strdup (ppd_option_names[i].name);
-
-  for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
-    if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
-      return g_strdup (lpoption_names[i].name);
-
-  return g_strdup_printf ("cups-%s", lpoption);
-}
-
-static int
-strptr_cmp (const void *a,
-           const void *b)
-{
-  char **aa = (char **)a;
-  char **bb = (char **)b;
-  return strcmp (*aa, *bb);
-}
-
-
-static gboolean
-string_in_table (const gchar *str,
-                const gchar *table[],
-                gint         table_len)
-{
-  return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
-}
-
-#define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
-
-static void
-handle_option (GtkPrinterOptionSet *set,
-              ppd_file_t          *ppd_file,
-              ppd_option_t        *ppd_option,
-              ppd_group_t         *toplevel_group,
-              GtkPrintSettings    *settings)
-{
-  GtkPrinterOption *option;
-  char *option_name;
-  int i;
-
-  if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
-    return;
-
-  option_name = get_ppd_option_name (ppd_option->keyword);
-
-  option = NULL;
-  if (ppd_option->ui == PPD_UI_PICKONE)
-    option = create_pickone_option (ppd_file, ppd_option, option_name);
-  else if (ppd_option->ui == PPD_UI_BOOLEAN)
-    option = create_boolean_option (ppd_file, ppd_option, option_name);
-#ifdef PRINT_IGNORED_OPTIONS
-  else
-    g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
-#endif
-
-  if (option)
-    {
-      const char *name;
-
-      name = ppd_group_name (toplevel_group);
-      if (STRING_IN_TABLE (name, color_group_whitelist) ||
-         STRING_IN_TABLE (ppd_option->keyword, color_option_whitelist))
-       {
-         option->group = g_strdup ("ColorPage");
-       }
-      else if (STRING_IN_TABLE (name, image_quality_group_whitelist) ||
-              STRING_IN_TABLE (ppd_option->keyword, image_quality_option_whitelist))
-       {
-         option->group = g_strdup ("ImageQualityPage");
-       }
-      else if (STRING_IN_TABLE (name, finishing_group_whitelist) ||
-              STRING_IN_TABLE (ppd_option->keyword, finishing_option_whitelist))
-       {
-         option->group = g_strdup ("FinishingPage");
-       }
-      else
-       {
-         for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
-           {
-             if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
-               {
-                  option->group = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
-                                                          "printing option group",
-                                                          cups_group_translations[i].translation));
-                 break;
-               }
-           }
-
-         if (i == G_N_ELEMENTS (cups_group_translations))
-           option->group = g_strdup (toplevel_group->text);
-       }
-
-      set_option_from_settings (option, settings);
-
-      gtk_printer_option_set_add (set, option);
-    }
-
-  g_free (option_name);
-}
-
-static void
-handle_group (GtkPrinterOptionSet *set,
-             ppd_file_t          *ppd_file,
-             ppd_group_t         *group,
-             ppd_group_t         *toplevel_group,
-             GtkPrintSettings    *settings)
-{
-  gint i;
-  const gchar *name;
-
-  /* Ignore installable options */
-  name = ppd_group_name (toplevel_group);
-  if (strcmp (name, "InstallableOptions") == 0)
-    return;
-
-  for (i = 0; i < group->num_options; i++)
-    handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
-
-  for (i = 0; i < group->num_subgroups; i++)
-    handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
-
-}
-
-#ifdef HAVE_COLORD
-
-typedef struct {
-        GtkPrintSettings     *settings;
-        GtkPrinter           *printer;
-} GtkPrintBackendCupsColordHelper;
-
-static void
-colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
-                                      GtkPrintBackendCupsColordHelper *helper)
-{
-  gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
-                                    helper->settings,
-                                    set);
-}
-#endif
-
-/*
- * Lookup translation and Gtk+ name of given IPP option name.
- */
-static gboolean
-get_ipp_option_translation (const gchar  *ipp_option_name,
-                            gchar       **gtk_option_name,
-                            gchar       **translation)
-{
-  gint i;
-
-  *gtk_option_name = NULL;
-  *translation = NULL;
-
-  for (i = 0; i < G_N_ELEMENTS (ipp_option_translations); i++)
-    {
-      if (g_strcmp0 (ipp_option_translations[i].ipp_option_name, ipp_option_name) == 0)
-        {
-          *gtk_option_name = g_strdup (ipp_option_translations[i].gtk_option_name);
-          *translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
-                                                 "printing option",
-                                                 ipp_option_translations[i].translation));
-          return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-/*
- * Lookup translation of given IPP choice.
- */
-static gchar *
-get_ipp_choice_translation (const gchar  *ipp_option_name,
-                            const gchar  *ipp_choice)
-{
-  const gchar *nptr;
-  guint64      index;
-  gchar       *translation = NULL;
-  gsize        ipp_choice_length;
-  gchar       *endptr;
-  gint         i;
-
-  for (i = 0; ipp_choice_translations[i].ipp_option_name != NULL; i++)
-    {
-      if (g_strcmp0 (ipp_choice_translations[i].ipp_option_name, ipp_option_name) == 0)
-        {
-          ipp_choice_length = strlen (ipp_choice_translations[i].ipp_choice);
-
-          if (g_strcmp0 (ipp_choice_translations[i].ipp_choice, ipp_choice) == 0)
-            {
-              translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
-                                                    ipp_option_name,
-                                                    ipp_choice_translations[i].translation));
-              break;
-            }
-          else if (g_str_has_suffix (ipp_choice_translations[i].ipp_choice, "-N") &&
-                   g_ascii_strncasecmp (ipp_choice_translations[i].ipp_choice,
-                                        ipp_choice,
-                                        ipp_choice_length - 2) == 0)
-            {
-              /* Find out index of the ipp_choice if it is supported for the choice. */
-              endptr = NULL;
-              nptr = ipp_choice + ipp_choice_length - 1;
-              index = g_ascii_strtoull (nptr,
-                                        &endptr,
-                                        10);
-
-              if (index != 0 || endptr != nptr)
-                {
-                  translation = get_ipp_choice_translation_string (index, i);
-                  break;
-                }
-            }
-        }
-    }
-
-  return translation;
-}
-
-/*
- * Format an IPP choice to a displayable string.
- */
-static gchar *
-format_ipp_choice (const gchar *ipp_choice)
-{
-  gboolean  after_space = TRUE;
-  gchar    *result = NULL;
-  gsize     i;
-
-  if (ipp_choice != NULL)
-    {
-      result = g_strdup (ipp_choice);
-      /* Replace all '-' by spaces. */
-      result = g_strdelimit (result, "-", ' ');
-      if (g_str_is_ascii (result))
-        {
-          /* Convert all leading characters to upper case. */
-          for (i = 0; i < strlen (result); i++)
-            {
-              if (after_space && g_ascii_isalpha (result[i]))
-                result[i] = g_ascii_toupper (result[i]);
-
-              after_space = g_ascii_isspace (result[i]);
-            }
-        }
-    }
-
-  return result;
-}
-
-/*
- * Look the IPP option up in given set of options.
- * Create it if it doesn't exist and set its default value
- * if available.
- */
-static GtkPrinterOption *
-setup_ipp_option (gchar               *ipp_option_name,
-                  gchar               *ipp_choice_default,
-                  GList               *ipp_choices,
-                  GtkPrinterOptionSet *set)
-{
-  GtkPrinterOption *option = NULL;
-  gchar            *gtk_option_name = NULL;
-  gchar            *translation = NULL;
-  gchar            *ipp_choice;
-  gsize             i;
-
-  get_ipp_option_translation (ipp_option_name,
-                              &gtk_option_name,
-                              &translation);
-
-  /* Look the option up in the given set of options. */
-  if (gtk_option_name != NULL)
-    option = gtk_printer_option_set_lookup (set, gtk_option_name);
-
-  /* The option was not found, create it from given choices. */
-  if (option == NULL &&
-      ipp_choices != NULL)
-    {
-      GList  *iter;
-      gsize   length;
-      char  **choices = NULL;
-      char  **choices_display = NULL;
-
-      option = gtk_printer_option_new (gtk_option_name,
-                                       translation,
-                                       GTK_PRINTER_OPTION_TYPE_PICKONE);
-
-      length = g_list_length (ipp_choices);
-
-      choices = g_new0 (char *, length);
-      choices_display = g_new0 (char *, length);
-
-      i = 0;
-      for (iter = ipp_choices; iter != NULL; iter = iter->next)
-        {
-          ipp_choice = (gchar *) iter->data;
-
-          choices[i] = g_strdup (ipp_choice);
-
-          translation = get_ipp_choice_translation (ipp_option_name,
-                                                    ipp_choice);
-          if (translation != NULL)
-            choices_display[i] = translation;
-          else
-            choices_display[i] = format_ipp_choice (ipp_choice);
-
-          i++;
-        }
-
-      if (choices != NULL &&
-          choices_display != NULL)
-        {
-          gtk_printer_option_choices_from_array (option,
-                                                 length,
-                                                 choices,
-                                                 choices_display);
-        }
-
-      option_set_is_ipp_option (option, TRUE);
-
-      gtk_printer_option_set_add (set, option);
-
-      g_free (choices);
-      g_free (choices_display);
-    }
-
-  /* The option exists. Set its default value if available. */
-  if (option != NULL &&
-      ipp_choice_default != NULL)
-    {
-      gtk_printer_option_set (option, ipp_choice_default);
-    }
-
-  return option;
-}
-
-static GtkPrinterOptionSet *
-cups_printer_get_options (GtkPrinter           *printer,
-                         GtkPrintSettings     *settings,
-                         GtkPageSetup         *page_setup,
-                         GtkPrintCapabilities  capabilities)
-{
-  GtkPrinterOptionSet *set;
-  GtkPrinterOption *option;
-  ppd_file_t *ppd_file;
-  int i;
-  char *print_at[] = { "now", "at", "on-hold" };
-  char *n_up[] = {"1", "2", "4", "6", "9", "16" };
-  char *prio[] = {"100", "80", "50", "30" };
-  /* Translators: These strings name the possible values of the
-   * job priority option in the print dialog
-   */
-  char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
-  char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
-  /* Translators: These strings name the possible arrangements of
-   * multiple pages on a sheet when printing
-   */
-  char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"),
-                                  N_("Right to left, top to bottom"), N_("Right to left, bottom to top"),
-                                  N_("Top to bottom, left to right"), N_("Top to bottom, right to left"),
-                                  N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
-  char *name;
-  int num_opts;
-  cups_option_t *opts = NULL;
-  GtkPrintBackendCups *backend;
-  GtkTextDirection text_direction;
-  GtkPrinterCups *cups_printer = NULL;
-#ifdef HAVE_COLORD
-  GtkPrintBackendCupsColordHelper *helper;
-#endif
-  char *default_number_up;
-
-  set = gtk_printer_option_set_new ();
-
-  /* Cups specific, non-ppd related settings */
-
-  for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
-    prio_display[i] = _(prio_display[i]);
-
-  /* Translators, this string is used to label the job priority option
-   * in the print dialog
-   */
-  option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
-  gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
-                                        prio, prio_display);
-  gtk_printer_option_set (option, "50");
-  set_option_from_settings (option, settings);
-  gtk_printer_option_set_add (set, option);
-  g_object_unref (option);
-
-  /* Translators, this string is used to label the billing info entry
-   * in the print dialog
-   */
-  option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
-  gtk_printer_option_set (option, "");
-  set_option_from_settings (option, settings);
-  gtk_printer_option_set_add (set, option);
-  g_object_unref (option);
-
-  backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
-  cups_printer = GTK_PRINTER_CUPS (printer);
-
-  if (backend != NULL && printer != NULL)
-    {
-      char *cover_default[] = {
-        "none",
-        "classified",
-        "confidential",
-        "secret",
-        "standard",
-        "topsecret",
-        "unclassified"
-      };
-      char *cover_display_default[] = {
-        /* Translators, these strings are names for various 'standard' cover
-         * pages that the printing system may support.
-         */
-        NC_("cover page", "None"),
-        NC_("cover page", "Classified"),
-        NC_("cover page", "Confidential"),
-        NC_("cover page", "Secret"),
-        NC_("cover page", "Standard"),
-        NC_("cover page", "Top Secret"),
-        NC_("cover page", "Unclassified")
-      };
-      char **cover = NULL;
-      char **cover_display = NULL;
-      char **cover_display_translated = NULL;
-      gint num_of_covers = 0;
-      gpointer value;
-      gint j;
-
-       /* Translators, this string is used to label the pages-per-sheet option
-        * in the print dialog
-        */
-      option = gtk_printer_option_new ("gtk-n-up", C_("printer option", "Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
-      gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), n_up, n_up);
-      default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
-      gtk_printer_option_set (option, default_number_up);
-      g_free (default_number_up);
-      set_option_from_settings (option, settings);
-      gtk_printer_option_set_add (set, option);
-      g_object_unref (option);
-
-      if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
-        {
-          for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
-            n_up_layout_display[i] = _(n_up_layout_display[i]);
-
-           /* Translators, this string is used to label the option in the print
-            * dialog that controls in what order multiple pages are arranged
-            */
-          option = gtk_printer_option_new ("gtk-n-up-layout", C_("printer option", "Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
-          gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
-                                                 n_up_layout, n_up_layout_display);
-
-          text_direction = gtk_widget_get_default_direction ();
-          if (text_direction == GTK_TEXT_DIR_LTR)
-            gtk_printer_option_set (option, "lrtb");
-          else
-            gtk_printer_option_set (option, "rltb");
-
-          set_option_from_settings (option, settings);
-          gtk_printer_option_set_add (set, option);
-          g_object_unref (option);
-        }
-
-      num_of_covers = cups_printer->number_of_covers;
-      cover = g_new (char *, num_of_covers + 1);
-      cover[num_of_covers] = NULL;
-      cover_display = g_new (char *, num_of_covers + 1);
-      cover_display[num_of_covers] = NULL;
-      cover_display_translated = g_new (char *, num_of_covers + 1);
-      cover_display_translated[num_of_covers] = NULL;
-
-      for (i = 0; i < num_of_covers; i++)
-        {
-          cover[i] = g_strdup (cups_printer->covers[i]);
-          value = NULL;
-          for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
-            if (strcmp (cover_default[j], cover[i]) == 0)
-              {
-                value = cover_display_default[j];
-                break;
-              }
-          cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (cups_printer->covers[i]);
-        }
-
-      for (i = 0; i < num_of_covers; i++)
-        cover_display_translated[i] = (gchar *)g_dpgettext2 (GETTEXT_PACKAGE, "cover page", cover_display[i]);
-
-      /* Translators, this is the label used for the option in the print
-       * dialog that controls the front cover page.
-       */
-      option = gtk_printer_option_new ("gtk-cover-before", C_("printer option", "Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
-      gtk_printer_option_choices_from_array (option, num_of_covers,
-                                        cover, cover_display_translated);
-
-      if (cups_printer->default_cover_before != NULL)
-        gtk_printer_option_set (option, cups_printer->default_cover_before);
-      else
-        gtk_printer_option_set (option, "none");
-      set_option_from_settings (option, settings);
-      gtk_printer_option_set_add (set, option);
-      g_object_unref (option);
-
-      /* Translators, this is the label used for the option in the print
-       * dialog that controls the back cover page.
-       */
-      option = gtk_printer_option_new ("gtk-cover-after", C_("printer option", "After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
-      gtk_printer_option_choices_from_array (option, num_of_covers,
-                                        cover, cover_display_translated);
-      if (cups_printer->default_cover_after != NULL)
-        gtk_printer_option_set (option, cups_printer->default_cover_after);
-      else
-        gtk_printer_option_set (option, "none");
-      set_option_from_settings (option, settings);
-      gtk_printer_option_set_add (set, option);
-      g_object_unref (option);
-
-      g_strfreev (cover);
-      g_strfreev (cover_display);
-      g_free (cover_display_translated);
-    }
-
-  /* Translators: this is the name of the option that controls when
-   * a print job is printed. Possible values are 'now', a specified time,
-   * or 'on hold'
-   */
-  option = gtk_printer_option_new ("gtk-print-time", C_("printer option", "Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
-  gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
-                                        print_at, print_at);
-  gtk_printer_option_set (option, "now");
-  set_option_from_settings (option, settings);
-  gtk_printer_option_set_add (set, option);
-  g_object_unref (option);
-
-  /* Translators: this is the name of the option that allows the user
-   * to specify a time when a print job will be printed.
-   */
-  option = gtk_printer_option_new ("gtk-print-time-text", C_("printer option", "Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
-  gtk_printer_option_set (option, "");
-  set_option_from_settings (option, settings);
-  gtk_printer_option_set_add (set, option);
-  g_object_unref (option);
-
-  /* Printer (ppd) specific settings */
-  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
-  if (ppd_file)
-    {
-      GtkPaperSize *paper_size;
-      ppd_option_t *ppd_option;
-      const gchar *ppd_name;
-
-      ppdMarkDefaults (ppd_file);
-
-      paper_size = gtk_page_setup_get_paper_size (page_setup);
-
-      ppd_option = ppdFindOption (ppd_file, "PageSize");
-      if (ppd_option)
-       {
-         ppd_name = gtk_paper_size_get_ppd_name (paper_size);
-
-         if (ppd_name)
-           strncpy (ppd_option->defchoice, ppd_name, PPD_MAX_NAME);
-         else
-           {
-             gchar *custom_name;
-             char width[G_ASCII_DTOSTR_BUF_SIZE];
-             char height[G_ASCII_DTOSTR_BUF_SIZE];
-
-             g_ascii_formatd (width, sizeof (width), "%.2f",
-                              gtk_paper_size_get_width (paper_size,
-                                                        GTK_UNIT_POINTS));
-             g_ascii_formatd (height, sizeof (height), "%.2f",
-                              gtk_paper_size_get_height (paper_size,
-                                                         GTK_UNIT_POINTS));
-             /* Translators: this format is used to display a custom
-              * paper size. The two placeholders are replaced with
-              * the width and height in points. E.g: "Custom
-              * 230.4x142.9"
-               */
-             custom_name = g_strdup_printf (_("Custom %s×%s"), width, height);
-             strncpy (ppd_option->defchoice, custom_name, PPD_MAX_NAME);
-             g_free (custom_name);
-           }
-       }
-
-      for (i = 0; i < ppd_file->num_groups; i++)
-        handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
-    }
-  else
-    {
-      /* Try IPP options */
-
-      option = setup_ipp_option ("sides",
-                                 cups_printer->sides_default,
-                                 cups_printer->sides_supported,
-                                 set);
-
-      if (option != NULL)
-        set_option_from_settings (option, settings);
-
-      option = setup_ipp_option ("output-bin",
-                                 cups_printer->output_bin_default,
-                                 cups_printer->output_bin_supported,
-                                 set);
-
-      if (option != NULL)
-        set_option_from_settings (option, settings);
-    }
-
-  /* Now honor the user set defaults for this printer */
-  num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
-
-  for (i = 0; i < num_opts; i++)
-    {
-      if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
-        continue;
-
-      name = get_lpoption_name (opts[i].name);
-      if (strcmp (name, "cups-job-sheets") == 0)
-        {
-          gchar **values;
-          gint    num_values;
-
-          values = g_strsplit (opts[i].value, ",", 2);
-          num_values = g_strv_length (values);
-
-          option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
-          if (option && num_values > 0)
-            gtk_printer_option_set (option, g_strstrip (values[0]));
-
-          option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
-          if (option && num_values > 1)
-            gtk_printer_option_set (option, g_strstrip (values[1]));
-
-          g_strfreev (values);
-        }
-      else if (strcmp (name, "cups-job-hold-until") == 0)
-        {
-          GtkPrinterOption *option2 = NULL;
-
-          option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
-          if (option && opts[i].value)
-            {
-              option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
-              if (option2)
-                {
-                  if (strcmp (opts[i].value, "indefinite") == 0)
-                    gtk_printer_option_set (option2, "on-hold");
-                  else
-                    {
-                      gtk_printer_option_set (option2, "at");
-                      gtk_printer_option_set (option, opts[i].value);
-                    }
-                }
-            }
-        }
-      else if (strcmp (name, "cups-sides") == 0)
-        {
-          option = gtk_printer_option_set_lookup (set, "gtk-duplex");
-          if (option && opts[i].value)
-            {
-              if (!option_is_ipp_option (option))
-                {
-                  if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
-                    gtk_printer_option_set (option, "DuplexTumble");
-                  else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
-                    gtk_printer_option_set (option, "DuplexNoTumble");
-                }
-              else
-                {
-                  gtk_printer_option_set (option, opts[i].value);
-                }
-            }
-        }
-      else
-        {
-          option = gtk_printer_option_set_lookup (set, name);
-          if (option)
-            gtk_printer_option_set (option, opts[i].value);
-        }
-      g_free (name);
-    }
-
-  cupsFreeOptions (num_opts, opts);
-
-#ifdef HAVE_COLORD
-  option = gtk_printer_option_new ("colord-profile",
-                                   /* TRANSLATORS: this this the ICC color profile to use for this job */
-                                   C_("printer option", "Printer Profile"),
-                                   GTK_PRINTER_OPTION_TYPE_INFO);
-
-  /* assign it to the color page */
-  option->group = g_strdup ("ColorPage");
-
-  /* TRANSLATORS: this is when color profile information is unavailable */
-  gtk_printer_option_set (option, C_("printer option value", "Unavailable"));
-  gtk_printer_option_set_add (set, option);
-
-  /* watch to see if the user changed the options */
-  helper = g_new (GtkPrintBackendCupsColordHelper, 1);
-  helper->printer = printer;
-  helper->settings = settings;
-  g_signal_connect_data (set, "changed",
-                         G_CALLBACK (colord_printer_option_set_changed_cb),
-                         helper,
-                         (GClosureNotify) g_free,
-                         0);
-
-  /* initial coldplug */
-  gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
-                                    settings, set);
-  g_object_bind_property (printer, "profile-title",
-                          option, "value",
-                          G_BINDING_DEFAULT);
-
-#endif
-
-  return set;
-}
-
-
-static void
-mark_option_from_set (GtkPrinterOptionSet *set,
-                     ppd_file_t          *ppd_file,
-                     ppd_option_t        *ppd_option)
-{
-  GtkPrinterOption *option;
-  char *name = get_ppd_option_name (ppd_option->keyword);
-
-  option = gtk_printer_option_set_lookup (set, name);
-
-  if (option)
-    ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
-
-  g_free (name);
-}
-
-
-static void
-mark_group_from_set (GtkPrinterOptionSet *set,
-                    ppd_file_t          *ppd_file,
-                    ppd_group_t         *group)
-{
-  int i;
-
-  for (i = 0; i < group->num_options; i++)
-    mark_option_from_set (set, ppd_file, &group->options[i]);
-
-  for (i = 0; i < group->num_subgroups; i++)
-    mark_group_from_set (set, ppd_file, &group->subgroups[i]);
-}
-
-static void
-set_conflicts_from_option (GtkPrinterOptionSet *set,
-                          ppd_file_t          *ppd_file,
-                          ppd_option_t        *ppd_option)
-{
-  GtkPrinterOption *option;
-  char *name;
-
-  if (ppd_option->conflicted)
-    {
-      name = get_ppd_option_name (ppd_option->keyword);
-      option = gtk_printer_option_set_lookup (set, name);
-
-      if (option)
-       gtk_printer_option_set_has_conflict (option, TRUE);
-#ifdef PRINT_IGNORED_OPTIONS
-      else
-       g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
-#endif
-
-      g_free (name);
-    }
-}
-
-static void
-set_conflicts_from_group (GtkPrinterOptionSet *set,
-                         ppd_file_t          *ppd_file,
-                         ppd_group_t         *group)
-{
-  int i;
-
-  for (i = 0; i < group->num_options; i++)
-    set_conflicts_from_option (set, ppd_file, &group->options[i]);
-
-  for (i = 0; i < group->num_subgroups; i++)
-    set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
-}
-
-static gboolean
-cups_printer_mark_conflicts (GtkPrinter          *printer,
-                            GtkPrinterOptionSet *options)
-{
-  ppd_file_t *ppd_file;
-  int num_conflicts;
-  int i;
-
-  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
-
-  if (ppd_file == NULL)
-    return FALSE;
-
-  ppdMarkDefaults (ppd_file);
-
-  for (i = 0; i < ppd_file->num_groups; i++)
-    mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
-
-  num_conflicts = ppdConflicts (ppd_file);
-
-  if (num_conflicts > 0)
-    {
-      for (i = 0; i < ppd_file->num_groups; i++)
-       set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
-    }
-
-  return num_conflicts > 0;
-}
-
-struct OptionData {
-  GtkPrinter *printer;
-  GtkPrinterOptionSet *options;
-  GtkPrintSettings *settings;
-  ppd_file_t *ppd_file;
-};
-
-typedef struct {
-  const char *cups;
-  const char *standard;
-} NameMapping;
-
-static void
-map_settings_to_option (GtkPrinterOption  *option,
-                       const NameMapping  table[],
-                       gint               n_elements,
-                       GtkPrintSettings  *settings,
-                       const gchar       *standard_name,
-                       const gchar       *cups_name,
-                       const gchar       *ipp_name)
-{
-  int i;
-  char *name;
-  const char *cups_value;
-  const char *ipp_value;
-  const char *standard_value;
-
-  /* If the cups-specific setting is set, always use that */
-  name = g_strdup_printf ("cups-%s", cups_name);
-  cups_value = gtk_print_settings_get (settings, name);
-  g_free (name);
-
-  if (cups_value != NULL)
-    {
-      gtk_printer_option_set (option, cups_value);
-      return;
-    }
-
-  /* If the IPP-specific setting is set, use that */
-  name = g_strdup_printf ("cups-%s", ipp_name);
-  ipp_value = gtk_print_settings_get (settings, name);
-  g_free (name);
-
-  if (ipp_value != NULL)
-    {
-      gtk_printer_option_set (option, ipp_value);
-      return;
-    }
-
-  /* Otherwise we try to convert from the general setting */
-  standard_value = gtk_print_settings_get (settings, standard_name);
-  if (standard_value == NULL)
-    return;
-
-  for (i = 0; i < n_elements; i++)
-    {
-      if (table[i].cups == NULL && table[i].standard == NULL)
-       {
-         gtk_printer_option_set (option, standard_value);
-         break;
-       }
-      else if (table[i].cups == NULL &&
-              strcmp (table[i].standard, standard_value) == 0)
-       {
-         set_option_off (option);
-         break;
-       }
-      else if (strcmp (table[i].standard, standard_value) == 0)
-       {
-         gtk_printer_option_set (option, table[i].cups);
-         break;
-       }
-    }
-}
-
-static void
-map_option_to_settings (const gchar       *value,
-                       const NameMapping  table[],
-                       gint               n_elements,
-                       GtkPrintSettings  *settings,
-                       const gchar       *standard_name,
-                       const gchar       *cups_name,
-                       const gchar       *ipp_name,
-                       gboolean           is_ipp_option)
-{
-  int i;
-  char *name;
-
-  for (i = 0; i < n_elements; i++)
-    {
-      if (table[i].cups == NULL && table[i].standard == NULL)
-       {
-         gtk_print_settings_set (settings,
-                                 standard_name,
-                                 value);
-         break;
-       }
-      else if (table[i].cups == NULL && table[i].standard != NULL)
-       {
-         if (value_is_off (value))
-           {
-             gtk_print_settings_set (settings,
-                                     standard_name,
-                                     table[i].standard);
-             break;
-           }
-       }
-      else if (strcmp (table[i].cups, value) == 0)
-       {
-         gtk_print_settings_set (settings,
-                                 standard_name,
-                                 table[i].standard);
-         break;
-       }
-    }
-
-  /* Always set the corresponding cups-specific setting */
-  if (is_ipp_option)
-    name = g_strdup_printf ("cups-%s", ipp_name);
-  else
-    name = g_strdup_printf ("cups-%s", cups_name);
-
-  gtk_print_settings_set (settings, name, value);
-
-  g_free (name);
-}
-
-
-static const NameMapping paper_source_map[] = {
-  { "Lower", "lower"},
-  { "Middle", "middle"},
-  { "Upper", "upper"},
-  { "Rear", "rear"},
-  { "Envelope", "envelope"},
-  { "Cassette", "cassette"},
-  { "LargeCapacity", "large-capacity"},
-  { "AnySmallFormat", "small-format"},
-  { "AnyLargeFormat", "large-format"},
-  { NULL, NULL}
-};
-
-static const NameMapping output_tray_map[] = {
-  { "Upper", "upper"},
-  { "Lower", "lower"},
-  { "Rear", "rear"},
-  { NULL, NULL}
-};
-
-static const NameMapping duplex_map[] = {
-  { "DuplexTumble", "vertical" },
-  { "DuplexNoTumble", "horizontal" },
-  { NULL, "simplex" }
-};
-
-static const NameMapping output_mode_map[] = {
-  { "Standard", "normal" },
-  { "Normal", "normal" },
-  { "Draft", "draft" },
-  { "Fast", "draft" },
-};
-
-static const NameMapping media_type_map[] = {
-  { "Transparency", "transparency"},
-  { "Standard", "stationery"},
-  { NULL, NULL}
-};
-
-static const NameMapping all_map[] = {
-  { NULL, NULL}
-};
-
-
-static void
-set_option_from_settings (GtkPrinterOption *option,
-                         GtkPrintSettings *settings)
-{
-  const char *cups_value;
-  char *value;
-
-  if (settings == NULL)
-    return;
-
-  if (strcmp (option->name, "gtk-paper-source") == 0)
-    map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
-                            settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
-                            "InputSlot", NULL);
-  else if (strcmp (option->name, "gtk-output-tray") == 0)
-    map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
-                           settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
-                           "OutputBin", "output-bin");
-  else if (strcmp (option->name, "gtk-duplex") == 0)
-    map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
-                           settings, GTK_PRINT_SETTINGS_DUPLEX,
-                           "Duplex", "sides");
-  else if (strcmp (option->name, "cups-OutputMode") == 0)
-    map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
-                           settings, GTK_PRINT_SETTINGS_QUALITY,
-                           "OutputMode", NULL);
-  else if (strcmp (option->name, "cups-Resolution") == 0)
-    {
-      cups_value = gtk_print_settings_get (settings, option->name);
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-      else
-       {
-         if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
-             gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
-             gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
-             option->value == NULL || option->value[0] == '\0')
-           {
-              int res = gtk_print_settings_get_resolution (settings);
-              int res_x = gtk_print_settings_get_resolution_x (settings);
-              int res_y = gtk_print_settings_get_resolution_y (settings);
-
-              if (res_x != res_y)
-                {
-                  value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
-                  gtk_printer_option_set (option, value);
-                  g_free (value);
-                }
-              else if (res != 0)
-                {
-                  value = g_strdup_printf ("%ddpi", res);
-                  gtk_printer_option_set (option, value);
-                  g_free (value);
-                }
-            }
-        }
-    }
-  else if (strcmp (option->name, "gtk-paper-type") == 0)
-    map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
-                           settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
-                           "MediaType", NULL);
-  else if (strcmp (option->name, "gtk-n-up") == 0)
-    {
-      map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
-                             settings, GTK_PRINT_SETTINGS_NUMBER_UP,
-                             "number-up", NULL);
-    }
-  else if (strcmp (option->name, "gtk-n-up-layout") == 0)
-    {
-      map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
-                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
-                             "number-up-layout", NULL);
-    }
-  else if (strcmp (option->name, "gtk-billing-info") == 0)
-    {
-      cups_value = gtk_print_settings_get (settings, "cups-job-billing");
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-    }
-  else if (strcmp (option->name, "gtk-job-prio") == 0)
-    {
-      cups_value = gtk_print_settings_get (settings, "cups-job-priority");
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-    }
-  else if (strcmp (option->name, "gtk-cover-before") == 0)
-    {
-      cups_value = gtk_print_settings_get (settings, "cover-before");
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-    }
-  else if (strcmp (option->name, "gtk-cover-after") == 0)
-    {
-      cups_value = gtk_print_settings_get (settings, "cover-after");
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-    }
-  else if (strcmp (option->name, "gtk-print-time") == 0)
-    {
-      cups_value = gtk_print_settings_get (settings, "print-at");
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-    }
-  else if (strcmp (option->name, "gtk-print-time-text") == 0)
-    {
-      cups_value = gtk_print_settings_get (settings, "print-at-time");
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-    }
-  else if (g_str_has_prefix (option->name, "cups-"))
-    {
-      cups_value = gtk_print_settings_get (settings, option->name);
-      if (cups_value)
-       gtk_printer_option_set (option, cups_value);
-    }
-}
-
-static void
-foreach_option_get_settings (GtkPrinterOption *option,
-                            gpointer          user_data)
-{
-  struct OptionData *data = user_data;
-  GtkPrintSettings *settings = data->settings;
-  const char *value;
-
-  value = option->value;
-
-  if (strcmp (option->name, "gtk-paper-source") == 0)
-    map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
-                           settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
-                           "InputSlot", NULL, FALSE);
-  else if (strcmp (option->name, "gtk-output-tray") == 0)
-    map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
-                           settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
-                           "OutputBin", "output-bin", option_is_ipp_option (option));
-  else if (strcmp (option->name, "gtk-duplex") == 0)
-    map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
-                           settings, GTK_PRINT_SETTINGS_DUPLEX,
-                           "Duplex", "sides", option_is_ipp_option (option));
-  else if (strcmp (option->name, "cups-OutputMode") == 0)
-    map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
-                           settings, GTK_PRINT_SETTINGS_QUALITY,
-                           "OutputMode", NULL, FALSE);
-  else if (strcmp (option->name, "cups-Resolution") == 0)
-    {
-      int res, res_x, res_y;
-
-      if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
-        {
-          if (res_x > 0 && res_y > 0)
-            gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
-        }
-      else if (sscanf (value, "%ddpi", &res) == 1)
-        {
-          if (res > 0)
-            gtk_print_settings_set_resolution (settings, res);
-        }
-
-      gtk_print_settings_set (settings, option->name, value);
-    }
-  else if (strcmp (option->name, "gtk-paper-type") == 0)
-    map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
-                           settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
-                           "MediaType", NULL, FALSE);
-  else if (strcmp (option->name, "gtk-n-up") == 0)
-    map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
-                           settings, GTK_PRINT_SETTINGS_NUMBER_UP,
-                           "number-up", NULL, FALSE);
-  else if (strcmp (option->name, "gtk-n-up-layout") == 0)
-    map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
-                           settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
-                           "number-up-layout", NULL, FALSE);
-  else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
-    gtk_print_settings_set (settings, "cups-job-billing", value);
-  else if (strcmp (option->name, "gtk-job-prio") == 0)
-    gtk_print_settings_set (settings, "cups-job-priority", value);
-  else if (strcmp (option->name, "gtk-cover-before") == 0)
-    gtk_print_settings_set (settings, "cover-before", value);
-  else if (strcmp (option->name, "gtk-cover-after") == 0)
-    gtk_print_settings_set (settings, "cover-after", value);
-  else if (strcmp (option->name, "gtk-print-time") == 0)
-    gtk_print_settings_set (settings, "print-at", value);
-  else if (strcmp (option->name, "gtk-print-time-text") == 0)
-    gtk_print_settings_set (settings, "print-at-time", value);
-  else if (g_str_has_prefix (option->name, "cups-"))
-    gtk_print_settings_set (settings, option->name, value);
-}
-
-static gboolean
-supports_am_pm (void)
-{
-  struct tm tmp_tm = { 0 };
-  char   time[8];
-  int    length;
-
-  length = strftime (time, sizeof (time), "%p", &tmp_tm);
-
-  return length != 0;
-}
-
-/* Converts local time to UTC time. Local time has to be in one of these
- * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
- * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
- * Returns a newly allocated string holding UTC time in HH:MM:SS format
- * or NULL.
- */
-gchar *
-localtime_to_utctime (const char *local_time)
-{
-  const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
-                             " %H : %M : %S ",
-                             " %I : %M %p ", " %p %I : %M ",
-                             " %H : %M ",
-                             " %I %p ", " %p %I "};
-  const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
-  const char *end = NULL;
-  struct tm  *actual_local_time;
-  struct tm  *actual_utc_time;
-  struct tm   local_print_time;
-  struct tm   utc_print_time;
-  struct tm   diff_time;
-  gchar      *utc_time = NULL;
-  int         i, n;
-
-  if (local_time == NULL || local_time[0] == '\0')
-    return NULL;
-
-  n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
-
-  for (i = 0; i < n; i++)
-    {
-      local_print_time.tm_hour = 0;
-      local_print_time.tm_min  = 0;
-      local_print_time.tm_sec  = 0;
-
-      if (supports_am_pm ())
-        end = strptime (local_time, formats_0[i], &local_print_time);
-      else
-        end = strptime (local_time, formats_1[i], &local_print_time);
-
-      if (end != NULL && end[0] == '\0')
-        break;
-    }
-
-  if (end != NULL && end[0] == '\0')
-    {
-      time_t rawtime;
-      time (&rawtime);
-
-      actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
-      actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
-
-      diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
-      diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
-      diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
-
-      utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
-      utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
-      utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
-
-      utc_time = g_strdup_printf ("%02d:%02d:%02d",
-                                  utc_print_time.tm_hour,
-                                  utc_print_time.tm_min,
-                                  utc_print_time.tm_sec);
-    }
-
-  return utc_time;
-}
-
-static void
-cups_printer_get_settings_from_options (GtkPrinter          *printer,
-                                       GtkPrinterOptionSet *options,
-                                       GtkPrintSettings    *settings)
-{
-  struct OptionData data;
-  const char *print_at, *print_at_time;
-
-  data.printer = printer;
-  data.options = options;
-  data.settings = settings;
-  data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
-
-  gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
-  if (data.ppd_file != NULL)
-    {
-      GtkPrinterOption *cover_before, *cover_after;
-
-      cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
-      cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
-      if (cover_before && cover_after)
-       {
-         char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
-         gtk_print_settings_set (settings, "cups-job-sheets", value);
-         g_free (value);
-       }
-
-      print_at = gtk_print_settings_get (settings, "print-at");
-      print_at_time = gtk_print_settings_get (settings, "print-at-time");
-
-      if (strcmp (print_at, "at") == 0)
-        {
-          gchar *utc_time = NULL;
-
-          utc_time = localtime_to_utctime (print_at_time);
-
-          if (utc_time != NULL)
-            {
-              gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
-              g_free (utc_time);
-            }
-          else
-            gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
-        }
-      else if (strcmp (print_at, "on-hold") == 0)
-       gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
-    }
-}
-
-static void
-cups_printer_prepare_for_print (GtkPrinter       *printer,
-                               GtkPrintJob      *print_job,
-                               GtkPrintSettings *settings,
-                               GtkPageSetup     *page_setup)
-{
-  GtkPrintPages pages;
-  GtkPageRange *ranges;
-  gint n_ranges;
-  GtkPageSet page_set;
-  GtkPaperSize *paper_size;
-  const char *ppd_paper_name;
-  double scale;
-  GtkPrintCapabilities  capabilities;
-
-  capabilities = cups_printer_get_capabilities (printer);
-  pages = gtk_print_settings_get_print_pages (settings);
-  gtk_print_job_set_pages (print_job, pages);
-
-  if (pages == GTK_PRINT_PAGES_RANGES)
-    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
-  else
-    {
-      ranges = NULL;
-      n_ranges = 0;
-    }
-
-  gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
-
-  if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
-    {
-      if (gtk_print_settings_get_collate (settings))
-        gtk_print_settings_set (settings, "cups-Collate", "True");
-      else
-        gtk_print_settings_set (settings, "cups-Collate", "False");
-      gtk_print_job_set_collate (print_job, FALSE);
-    }
-  else
-    {
-      gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
-    }
-
-  if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
-    {
-      if (gtk_print_settings_get_reverse (settings))
-        gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
-      gtk_print_job_set_reverse (print_job, FALSE);
-    }
-  else
-    {
-      gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
-    }
-
-  if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
-    {
-      if (gtk_print_settings_get_n_copies (settings) > 1)
-        gtk_print_settings_set_int (settings, "cups-copies",
-                                    gtk_print_settings_get_n_copies (settings));
-      gtk_print_job_set_num_copies (print_job, 1);
-    }
-  else
-    {
-      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);
-
-  page_set = gtk_print_settings_get_page_set (settings);
-  if (page_set == GTK_PAGE_SET_EVEN)
-    gtk_print_settings_set (settings, "cups-page-set", "even");
-  else if (page_set == GTK_PAGE_SET_ODD)
-    gtk_print_settings_set (settings, "cups-page-set", "odd");
-  gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
-
-  paper_size = gtk_page_setup_get_paper_size (page_setup);
-  ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
-  if (ppd_paper_name != NULL)
-    gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
-  else if (gtk_paper_size_is_ipp (paper_size))
-    gtk_print_settings_set (settings, "cups-media", gtk_paper_size_get_name (paper_size));
-  else
-    {
-      char width[G_ASCII_DTOSTR_BUF_SIZE];
-      char height[G_ASCII_DTOSTR_BUF_SIZE];
-      char *custom_name;
-
-      g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
-      g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
-      custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
-      gtk_print_settings_set (settings, "cups-PageSize", custom_name);
-      g_free (custom_name);
-    }
-
-  if (gtk_print_settings_get_number_up (settings) > 1)
-    {
-      GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
-      GEnumClass        *enum_class;
-      GEnumValue        *enum_value;
-
-      switch (gtk_page_setup_get_orientation (page_setup))
-        {
-          case GTK_PAGE_ORIENTATION_PORTRAIT:
-            break;
-          case GTK_PAGE_ORIENTATION_LANDSCAPE:
-            if (layout < 4)
-              layout = layout + 2 + 4 * (1 - layout / 2);
-            else
-              layout = layout - 3 - 2 * (layout % 2);
-            break;
-          case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
-            layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
-            break;
-          case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
-            if (layout < 4)
-              layout = layout + 5 - 2 * (layout % 2);
-            else
-              layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
-            break;
-        }
-
-      enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
-      enum_value = g_enum_get_value (enum_class, layout);
-      gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
-      g_type_class_unref (enum_class);
-
-      if (!(capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP))
-        {
-          gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
-          gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings));
-        }
-    }
-
-  gtk_print_job_set_rotate (print_job, TRUE);
-}
-
-static GtkPageSetup *
-create_page_setup (ppd_file_t *ppd_file,
-                  ppd_size_t *size)
- {
-   char *display_name;
-   GtkPageSetup *page_setup;
-   GtkPaperSize *paper_size;
-   ppd_option_t *option;
-   ppd_choice_t *choice;
-
-  display_name = NULL;
-  option = ppdFindOption (ppd_file, "PageSize");
-  if (option)
-    {
-      choice = ppdFindChoice (option, size->name);
-      if (choice)
-       display_name = ppd_text_to_utf8 (ppd_file, choice->text);
-    }
-
-  if (display_name == NULL)
-    display_name = g_strdup (size->name);
-
-  page_setup = gtk_page_setup_new ();
-  paper_size = gtk_paper_size_new_from_ppd (size->name,
-                                           display_name,
-                                           size->width,
-                                           size->length);
-  gtk_page_setup_set_paper_size (page_setup, paper_size);
-  gtk_paper_size_free (paper_size);
-
-  gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
-  gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
-  gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
-  gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
-
-  g_free (display_name);
-
-  return page_setup;
-}
-
-static GtkPageSetup *
-create_page_setup_from_media (gchar     *media,
-                              MediaSize *media_size,
-                              gboolean   media_margin_default_set,
-                              gint       media_bottom_margin_default,
-                              gint       media_top_margin_default,
-                              gint       media_left_margin_default,
-                              gint       media_right_margin_default)
-{
-  GtkPageSetup *page_setup;
-  GtkPaperSize *paper_size;
-
-  page_setup = gtk_page_setup_new ();
-  paper_size = gtk_paper_size_new_from_ipp (media,
-                                            POINTS_PER_INCH * (media_size->x_dimension / MM_PER_INCH),
-                                            POINTS_PER_INCH * (media_size->y_dimension / MM_PER_INCH));
-  gtk_page_setup_set_paper_size (page_setup, paper_size);
-  gtk_paper_size_free (paper_size);
-
-  if (media_margin_default_set)
-    {
-      gtk_page_setup_set_bottom_margin (page_setup, media_bottom_margin_default, GTK_UNIT_MM);
-      gtk_page_setup_set_top_margin (page_setup, media_top_margin_default, GTK_UNIT_MM);
-      gtk_page_setup_set_left_margin (page_setup, media_left_margin_default, GTK_UNIT_MM);
-      gtk_page_setup_set_right_margin (page_setup, media_right_margin_default, GTK_UNIT_MM);
-    }
-
-  return page_setup;
-}
-
-static GList *
-cups_printer_list_papers (GtkPrinter *printer)
-{
-  ppd_file_t *ppd_file;
-  ppd_size_t *size;
-  GtkPageSetup *page_setup;
-  GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
-  GList *result = NULL;
-  int i;
-
-  ppd_file = gtk_printer_cups_get_ppd (cups_printer);
-  if (ppd_file != NULL)
-    {
-      for (i = 0; i < ppd_file->num_sizes; i++)
-        {
-          size = &ppd_file->sizes[i];
-
-          page_setup = create_page_setup (ppd_file, size);
-
-          result = g_list_prepend (result, page_setup);
-        }
-    }
-  else if (cups_printer->media_supported != NULL &&
-           cups_printer->media_size_supported != NULL &&
-           /*
-            * 'media_supported' list can contain names of minimal and maximal sizes
-            * for which we don't create item in 'media_size_supported' list.
-            */
-           g_list_length (cups_printer->media_supported) >=
-           g_list_length (cups_printer->media_size_supported))
-    {
-      MediaSize *media_size;
-      GList     *media_iter;
-      GList     *media_size_iter;
-      gchar     *media;
-
-      for (media_iter = cups_printer->media_supported,
-           media_size_iter = cups_printer->media_size_supported;
-           media_size_iter != NULL;
-           media_iter = media_iter->next,
-           media_size_iter = media_size_iter->next)
-        {
-          media = (gchar *) media_iter->data;
-          media_size = (MediaSize *) media_size_iter->data;
-
-          page_setup = create_page_setup_from_media (media,
-                                                     media_size,
-                                                     cups_printer->media_margin_default_set,
-                                                     cups_printer->media_bottom_margin_default,
-                                                     cups_printer->media_top_margin_default,
-                                                     cups_printer->media_left_margin_default,
-                                                     cups_printer->media_right_margin_default);
-
-          result = g_list_prepend (result, page_setup);
-        }
-    }
-
-  result = g_list_reverse (result);
-
-  return result;
-}
-
-static GtkPageSetup *
-cups_printer_get_default_page_size (GtkPrinter *printer)
-{
-  GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
-  GtkPageSetup   *result = NULL;
-  ppd_option_t   *option;
-  ppd_file_t     *ppd_file;
-  ppd_size_t     *size;
-
-  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
-  if (ppd_file != NULL)
-    {
-      option = ppdFindOption (ppd_file, "PageSize");
-      if (option == NULL)
-        return NULL;
-
-      size = ppdPageSize (ppd_file, option->defchoice);
-      if (size == NULL)
-        return NULL;
-
-      result = create_page_setup (ppd_file, size);
-    }
-  else if (cups_printer->media_default != NULL)
-    {
-      MediaSize *media_size;
-      GList     *media_iter;
-      GList     *media_size_iter;
-      gchar     *media;
-
-      for (media_iter = cups_printer->media_supported,
-           media_size_iter = cups_printer->media_size_supported;
-           media_size_iter != NULL;
-           media_iter = media_iter->next,
-           media_size_iter = media_size_iter->next)
-        {
-          media = (gchar *) media_iter->data;
-          media_size = (MediaSize *) media_size_iter->data;
-
-          if (g_strcmp0 (cups_printer->media_default, media) == 0)
-            {
-              result = create_page_setup_from_media (media,
-                                                     media_size,
-                                                     cups_printer->media_margin_default_set,
-                                                     cups_printer->media_bottom_margin_default,
-                                                     cups_printer->media_top_margin_default,
-                                                     cups_printer->media_left_margin_default,
-                                                     cups_printer->media_right_margin_default);
-            }
-        }
-    }
-
-  return result;
-}
-
-static gboolean
-cups_printer_get_hard_margins (GtkPrinter *printer,
-                              gdouble    *top,
-                              gdouble    *bottom,
-                              gdouble    *left,
-                              gdouble    *right)
-{
-  GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
-  ppd_file_t     *ppd_file;
-  gboolean        result = FALSE;
-
-  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
-  if (ppd_file != NULL)
-    {
-      *left = ppd_file->custom_margins[0];
-      *bottom = ppd_file->custom_margins[1];
-      *right = ppd_file->custom_margins[2];
-      *top = ppd_file->custom_margins[3];
-      result = TRUE;
-    }
-  else if (cups_printer->media_margin_default_set)
-    {
-      *left = POINTS_PER_INCH * cups_printer->media_left_margin_default / MM_PER_INCH;
-      *bottom = POINTS_PER_INCH * cups_printer->media_bottom_margin_default / MM_PER_INCH;
-      *right = POINTS_PER_INCH * cups_printer->media_right_margin_default / MM_PER_INCH;
-      *top = POINTS_PER_INCH * cups_printer->media_top_margin_default / MM_PER_INCH;
-      result = TRUE;
-    }
-
-  return result;
-}
-
-static GtkPrintCapabilities
-cups_printer_get_capabilities (GtkPrinter *printer)
-{
-  GtkPrintCapabilities  capabilities = 0;
-  GtkPrinterCups       *cups_printer = GTK_PRINTER_CUPS (printer);
-
-  if (gtk_printer_cups_get_ppd (cups_printer))
-    {
-      capabilities = GTK_PRINT_CAPABILITY_REVERSE;
-    }
-
-  if (cups_printer->supports_copies)
-    {
-      capabilities |= GTK_PRINT_CAPABILITY_COPIES;
-    }
-
-  if (cups_printer->supports_collate)
-    {
-      capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
-    }
-
-  if (cups_printer->supports_number_up)
-    {
-      capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
-                      GTK_PRINT_CAPABILITY_NUMBER_UP;
-    }
-
-  return capabilities;
-}
-
-static void
-secrets_service_appeared_cb (GDBusConnection *connection,
-                             const gchar     *name,
-                             const gchar     *name_owner,
-                             gpointer         user_data)
-{
-  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
-
-  backend->secrets_service_available = TRUE;
-}
-
-static void
-secrets_service_vanished_cb (GDBusConnection *connection,
-                             const gchar     *name,
-                             gpointer         user_data)
-{
-  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
-
-  backend->secrets_service_available = FALSE;
-}
diff --git a/modules/printbackends/cups/gtkprintbackendcups.h b/modules/printbackends/cups/gtkprintbackendcups.h
deleted file mode 100644 (file)
index d4e35dd..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/* GTK - The GIMP Toolkit
- * gtkprintbackendcups.h: Default implementation of GtkPrintBackend for the Common Unix Print System (CUPS)
- * Copyright (C) 2006, 2007 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_CUPS_H__
-#define __GTK_PRINT_BACKEND_CUPS_H__
-
-#include <glib-object.h>
-#include "gtkprintbackend.h"
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_PRINT_BACKEND_CUPS             (gtk_print_backend_cups_get_type ())
-#define GTK_PRINT_BACKEND_CUPS(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCups))
-#define GTK_IS_PRINT_BACKEND_CUPS(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CUPS))
-
-typedef struct _GtkPrintBackendCups      GtkPrintBackendCups;
-
-GtkPrintBackend *gtk_print_backend_cups_new      (void);
-GType          gtk_print_backend_cups_get_type (void) G_GNUC_CONST;
-
-G_END_DECLS
-
-#endif /* __GTK_PRINT_BACKEND_CUPS_H__ */
-
-
diff --git a/modules/printbackends/cups/gtkprintercups.c b/modules/printbackends/cups/gtkprintercups.c
deleted file mode 100644 (file)
index 525382a..0000000
+++ /dev/null
@@ -1,650 +0,0 @@
-/* GtkPrinterCupsCups
- * Copyright (C) 2006 John (J5) Palmieri  <johnp@redhat.com>
- * Copyright (C) 2011 Richard Hughes <rhughes@redhat.com>
- *
- * 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>
-
-#ifdef HAVE_COLORD
-#include <colord.h>
-#endif
-
-#include "gtkintl.h"
-#include "gtkprintercups.h"
-
-enum {
-  PROP_0,
-  PROP_PROFILE_TITLE
-};
-
-static void gtk_printer_cups_init       (GtkPrinterCups      *printer);
-static void gtk_printer_cups_class_init (GtkPrinterCupsClass *class);
-static void gtk_printer_cups_finalize   (GObject             *object);
-
-static GtkPrinterClass *gtk_printer_cups_parent_class;
-static GType gtk_printer_cups_type = 0;
-
-static void gtk_printer_cups_set_property (GObject      *object,
-                                           guint         prop_id,
-                                           const GValue *value,
-                                           GParamSpec   *pspec);
-static void gtk_printer_cups_get_property (GObject      *object,
-                                           guint         prop_id,
-                                           GValue       *value,
-                                           GParamSpec   *pspec);
-
-void 
-gtk_printer_cups_register_type (GTypeModule *module)
-{
-  const GTypeInfo object_info =
-  {
-    sizeof (GtkPrinterCupsClass),
-    (GBaseInitFunc) NULL,
-    (GBaseFinalizeFunc) NULL,
-    (GClassInitFunc) gtk_printer_cups_class_init,
-    NULL,           /* class_finalize */
-    NULL,           /* class_data */
-    sizeof (GtkPrinterCups),
-    0,              /* n_preallocs */
-    (GInstanceInitFunc) gtk_printer_cups_init,
-  };
-
- gtk_printer_cups_type = g_type_module_register_type (module,
-                                                      GTK_TYPE_PRINTER,
-                                                      "GtkPrinterCups",
-                                                      &object_info, 0);
-}
-
-GType
-gtk_printer_cups_get_type (void)
-{
-  return gtk_printer_cups_type;
-}
-
-static void
-gtk_printer_cups_class_init (GtkPrinterCupsClass *class)
-{
-  GObjectClass *object_class = (GObjectClass *) class;
-
-  object_class->finalize = gtk_printer_cups_finalize;
-  object_class->set_property = gtk_printer_cups_set_property;
-  object_class->get_property = gtk_printer_cups_get_property;
-
-  gtk_printer_cups_parent_class = g_type_class_peek_parent (class);
-
-  g_object_class_install_property (G_OBJECT_CLASS (class),
-                                   PROP_PROFILE_TITLE,
-                                   g_param_spec_string ("profile-title",
-                                                        P_("Color Profile Title"),
-                                                        P_("The title of the color profile to use"),
-                                                        "",
-                                                        G_PARAM_READABLE));
-}
-
-static void
-gtk_printer_cups_init (GtkPrinterCups *printer)
-{
-  printer->device_uri = NULL;
-  printer->original_device_uri = NULL;
-  printer->printer_uri = NULL;
-  printer->state = 0;
-  printer->hostname = NULL;
-  printer->port = 0;
-  printer->ppd_name = NULL;
-  printer->ppd_file = NULL;
-  printer->default_cover_before = NULL;
-  printer->default_cover_after = NULL;
-  printer->remote = FALSE;
-  printer->get_remote_ppd_poll = 0;
-  printer->get_remote_ppd_attempts = 0;
-  printer->remote_cups_connection_test = NULL;
-  printer->auth_info_required = NULL;
-  printer->default_number_up = 1;
-#ifdef HAVE_CUPS_API_1_6
-  printer->avahi_browsed = FALSE;
-  printer->avahi_name = NULL;
-  printer->avahi_type = NULL;
-  printer->avahi_domain = NULL;
-#endif
-  printer->ipp_version_major = 1;
-  printer->ipp_version_minor = 1;
-  printer->supports_copies = FALSE;
-  printer->supports_collate = FALSE;
-  printer->supports_number_up = FALSE;
-  printer->media_default = NULL;
-  printer->media_supported = NULL;
-  printer->media_size_supported = NULL;
-  printer->media_bottom_margin_default = 0;
-  printer->media_top_margin_default = 0;
-  printer->media_left_margin_default = 0;
-  printer->media_right_margin_default = 0;
-  printer->media_margin_default_set = FALSE;
-  printer->sides_default = NULL;
-  printer->sides_supported = NULL;
-  printer->number_of_covers = 0;
-  printer->covers = NULL;
-  printer->output_bin_default = NULL;
-  printer->output_bin_supported = NULL;
-}
-
-static void
-gtk_printer_cups_finalize (GObject *object)
-{
-  GtkPrinterCups *printer;
-
-  g_return_if_fail (object != NULL);
-
-  printer = GTK_PRINTER_CUPS (object);
-
-  g_free (printer->device_uri);
-  g_free (printer->original_device_uri);
-  g_free (printer->printer_uri);
-  g_free (printer->hostname);
-  g_free (printer->ppd_name);
-  g_free (printer->default_cover_before);
-  g_free (printer->default_cover_after);
-  g_strfreev (printer->auth_info_required);
-
-#ifdef HAVE_COLORD
-  if (printer->colord_cancellable)
-    {
-      g_cancellable_cancel (printer->colord_cancellable);
-      g_object_unref (printer->colord_cancellable);
-    }
-  g_free (printer->colord_title);
-  g_free (printer->colord_qualifier);
-  if (printer->colord_client)
-    g_object_unref (printer->colord_client);
-  if (printer->colord_device)
-    g_object_unref (printer->colord_device);
-  if (printer->colord_profile)
-    g_object_unref (printer->colord_profile);
-#endif
-
-#ifdef HAVE_CUPS_API_1_6
-  g_free (printer->avahi_name);
-  g_free (printer->avahi_type);
-  g_free (printer->avahi_domain);
-#endif
-
-  g_strfreev (printer->covers);
-
-  if (printer->ppd_file)
-    ppdClose (printer->ppd_file);
-
-  g_free (printer->media_default);
-  g_list_free_full (printer->media_supported, g_free);
-  g_list_free_full (printer->media_size_supported, g_free);
-
-  g_free (printer->sides_default);
-  g_list_free_full (printer->sides_supported, g_free);
-
-  g_free (printer->output_bin_default);
-  g_list_free_full (printer->output_bin_supported, g_free);
-
-  if (printer->get_remote_ppd_poll > 0)
-    g_source_remove (printer->get_remote_ppd_poll);
-  printer->get_remote_ppd_attempts = 0;
-
-  gtk_cups_connection_test_free (printer->remote_cups_connection_test);
-
-  G_OBJECT_CLASS (gtk_printer_cups_parent_class)->finalize (object);
-}
-
-static void
-gtk_printer_cups_set_property (GObject         *object,
-                               guint            prop_id,
-                               const GValue    *value,
-                               GParamSpec      *pspec)
-{
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-gtk_printer_cups_get_property (GObject    *object,
-                               guint       prop_id,
-                               GValue     *value,
-                               GParamSpec *pspec)
-{
-#ifdef HAVE_COLORD
-  GtkPrinterCups *printer = GTK_PRINTER_CUPS (object);
-#endif
-
-  switch (prop_id)
-    {
-    case PROP_PROFILE_TITLE:
-#ifdef HAVE_COLORD
-      if (printer->colord_title)
-        g_value_set_string (value, printer->colord_title);
-      else
-        g_value_set_static_string (value, "");
-#else
-      g_value_set_static_string (value, NULL);
-#endif
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-#ifdef HAVE_COLORD
-
-static void
-colord_update_ui_from_settings (GtkPrinterCups *printer)
-{
-  const gchar *title = NULL;
-
-  /* not yet connected to colord */
-  if (printer->colord_client == NULL)
-    goto out;
-  if (!cd_client_get_connected (printer->colord_client))
-    goto out;
-
-  /* failed to get a colord device for the printer */
-  if (printer->colord_device == NULL)
-    {
-      /* TRANSLATORS: when we're running an old CUPS, and
-       * it hasn't registered the device with colord */
-      title = _("Color management unavailable");
-      goto out;
-    }
-
-  /* when colord prevents us from connecting (should not happen) */
-  if (!cd_device_get_connected (printer->colord_device))
-    goto out;
-
-  /* failed to get a colord device for the printer */
-  if (printer->colord_profile == NULL)
-    {
-      /* TRANSLATORS: when there is no color profile available */
-      title = _("No profile available");
-      goto out;
-    }
-
-  /* when colord prevents us from connecting (should not happen) */
-  if (!cd_profile_get_connected (printer->colord_profile))
-    goto out;
-  title = cd_profile_get_title (printer->colord_profile);
-  if (title == NULL)
-    {
-      /* TRANSLATORS: when the color profile has no title */
-      title = _("Unspecified profile");
-      goto out;
-    }
-
-out:
-  /* SUCCESS! */
-  if (g_strcmp0 (title, printer->colord_title) != 0)
-    {
-      g_free (printer->colord_title);
-      printer->colord_title = g_strdup (title);
-      g_object_notify (G_OBJECT (printer), "profile-title");
-    }
-  return;
-}
-
-static void
-colord_client_profile_connect_cb (GObject *source_object,
-                                  GAsyncResult *res,
-                                  gpointer user_data)
-{
-  gboolean ret;
-  GError *error = NULL;
-  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
-
-  ret = cd_profile_connect_finish (CD_PROFILE (source_object),
-                                   res,
-                                   &error);
-  if (!ret)
-    {
-      g_warning ("failed to get properties from the profile: %s",
-                 error->message);
-      g_error_free (error);
-    }
-
-  /* update the UI */
-  colord_update_ui_from_settings (printer);
-
-  g_object_unref (printer);
-}
-
-static void
-colord_client_device_get_profile_for_qualifiers_cb (GObject *source_object,
-                                                    GAsyncResult *res,
-                                                    gpointer user_data)
-{
-  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
-  GError *error = NULL;
-
-  printer->colord_profile = cd_device_get_profile_for_qualifiers_finish (printer->colord_device,
-                                                                         res,
-                                                                         &error);
-  if (printer->colord_profile == NULL)
-    {
-      /* not having a profile for a qualifier is not a warning */
-      g_debug ("no profile for device %s: %s",
-               cd_device_get_id (printer->colord_device),
-               error->message);
-      g_error_free (error);
-      goto out;
-    }
-
-  /* get details about the profile */
-  cd_profile_connect (printer->colord_profile,
-                      printer->colord_cancellable,
-                      colord_client_profile_connect_cb,
-                      g_object_ref (printer));
-out:
-  /* update the UI */
-  colord_update_ui_from_settings (printer);
-
-  g_object_unref (printer);
-}
-
-void
-gtk_printer_cups_update_settings (GtkPrinterCups *printer,
-                                  GtkPrintSettings *settings,
-                                  GtkPrinterOptionSet *set)
-{
-  gchar *qualifier = NULL;
-  gchar **qualifiers = NULL;
-  GtkPrinterOption *option;
-  const gchar *format[3];
-
-  /* nothing set yet */
-  if (printer->colord_device == NULL)
-    goto out;
-  if (!cd_device_get_connected (printer->colord_device))
-    goto out;
-
-  /* cupsICCQualifier1 */
-  option = gtk_printer_option_set_lookup (set, "cups-ColorSpace");
-  if (option == NULL)
-    option = gtk_printer_option_set_lookup (set, "cups-ColorModel");
-  if (option != NULL)
-    format[0] = option->value;
-  else
-    format[0] = "*";
-
-  /* cupsICCQualifier2 */
-  option = gtk_printer_option_set_lookup (set, "cups-OutputMode");
-  if (option != NULL)
-    format[1] = option->value;
-  else
-    format[1] = "*";
-
-  /* cupsICCQualifier3 */
-  option = gtk_printer_option_set_lookup (set, "cups-Resolution");
-  if (option != NULL)
-    format[2] = option->value;
-  else
-    format[2] = "*";
-
-  /* get profile for the device given the qualifier */
-  qualifier = g_strdup_printf ("%s.%s.%s,%s.%s.*,%s.*.*",
-                               format[0], format[1], format[2],
-                               format[0], format[1],
-                               format[0]);
-
-  /* only requery colord if the option that was changed would give
-   * us a different profile result */
-  if (g_strcmp0 (qualifier, printer->colord_qualifier) == 0)
-    goto out;
-
-  qualifiers = g_strsplit (qualifier, ",", -1);
-  cd_device_get_profile_for_qualifiers (printer->colord_device,
-                                        (const gchar **) qualifiers,
-                                        printer->colord_cancellable,
-                                        colord_client_device_get_profile_for_qualifiers_cb,
-                                        g_object_ref (printer));
-
-  /* save for the future */
-  g_free (printer->colord_qualifier);
-  printer->colord_qualifier = g_strdup (qualifier);
-
-  /* update the UI */
-  colord_update_ui_from_settings (printer);
-out:
-  g_free (qualifier);
-  g_strfreev (qualifiers);
-}
-
-static void
-colord_client_device_connect_cb (GObject *source_object,
-                                 GAsyncResult *res,
-                                 gpointer user_data)
-{
-  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
-  gboolean ret;
-  GError *error = NULL;
-
-  /* get details about the device */
-  ret = cd_device_connect_finish (CD_DEVICE (source_object), res, &error);
-  if (!ret)
-    {
-      g_warning ("failed to get properties from the colord device: %s",
-                 error->message);
-      g_error_free (error);
-      goto out;
-    }
-out:
-  /* update the UI */
-  colord_update_ui_from_settings (printer);
-
-  g_object_unref (printer);
-}
-
-static void
-colord_client_find_device_cb (GObject *source_object,
-                              GAsyncResult *res,
-                              gpointer user_data)
-{
-  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
-  GError *error = NULL;
-
-  /* get the new device */
-  printer->colord_device = cd_client_find_device_finish (printer->colord_client,
-                                               res,
-                                               &error);
-  if (printer->colord_device == NULL)
-    {
-      g_warning ("failed to get find a colord device: %s",
-                 error->message);
-      g_error_free (error);
-      goto out;
-    }
-
-  /* get details about the device */
-  g_cancellable_reset (printer->colord_cancellable);
-  cd_device_connect (printer->colord_device,
-                     printer->colord_cancellable,
-                     colord_client_device_connect_cb,
-                     g_object_ref (printer));
-out:
-  /* update the UI */
-  colord_update_ui_from_settings (printer);
-
-  g_object_unref (printer);
-}
-
-static void
-colord_update_device (GtkPrinterCups *printer)
-{
-  gchar *colord_device_id = NULL;
-
-  /* not yet connected to the daemon */
-  if (!cd_client_get_connected (printer->colord_client))
-    goto out;
-
-  /* not yet assigned a printer */
-  if (printer->ppd_file == NULL)
-    goto out;
-
-  /* old cached profile no longer valid */
-  if (printer->colord_profile)
-    {
-      g_object_unref (printer->colord_profile);
-      printer->colord_profile = NULL;
-    }
-
-  /* old cached device no longer valid */
-  if (printer->colord_device)
-    {
-      g_object_unref (printer->colord_device);
-      printer->colord_device = NULL;
-    }
-
-  /* generate a known ID */
-  colord_device_id = g_strdup_printf ("cups-%s", gtk_printer_get_name (GTK_PRINTER (printer)));
-
-  g_cancellable_reset (printer->colord_cancellable);
-  cd_client_find_device (printer->colord_client,
-                         colord_device_id,
-                         printer->colord_cancellable,
-                         colord_client_find_device_cb,
-                         g_object_ref (printer));
-out:
-  g_free (colord_device_id);
-
-  /* update the UI */
-  colord_update_ui_from_settings (printer);
-}
-
-static void
-colord_client_connect_cb (GObject *source_object,
-                          GAsyncResult *res,
-                          gpointer user_data)
-{
-  gboolean ret;
-  GError *error = NULL;
-  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
-  static gboolean colord_warned = FALSE;
-
-  ret = cd_client_connect_finish (CD_CLIENT (source_object),
-                                  res, &error);
-  if (!ret)
-    {
-      if (!colord_warned)
-        {
-          g_warning ("failed to contact colord: %s", error->message);
-          colord_warned = TRUE;
-        }
-      g_error_free (error);
-    }
-
-  /* refresh the device */
-  colord_update_device (printer);
-
-  g_object_unref (printer);
-}
-
-static void
-colord_printer_details_aquired_cb (GtkPrinterCups *printer,
-                                   gboolean success,
-                                   gpointer user_data)
-{
-  /* refresh the device */
-  if (printer->colord_client)
-    colord_update_device (printer);
-}
-#endif
-
-/**
- * gtk_printer_cups_new:
- *
- * Creates a new #GtkPrinterCups.
- *
- * Returns: a new #GtkPrinterCups
- *
- * Since: 2.10
- **/
-GtkPrinterCups *
-gtk_printer_cups_new (const char      *name,
-                      GtkPrintBackend *backend,
-                      gpointer         colord_client)
-{
-  GObject *result;
-  gboolean accepts_pdf;
-  GtkPrinterCups *printer;
-
-#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
-  accepts_pdf = TRUE;
-#else
-  accepts_pdf = FALSE;
-#endif
-
-  result = g_object_new (GTK_TYPE_PRINTER_CUPS,
-                        "name", name,
-                        "backend", backend,
-                        "is-virtual", FALSE,
-                        "accepts-pdf", accepts_pdf,
-                         NULL);
-  printer = GTK_PRINTER_CUPS (result);
-
-#ifdef HAVE_COLORD
-  /* connect to colord */
-  if (colord_client != NULL)
-    {
-      printer->colord_cancellable = g_cancellable_new ();
-      printer->colord_client = g_object_ref (CD_CLIENT (colord_client));
-      cd_client_connect (printer->colord_client,
-                         printer->colord_cancellable,
-                         colord_client_connect_cb,
-                         g_object_ref (printer));
-    }
-
-    /* update the device when we read the PPD */
-    g_signal_connect (printer, "details-acquired",
-                      G_CALLBACK (colord_printer_details_aquired_cb),
-                      printer);
-#endif
-
-  /*
-   * IPP version 1.1 has to be supported
-   * by all implementations according to rfc 2911
-   */
-  printer->ipp_version_major = 1;
-  printer->ipp_version_minor = 1;
-
-  return printer;
-}
-
-ppd_file_t *
-gtk_printer_cups_get_ppd (GtkPrinterCups *printer)
-{
-  return printer->ppd_file;
-}
-
-const gchar *
-gtk_printer_cups_get_ppd_name (GtkPrinterCups  *printer)
-{
-  const gchar *result;
-
-  result = printer->ppd_name;
-
-  if (result == NULL)
-    result = gtk_printer_get_name (GTK_PRINTER (printer));
-
-  return result;
-}
diff --git a/modules/printbackends/cups/gtkprintercups.h b/modules/printbackends/cups/gtkprintercups.h
deleted file mode 100644 (file)
index f26bbab..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* GtkPrinterCups
- * Copyright (C) 2006 John (J5) Palmieri <johnp@redhat.com>
- *
- * 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_CUPS_H__
-#define __GTK_PRINTER_CUPS_H__
-
-#include <glib-object.h>
-#include <cups/cups.h>
-#include <cups/ppd.h>
-#include "gtkcupsutils.h"
-
-#include <gtk/gtkunixprint.h>
-#include <gtk/gtkprinter-private.h>
-
-#ifdef HAVE_COLORD
-#include <colord.h>
-#endif
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_PRINTER_CUPS                  (gtk_printer_cups_get_type ())
-#define GTK_PRINTER_CUPS(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCups))
-#define GTK_PRINTER_CUPS_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass))
-#define GTK_IS_PRINTER_CUPS(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CUPS))
-#define GTK_IS_PRINTER_CUPS_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CUPS))
-#define GTK_PRINTER_CUPS_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass))
-
-typedef struct _GtkPrinterCups         GtkPrinterCups;
-typedef struct _GtkPrinterCupsClass     GtkPrinterCupsClass;
-typedef struct _GtkPrinterCupsPrivate   GtkPrinterCupsPrivate;
-
-struct _GtkPrinterCups
-{
-  GtkPrinter parent_instance;
-
-  gchar *device_uri;
-  gchar *original_device_uri;
-  gchar *printer_uri;
-  gchar *hostname;
-  gint port;
-  gchar **auth_info_required;
-
-  ipp_pstate_t state;
-  gboolean reading_ppd;
-  gchar      *ppd_name;
-  ppd_file_t *ppd_file;
-
-  gchar    *media_default;
-  GList    *media_supported;
-  GList    *media_size_supported;
-  gint      media_bottom_margin_default;
-  gint      media_top_margin_default;
-  gint      media_left_margin_default;
-  gint      media_right_margin_default;
-  gboolean  media_margin_default_set;
-  gchar    *sides_default;
-  GList    *sides_supported;
-  gchar    *output_bin_default;
-  GList    *output_bin_supported;
-
-  gchar  *default_cover_before;
-  gchar  *default_cover_after;
-
-  gint    default_number_up;
-
-  gboolean remote;
-  guint get_remote_ppd_poll;
-  gint  get_remote_ppd_attempts;
-  GtkCupsConnectionTest *remote_cups_connection_test;
-#ifdef HAVE_COLORD
-  CdClient     *colord_client;
-  CdDevice     *colord_device;
-  CdProfile    *colord_profile;
-  GCancellable *colord_cancellable;
-  gchar        *colord_title;
-  gchar        *colord_qualifier;
-#endif
-#ifdef HAVE_CUPS_API_1_6
-  gboolean  avahi_browsed;
-  gchar    *avahi_name;
-  gchar    *avahi_type;
-  gchar    *avahi_domain;
-#endif
-  guchar ipp_version_major;
-  guchar ipp_version_minor;
-  gboolean supports_copies;
-  gboolean supports_collate;
-  gboolean supports_number_up;
-  char   **covers;
-  int      number_of_covers;
-};
-
-struct _GtkPrinterCupsClass
-{
-  GtkPrinterClass parent_class;
-
-};
-
-GType                    gtk_printer_cups_get_type      (void) G_GNUC_CONST;
-void                     gtk_printer_cups_register_type (GTypeModule     *module);
-
-GtkPrinterCups          *gtk_printer_cups_new           (const char      *name,
-                                                         GtkPrintBackend *backend,
-                                                         gpointer         colord_client);
-ppd_file_t             *gtk_printer_cups_get_ppd       (GtkPrinterCups  *printer);
-const gchar            *gtk_printer_cups_get_ppd_name  (GtkPrinterCups  *printer);
-
-#ifdef HAVE_COLORD
-void                     gtk_printer_cups_update_settings (GtkPrinterCups *printer,
-                                                         GtkPrintSettings *settings,
-                                                         GtkPrinterOptionSet *set);
-#endif
-
-G_END_DECLS
-
-#endif /* __GTK_PRINTER_CUPS_H__ */
diff --git a/modules/printbackends/cups/meson.build b/modules/printbackends/cups/meson.build
deleted file mode 100644 (file)
index ef7a30e..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-enable_colord = get_option('colord')
-if enable_colord != 'no'
-  want_colord = enable_colord == 'yes'
-  colord_dep = dependency('colord', version: '>= 0.1.9', required: want_colord)
-  cdata.set('HAVE_COLORD', colord_dep.found())
-else
-  colord_dep = []
-endif
-
-shared_module('printbackend-cups',
-              'gtkprintbackendcups.c',
-              'gtkprintercups.c',
-              'gtkcupsutils.c',
-              'gtkcupssecretsutils.c',
-              c_args: [
-                '-DGTK_COMPILATION',
-                '-DGTK_DISABLE_DEPRECATION_WARNINGS',
-                '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
-              ],
-              dependencies: [libgtk_dep, libcups, colord_dep],
-              install_dir: printbackends_install_dir,
-              install : true)
diff --git a/modules/printbackends/file/gtkprintbackendfile.c b/modules/printbackends/file/gtkprintbackendfile.c
deleted file mode 100644 (file)
index b568e01..0000000
+++ /dev/null
@@ -1,846 +0,0 @@
-/* GTK - The GIMP Toolkit
- * gtkprintbackendfile.c: Default implementation of GtkPrintBackend 
- * for printing to a file
- * Copyright (C) 2003, 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 <cairo-svg.h>
-
-#include <glib/gi18n-lib.h>
-
-#include "gtk/gtk.h"
-#include "gtk/gtkprinter-private.h"
-
-#include "gtkprintbackendfile.h"
-
-typedef struct _GtkPrintBackendFileClass GtkPrintBackendFileClass;
-
-#define GTK_PRINT_BACKEND_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass))
-#define GTK_IS_PRINT_BACKEND_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_FILE))
-#define GTK_PRINT_BACKEND_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass))
-
-#define _STREAM_MAX_CHUNK_SIZE 8192
-
-struct _GtkPrintBackendFileClass
-{
-  GtkPrintBackendClass parent_class;
-};
-
-struct _GtkPrintBackendFile
-{
-  GtkPrintBackend parent_instance;
-};
-
-typedef enum
-{
-  FORMAT_PDF,
-  FORMAT_PS,
-  FORMAT_SVG,
-  N_FORMATS
-} OutputFormat;
-
-static const gchar* formats[N_FORMATS] =
-{
-  "pdf",
-  "ps",
-  "svg"
-};
-
-static GObjectClass *backend_parent_class;
-
-static void                 gtk_print_backend_file_class_init      (GtkPrintBackendFileClass *class);
-static void                 gtk_print_backend_file_init            (GtkPrintBackendFile      *impl);
-static void                 file_printer_get_settings_from_options (GtkPrinter              *printer,
-                                                                   GtkPrinterOptionSet     *options,
-                                                                   GtkPrintSettings        *settings);
-static GtkPrinterOptionSet *file_printer_get_options               (GtkPrinter              *printer,
-                                                                   GtkPrintSettings        *settings,
-                                                                   GtkPageSetup            *page_setup,
-                                                                   GtkPrintCapabilities     capabilities);
-static void                 file_printer_prepare_for_print         (GtkPrinter              *printer,
-                                                                   GtkPrintJob             *print_job,
-                                                                   GtkPrintSettings        *settings,
-                                                                   GtkPageSetup            *page_setup);
-static void                 gtk_print_backend_file_print_stream    (GtkPrintBackend         *print_backend,
-                                                                   GtkPrintJob             *job,
-                                                                   GIOChannel              *data_io,
-                                                                   GtkPrintJobCompleteFunc  callback,
-                                                                   gpointer                 user_data,
-                                                                   GDestroyNotify           dnotify);
-static cairo_surface_t *    file_printer_create_cairo_surface      (GtkPrinter              *printer,
-                                                                   GtkPrintSettings        *settings,
-                                                                   gdouble                  width,
-                                                                   gdouble                  height,
-                                                                   GIOChannel              *cache_io);
-
-static GList *              file_printer_list_papers               (GtkPrinter              *printer);
-static GtkPageSetup *       file_printer_get_default_page_size     (GtkPrinter              *printer);
-
-G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendFile, gtk_print_backend_file, GTK_TYPE_PRINT_BACKEND)
-
-void
-g_io_module_load (GIOModule *module)
-{
-  g_type_module_use (G_TYPE_MODULE (module));
-
-  gtk_print_backend_file_register_type (G_TYPE_MODULE (module));
-
-  g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
-                                  GTK_TYPE_PRINT_BACKEND_FILE,
-                                  "file",
-                                  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_file_new:
- *
- * Creates a new #GtkPrintBackendFile object. #GtkPrintBackendFile
- * implements the #GtkPrintBackend interface with direct access to
- * the filesystem using Unix/Linux API calls
- *
- * Returns: the new #GtkPrintBackendFile object
- **/
-GtkPrintBackend *
-gtk_print_backend_file_new (void)
-{
-  return g_object_new (GTK_TYPE_PRINT_BACKEND_FILE, NULL);
-}
-
-static void
-gtk_print_backend_file_class_init (GtkPrintBackendFileClass *class)
-{
-  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
-
-  backend_parent_class = g_type_class_peek_parent (class);
-
-  backend_class->print_stream = gtk_print_backend_file_print_stream;
-  backend_class->printer_create_cairo_surface = file_printer_create_cairo_surface;
-  backend_class->printer_get_options = file_printer_get_options;
-  backend_class->printer_get_settings_from_options = file_printer_get_settings_from_options;
-  backend_class->printer_prepare_for_print = file_printer_prepare_for_print;
-  backend_class->printer_list_papers = file_printer_list_papers;
-  backend_class->printer_get_default_page_size = file_printer_get_default_page_size;
-}
-
-static void
-gtk_print_backend_file_class_finalize (GtkPrintBackendFileClass *class)
-{
-}
-
-/* return N_FORMATS if no explicit format in the settings */
-static OutputFormat
-format_from_settings (GtkPrintSettings *settings)
-{
-  const gchar *value;
-  gint i;
-
-  if (settings == NULL)
-    return N_FORMATS;
-
-  value = gtk_print_settings_get (settings,
-                                  GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
-  if (value == NULL)
-    return N_FORMATS;
-
-  for (i = 0; i < N_FORMATS; ++i)
-    if (strcmp (value, formats[i]) == 0)
-      break;
-
-  g_assert (i < N_FORMATS);
-
-  return (OutputFormat) i;
-}
-
-static gchar *
-output_file_from_settings (GtkPrintSettings *settings,
-                          const gchar      *default_format)
-{
-  gchar *uri = NULL;
-
-  if (settings)
-    uri = g_strdup (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI));
-
-  if (uri == NULL)
-    { 
-      const gchar *extension, *basename = NULL, *output_dir = NULL;
-      gchar *name, *locale_name, *path;
-
-      if (default_format)
-        extension = default_format;
-      else
-        {
-          OutputFormat format;
-
-          format = format_from_settings (settings);
-          switch (format)
-            {
-              default:
-              case FORMAT_PDF:
-                extension = "pdf";
-                break;
-              case FORMAT_PS:
-                extension = "ps";
-                break;
-              case FORMAT_SVG:
-                extension = "svg";
-                break;
-            }
-        }
-
-      if (settings)
-        basename = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME);
-      if (basename == NULL)
-        basename = _("output");
-
-      name = g_strconcat (basename, ".", extension, NULL);
-
-      locale_name = g_filename_from_utf8 (name, -1, NULL, NULL, NULL);
-      g_free (name);
-
-      if (locale_name != NULL)
-        {
-          if (settings)
-            output_dir = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_DIR);
-          if (output_dir == NULL)
-            {
-              const gchar *document_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
-
-              if (document_dir == NULL)
-                {
-                  gchar *current_dir = g_get_current_dir ();
-                  path = g_build_filename (current_dir, locale_name, NULL);
-                  g_free (current_dir);
-                }
-              else
-                path = g_build_filename (document_dir, locale_name, NULL);
-
-              uri = g_filename_to_uri (path, NULL, NULL); 
-           }
-          else
-            {
-              path = g_build_filename (output_dir, locale_name, NULL);
-              uri = g_filename_to_uri (path, NULL, NULL);
-            }
-
-          g_free (path); 
-          g_free (locale_name);
-        }
-    }
-
-  return uri;
-}
-
-static cairo_status_t
-_cairo_write (void                *closure,
-              const unsigned char *data,
-              unsigned int         length)
-{
-  GIOChannel *io = (GIOChannel *)closure;
-  gsize written = 0;
-  GError *error;
-
-  error = NULL;
-
-  GTK_NOTE (PRINTING,
-            g_print ("FILE Backend: Writting %u byte chunk to temp file\n", length));
-
-  while (length > 0) 
-    {
-      GIOStatus status;
-
-      status = g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error);
-
-      if (status == G_IO_STATUS_ERROR)
-        {
-          if (error != NULL)
-            {
-              GTK_NOTE (PRINTING,
-                        g_print ("FILE Backend: Error writting to temp file, %s\n", error->message));
-
-              g_error_free (error);
-            }
-
-         return CAIRO_STATUS_WRITE_ERROR;
-       }    
-
-      GTK_NOTE (PRINTING,
-                g_print ("FILE Backend: Wrote %zd bytes to temp file\n", written));
-      
-      data += written;
-      length -= written;
-    }
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-
-static cairo_surface_t *
-file_printer_create_cairo_surface (GtkPrinter       *printer,
-                                  GtkPrintSettings *settings,
-                                  gdouble           width, 
-                                  gdouble           height,
-                                  GIOChannel       *cache_io)
-{
-  cairo_surface_t *surface;
-  OutputFormat format;
-  const cairo_svg_version_t *versions;
-  int num_versions = 0;
-
-  format = format_from_settings (settings);
-
-  switch (format)
-    {
-      default:
-      case FORMAT_PDF:
-        surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height);
-        break;
-      case FORMAT_PS:
-        surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height);
-        break;
-      case FORMAT_SVG:
-        surface = cairo_svg_surface_create_for_stream (_cairo_write, cache_io, width, height);
-        cairo_svg_get_versions (&versions, &num_versions);
-        if (num_versions > 0)
-          cairo_svg_surface_restrict_to_version (surface, versions[num_versions - 1]);
-        break;
-    }
-
-  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;
-  GFileOutputStream *target_io_stream;
-  gpointer user_data;
-  GDestroyNotify dnotify;
-} _PrintStreamData;
-
-static void
-file_print_cb (GtkPrintBackendFile *print_backend,
-               GError              *error,
-               gpointer            user_data)
-{
-  gchar *uri;
-
-  _PrintStreamData *ps = (_PrintStreamData *) user_data;
-  GtkRecentManager *recent_manager;
-
-  if (ps->target_io_stream != NULL)
-    (void)g_output_stream_close (G_OUTPUT_STREAM (ps->target_io_stream), NULL, NULL);
-
-  if (ps->callback)
-    ps->callback (ps->job, ps->user_data, error);
-
-  if (ps->dnotify)
-    ps->dnotify (ps->user_data);
-
-  gtk_print_job_set_status (ps->job,
-                           (error != NULL)
-                              ? GTK_PRINT_STATUS_FINISHED_ABORTED
-                              : GTK_PRINT_STATUS_FINISHED);
-
-  recent_manager = gtk_recent_manager_get_default ();
-  uri = output_file_from_settings (gtk_print_job_get_settings (ps->job), NULL);
-  gtk_recent_manager_add_item (recent_manager, uri);
-  g_free (uri);
-
-  if (ps->job)
-    g_object_unref (ps->job);
-
-  g_free (ps);
-}
-
-static gboolean
-file_write (GIOChannel   *source,
-            GIOCondition  con,
-            gpointer      user_data)
-{
-  gchar buf[_STREAM_MAX_CHUNK_SIZE];
-  gsize bytes_read;
-  GError *error;
-  GIOStatus read_status;
-  _PrintStreamData *ps = (_PrintStreamData *) user_data;
-
-  error = NULL;
-
-  read_status =
-    g_io_channel_read_chars (source,
-                             buf,
-                             _STREAM_MAX_CHUNK_SIZE,
-                             &bytes_read,
-                             &error);
-
-  if (read_status != G_IO_STATUS_ERROR)
-    {
-      gsize bytes_written;
-
-      g_output_stream_write_all (G_OUTPUT_STREAM (ps->target_io_stream),
-                                 buf,
-                                 bytes_read,
-                                 &bytes_written,
-                                 NULL,
-                                 &error);
-    }
-
-  if (error != NULL || read_status == G_IO_STATUS_EOF)
-    {
-      file_print_cb (GTK_PRINT_BACKEND_FILE (ps->backend), error, user_data);
-
-      if (error != NULL)
-        {
-          GTK_NOTE (PRINTING,
-                    g_print ("FILE Backend: %s\n", error->message));
-
-          g_error_free (error);
-        }
-
-      return FALSE;
-    }
-
-  GTK_NOTE (PRINTING,
-            g_print ("FILE Backend: Writting %lu byte chunk to target file\n", bytes_read));
-
-  return TRUE;
-}
-
-static void
-gtk_print_backend_file_print_stream (GtkPrintBackend        *print_backend,
-                                    GtkPrintJob            *job,
-                                    GIOChannel             *data_io,
-                                    GtkPrintJobCompleteFunc callback,
-                                    gpointer                user_data,
-                                    GDestroyNotify          dnotify)
-{
-  GError *internal_error = NULL;
-  _PrintStreamData *ps;
-  GtkPrintSettings *settings;
-  gchar *uri;
-  GFile *file = NULL;
-
-  settings = gtk_print_job_get_settings (job);
-
-  ps = g_new0 (_PrintStreamData, 1);
-  ps->callback = callback;
-  ps->user_data = user_data;
-  ps->dnotify = dnotify;
-  ps->job = g_object_ref (job);
-  ps->backend = print_backend;
-
-  internal_error = NULL;
-  uri = output_file_from_settings (settings, NULL);
-
-  if (uri == NULL)
-    goto error;
-
-  file = g_file_new_for_uri (uri);
-  ps->target_io_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &internal_error);
-
-  g_object_unref (file);
-  g_free (uri);
-
-error:
-  if (internal_error != NULL)
-    {
-      file_print_cb (GTK_PRINT_BACKEND_FILE (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) file_write,
-                  ps);
-}
-
-static void
-gtk_print_backend_file_init (GtkPrintBackendFile *backend)
-{
-  GtkPrinter *printer;
-  
-  printer = g_object_new (GTK_TYPE_PRINTER,
-                         "name", _("Print to File"),
-                         "backend", backend,
-                         "is-virtual", TRUE,
-                         "accepts-pdf", TRUE,
-                         NULL); 
-
-  gtk_printer_set_has_details (printer, TRUE);
-  gtk_printer_set_icon_name (printer, "document-save");
-  gtk_printer_set_is_active (printer, TRUE);
-
-  gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer);
-  g_object_unref (printer);
-
-  gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
-}
-
-typedef struct {
-  GtkPrinter          *printer;
-  GtkPrinterOptionSet *set;
-} _OutputFormatChangedData;
-
-static void
-set_printer_format_from_option_set (GtkPrinter          *printer,
-                                   GtkPrinterOptionSet *set)
-{
-  GtkPrinterOption *format_option;
-  const gchar *value;
-  gint i;
-
-  format_option = gtk_printer_option_set_lookup (set, "output-file-format");
-  if (format_option && format_option->value)
-    {
-      value = format_option->value;
-      if (value)
-        {
-         for (i = 0; i < N_FORMATS; ++i)
-           if (strcmp (value, formats[i]) == 0)
-             break;
-
-         g_assert (i < N_FORMATS);
-
-         switch (i)
-           {
-             case FORMAT_PDF:
-               gtk_printer_set_accepts_pdf (printer, TRUE);
-               gtk_printer_set_accepts_ps (printer, FALSE);
-               break;
-             case FORMAT_PS:
-               gtk_printer_set_accepts_pdf (printer, FALSE);
-               gtk_printer_set_accepts_ps (printer, TRUE);
-               break;
-             case FORMAT_SVG:
-             default:
-               gtk_printer_set_accepts_pdf (printer, FALSE);
-               gtk_printer_set_accepts_ps (printer, FALSE);
-               break;
-           }
-       }
-    }
-}
-
-static void
-file_printer_output_file_format_changed (GtkPrinterOption    *format_option,
-                                        gpointer             user_data)
-{
-  GtkPrinterOption *uri_option;
-  gchar            *base = NULL;
-  _OutputFormatChangedData *data = (_OutputFormatChangedData *) user_data;
-
-  if (! format_option->value)
-    return;
-
-  uri_option = gtk_printer_option_set_lookup (data->set,
-                                              "gtk-main-page-custom-input");
-
-  if (uri_option && uri_option->value)
-    {
-      const gchar *uri = uri_option->value;
-      const gchar *dot = strrchr (uri, '.');
-
-      if (dot)
-        {
-          gint i;
-
-          /*  check if the file extension matches one of the known ones  */
-          for (i = 0; i < N_FORMATS; i++)
-            if (strcmp (dot + 1, formats[i]) == 0)
-              break;
-
-          if (i < N_FORMATS && strcmp (formats[i], format_option->value))
-            {
-              /*  the file extension is known but doesn't match the
-               *  selected one, strip it away
-               */
-              base = g_strndup (uri, dot - uri);
-            }
-        }
-      else
-        {
-          /*  there's no file extension  */
-          base = g_strdup (uri);
-        }
-    }
-
-  if (base)
-    {
-      gchar *tmp = g_strdup_printf ("%s.%s", base, format_option->value);
-
-      gtk_printer_option_set (uri_option, tmp);
-      g_free (tmp);
-      g_free (base);
-    }
-
-  set_printer_format_from_option_set (data->printer, data->set);
-}
-
-static GtkPrinterOptionSet *
-file_printer_get_options (GtkPrinter           *printer,
-                         GtkPrintSettings     *settings,
-                         GtkPageSetup         *page_setup,
-                         GtkPrintCapabilities  capabilities)
-{
-  GtkPrinterOptionSet *set;
-  GtkPrinterOption *option;
-  const gchar *n_up[] = {"1", "2", "4", "6", "9", "16" };
-  const gchar *pages_per_sheet = NULL;
-  const gchar *format_names[N_FORMATS] = { N_("PDF"), N_("PostScript"), N_("SVG") };
-  const gchar *supported_formats[N_FORMATS];
-  gchar *display_format_names[N_FORMATS];
-  gint n_formats = 0;
-  OutputFormat format;
-  gchar *uri;
-  gint current_format = 0;
-  _OutputFormatChangedData *format_changed_data;
-
-  format = format_from_settings (settings);
-
-  set = gtk_printer_option_set_new ();
-
-  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)! */);
-  if (settings)
-    pages_per_sheet = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_NUMBER_UP);
-  if (pages_per_sheet)
-    gtk_printer_option_set (option, pages_per_sheet);
-  else
-    gtk_printer_option_set (option, "1");
-  gtk_printer_option_set_add (set, option);
-  g_object_unref (option);
-
-  if (capabilities & (GTK_PRINT_CAPABILITY_GENERATE_PDF | GTK_PRINT_CAPABILITY_GENERATE_PS))
-    {
-      if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PDF)
-        {
-         if (format == FORMAT_PDF || format == N_FORMATS)
-            {
-              format = FORMAT_PDF;
-             current_format = n_formats;
-            }
-          supported_formats[n_formats] = formats[FORMAT_PDF];
-         display_format_names[n_formats] = _(format_names[FORMAT_PDF]);
-         n_formats++;
-       }
-      if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PS)
-        {
-         if (format == FORMAT_PS || format == N_FORMATS)
-           current_format = n_formats;
-          supported_formats[n_formats] = formats[FORMAT_PS];
-          display_format_names[n_formats] = _(format_names[FORMAT_PS]);
-         n_formats++;
-       }
-    }
-  else
-    {
-      switch (format)
-        {
-          default:
-          case FORMAT_PDF:
-            current_format = FORMAT_PDF;
-            break;
-          case FORMAT_PS:
-            current_format = FORMAT_PS;
-            break;
-          case FORMAT_SVG:
-            current_format = FORMAT_SVG;            
-            break;
-        }
-
-      for (n_formats = 0; n_formats < N_FORMATS; ++n_formats)
-        {
-         supported_formats[n_formats] = formats[n_formats];
-          display_format_names[n_formats] = _(format_names[n_formats]);
-       }
-    }
-
-  uri = output_file_from_settings (settings, supported_formats[current_format]);
-
-  option = gtk_printer_option_new ("gtk-main-page-custom-input", _("File"), 
-                                  GTK_PRINTER_OPTION_TYPE_FILESAVE);
-  gtk_printer_option_set_activates_default (option, TRUE);
-  gtk_printer_option_set (option, uri);
-  g_free (uri);
-  option->group = g_strdup ("GtkPrintDialogExtension");
-  gtk_printer_option_set_add (set, option);
-
-  if (n_formats > 1)
-    {
-      option = gtk_printer_option_new ("output-file-format", _("_Output format"), 
-                                      GTK_PRINTER_OPTION_TYPE_ALTERNATIVE);
-      option->group = g_strdup ("GtkPrintDialogExtension");
-
-      gtk_printer_option_choices_from_array (option, n_formats,
-                                            (char **) supported_formats,
-                                            display_format_names);
-      gtk_printer_option_set (option, supported_formats[current_format]);
-      gtk_printer_option_set_add (set, option);
-
-      set_printer_format_from_option_set (printer, set);
-      format_changed_data = g_new (_OutputFormatChangedData, 1);
-      format_changed_data->printer = printer;
-      format_changed_data->set = set;
-      g_signal_connect_data (option, "changed",
-                            G_CALLBACK (file_printer_output_file_format_changed),
-                            format_changed_data, (GClosureNotify)g_free, 0);
-
-      g_object_unref (option);
-    }
-
-  return set;
-}
-
-static void
-file_printer_get_settings_from_options (GtkPrinter          *printer,
-                                       GtkPrinterOptionSet *options,
-                                       GtkPrintSettings    *settings)
-{
-  GtkPrinterOption *option;
-
-  option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input");
-  gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, option->value);
-
-  option = gtk_printer_option_set_lookup (options, "output-file-format");
-  if (option)
-    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT, option->value);
-
-  option = gtk_printer_option_set_lookup (options, "gtk-n-up");
-  if (option)
-    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value);
-
-  option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout");
-  if (option)
-    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value);
-}
-
-static void
-file_printer_prepare_for_print (GtkPrinter       *printer,
-                               GtkPrintJob      *print_job,
-                               GtkPrintSettings *settings,
-                               GtkPageSetup     *page_setup)
-{
-  gdouble scale;
-  GtkPrintPages pages;
-  GtkPageRange *ranges;
-  gint n_ranges;
-  OutputFormat format;
-
-  pages = gtk_print_settings_get_print_pages (settings);
-  gtk_print_job_set_pages (print_job, pages);
-
-  if (pages == GTK_PRINT_PAGES_RANGES)
-    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
-  else
-    {
-      ranges = NULL;
-      n_ranges = 0;
-    }
-
-  gtk_print_job_set_page_ranges (print_job, ranges, n_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));
-  gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
-  gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (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));
-
-  format = format_from_settings (settings);
-  switch (format)
-    {
-      case FORMAT_PDF:
-       gtk_print_job_set_rotate (print_job, FALSE);
-        break;
-      default:
-      case FORMAT_PS:
-      case FORMAT_SVG:
-       gtk_print_job_set_rotate (print_job, TRUE);
-        break;
-    }
-}
-
-static GList *
-file_printer_list_papers (GtkPrinter *printer)
-{
-  GList *result = NULL;
-  GList *papers, *p;
-  GtkPageSetup *page_setup;
-
-  papers = gtk_paper_size_get_paper_sizes (FALSE);
-
-  for (p = papers; p; p = p->next)
-    {
-      GtkPaperSize *paper_size = p->data;
-
-      page_setup = gtk_page_setup_new ();
-      gtk_page_setup_set_paper_size (page_setup, paper_size);
-      gtk_paper_size_free (paper_size);
-      result = g_list_prepend (result, page_setup);
-    }
-
-  g_list_free (papers);
-
-  return g_list_reverse (result);
-}
-
-static GtkPageSetup *
-file_printer_get_default_page_size (GtkPrinter *printer)
-{
-  GtkPageSetup *result = NULL;
-
-  return result;
-}
diff --git a/modules/printbackends/file/gtkprintbackendfile.h b/modules/printbackends/file/gtkprintbackendfile.h
deleted file mode 100644 (file)
index b4ae41a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/* GTK - The GIMP Toolkit
- * gtkprintbackendpdf.h: Default implementation of GtkPrintBackend 
- * for printing to a file
- * Copyright (C) 2003, 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_FILE_H__
-#define __GTK_PRINT_BACKEND_FILE_H__
-
-#include <glib-object.h>
-#include "gtkprintbackend.h"
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_PRINT_BACKEND_FILE    (gtk_print_backend_file_get_type ())
-#define GTK_PRINT_BACKEND_FILE(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFile))
-#define GTK_IS_PRINT_BACKEND_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_FILE))
-
-typedef struct _GtkPrintBackendFile    GtkPrintBackendFile;
-
-GtkPrintBackend *gtk_print_backend_file_new      (void);
-GType            gtk_print_backend_file_get_type (void) G_GNUC_CONST;
-
-G_END_DECLS
-
-#endif /* __GTK_PRINT_BACKEND_FILE_H__ */
diff --git a/modules/printbackends/file/meson.build b/modules/printbackends/file/meson.build
deleted file mode 100644 (file)
index b1604be..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-shared_module('printbackend-file',
-              'gtkprintbackendfile.c',
-              c_args: [
-                '-DGTK_COMPILATION',
-                '-DGTK_DISABLE_DEPRECATION_WARNINGS',
-                '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
-              ],
-              dependencies: libgtk_dep,
-              install_dir: printbackends_install_dir,
-              install : true)
diff --git a/modules/printbackends/gtkcloudprintaccount.c b/modules/printbackends/gtkcloudprintaccount.c
new file mode 100644 (file)
index 0000000..ee65a18
--- /dev/null
@@ -0,0 +1,662 @@
+/* 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.
+ *
+ * Returns: 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;
+  GTask *task;
+  GError *error = NULL;
+
+  GTK_NOTE (PRINTING,
+           g_print ("Cloud Print Backend: (%p) 'printer' REST call for "
+                    "printer id %s", 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_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/gtkcloudprintaccount.h b/modules/printbackends/gtkcloudprintaccount.h
new file mode 100644 (file)
index 0000000..ef78874
--- /dev/null
@@ -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/gtkcupssecretsutils.c b/modules/printbackends/gtkcupssecretsutils.c
new file mode 100644 (file)
index 0000000..925f7d5
--- /dev/null
@@ -0,0 +1,1041 @@
+/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords
+ * Copyright (C) 2014, Intevation GmbH
+ *
+ * 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 <glib.h>
+#include <gio/gio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gtkcupssecretsutils.h"
+
+#define SECRETS_BUS              "org.freedesktop.secrets"
+#define SECRETS_IFACE(interface) "org.freedesktop.Secret."interface
+#define SECRETS_PATH             "/org/freedesktop/secrets"
+#define SECRETS_TIMEOUT          5000
+
+typedef enum
+{
+  SECRETS_SERVICE_ACTION_QUERY,
+  SECRETS_SERVICE_ACTION_STORE
+} SecretsServiceAction;
+
+typedef struct
+{
+  GDBusConnection       *dbus_connection;
+  SecretsServiceAction   action;
+  gchar                **auth_info,
+                       **auth_info_labels,
+                       **auth_info_required,
+                        *printer_uri,
+                        *session_path,
+                        *collection_path;
+  GDBusProxy            *item_proxy;
+  guint                  prompt_subscription;
+} SecretsServiceData;
+
+/**
+ * create_attributes:
+ * @printer_uri: URI for the printer
+ * @additional_labels: Optional labels for additional attributes
+ * @additional_attrs: Optional additional attributes
+ *
+ * Creates a GVariant dictionary with key / value pairs that
+ * can be used to identify a secret item.
+ *
+ * Returns: A GVariant dictionary of string pairs or NULL on error.
+ */
+static GVariant *
+create_attributes (const gchar  *printer_uri,
+                   gchar       **additional_attrs,
+                   gchar       **additional_labels)
+{
+  GVariantBuilder *attr_builder = NULL;
+  GVariant        *ret = NULL;
+
+  if (printer_uri == NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("create_attributes called with invalid parameters.\n"));
+      return NULL;
+    }
+
+  attr_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY);
+  /* The printer uri is the main identifying part */
+  g_variant_builder_add (attr_builder, "{ss}", "uri", printer_uri);
+
+  if (additional_labels != NULL)
+    {
+      int i;
+      for (i = 0; additional_labels[i] != NULL; i++)
+        {
+          g_variant_builder_add (attr_builder, "{ss}",
+                                 additional_labels[i],
+                                 additional_attrs[i]);
+        }
+    }
+
+  ret = g_variant_builder_end (attr_builder);
+  g_variant_builder_unref (attr_builder);
+
+  return ret;
+}
+
+static void
+get_secret_cb (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output,
+                     *attributes;
+  gchar             **auth_info = NULL,
+                     *key = NULL,
+                     *value = NULL;
+  GVariantIter       *iter = NULL;
+  guint               i, required_len;
+  gint                pw_field = -1;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+                                     res,
+                                     &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  attributes = g_dbus_proxy_get_cached_property (task_data->item_proxy,
+                                                 "Attributes");
+  if (attributes == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Failed to lookup attributes.\n"));
+      g_variant_unref (output);
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  /* Iterate over the attributes to fill the auth info */
+  g_variant_get (attributes, "a{ss}", &iter);
+
+  auth_info = g_new0 (gchar *,
+                      g_strv_length (task_data->auth_info_required) + 1);
+
+  while (g_variant_iter_loop (iter, "{ss}", &key, &value))
+    {
+      /* Match attributes with required auth info */
+      for (i = 0; task_data->auth_info_required[i] != NULL; i++)
+        {
+          if ((strcmp (key, "user") == 0 ||
+               strcmp (key, "username") == 0) &&
+              strcmp (task_data->auth_info_required[i],
+                      "username") == 0)
+            {
+              auth_info[i] = g_strdup (value);
+            }
+          else if (strcmp (key, "domain") == 0 &&
+                   strcmp (task_data->auth_info_required[i], "domain") == 0)
+            {
+              auth_info[i] = g_strdup (value);
+            }
+          else if ((strcmp (key, "hostname") == 0 ||
+                    strcmp (key, "server") == 0 ) &&
+                   strcmp (task_data->auth_info_required[i], "hostname") == 0)
+            {
+              auth_info[i] = g_strdup (value);
+            }
+          else if (strcmp (task_data->auth_info_required[i], "password") == 0)
+            {
+              pw_field = i;
+            }
+        }
+    }
+
+  if (pw_field == -1)
+    {
+      /* should not happen... */
+      GTK_NOTE (PRINTING, g_print ("No password required?.\n"));
+      g_variant_unref (output);
+      goto fail;
+    }
+  else
+    {
+      GVariant      *secret,
+                    *s_value;
+      gconstpointer  ba_passwd = NULL;
+      gsize          len = 0;
+
+      secret = g_variant_get_child_value (output, 0);
+      g_variant_unref (output);
+      if (secret == NULL || g_variant_n_children (secret) != 4)
+        {
+          GTK_NOTE (PRINTING, g_print ("Get secret response invalid.\n"));
+          if (secret != NULL)
+            g_variant_unref (secret);
+          goto fail;
+        }
+      s_value = g_variant_get_child_value (secret, 2);
+      ba_passwd = g_variant_get_fixed_array (s_value,
+                                             &len,
+                                             sizeof (guchar));
+
+      g_variant_unref (secret);
+
+      if (ba_passwd == NULL)
+        {
+          GTK_NOTE (PRINTING, g_print ("Invalid / no secret found.\n"));
+          g_variant_unref (s_value);
+          goto fail;
+        }
+
+      auth_info[pw_field] = g_strndup (ba_passwd, len);
+      g_variant_unref (s_value);
+    }
+
+  for (i = 0; task_data->auth_info_required[i] != NULL; i++)
+    {
+      if (auth_info[i] == NULL)
+        {
+          /* Error out if we did not find everything */
+          GTK_NOTE (PRINTING, g_print ("Failed to lookup required attribute: %s.\n",
+                                       task_data->auth_info_required[i]));
+          goto fail;
+        }
+    }
+
+  g_task_return_pointer (task, auth_info, NULL);
+  return;
+
+fail:
+  /* Error out */
+  GTK_NOTE (PRINTING, g_print ("Failed to lookup secret.\n"));
+  required_len = g_strv_length (task_data->auth_info_required);
+  for (i = 0; i < required_len; i++)
+    {
+      /* Not all fields of auth_info are neccessarily written so we can not
+         use strfreev here */
+      g_free (auth_info[i]);
+    }
+  g_free (auth_info);
+  g_task_return_pointer (task, NULL, NULL);
+}
+
+static void
+create_item_cb (GObject      *source_object,
+                GAsyncResult *res,
+                gpointer      user_data)
+{
+  GTask    *task;
+  GError   *error = NULL;
+  GVariant *output;
+  gchar    *item = NULL;
+
+  task = user_data;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_variant_get (output, "(&o&o)", &item, NULL);
+  if (item != NULL && strlen (item) > 1)
+    {
+      GTK_NOTE (PRINTING, g_print ("Successfully stored auth info.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+  g_variant_unref (output);
+}
+
+static void
+do_store_auth_info (GTask *task)
+{
+  GVariant            *attributes = NULL,
+                      *properties = NULL,
+                      *secret = NULL;
+  gchar              **additional_attrs = NULL,
+                     **additional_labels = NULL,
+                      *password = NULL;
+  SecretsServiceData  *task_data = g_task_get_task_data (task);
+  guint                i,
+                       length,
+                       additional_count = 0;
+  GVariantBuilder     *prop_builder = NULL;
+
+  length = g_strv_length (task_data->auth_info_labels);
+
+  additional_attrs = g_new0 (gchar *, length + 1);
+  additional_labels = g_new0 (gchar *, length + 1);
+  /* The labels user and server are chosen to be compatible with
+     the attributes used by system-config-printer */
+  for (i = 0; task_data->auth_info_labels[i] != NULL; i++)
+    {
+      if (g_strcmp0 (task_data->auth_info_labels[i], "username") == 0)
+        {
+          additional_attrs[additional_count] = task_data->auth_info[i];
+          additional_labels[additional_count++] = "user";
+        }
+      else if (g_strcmp0 (task_data->auth_info_labels[i], "hostname") == 0)
+        {
+          additional_attrs[additional_count] = task_data->auth_info[i];
+          additional_labels[additional_count++] = "server";
+        }
+      else if (g_strcmp0 (task_data->auth_info_labels[i], "password") == 0)
+        {
+          password = task_data->auth_info[i];
+        }
+    }
+
+  attributes = create_attributes (task_data->printer_uri,
+                                  additional_attrs,
+                                  additional_labels);
+  g_free (additional_labels);
+  g_free (additional_attrs);
+  if (attributes == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  if (password == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("No secret to store.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  prop_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY);
+
+  g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Label"),
+                         g_variant_new_string (task_data->printer_uri));
+  g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Attributes"),
+                         attributes);
+
+  properties = g_variant_builder_end (prop_builder);
+
+  g_variant_builder_unref (prop_builder);
+
+  secret = g_variant_new ("(oay@ays)",
+                          task_data->session_path,
+                          NULL,
+                          g_variant_new_bytestring (password),
+                          "text/plain");
+
+  g_dbus_connection_call (task_data->dbus_connection,
+                          SECRETS_BUS,
+                          task_data->collection_path,
+                          SECRETS_IFACE ("Collection"),
+                          "CreateItem",
+                          g_variant_new ("(@a{sv}@(oayays)b)",
+                                         properties,
+                                         secret,
+                                         TRUE),
+                          G_VARIANT_TYPE ("(oo)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          SECRETS_TIMEOUT,
+                          g_task_get_cancellable (task),
+                          create_item_cb,
+                          task);
+}
+
+static void
+prompt_completed_cb (GDBusConnection *connection,
+                     const gchar     *sender_name,
+                     const gchar     *object_path,
+                     const gchar     *interface_name,
+                     const gchar     *signal_name,
+                     GVariant        *parameters,
+                     gpointer         user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GVariant           *dismissed;
+  gboolean            is_dismissed = TRUE;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  g_dbus_connection_signal_unsubscribe (task_data->dbus_connection,
+                                        task_data->prompt_subscription);
+  task_data->prompt_subscription = 0;
+
+  dismissed = g_variant_get_child_value (parameters, 0);
+
+  if (dismissed == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid prompt signal.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  g_variant_get (dismissed, "b", &is_dismissed);
+  g_variant_unref (dismissed);
+
+  if (is_dismissed)
+    {
+      GTK_NOTE (PRINTING, g_print ("Collection unlock dismissed.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  /* Prompt successfull, proceed to get or store secret */
+  switch (task_data->action)
+    {
+      case SECRETS_SERVICE_ACTION_STORE:
+        do_store_auth_info (task);
+        break;
+
+      case SECRETS_SERVICE_ACTION_QUERY:
+        g_dbus_proxy_call (task_data->item_proxy,
+                           "GetSecret",
+                           g_variant_new ("(o)",
+                                          task_data->session_path),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           SECRETS_TIMEOUT,
+                           g_task_get_cancellable (task),
+                           get_secret_cb,
+                           task);
+        break;
+    }
+}
+
+static void
+prompt_cb (GObject      *source_object,
+           GAsyncResult *res,
+           gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_variant_unref (output);
+
+  /* Connect to the prompt's completed signal */
+  task_data->prompt_subscription =
+    g_dbus_connection_signal_subscribe (task_data->dbus_connection,
+                                        NULL,
+                                        SECRETS_IFACE ("Prompt"),
+                                        "Completed",
+                                        NULL,
+                                        NULL,
+                                        G_DBUS_SIGNAL_FLAGS_NONE,
+                                        prompt_completed_cb,
+                                        task,
+                                        NULL);
+}
+
+static void
+unlock_collection_cb (GObject      *source_object,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output;
+  const gchar        *prompt_path;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_variant_get (output, "(@ao&o)", NULL, &prompt_path);
+
+  if (prompt_path != NULL && strlen (prompt_path) > 1)
+    {
+      g_dbus_connection_call (task_data->dbus_connection,
+                              SECRETS_BUS,
+                              prompt_path,
+                              SECRETS_IFACE ("Prompt"),
+                              "Prompt",
+                              g_variant_new ("(s)", "0"),
+                              G_VARIANT_TYPE ("()"),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              SECRETS_TIMEOUT,
+                              g_task_get_cancellable (task),
+                              prompt_cb,
+                              task);
+    }
+  else
+    {
+      switch (task_data->action)
+        {
+          case SECRETS_SERVICE_ACTION_STORE:
+            do_store_auth_info (task);
+            break;
+
+          case SECRETS_SERVICE_ACTION_QUERY:
+            /* Prompt successfull proceed to get secret */
+            g_dbus_proxy_call (task_data->item_proxy,
+                               "GetSecret",
+                               g_variant_new ("(o)",
+                                              task_data->session_path),
+                               G_DBUS_CALL_FLAGS_NONE,
+                               SECRETS_TIMEOUT,
+                               g_task_get_cancellable (task),
+                               get_secret_cb,
+                               task);
+            break;
+        }
+    }
+  g_variant_unref (output);
+}
+
+static void
+unlock_read_alias_cb (GObject      *source_object,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  GTask *task;
+  SecretsServiceData *task_data;
+  GError *error = NULL;
+  GVariant *output, *subresult;
+  gsize path_len = 0;
+  const gchar *collection_path;
+  const gchar *to_unlock[2];
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  subresult = g_variant_get_child_value (output, 0);
+  g_variant_unref (output);
+
+  if (subresult == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid ReadAlias response.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  collection_path = g_variant_get_string (subresult, &path_len);
+  to_unlock[0] = collection_path;
+  to_unlock[1] = NULL;
+
+  task_data->collection_path = g_strdup (collection_path);
+
+  g_dbus_connection_call (task_data->dbus_connection,
+                          SECRETS_BUS,
+                          SECRETS_PATH,
+                          SECRETS_IFACE ("Service"),
+                          "Unlock",
+                          g_variant_new ("(^ao)", to_unlock),
+                          G_VARIANT_TYPE ("(aoo)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          SECRETS_TIMEOUT,
+                          g_task_get_cancellable (task),
+                          unlock_collection_cb,
+                          task);
+
+  g_variant_unref (subresult);
+}
+
+static void
+item_proxy_cb (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GDBusProxy         *item_proxy;
+  GVariant           *locked;
+  gboolean            is_locked;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  item_proxy = g_dbus_proxy_new_finish (res,
+                                        &error);
+  if (item_proxy == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  task_data->item_proxy = item_proxy;
+
+  locked = g_dbus_proxy_get_cached_property (item_proxy, "Locked");
+
+  if (locked == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Failed to look up \"Locked\" property on item.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  g_variant_get (locked, "b", &is_locked);
+  g_variant_unref (locked);
+
+  if (is_locked)
+    {
+      /* Go down the unlock -> lookup path */
+      g_dbus_connection_call (task_data->dbus_connection,
+                              SECRETS_BUS,
+                              SECRETS_PATH,
+                              SECRETS_IFACE ("Service"),
+                              "ReadAlias",
+                              g_variant_new ("(s)", "default"),
+                              G_VARIANT_TYPE ("(o)"),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              SECRETS_TIMEOUT,
+                              g_task_get_cancellable (task),
+                              unlock_read_alias_cb,
+                              task);
+      return;
+    }
+
+  /* Unlocked proceed to get or store secret */
+  switch (task_data->action)
+    {
+      case SECRETS_SERVICE_ACTION_STORE:
+        do_store_auth_info (task);
+        break;
+
+      case SECRETS_SERVICE_ACTION_QUERY:
+        g_dbus_proxy_call (item_proxy,
+                           "GetSecret",
+                           g_variant_new ("(o)",
+                                          task_data->session_path),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           SECRETS_TIMEOUT,
+                           g_task_get_cancellable (task),
+                           get_secret_cb,
+                           task);
+        break;
+    }
+}
+
+static void
+search_items_cb (GObject      *source_object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+  GVariant           *output;
+  gsize               array_cnt,
+                      i;
+  gboolean            found_item = FALSE;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  array_cnt = g_variant_n_children (output);
+
+  for (i = 0; i < array_cnt; i++)
+    {
+      GVariant * const   item_paths = g_variant_get_child_value (output, i);
+      const gchar      **items = NULL;
+
+      if (item_paths == NULL)
+        {
+          GTK_NOTE (PRINTING,
+                    g_print ("SearchItems returned invalid result.\n"));
+          continue;
+        }
+
+      items = g_variant_get_objv (item_paths, NULL);
+
+      if (*items == NULL)
+        {
+          g_variant_unref (item_paths);
+          g_free ((gpointer) items);
+          continue;
+        }
+
+      /* Access the first found item. */
+      found_item = TRUE;
+      g_dbus_proxy_new (task_data->dbus_connection,
+                        G_DBUS_PROXY_FLAGS_NONE,
+                        NULL,
+                        SECRETS_BUS,
+                        *items,
+                        SECRETS_IFACE ("Item"),
+                        g_task_get_cancellable (task),
+                        item_proxy_cb,
+                        task);
+      g_free ((gpointer) items);
+      g_variant_unref (item_paths);
+      break;
+    }
+  g_variant_unref (output);
+
+  if (!found_item)
+    {
+      GTK_NOTE (PRINTING, g_print ("No match found in secrets service.\n"));
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+}
+
+static void
+open_session_cb (GObject      *source_object,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+  GTask              *task;
+  GVariant           *output,
+                     *session_variant;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  session_variant = g_variant_get_child_value (output, 1);
+
+  if (session_variant == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid session path response.\n"));
+      g_variant_unref (output);
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  task_data->session_path = g_variant_dup_string (session_variant, NULL);
+
+  if (task_data->session_path == NULL)
+    {
+      GTK_NOTE (PRINTING, g_print ("Invalid session path string value.\n"));
+      g_variant_unref (session_variant);
+      g_variant_unref (output);
+      g_task_return_pointer (task, NULL, NULL);
+      return;
+    }
+
+  g_variant_unref (session_variant);
+  g_variant_unref (output);
+
+  switch (task_data->action)
+    {
+      case SECRETS_SERVICE_ACTION_QUERY:
+        {
+          /* Search for the secret item */
+          GVariant *secrets_attrs;
+
+          secrets_attrs = create_attributes (task_data->printer_uri, NULL, NULL);
+          if (secrets_attrs == NULL)
+            {
+              GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n"));
+              g_task_return_pointer (task, NULL, NULL);
+              return;
+            }
+
+          g_dbus_connection_call (task_data->dbus_connection,
+                                  SECRETS_BUS,
+                                  SECRETS_PATH,
+                                  SECRETS_IFACE ("Service"),
+                                  "SearchItems",
+                                  g_variant_new ("(@a{ss})", secrets_attrs),
+                                  G_VARIANT_TYPE ("(aoao)"),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  SECRETS_TIMEOUT,
+                                  g_task_get_cancellable (task),
+                                  search_items_cb,
+                                  task);
+          break;
+        }
+
+      case SECRETS_SERVICE_ACTION_STORE:
+        {
+          /* Look up / unlock the default collection for storing */
+          g_dbus_connection_call (task_data->dbus_connection,
+                                  SECRETS_BUS,
+                                  SECRETS_PATH,
+                                  SECRETS_IFACE ("Service"),
+                                  "ReadAlias",
+                                  g_variant_new ("(s)", "default"),
+                                  G_VARIANT_TYPE ("(o)"),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  SECRETS_TIMEOUT,
+                                  g_task_get_cancellable (task),
+                                  unlock_read_alias_cb,
+                                  task);
+          break;
+        }
+    }
+}
+
+static void
+get_connection_cb (GObject      *source_object,
+                   GAsyncResult *res,
+                   gpointer      user_data)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+  GError             *error = NULL;
+
+  task = user_data;
+  task_data = g_task_get_task_data (task);
+
+  task_data->dbus_connection = g_bus_get_finish (res, &error);
+  if (task_data->dbus_connection == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  /* Now open a session */
+  g_dbus_connection_call (task_data->dbus_connection,
+                          SECRETS_BUS,
+                          SECRETS_PATH,
+                          SECRETS_IFACE ("Service"),
+                          "OpenSession",
+                          g_variant_new ("(sv)", "plain",
+                                         g_variant_new_string ("")),
+                          G_VARIANT_TYPE ("(vo)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          SECRETS_TIMEOUT,
+                          g_task_get_cancellable (task),
+                          open_session_cb,
+                          task);
+}
+
+/**
+ * gtk_cups_secrets_service_watch:
+ * @appeared: The callback to call when the service interface appears
+ * @vanished: The callback to call when the service interface vanishes
+ * @user_data: A reference to the watching printbackend
+ *
+ * Registers a watch for the secrets service interface.
+ *
+ * Returns: The watcher id
+ */
+guint
+gtk_cups_secrets_service_watch (GBusNameAppearedCallback appeared,
+                                GBusNameVanishedCallback vanished,
+                                gpointer                 user_data)
+{
+  return g_bus_watch_name (G_BUS_TYPE_SESSION,
+                           SECRETS_BUS,
+                           G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+                           appeared,
+                           vanished,
+                           user_data,
+                           NULL);
+}
+
+void
+cleanup_task_data (gpointer data)
+{
+  gint                i;
+  SecretsServiceData *task_data = data;
+
+  g_free (task_data->collection_path);
+  g_strfreev (task_data->auth_info_labels);
+  g_strfreev (task_data->auth_info_required);
+  g_free (task_data->printer_uri);
+
+  if (task_data->auth_info != NULL)
+    {
+      for (i = 0; task_data->auth_info[i] != NULL; i++)
+        {
+          memset (task_data->auth_info[i], 0, strlen (task_data->auth_info[i]));
+          g_clear_pointer (&task_data->auth_info[i], g_free);
+        }
+      g_clear_pointer (&task_data->auth_info, g_free);
+    }
+
+  if (task_data->prompt_subscription != 0)
+    {
+      g_dbus_connection_signal_unsubscribe (task_data->dbus_connection,
+                                            task_data->prompt_subscription);
+      task_data->prompt_subscription = 0;
+    }
+
+  if (task_data->session_path != NULL)
+    {
+      g_dbus_connection_call (task_data->dbus_connection,
+                              SECRETS_BUS,
+                              task_data->session_path,
+                              SECRETS_IFACE ("Session"),
+                              "Close",
+                              NULL,
+                              G_VARIANT_TYPE ("()"),
+                              G_DBUS_CALL_FLAGS_NONE,
+                              SECRETS_TIMEOUT,
+                              NULL,
+                              NULL,
+                              NULL);
+    }
+
+  g_clear_object (&task_data->dbus_connection);
+  g_clear_pointer (&task_data->session_path, g_free);
+  g_clear_object (&task_data->item_proxy);
+}
+
+/**
+ * gtk_cups_secrets_service_query_task:
+ * @source_object: Source object for this task
+ * @cancellable: Cancellable to cancel this task
+ * @callback: Callback to call once the query is finished
+ * @user_data: The user_data passed to the callback
+ * @printer_uri: URI of the printer
+ * @auth_info_required: Info required for authentication
+ *
+ * Checks if a secrets service as described by the secrets-service standard
+ * is available and if so it tries to find the authentication info in the
+ * default collection of the service.
+ *
+ * This is the entry point to a chain of async calls to open a session,
+ * search the secret, unlock the collection (if necessary) and finally
+ * to lookup the secret.
+ *
+ * See: http://standards.freedesktop.org/secret-service/ for documentation
+ * of the used API.
+ */
+void
+gtk_cups_secrets_service_query_task (gpointer              source_object,
+                                     GCancellable         *cancellable,
+                                     GAsyncReadyCallback   callback,
+                                     gpointer              user_data,
+                                     const gchar          *printer_uri,
+                                     gchar               **auth_info_required)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+
+  task_data = g_new0 (SecretsServiceData, 1);
+  task_data->action = SECRETS_SERVICE_ACTION_QUERY;
+  task_data->printer_uri = g_strdup (printer_uri);
+  task_data->auth_info_required = g_strdupv (auth_info_required);
+
+  task = g_task_new (source_object, cancellable, callback, user_data);
+
+  g_task_set_task_data (task, task_data, cleanup_task_data);
+
+  g_bus_get (G_BUS_TYPE_SESSION, cancellable,
+             get_connection_cb, task);
+}
+
+static void
+store_done_cb (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+  GTask  *task = (GTask *) res;
+  GError *error = NULL;
+
+  g_task_propagate_pointer (task, &error);
+
+  if (error != NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("Failed to store auth info: %s\n", error->message));
+      g_error_free (error);
+    }
+
+  g_object_unref (task);
+  GTK_NOTE (PRINTING,
+            g_print ("gtk_cups_secrets_service_store finished.\n"));
+}
+
+/**
+ * gtk_cups_secrets_service_store:
+ * @auth_info: Auth info that should be stored
+ * @auth_info_labels: The keys to use for the auth info
+ * @printer_uri: URI of the printer
+ *
+ * Tries to store the auth_info in a secrets service.
+ */
+void
+gtk_cups_secrets_service_store (gchar       **auth_info,
+                                gchar       **auth_info_labels,
+                                const gchar  *printer_uri)
+{
+  GTask              *task;
+  SecretsServiceData *task_data;
+
+  if (auth_info == NULL || auth_info_labels == NULL || printer_uri == NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("Invalid call to gtk_cups_secrets_service_store.\n"));
+      return;
+    }
+
+  task_data = g_new0 (SecretsServiceData, 1);
+  task_data->action = SECRETS_SERVICE_ACTION_STORE;
+  task_data->printer_uri = g_strdup (printer_uri);
+  task_data->auth_info = g_strdupv (auth_info);
+  task_data->auth_info_labels = g_strdupv (auth_info_labels);
+
+  task = g_task_new (NULL, NULL, store_done_cb, NULL);
+
+  g_task_set_task_data (task, task_data, cleanup_task_data);
+
+  g_bus_get (G_BUS_TYPE_SESSION, NULL,
+             get_connection_cb, task);
+}
diff --git a/modules/printbackends/gtkcupssecretsutils.h b/modules/printbackends/gtkcupssecretsutils.h
new file mode 100644 (file)
index 0000000..1a0424a
--- /dev/null
@@ -0,0 +1,41 @@
+/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords
+ * Copyright (C) 2014 Intevation GmbH
+ *
+ * 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_SECRETS_UTILS_H__
+#define __GTK_SECRETS_UTILS_H__
+
+#include <glib.h>
+
+#include "gtkcupsutils.h"
+
+G_BEGIN_DECLS
+
+void  gtk_cups_secrets_service_query_task (gpointer                   source_object,
+                                           GCancellable              *cancellable,
+                                           GAsyncReadyCallback        callback,
+                                           gpointer                   user_data,
+                                           const gchar               *printer_uri,
+                                           gchar                    **auth_info_required);
+guint gtk_cups_secrets_service_watch      (GBusNameAppearedCallback   appeared,
+                                           GBusNameVanishedCallback   vanished,
+                                           gpointer                   user_data);
+void  gtk_cups_secrets_service_store      (gchar                    **auth_info,
+                                           gchar                    **auth_info_labels,
+                                           const gchar               *printer_uri);
+
+G_END_DECLS
+
+#endif /* __GTK_SECRETS_UTILS_H__ */
diff --git a/modules/printbackends/gtkcupsutils.c b/modules/printbackends/gtkcupsutils.c
new file mode 100644 (file)
index 0000000..54239ca
--- /dev/null
@@ -0,0 +1,1663 @@
+/* GTK - The GIMP Toolkit
+ * gtkcupsutils.h: Statemachine implementation of POST and GET 
+ * cups calls which can be used to create a non-blocking cups API
+ * Copyright (C) 2006, 2007 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 <gtk/gtk.h>
+#include "gtkcupsutils.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
+
+static void _connect            (GtkCupsRequest *request);
+static void _post_send          (GtkCupsRequest *request);
+static void _post_write_request (GtkCupsRequest *request);
+static void _post_write_data    (GtkCupsRequest *request);
+static void _post_check         (GtkCupsRequest *request);
+static void _post_auth          (GtkCupsRequest *request);
+static void _post_read_response (GtkCupsRequest *request);
+
+static void _get_send           (GtkCupsRequest *request);
+static void _get_check          (GtkCupsRequest *request);
+static void _get_auth           (GtkCupsRequest *request);
+static void _get_read_data      (GtkCupsRequest *request);
+
+struct _GtkCupsResult
+{
+  gchar *error_msg;
+  ipp_t *ipp_response;
+  GtkCupsErrorType error_type;
+
+  /* some error types like HTTP_ERROR have a status and a code */
+  int error_status;            
+  int error_code;
+
+  guint is_error : 1;
+  guint is_ipp_response : 1;
+};
+
+
+#define _GTK_CUPS_MAX_ATTEMPTS 10 
+#define _GTK_CUPS_MAX_CHUNK_SIZE 8192
+
+static GtkCupsRequestStateFunc post_states[] = {
+  _connect,
+  _post_send,
+  _post_write_request,
+  _post_write_data,
+  _post_check,
+  _post_auth,
+  _post_read_response
+};
+
+static GtkCupsRequestStateFunc get_states[] = {
+  _connect,
+  _get_send,
+  _get_check,
+  _get_auth,
+  _get_read_data
+};
+
+#ifndef HAVE_CUPS_API_1_6
+#define ippSetOperation(ipp_request, ipp_op_id) ipp_request->request.op.operation_id = ipp_op_id
+#define ippSetRequestId(ipp_request, ipp_rq_id) ipp_request->request.op.request_id = ipp_rq_id
+#define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state
+#define ippGetString(attr, index, foo) attr->values[index].string.text
+#define ippGetCount(attr) attr->num_values
+
+int
+ippSetVersion (ipp_t *ipp,
+               int    major,
+               int    minor)
+{
+  if (!ipp || major < 0 || minor < 0)
+    return 0;
+
+  ipp->request.any.version[0] = major;
+  ipp->request.any.version[1] = minor;
+
+  return 1;
+}
+#endif
+
+static void
+gtk_cups_result_set_error (GtkCupsResult    *result,
+                           GtkCupsErrorType  error_type,
+                           int               error_status,
+                           int               error_code, 
+                           const char       *error_msg,
+                          ...)
+{
+  va_list args;
+
+  result->is_ipp_response = FALSE;
+  result->is_error = TRUE;
+  result->error_type = error_type;
+  result->error_status = error_status;
+  result->error_code = error_code;
+
+  va_start (args, error_msg);
+  result->error_msg = g_strdup_vprintf (error_msg, args);
+  va_end (args);
+}
+
+GtkCupsRequest *
+gtk_cups_request_new_with_username (http_t             *connection,
+                                    GtkCupsRequestType  req_type, 
+                                    gint                operation_id,
+                                    GIOChannel         *data_io,
+                                    const char         *server,
+                                    const char         *resource,
+                                    const char         *username)
+{
+  GtkCupsRequest *request;
+  cups_lang_t *language;
+  
+  request = g_new0 (GtkCupsRequest, 1);
+  request->result = g_new0 (GtkCupsResult, 1);
+
+  request->result->error_msg = NULL;
+  request->result->ipp_response = NULL;
+
+  request->result->is_error = FALSE;
+  request->result->is_ipp_response = FALSE;
+
+  request->type = req_type;
+  request->state = GTK_CUPS_REQUEST_START;
+
+  request->password_state = GTK_CUPS_PASSWORD_NONE;
+
+   if (server)
+    request->server = g_strdup (server);
+  else
+    request->server = g_strdup (cupsServer ());
+
+
+  if (resource)
+    request->resource = g_strdup (resource);
+  else
+    request->resource = g_strdup ("/");
+  if (connection != NULL)
+    {
+      request->http = connection;
+      request->own_http = FALSE;
+    }
+  else
+    {
+      request->http = NULL;
+      request->http = httpConnectEncrypt (request->server, 
+                                          ippPort (), 
+                                          cupsEncryption ());
+
+      if (request->http)
+        httpBlocking (request->http, 0);
+        
+      request->own_http = TRUE;
+    }
+
+  request->last_status = HTTP_CONTINUE;
+
+  request->attempts = 0;
+  request->data_io = data_io;
+
+  request->ipp_request = ippNew ();
+  ippSetOperation (request->ipp_request, operation_id);
+  ippSetRequestId (request->ipp_request, 1);
+
+  language = cupsLangDefault ();
+
+  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                                   "attributes-charset", 
+                                   NULL, "utf-8");
+       
+  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                                   "attributes-natural-language", 
+                                   NULL, language->language);
+
+  if (username != NULL)
+    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                                     "requesting-user-name",
+                                     NULL, username);
+  else
+    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                                     "requesting-user-name",
+                                     NULL, cupsUser ());
+
+  request->auth_info_required = NULL;
+  request->auth_info = NULL;
+  request->need_auth_info = FALSE;
+
+  cupsLangFree (language);
+
+  return request;
+}
+
+GtkCupsRequest *
+gtk_cups_request_new (http_t             *connection,
+                      GtkCupsRequestType  req_type, 
+                      gint                operation_id,
+                      GIOChannel         *data_io,
+                      const char         *server,
+                      const char         *resource)
+{
+  return gtk_cups_request_new_with_username (connection,
+                                             req_type,
+                                             operation_id,
+                                             data_io,
+                                             server,
+                                             resource,
+                                             NULL);
+}
+
+static void
+gtk_cups_result_free (GtkCupsResult *result)
+{
+  g_free (result->error_msg);
+
+  if (result->ipp_response)
+    ippDelete (result->ipp_response);
+
+  g_free (result);
+}
+
+void
+gtk_cups_request_free (GtkCupsRequest *request)
+{
+  if (request->own_http)
+    {
+      if (request->http)
+        httpClose (request->http);
+    }
+  
+  if (request->ipp_request)
+    ippDelete (request->ipp_request);
+
+  g_free (request->server);
+  g_free (request->resource);
+  if (request->password != NULL)
+    {
+      memset (request->password, 0, strlen (request->password));
+      g_free (request->password);
+    }
+
+  g_free (request->username);
+  g_strfreev (request->auth_info_required);
+
+  gtk_cups_result_free (request->result);
+
+  g_free (request);
+}
+
+gboolean 
+gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
+{
+  if (connect_only && request->state != GTK_CUPS_REQUEST_START)
+    return FALSE;
+
+  do
+    {
+      if (request->type == GTK_CUPS_POST)
+        post_states[request->state] (request);
+      else if (request->type == GTK_CUPS_GET)
+        get_states[request->state] (request);
+
+      if (gtk_cups_result_is_error (request->result))
+        request->state = GTK_CUPS_REQUEST_DONE;
+
+      if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
+          request->state != GTK_CUPS_REQUEST_DONE)
+        {
+          /* TODO: should add a status or error code for too many failed attempts */
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_GENERAL,
+                                     0,
+                                     0,
+                                     "Too many failed attempts");
+
+          request->state = GTK_CUPS_REQUEST_DONE;
+        }
+
+      if (request->state == GTK_CUPS_REQUEST_DONE)
+        {
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          return TRUE;
+        }
+    }
+  /* We need to recheck using httpCheck if the poll_state is read, because
+   * Cups has an internal read buffer. And if this buffer is filled, we may
+   * never get a poll event again. */
+  while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
+
+  return FALSE;
+}
+
+GtkCupsPollState 
+gtk_cups_request_get_poll_state (GtkCupsRequest *request)
+{
+  return request->poll_state;
+}
+
+
+
+GtkCupsResult *
+gtk_cups_request_get_result (GtkCupsRequest *request)
+{
+  return request->result;
+}
+
+void            
+gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
+                                 ipp_tag_t       group,
+                                 ipp_tag_t       tag,
+                                 const char     *name,
+                                 const char     *charset,
+                                 const char     *value)
+{
+  ippAddString (request->ipp_request,
+                group,
+                tag,
+                name,
+                charset,
+                value);
+}
+
+void            
+gtk_cups_request_ipp_add_strings (GtkCupsRequest    *request,
+                                 ipp_tag_t          group,
+                                 ipp_tag_t          tag,
+                                 const char        *name,
+                                 int                num_values,
+                                 const char        *charset,
+                                 const char *const *values)
+{
+  ippAddStrings (request->ipp_request,
+                group,
+                tag,
+                name,
+                num_values,
+                charset,
+                values);
+}
+
+const char *
+gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
+                                 ipp_tag_t       tag,
+                                 const char     *name)
+{
+  ipp_attribute_t *attribute = NULL;
+
+  if (request != NULL && request->ipp_request != NULL)
+    attribute = ippFindAttribute (request->ipp_request,
+                                  name,
+                                  tag);
+
+  if (attribute != NULL && ippGetCount (attribute) > 0)
+      return ippGetString (attribute, 0, NULL);
+  else
+    return NULL;
+}
+
+
+typedef struct
+{
+  const char   *name;
+  ipp_tag_t    value_tag;
+} ipp_option_t;
+
+static const ipp_option_t ipp_options[] = {
+  { "blackplot",               IPP_TAG_BOOLEAN },
+  { "brightness",              IPP_TAG_INTEGER },
+  { "columns",                 IPP_TAG_INTEGER },
+  { "copies",                  IPP_TAG_INTEGER },
+  { "finishings",              IPP_TAG_ENUM },
+  { "fitplot",                 IPP_TAG_BOOLEAN },
+  { "gamma",                   IPP_TAG_INTEGER },
+  { "hue",                     IPP_TAG_INTEGER },
+  { "job-k-limit",             IPP_TAG_INTEGER },
+  { "job-page-limit",          IPP_TAG_INTEGER },
+  { "job-priority",            IPP_TAG_INTEGER },
+  { "job-quota-period",                IPP_TAG_INTEGER },
+  { "landscape",               IPP_TAG_BOOLEAN },
+  { "media",                   IPP_TAG_KEYWORD },
+  { "mirror",                  IPP_TAG_BOOLEAN },
+  { "natural-scaling",         IPP_TAG_INTEGER },
+  { "number-up",               IPP_TAG_INTEGER },
+  { "orientation-requested",   IPP_TAG_ENUM },
+  { "page-bottom",             IPP_TAG_INTEGER },
+  { "page-left",               IPP_TAG_INTEGER },
+  { "page-ranges",             IPP_TAG_RANGE },
+  { "page-right",              IPP_TAG_INTEGER },
+  { "page-top",                        IPP_TAG_INTEGER },
+  { "penwidth",                        IPP_TAG_INTEGER },
+  { "ppi",                     IPP_TAG_INTEGER },
+  { "prettyprint",             IPP_TAG_BOOLEAN },
+  { "printer-resolution",      IPP_TAG_RESOLUTION },
+  { "print-quality",           IPP_TAG_ENUM },
+  { "saturation",              IPP_TAG_INTEGER },
+  { "scaling",                 IPP_TAG_INTEGER },
+  { "sides",                   IPP_TAG_KEYWORD },
+  { "wrap",                    IPP_TAG_BOOLEAN },
+  { "number-up-layout",                IPP_TAG_INTEGER }
+};
+
+
+static ipp_tag_t
+_find_option_tag (const gchar *option)
+{
+  int lower_bound, upper_bound, num_options;
+  int current_option;
+  ipp_tag_t result;
+
+  result = IPP_TAG_ZERO;
+
+  lower_bound = 0;
+  upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
+  
+  while (1)
+    {
+      int match;
+      current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
+
+      match = strcasecmp (option, ipp_options[current_option].name);
+      if (match == 0)
+        {
+         result = ipp_options[current_option].value_tag;
+         return result;
+       }
+      else if (match < 0)
+        {
+          upper_bound = current_option - 1;
+       }
+      else
+        {
+          lower_bound = current_option + 1;
+       }
+
+      if (upper_bound == lower_bound && upper_bound == current_option)
+        return result;
+
+      if (upper_bound < 0)
+        return result;
+
+      if (lower_bound > num_options)
+        return result;
+
+      if (upper_bound < lower_bound)
+        return result;
+    }
+}
+
+/*
+ * Note that this function uses IPP_TAG_JOB, so it is
+ * only suitable for IPP Group 2 attributes.
+ * See RFC 2911.
+ */
+void
+gtk_cups_request_encode_option (GtkCupsRequest *request,
+                                const gchar    *option,
+                               const gchar    *value)
+{
+  ipp_tag_t option_tag;
+
+  g_return_if_fail (option != NULL);
+  g_return_if_fail (value != NULL);
+
+  option_tag = _find_option_tag (option);
+
+  if (option_tag == IPP_TAG_ZERO)
+    {
+      option_tag = IPP_TAG_NAME;
+      if (strcasecmp (value, "true") == 0 ||
+          strcasecmp (value, "false") == 0)
+        {
+          option_tag = IPP_TAG_BOOLEAN;
+        }
+    }
+        
+  switch (option_tag)
+    {
+      case IPP_TAG_INTEGER:
+      case IPP_TAG_ENUM:
+        ippAddInteger (request->ipp_request,
+                       IPP_TAG_JOB,
+                       option_tag,
+                       option,
+                       strtol (value, NULL, 0));
+        break;
+
+      case IPP_TAG_BOOLEAN:
+        {
+          char b;
+          
+          if (strcasecmp (value, "true") == 0 ||
+             strcasecmp (value, "on") == 0 ||
+             strcasecmp (value, "yes") == 0) 
+           b = 1;
+         else
+            b = 0;
+
+          ippAddBoolean (request->ipp_request,
+                         IPP_TAG_JOB,
+                         option,
+                         b);
+        
+          break;
+        }
+        
+      case IPP_TAG_RANGE:
+        {
+          char *s;
+          int lower;
+          int upper;
+          
+          if (*value == '-')
+           {
+             lower = 1;
+             s = (char *)value;
+           }
+         else
+           lower = strtol (value, &s, 0);
+
+         if (*s == '-')
+           {
+             if (s[1])
+               upper = strtol (s + 1, NULL, 0);
+             else
+               upper = 2147483647;
+            }
+         else
+           upper = lower;
+         
+          ippAddRange (request->ipp_request,
+                       IPP_TAG_JOB,
+                       option,
+                       lower,
+                       upper);
+
+          break;
+        }
+
+      case IPP_TAG_RESOLUTION:
+        {
+          char *s;
+          int xres;
+          int yres;
+          ipp_res_t units;
+          
+          xres = strtol (value, &s, 0);
+
+         if (*s == 'x')
+           yres = strtol (s + 1, &s, 0);
+         else
+           yres = xres;
+
+         if (strcasecmp (s, "dpc") == 0)
+            units = IPP_RES_PER_CM;
+          else
+            units = IPP_RES_PER_INCH;
+          
+          ippAddResolution (request->ipp_request,
+                            IPP_TAG_JOB,
+                            option,
+                            units,
+                            xres,
+                            yres);
+
+          break;
+        }
+
+      default:
+        {
+          char *values;
+          char *s;
+          int in_quotes;
+          char *next;
+          GPtrArray *strings;
+          
+          values = g_strdup (value);
+          strings = NULL;
+         in_quotes = 0;
+
+          for (s = values, next = s; *s != '\0'; s++)
+            {
+              if (in_quotes != 2 && *s == '\'')
+                {
+                  /* skip quoted value */
+                  if (in_quotes == 0)
+                    in_quotes = 1;
+                  else
+                    in_quotes = 0;
+                }
+              else if (in_quotes != 1 && *s == '\"')
+                {
+                  /* skip quoted value */
+                  if (in_quotes == 0)
+                    in_quotes = 2;
+                  else
+                    in_quotes = 0;
+                }
+              else if (in_quotes == 0 && *s == ',')
+                {
+                  /* found delimiter, add to value array */
+                  *s = '\0';
+                  if (strings == NULL)
+                    strings = g_ptr_array_new ();
+                  g_ptr_array_add (strings, next);
+                  next = s + 1;
+                }
+              else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
+                {
+                  /* skip escaped character */
+                  s++;
+                }
+            }
+          
+          if (strings == NULL)
+            {
+              /* single value */
+              ippAddString (request->ipp_request,
+                            IPP_TAG_JOB,
+                            option_tag,
+                            option,
+                            NULL,
+                            value);
+            }
+          else
+            {
+              /* multiple values */
+              
+              /* add last value */
+              g_ptr_array_add (strings, next);
+              
+              ippAddStrings (request->ipp_request,
+                             IPP_TAG_JOB,
+                             option_tag,
+                             option,
+                             strings->len,
+                             NULL,
+                             (const char **) strings->pdata);
+              g_ptr_array_free (strings, TRUE);
+            }
+
+          g_free (values);
+        }
+
+        break;
+    }
+}
+                               
+void
+gtk_cups_request_set_ipp_version (GtkCupsRequest     *request,
+                                 gint                major,
+                                 gint                minor)
+{
+  ippSetVersion (request->ipp_request, major, minor);
+}
+
+static void
+_connect (GtkCupsRequest *request)
+{
+  request->poll_state = GTK_CUPS_HTTP_IDLE;
+  request->bytes_received = 0;
+
+  if (request->http == NULL)
+    {
+      request->http = httpConnectEncrypt (request->server, 
+                                          ippPort (), 
+                                          cupsEncryption ());
+
+      if (request->http == NULL)
+        request->attempts++;
+
+      if (request->http)
+        httpBlocking (request->http, 0);
+        
+      request->own_http = TRUE;
+    }
+  else
+    {
+      request->attempts = 0;
+      request->state++;
+
+      /* we always write to the socket after we get
+         the connection */
+      request->poll_state = GTK_CUPS_HTTP_WRITE;
+    }
+}
+
+static void 
+_post_send (GtkCupsRequest *request)
+{
+  gchar length[255];
+  struct stat data_info;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  request->poll_state = GTK_CUPS_HTTP_WRITE;
+
+  if (request->data_io != NULL)
+    {
+      fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
+      sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
+    }
+  else
+    sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
+       
+  httpClearFields (request->http);
+  httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
+  httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+#ifdef HAVE_HTTPGETAUTHSTRING
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
+#else
+#ifdef HAVE_HTTP_AUTHSTRING
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+#endif
+#endif
+
+  if (httpPost (request->http, request->resource))
+    {
+      if (httpReconnect (request->http))
+        {
+          request->state = GTK_CUPS_POST_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+          /* TODO: should add a status or error code for failed post */
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_GENERAL,
+                                     0,
+                                     0,
+                                     "Failed Post");
+        }
+
+      request->attempts++;
+      return;    
+    }
+        
+    request->attempts = 0;
+
+    request->state = GTK_CUPS_POST_WRITE_REQUEST;
+    ippSetState (request->ipp_request, IPP_IDLE);
+}
+
+static void 
+_post_write_request (GtkCupsRequest *request)
+{
+  ipp_state_t ipp_status;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  request->poll_state = GTK_CUPS_HTTP_WRITE;
+  
+  ipp_status = ippWrite (request->http, request->ipp_request);
+
+  if (ipp_status == IPP_ERROR)
+    {
+      int cups_error = cupsLastError ();
+      request->state = GTK_CUPS_POST_DONE;
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+      gtk_cups_result_set_error (request->result, 
+                                 GTK_CUPS_ERROR_IPP,
+                                 ipp_status,
+                                 cups_error,
+                                 "%s", 
+                                 ippErrorString (cups_error));
+      return;
+    }
+
+  if (ipp_status == IPP_DATA)
+    {
+      if (request->data_io != NULL)
+        request->state = GTK_CUPS_POST_WRITE_DATA;
+      else
+        {
+          request->state = GTK_CUPS_POST_CHECK;
+          request->poll_state = GTK_CUPS_HTTP_READ;
+       }
+    }
+}
+
+static void 
+_post_write_data (GtkCupsRequest *request)
+{
+  gsize bytes;
+  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
+  http_status_t http_status;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  request->poll_state = GTK_CUPS_HTTP_WRITE;
+  
+  if (httpCheck (request->http))
+    http_status = httpUpdate (request->http);
+  else
+    http_status = request->last_status;
+
+  request->last_status = http_status;
+
+
+  if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
+    {
+      GIOStatus io_status;
+      GError *error;
+
+      error = NULL;
+
+      /* send data */
+      io_status =
+        g_io_channel_read_chars (request->data_io, 
+                                buffer, 
+                                _GTK_CUPS_MAX_CHUNK_SIZE,
+                                &bytes,
+                                &error);
+
+      if (io_status == G_IO_STATUS_ERROR)
+        {
+          request->state = GTK_CUPS_POST_DONE;
+         request->poll_state = GTK_CUPS_HTTP_IDLE;
+     
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_IO,
+                                     io_status,
+                                     error->code, 
+                                     "Error reading from cache file: %s",
+                                     error->message);
+
+         g_error_free (error);
+          return;
+       }
+      else if (bytes == 0 && io_status == G_IO_STATUS_EOF)
+        {
+          request->state = GTK_CUPS_POST_CHECK;
+         request->poll_state = GTK_CUPS_HTTP_READ;
+
+          request->attempts = 0;
+          return;
+        }
+
+
+      if (httpWrite2 (request->http, buffer, bytes) < bytes)
+        {
+          int http_errno;
+
+          http_errno = httpError (request->http);
+
+          request->state = GTK_CUPS_POST_DONE;
+         request->poll_state = GTK_CUPS_HTTP_IDLE;
+     
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     http_errno, 
+                                     "Error writing to socket in Post %s", 
+                                     g_strerror (http_errno));
+          return;
+        }
+    }
+  else if (http_status == HTTP_UNAUTHORIZED)
+    {
+      request->state = GTK_CUPS_POST_CHECK;
+      request->poll_state = GTK_CUPS_HTTP_READ;
+
+      request->attempts = 0;
+      return;
+    }
+  else
+    {
+      request->attempts++;
+    }
+}
+
+static void
+_post_auth (GtkCupsRequest *request)
+{
+  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
+    {
+      if (request->password == NULL)
+        {
+          request->state = GTK_CUPS_POST_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     1,
+                                     "Canceled by user");
+        }
+      else
+        request->state = GTK_CUPS_POST_CHECK;
+    }
+}
+
+static void
+_get_auth (GtkCupsRequest *request)
+{
+  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
+    {
+      if (request->password == NULL)
+        {
+          request->state = GTK_CUPS_GET_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     1,
+                                     "Canceled by user");
+        }
+      else
+        request->state = GTK_CUPS_GET_CHECK;
+    }
+}
+
+/* Very ugly hack: cups has a stupid synchronous password callback 
+ * that doesn't even take the request or user data parameters, so 
+ * we have to use a static variable to pass the password to it.
+ * Not threadsafe !
+ * The callback sets cups_password to NULL to signal that the 
+ * password has been used.
+ */
+static char *cups_password = NULL;
+static char *cups_username = NULL;
+
+static const char *
+passwordCB (const char *prompt)
+{
+  char *pwd = cups_password;
+  cups_password = NULL;
+
+  cupsSetUser (cups_username);
+
+  return pwd;
+}
+
+static void 
+_post_check (GtkCupsRequest *request)
+{
+  http_status_t http_status;
+
+  http_status = request->last_status;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status));
+
+  request->poll_state = GTK_CUPS_HTTP_READ;
+
+  if (http_status == HTTP_CONTINUE)
+    {
+      goto again; 
+    }
+  else if (http_status == HTTP_UNAUTHORIZED)
+    {
+      int auth_result = -1;
+      httpFlush (request->http);
+
+      if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
+        {
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
+          request->state = GTK_CUPS_POST_AUTH;
+          request->need_password = TRUE;
+
+          return;
+        }
+
+      /* Negotiate */
+      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
+        {
+          auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+        }
+      /* Basic, BasicDigest, Digest and PeerCred */
+      else
+        {
+          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
+            {
+              cups_username = request->username;
+              cupsSetPasswordCB (passwordCB);
+
+              /* This call success for PeerCred authentication */
+              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+
+              if (auth_result != 0)
+                {
+                  /* move to AUTH state to let the backend 
+                   * ask for a password
+                   */ 
+                  request->poll_state = GTK_CUPS_HTTP_IDLE;
+                  request->state = GTK_CUPS_POST_AUTH;
+                  request->need_password = TRUE;
+
+                  return;
+                }
+            }
+          else
+            {
+              cups_password = request->password;
+              cups_username = request->username;
+
+              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+
+              if (cups_password != NULL)
+                return;
+
+              if (request->password != NULL)
+                {
+                  memset (request->password, 0, strlen (request->password));
+                  g_free (request->password);
+                  request->password = NULL;
+                }
+
+              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
+            }
+        }
+
+      if (auth_result ||
+          httpReconnect (request->http))
+        {
+          /* if the password has been used, reset password_state
+           * so that we ask for a new one next time around
+           */ 
+          if (cups_password == NULL)
+            request->password_state = GTK_CUPS_PASSWORD_NONE;
+
+          request->state = GTK_CUPS_POST_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     0,
+                                     "Not authorized");
+          return;
+        }
+      
+      if (request->data_io != NULL)
+        g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
+
+      request->state = GTK_CUPS_POST_CONNECT;
+      request->poll_state = GTK_CUPS_HTTP_WRITE;
+    }
+  else if (http_status == HTTP_ERROR)
+    {
+      int error = httpError (request->http);
+#ifdef G_OS_WIN32
+      if (error != WSAENETDOWN && error != WSAENETUNREACH)
+#else
+      if (error != ENETDOWN && error != ENETUNREACH)     
+#endif /* G_OS_WIN32 */
+        {
+          request->attempts++;
+          goto again;
+        }
+      else
+        {
+          request->state = GTK_CUPS_POST_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+     
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     error, 
+                                     "Unknown HTTP error");
+
+          return;
+        }
+    }
+  else if (http_status == HTTP_UPGRADE_REQUIRED)
+    {
+      /* Flush any error message... */
+      httpFlush (request->http);
+
+      cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
+      request->state = GTK_CUPS_POST_CONNECT;
+
+      /* Reconnect... */
+      httpReconnect (request->http);
+
+      /* Upgrade with encryption... */
+      httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
+      request->attempts++;
+      goto again;
+    }
+  else if (http_status != HTTP_OK)
+    {
+      int http_errno;
+
+      http_errno = httpError (request->http);
+
+      if (http_errno == EPIPE)
+        request->state = GTK_CUPS_POST_CONNECT;
+      else
+        {
+          request->state = GTK_CUPS_POST_DONE;
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     http_errno, 
+                                     "HTTP Error in POST %s", 
+                                     g_strerror (http_errno));
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          httpFlush (request->http); 
+          return;
+        }
+
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+      request->last_status = HTTP_CONTINUE;
+
+      httpFlush (request->http);
+      if (request->own_http)
+        httpClose (request->http);
+      request->http = NULL;
+
+      return;
+    }
+  else
+    {
+      request->state = GTK_CUPS_POST_READ_RESPONSE;
+      return;
+    }
+
+ again:
+  http_status = HTTP_CONTINUE;
+
+  if (httpCheck (request->http))
+    http_status = httpUpdate (request->http);
+
+  request->last_status = http_status;
+}
+
+static void 
+_post_read_response (GtkCupsRequest *request)
+{
+  ipp_state_t ipp_status;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  request->poll_state = GTK_CUPS_HTTP_READ;
+
+  if (request->result->ipp_response == NULL)
+    request->result->ipp_response = ippNew();
+
+  ipp_status = ippRead (request->http, 
+                        request->result->ipp_response);
+
+  if (ipp_status == IPP_ERROR)
+    {
+      int ipp_error = cupsLastError ();
+      gtk_cups_result_set_error (request->result,  
+                                 GTK_CUPS_ERROR_IPP,
+                                 ipp_status,
+                                 ipp_error,
+                                 "%s",
+                                 ippErrorString (ipp_error));
+      
+      ippDelete (request->result->ipp_response);
+      request->result->ipp_response = NULL;
+
+      request->state = GTK_CUPS_POST_DONE;
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+    }
+  else if (ipp_status == IPP_DATA)
+    {
+      request->state = GTK_CUPS_POST_DONE;
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+    }
+}
+
+static void 
+_get_send (GtkCupsRequest *request)
+{
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  request->poll_state = GTK_CUPS_HTTP_WRITE;
+
+  if (request->data_io == NULL)
+    {
+      gtk_cups_result_set_error (request->result,
+                                 GTK_CUPS_ERROR_IO,
+                                 G_IO_STATUS_ERROR,
+                                 G_IO_CHANNEL_ERROR_FAILED, 
+                                 "Get requires an open io channel");
+
+      request->state = GTK_CUPS_GET_DONE;
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+      return;
+    }
+
+  httpClearFields (request->http);
+#ifdef HAVE_HTTPGETAUTHSTRING
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
+#else
+#ifdef HAVE_HTTP_AUTHSTRING
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+#endif
+#endif
+
+  if (httpGet (request->http, request->resource))
+    {
+      if (httpReconnect (request->http))
+        {
+          request->state = GTK_CUPS_GET_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+        
+          /* TODO: should add a status or error code for failed GET */ 
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_GENERAL,
+                                     0,
+                                     0,
+                                     "Failed Get");
+        }
+
+      request->attempts++;
+      return;    
+    }
+
+  if (httpCheck (request->http))
+    request->last_status = httpUpdate (request->http);
+        
+  request->attempts = 0;
+
+  request->state = GTK_CUPS_GET_CHECK;
+  request->poll_state = GTK_CUPS_HTTP_READ;
+  
+  ippSetState (request->ipp_request, IPP_IDLE);
+}
+
+static void 
+_get_check (GtkCupsRequest *request)
+{
+  http_status_t http_status;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  http_status = request->last_status;
+
+  request->poll_state = GTK_CUPS_HTTP_READ;
+
+  if (http_status == HTTP_CONTINUE)
+    {
+      goto again; 
+    }
+  else if (http_status == HTTP_UNAUTHORIZED)
+    {
+      int auth_result = -1;
+      httpFlush (request->http);
+
+      if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
+        {
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
+          request->state = GTK_CUPS_GET_AUTH;
+          request->need_password = TRUE;
+
+          return;
+        }
+
+      /* Negotiate */
+      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
+        {
+          auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+        }
+      /* Basic, BasicDigest, Digest and PeerCred */
+      else
+        {
+          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
+            {
+              cups_username = request->username;
+              cupsSetPasswordCB (passwordCB);
+
+              /* This call success for PeerCred authentication */
+              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+
+              if (auth_result != 0)
+                {
+                  /* move to AUTH state to let the backend
+                   * ask for a password
+                   */
+                  request->poll_state = GTK_CUPS_HTTP_IDLE;
+                  request->state = GTK_CUPS_GET_AUTH;
+                  request->need_password = TRUE;
+
+                  return;
+                }
+            }
+          else
+            {
+              cups_password = request->password;
+              cups_username = request->username;
+
+              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+
+              if (cups_password != NULL)
+                return;
+
+              if (request->password != NULL)
+                {
+                  memset (request->password, 0, strlen (request->password));
+                  g_free (request->password);
+                  request->password = NULL;
+                }
+
+              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
+            }
+        }
+
+      if (auth_result ||
+          httpReconnect (request->http))
+        {
+          /* if the password has been used, reset password_state
+           * so that we ask for a new one next time around
+           */
+          if (cups_password == NULL)
+            request->password_state = GTK_CUPS_PASSWORD_NONE;
+
+          request->state = GTK_CUPS_GET_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     0,
+                                     "Not authorized");
+          return;
+        }
+
+      request->state = GTK_CUPS_GET_CONNECT;
+      request->last_status = HTTP_CONTINUE;
+
+     return;
+    }
+  else if (http_status == HTTP_UPGRADE_REQUIRED)
+    {
+      /* Flush any error message... */
+      httpFlush (request->http);
+
+      cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
+      request->state = GTK_CUPS_GET_CONNECT;
+
+      /* Reconnect... */
+      httpReconnect (request->http);
+
+      /* Upgrade with encryption... */
+      httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
+      request->attempts++;
+      goto again;
+    }
+  else if (http_status != HTTP_OK)
+    {
+      int http_errno;
+
+      http_errno = httpError (request->http);
+
+      if (http_errno == EPIPE)
+        request->state = GTK_CUPS_GET_CONNECT;
+      else
+        {
+          request->state = GTK_CUPS_GET_DONE;
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     http_errno, 
+                                     "HTTP Error in GET %s", 
+                                     g_strerror (http_errno));
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          httpFlush (request->http);
+
+          return;
+        }
+
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+      request->last_status = HTTP_CONTINUE;
+
+      httpFlush (request->http);
+      if (request->own_http)
+        httpClose (request->http);
+      request->http = NULL;
+
+      return;
+    }
+  else
+    {
+      request->state = GTK_CUPS_GET_READ_DATA;
+      return;
+    }
+
+ again:
+  http_status = HTTP_CONTINUE;
+
+  if (httpCheck (request->http))
+    http_status = httpUpdate (request->http);
+
+  request->last_status = http_status;
+
+}
+
+static void 
+_get_read_data (GtkCupsRequest *request)
+{
+  char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
+  gsize bytes;
+  gsize bytes_written;
+  GIOStatus io_status;
+  GError *error;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  error = NULL;
+
+  request->poll_state = GTK_CUPS_HTTP_READ;
+
+  bytes = httpRead2 (request->http, buffer, sizeof (buffer));
+  request->bytes_received += bytes;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
+
+  io_status =
+    g_io_channel_write_chars (request->data_io, 
+                              buffer, 
+                             bytes, 
+                             &bytes_written,
+                             &error);
+
+  if (io_status == G_IO_STATUS_ERROR)
+    {
+      request->state = GTK_CUPS_GET_DONE;
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+    
+      gtk_cups_result_set_error (request->result,
+                                 GTK_CUPS_ERROR_IO,
+                                 io_status,
+                                 error->code, 
+                                 error->message);
+      g_error_free (error);
+    }
+
+  /* Stop if we do not expect any more data or EOF was received. */
+  if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
+    {
+      request->state = GTK_CUPS_GET_DONE;
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+      return;
+    }
+}
+
+gboolean
+gtk_cups_request_is_done (GtkCupsRequest *request)
+{
+  return (request->state == GTK_CUPS_REQUEST_DONE);
+}
+
+gboolean
+gtk_cups_result_is_error (GtkCupsResult *result)
+{
+  return result->is_error;
+}
+
+ipp_t *
+gtk_cups_result_get_response (GtkCupsResult *result)
+{
+  return result->ipp_response;
+}
+
+GtkCupsErrorType
+gtk_cups_result_get_error_type (GtkCupsResult *result)
+{
+  return result->error_type;
+}
+
+int
+gtk_cups_result_get_error_status (GtkCupsResult *result)
+{
+  return result->error_status;
+}
+
+int
+gtk_cups_result_get_error_code (GtkCupsResult *result)
+{
+  return result->error_code;
+}
+
+const char *
+gtk_cups_result_get_error_string (GtkCupsResult *result)
+{
+  return result->error_msg; 
+}
+
+/* This function allocates new instance of GtkCupsConnectionTest() and creates
+ * a socket for communication with a CUPS server 'server'.
+ */
+GtkCupsConnectionTest *
+gtk_cups_connection_test_new (const char *server,
+                              const int   port)
+{
+  GtkCupsConnectionTest *result = NULL;
+  gchar                 *port_str = NULL;
+
+  result = g_new (GtkCupsConnectionTest, 1);
+
+  if (port >= 0)
+    port_str = g_strdup_printf ("%d", port);
+  else
+    port_str = g_strdup_printf ("%d", ippPort ());
+
+  if (server != NULL)
+    result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
+  else
+    result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
+
+  g_free (port_str);
+
+  result->socket = -1;
+  result->current_addr = NULL;
+  result->last_wrong_addr = NULL;
+  result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+
+  result->at_init = gtk_cups_connection_test_get_state (result);
+
+  return result;
+}
+
+
+/* A non-blocking test whether it is possible to connect to a CUPS server specified
+ * inside of GtkCupsConnectionTest structure.
+ *  - you need to check it more then once.
+ * The connection is closed after a successful connection.
+ */
+GtkCupsConnectionState 
+gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
+{
+  GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+  http_addrlist_t       *iter;
+  gint                   error_code;
+  gint                   flags;
+  gint                   code;
+
+  if (test == NULL)
+    return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+
+  if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
+    {
+      test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+      return GTK_CUPS_CONNECTION_AVAILABLE;
+    }
+  else
+    {
+      if (test->socket == -1)
+        {
+          if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
+            iter = test->last_wrong_addr->next;
+          else
+            {
+              test->last_wrong_addr = NULL;
+              iter = test->addrlist;
+            }
+
+          while (iter)
+            {
+              test->socket = socket (iter->addr.addr.sa_family,
+                                     SOCK_STREAM,
+                                     0);
+
+              if (test->socket >= 0)
+                {
+                  flags = fcntl (test->socket, F_GETFL);
+
+                  if (flags != -1)
+                    flags |= O_NONBLOCK;
+
+                  fcntl (test->socket, F_SETFL, flags);
+              
+                  test->current_addr = iter;
+              
+                  break;
+                }
+               iter = iter->next;
+            }
+        }
+
+      if (test->socket >= 0)
+        {
+          code = connect (test->socket,
+                          &test->current_addr->addr.addr,
+                          httpAddrLength (&test->current_addr->addr));
+
+          error_code = errno;
+
+          if (code == 0 || error_code == EISCONN)
+            {
+              close (test->socket);
+              test->socket = -1;
+              test->current_addr = NULL;
+              result = GTK_CUPS_CONNECTION_AVAILABLE;
+            }
+          else
+            {
+              if (error_code == EALREADY || error_code == EINPROGRESS)
+                result = GTK_CUPS_CONNECTION_IN_PROGRESS;
+              else
+                {
+                  close (test->socket);
+                  test->socket = -1;
+                  test->last_wrong_addr = test->current_addr;
+                  result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+                }
+            }
+         }
+
+      return result;
+    }
+}
+
+/* This function frees memory used by the GtkCupsConnectionTest structure.
+ */
+void 
+gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
+{
+  if (test == NULL)
+    return;
+
+  test->current_addr = NULL;
+  test->last_wrong_addr = NULL;
+  httpAddrFreeList (test->addrlist);
+  if (test->socket != -1)
+    {
+      close (test->socket);
+      test->socket = -1;
+    }
+  g_free (test);
+}
diff --git a/modules/printbackends/gtkcupsutils.h b/modules/printbackends/gtkcupsutils.h
new file mode 100644 (file)
index 0000000..500bb4f
--- /dev/null
@@ -0,0 +1,196 @@
+/* gtkcupsutils.h 
+ * Copyright (C) 2006 John (J5) Palmieri <johnp@redhat.com>
+ *
+ * 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_CUPS_UTILS_H__
+#define __GTK_CUPS_UTILS_H__
+
+#include <glib.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/http.h>
+#include <cups/ipp.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkCupsRequest        GtkCupsRequest;
+typedef struct _GtkCupsResult         GtkCupsResult;
+typedef struct _GtkCupsConnectionTest GtkCupsConnectionTest;
+
+typedef enum
+{
+  GTK_CUPS_ERROR_HTTP,
+  GTK_CUPS_ERROR_IPP,
+  GTK_CUPS_ERROR_IO,
+  GTK_CUPS_ERROR_AUTH,
+  GTK_CUPS_ERROR_GENERAL
+} GtkCupsErrorType;
+
+typedef enum
+{
+  GTK_CUPS_POST,
+  GTK_CUPS_GET
+} GtkCupsRequestType;
+
+
+/** 
+ * Direction we should be polling the http socket on.
+ * We are either reading or writting at each state.
+ * This makes it easy for mainloops to connect to poll.
+ */
+typedef enum
+{
+  GTK_CUPS_HTTP_IDLE,
+  GTK_CUPS_HTTP_READ,
+  GTK_CUPS_HTTP_WRITE
+} GtkCupsPollState;
+
+typedef enum
+{
+  GTK_CUPS_CONNECTION_AVAILABLE,
+  GTK_CUPS_CONNECTION_NOT_AVAILABLE,
+  GTK_CUPS_CONNECTION_IN_PROGRESS  
+} GtkCupsConnectionState;
+
+typedef enum
+{
+  GTK_CUPS_PASSWORD_NONE,
+  GTK_CUPS_PASSWORD_REQUESTED,
+  GTK_CUPS_PASSWORD_HAS,
+  GTK_CUPS_PASSWORD_APPLIED,
+  GTK_CUPS_PASSWORD_NOT_VALID
+} GtkCupsPasswordState;
+
+struct _GtkCupsRequest 
+{
+  GtkCupsRequestType type;
+
+  http_t *http;
+  http_status_t last_status;
+  ipp_t *ipp_request;
+
+  gchar *server;
+  gchar *resource;
+  GIOChannel *data_io;
+  gint attempts;
+
+  GtkCupsResult *result;
+
+  gint state;
+  GtkCupsPollState poll_state;
+  guint64 bytes_received;
+
+  gchar *password;
+  gchar *username;
+
+  gint own_http : 1;
+  gint need_password : 1;
+  gint need_auth_info : 1;
+  gchar **auth_info_required;
+  gchar **auth_info;
+  GtkCupsPasswordState password_state;
+};
+
+struct _GtkCupsConnectionTest
+{
+  GtkCupsConnectionState at_init;
+  http_addrlist_t       *addrlist;
+  http_addrlist_t       *current_addr;
+  http_addrlist_t       *last_wrong_addr;
+  gint                   socket;
+};
+
+#define GTK_CUPS_REQUEST_START 0
+#define GTK_CUPS_REQUEST_DONE 500
+
+/* POST states */
+enum 
+{
+  GTK_CUPS_POST_CONNECT = GTK_CUPS_REQUEST_START,
+  GTK_CUPS_POST_SEND,
+  GTK_CUPS_POST_WRITE_REQUEST,
+  GTK_CUPS_POST_WRITE_DATA,
+  GTK_CUPS_POST_CHECK,
+  GTK_CUPS_POST_AUTH,
+  GTK_CUPS_POST_READ_RESPONSE,
+  GTK_CUPS_POST_DONE = GTK_CUPS_REQUEST_DONE
+};
+
+/* GET states */
+enum
+{
+  GTK_CUPS_GET_CONNECT = GTK_CUPS_REQUEST_START,
+  GTK_CUPS_GET_SEND,
+  GTK_CUPS_GET_CHECK,
+  GTK_CUPS_GET_AUTH,
+  GTK_CUPS_GET_READ_DATA,
+  GTK_CUPS_GET_DONE = GTK_CUPS_REQUEST_DONE
+};
+
+GtkCupsRequest        * gtk_cups_request_new_with_username (http_t             *connection,
+                                                           GtkCupsRequestType  req_type,
+                                                           gint                operation_id,
+                                                           GIOChannel         *data_io,
+                                                           const char         *server,
+                                                           const char         *resource,
+                                                           const char         *username);
+GtkCupsRequest        * gtk_cups_request_new               (http_t             *connection,
+                                                           GtkCupsRequestType  req_type,
+                                                           gint                operation_id,
+                                                           GIOChannel         *data_io,
+                                                           const char         *server,
+                                                           const char         *resource);
+void                    gtk_cups_request_ipp_add_string    (GtkCupsRequest     *request,
+                                                           ipp_tag_t           group,
+                                                           ipp_tag_t           tag,
+                                                           const char         *name,
+                                                           const char         *charset,
+                                                           const char         *value);
+void                    gtk_cups_request_ipp_add_strings   (GtkCupsRequest     *request,
+                                                           ipp_tag_t           group,
+                                                           ipp_tag_t           tag,
+                                                           const char         *name,
+                                                           int                 num_values,
+                                                           const char         *charset,
+                                                           const char * const *values);
+const char            * gtk_cups_request_ipp_get_string    (GtkCupsRequest     *request,
+                                                           ipp_tag_t           tag,
+                                                           const char         *name);
+gboolean                gtk_cups_request_read_write        (GtkCupsRequest     *request,
+                                                            gboolean            connect_only);
+GtkCupsPollState        gtk_cups_request_get_poll_state    (GtkCupsRequest     *request);
+void                    gtk_cups_request_free              (GtkCupsRequest     *request);
+GtkCupsResult         * gtk_cups_request_get_result        (GtkCupsRequest     *request);
+gboolean                gtk_cups_request_is_done           (GtkCupsRequest     *request);
+void                    gtk_cups_request_encode_option     (GtkCupsRequest     *request,
+                                                           const gchar        *option,
+                                                           const gchar        *value);
+void                    gtk_cups_request_set_ipp_version   (GtkCupsRequest     *request,
+                                                           gint                major,
+                                                           gint                minor);
+gboolean                gtk_cups_result_is_error           (GtkCupsResult      *result);
+ipp_t                 * gtk_cups_result_get_response       (GtkCupsResult      *result);
+GtkCupsErrorType        gtk_cups_result_get_error_type     (GtkCupsResult      *result);
+int                     gtk_cups_result_get_error_status   (GtkCupsResult      *result);
+int                     gtk_cups_result_get_error_code     (GtkCupsResult      *result);
+const char            * gtk_cups_result_get_error_string   (GtkCupsResult      *result);
+GtkCupsConnectionTest * gtk_cups_connection_test_new       (const char         *server,
+                                                            const int           port);
+GtkCupsConnectionState  gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test);
+void                    gtk_cups_connection_test_free      (GtkCupsConnectionTest *test);
+
+G_END_DECLS
+#endif 
diff --git a/modules/printbackends/gtkprintbackendcloudprint.c b/modules/printbackends/gtkprintbackendcloudprint.c
new file mode 100644 (file)
index 0000000..b556034
--- /dev/null
@@ -0,0 +1,1036 @@
+/* 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"
+
+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);
+}
diff --git a/modules/printbackends/gtkprintbackendcloudprint.h b/modules/printbackends/gtkprintbackendcloudprint.h
new file mode 100644 (file)
index 0000000..044ad4b
--- /dev/null
@@ -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/gtkprintbackendcups.c b/modules/printbackends/gtkprintbackendcups.c
new file mode 100644 (file)
index 0000000..09e18b1
--- /dev/null
@@ -0,0 +1,6745 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcups.h: Default implementation of GtkPrintBackend
+ * for the Common Unix Print System (CUPS)
+ * Copyright (C) 2006, 2007 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 <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <time.h>
+/* Cups 1.6 deprecates ppdFindAttr(), ppdFindCustomOption(),
+ * ppdFirstCustomParam(), and ppdNextCustomParam() among others. This
+ * turns off the warning so that it will compile.
+ */
+#ifdef HAVE_CUPS_API_1_6
+# define _PPD_DEPRECATED
+#endif
+
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/http.h>
+#include <cups/ipp.h>
+#include <errno.h>
+#include <cairo.h>
+#include <cairo-pdf.h>
+#include <cairo-ps.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include <gtk/gtk.h>
+#include <gtk/gtkprintbackend.h>
+#include <gtk/gtkunixprint.h>
+#include <gtk/gtkprinter-private.h>
+
+#include "gtkprintbackendcups.h"
+#include "gtkprintercups.h"
+
+#include "gtkcupsutils.h"
+#include "gtkcupssecretsutils.h"
+
+#include <gtkprintutils.h>
+
+#ifdef HAVE_COLORD
+#include <colord.h>
+#endif
+
+typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
+
+#define GTK_PRINT_BACKEND_CUPS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
+#define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
+#define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
+
+#define _CUPS_MAX_ATTEMPTS 10
+#define _CUPS_MAX_CHUNK_SIZE 8192
+
+#ifdef HAVE_CUPS_API_1_6
+#define AVAHI_IF_UNSPEC -1
+#define AVAHI_PROTO_INET 0
+#define AVAHI_PROTO_INET6 1
+#define AVAHI_PROTO_UNSPEC -1
+
+#define AVAHI_BUS "org.freedesktop.Avahi"
+#define AVAHI_SERVER_IFACE "org.freedesktop.Avahi.Server"
+#define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser"
+#define AVAHI_SERVICE_RESOLVER_IFACE "org.freedesktop.Avahi.ServiceResolver"
+#endif
+
+/* define this to see warnings about ignored ppd options */
+#undef PRINT_IGNORED_OPTIONS
+
+#define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
+#define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].string.text;}
+
+typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
+                                                   GtkCupsResult   *result,
+                                                   gpointer         user_data);
+
+typedef enum
+{
+  DISPATCH_SETUP,
+  DISPATCH_REQUEST,
+  DISPATCH_SEND,
+  DISPATCH_CHECK,
+  DISPATCH_READ,
+  DISPATCH_ERROR
+} GtkPrintCupsDispatchState;
+
+typedef struct
+{
+  GSource source;
+
+  http_t *http;
+  GtkCupsRequest *request;
+  GtkCupsPollState poll_state;
+  GPollFD *data_poll;
+  GtkPrintBackendCups *backend;
+  GtkPrintCupsResponseCallbackFunc callback;
+  gpointer                         callback_data;
+
+} GtkPrintCupsDispatchWatch;
+
+struct _GtkPrintBackendCupsClass
+{
+  GtkPrintBackendClass parent_class;
+};
+
+struct _GtkPrintBackendCups
+{
+  GtkPrintBackend parent_instance;
+
+  char *default_printer;
+
+  guint list_printers_poll;
+  guint list_printers_pending : 1;
+  gint  list_printers_attempts;
+  guint got_default_printer   : 1;
+  guint default_printer_poll;
+  GtkCupsConnectionTest *cups_connection_test;
+  gint  reading_ppds;
+
+  GList      *requests;
+  GHashTable *auth;
+  gchar      *username;
+  gboolean    authentication_lock;
+#ifdef HAVE_COLORD
+  CdClient   *colord_client;
+#endif
+#ifdef HAVE_CUPS_API_1_6
+  GDBusConnection *dbus_connection;
+  gchar           *avahi_default_printer;
+  guint            avahi_service_browser_subscription_id;
+  guint            avahi_service_browser_subscription_ids[2];
+  gchar           *avahi_service_browser_paths[2];
+  GCancellable    *avahi_cancellable;
+#endif
+  gboolean      secrets_service_available;
+  guint         secrets_service_watch_id;
+  GCancellable *secrets_service_cancellable;
+};
+
+static GObjectClass *backend_parent_class;
+
+static void                 gtk_print_backend_cups_class_init      (GtkPrintBackendCupsClass          *class);
+static void                 gtk_print_backend_cups_init            (GtkPrintBackendCups               *impl);
+static void                 gtk_print_backend_cups_finalize        (GObject                           *object);
+static void                 gtk_print_backend_cups_dispose         (GObject                           *object);
+static void                 cups_get_printer_list                  (GtkPrintBackend                   *print_backend);
+static void                 cups_get_default_printer               (GtkPrintBackendCups               *print_backend);
+static void                 cups_get_local_default_printer         (GtkPrintBackendCups               *print_backend);
+static void                 cups_request_execute                   (GtkPrintBackendCups               *print_backend,
+                                                                   GtkCupsRequest                    *request,
+                                                                   GtkPrintCupsResponseCallbackFunc   callback,
+                                                                   gpointer                           user_data,
+                                                                   GDestroyNotify                     notify);
+static void                 cups_printer_get_settings_from_options (GtkPrinter                        *printer,
+                                                                   GtkPrinterOptionSet               *options,
+                                                                   GtkPrintSettings                  *settings);
+static gboolean             cups_printer_mark_conflicts            (GtkPrinter                        *printer,
+                                                                   GtkPrinterOptionSet               *options);
+static GtkPrinterOptionSet *cups_printer_get_options               (GtkPrinter                        *printer,
+                                                                   GtkPrintSettings                  *settings,
+                                                                   GtkPageSetup                      *page_setup,
+                                                                    GtkPrintCapabilities               capabilities);
+static void                 cups_printer_prepare_for_print         (GtkPrinter                        *printer,
+                                                                   GtkPrintJob                       *print_job,
+                                                                   GtkPrintSettings                  *settings,
+                                                                   GtkPageSetup                      *page_setup);
+static GList *              cups_printer_list_papers               (GtkPrinter                        *printer);
+static GtkPageSetup *       cups_printer_get_default_page_size     (GtkPrinter                        *printer);
+static void                 cups_printer_request_details           (GtkPrinter                        *printer);
+static gboolean             cups_request_default_printer           (GtkPrintBackendCups               *print_backend);
+static gboolean             cups_request_ppd                       (GtkPrinter                        *printer);
+static gboolean             cups_printer_get_hard_margins          (GtkPrinter                        *printer,
+                                                                   gdouble                           *top,
+                                                                   gdouble                           *bottom,
+                                                                   gdouble                           *left,
+                                                                   gdouble                           *right);
+static GtkPrintCapabilities cups_printer_get_capabilities          (GtkPrinter                        *printer);
+static void                 set_option_from_settings               (GtkPrinterOption                  *option,
+                                                                   GtkPrintSettings                  *setting);
+static void                 cups_begin_polling_info                (GtkPrintBackendCups               *print_backend,
+                                                                   GtkPrintJob                       *job,
+                                                                   int                                job_id);
+static gboolean             cups_job_info_poll_timeout             (gpointer                           user_data);
+static void                 gtk_print_backend_cups_print_stream    (GtkPrintBackend                   *backend,
+                                                                   GtkPrintJob                       *job,
+                                                                   GIOChannel                        *data_io,
+                                                                   GtkPrintJobCompleteFunc            callback,
+                                                                   gpointer                           user_data,
+                                                                   GDestroyNotify                     dnotify);
+static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter                        *printer,
+                                                                   GtkPrintSettings                  *settings,
+                                                                   gdouble                            width,
+                                                                   gdouble                            height,
+                                                                   GIOChannel                        *cache_io);
+
+static void                 gtk_print_backend_cups_set_password    (GtkPrintBackend                   *backend,
+                                                                    gchar                            **auth_info_required,
+                                                                    gchar                            **auth_info,
+                                                                    gboolean                           store_auth_info);
+
+void                        overwrite_and_free                      (gpointer                          data);
+static gboolean             is_address_local                        (const gchar                      *address);
+static gboolean             request_auth_info                       (gpointer                          data);
+static void                 lookup_auth_info                        (gpointer                          data);
+
+#ifdef HAVE_CUPS_API_1_6
+static void                 avahi_request_printer_list              (GtkPrintBackendCups              *cups_backend);
+#endif
+
+static void                 secrets_service_appeared_cb             (GDBusConnection *connection,
+                                                                     const gchar *name,
+                                                                     const gchar *name_owner,
+                                                                     gpointer user_data);
+static void                 secrets_service_vanished_cb             (GDBusConnection *connection,
+                                                                     const gchar *name,
+                                                                     gpointer user_data);
+
+G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendCups, gtk_print_backend_cups, GTK_TYPE_PRINT_BACKEND)
+
+void
+g_io_module_load (GIOModule *module)
+{
+  g_type_module_use (G_TYPE_MODULE (module));
+
+  gtk_print_backend_cups_register_type (G_TYPE_MODULE (module));
+  gtk_printer_cups_register_type (G_TYPE_MODULE (module));
+
+  g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
+                                  GTK_TYPE_PRINT_BACKEND_CUPS,
+                                  "cups",
+                                  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);
+}
+
+/* CUPS 1.6 Getter/Setter Functions CUPS 1.6 makes private most of the
+ * IPP structures and enforces access via new getter functions, which
+ * are unfortunately not available in earlier versions. We define
+ * below those getter functions as macros for use when building
+ * against earlier CUPS versions.
+ */
+#ifndef HAVE_CUPS_API_1_6
+#define ippGetOperation(ipp_request) ipp_request->request.op.operation_id
+#define ippGet:Integer(attr, index) attr->values[index].integer
+#define ippGetBoolean(attr, index) attr->values[index].boolean
+#define ippGetString(attr, index, foo) attr->values[index].string.text
+#define ippGetValueTag(attr) attr->value_tag
+#define ippGetName(attr) attr->name
+#define ippGetCount(attr) attr->num_values
+#define ippGetGroupTag(attr) attr->group_tag
+#define ippGetCollection(attr, index) attr->values[index].collection
+
+static int
+ippGetRange (ipp_attribute_t *attr,
+             int element,
+             int *upper)
+{
+  *upper = attr->values[element].range.upper;
+  return (attr->values[element].range.lower);
+}
+
+static ipp_attribute_t *
+ippFirstAttribute (ipp_t *ipp)
+{
+  if (!ipp)
+    return (NULL);
+
+  return (ipp->current = ipp->attrs);
+}
+
+static ipp_attribute_t *
+ippNextAttribute (ipp_t *ipp)
+{
+  if (!ipp || !ipp->current)
+    return (NULL);
+
+  return (ipp->current = ipp->current->next);
+}
+#endif
+
+/*
+ * GtkPrintBackendCups
+ */
+
+/**
+ * gtk_print_backend_cups_new:
+ *
+ * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
+ * implements the #GtkPrintBackend interface with direct access to
+ * the filesystem using Unix/Linux API calls
+ *
+ * Returns: the new #GtkPrintBackendCups object
+ */
+GtkPrintBackend *
+gtk_print_backend_cups_new (void)
+{
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
+
+  return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
+}
+
+static void
+gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
+
+  backend_parent_class = g_type_class_peek_parent (class);
+
+  gobject_class->finalize = gtk_print_backend_cups_finalize;
+  gobject_class->dispose = gtk_print_backend_cups_dispose;
+
+  backend_class->request_printer_list = cups_get_printer_list;
+  backend_class->print_stream = gtk_print_backend_cups_print_stream;
+  backend_class->printer_request_details = cups_printer_request_details;
+  backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
+  backend_class->printer_get_options = cups_printer_get_options;
+  backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
+  backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
+  backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
+  backend_class->printer_list_papers = cups_printer_list_papers;
+  backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
+  backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
+  backend_class->printer_get_capabilities = cups_printer_get_capabilities;
+  backend_class->set_password = gtk_print_backend_cups_set_password;
+}
+
+static void
+gtk_print_backend_cups_class_finalize (GtkPrintBackendCupsClass *class)
+{
+}
+
+static gboolean
+option_is_ipp_option (GtkPrinterOption *option)
+{
+  gpointer data = g_object_get_data (G_OBJECT (option), "is-ipp-option");
+
+  if (data != NULL)
+    return GPOINTER_TO_UINT (data) != 0;
+  else
+    return FALSE;
+}
+
+static void
+option_set_is_ipp_option (GtkPrinterOption *option,
+                          gboolean          is_ipp_option)
+{
+  g_object_set_data (G_OBJECT (option),
+                     "is-ipp-option",
+                     GUINT_TO_POINTER (is_ipp_option ? 1 : 0));
+}
+
+static cairo_status_t
+_cairo_write_to_cups (void                *closure,
+                      const unsigned char *data,
+                      unsigned int         length)
+{
+  GIOChannel *io = (GIOChannel *)closure;
+  gsize written;
+  GError *error;
+
+  error = NULL;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length));
+
+  while (length > 0)
+    {
+      g_io_channel_write_chars (io, (gchar *)data, length, &written, &error);
+
+      if (error != NULL)
+       {
+         GTK_NOTE (PRINTING,
+                    g_print ("CUPS Backend: Error writing to temp file, %s\n",
+                             error->message));
+
+          g_error_free (error);
+         return CAIRO_STATUS_WRITE_ERROR;
+       }
+
+      GTK_NOTE (PRINTING,
+                g_print ("CUPS Backend: Wrote %"G_GSIZE_FORMAT" bytes to temp file\n", written));
+
+      data += written;
+      length -= written;
+    }
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+cups_printer_create_cairo_surface (GtkPrinter       *printer,
+                                  GtkPrintSettings *settings,
+                                  gdouble           width,
+                                  gdouble           height,
+                                  GIOChannel       *cache_io)
+{
+  cairo_surface_t *surface;
+  ppd_file_t      *ppd_file = NULL;
+  ppd_attr_t      *ppd_attr = NULL;
+  ppd_attr_t      *ppd_attr_res = NULL;
+  ppd_attr_t      *ppd_attr_screen_freq = NULL;
+  ppd_attr_t      *ppd_attr_res_screen_freq = NULL;
+  gchar           *res_string = NULL;
+  gint             level = 2;
+
+  if (gtk_printer_accepts_pdf (printer))
+    surface = cairo_pdf_surface_create_for_stream (_cairo_write_to_cups, cache_io, width, height);
+  else
+    surface = cairo_ps_surface_create_for_stream  (_cairo_write_to_cups, cache_io, width, height);
+
+  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+
+  if (ppd_file != NULL)
+    {
+      ppd_attr = ppdFindAttr (ppd_file, "LanguageLevel", NULL);
+
+      if (ppd_attr != NULL)
+        level = atoi (ppd_attr->value);
+
+      if (gtk_print_settings_get_resolution (settings) == 0)
+        {
+          ppd_attr_res = ppdFindAttr (ppd_file, "DefaultResolution", NULL);
+
+          if (ppd_attr_res != NULL)
+            {
+              int res, res_x, res_y;
+
+              if (sscanf (ppd_attr_res->value, "%dx%ddpi", &res_x, &res_y) == 2)
+                {
+                  if (res_x > 0 && res_y > 0)
+                    gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
+                }
+              else if (sscanf (ppd_attr_res->value, "%ddpi", &res) == 1)
+                {
+                  if (res > 0)
+                    gtk_print_settings_set_resolution (settings, res);
+                }
+            }
+        }
+
+      res_string = g_strdup_printf ("%ddpi",
+                                    gtk_print_settings_get_resolution (settings));
+      ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
+      g_free (res_string);
+
+      if (ppd_attr_res_screen_freq == NULL)
+        {
+          res_string = g_strdup_printf ("%dx%ddpi",
+                                        gtk_print_settings_get_resolution_x (settings),
+                                        gtk_print_settings_get_resolution_y (settings));
+          ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
+          g_free (res_string);
+        }
+
+      ppd_attr_screen_freq = ppdFindAttr (ppd_file, "ScreenFreq", NULL);
+
+      if (ppd_attr_res_screen_freq != NULL && atof (ppd_attr_res_screen_freq->value) > 0.0)
+        gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_res_screen_freq->value));
+      else if (ppd_attr_screen_freq != NULL && atof (ppd_attr_screen_freq->value) > 0.0)
+        gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_screen_freq->value));
+    }
+
+  if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_PS)
+    {
+      if (level == 2)
+        cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
+
+      if (level == 3)
+        cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_3);
+    }
+
+  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 {
+  GtkPrintJobCompleteFunc callback;
+  GtkPrintJob *job;
+  gpointer user_data;
+  GDestroyNotify dnotify;
+  http_t *http;
+} CupsPrintStreamData;
+
+static void
+cups_free_print_stream_data (CupsPrintStreamData *data)
+{
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  if (data->dnotify)
+    data->dnotify (data->user_data);
+  g_object_unref (data->job);
+  if (data->http != NULL)
+    httpClose (data->http);
+  g_free (data);
+}
+
+static void
+cups_print_cb (GtkPrintBackendCups *print_backend,
+               GtkCupsResult       *result,
+               gpointer             user_data)
+{
+  GError *error = NULL;
+  CupsPrintStreamData *ps = user_data;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  if (gtk_cups_result_is_error (result))
+    error = g_error_new_literal (gtk_print_error_quark (),
+                                 GTK_PRINT_ERROR_INTERNAL_ERROR,
+                                 gtk_cups_result_get_error_string (result));
+
+  if (ps->callback)
+    ps->callback (ps->job, ps->user_data, error);
+
+  if (error == NULL)
+    {
+      int job_id = 0;
+      ipp_attribute_t *attr;           /* IPP job-id attribute */
+      ipp_t *response = gtk_cups_result_get_response (result);
+
+      if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
+       job_id = ippGetInteger (attr, 0);
+
+      if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
+       gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
+      else
+       {
+         gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
+         cups_begin_polling_info (print_backend, ps->job, job_id);
+       }
+    }
+  else
+    gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
+
+
+  if (error)
+    g_error_free (error);
+}
+
+typedef struct {
+  GtkCupsRequest *request;
+  GtkPrinterCups *printer;
+} CupsOptionsData;
+
+static void
+add_cups_options (const gchar *key,
+                 const gchar *value,
+                 gpointer     user_data)
+{
+  CupsOptionsData *data = (CupsOptionsData *) user_data;
+  GtkCupsRequest *request = data->request;
+  GtkPrinterCups *printer = data->printer;
+  gboolean custom_value = FALSE;
+  gchar *new_value = NULL;
+  gint i;
+
+  if (!key || !value)
+    return;
+
+  if (!g_str_has_prefix (key, "cups-"))
+    return;
+
+  if (strcmp (value, "gtk-ignore-value") == 0)
+    return;
+
+  key = key + strlen ("cups-");
+
+  if (printer && printer->ppd_file)
+    {
+      ppd_coption_t *coption;
+      gboolean       found = FALSE;
+      gboolean       custom_values_enabled = FALSE;
+
+      coption = ppdFindCustomOption (printer->ppd_file, key);
+      if (coption && coption->option)
+        {
+          for (i = 0; i < coption->option->num_choices; i++)
+            {
+              /* Are custom values enabled ? */
+              if (g_str_equal (coption->option->choices[i].choice, "Custom"))
+                custom_values_enabled = TRUE;
+
+              /* Is the value among available choices ? */
+              if (g_str_equal (coption->option->choices[i].choice, value))
+                found = TRUE;
+            }
+
+          if (custom_values_enabled && !found)
+            custom_value = TRUE;
+        }
+    }
+
+  /* Add "Custom." prefix to custom values if not already added. */
+  if (custom_value && !g_str_has_prefix (value, "Custom."))
+    {
+      new_value = g_strdup_printf ("Custom.%s", value);
+      gtk_cups_request_encode_option (request, key, new_value);
+      g_free (new_value);
+    }
+  else
+    gtk_cups_request_encode_option (request, key, value);
+}
+
+static void
+gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
+                                     GtkPrintJob             *job,
+                                    GIOChannel              *data_io,
+                                    GtkPrintJobCompleteFunc  callback,
+                                    gpointer                 user_data,
+                                    GDestroyNotify           dnotify)
+{
+  GtkPrinterCups *cups_printer;
+  CupsPrintStreamData *ps;
+  CupsOptionsData *options_data;
+  GtkCupsRequest *request = NULL;
+  GtkPrintSettings *settings;
+  const gchar *title;
+  char  printer_absolute_uri[HTTP_MAX_URI];
+  http_t *http = NULL;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
+  settings = gtk_print_job_get_settings (job);
+
+#ifdef HAVE_CUPS_API_1_6
+  if (cups_printer->avahi_browsed)
+    {
+      http = httpConnect (cups_printer->hostname, cups_printer->port);
+      if (http)
+        {
+          request = gtk_cups_request_new_with_username (http,
+                                                        GTK_CUPS_POST,
+                                                        IPP_PRINT_JOB,
+                                                        data_io,
+                                                        cups_printer->hostname,
+                                                        cups_printer->device_uri,
+                                                        GTK_PRINT_BACKEND_CUPS (print_backend)->username);
+          g_snprintf (printer_absolute_uri, HTTP_MAX_URI, "%s", cups_printer->printer_uri);
+        }
+      else
+        {
+          GError *error = NULL;
+
+          GTK_NOTE (PRINTING,
+                    g_warning ("CUPS Backend: Error connecting to %s:%d",
+                               cups_printer->hostname,
+                               cups_printer->port));
+
+          error = g_error_new (gtk_print_error_quark (),
+                               GTK_CUPS_ERROR_GENERAL,
+                               "Error connecting to %s",
+                               cups_printer->hostname);
+
+          gtk_print_job_set_status (job, GTK_PRINT_STATUS_FINISHED_ABORTED);
+
+          if (callback)
+            {
+              callback (job, user_data, error);
+            }
+
+          g_clear_error (&error);
+
+          return;
+        }
+    }
+  else
+#endif
+    {
+      request = gtk_cups_request_new_with_username (NULL,
+                                                    GTK_CUPS_POST,
+                                                    IPP_PRINT_JOB,
+                                                    data_io,
+                                                    NULL,
+                                                    cups_printer->device_uri,
+                                                    GTK_PRINT_BACKEND_CUPS (print_backend)->username);
+
+      httpAssembleURIf (HTTP_URI_CODING_ALL,
+                        printer_absolute_uri,
+                        sizeof (printer_absolute_uri),
+                        "ipp",
+                        NULL,
+                        "localhost",
+                        ippPort (),
+                        "/printers/%s",
+                        gtk_printer_get_name (gtk_print_job_get_printer (job)));
+    }
+
+  gtk_cups_request_set_ipp_version (request,
+                                    cups_printer->ipp_version_major,
+                                    cups_printer->ipp_version_minor);
+
+  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
+                                   IPP_TAG_URI, "printer-uri",
+                                   NULL, printer_absolute_uri);
+
+  title = gtk_print_job_get_title (job);
+  if (title) {
+    char *title_truncated = NULL;
+    size_t title_bytes = strlen (title);
+
+    if (title_bytes >= IPP_MAX_NAME)
+      {
+        gchar *end;
+
+        end = g_utf8_find_prev_char (title, title + IPP_MAX_NAME - 1);
+        title_truncated = g_utf8_substring (title,
+                                            0,
+                                            g_utf8_pointer_to_offset (title, end));
+      }
+
+    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION,
+                                     IPP_TAG_NAME, "job-name",
+                                     NULL,
+                                     title_truncated ? title_truncated : title);
+    g_free (title_truncated);
+  }
+
+  options_data = g_new0 (CupsOptionsData, 1);
+  options_data->request = request;
+  options_data->printer = cups_printer;
+  gtk_print_settings_foreach (settings, add_cups_options, options_data);
+  g_free (options_data);
+
+  ps = g_new0 (CupsPrintStreamData, 1);
+  ps->callback = callback;
+  ps->user_data = user_data;
+  ps->dnotify = dnotify;
+  ps->job = g_object_ref (job);
+  ps->http = http;
+
+  request->need_auth_info = FALSE;
+  request->auth_info_required = NULL;
+
+  /* Check if auth_info_required is set and if it should be handled.
+   * The cups libraries handle the ticket exchange for "negotiate". */
+  if (cups_printer->auth_info_required != NULL &&
+      g_strv_length (cups_printer->auth_info_required) == 1 &&
+      g_strcmp0 (cups_printer->auth_info_required[0], "negotiate") == 0)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("CUPS Backend: Ignoring auth-info-required \"%s\"\n",
+                         cups_printer->auth_info_required[0]));
+    }
+  else if (cups_printer->auth_info_required != NULL)
+    {
+      request->need_auth_info = TRUE;
+      request->auth_info_required = g_strdupv (cups_printer->auth_info_required);
+    }
+
+  cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
+                        request,
+                        (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
+                        ps,
+                        (GDestroyNotify)cups_free_print_stream_data);
+}
+
+void overwrite_and_free (gpointer data)
+{
+  gchar *password = (gchar *) data;
+
+  if (password != NULL)
+    {
+      memset (password, 0, strlen (password));
+      g_free (password);
+    }
+}
+
+static void
+gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
+{
+#ifdef HAVE_CUPS_API_1_6
+  gint i;
+#endif
+
+  backend_cups->list_printers_poll = FALSE;
+  backend_cups->got_default_printer = FALSE;
+  backend_cups->list_printers_pending = FALSE;
+  backend_cups->list_printers_attempts = 0;
+  backend_cups->reading_ppds = 0;
+
+  backend_cups->requests = NULL;
+  backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free);
+  backend_cups->authentication_lock = FALSE;
+
+  backend_cups->default_printer_poll = 0;
+  backend_cups->cups_connection_test = NULL;
+
+  backend_cups->username = NULL;
+
+#ifdef HAVE_COLORD
+  backend_cups->colord_client = cd_client_new ();
+#endif
+
+#ifdef HAVE_CUPS_API_1_6
+  backend_cups->dbus_connection = NULL;
+  backend_cups->avahi_default_printer = NULL;
+  backend_cups->avahi_service_browser_subscription_id = 0;
+  for (i = 0; i < 2; i++)
+    {
+      backend_cups->avahi_service_browser_paths[i] = NULL;
+      backend_cups->avahi_service_browser_subscription_ids[i] = 0;
+    }
+#endif
+
+  cups_get_local_default_printer (backend_cups);
+
+  backend_cups->secrets_service_available = FALSE;
+  backend_cups->secrets_service_cancellable = g_cancellable_new ();
+  backend_cups->secrets_service_watch_id =
+    gtk_cups_secrets_service_watch (secrets_service_appeared_cb,
+                                    secrets_service_vanished_cb,
+                                    backend_cups);
+}
+
+static void
+gtk_print_backend_cups_finalize (GObject *object)
+{
+  GtkPrintBackendCups *backend_cups;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: finalizing CUPS backend module\n"));
+
+  backend_cups = GTK_PRINT_BACKEND_CUPS (object);
+
+  g_free (backend_cups->default_printer);
+  backend_cups->default_printer = NULL;
+
+  gtk_cups_connection_test_free (backend_cups->cups_connection_test);
+  backend_cups->cups_connection_test = NULL;
+
+  g_hash_table_destroy (backend_cups->auth);
+
+  g_free (backend_cups->username);
+
+#ifdef HAVE_COLORD
+  g_object_unref (backend_cups->colord_client);
+#endif
+
+#ifdef HAVE_CUPS_API_1_6
+  g_clear_object (&backend_cups->avahi_cancellable);
+  g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
+  g_clear_object (&backend_cups->dbus_connection);
+#endif
+
+  g_clear_object (&backend_cups->secrets_service_cancellable);
+  if (backend_cups->secrets_service_watch_id != 0)
+    {
+      g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
+    }
+
+  backend_parent_class->finalize (object);
+}
+
+static void
+gtk_print_backend_cups_dispose (GObject *object)
+{
+  GtkPrintBackendCups *backend_cups;
+#ifdef HAVE_CUPS_API_1_6
+  gint                 i;
+#endif
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  backend_cups = GTK_PRINT_BACKEND_CUPS (object);
+
+  if (backend_cups->list_printers_poll > 0)
+    g_source_remove (backend_cups->list_printers_poll);
+  backend_cups->list_printers_poll = 0;
+  backend_cups->list_printers_attempts = 0;
+
+  if (backend_cups->default_printer_poll > 0)
+    g_source_remove (backend_cups->default_printer_poll);
+  backend_cups->default_printer_poll = 0;
+
+#ifdef HAVE_CUPS_API_1_6
+  g_cancellable_cancel (backend_cups->avahi_cancellable);
+
+  for (i = 0; i < 2; i++)
+    {
+      if (backend_cups->avahi_service_browser_subscription_ids[i] > 0)
+        {
+          g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
+                                                backend_cups->avahi_service_browser_subscription_ids[i]);
+          backend_cups->avahi_service_browser_subscription_ids[i] = 0;
+        }
+
+      if (backend_cups->avahi_service_browser_paths[i])
+        {
+          g_dbus_connection_call (backend_cups->dbus_connection,
+                                  AVAHI_BUS,
+                                  backend_cups->avahi_service_browser_paths[i],
+                                  AVAHI_SERVICE_BROWSER_IFACE,
+                                  "Free",
+                                  NULL,
+                                  NULL,
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  -1,
+                                  NULL,
+                                  NULL,
+                                  NULL);
+          g_clear_pointer (&backend_cups->avahi_service_browser_paths[i], g_free);
+        }
+    }
+
+  if (backend_cups->avahi_service_browser_subscription_id > 0)
+    {
+      g_dbus_connection_signal_unsubscribe (backend_cups->dbus_connection,
+                                            backend_cups->avahi_service_browser_subscription_id);
+      backend_cups->avahi_service_browser_subscription_id = 0;
+    }
+#endif
+
+  backend_parent_class->dispose (object);
+}
+
+static gboolean
+is_address_local (const gchar *address)
+{
+  if (address[0] == '/' ||
+      strcmp (address, "127.0.0.1") == 0 ||
+      strcmp (address, "[::1]") == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static void
+gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
+                                     gchar           **auth_info_required,
+                                     gchar           **auth_info,
+                                     gboolean          store_auth_info)
+{
+  GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
+  GList *l;
+  char   dispatch_hostname[HTTP_MAX_URI];
+  gchar *username = NULL;
+  gchar *hostname = NULL;
+  gchar *password = NULL;
+  gint   length;
+  gint   i;
+
+  length = g_strv_length (auth_info_required);
+
+  if (auth_info != NULL)
+    for (i = 0; i < length; i++)
+      {
+        if (g_strcmp0 (auth_info_required[i], "username") == 0)
+          username = g_strdup (auth_info[i]);
+        else if (g_strcmp0 (auth_info_required[i], "hostname") == 0)
+          hostname = g_strdup (auth_info[i]);
+        else if (g_strcmp0 (auth_info_required[i], "password") == 0)
+          password = g_strdup (auth_info[i]);
+      }
+
+  if (hostname != NULL && username != NULL && password != NULL)
+    {
+      gchar *key = g_strconcat (username, "@", hostname, NULL);
+      g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
+      GTK_NOTE (PRINTING,
+                g_print ("CUPS backend: caching password for %s\n", key));
+    }
+
+  g_free (cups_backend->username);
+  cups_backend->username = g_strdup (username);
+
+
+  for (l = cups_backend->requests; l; l = l->next)
+    {
+      GtkPrintCupsDispatchWatch *dispatch = l->data;
+
+      httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname));
+      if (is_address_local (dispatch_hostname))
+        strcpy (dispatch_hostname, "localhost");
+
+      if (dispatch->request->need_auth_info)
+        {
+          if (auth_info != NULL)
+            {
+              dispatch->request->auth_info = g_new0 (gchar *, length + 1);
+              for (i = 0; i < length; i++)
+                dispatch->request->auth_info[i] = g_strdup (auth_info[i]);
+            }
+          /* Save the password if the user requested it */
+          if (password != NULL && store_auth_info)
+            {
+              const gchar *printer_uri =
+                  gtk_cups_request_ipp_get_string (dispatch->request,
+                                                   IPP_TAG_URI,
+                                                   "printer-uri");
+
+              gtk_cups_secrets_service_store (auth_info, auth_info_required,
+                                              printer_uri);
+            }
+          dispatch->backend->authentication_lock = FALSE;
+          dispatch->request->need_auth_info = FALSE;
+        }
+      else if (dispatch->request->password_state == GTK_CUPS_PASSWORD_REQUESTED || auth_info == NULL)
+        {
+          overwrite_and_free (dispatch->request->password);
+          dispatch->request->password = g_strdup (password);
+          g_free (dispatch->request->username);
+          dispatch->request->username = g_strdup (username);
+          dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
+          dispatch->backend->authentication_lock = FALSE;
+        }
+    }
+}
+
+static gboolean
+request_password (gpointer data)
+{
+  GtkPrintCupsDispatchWatch *dispatch = data;
+  const gchar               *username;
+  gchar                     *password;
+  gchar                     *prompt = NULL;
+  gchar                     *key = NULL;
+  char                       hostname[HTTP_MAX_URI];
+  gchar                    **auth_info_required;
+  gchar                    **auth_info_default;
+  gchar                    **auth_info_display;
+  gboolean                  *auth_info_visible;
+  gint                       length = 3;
+  gint                       i;
+
+  if (dispatch->backend->authentication_lock)
+    return G_SOURCE_REMOVE;
+
+  httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
+  if (is_address_local (hostname))
+    strcpy (hostname, "localhost");
+
+  if (dispatch->backend->username != NULL)
+    username = dispatch->backend->username;
+  else
+    username = cupsUser ();
+
+  auth_info_required = g_new0 (gchar*, length + 1);
+  auth_info_required[0] = g_strdup ("hostname");
+  auth_info_required[1] = g_strdup ("username");
+  auth_info_required[2] = g_strdup ("password");
+
+  auth_info_default = g_new0 (gchar*, length + 1);
+  auth_info_default[0] = g_strdup (hostname);
+  auth_info_default[1] = g_strdup (username);
+
+  auth_info_display = g_new0 (gchar*, length + 1);
+  auth_info_display[1] = g_strdup (_("Username:"));
+  auth_info_display[2] = g_strdup (_("Password:"));
+
+  auth_info_visible = g_new0 (gboolean, length + 1);
+  auth_info_visible[1] = TRUE;
+
+  key = g_strconcat (username, "@", hostname, NULL);
+  password = g_hash_table_lookup (dispatch->backend->auth, key);
+
+  if (password && dispatch->request->password_state != GTK_CUPS_PASSWORD_NOT_VALID)
+    {
+      GTK_NOTE (PRINTING,
+                g_print ("CUPS backend: using stored password for %s\n", key));
+
+      overwrite_and_free (dispatch->request->password);
+      dispatch->request->password = g_strdup (password);
+      g_free (dispatch->request->username);
+      dispatch->request->username = g_strdup (username);
+      dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
+    }
+  else
+    {
+      const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
+      const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
+      char *printer_name = NULL;
+
+      if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
+        printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
+
+      if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID)
+        g_hash_table_remove (dispatch->backend->auth, key);
+
+      dispatch->request->password_state = GTK_CUPS_PASSWORD_REQUESTED;
+
+      dispatch->backend->authentication_lock = TRUE;
+
+      switch (ippGetOperation (dispatch->request->ipp_request))
+        {
+          case IPP_PRINT_JOB:
+            if (job_title != NULL && printer_name != NULL)
+              prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
+            else
+              prompt = g_strdup_printf ( _("Authentication is required to print a document on %s"), hostname);
+            break;
+          case IPP_GET_JOB_ATTRIBUTES:
+            if (job_title != NULL)
+              prompt = g_strdup_printf ( _("Authentication is required to get attributes of job “%s”"), job_title);
+            else
+              prompt = g_strdup ( _("Authentication is required to get attributes of a job"));
+            break;
+          case IPP_GET_PRINTER_ATTRIBUTES:
+            if (printer_name != NULL)
+              prompt = g_strdup_printf ( _("Authentication is required to get attributes of printer %s"), printer_name);
+            else
+              prompt = g_strdup ( _("Authentication is required to get attributes of a printer"));
+            break;
+          case CUPS_GET_DEFAULT:
+            prompt = g_strdup_printf ( _("Authentication is required to get default printer of %s"), hostname);
+            break;
+          case CUPS_GET_PRINTERS:
+            prompt = g_strdup_printf ( _("Authentication is required to get printers from %s"), hostname);
+            break;
+          default:
+            /* work around gcc warning about 0 not being a value for this enum */
+            if (ippGetOperation (dispatch->request->ipp_request) == 0)
+              prompt = g_strdup_printf ( _("Authentication is required to get a file from %s"), hostname);
+            else
+              prompt = g_strdup_printf ( _("Authentication is required on %s"), hostname);
+            break;
+        }
+
+      g_free (printer_name);
+
+      g_signal_emit_by_name (dispatch->backend, "request-password",
+                             auth_info_required, auth_info_default,
+                             auth_info_display, auth_info_visible, prompt,
+                             FALSE); /* Cups password is only cached not stored. */
+
+      g_free (prompt);
+    }
+
+  for (i = 0; i < length; i++)
+    {
+      g_free (auth_info_required[i]);
+      g_free (auth_info_default[i]);
+      g_free (auth_info_display[i]);
+    }
+
+  g_free (auth_info_required);
+  g_free (auth_info_default);
+  g_free (auth_info_display);
+  g_free (auth_info_visible);
+  g_free (key);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+cups_dispatch_add_poll (GSource *source)
+{
+  GtkPrintCupsDispatchWatch *dispatch;
+  GtkCupsPollState poll_state;
+
+  dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+  poll_state = gtk_cups_request_get_poll_state (dispatch->request);
+
+  /* Remove the old source if the poll state changed. */
+  if (poll_state != dispatch->poll_state && dispatch->data_poll != NULL)
+    {
+      g_source_remove_poll (source, dispatch->data_poll);
+      g_free (dispatch->data_poll);
+      dispatch->data_poll = NULL;
+    }
+
+  if (dispatch->request->http != NULL)
+    {
+      if (dispatch->data_poll == NULL)
+        {
+         dispatch->data_poll = g_new0 (GPollFD, 1);
+         dispatch->poll_state = poll_state;
+
+         if (poll_state == GTK_CUPS_HTTP_READ)
+           dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
+         else if (poll_state == GTK_CUPS_HTTP_WRITE)
+           dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
+         else
+           dispatch->data_poll->events = 0;
+
+          dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
+          g_source_add_poll (source, dispatch->data_poll);
+        }
+    }
+}
+
+static gboolean
+check_auth_info (gpointer user_data)
+{
+  GtkPrintCupsDispatchWatch *dispatch;
+  dispatch = (GtkPrintCupsDispatchWatch *) user_data;
+
+  if (!dispatch->request->need_auth_info)
+    {
+      if (dispatch->request->auth_info == NULL)
+        {
+          dispatch->callback (GTK_PRINT_BACKEND (dispatch->backend),
+                              gtk_cups_request_get_result (dispatch->request),
+                              dispatch->callback_data);
+          g_source_destroy ((GSource *) dispatch);
+        }
+      else
+        {
+          gint length;
+          gint i;
+
+          length = g_strv_length (dispatch->request->auth_info_required);
+
+          gtk_cups_request_ipp_add_strings (dispatch->request,
+                                            IPP_TAG_JOB,
+                                            IPP_TAG_TEXT,
+                                            "auth-info",
+                                            length,
+                                            NULL,
+                                            (const char * const *) dispatch->request->auth_info);
+
+          g_source_attach ((GSource *) dispatch, NULL);
+          g_source_unref ((GSource *) dispatch);
+
+          for (i = 0; i < length; i++)
+            overwrite_and_free (dispatch->request->auth_info[i]);
+          g_free (dispatch->request->auth_info);
+          dispatch->request->auth_info = NULL;
+        }
+
+      return G_SOURCE_REMOVE;
+    }
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+lookup_auth_info_cb (GObject      *source_object,
+                     GAsyncResult *res,
+                     gpointer      user_data)
+{
+  GTask                      *task;
+  GtkPrintCupsDispatchWatch  *dispatch;
+  gchar                     **auth_info;
+  GError                     *error = NULL;
+  gint                        i;
+
+  task = (GTask *) res;
+  dispatch = user_data;
+  auth_info = g_task_propagate_pointer (task, &error);
+
+  if (auth_info == NULL)
+    {
+      if (error != NULL)
+        {
+          GTK_NOTE (PRINTING,
+                    g_print ("Failed to look up auth info: %s\n", error->message));
+          g_error_free (error);
+        }
+      else
+        {
+          /* Error note should have been shown by the function causing this */
+          GTK_NOTE (PRINTING, g_print ("Failed to look up auth info.\n"));
+        }
+      dispatch->backend->authentication_lock = FALSE;
+      g_object_unref (task);
+      request_auth_info (dispatch);
+      return;
+    }
+
+  gtk_print_backend_cups_set_password (GTK_PRINT_BACKEND (dispatch->backend),
+                                       dispatch->request->auth_info_required, auth_info,
+                                       FALSE);
+  for (i = 0; auth_info[i] != NULL; i++)
+    {
+      overwrite_and_free (auth_info[i]);
+      auth_info[i] = NULL;
+    }
+  g_clear_pointer (auth_info, g_free);
+
+  g_object_unref (task);
+}
+
+static void
+lookup_auth_info (gpointer user_data)
+{
+  GtkPrintCupsDispatchWatch  *dispatch;
+  gsize                       length,
+                              i;
+  gboolean                    need_secret_auth_info = FALSE;
+  const gchar                *printer_uri;
+
+  dispatch = user_data;
+
+  if (dispatch->backend->authentication_lock)
+    return;
+
+  length = g_strv_length (dispatch->request->auth_info_required);
+
+  for (i = 0; i < length; i++)
+    {
+      if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
+        {
+          need_secret_auth_info = TRUE;
+          break;
+        }
+    }
+
+  g_idle_add (check_auth_info, user_data);
+
+  if (dispatch->backend->secrets_service_available && need_secret_auth_info)
+    {
+      dispatch->backend->authentication_lock = TRUE;
+      printer_uri = gtk_cups_request_ipp_get_string (dispatch->request,
+                                                     IPP_TAG_URI,
+                                                     "printer-uri");
+      gtk_cups_secrets_service_query_task (dispatch->backend,
+                                           dispatch->backend->secrets_service_cancellable,
+                                           lookup_auth_info_cb,
+                                           dispatch,
+                                           printer_uri,
+                                           dispatch->request->auth_info_required);
+      return;
+    }
+
+  request_auth_info (user_data);
+}
+
+static gboolean
+request_auth_info (gpointer user_data)
+{
+  GtkPrintCupsDispatchWatch  *dispatch;
+  const char                 *job_title;
+  const char                 *printer_uri;
+  gchar                      *prompt = NULL;
+  char                       *printer_name = NULL;
+  gint                        length;
+  gint                        i;
+  gboolean                   *auth_info_visible = NULL;
+  gchar                     **auth_info_default = NULL;
+  gchar                     **auth_info_display = NULL;
+
+  dispatch = (GtkPrintCupsDispatchWatch *) user_data;
+
+  if (dispatch->backend->authentication_lock)
+    return FALSE;
+
+  job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
+  printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
+  length = g_strv_length (dispatch->request->auth_info_required);
+
+  auth_info_visible = g_new0 (gboolean, length);
+  auth_info_default = g_new0 (gchar *, length + 1);
+  auth_info_display = g_new0 (gchar *, length + 1);
+
+  for (i = 0; i < length; i++)
+    {
+      if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0)
+        {
+          auth_info_display[i] = g_strdup (_("Domain:"));
+          auth_info_default[i] = g_strdup ("WORKGROUP");
+          auth_info_visible[i] = TRUE;
+        }
+      else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0)
+        {
+          auth_info_display[i] = g_strdup (_("Username:"));
+          if (dispatch->backend->username != NULL)
+            auth_info_default[i] = g_strdup (dispatch->backend->username);
+          else
+            auth_info_default[i] = g_strdup (cupsUser ());
+          auth_info_visible[i] = TRUE;
+        }
+      else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
+        {
+          auth_info_display[i] = g_strdup (_("Password:"));
+          auth_info_visible[i] = FALSE;
+        }
+    }
+
+  if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
+    printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
+
+  dispatch->backend->authentication_lock = TRUE;
+
+  if (job_title != NULL)
+    {
+      if (printer_name != NULL)
+        prompt = g_strdup_printf ( _("Authentication is required to print document “%s” on printer %s"), job_title, printer_name);
+      else
+        prompt = g_strdup_printf ( _("Authentication is required to print document “%s”"), job_title);
+    }
+  else
+    {
+      if (printer_name != NULL)
+        prompt = g_strdup_printf ( _("Authentication is required to print this document on printer %s"), printer_name);
+      else
+        prompt = g_strdup ( _("Authentication is required to print this document"));
+    }
+
+  g_signal_emit_by_name (dispatch->backend, "request-password",
+                         dispatch->request->auth_info_required,
+                         auth_info_default,
+                         auth_info_display,
+                         auth_info_visible,
+                         prompt,
+                         dispatch->backend->secrets_service_available);
+
+  for (i = 0; i < length; i++)
+    {
+      g_free (auth_info_default[i]);
+      g_free (auth_info_display[i]);
+    }
+
+  g_free (auth_info_default);
+  g_free (auth_info_display);
+  g_free (printer_name);
+  g_free (prompt);
+
+  return FALSE;
+}
+
+static gboolean
+cups_dispatch_watch_check (GSource *source)
+{
+  GtkPrintCupsDispatchWatch *dispatch;
+  GtkCupsPollState poll_state;
+  gboolean result;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
+
+  dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+  poll_state = gtk_cups_request_get_poll_state (dispatch->request);
+
+  if (poll_state != GTK_CUPS_HTTP_IDLE && !dispatch->request->need_password)
+    if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
+       return FALSE;
+
+  result = gtk_cups_request_read_write (dispatch->request, FALSE);
+  if (result && dispatch->data_poll != NULL)
+    {
+      g_source_remove_poll (source, dispatch->data_poll);
+      g_free (dispatch->data_poll);
+      dispatch->data_poll = NULL;
+    }
+
+  if (dispatch->request->need_password && dispatch->request->password_state != GTK_CUPS_PASSWORD_REQUESTED)
+    {
+      dispatch->request->need_password = FALSE;
+      g_idle_add (request_password, dispatch);
+      result = FALSE;
+    }
+
+  return result;
+}
+
+static gboolean
+cups_dispatch_watch_prepare (GSource *source,
+                            gint    *timeout_)
+{
+  GtkPrintCupsDispatchWatch *dispatch;
+  gboolean result;
+
+  dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
+
+  *timeout_ = -1;
+
+  result = gtk_cups_request_read_write (dispatch->request, TRUE);
+
+  cups_dispatch_add_poll (source);
+
+  return result;
+}
+
+static gboolean
+cups_dispatch_watch_dispatch (GSource     *source,
+                             GSourceFunc  callback,
+                             gpointer     user_data)
+{
+  GtkPrintCupsDispatchWatch *dispatch;
+  GtkPrintCupsResponseCallbackFunc ep_callback;
+  GtkCupsResult *result;
+
+  g_assert (callback != NULL);
+
+  ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
+
+  dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+  result = gtk_cups_request_get_result (dispatch->request);
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
+
+  if (gtk_cups_result_is_error (result))
+    {
+      GTK_NOTE (PRINTING,
+                g_print("Error result: %s (type %i, status %i, code %i)\n",
+                        gtk_cups_result_get_error_string (result),
+                        gtk_cups_result_get_error_type (result),
+                        gtk_cups_result_get_error_status (result),
+                        gtk_cups_result_get_error_code (result)));
+     }
+
+  ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
+
+  return FALSE;
+}
+
+static void
+cups_dispatch_watch_finalize (GSource *source)
+{
+  GtkPrintCupsDispatchWatch *dispatch;
+  GtkCupsResult *result;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
+
+  dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+  result = gtk_cups_request_get_result (dispatch->request);
+  if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH)
+    {
+      const gchar *username;
+      gchar        hostname[HTTP_MAX_URI];
+      gchar       *key;
+
+      httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
+      if (is_address_local (hostname))
+        strcpy (hostname, "localhost");
+
+      if (dispatch->backend->username != NULL)
+        username = dispatch->backend->username;
+      else
+        username = cupsUser ();
+
+      key = g_strconcat (username, "@", hostname, NULL);
+      GTK_NOTE (PRINTING,
+                g_print ("CUPS backend: removing stored password for %s\n", key));
+      g_hash_table_remove (dispatch->backend->auth, key);
+      g_free (key);
+
+      if (dispatch->backend)
+        dispatch->backend->authentication_lock = FALSE;
+    }
+
+  gtk_cups_request_free (dispatch->request);
+
+  if (dispatch->backend)
+    {
+      /* We need to unref this at idle time, because it might be the
+       * last reference to this module causing the code to be
+       * unloaded (including this particular function!)
+       * Update: Doing this at idle caused a deadlock taking the
+       * mainloop context lock while being in a GSource callout for
+       * multithreaded apps. So, for now we just disable unloading
+       * of print backends. See _gtk_print_backend_create for the
+       * disabling.
+       */
+
+      dispatch->backend->requests = g_list_remove (dispatch->backend->requests, dispatch);
+
+
+      g_object_unref (dispatch->backend);
+      dispatch->backend = NULL;
+    }
+
+  if (dispatch->data_poll)
+    {
+      g_source_remove_poll (source, dispatch->data_poll);
+      g_free (dispatch->data_poll);
+      dispatch->data_poll = NULL;
+    }
+}
+
+static GSourceFuncs _cups_dispatch_watch_funcs = {
+  cups_dispatch_watch_prepare,
+  cups_dispatch_watch_check,
+  cups_dispatch_watch_dispatch,
+  cups_dispatch_watch_finalize
+};
+
+
+static void
+cups_request_execute (GtkPrintBackendCups              *print_backend,
+                      GtkCupsRequest                   *request,
+                      GtkPrintCupsResponseCallbackFunc  callback,
+                      gpointer                          user_data,
+                      GDestroyNotify                    notify)
+{
+  GtkPrintCupsDispatchWatch *dispatch;
+
+  dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs,
+                                                         sizeof (GtkPrintCupsDispatchWatch));
+  g_source_set_name (&dispatch->source, "GTK+ CUPS backend");
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
+
+  dispatch->request = request;
+  dispatch->backend = g_object_ref (print_backend);
+  dispatch->poll_state = GTK_CUPS_HTTP_IDLE;
+  dispatch->data_poll = NULL;
+  dispatch->callback = NULL;
+  dispatch->callback_data = NULL;
+
+  print_backend->requests = g_list_prepend (print_backend->requests, dispatch);
+
+  g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
+
+  if (request->need_auth_info)
+    {
+      dispatch->callback = callback;
+      dispatch->callback_data = user_data;
+      lookup_auth_info (dispatch);
+    }
+  else
+    {
+      g_source_attach ((GSource *) dispatch, NULL);
+      g_source_unref ((GSource *) dispatch);
+    }
+}
+
+typedef struct {
+  GtkPrintBackendCups *print_backend;
+  GtkPrintJob *job;
+  int job_id;
+  int counter;
+} CupsJobPollData;
+
+static void
+job_object_died        (gpointer  user_data,
+                GObject  *where_the_object_was)
+{
+  CupsJobPollData *data = user_data;
+  data->job = NULL;
+}
+
+static void
+cups_job_poll_data_free (CupsJobPollData *data)
+{
+  if (data->job)
+    g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
+
+  g_free (data);
+}
+
+static void
+cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
+                         GtkCupsResult       *result,
+                         gpointer             user_data)
+{
+  CupsJobPollData *data = user_data;
+  ipp_attribute_t *attr;
+  ipp_t *response;
+  int state;
+  gboolean done;
+
+  if (data->job == NULL)
+    {
+      cups_job_poll_data_free (data);
+      return;
+    }
+
+  data->counter++;
+
+  response = gtk_cups_result_get_response (result);
+
+  state = 0;
+
+#ifdef HAVE_CUPS_API_1_6
+  attr = ippFindAttribute (response, "job-state", IPP_TAG_ENUM);
+  state = ippGetInteger (attr, 0);
+#else
+  for (attr = response->attrs; attr != NULL; attr = attr->next)
+    {
+      if (!attr->name)
+        continue;
+
+      _CUPS_MAP_ATTR_INT (attr, state, "job-state");
+    }
+#endif
+
+  done = FALSE;
+  switch (state)
+    {
+    case IPP_JOB_PENDING:
+    case IPP_JOB_HELD:
+    case IPP_JOB_STOPPED:
+      gtk_print_job_set_status (data->job,
+                               GTK_PRINT_STATUS_PENDING);
+      break;
+    case IPP_JOB_PROCESSING:
+      gtk_print_job_set_status (data->job,
+                               GTK_PRINT_STATUS_PRINTING);
+      break;
+    default:
+    case IPP_JOB_CANCELLED:
+    case IPP_JOB_ABORTED:
+      gtk_print_job_set_status (data->job,
+                               GTK_PRINT_STATUS_FINISHED_ABORTED);
+      done = TRUE;
+      break;
+    case 0:
+    case IPP_JOB_COMPLETED:
+      gtk_print_job_set_status (data->job,
+                               GTK_PRINT_STATUS_FINISHED);
+      done = TRUE;
+      break;
+    }
+
+  if (!done && data->job != NULL)
+    {
+      guint32 timeout;
+      guint id;
+
+      if (data->counter < 5)
+       timeout = 100;
+      else if (data->counter < 10)
+       timeout = 500;
+      else
+       timeout = 1000;
+
+      id = g_timeout_add (timeout, cups_job_info_poll_timeout, data);
+      g_source_set_name_by_id (id, "[gtk+] cups_job_info_poll_timeout");
+    }
+  else
+    cups_job_poll_data_free (data);
+}
+
+static void
+cups_request_job_info (CupsJobPollData *data)
+{
+  GtkCupsRequest *request;
+  gchar *job_uri;
+
+  request = gtk_cups_request_new_with_username (NULL,
+                                                GTK_CUPS_POST,
+                                                IPP_GET_JOB_ATTRIBUTES,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                data->print_backend->username);
+
+  job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
+  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                                   "job-uri", NULL, job_uri);
+  g_free (job_uri);
+
+  cups_request_execute (data->print_backend,
+                        request,
+                        (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
+                        data,
+                        NULL);
+}
+
+static gboolean
+cups_job_info_poll_timeout (gpointer user_data)
+{
+  CupsJobPollData *data = user_data;
+
+  if (data->job == NULL)
+    cups_job_poll_data_free (data);
+  else
+    cups_request_job_info (data);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+cups_begin_polling_info (GtkPrintBackendCups *print_backend,
+                        GtkPrintJob         *job,
+                        gint                 job_id)
+{
+  CupsJobPollData *data;
+
+  data = g_new0 (CupsJobPollData, 1);
+
+  data->print_backend = print_backend;
+  data->job = job;
+  data->job_id = job_id;
+  data->counter = 0;
+
+  g_object_weak_ref (G_OBJECT (job), job_object_died, data);
+
+  cups_request_job_info (data);
+}
+
+static void
+mark_printer_inactive (GtkPrinter      *printer,
+                       GtkPrintBackend *backend)
+{
+  gtk_printer_set_is_active (printer, FALSE);
+  g_signal_emit_by_name (backend, "printer-removed", printer);
+}
+
+static gint
+find_printer (GtkPrinter  *printer,
+             const gchar *find_name)
+{
+  const gchar *printer_name;
+
+  printer_name = gtk_printer_get_name (printer);
+  return g_ascii_strcasecmp (printer_name, find_name);
+}
+/* Printer messages we're interested in */
+static const char * const printer_messages[] =
+  {
+    "toner-low",
+    "toner-empty",
+    "developer-low",
+    "developer-empty",
+    "marker-supply-low",
+    "marker-supply-empty",
+    "cover-open",
+    "door-open",
+    "media-low",
+    "media-empty",
+    "offline",
+    "other"
+  };
+
+/* Attributes we're interested in for printers */
+static const char * const printer_attrs[] =
+  {
+    "printer-name",
+    "printer-uri-supported",
+    "member-uris",
+    "printer-location",
+    "printer-info",
+    "printer-state-message",
+    "printer-state-reasons",
+    "printer-state",
+    "queued-job-count",
+    "printer-is-accepting-jobs",
+    "job-sheets-supported",
+    "job-sheets-default",
+    "printer-type",
+    "auth-info-required",
+    "number-up-default",
+    "ipp-versions-supported",
+    "multiple-document-handling-supported",
+    "copies-supported",
+    "number-up-supported",
+    "device-uri"
+  };
+
+/* Attributes we're interested in for printers without PPD */
+static const char * const printer_attrs_detailed[] =
+  {
+    "printer-name",
+    "printer-uri-supported",
+    "member-uris",
+    "printer-location",
+    "printer-info",
+    "printer-state-message",
+    "printer-state-reasons",
+    "printer-state",
+    "queued-job-count",
+    "printer-is-accepting-jobs",
+    "job-sheets-supported",
+    "job-sheets-default",
+    "printer-type",
+    "auth-info-required",
+    "number-up-default",
+    "ipp-versions-supported",
+    "multiple-document-handling-supported",
+    "copies-supported",
+    "number-up-supported",
+    "media-col-default",
+    "media-col-supported",
+    "media-default",
+    "media-size-supported",
+    "media-supported",
+    "media-left-margin-supported",
+    "media-right-margin-supported",
+    "media-bottom-margin-supported",
+    "media-top-margin-supported",
+    "sides-default",
+    "sides-supported",
+    "output-bin-default",
+    "output-bin-supported",
+  };
+
+typedef enum
+  {
+    GTK_PRINTER_STATE_LEVEL_NONE = 0,
+    GTK_PRINTER_STATE_LEVEL_INFO = 1,
+    GTK_PRINTER_STATE_LEVEL_WARNING = 2,
+    GTK_PRINTER_STATE_LEVEL_ERROR = 3
+  } PrinterStateLevel;
+
+typedef struct
+{
+  float x_dimension;
+  float y_dimension;
+} MediaSize;
+
+typedef struct
+{
+  const gchar *printer_name;
+  const gchar *printer_uri;
+  const gchar *member_uris;
+  const gchar *location;
+  const gchar *description;
+  gchar *state_msg;
+  const gchar *reason_msg;
+  PrinterStateLevel reason_level;
+  gint state;
+  gint job_count;
+  gboolean is_paused;
+  gboolean is_accepting_jobs;
+  const gchar *default_cover_before;
+  const gchar *default_cover_after;
+  gboolean default_printer;
+  gboolean got_printer_type;
+  gboolean remote_printer;
+#ifdef HAVE_CUPS_API_1_6
+  gboolean avahi_printer;
+#endif
+  gchar  **auth_info_required;
+  gint     default_number_up;
+  guchar   ipp_version_major;
+  guchar   ipp_version_minor;
+  gboolean supports_copies;
+  gboolean supports_collate;
+  gboolean supports_number_up;
+  gchar    *media_default;
+  GList    *media_supported;
+  GList    *media_size_supported;
+  float     media_bottom_margin_default;
+  float     media_top_margin_default;
+  float     media_left_margin_default;
+  float     media_right_margin_default;
+  gboolean  media_margin_default_set;
+  gchar    *sides_default;
+  GList    *sides_supported;
+  char    **covers;
+  int       number_of_covers;
+  gchar    *output_bin_default;
+  GList    *output_bin_supported;
+  gchar    *original_device_uri;
+} PrinterSetupInfo;
+
+static void
+printer_setup_info_free (PrinterSetupInfo *info)
+{
+  g_free (info->original_device_uri);
+  g_free (info->state_msg);
+  g_strfreev (info->covers);
+  g_slice_free (PrinterSetupInfo, info);
+}
+
+static void
+get_ipp_version (const char *ipp_version_string,
+                 guchar     *ipp_version_major,
+                 guchar     *ipp_version_minor)
+{
+  gchar **ipp_version_strv;
+  gchar  *endptr;
+
+  *ipp_version_major = 1;
+  *ipp_version_minor = 1;
+
+  if (ipp_version_string)
+    {
+      ipp_version_strv = g_strsplit (ipp_version_string, ".", 0);
+
+      if (ipp_version_strv)
+        {
+          if (g_strv_length (ipp_version_strv) == 2)
+            {
+              *ipp_version_major = (guchar) g_ascii_strtoull (ipp_version_strv[0], &endptr, 10);
+              if (endptr == ipp_version_strv[0])
+                *ipp_version_major = 1;
+
+              *ipp_version_minor = (guchar) g_ascii_strtoull (ipp_version_strv[1], &endptr, 10);
+              if (endptr == ipp_version_strv[1])
+                *ipp_version_minor = 1;
+            }
+
+          g_strfreev (ipp_version_strv);
+        }
+    }
+}
+
+static void
+get_server_ipp_version (guchar *ipp_version_major,
+                        guchar *ipp_version_minor)
+{
+  *ipp_version_major = 1;
+  *ipp_version_minor = 1;
+
+  if (IPP_VERSION && strlen (IPP_VERSION) == 2)
+    {
+      *ipp_version_major = (unsigned char) IPP_VERSION[0];
+      *ipp_version_minor = (unsigned char) IPP_VERSION[1];
+    }
+}
+
+static gint
+ipp_version_cmp (guchar ipp_version_major1,
+                 guchar ipp_version_minor1,
+                 guchar ipp_version_major2,
+                 guchar ipp_version_minor2)
+{
+  if (ipp_version_major1 == ipp_version_major2 &&
+      ipp_version_minor1 == ipp_version_minor2)
+    {
+      return 0;
+    }
+  else if (ipp_version_major1 < ipp_version_major2 ||
+           (ipp_version_major1 == ipp_version_major2 &&
+            ipp_version_minor1 < ipp_version_minor2))
+    {
+      return -1;
+    }
+  else
+    {
+      return 1;
+    }
+}
+
+static void
+cups_printer_handle_attribute (GtkPrintBackendCups *cups_backend,
+                              ipp_attribute_t *attr,
+                              PrinterSetupInfo *info)
+{
+  gint i, j;
+  if (strcmp (ippGetName (attr), "printer-name") == 0 &&
+      ippGetValueTag (attr) == IPP_TAG_NAME)
+    info->printer_name = ippGetString (attr, 0, NULL);
+  else if (strcmp (ippGetName (attr), "printer-uri-supported") == 0 &&
+          ippGetValueTag (attr) == IPP_TAG_URI)
+    info->printer_uri = ippGetString (attr, 0, NULL);
+  else if (strcmp (ippGetName (attr), "member-uris") == 0 &&
+          ippGetValueTag (attr) == IPP_TAG_URI)
+    info->member_uris = ippGetString (attr, 0, NULL);
+  else if (strcmp (ippGetName (attr), "printer-location") == 0)
+    info->location = ippGetString (attr, 0, NULL);
+  else if (strcmp (ippGetName (attr), "printer-info") == 0)
+    info->description = ippGetString (attr, 0, NULL);
+  else if (strcmp (ippGetName (attr), "printer-state-message") == 0)
+    info->state_msg = g_strdup (ippGetString (attr, 0, NULL));
+  else if (strcmp (ippGetName (attr), "printer-state-reasons") == 0)
+    /* Store most important reason to reason_msg and set
+       its importance at printer_state_reason_level */
+    {
+      for (i = 0; i < ippGetCount (attr); i++)
+       {
+         if (strcmp (ippGetString (attr, i, NULL), "none") != 0)
+           {
+             gboolean interested_in = FALSE;
+             /* Sets is_paused flag for paused printer. */
+             if (strcmp (ippGetString (attr, i, NULL), "paused") == 0)
+               {
+                 info->is_paused = TRUE;
+               }
+
+             for (j = 0; j < G_N_ELEMENTS (printer_messages); j++)
+               if (strncmp (ippGetString (attr, i, NULL), printer_messages[j],
+                            strlen (printer_messages[j])) == 0)
+                 {
+                   interested_in = TRUE;
+                   break;
+                 }
+
+             if (interested_in)
+               {
+                 if (g_str_has_suffix (ippGetString (attr, i, NULL), "-report"))
+                   {
+                     if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_INFO)
+                       {
+                         info->reason_msg = ippGetString (attr, i, NULL);
+                         info->reason_level = GTK_PRINTER_STATE_LEVEL_INFO;
+                       }
+                   }
+                 else if (g_str_has_suffix (ippGetString (attr, i, NULL), "-warning"))
+                   {
+                     if (info->reason_level <= GTK_PRINTER_STATE_LEVEL_WARNING)
+                       {
+                         info->reason_msg = ippGetString (attr, i, NULL);
+                         info->reason_level = GTK_PRINTER_STATE_LEVEL_WARNING;
+                       }
+                   }
+                 else  /* It is error in the case of no suffix. */
+                   {
+                     info->reason_msg = ippGetString (attr, i, NULL);
+                     info->reason_level = GTK_PRINTER_STATE_LEVEL_ERROR;
+                   }
+               }
+           }
+       }
+    }
+  else if (strcmp (ippGetName (attr), "printer-state") == 0)
+    info->state = ippGetInteger (attr, 0);
+  else if (strcmp (ippGetName (attr), "queued-job-count") == 0)
+    info->job_count = ippGetInteger (attr, 0);
+  else if (strcmp (ippGetName (attr), "printer-is-accepting-jobs") == 0)
+    {
+      if (ippGetBoolean (attr, 0) == 1)
+       info->is_accepting_jobs = TRUE;
+      else
+       info->is_accepting_jobs = FALSE;
+    }
+  else if (strcmp (ippGetName (attr), "job-sheets-supported") == 0)
+    {
+      info->number_of_covers = ippGetCount (attr);
+      info->covers = g_new (char *, info->number_of_covers + 1);
+      for (i = 0; i < info->number_of_covers; i++)
+        info->covers[i] = g_strdup (ippGetString (attr, i, NULL));
+      info->covers[info->number_of_covers] = NULL;
+    }
+  else if (strcmp (ippGetName (attr), "job-sheets-default") == 0)
+    {
+      if (ippGetCount (attr) == 2)
+       {
+         info->default_cover_before = ippGetString (attr, 0, NULL);
+         info->default_cover_after = ippGetString (attr, 1, NULL);
+       }
+    }
+  else if (strcmp (ippGetName (attr), "printer-type") == 0)
+    {
+      info->got_printer_type = TRUE;
+      if (ippGetInteger (attr, 0) & 0x00020000)
+       info->default_printer = TRUE;
+      else
+       info->default_printer = FALSE;
+
+      if (ippGetInteger (attr, 0) & 0x00000002)
+       info->remote_printer = TRUE;
+      else
+       info->remote_printer = FALSE;
+    }
+  else if (strcmp (ippGetName (attr), "auth-info-required") == 0)
+    {
+      if (strcmp (ippGetString (attr, 0, NULL), "none") != 0)
+       {
+         info->auth_info_required = g_new0 (gchar *, ippGetCount (attr) + 1);
+         for (i = 0; i < ippGetCount (attr); i++)
+           info->auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL));
+       }
+    }
+  else if (strcmp (ippGetName (attr), "number-up-default") == 0)
+    {
+      info->default_number_up = ippGetInteger (attr, 0);
+    }
+  else if (g_strcmp0 (ippGetName (attr), "ipp-versions-supported") == 0)
+    {
+      guchar server_ipp_version_major;
+      guchar server_ipp_version_minor;
+      guchar ipp_version_major;
+      guchar ipp_version_minor;
+
+      get_server_ipp_version (&server_ipp_version_major,
+                              &server_ipp_version_minor);
+
+      for (i = 0; i < ippGetCount (attr); i++)
+        {
+          get_ipp_version (ippGetString (attr, i, NULL),
+                           &ipp_version_major,
+                           &ipp_version_minor);
+
+          if (ipp_version_cmp (ipp_version_major,
+                               ipp_version_minor,
+                               info->ipp_version_major,
+                               info->ipp_version_minor) > 0 &&
+              ipp_version_cmp (ipp_version_major,
+                               ipp_version_minor,
+                               server_ipp_version_major,
+                               server_ipp_version_minor) <= 0)
+            {
+              info->ipp_version_major = ipp_version_major;
+              info->ipp_version_minor = ipp_version_minor;
+            }
+        }
+    }
+  else if (g_strcmp0 (ippGetName (attr), "number-up-supported") == 0)
+    {
+      if (ippGetCount (attr) == 6)
+        {
+          info->supports_number_up = TRUE;
+        }
+    }
+  else if (g_strcmp0 (ippGetName (attr), "copies-supported") == 0)
+    {
+      int upper = 1;
+
+      ippGetRange (attr, 0, &upper);
+      if (upper > 1)
+        {
+          info->supports_copies = TRUE;
+        }
+    }
+  else if (g_strcmp0 (ippGetName (attr), "multiple-document-handling-supported") == 0)
+    {
+      for (i = 0; i < ippGetCount (attr); i++)
+        {
+          if (g_strcmp0 (ippGetString (attr, i, NULL), "separate-documents-collated-copies") == 0)
+            {
+              info->supports_collate = TRUE;
+            }
+        }
+    }
+  else if (g_strcmp0 (ippGetName (attr), "sides-default") == 0)
+    {
+      info->sides_default = g_strdup (ippGetString (attr, 0, NULL));
+    }
+  else if (g_strcmp0 (ippGetName (attr), "sides-supported") == 0)
+    {
+      for (i = 0; i < ippGetCount (attr); i++)
+        info->sides_supported = g_list_prepend (info->sides_supported, g_strdup (ippGetString (attr, i, NULL)));
+
+      info->sides_supported = g_list_reverse (info->sides_supported);
+    }
+  else if (g_strcmp0 (ippGetName (attr), "media-default") == 0)
+    {
+      if (ippGetValueTag (attr) == IPP_TAG_KEYWORD ||
+          ippGetValueTag (attr) == IPP_TAG_NAME)
+        info->media_default = g_strdup (ippGetString (attr, 0, NULL));
+    }
+  else if (g_strcmp0 (ippGetName (attr), "media-col-default") == 0)
+    {
+      ipp_attribute_t *iter;
+      ipp_t           *col;
+      gint             num_of_margins = 0;
+
+      for (i = 0; i < ippGetCount (attr); i++)
+        {
+          col = ippGetCollection (attr, i);
+          for (iter = ippFirstAttribute (col); iter != NULL; iter = ippNextAttribute (col))
+            {
+              switch (ippGetValueTag (iter))
+                {
+                  case IPP_TAG_INTEGER:
+                    if (g_strcmp0 (ippGetName (iter), "media-bottom-margin") == 0)
+                      {
+                        info->media_bottom_margin_default = ippGetInteger (iter, 0) / 100.0;
+                        num_of_margins++;
+                      }
+                    else if (g_strcmp0 (ippGetName (iter), "media-top-margin") == 0)
+                      {
+                        info->media_top_margin_default = ippGetInteger (iter, 0) / 100.0;
+                        num_of_margins++;
+                      }
+                    else if (g_strcmp0 (ippGetName (iter), "media-left-margin") == 0)
+                      {
+                        info->media_left_margin_default = ippGetInteger (iter, 0) / 100.0;
+                        num_of_margins++;
+                      }
+                    else if (g_strcmp0 (ippGetName (iter), "media-right-margin") == 0)
+                      {
+                        info->media_right_margin_default = ippGetInteger (iter, 0) / 100.0;
+                        num_of_margins++;
+                      }
+                    break;
+
+                  default:
+                    break;
+                }
+            }
+        }
+
+      if (num_of_margins == 4)
+        info->media_margin_default_set = TRUE;
+    }
+  else if (g_strcmp0 (ippGetName (attr), "media-supported") == 0)
+    {
+      for (i = 0; i < ippGetCount (attr); i++)
+        info->media_supported = g_list_prepend (info->media_supported, g_strdup (ippGetString (attr, i, NULL)));
+
+      info->media_supported = g_list_reverse (info->media_supported);
+    }
+  else if (g_strcmp0 (ippGetName (attr), "media-size-supported") == 0)
+    {
+      ipp_attribute_t *iter;
+      MediaSize       *media_size;
+      gboolean         number_of_dimensions;
+      ipp_t           *media_size_collection;
+
+      for (i = 0; i < ippGetCount (attr); i++)
+        {
+          media_size_collection = ippGetCollection (attr, i);
+          media_size = g_new0 (MediaSize, 1);
+          number_of_dimensions = 0;
+
+          for (iter = ippFirstAttribute (media_size_collection);
+               iter != NULL;
+               iter = ippNextAttribute (media_size_collection))
+            {
+              if (g_strcmp0 (ippGetName (iter), "x-dimension") == 0 &&
+                  ippGetValueTag (iter) == IPP_TAG_INTEGER)
+                {
+                  media_size->x_dimension = ippGetInteger (iter, 0) / 100.0;
+                  number_of_dimensions++;
+                }
+              else if (g_strcmp0 (ippGetName (iter), "y-dimension") == 0 &&
+                  ippGetValueTag (iter) == IPP_TAG_INTEGER)
+                {
+                  media_size->y_dimension = ippGetInteger (iter, 0) / 100.0;
+                  number_of_dimensions++;
+                }
+            }
+
+          if (number_of_dimensions == 2)
+            info->media_size_supported = g_list_prepend (info->media_size_supported, media_size);
+          else
+            g_free (media_size);
+        }
+
+      info->media_size_supported = g_list_reverse (info->media_size_supported);
+    }
+  else if (g_strcmp0 (ippGetName (attr), "output-bin-default") == 0)
+    {
+      info->output_bin_default = g_strdup (ippGetString (attr, 0, NULL));
+    }
+  else if (g_strcmp0 (ippGetName (attr), "output-bin-supported") == 0)
+    {
+      for (i = 0; i < ippGetCount (attr); i++)
+        info->output_bin_supported = g_list_prepend (info->output_bin_supported, g_strdup (ippGetString (attr, i, NULL)));
+
+      info->output_bin_supported = g_list_reverse (info->output_bin_supported);
+    }
+  else if (g_strcmp0 (ippGetName (attr), "device-uri") == 0)
+    {
+      info->original_device_uri = g_strdup (ippGetString (attr, 0, NULL));
+    }
+  else
+    {
+      GTK_NOTE (PRINTING,
+               g_print ("CUPS Backend: Attribute %s ignored\n", ippGetName (attr)));
+    }
+}
+
+static GtkPrinter*
+cups_create_printer (GtkPrintBackendCups *cups_backend,
+                    PrinterSetupInfo *info)
+{
+  GtkPrinterCups *cups_printer;
+  GtkPrinter *printer;
+  GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
+  char uri[HTTP_MAX_URI];      /* Printer URI */
+  char method[HTTP_MAX_URI];   /* Method/scheme name */
+  char username[HTTP_MAX_URI]; /* Username:password */
+  char hostname[HTTP_MAX_URI]; /* Hostname */
+  char resource[HTTP_MAX_URI]; /* Resource name */
+  int  port;                   /* Port number */
+  char *cups_server;            /* CUPS server */
+
+#ifdef HAVE_COLORD
+#ifdef HAVE_CUPS_API_1_6
+  if (info->avahi_printer)
+    cups_printer = gtk_printer_cups_new (info->printer_name,
+                                        backend,
+                                        NULL);
+  else
+#endif
+    cups_printer = gtk_printer_cups_new (info->printer_name,
+                                        backend,
+                                        cups_backend->colord_client);
+#else
+  cups_printer = gtk_printer_cups_new (info->printer_name, backend, NULL);
+#endif
+
+  cups_printer->device_uri = g_strdup_printf ("/printers/%s",
+                                             info->printer_name);
+
+  /* Check to see if we are looking at a class */
+  if (info->member_uris)
+    {
+      cups_printer->printer_uri = g_strdup (info->member_uris);
+      /* TODO if member_uris is a class we need to recursivly find a printer */
+      GTK_NOTE (PRINTING,
+               g_print ("CUPS Backend: Found class with printer %s\n",
+                        info->member_uris));
+    }
+  else
+    {
+      cups_printer->printer_uri = g_strdup (info->printer_uri);
+      GTK_NOTE (PRINTING,
+               g_print ("CUPS Backend: Found printer %s\n",
+                        info->printer_uri));
+    }
+
+  httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri,
+                  method, sizeof (method),
+                  username, sizeof (username),
+                  hostname, sizeof (hostname),
+                  &port,
+                  resource, sizeof (resource));
+
+  if (strncmp (resource, "/printers/", 10) == 0)
+    {
+      cups_printer->ppd_name = g_strdup (resource + 10);
+      GTK_NOTE (PRINTING,
+               g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, info->printer_name));
+    }
+
+  gethostname (uri, sizeof (uri));
+  cups_server = g_strdup (cupsServer());
+
+  if (strcasecmp (uri, hostname) == 0)
+    strcpy (hostname, "localhost");
+
+  /* if the cups server is local and listening at a unix domain socket
+   * then use the socket connection
+   */
+  if ((strstr (hostname, "localhost") != NULL) &&
+      (cups_server[0] == '/'))
+    strcpy (hostname, cups_server);
+
+  g_free (cups_server);
+
+  cups_printer->default_cover_before = g_strdup (info->default_cover_before);
+  cups_printer->default_cover_after = g_strdup (info->default_cover_after);
+  cups_printer->original_device_uri = g_strdup (info->original_device_uri);
+
+  if (info->default_number_up > 0)
+    cups_printer->default_number_up = info->default_number_up;
+
+  cups_printer->hostname = g_strdup (hostname);
+  cups_printer->port = port;
+
+  cups_printer->auth_info_required = g_strdupv (info->auth_info_required);
+  g_strfreev (info->auth_info_required);
+
+  printer = GTK_PRINTER (cups_printer);
+
+  if (cups_backend->default_printer != NULL &&
+      strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
+    gtk_printer_set_is_default (printer, TRUE);
+
+#ifdef HAVE_CUPS_API_1_6
+  cups_printer->avahi_browsed = info->avahi_printer;
+#endif
+
+  gtk_print_backend_add_printer (backend, printer);
+  return printer;
+}
+
+static void
+set_printer_icon_name_from_info (GtkPrinter       *printer,
+                                 PrinterSetupInfo *info)
+{
+  /* Set printer icon according to importance
+     (none, report, warning, error - report is omitted). */
+  if (info->reason_level == GTK_PRINTER_STATE_LEVEL_ERROR)
+    gtk_printer_set_icon_name (printer, "printer-error");
+  else if (info->reason_level == GTK_PRINTER_STATE_LEVEL_WARNING)
+    gtk_printer_set_icon_name (printer, "printer-warning");
+  else if (gtk_printer_is_paused (printer))
+    gtk_printer_set_icon_name (printer, "printer-paused");
+  else
+    gtk_printer_set_icon_name (printer, "printer");
+}
+
+static gchar *
+get_reason_msg_desc (guint i,
+                     const gchar *printer_name)
+{
+  gchar *reason_msg_desc;
+
+  /* The numbers must match the indices in the printer_messages array */
+  switch (i)
+    {
+      case 0:
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on toner."),
+                                           printer_name);
+        break;
+      case 1:
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” has no toner left."),
+                                           printer_name);
+        break;
+      case 2:
+        /* Translators: "Developer" like on photo development context */
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on developer."),
+                                           printer_name);
+        break;
+      case 3:
+        /* Translators: "Developer" like on photo development context */
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of developer."),
+                                           printer_name);
+        break;
+      case 4:
+        /* Translators: "marker" is one color bin of the printer */
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on at least one marker supply."),
+                                           printer_name);
+        break;
+      case 5:
+        /* Translators: "marker" is one color bin of the printer */
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of at least one marker supply."),
+                                           printer_name);
+        break;
+      case 6:
+        reason_msg_desc = g_strdup_printf (_("The cover is open on printer “%s”."),
+                                           printer_name);
+        break;
+      case 7:
+        reason_msg_desc = g_strdup_printf (_("The door is open on printer “%s”."),
+                                           printer_name);
+        break;
+      case 8:
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is low on paper."),
+                                           printer_name);
+        break;
+      case 9:
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is out of paper."),
+                                           printer_name);
+        break;
+      case 10:
+        reason_msg_desc = g_strdup_printf (_("Printer “%s” is currently offline."),
+                                           printer_name);
+        break;
+      case 11:
+        reason_msg_desc = g_strdup_printf (_("There is a problem on printer “%s”."),
+                                           printer_name);
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+
+  return reason_msg_desc;
+}
+
+static void
+set_info_state_message (PrinterSetupInfo *info)
+{
+  gint i;
+
+  if (info->state_msg == NULL || strlen (info->state_msg) == 0)
+    {
+      gchar *tmp_msg2 = NULL;
+      if (info->is_paused && !info->is_accepting_jobs)
+        /* Translators: this is a printer status. */
+        tmp_msg2 = g_strdup ( _("Paused; Rejecting Jobs"));
+      if (info->is_paused && info->is_accepting_jobs)
+        /* Translators: this is a printer status. */
+        tmp_msg2 = g_strdup ( _("Paused"));
+      if (!info->is_paused && !info->is_accepting_jobs)
+        /* Translators: this is a printer status. */
+        tmp_msg2 = g_strdup ( _("Rejecting Jobs"));
+
+      if (tmp_msg2 != NULL)
+        {
+          g_free (info->state_msg);
+          info->state_msg = tmp_msg2;
+        }
+    }
+
+  /* Set description of the reason and combine it with printer-state-message. */
+  if (info->reason_msg)
+    {
+      gchar *reason_msg_desc = NULL;
+      gboolean found = FALSE;
+
+      for (i = 0; i < G_N_ELEMENTS (printer_messages); i++)
+        {
+          if (strncmp (info->reason_msg, printer_messages[i],
+                       strlen (printer_messages[i])) == 0)
+            {
+              reason_msg_desc = get_reason_msg_desc (i, info->printer_name);
+              found = TRUE;
+              break;
+            }
+        }
+
+      if (!found)
+        info->reason_level = GTK_PRINTER_STATE_LEVEL_NONE;
+
+      if (info->reason_level >= GTK_PRINTER_STATE_LEVEL_WARNING)
+        {
+          if (info->state_msg == NULL || info->state_msg[0] == '\0')
+            {
+              g_free (info->state_msg);
+              info->state_msg = reason_msg_desc;
+              reason_msg_desc = NULL;
+            }
+          else
+            {
+              gchar *tmp_msg = NULL;
+              /* Translators: this string connects multiple printer states together. */
+              tmp_msg = g_strjoin ( _("; "), info->state_msg,
+                                   reason_msg_desc, NULL);
+              g_free (info->state_msg);
+              info->state_msg = tmp_msg;
+            }
+        }
+
+      g_free (reason_msg_desc);
+    }
+}
+
+static void
+set_default_printer (GtkPrintBackendCups *cups_backend,
+                     const gchar         *default_printer_name)
+{
+  cups_backend->default_printer = g_strdup (default_printer_name);
+  cups_backend->got_default_printer = TRUE;
+
+  if (cups_backend->default_printer != NULL)
+    {
+      GtkPrinter *default_printer = NULL;
+      default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
+                                                        cups_backend->default_printer);
+      if (default_printer != NULL)
+        {
+          gtk_printer_set_is_default (default_printer, TRUE);
+          g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
+                                 "printer-status-changed", default_printer);
+        }
+    }
+}
+
+#ifdef HAVE_CUPS_API_1_6
+static void
+cups_request_printer_info_cb (GtkPrintBackendCups *cups_backend,
+                              GtkCupsResult       *result,
+                              gpointer             user_data)
+{
+  PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
+  GtkPrintBackend  *backend = GTK_PRINT_BACKEND (cups_backend);
+  ipp_attribute_t  *attr;
+  GtkPrinter       *printer;
+  gboolean          status_changed = FALSE;
+  ipp_t            *response;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  if (gtk_cups_result_is_error (result))
+    {
+      GTK_NOTE (PRINTING,
+                g_warning ("CUPS Backend: Error getting printer info: %s %d %d",
+                           gtk_cups_result_get_error_string (result),
+                           gtk_cups_result_get_error_type (result),
+                           gtk_cups_result_get_error_code (result)));
+
+      goto done;
+    }
+
+  response = gtk_cups_result_get_response (result);
+  attr = ippFirstAttribute (response);
+  while (attr && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
+    attr = ippNextAttribute (response);
+
+  if (attr)
+    {
+      while (attr && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
+        {
+          cups_printer_handle_attribute (cups_backend, attr, info);
+          attr = ippNextAttribute (response);
+        }
+
+      if (info->printer_name && info->printer_uri)
+        {
+          set_info_state_message (info);
+
+          printer = gtk_print_backend_find_printer (backend, info->printer_name);
+          if (printer != NULL)
+            g_object_ref (printer);
+          else
+            goto done;
+
+          if (info->got_printer_type &&
+              info->default_printer &&
+              cups_backend->avahi_default_printer == NULL)
+            cups_backend->avahi_default_printer = g_strdup (info->printer_name);
+
+          gtk_printer_set_is_paused (printer, info->is_paused);
+          gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
+
+          GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
+          GTK_PRINTER_CUPS (printer)->state = info->state;
+          GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
+          GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
+          GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
+          GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
+          GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
+          GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
+          GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
+          status_changed = gtk_printer_set_job_count (printer, info->job_count);
+          status_changed |= gtk_printer_set_location (printer, info->location);
+          status_changed |= gtk_printer_set_description (printer, info->description);
+          status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
+          status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
+
+          set_printer_icon_name_from_info (printer, info);
+
+          GTK_PRINTER_CUPS (printer)->media_default = info->media_default;
+          GTK_PRINTER_CUPS (printer)->media_supported = info->media_supported;
+          GTK_PRINTER_CUPS (printer)->media_size_supported = info->media_size_supported;
+          if (info->media_margin_default_set)
+            {
+              GTK_PRINTER_CUPS (printer)->media_margin_default_set = TRUE;
+              GTK_PRINTER_CUPS (printer)->media_bottom_margin_default = info->media_bottom_margin_default;
+              GTK_PRINTER_CUPS (printer)->media_top_margin_default = info->media_top_margin_default;
+              GTK_PRINTER_CUPS (printer)->media_left_margin_default = info->media_left_margin_default;
+              GTK_PRINTER_CUPS (printer)->media_right_margin_default = info->media_right_margin_default;
+            }
+          GTK_PRINTER_CUPS (printer)->sides_default = info->sides_default;
+          GTK_PRINTER_CUPS (printer)->sides_supported = info->sides_supported;
+          GTK_PRINTER_CUPS (printer)->output_bin_default = info->output_bin_default;
+          GTK_PRINTER_CUPS (printer)->output_bin_supported = info->output_bin_supported;
+
+          gtk_printer_set_has_details (printer, TRUE);
+          g_signal_emit_by_name (printer, "details-acquired", TRUE);
+
+          if (status_changed)
+            g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
+                                   "printer-status-changed", printer);
+
+          /* The ref is held by GtkPrintBackend, in add_printer() */
+          g_object_unref (printer);
+        }
+    }
+
+done:
+  if (!cups_backend->got_default_printer &&
+      gtk_print_backend_printer_list_is_done (backend) &&
+      cups_backend->avahi_default_printer != NULL)
+    {
+      set_default_printer (cups_backend, cups_backend->avahi_default_printer);
+    }
+
+  printer_setup_info_free (info);
+}
+
+static void
+cups_request_printer_info (const gchar         *printer_uri,
+                           const gchar         *host,
+                           gint                 port,
+                           GtkPrintBackendCups *backend)
+{
+  GtkCupsRequest *request;
+  http_t         *http;
+
+  http = httpConnect (host, port);
+  if (http)
+    {
+      request = gtk_cups_request_new_with_username (http,
+                                                    GTK_CUPS_POST,
+                                                    IPP_GET_PRINTER_ATTRIBUTES,
+                                                    NULL,
+                                                    NULL,
+                                                    NULL,
+                                                    backend->username);
+
+      gtk_cups_request_set_ipp_version (request, 1, 1);
+
+      gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                                       "printer-uri", NULL, printer_uri);
+
+      gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                                        "requested-attributes", G_N_ELEMENTS (printer_attrs_detailed),
+                                        NULL, printer_attrs_detailed);
+
+      cups_request_execute (backend,
+                            request,
+                            (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
+                            http,
+                            (GDestroyNotify) httpClose);
+    }
+}
+
+typedef struct
+{
+  gchar               *printer_uri;
+  gchar               *location;
+  gchar               *host;
+  gint                 port;
+  gchar               *printer_name;
+  gchar               *name;
+  gboolean             got_printer_type;
+  guint                printer_type;
+  gboolean             got_printer_state;
+  guint                printer_state;
+  gchar               *type;
+  gchar               *domain;
+  gchar               *UUID;
+  GtkPrintBackendCups *backend;
+} AvahiConnectionTestData;
+
+static GtkPrinter *
+find_printer_by_uuid (GtkPrintBackendCups *backend,
+                      const gchar         *UUID)
+{
+  GtkPrinterCups *printer;
+  GtkPrinter     *result = NULL;
+  GList          *printers;
+  GList          *iter;
+  gchar          *printer_uuid;
+
+  printers = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
+  for (iter = printers; iter != NULL; iter = iter->next)
+    {
+      printer = GTK_PRINTER_CUPS (iter->data);
+      if (printer->original_device_uri != NULL)
+        {
+          printer_uuid = g_strrstr (printer->original_device_uri, "uuid=");
+          if (printer_uuid != NULL && strlen (printer_uuid) >= 41)
+            {
+              printer_uuid += 5;
+              printer_uuid = g_strndup (printer_uuid, 36);
+
+#if GLIB_CHECK_VERSION(2, 52, 0)
+              if (g_uuid_string_is_valid (printer_uuid))
+#endif
+                {
+                  if (g_strcmp0 (printer_uuid, UUID) == 0)
+                    {
+                      result = GTK_PRINTER (printer);
+                      g_free (printer_uuid);
+                      break;
+                    }
+                }
+
+              g_free (printer_uuid);
+            }
+        }
+    }
+
+  g_list_free (printers);
+
+  return result;
+}
+
+/*
+ *  Create new GtkPrinter from informations included in TXT records.
+ */
+static void
+create_cups_printer_from_avahi_data (AvahiConnectionTestData *data)
+{
+  PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
+  GtkPrinter       *printer;
+
+  info->avahi_printer = TRUE;
+  info->printer_name = data->printer_name;
+  info->printer_uri = data->printer_uri;
+
+  if (data->got_printer_state)
+    {
+      info->state = data->printer_state;
+      info->is_paused = info->state == IPP_PRINTER_STOPPED;
+    }
+
+  info->got_printer_type = data->got_printer_type;
+  if (data->got_printer_type)
+    {
+      if (data->printer_type & CUPS_PRINTER_DEFAULT)
+        info->default_printer = TRUE;
+      else
+        info->default_printer = FALSE;
+
+      if (data->printer_type & CUPS_PRINTER_REMOTE)
+        info->remote_printer = TRUE;
+      else
+        info->remote_printer = FALSE;
+
+      if (data->printer_type & CUPS_PRINTER_REJECTING)
+        info->is_accepting_jobs = FALSE;
+      else
+        info->is_accepting_jobs = TRUE;
+
+      if (info->default_printer &&
+          data->backend->avahi_default_printer == NULL)
+        data->backend->avahi_default_printer = g_strdup (info->printer_name);
+    }
+
+  set_info_state_message (info);
+
+  printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (data->backend), data->printer_name);
+
+  if (printer == NULL && data->UUID != NULL)
+    printer = find_printer_by_uuid (data->backend, data->UUID);
+
+  if (printer == NULL)
+    {
+      printer = cups_create_printer (data->backend, info);
+
+      if (data->got_printer_type)
+        {
+          gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
+          GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
+
+          if (info->default_printer &&
+              data->backend->avahi_default_printer == NULL)
+            data->backend->avahi_default_printer = g_strdup (info->printer_name);
+        }
+
+      if (data->got_printer_state)
+        GTK_PRINTER_CUPS (printer)->state = info->state;
+
+      GTK_PRINTER_CUPS (printer)->avahi_name = g_strdup (data->name);
+      GTK_PRINTER_CUPS (printer)->avahi_type = g_strdup (data->type);
+      GTK_PRINTER_CUPS (printer)->avahi_domain = g_strdup (data->domain);
+      g_free (GTK_PRINTER_CUPS (printer)->hostname);
+      GTK_PRINTER_CUPS (printer)->hostname = g_strdup (data->host);
+      GTK_PRINTER_CUPS (printer)->port = data->port;
+      gtk_printer_set_location (printer, data->location);
+      gtk_printer_set_state_message (printer, info->state_msg);
+
+      set_printer_icon_name_from_info (printer, info);
+
+      if (!gtk_printer_is_active (printer))
+        gtk_printer_set_is_active (printer, TRUE);
+
+      g_signal_emit_by_name (data->backend, "printer-added", printer);
+      gtk_printer_set_is_new (printer, FALSE);
+      g_signal_emit_by_name (data->backend, "printer-list-changed");
+
+      if (!data->backend->got_default_printer &&
+          gtk_print_backend_printer_list_is_done (GTK_PRINT_BACKEND (data->backend)) &&
+          data->backend->avahi_default_printer != NULL)
+        set_default_printer (data->backend, data->backend->avahi_default_printer);
+
+      /* The ref is held by GtkPrintBackend, in add_printer() */
+      g_object_unref (printer);
+    }
+
+  printer_setup_info_free (info);
+}
+
+static void
+avahi_connection_test_cb (GObject      *source_object,
+                          GAsyncResult *res,
+                          gpointer      user_data)
+{
+  AvahiConnectionTestData *data = (AvahiConnectionTestData *) user_data;
+  GSocketConnection       *connection;
+
+  connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object),
+                                                       res,
+                                                       NULL);
+  g_object_unref (source_object);
+
+  if (connection != NULL)
+    {
+      g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
+      g_object_unref (connection);
+
+      create_cups_printer_from_avahi_data (data);
+    }
+
+  g_free (data->printer_uri);
+  g_free (data->location);
+  g_free (data->host);
+  g_free (data->printer_name);
+  g_free (data->name);
+  g_free (data->type);
+  g_free (data->domain);
+  g_free (data);
+}
+
+gboolean
+avahi_txt_get_key_value_pair (const gchar  *entry,
+                              gchar       **key,
+                              gchar       **value)
+{
+  const gchar *equal_sign;
+
+  *key = NULL;
+  *value = NULL;
+
+  if (entry != NULL)
+    {
+      /* See RFC 6763 section 6.3 */
+      equal_sign = strstr (entry, "=");
+
+      if (equal_sign != NULL)
+        {
+          *key = g_strndup (entry, equal_sign - entry);
+          *value = g_strdup (equal_sign + 1);
+
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+avahi_service_resolver_cb (GObject      *source_object,
+                           GAsyncResult *res,
+                           gpointer      user_data)
+{
+  AvahiConnectionTestData *data;
+  GtkPrintBackendCups     *backend;
+  const gchar             *name;
+  const gchar             *host;
+  const gchar             *type;
+  const gchar             *domain;
+  const gchar             *address;
+  const gchar             *protocol_string;
+  GVariant                *output;
+  GVariant                *txt;
+  GVariant                *child;
+  guint32                  flags;
+  guint16                  port;
+  GError                  *error = NULL;
+  gchar                   *queue_name = NULL;
+  gchar                   *tmp;
+  gchar                   *printer_name;
+  gchar                   *endptr;
+  gchar                   *key;
+  gchar                   *value;
+  gsize                    length;
+  gint                     interface;
+  gint                     protocol;
+  gint                     aprotocol;
+  gint                     i;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output)
+    {
+      backend = GTK_PRINT_BACKEND_CUPS (user_data);
+
+      g_variant_get (output, "(ii&s&s&s&si&sq@aayu)",
+                     &interface,
+                     &protocol,
+                     &name,
+                     &type,
+                     &domain,
+                     &host,
+                     &aprotocol,
+                     &address,
+                     &port,
+                     &txt,
+                     &flags);
+
+      data = g_new0 (AvahiConnectionTestData, 1);
+
+      for (i = 0; i < g_variant_n_children (txt); i++)
+        {
+          child = g_variant_get_child_value (txt, i);
+
+          length = g_variant_get_size (child);
+          if (length > 0)
+            {
+              tmp = g_strndup (g_variant_get_data (child), length);
+              g_variant_unref (child);
+
+              if (!avahi_txt_get_key_value_pair (tmp, &key, &value))
+                {
+                  g_free (tmp);
+                  continue;
+                }
+
+              if (g_strcmp0 (key, "rp") == 0)
+                {
+                  queue_name = g_strdup (value);
+
+                  printer_name = g_strrstr (queue_name, "/");
+                  if (printer_name != NULL)
+                    data->printer_name = g_strdup (printer_name + 1);
+                  else
+                    data->printer_name = g_strdup (queue_name);
+                }
+              else if (g_strcmp0 (key, "note") == 0)
+                {
+                  data->location = g_strdup (value);
+                }
+              else if (g_strcmp0 (key, "printer-type") == 0)
+                {
+                  endptr = NULL;
+                  data->printer_type = g_ascii_strtoull (value, &endptr, 16);
+                  if (data->printer_type != 0 || endptr != value)
+                    data->got_printer_type = TRUE;
+                }
+              else if (g_strcmp0 (key, "printer-state") == 0)
+                {
+                  endptr = NULL;
+                  data->printer_state = g_ascii_strtoull (value, &endptr, 10);
+                  if (data->printer_state != 0 || endptr != value)
+                    data->got_printer_state = TRUE;
+                }
+              else if (g_strcmp0 (key, "UUID") == 0)
+                {
+                  if (*value != '\0')
+                    data->UUID = g_strdup (value);
+                }
+
+              g_clear_pointer (&key, g_free);
+              g_clear_pointer (&value, g_free);
+              g_free (tmp);
+            }
+          else
+            {
+              g_variant_unref (child);
+            }
+        }
+
+      if (queue_name)
+        {
+          if (g_strcmp0 (type, "_ipp._tcp") == 0)
+            protocol_string = "ipp";
+          else
+            protocol_string = "ipps";
+
+          if (aprotocol == AVAHI_PROTO_INET6)
+            data->printer_uri = g_strdup_printf ("%s://[%s]:%u/%s", protocol_string, address, port, queue_name);
+          else
+            data->printer_uri = g_strdup_printf ("%s://%s:%u/%s", protocol_string, address, port, queue_name);
+
+          data->host = g_strdup (address);
+          data->port = port;
+
+          data->name = g_strdup (name);
+          data->type = g_strdup (type);
+          data->domain = g_strdup (domain);
+          data->backend = backend;
+
+          /* It can happen that the address is not reachable */
+          g_socket_client_connect_to_host_async (g_socket_client_new (),
+                                                 address,
+                                                 port,
+                                                 backend->avahi_cancellable,
+                                                 avahi_connection_test_cb,
+                                                 data);
+          g_free (queue_name);
+        }
+      else
+        {
+          g_free (data->printer_name);
+          g_free (data->location);
+          g_free (data);
+        }
+
+      g_variant_unref (txt);
+      g_variant_unref (output);
+    }
+  else
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("%s", error->message);
+      g_error_free (error);
+    }
+}
+
+static void
+avahi_service_browser_signal_handler (GDBusConnection *connection,
+                                      const gchar     *sender_name,
+                                      const gchar     *object_path,
+                                      const gchar     *interface_name,
+                                      const gchar     *signal_name,
+                                      GVariant        *parameters,
+                                      gpointer         user_data)
+{
+  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+  gchar               *name;
+  gchar               *type;
+  gchar               *domain;
+  guint                flags;
+  gint                 interface;
+  gint                 protocol;
+
+  if (g_strcmp0 (signal_name, "ItemNew") == 0)
+    {
+      g_variant_get (parameters, "(ii&s&s&su)",
+                     &interface,
+                     &protocol,
+                     &name,
+                     &type,
+                     &domain,
+                     &flags);
+
+      if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
+          g_strcmp0 (type, "_ipps._tcp") == 0)
+        {
+          g_dbus_connection_call (backend->dbus_connection,
+                                  AVAHI_BUS,
+                                  "/",
+                                  AVAHI_SERVER_IFACE,
+                                  "ResolveService",
+                                  g_variant_new ("(iisssiu)",
+                                                 interface,
+                                                 protocol,
+                                                 name,
+                                                 type,
+                                                 domain,
+                                                 AVAHI_PROTO_UNSPEC,
+                                                 0),
+                                  G_VARIANT_TYPE ("(iissssisqaayu)"),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  -1,
+                                  backend->avahi_cancellable,
+                                  avahi_service_resolver_cb,
+                                  user_data);
+        }
+    }
+  else if (g_strcmp0 (signal_name, "ItemRemove") == 0)
+    {
+      g_variant_get (parameters, "(ii&s&s&su)",
+                     &interface,
+                     &protocol,
+                     &name,
+                     &type,
+                     &domain,
+                     &flags);
+
+      if (g_strcmp0 (type, "_ipp._tcp") == 0 ||
+          g_strcmp0 (type, "_ipps._tcp") == 0)
+        {
+          GtkPrinterCups *printer;
+          GList          *list;
+          GList          *iter;
+
+          list = gtk_print_backend_get_printer_list (GTK_PRINT_BACKEND (backend));
+          for (iter = list; iter; iter = iter->next)
+            {
+              printer = GTK_PRINTER_CUPS (iter->data);
+              if (g_strcmp0 (printer->avahi_name, name) == 0 &&
+                  g_strcmp0 (printer->avahi_type, type) == 0 &&
+                  g_strcmp0 (printer->avahi_domain, domain) == 0)
+                {
+                  if (g_strcmp0 (gtk_printer_get_name (GTK_PRINTER (printer)),
+                                 backend->avahi_default_printer) == 0)
+                    g_clear_pointer (&backend->avahi_default_printer, g_free);
+
+                  g_signal_emit_by_name (backend, "printer-removed", printer);
+                  gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
+                                                    GTK_PRINTER (printer));
+                  g_signal_emit_by_name (backend, "printer-list-changed");
+                  break;
+                }
+            }
+
+          g_list_free (list);
+        }
+    }
+}
+
+static void
+avahi_service_browser_new_cb (GObject      *source_object,
+                              GAsyncResult *res,
+                              gpointer      user_data)
+{
+  GtkPrintBackendCups *cups_backend;
+  GVariant            *output;
+  GError              *error = NULL;
+  gint                 i;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  if (output)
+    {
+      cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
+      i = cups_backend->avahi_service_browser_paths[0] ? 1 : 0;
+
+      g_variant_get (output, "(o)", &cups_backend->avahi_service_browser_paths[i]);
+
+      cups_backend->avahi_service_browser_subscription_ids[i] =
+        g_dbus_connection_signal_subscribe (cups_backend->dbus_connection,
+                                            NULL,
+                                            AVAHI_SERVICE_BROWSER_IFACE,
+                                            NULL,
+                                            cups_backend->avahi_service_browser_paths[i],
+                                            NULL,
+                                            G_DBUS_SIGNAL_FLAGS_NONE,
+                                            avahi_service_browser_signal_handler,
+                                            user_data,
+                                            NULL);
+
+      /*
+       * The general subscription for all service browsers is not needed
+       * now because we are already subscribed to service browsers
+       * specific to _ipp._tcp and _ipps._tcp services.
+       */
+      if (cups_backend->avahi_service_browser_paths[0] &&
+          cups_backend->avahi_service_browser_paths[1] &&
+          cups_backend->avahi_service_browser_subscription_id > 0)
+        {
+          g_dbus_connection_signal_unsubscribe (cups_backend->dbus_connection,
+                                                cups_backend->avahi_service_browser_subscription_id);
+          cups_backend->avahi_service_browser_subscription_id = 0;
+        }
+
+      g_variant_unref (output);
+    }
+  else
+    {
+      /*
+       * The creation of ServiceBrowser fails with G_IO_ERROR_DBUS_ERROR
+       * if Avahi is disabled.
+       */
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) &&
+          !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("%s", error->message);
+      g_error_free (error);
+    }
+}
+
+static void
+avahi_create_browsers (GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  GDBusConnection     *dbus_connection;
+  GtkPrintBackendCups *cups_backend;
+  GError              *error = NULL;
+
+  dbus_connection = g_bus_get_finish (res, &error);
+  if (!dbus_connection)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("Couldn't connect to D-Bus system bus, %s", error->message);
+
+      g_error_free (error);
+      return;
+    }
+
+  cups_backend = GTK_PRINT_BACKEND_CUPS (user_data);
+  cups_backend->dbus_connection = dbus_connection;
+
+  /*
+   * We need to subscribe to signals of service browser before
+   * we actually create it because it starts to emit them right
+   * after its creation.
+   */
+  cups_backend->avahi_service_browser_subscription_id =
+    g_dbus_connection_signal_subscribe  (cups_backend->dbus_connection,
+                                         NULL,
+                                         AVAHI_SERVICE_BROWSER_IFACE,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         G_DBUS_SIGNAL_FLAGS_NONE,
+                                         avahi_service_browser_signal_handler,
+                                         cups_backend,
+                                         NULL);
+
+  /*
+   * Create service browsers for _ipp._tcp and _ipps._tcp services.
+   */
+  g_dbus_connection_call (cups_backend->dbus_connection,
+                          AVAHI_BUS,
+                          "/",
+                          AVAHI_SERVER_IFACE,
+                          "ServiceBrowserNew",
+                          g_variant_new ("(iissu)",
+                                         AVAHI_IF_UNSPEC,
+                                         AVAHI_PROTO_UNSPEC,
+                                         "_ipp._tcp",
+                                         "",
+                                         0),
+                          G_VARIANT_TYPE ("(o)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          cups_backend->avahi_cancellable,
+                          avahi_service_browser_new_cb,
+                          cups_backend);
+
+  g_dbus_connection_call (cups_backend->dbus_connection,
+                          AVAHI_BUS,
+                          "/",
+                          AVAHI_SERVER_IFACE,
+                          "ServiceBrowserNew",
+                          g_variant_new ("(iissu)",
+                                         AVAHI_IF_UNSPEC,
+                                         AVAHI_PROTO_UNSPEC,
+                                         "_ipps._tcp",
+                                         "",
+                                         0),
+                          G_VARIANT_TYPE ("(o)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          cups_backend->avahi_cancellable,
+                          avahi_service_browser_new_cb,
+                          cups_backend);
+}
+
+static void
+avahi_request_printer_list (GtkPrintBackendCups *cups_backend)
+{
+  cups_backend->avahi_cancellable = g_cancellable_new ();
+  g_bus_get (G_BUS_TYPE_SYSTEM, cups_backend->avahi_cancellable, avahi_create_browsers, cups_backend);
+}
+#endif
+
+static void
+cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
+                              GtkCupsResult       *result,
+                              gpointer             user_data)
+{
+  GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
+  ipp_attribute_t *attr;
+  ipp_t *response;
+  gboolean list_has_changed;
+  GList *removed_printer_checklist;
+  gchar *remote_default_printer = NULL;
+  GList *iter;
+
+  list_has_changed = FALSE;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  cups_backend->list_printers_pending = FALSE;
+
+  if (gtk_cups_result_is_error (result))
+    {
+      GTK_NOTE (PRINTING,
+                g_warning ("CUPS Backend: Error getting printer list: %s %d %d",
+                           gtk_cups_result_get_error_string (result),
+                           gtk_cups_result_get_error_type (result),
+                           gtk_cups_result_get_error_code (result)));
+
+      if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
+          gtk_cups_result_get_error_code (result) == 1)
+        {
+          /* Canceled by user, stop popping up more password dialogs */
+          if (cups_backend->list_printers_poll > 0)
+            g_source_remove (cups_backend->list_printers_poll);
+          cups_backend->list_printers_poll = 0;
+          cups_backend->list_printers_attempts = 0;
+        }
+
+      goto done;
+    }
+
+  /* Gather the names of the printers in the current queue
+   * so we may check to see if they were removed
+   */
+  removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
+
+  response = gtk_cups_result_get_response (result);
+#ifdef HAVE_CUPS_API_1_6
+  for (attr = ippFirstAttribute (response); attr != NULL;
+       attr = ippNextAttribute (response))
+    {
+      GtkPrinter *printer;
+      gboolean status_changed = FALSE;
+      GList *node;
+      PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
+
+      /* Skip leading attributes until we hit a printer...
+       */
+      while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
+        attr = ippNextAttribute (response);
+
+      if (attr == NULL)
+        break;
+      while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
+      {
+       cups_printer_handle_attribute (cups_backend, attr, info);
+        attr = ippNextAttribute (response);
+      }
+#else
+  for (attr = response->attrs; attr != NULL; attr = attr->next)
+    {
+      GtkPrinter *printer;
+      gboolean status_changed = FALSE;
+      GList *node;
+      PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo);
+      info->default_number_up = 1;
+
+      /* Skip leading attributes until we hit a printer...
+       */
+      while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
+        attr = attr->next;
+
+      if (attr == NULL)
+        break;
+      while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
+      {
+       cups_printer_handle_attribute (cups_backend, attr, info);
+        attr = attr->next;
+      }
+#endif
+
+      if (info->printer_name == NULL ||
+         (info->printer_uri == NULL && info->member_uris == NULL))
+      {
+        if (attr == NULL)
+         break;
+       else
+          continue;
+      }
+
+      if (info->got_printer_type)
+        {
+          if (info->default_printer && !cups_backend->got_default_printer)
+            {
+              if (!info->remote_printer)
+                {
+                  cups_backend->got_default_printer = TRUE;
+                  cups_backend->default_printer = g_strdup (info->printer_name);
+                }
+              else
+                {
+                  if (remote_default_printer == NULL)
+                    remote_default_printer = g_strdup (info->printer_name);
+                }
+            }
+        }
+      else
+        {
+          if (!cups_backend->got_default_printer)
+            cups_get_default_printer (cups_backend);
+        }
+
+      /* remove name from checklist if it was found */
+      node = g_list_find_custom (removed_printer_checklist,
+                                info->printer_name,
+                                (GCompareFunc) find_printer);
+      removed_printer_checklist = g_list_delete_link (removed_printer_checklist,
+                                                     node);
+
+      printer = gtk_print_backend_find_printer (backend, info->printer_name);
+      if (!printer)
+       {
+         printer = cups_create_printer (cups_backend, info);
+         list_has_changed = TRUE;
+       }
+
+      else
+       g_object_ref (printer);
+
+      GTK_PRINTER_CUPS (printer)->remote = info->remote_printer;
+
+      gtk_printer_set_is_paused (printer, info->is_paused);
+      gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
+
+      if (!gtk_printer_is_active (printer))
+        {
+         gtk_printer_set_is_active (printer, TRUE);
+         gtk_printer_set_is_new (printer, TRUE);
+          list_has_changed = TRUE;
+        }
+
+      if (gtk_printer_is_new (printer))
+        {
+         g_signal_emit_by_name (backend, "printer-added", printer);
+
+         gtk_printer_set_is_new (printer, FALSE);
+        }
+
+      GTK_PRINTER_CUPS (printer)->state = info->state;
+      GTK_PRINTER_CUPS (printer)->ipp_version_major = info->ipp_version_major;
+      GTK_PRINTER_CUPS (printer)->ipp_version_minor = info->ipp_version_minor;
+      GTK_PRINTER_CUPS (printer)->supports_copies = info->supports_copies;
+      GTK_PRINTER_CUPS (printer)->supports_collate = info->supports_collate;
+      GTK_PRINTER_CUPS (printer)->supports_number_up = info->supports_number_up;
+      GTK_PRINTER_CUPS (printer)->number_of_covers = info->number_of_covers;
+      GTK_PRINTER_CUPS (printer)->covers = g_strdupv (info->covers);
+      status_changed = gtk_printer_set_job_count (printer, info->job_count);
+      status_changed |= gtk_printer_set_location (printer, info->location);
+      status_changed |= gtk_printer_set_description (printer,
+                                                    info->description);
+
+      set_info_state_message (info);
+
+      status_changed |= gtk_printer_set_state_message (printer, info->state_msg);
+      status_changed |= gtk_printer_set_is_accepting_jobs (printer, info->is_accepting_jobs);
+
+      set_printer_icon_name_from_info (printer, info);
+
+      if (status_changed)
+        g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
+                               "printer-status-changed", printer);
+
+      /* The ref is held by GtkPrintBackend, in add_printer() */
+      g_object_unref (printer);
+      printer_setup_info_free (info);
+
+      if (attr == NULL)
+        break;
+    }
+
+  /* look at the removed printers checklist and mark any printer
+     as inactive if it is in the list, emitting a printer_removed signal */
+  if (removed_printer_checklist != NULL)
+    {
+      for (iter = removed_printer_checklist; iter; iter = iter->next)
+        {
+#ifdef HAVE_CUPS_API_1_6
+          if (!GTK_PRINTER_CUPS (iter->data)->avahi_browsed)
+#endif
+            {
+              mark_printer_inactive (GTK_PRINTER (iter->data), backend);
+              list_has_changed = TRUE;
+            }
+        }
+
+      g_list_free (removed_printer_checklist);
+    }
+
+done:
+  if (list_has_changed)
+    g_signal_emit_by_name (backend, "printer-list-changed");
+
+  gtk_print_backend_set_list_done (backend);
+
+  if (!cups_backend->got_default_printer && remote_default_printer != NULL)
+    {
+      set_default_printer (cups_backend, remote_default_printer);
+      g_free (remote_default_printer);
+    }
+
+#ifdef HAVE_CUPS_API_1_6
+  if (!cups_backend->got_default_printer && cups_backend->avahi_default_printer != NULL)
+    {
+      set_default_printer (cups_backend, cups_backend->avahi_default_printer);
+    }
+#endif
+}
+
+static void
+update_backend_status (GtkPrintBackendCups    *cups_backend,
+                       GtkCupsConnectionState  state)
+{
+  switch (state)
+    {
+    case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
+      g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
+      break;
+    case GTK_CUPS_CONNECTION_AVAILABLE:
+      g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
+      break;
+    default: ;
+    }
+}
+
+static gboolean
+cups_request_printer_list (GtkPrintBackendCups *cups_backend)
+{
+  GtkCupsConnectionState state;
+  GtkCupsRequest *request;
+
+  if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
+    return TRUE;
+
+  state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
+  update_backend_status (cups_backend, state);
+
+  if (cups_backend->list_printers_attempts == 60)
+    {
+      cups_backend->list_printers_attempts = -1;
+      if (cups_backend->list_printers_poll > 0)
+        g_source_remove (cups_backend->list_printers_poll);
+      cups_backend->list_printers_poll = g_timeout_add (200, (GSourceFunc) cups_request_printer_list, cups_backend);
+      g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk+] cups_request_printer_list");
+    }
+  else if (cups_backend->list_printers_attempts != -1)
+    cups_backend->list_printers_attempts++;
+
+  if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
+    return TRUE;
+  else
+    if (cups_backend->list_printers_attempts > 0)
+      cups_backend->list_printers_attempts = 60;
+
+  cups_backend->list_printers_pending = TRUE;
+
+  request = gtk_cups_request_new_with_username (NULL,
+                                                GTK_CUPS_POST,
+                                                CUPS_GET_PRINTERS,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                cups_backend->username);
+
+  gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                                   "requested-attributes", G_N_ELEMENTS (printer_attrs),
+                                   NULL, printer_attrs);
+
+  cups_request_execute (cups_backend,
+                        request,
+                        (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
+                       request,
+                       NULL);
+
+  return TRUE;
+}
+
+static void
+cups_get_printer_list (GtkPrintBackend *backend)
+{
+  GtkPrintBackendCups *cups_backend;
+
+  cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
+
+  if (cups_backend->cups_connection_test == NULL)
+    cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
+
+  if (cups_backend->list_printers_poll == 0)
+    {
+      if (cups_request_printer_list (cups_backend))
+        {
+          cups_backend->list_printers_poll = g_timeout_add (50, (GSourceFunc) cups_request_printer_list, backend);
+          g_source_set_name_by_id (cups_backend->list_printers_poll, "[gtk+] cups_request_printer_list");
+        }
+
+#ifdef HAVE_CUPS_API_1_6
+      avahi_request_printer_list (cups_backend);
+#endif
+    }
+}
+
+typedef struct {
+  GtkPrinterCups *printer;
+  GIOChannel *ppd_io;
+  http_t *http;
+} GetPPDData;
+
+static void
+get_ppd_data_free (GetPPDData *data)
+{
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+  httpClose (data->http);
+  g_io_channel_unref (data->ppd_io);
+  g_object_unref (data->printer);
+  g_free (data);
+}
+
+static void
+cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
+                     GtkCupsResult       *result,
+                     GetPPDData          *data)
+{
+  GtkPrinter *printer;
+  struct stat data_info;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  printer = GTK_PRINTER (data->printer);
+  GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
+  print_backend->reading_ppds--;
+
+#ifndef HAVE_CUPS_API_1_6
+  if (gtk_cups_result_is_error (result))
+    {
+      gboolean success = FALSE;
+
+      /* If we get a 404 then it is just a raw printer without a ppd
+         and not an error. */
+      if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
+          (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
+        {
+          gtk_printer_set_has_details (printer, TRUE);
+          success = TRUE;
+        }
+
+      g_signal_emit_by_name (printer, "details-acquired", success);
+
+      return;
+    }
+#endif
+
+  if (!gtk_cups_result_is_error (result))
+    {
+      /* let ppdOpenFd take over the ownership of the open file */
+      g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
+      data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
+      ppdLocalize (data->printer->ppd_file);
+      ppdMarkDefaults (data->printer->ppd_file);
+    }
+
+#ifdef HAVE_CUPS_API_1_6
+  fstat (g_io_channel_unix_get_fd (data->ppd_io), &data_info);
+  /*
+   * Standalone Avahi printers and raw printers don't have PPD files or have
+   * empty PPD files. Try to get printer details via IPP.
+   * Always do this for Avahi printers.
+   */
+  if (data_info.st_size == 0 ||
+      GTK_PRINTER_CUPS (printer)->avahi_browsed ||
+      (gtk_cups_result_is_error (result) &&
+       ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
+         (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))))
+    {
+      cups_request_printer_info (GTK_PRINTER_CUPS (printer)->printer_uri,
+                                 GTK_PRINTER_CUPS (printer)->hostname,
+                                 GTK_PRINTER_CUPS (printer)->port,
+                                 GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer)));
+
+      return;
+    }
+#endif
+
+  gtk_printer_set_has_details (printer, TRUE);
+  g_signal_emit_by_name (printer, "details-acquired", TRUE);
+}
+
+static gboolean
+cups_request_ppd (GtkPrinter *printer)
+{
+  GError *error;
+  GtkPrintBackend *print_backend;
+  GtkPrinterCups *cups_printer;
+  GtkCupsRequest *request;
+  char *ppd_filename = NULL;
+  gchar *resource;
+  http_t *http;
+  GetPPDData *data;
+  int fd;
+
+  cups_printer = GTK_PRINTER_CUPS (printer);
+
+  error = NULL;
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: %s\n", G_STRFUNC));
+
+  if (cups_printer->remote
+#ifdef HAVE_CUPS_API_1_6
+      && !cups_printer->avahi_browsed
+#endif
+      )
+    {
+      GtkCupsConnectionState state;
+
+      state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
+
+      if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
+        {
+          if (cups_printer->get_remote_ppd_attempts == 60)
+            {
+              cups_printer->get_remote_ppd_attempts = -1;
+              if (cups_printer->get_remote_ppd_poll > 0)
+                g_source_remove (cups_printer->get_remote_ppd_poll);
+              cups_printer->get_remote_ppd_poll = g_timeout_add (200, (GSourceFunc) cups_request_ppd, printer);
+              g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk+] cups_request_ppd");
+            }
+          else if (cups_printer->get_remote_ppd_attempts != -1)
+            cups_printer->get_remote_ppd_attempts++;
+
+          return TRUE;
+        }
+
+      gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
+      cups_printer->remote_cups_connection_test = NULL;
+      cups_printer->get_remote_ppd_poll = 0;
+      cups_printer->get_remote_ppd_attempts = 0;
+
+      if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
+        {
+          g_signal_emit_by_name (printer, "details-acquired", FALSE);
+          return FALSE;
+        }
+    }
+
+  http = httpConnectEncrypt (cups_printer->hostname,
+                            cups_printer->port,
+                            cupsEncryption ());
+
+  data = g_new0 (GetPPDData, 1);
+
+  fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
+                        &ppd_filename,
+                        &error);
+
+#ifdef G_ENABLE_DEBUG
+  /* If we are debugging printing don't delete the tmp files */
+  if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
+    unlink (ppd_filename);
+#else
+  unlink (ppd_filename);
+#endif /* G_ENABLE_DEBUG */
+
+  if (error != NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_warning ("CUPS Backend: Failed to create temp file, %s\n",
+                           error->message));
+      g_error_free (error);
+      httpClose (http);
+      g_free (ppd_filename);
+      g_free (data);
+
+      g_signal_emit_by_name (printer, "details-acquired", FALSE);
+      return FALSE;
+    }
+
+  data->http = http;
+  fchmod (fd, S_IRUSR | S_IWUSR);
+  data->ppd_io = g_io_channel_unix_new (fd);
+  g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
+  g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
+
+  data->printer = (GtkPrinterCups *) g_object_ref (printer);
+
+  resource = g_strdup_printf ("/printers/%s.ppd",
+                              gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
+
+  print_backend = gtk_printer_get_backend (printer);
+
+  request = gtk_cups_request_new_with_username (data->http,
+                                                GTK_CUPS_GET,
+                                                0,
+                                                data->ppd_io,
+                                                cups_printer->hostname,
+                                                resource,
+                                                GTK_PRINT_BACKEND_CUPS (print_backend)->username);
+
+  gtk_cups_request_set_ipp_version (request,
+                                    cups_printer->ipp_version_major,
+                                    cups_printer->ipp_version_minor);
+
+  GTK_NOTE (PRINTING,
+            g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
+
+
+  cups_printer->reading_ppd = TRUE;
+  GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
+
+  cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
+                        request,
+                        (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
+                        data,
+                        (GDestroyNotify)get_ppd_data_free);
+
+  g_free (resource);
+  g_free (ppd_filename);
+
+  return FALSE;
+}
+
+/* Ordering matters for default preference */
+static const char *lpoptions_locations[] = {
+  "/etc/cups/lpoptions",
+  ".lpoptions",
+  ".cups/lpoptions"
+};
+
+static void
+cups_parse_user_default_printer (const char  *filename,
+                                 char       **printer_name)
+{
+  FILE *fp;
+  char line[1024], *lineptr, *defname = NULL;
+
+  if ((fp = g_fopen (filename, "r")) == NULL)
+    return;
+
+  while (fgets (line, sizeof (line), fp) != NULL)
+    {
+      if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
+        continue;
+
+      lineptr = line + 8;
+      while (isspace (*lineptr))
+        lineptr++;
+
+      if (!*lineptr)
+        continue;
+
+      defname = lineptr;
+      while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
+        lineptr++;
+
+      *lineptr = '\0';
+
+      g_free (*printer_name);
+
+      *printer_name = g_strdup (defname);
+    }
+
+  fclose (fp);
+}
+
+static void
+cups_get_user_default_printer (char **printer_name)
+{
+  int i;
+
+  for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
+    {
+      if (g_path_is_absolute (lpoptions_locations[i]))
+        {
+          cups_parse_user_default_printer (lpoptions_locations[i],
+                                           printer_name);
+        }
+      else
+        {
+          char *filename;
+
+          filename = g_build_filename (g_get_home_dir (),
+                                       lpoptions_locations[i], NULL);
+          cups_parse_user_default_printer (filename, printer_name);
+          g_free (filename);
+        }
+    }
+}
+
+static int
+cups_parse_user_options (const char     *filename,
+                         const char     *printer_name,
+                         int             num_options,
+                         cups_option_t **options)
+{
+  FILE *fp;
+  gchar line[1024], *lineptr, *name;
+
+  if ((fp = g_fopen (filename, "r")) == NULL)
+    return num_options;
+
+  while (fgets (line, sizeof (line), fp) != NULL)
+    {
+      if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
+        lineptr = line + 4;
+      else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
+        lineptr = line + 7;
+      else
+        continue;
+
+      /* Skip leading whitespace */
+      while (isspace (*lineptr))
+        lineptr++;
+
+      if (!*lineptr)
+        continue;
+
+      name = lineptr;
+      while (!isspace (*lineptr) && *lineptr)
+        {
+          lineptr++;
+        }
+
+      if (!*lineptr)
+        continue;
+
+      *lineptr++ = '\0';
+
+      if (strcasecmp (name, printer_name) != 0)
+          continue;
+
+      /* We found our printer, parse the options */
+      num_options = cupsParseOptions (lineptr, num_options, options);
+    }
+
+  fclose (fp);
+
+  return num_options;
+}
+
+static int
+cups_get_user_options (const char     *printer_name,
+                       int             num_options,
+                       cups_option_t **options)
+{
+  int i;
+
+  for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
+    {
+      if (g_path_is_absolute (lpoptions_locations[i]))
+        {
+           num_options = cups_parse_user_options (lpoptions_locations[i],
+                                                  printer_name,
+                                                  num_options,
+                                                  options);
+        }
+      else
+        {
+          char *filename;
+
+          filename = g_build_filename (g_get_home_dir (),
+                                       lpoptions_locations[i], NULL);
+          num_options = cups_parse_user_options (filename, printer_name,
+                                                 num_options, options);
+          g_free (filename);
+        }
+    }
+
+  return num_options;
+}
+
+/* This function requests default printer from a CUPS server in regular intervals.
+ * In the case of unreachable CUPS server the request is repeated later.
+ * The default printer is not requested in the case of previous success.
+ */
+static void
+cups_get_default_printer (GtkPrintBackendCups *backend)
+{
+  GtkPrintBackendCups *cups_backend;
+
+  cups_backend = backend;
+
+  if (cups_backend->cups_connection_test == NULL)
+    cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL, -1);
+
+  if (cups_backend->default_printer_poll == 0)
+    {
+      if (cups_request_default_printer (cups_backend))
+        {
+          cups_backend->default_printer_poll = g_timeout_add (200, (GSourceFunc) cups_request_default_printer, backend);
+          g_source_set_name_by_id (cups_backend->default_printer_poll, "[gtk+] cups_request_default_printer");
+        }
+    }
+}
+
+/* This function gets default printer from local settings.*/
+static void
+cups_get_local_default_printer (GtkPrintBackendCups *backend)
+{
+  const char *str;
+  char *name = NULL;
+
+  if ((str = g_getenv ("LPDEST")) != NULL)
+    {
+      backend->default_printer = g_strdup (str);
+      backend->got_default_printer = TRUE;
+      return;
+    }
+  else if ((str = g_getenv ("PRINTER")) != NULL &&
+          strcmp (str, "lp") != 0)
+    {
+      backend->default_printer = g_strdup (str);
+      backend->got_default_printer = TRUE;
+      return;
+    }
+
+  /* Figure out user setting for default printer */
+  cups_get_user_default_printer (&name);
+  if (name != NULL)
+    {
+      backend->default_printer = name;
+      backend->got_default_printer = TRUE;
+      return;
+    }
+}
+
+static void
+cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
+                                GtkCupsResult       *result,
+                                gpointer             user_data)
+{
+  ipp_t *response;
+  ipp_attribute_t *attr;
+  GtkPrinter *printer;
+
+  if (gtk_cups_result_is_error (result))
+    {
+      if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
+          gtk_cups_result_get_error_code (result) == 1)
+        {
+          /* Canceled by user, stop popping up more password dialogs */
+          if (print_backend->list_printers_poll > 0)
+            g_source_remove (print_backend->list_printers_poll);
+          print_backend->list_printers_poll = 0;
+        }
+
+      return;
+    }
+
+  response = gtk_cups_result_get_response (result);
+
+  if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
+      print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
+
+  print_backend->got_default_printer = TRUE;
+
+  if (print_backend->default_printer != NULL)
+    {
+      printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
+      if (printer != NULL)
+        {
+          gtk_printer_set_is_default (printer, TRUE);
+          g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
+        }
+    }
+
+  /* Make sure to kick off get_printers if we are polling it,
+   * as we could have blocked this reading the default printer
+   */
+  if (print_backend->list_printers_poll != 0)
+    cups_request_printer_list (print_backend);
+}
+
+static gboolean
+cups_request_default_printer (GtkPrintBackendCups *print_backend)
+{
+  GtkCupsConnectionState state;
+  GtkCupsRequest *request;
+
+  state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
+  update_backend_status (print_backend, state);
+
+  if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
+    return TRUE;
+
+  request = gtk_cups_request_new_with_username (NULL,
+                                                GTK_CUPS_POST,
+                                                CUPS_GET_DEFAULT,
+                                                NULL,
+                                                NULL,
+                                                NULL,
+                                                print_backend->username);
+
+  cups_request_execute (print_backend,
+                        request,
+                        (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
+                       g_object_ref (print_backend),
+                       g_object_unref);
+
+  return FALSE;
+}
+
+static void
+cups_printer_request_details (GtkPrinter *printer)
+{
+  GtkPrinterCups *cups_printer;
+
+  cups_printer = GTK_PRINTER_CUPS (printer);
+  if (!cups_printer->reading_ppd &&
+      gtk_printer_cups_get_ppd (cups_printer) == NULL)
+    {
+      if (cups_printer->remote
+#ifdef HAVE_CUPS_API_1_6
+          && !cups_printer->avahi_browsed
+#endif
+          )
+        {
+          if (cups_printer->get_remote_ppd_poll == 0)
+            {
+              cups_printer->remote_cups_connection_test =
+                gtk_cups_connection_test_new (cups_printer->hostname,
+                                              cups_printer->port);
+
+              if (cups_request_ppd (printer))
+                {
+                  cups_printer->get_remote_ppd_poll = g_timeout_add (50, (GSourceFunc) cups_request_ppd, printer);
+                  g_source_set_name_by_id (cups_printer->get_remote_ppd_poll, "[gtk+] cups_request_ppd");
+                }
+            }
+        }
+      else
+        cups_request_ppd (printer);
+    }
+}
+
+static char *
+ppd_text_to_utf8 (ppd_file_t *ppd_file,
+                 const char *text)
+{
+  const char *encoding = NULL;
+  char *res;
+
+  if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
+    {
+      return g_strdup (text);
+    }
+  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
+    {
+      encoding = "ISO-8859-1";
+    }
+  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
+    {
+      encoding = "ISO-8859-2";
+    }
+  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
+    {
+      encoding = "ISO-8859-5";
+    }
+  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
+    {
+      encoding = "SHIFT-JIS";
+    }
+  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
+    {
+      encoding = "MACINTOSH";
+    }
+  else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
+    {
+      encoding = "WINDOWS-1252";
+    }
+  else
+    {
+      /* Fallback, try iso-8859-1... */
+      encoding = "ISO-8859-1";
+    }
+
+  res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
+
+  if (res == NULL)
+    {
+      GTK_NOTE (PRINTING,
+                g_warning ("CUPS Backend: Unable to convert PPD text\n"));
+      res = g_strdup ("???");
+    }
+
+  return res;
+}
+
+/* TODO: Add more translations for common settings here */
+
+static const struct {
+  const char *keyword;
+  const char *translation;
+} cups_option_translations[] = {
+  { "Duplex", NC_("printing option", "Two Sided") },
+  { "MediaType", NC_("printing option", "Paper Type") },
+  { "InputSlot", NC_("printing option", "Paper Source") },
+  { "OutputBin", NC_("printing option", "Output Tray") },
+  { "Resolution", NC_("printing option", "Resolution") },
+  { "PreFilter", NC_("printing option", "GhostScript pre-filtering") }
+};
+
+
+static const struct {
+  const char *keyword;
+  const char *choice;
+  const char *translation;
+} cups_choice_translations[] = {
+  { "Duplex", "None", NC_("printing option value", "One Sided") },
+  /* Translators: this is an option of "Two Sided" */
+  { "Duplex", "DuplexNoTumble", NC_("printing option value", "Long Edge (Standard)") },
+  /* Translators: this is an option of "Two Sided" */
+  { "Duplex", "DuplexTumble", NC_("printing option value", "Short Edge (Flip)") },
+  /* Translators: this is an option of "Paper Source" */
+  { "InputSlot", "Auto", NC_("printing option value", "Auto Select") },
+  /* Translators: this is an option of "Paper Source" */
+  { "InputSlot", "AutoSelect", NC_("printing option value", "Auto Select") },
+  /* Translators: this is an option of "Paper Source" */
+  { "InputSlot", "Default", NC_("printing option value", "Printer Default") },
+  /* Translators: this is an option of "Paper Source" */
+  { "InputSlot", "None", NC_("printing option value", "Printer Default") },
+  /* Translators: this is an option of "Paper Source" */
+  { "InputSlot", "PrinterDefault", NC_("printing option value", "Printer Default") },
+  /* Translators: this is an option of "Paper Source" */
+  { "InputSlot", "Unspecified", NC_("printing option value", "Auto Select") },
+  /* Translators: this is an option of "Resolution" */
+  { "Resolution", "default", NC_("printing option value", "Printer Default") },
+  /* Translators: this is an option of "GhostScript" */
+  { "PreFilter", "EmbedFonts", NC_("printing option value", "Embed GhostScript fonts only") },
+  /* Translators: this is an option of "GhostScript" */
+  { "PreFilter", "Level1", NC_("printing option value", "Convert to PS level 1") },
+  /* Translators: this is an option of "GhostScript" */
+  { "PreFilter", "Level2", NC_("printing option value", "Convert to PS level 2") },
+  /* Translators: this is an option of "GhostScript" */
+  { "PreFilter", "No", NC_("printing option value", "No pre-filtering") }
+};
+
+static const struct {
+  const char *name;
+  const char *translation;
+} cups_group_translations[] = {
+/* Translators: "Miscellaneous" is the label for a button, that opens
+   up an extra panel of settings in a print dialog. */
+  { "Miscellaneous", NC_("printing option group", "Miscellaneous") }
+};
+
+static const struct {
+  const char *ppd_keyword;
+  const char *name;
+} ppd_option_names[] = {
+  { "Duplex", "gtk-duplex" },
+  { "MediaType", "gtk-paper-type" },
+  { "InputSlot", "gtk-paper-source" },
+  { "OutputBin", "gtk-output-tray" }
+};
+
+static const struct {
+  const char *ipp_option_name;
+  const char *gtk_option_name;
+  const char *translation;
+} ipp_option_translations[] = {
+  { "sides", "gtk-duplex", NC_("printing option", "Two Sided") },
+  { "output-bin", "gtk-output-tray", NC_("printing option", "Output Tray") }
+};
+
+static const struct {
+  const char *ipp_option_name;
+  const char *ipp_choice;
+  const char *translation;
+} ipp_choice_translations[] = {
+  { "sides", "one-sided", NC_("sides", "One Sided") },
+  /* Translators: this is an option of "Two Sided" */
+  { "sides", "two-sided-long-edge", NC_("sides", "Long Edge (Standard)") },
+  /* Translators: this is an option of "Two Sided" */
+  { "sides", "two-sided-short-edge", NC_("sides", "Short Edge (Flip)") },
+
+  /* Translators: Top output bin */
+  { "output-bin", "top", NC_("output-bin", "Top Bin") },
+  /* Translators: Middle output bin */
+  { "output-bin", "middle", NC_("output-bin", "Middle Bin") },
+  /* Translators: Bottom output bin */
+  { "output-bin", "bottom", NC_("output-bin", "Bottom Bin") },
+  /* Translators: Side output bin */
+  { "output-bin", "side", NC_("output-bin", "Side Bin") },
+  /* Translators: Left output bin */
+  { "output-bin", "left", NC_("output-bin", "Left Bin") },
+  /* Translators: Right output bin */
+  { "output-bin", "right", NC_("output-bin", "Right Bin") },
+  /* Translators: Center output bin */
+  { "output-bin", "center", NC_("output-bin", "Center Bin") },
+  /* Translators: Rear output bin */
+  { "output-bin", "rear", NC_("output-bin", "Rear Bin") },
+  /* Translators: Output bin where one sided output is oriented in the face-up position */
+  { "output-bin", "face-up", NC_("output-bin", "Face Up Bin") },
+  /* Translators: Output bin where one sided output is oriented in the face-down position */
+  { "output-bin", "face-down", NC_("output-bin", "Face Down Bin") },
+  /* Translators: Large capacity output bin */
+  { "output-bin", "large-capacity", NC_("output-bin", "Large Capacity Bin") },
+  { NULL, NULL, NULL }
+};
+
+/*
+ * Handles "format not a string literal" error
+ * https://mail.gnome.org/archives/desktop-devel-list/2016-March/msg00075.html
+ */
+static gchar *
+get_ipp_choice_translation_string (gint  index,
+                                  guint i)
+{
+  gchar *translation;
+
+  if (i < G_N_ELEMENTS (ipp_choice_translations))
+    translation = g_strdup (_(ipp_choice_translations[i].translation));
+  else
+    {
+      switch (i)
+        {
+          case 14:
+            /* Translators: Output stacker number %d */
+            translation = g_strdup_printf (C_("output-bin", "Stacker %d"), index);
+            break;
+          case 15:
+            /* Translators: Output mailbox number %d */
+            translation = g_strdup_printf (C_("output-bin", "Mailbox %d"), index);
+            break;
+          case 16:
+            /* Translators: Private mailbox */
+            translation = g_strdup (C_("output-bin", "My Mailbox"));
+            break;
+          case 17:
+            /* Translators: Output tray number %d */
+            translation = g_strdup_printf (C_("output-bin", "Tray %d"), index);
+            break;
+          default:
+            g_assert_not_reached ();
+        }
+    }
+
+  return translation;
+}
+
+static const struct {
+  const char *lpoption;
+  const char *name;
+} lpoption_names[] = {
+  { "number-up", "gtk-n-up" },
+  { "number-up-layout", "gtk-n-up-layout" },
+  { "job-billing", "gtk-billing-info" },
+  { "job-priority", "gtk-job-prio" }
+};
+
+/* keep sorted when changing */
+static const char *color_option_whitelist[] = {
+  "BRColorEnhancement",
+  "BRColorMatching",
+  "BRColorMatching",
+  "BRColorMode",
+  "BRGammaValue",
+  "BRImprovedGray",
+  "BlackSubstitution",
+  "ColorModel",
+  "HPCMYKInks",
+  "HPCSGraphics",
+  "HPCSImages",
+  "HPCSText",
+  "HPColorSmart",
+  "RPSBlackMode",
+  "RPSBlackOverPrint",
+  "Rcmyksimulation",
+};
+
+/* keep sorted when changing */
+static const char *color_group_whitelist[] = {
+  "ColorPage",
+  "FPColorWise1",
+  "FPColorWise2",
+  "FPColorWise3",
+  "FPColorWise4",
+  "FPColorWise5",
+  "HPColorOptionsPanel",
+};
+
+/* keep sorted when changing */
+static const char *image_quality_option_whitelist[] = {
+  "BRDocument",
+  "BRHalfTonePattern",
+  "BRNormalPrt",
+  "BRPrintQuality",
+  "BitsPerPixel",
+  "Darkness",
+  "Dithering",
+  "EconoMode",
+  "Economode",
+  "HPEconoMode",
+  "HPEdgeControl",
+  "HPGraphicsHalftone",
+  "HPHalftone",
+  "HPLJDensity",
+  "HPPhotoHalftone",
+  "OutputMode",
+  "REt",
+  "RPSBitsPerPixel",
+  "RPSDitherType",
+  "Resolution",
+  "ScreenLock",
+  "Smoothing",
+  "TonerSaveMode",
+  "UCRGCRForImage",
+};
+
+/* keep sorted when changing */
+static const char *image_quality_group_whitelist[] = {
+  "FPImageQuality1",
+  "FPImageQuality2",
+  "FPImageQuality3",
+  "ImageQualityPage",
+};
+
+/* keep sorted when changing */
+static const char * finishing_option_whitelist[] = {
+  "BindColor",
+  "BindEdge",
+  "BindType",
+  "BindWhen",
+  "Booklet",
+  "FoldType",
+  "FoldWhen",
+  "HPStaplerOptions",
+  "Jog",
+  "Slipsheet",
+  "Sorter",
+  "StapleLocation",
+  "StapleOrientation",
+  "StapleWhen",
+  "StapleX",
+  "StapleY",
+};
+
+/* keep sorted when changing */
+static const char *finishing_group_whitelist[] = {
+  "FPFinishing1",
+  "FPFinishing2",
+  "FPFinishing3",
+  "FPFinishing4",
+  "FinishingPage",
+  "HPFinishingPanel",
+};
+
+/* keep sorted when changing */
+static const char *cups_option_blacklist[] = {
+  "Collate",
+  "Copies",
+  "OutputOrder",
+  "PageRegion",
+  "PageSize",
+};
+
+static char *
+get_option_text (ppd_file_t   *ppd_file,
+                ppd_option_t *option)
+{
+  int i;
+  char *utf8;
+
+  for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
+    {
+      if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
+        return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
+                                       "printing option",
+                                       cups_option_translations[i].translation));
+    }
+
+  utf8 = ppd_text_to_utf8 (ppd_file, option->text);
+
+  /* Some ppd files have spaces in the text before the colon */
+  g_strchomp (utf8);
+
+  return utf8;
+}
+
+static char *
+get_choice_text (ppd_file_t   *ppd_file,
+                ppd_choice_t *choice)
+{
+  int i;
+  ppd_option_t *option = choice->option;
+  const char *keyword = option->keyword;
+
+  for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
+    {
+      if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
+         strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
+        return g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
+                                       "printing option value",
+                                       cups_choice_translations[i].translation));
+    }
+  return ppd_text_to_utf8 (ppd_file, choice->text);
+}
+
+static gboolean
+group_has_option (ppd_group_t  *group,
+                 ppd_option_t *option)
+{
+  int i;
+
+  if (group == NULL)
+    return FALSE;
+
+  if (group->num_options > 0 &&
+      option >= group->options && option < group->options + group->num_options)
+    return TRUE;
+
+  for (i = 0; i < group->num_subgroups; i++)
+    {
+      if (group_has_option (&group->subgroups[i],option))
+       return TRUE;
+    }
+  return FALSE;
+}
+
+static void
+set_option_off (GtkPrinterOption *option)
+{
+  /* Any of these will do, _set only applies the value
+   * if its allowed of the option */
+  gtk_printer_option_set (option, "False");
+  gtk_printer_option_set (option, "Off");
+  gtk_printer_option_set (option, "None");
+}
+
+static gboolean
+value_is_off (const char *value)
+{
+  return  (strcasecmp (value, "None") == 0 ||
+          strcasecmp (value, "Off") == 0 ||
+          strcasecmp (value, "False") == 0);
+}
+
+static const char *
+ppd_group_name (ppd_group_t *group)
+{
+  return group->name;
+}
+
+static int
+available_choices (ppd_file_t     *ppd,
+                  ppd_option_t   *option,
+                  ppd_choice_t ***available,
+                  gboolean        keep_if_only_one_option)
+{
+  ppd_option_t *other_option;
+  int i, j;
+  gchar *conflicts;
+  ppd_const_t *constraint;
+  const char *choice, *other_choice;
+  ppd_option_t *option1, *option2;
+  ppd_group_t *installed_options;
+  int num_conflicts;
+  gboolean all_default;
+  int add_auto;
+
+  if (available)
+    *available = NULL;
+
+  conflicts = g_new0 (char, option->num_choices);
+
+  installed_options = NULL;
+  for (i = 0; i < ppd->num_groups; i++)
+    {
+      const char *name;
+
+      name = ppd_group_name (&ppd->groups[i]);
+      if (strcmp (name, "InstallableOptions") == 0)
+       {
+         installed_options = &ppd->groups[i];
+         break;
+       }
+    }
+
+  for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
+    {
+      option1 = ppdFindOption (ppd, constraint->option1);
+      if (option1 == NULL)
+       continue;
+
+      option2 = ppdFindOption (ppd, constraint->option2);
+      if (option2 == NULL)
+       continue;
+
+      if (option == option1)
+       {
+         choice = constraint->choice1;
+         other_option = option2;
+         other_choice = constraint->choice2;
+       }
+      else if (option == option2)
+       {
+         choice = constraint->choice2;
+         other_option = option1;
+         other_choice = constraint->choice1;
+       }
+      else
+       continue;
+
+      /* We only care of conflicts with installed_options and PageSize */
+      if (!group_has_option (installed_options, other_option) &&
+         (strcmp (other_option->keyword, "PageSize") != 0))
+       continue;
+
+      if (*other_choice == 0)
+       {
+         /* Conflict only if the installed option is not off */
+         if (value_is_off (other_option->defchoice))
+           continue;
+       }
+      /* Conflict if the installed option has the specified default */
+      else if (strcasecmp (other_choice, other_option->defchoice) != 0)
+       continue;
+
+      if (*choice == 0)
+       {
+         /* Conflict with all non-off choices */
+         for (j = 0; j < option->num_choices; j++)
+           {
+             if (!value_is_off (option->choices[j].choice))
+               conflicts[j] = 1;
+           }
+       }
+      else
+       {
+         for (j = 0; j < option->num_choices; j++)
+           {
+             if (strcasecmp (option->choices[j].choice, choice) == 0)
+               conflicts[j] = 1;
+           }
+       }
+    }
+
+  num_conflicts = 0;
+  all_default = TRUE;
+  for (j = 0; j < option->num_choices; j++)
+    {
+      if (conflicts[j])
+       num_conflicts++;
+      else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
+       all_default = FALSE;
+    }
+
+  if ((all_default && !keep_if_only_one_option) ||
+      (num_conflicts == option->num_choices))
+    {
+      g_free (conflicts);
+
+      return 0;
+    }
+
+  /* Some ppds don't have a "use printer default" option for
+   * InputSlot. This means you always have to select a particular slot,
+   * and you can't auto-pick source based on the paper size. To support
+   * this we always add an auto option if there isn't one already. If
+   * the user chooses the generated option we don't send any InputSlot
+   * value when printing. The way we detect existing auto-cases is based
+   * on feedback from Michael Sweet of cups fame.
+   */
+  add_auto = 0;
+  if (strcmp (option->keyword, "InputSlot") == 0)
+    {
+      gboolean found_auto = FALSE;
+      for (j = 0; j < option->num_choices; j++)
+       {
+         if (!conflicts[j])
+           {
+             if (strcmp (option->choices[j].choice, "Auto") == 0 ||
+                 strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
+                 strcmp (option->choices[j].choice, "Default") == 0 ||
+                 strcmp (option->choices[j].choice, "None") == 0 ||
+                 strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
+                 strcmp (option->choices[j].choice, "Unspecified") == 0 ||
+                 option->choices[j].code == NULL ||
+                 option->choices[j].code[0] == 0)
+               {
+                 found_auto = TRUE;
+                 break;
+               }
+           }
+       }
+
+      if (!found_auto)
+       add_auto = 1;
+    }
+
+  if (available)
+    {
+      *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
+
+      i = 0;
+      for (j = 0; j < option->num_choices; j++)
+       {
+         if (!conflicts[j])
+           (*available)[i++] = &option->choices[j];
+       }
+
+      if (add_auto)
+       (*available)[i++] = NULL;
+    }
+
+  g_free (conflicts);
+
+  return option->num_choices - num_conflicts + add_auto;
+}
+
+static GtkPrinterOption *
+create_pickone_option (ppd_file_t   *ppd_file,
+                      ppd_option_t *ppd_option,
+                      const gchar  *gtk_name)
+{
+  GtkPrinterOption *option;
+  ppd_choice_t **available;
+  char *label;
+  int n_choices;
+  int i;
+  ppd_coption_t *coption;
+
+  g_assert (ppd_option->ui == PPD_UI_PICKONE);
+
+  option = NULL;
+
+  n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
+  if (n_choices > 0)
+    {
+
+      /* right now only support one parameter per custom option
+       * if more than one print warning and only offer the default choices
+       */
+
+      label = get_option_text (ppd_file, ppd_option);
+
+      coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
+
+      if (coption)
+        {
+         ppd_cparam_t *cparam;
+
+          cparam = ppdFirstCustomParam (coption);
+
+          if (ppdNextCustomParam (coption) == NULL)
+           {
+              switch (cparam->type)
+               {
+                case PPD_CUSTOM_INT:
+                 option = gtk_printer_option_new (gtk_name, label,
+                                        GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
+                 break;
+                case PPD_CUSTOM_PASSCODE:
+                 option = gtk_printer_option_new (gtk_name, label,
+                                        GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
+                 break;
+                case PPD_CUSTOM_PASSWORD:
+                   option = gtk_printer_option_new (gtk_name, label,
+                                        GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
+                 break;
+               case PPD_CUSTOM_REAL:
+                   option = gtk_printer_option_new (gtk_name, label,
+                                        GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
+                 break;
+                case PPD_CUSTOM_STRING:
+                 option = gtk_printer_option_new (gtk_name, label,
+                                        GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
+                 break;
+#ifdef PRINT_IGNORED_OPTIONS
+                case PPD_CUSTOM_POINTS:
+                 g_warning ("CUPS Backend: PPD Custom Points Option not supported");
+                 break;
+                case PPD_CUSTOM_CURVE:
+                  g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
+                 break;
+                case PPD_CUSTOM_INVCURVE:
+                 g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
+                 break;
+#endif
+                default:
+                  break;
+               }
+           }
+#ifdef PRINT_IGNORED_OPTIONS
+         else
+           g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
+#endif
+       }
+
+      if (!option)
+        option = gtk_printer_option_new (gtk_name, label,
+                                        GTK_PRINTER_OPTION_TYPE_PICKONE);
+      g_free (label);
+
+      gtk_printer_option_allocate_choices (option, n_choices);
+      for (i = 0; i < n_choices; i++)
+       {
+         if (available[i] == NULL)
+           {
+             /* This was auto-added */
+             option->choices[i] = g_strdup ("gtk-ignore-value");
+             option->choices_display[i] = g_strdup (_("Printer Default"));
+           }
+         else
+           {
+             option->choices[i] = g_strdup (available[i]->choice);
+             option->choices_display[i] = get_choice_text (ppd_file, available[i]);
+           }
+       }
+
+      if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
+        {
+          if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
+            gtk_printer_option_set (option, ppd_option->defchoice + 7);
+          else
+            gtk_printer_option_set (option, ppd_option->defchoice);
+        }
+      else
+        {
+          gtk_printer_option_set (option, ppd_option->defchoice);
+        }
+    }
+#ifdef PRINT_IGNORED_OPTIONS
+  else
+    g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
+#endif
+  g_free (available);
+
+  return option;
+}
+
+static GtkPrinterOption *
+create_boolean_option (ppd_file_t   *ppd_file,
+                      ppd_option_t *ppd_option,
+                      const gchar  *gtk_name)
+{
+  GtkPrinterOption *option;
+  ppd_choice_t **available;
+  char *label;
+  int n_choices;
+
+  g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
+
+  option = NULL;
+
+  n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
+  if (n_choices == 2)
+    {
+      label = get_option_text (ppd_file, ppd_option);
+      option = gtk_printer_option_new (gtk_name, label,
+                                      GTK_PRINTER_OPTION_TYPE_BOOLEAN);
+      g_free (label);
+
+      gtk_printer_option_allocate_choices (option, 2);
+      option->choices[0] = g_strdup ("True");
+      option->choices_display[0] = g_strdup ("True");
+      option->choices[1] = g_strdup ("False");
+      option->choices_display[1] = g_strdup ("False");
+
+      gtk_printer_option_set (option, ppd_option->defchoice);
+    }
+#ifdef PRINT_IGNORED_OPTIONS
+  else
+    g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
+#endif
+  g_free (available);
+
+  return option;
+}
+
+static gchar *
+get_ppd_option_name (const gchar *keyword)
+{
+  int i;
+
+  for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
+    if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
+      return g_strdup (ppd_option_names[i].name);
+
+  return g_strdup_printf ("cups-%s", keyword);
+}
+
+static gchar *
+get_lpoption_name (const gchar *lpoption)
+{
+  int i;
+
+  for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
+    if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
+      return g_strdup (ppd_option_names[i].name);
+
+  for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
+    if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
+      return g_strdup (lpoption_names[i].name);
+
+  return g_strdup_printf ("cups-%s", lpoption);
+}
+
+static int
+strptr_cmp (const void *a,
+           const void *b)
+{
+  char **aa = (char **)a;
+  char **bb = (char **)b;
+  return strcmp (*aa, *bb);
+}
+
+
+static gboolean
+string_in_table (const gchar *str,
+                const gchar *table[],
+                gint         table_len)
+{
+  return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
+}
+
+#define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
+
+static void
+handle_option (GtkPrinterOptionSet *set,
+              ppd_file_t          *ppd_file,
+              ppd_option_t        *ppd_option,
+              ppd_group_t         *toplevel_group,
+              GtkPrintSettings    *settings)
+{
+  GtkPrinterOption *option;
+  char *option_name;
+  int i;
+
+  if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
+    return;
+
+  option_name = get_ppd_option_name (ppd_option->keyword);
+
+  option = NULL;
+  if (ppd_option->ui == PPD_UI_PICKONE)
+    option = create_pickone_option (ppd_file, ppd_option, option_name);
+  else if (ppd_option->ui == PPD_UI_BOOLEAN)
+    option = create_boolean_option (ppd_file, ppd_option, option_name);
+#ifdef PRINT_IGNORED_OPTIONS
+  else
+    g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
+#endif
+
+  if (option)
+    {
+      const char *name;
+
+      name = ppd_group_name (toplevel_group);
+      if (STRING_IN_TABLE (name, color_group_whitelist) ||
+         STRING_IN_TABLE (ppd_option->keyword, color_option_whitelist))
+       {
+         option->group = g_strdup ("ColorPage");
+       }
+      else if (STRING_IN_TABLE (name, image_quality_group_whitelist) ||
+              STRING_IN_TABLE (ppd_option->keyword, image_quality_option_whitelist))
+       {
+         option->group = g_strdup ("ImageQualityPage");
+       }
+      else if (STRING_IN_TABLE (name, finishing_group_whitelist) ||
+              STRING_IN_TABLE (ppd_option->keyword, finishing_option_whitelist))
+       {
+         option->group = g_strdup ("FinishingPage");
+       }
+      else
+       {
+         for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
+           {
+             if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
+               {
+                  option->group = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
+                                                          "printing option group",
+                                                          cups_group_translations[i].translation));
+                 break;
+               }
+           }
+
+         if (i == G_N_ELEMENTS (cups_group_translations))
+           option->group = g_strdup (toplevel_group->text);
+       }
+
+      set_option_from_settings (option, settings);
+
+      gtk_printer_option_set_add (set, option);
+    }
+
+  g_free (option_name);
+}
+
+static void
+handle_group (GtkPrinterOptionSet *set,
+             ppd_file_t          *ppd_file,
+             ppd_group_t         *group,
+             ppd_group_t         *toplevel_group,
+             GtkPrintSettings    *settings)
+{
+  gint i;
+  const gchar *name;
+
+  /* Ignore installable options */
+  name = ppd_group_name (toplevel_group);
+  if (strcmp (name, "InstallableOptions") == 0)
+    return;
+
+  for (i = 0; i < group->num_options; i++)
+    handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
+
+  for (i = 0; i < group->num_subgroups; i++)
+    handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
+
+}
+
+#ifdef HAVE_COLORD
+
+typedef struct {
+        GtkPrintSettings     *settings;
+        GtkPrinter           *printer;
+} GtkPrintBackendCupsColordHelper;
+
+static void
+colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
+                                      GtkPrintBackendCupsColordHelper *helper)
+{
+  gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
+                                    helper->settings,
+                                    set);
+}
+#endif
+
+/*
+ * Lookup translation and Gtk+ name of given IPP option name.
+ */
+static gboolean
+get_ipp_option_translation (const gchar  *ipp_option_name,
+                            gchar       **gtk_option_name,
+                            gchar       **translation)
+{
+  gint i;
+
+  *gtk_option_name = NULL;
+  *translation = NULL;
+
+  for (i = 0; i < G_N_ELEMENTS (ipp_option_translations); i++)
+    {
+      if (g_strcmp0 (ipp_option_translations[i].ipp_option_name, ipp_option_name) == 0)
+        {
+          *gtk_option_name = g_strdup (ipp_option_translations[i].gtk_option_name);
+          *translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
+                                                 "printing option",
+                                                 ipp_option_translations[i].translation));
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+/*
+ * Lookup translation of given IPP choice.
+ */
+static gchar *
+get_ipp_choice_translation (const gchar  *ipp_option_name,
+                            const gchar  *ipp_choice)
+{
+  const gchar *nptr;
+  guint64      index;
+  gchar       *translation = NULL;
+  gsize        ipp_choice_length;
+  gchar       *endptr;
+  gint         i;
+
+  for (i = 0; ipp_choice_translations[i].ipp_option_name != NULL; i++)
+    {
+      if (g_strcmp0 (ipp_choice_translations[i].ipp_option_name, ipp_option_name) == 0)
+        {
+          ipp_choice_length = strlen (ipp_choice_translations[i].ipp_choice);
+
+          if (g_strcmp0 (ipp_choice_translations[i].ipp_choice, ipp_choice) == 0)
+            {
+              translation = g_strdup (g_dpgettext2 (GETTEXT_PACKAGE,
+                                                    ipp_option_name,
+                                                    ipp_choice_translations[i].translation));
+              break;
+            }
+          else if (g_str_has_suffix (ipp_choice_translations[i].ipp_choice, "-N") &&
+                   g_ascii_strncasecmp (ipp_choice_translations[i].ipp_choice,
+                                        ipp_choice,
+                                        ipp_choice_length - 2) == 0)
+            {
+              /* Find out index of the ipp_choice if it is supported for the choice. */
+              endptr = NULL;
+              nptr = ipp_choice + ipp_choice_length - 1;
+              index = g_ascii_strtoull (nptr,
+                                        &endptr,
+                                        10);
+
+              if (index != 0 || endptr != nptr)
+                {
+                  translation = get_ipp_choice_translation_string (index, i);
+                  break;
+                }
+            }
+        }
+    }
+
+  return translation;
+}
+
+/*
+ * Format an IPP choice to a displayable string.
+ */
+static gchar *
+format_ipp_choice (const gchar *ipp_choice)
+{
+  gboolean  after_space = TRUE;
+  gchar    *result = NULL;
+  gsize     i;
+
+  if (ipp_choice != NULL)
+    {
+      result = g_strdup (ipp_choice);
+      /* Replace all '-' by spaces. */
+      result = g_strdelimit (result, "-", ' ');
+      if (g_str_is_ascii (result))
+        {
+          /* Convert all leading characters to upper case. */
+          for (i = 0; i < strlen (result); i++)
+            {
+              if (after_space && g_ascii_isalpha (result[i]))
+                result[i] = g_ascii_toupper (result[i]);
+
+              after_space = g_ascii_isspace (result[i]);
+            }
+        }
+    }
+
+  return result;
+}
+
+/*
+ * Look the IPP option up in given set of options.
+ * Create it if it doesn't exist and set its default value
+ * if available.
+ */
+static GtkPrinterOption *
+setup_ipp_option (gchar               *ipp_option_name,
+                  gchar               *ipp_choice_default,
+                  GList               *ipp_choices,
+                  GtkPrinterOptionSet *set)
+{
+  GtkPrinterOption *option = NULL;
+  gchar            *gtk_option_name = NULL;
+  gchar            *translation = NULL;
+  gchar            *ipp_choice;
+  gsize             i;
+
+  get_ipp_option_translation (ipp_option_name,
+                              &gtk_option_name,
+                              &translation);
+
+  /* Look the option up in the given set of options. */
+  if (gtk_option_name != NULL)
+    option = gtk_printer_option_set_lookup (set, gtk_option_name);
+
+  /* The option was not found, create it from given choices. */
+  if (option == NULL &&
+      ipp_choices != NULL)
+    {
+      GList  *iter;
+      gsize   length;
+      char  **choices = NULL;
+      char  **choices_display = NULL;
+
+      option = gtk_printer_option_new (gtk_option_name,
+                                       translation,
+                                       GTK_PRINTER_OPTION_TYPE_PICKONE);
+
+      length = g_list_length (ipp_choices);
+
+      choices = g_new0 (char *, length);
+      choices_display = g_new0 (char *, length);
+
+      i = 0;
+      for (iter = ipp_choices; iter != NULL; iter = iter->next)
+        {
+          ipp_choice = (gchar *) iter->data;
+
+          choices[i] = g_strdup (ipp_choice);
+
+          translation = get_ipp_choice_translation (ipp_option_name,
+                                                    ipp_choice);
+          if (translation != NULL)
+            choices_display[i] = translation;
+          else
+            choices_display[i] = format_ipp_choice (ipp_choice);
+
+          i++;
+        }
+
+      if (choices != NULL &&
+          choices_display != NULL)
+        {
+          gtk_printer_option_choices_from_array (option,
+                                                 length,
+                                                 choices,
+                                                 choices_display);
+        }
+
+      option_set_is_ipp_option (option, TRUE);
+
+      gtk_printer_option_set_add (set, option);
+
+      g_free (choices);
+      g_free (choices_display);
+    }
+
+  /* The option exists. Set its default value if available. */
+  if (option != NULL &&
+      ipp_choice_default != NULL)
+    {
+      gtk_printer_option_set (option, ipp_choice_default);
+    }
+
+  return option;
+}
+
+static GtkPrinterOptionSet *
+cups_printer_get_options (GtkPrinter           *printer,
+                         GtkPrintSettings     *settings,
+                         GtkPageSetup         *page_setup,
+                         GtkPrintCapabilities  capabilities)
+{
+  GtkPrinterOptionSet *set;
+  GtkPrinterOption *option;
+  ppd_file_t *ppd_file;
+  int i;
+  char *print_at[] = { "now", "at", "on-hold" };
+  char *n_up[] = {"1", "2", "4", "6", "9", "16" };
+  char *prio[] = {"100", "80", "50", "30" };
+  /* Translators: These strings name the possible values of the
+   * job priority option in the print dialog
+   */
+  char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
+  char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
+  /* Translators: These strings name the possible arrangements of
+   * multiple pages on a sheet when printing
+   */
+  char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"),
+                                  N_("Right to left, top to bottom"), N_("Right to left, bottom to top"),
+                                  N_("Top to bottom, left to right"), N_("Top to bottom, right to left"),
+                                  N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
+  char *name;
+  int num_opts;
+  cups_option_t *opts = NULL;
+  GtkPrintBackendCups *backend;
+  GtkTextDirection text_direction;
+  GtkPrinterCups *cups_printer = NULL;
+#ifdef HAVE_COLORD
+  GtkPrintBackendCupsColordHelper *helper;
+#endif
+  char *default_number_up;
+
+  set = gtk_printer_option_set_new ();
+
+  /* Cups specific, non-ppd related settings */
+
+  for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
+    prio_display[i] = _(prio_display[i]);
+
+  /* Translators, this string is used to label the job priority option
+   * in the print dialog
+   */
+  option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
+  gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
+                                        prio, prio_display);
+  gtk_printer_option_set (option, "50");
+  set_option_from_settings (option, settings);
+  gtk_printer_option_set_add (set, option);
+  g_object_unref (option);
+
+  /* Translators, this string is used to label the billing info entry
+   * in the print dialog
+   */
+  option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
+  gtk_printer_option_set (option, "");
+  set_option_from_settings (option, settings);
+  gtk_printer_option_set_add (set, option);
+  g_object_unref (option);
+
+  backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
+  cups_printer = GTK_PRINTER_CUPS (printer);
+
+  if (backend != NULL && printer != NULL)
+    {
+      char *cover_default[] = {
+        "none",
+        "classified",
+        "confidential",
+        "secret",
+        "standard",
+        "topsecret",
+        "unclassified"
+      };
+      char *cover_display_default[] = {
+        /* Translators, these strings are names for various 'standard' cover
+         * pages that the printing system may support.
+         */
+        NC_("cover page", "None"),
+        NC_("cover page", "Classified"),
+        NC_("cover page", "Confidential"),
+        NC_("cover page", "Secret"),
+        NC_("cover page", "Standard"),
+        NC_("cover page", "Top Secret"),
+        NC_("cover page", "Unclassified")
+      };
+      char **cover = NULL;
+      char **cover_display = NULL;
+      char **cover_display_translated = NULL;
+      gint num_of_covers = 0;
+      gpointer value;
+      gint j;
+
+       /* Translators, this string is used to label the pages-per-sheet option
+        * in the print dialog
+        */
+      option = gtk_printer_option_new ("gtk-n-up", C_("printer option", "Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
+      gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), n_up, n_up);
+      default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
+      gtk_printer_option_set (option, default_number_up);
+      g_free (default_number_up);
+      set_option_from_settings (option, settings);
+      gtk_printer_option_set_add (set, option);
+      g_object_unref (option);
+
+      if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
+        {
+          for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
+            n_up_layout_display[i] = _(n_up_layout_display[i]);
+
+           /* Translators, this string is used to label the option in the print
+            * dialog that controls in what order multiple pages are arranged
+            */
+          option = gtk_printer_option_new ("gtk-n-up-layout", C_("printer option", "Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
+          gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
+                                                 n_up_layout, n_up_layout_display);
+
+          text_direction = gtk_widget_get_default_direction ();
+          if (text_direction == GTK_TEXT_DIR_LTR)
+            gtk_printer_option_set (option, "lrtb");
+          else
+            gtk_printer_option_set (option, "rltb");
+
+          set_option_from_settings (option, settings);
+          gtk_printer_option_set_add (set, option);
+          g_object_unref (option);
+        }
+
+      num_of_covers = cups_printer->number_of_covers;
+      cover = g_new (char *, num_of_covers + 1);
+      cover[num_of_covers] = NULL;
+      cover_display = g_new (char *, num_of_covers + 1);
+      cover_display[num_of_covers] = NULL;
+      cover_display_translated = g_new (char *, num_of_covers + 1);
+      cover_display_translated[num_of_covers] = NULL;
+
+      for (i = 0; i < num_of_covers; i++)
+        {
+          cover[i] = g_strdup (cups_printer->covers[i]);
+          value = NULL;
+          for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
+            if (strcmp (cover_default[j], cover[i]) == 0)
+              {
+                value = cover_display_default[j];
+                break;
+              }
+          cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (cups_printer->covers[i]);
+        }
+
+      for (i = 0; i < num_of_covers; i++)
+        cover_display_translated[i] = (gchar *)g_dpgettext2 (GETTEXT_PACKAGE, "cover page", cover_display[i]);
+
+      /* Translators, this is the label used for the option in the print
+       * dialog that controls the front cover page.
+       */
+      option = gtk_printer_option_new ("gtk-cover-before", C_("printer option", "Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
+      gtk_printer_option_choices_from_array (option, num_of_covers,
+                                        cover, cover_display_translated);
+
+      if (cups_printer->default_cover_before != NULL)
+        gtk_printer_option_set (option, cups_printer->default_cover_before);
+      else
+        gtk_printer_option_set (option, "none");
+      set_option_from_settings (option, settings);
+      gtk_printer_option_set_add (set, option);
+      g_object_unref (option);
+
+      /* Translators, this is the label used for the option in the print
+       * dialog that controls the back cover page.
+       */
+      option = gtk_printer_option_new ("gtk-cover-after", C_("printer option", "After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
+      gtk_printer_option_choices_from_array (option, num_of_covers,
+                                        cover, cover_display_translated);
+      if (cups_printer->default_cover_after != NULL)
+        gtk_printer_option_set (option, cups_printer->default_cover_after);
+      else
+        gtk_printer_option_set (option, "none");
+      set_option_from_settings (option, settings);
+      gtk_printer_option_set_add (set, option);
+      g_object_unref (option);
+
+      g_strfreev (cover);
+      g_strfreev (cover_display);
+      g_free (cover_display_translated);
+    }
+
+  /* Translators: this is the name of the option that controls when
+   * a print job is printed. Possible values are 'now', a specified time,
+   * or 'on hold'
+   */
+  option = gtk_printer_option_new ("gtk-print-time", C_("printer option", "Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
+  gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
+                                        print_at, print_at);
+  gtk_printer_option_set (option, "now");
+  set_option_from_settings (option, settings);
+  gtk_printer_option_set_add (set, option);
+  g_object_unref (option);
+
+  /* Translators: this is the name of the option that allows the user
+   * to specify a time when a print job will be printed.
+   */
+  option = gtk_printer_option_new ("gtk-print-time-text", C_("printer option", "Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
+  gtk_printer_option_set (option, "");
+  set_option_from_settings (option, settings);
+  gtk_printer_option_set_add (set, option);
+  g_object_unref (option);
+
+  /* Printer (ppd) specific settings */
+  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+  if (ppd_file)
+    {
+      GtkPaperSize *paper_size;
+      ppd_option_t *ppd_option;
+      const gchar *ppd_name;
+
+      ppdMarkDefaults (ppd_file);
+
+      paper_size = gtk_page_setup_get_paper_size (page_setup);
+
+      ppd_option = ppdFindOption (ppd_file, "PageSize");
+      if (ppd_option)
+       {
+         ppd_name = gtk_paper_size_get_ppd_name (paper_size);
+
+         if (ppd_name)
+           strncpy (ppd_option->defchoice, ppd_name, PPD_MAX_NAME);
+         else
+           {
+             gchar *custom_name;
+             char width[G_ASCII_DTOSTR_BUF_SIZE];
+             char height[G_ASCII_DTOSTR_BUF_SIZE];
+
+             g_ascii_formatd (width, sizeof (width), "%.2f",
+                              gtk_paper_size_get_width (paper_size,
+                                                        GTK_UNIT_POINTS));
+             g_ascii_formatd (height, sizeof (height), "%.2f",
+                              gtk_paper_size_get_height (paper_size,
+                                                         GTK_UNIT_POINTS));
+             /* Translators: this format is used to display a custom
+              * paper size. The two placeholders are replaced with
+              * the width and height in points. E.g: "Custom
+              * 230.4x142.9"
+               */
+             custom_name = g_strdup_printf (_("Custom %s×%s"), width, height);
+             strncpy (ppd_option->defchoice, custom_name, PPD_MAX_NAME);
+             g_free (custom_name);
+           }
+       }
+
+      for (i = 0; i < ppd_file->num_groups; i++)
+        handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
+    }
+  else
+    {
+      /* Try IPP options */
+
+      option = setup_ipp_option ("sides",
+                                 cups_printer->sides_default,
+                                 cups_printer->sides_supported,
+                                 set);
+
+      if (option != NULL)
+        set_option_from_settings (option, settings);
+
+      option = setup_ipp_option ("output-bin",
+                                 cups_printer->output_bin_default,
+                                 cups_printer->output_bin_supported,
+                                 set);
+
+      if (option != NULL)
+        set_option_from_settings (option, settings);
+    }
+
+  /* Now honor the user set defaults for this printer */
+  num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
+
+  for (i = 0; i < num_opts; i++)
+    {
+      if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
+        continue;
+
+      name = get_lpoption_name (opts[i].name);
+      if (strcmp (name, "cups-job-sheets") == 0)
+        {
+          gchar **values;
+          gint    num_values;
+
+          values = g_strsplit (opts[i].value, ",", 2);
+          num_values = g_strv_length (values);
+
+          option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
+          if (option && num_values > 0)
+            gtk_printer_option_set (option, g_strstrip (values[0]));
+
+          option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
+          if (option && num_values > 1)
+            gtk_printer_option_set (option, g_strstrip (values[1]));
+
+          g_strfreev (values);
+        }
+      else if (strcmp (name, "cups-job-hold-until") == 0)
+        {
+          GtkPrinterOption *option2 = NULL;
+
+          option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
+          if (option && opts[i].value)
+            {
+              option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
+              if (option2)
+                {
+                  if (strcmp (opts[i].value, "indefinite") == 0)
+                    gtk_printer_option_set (option2, "on-hold");
+                  else
+                    {
+                      gtk_printer_option_set (option2, "at");
+                      gtk_printer_option_set (option, opts[i].value);
+                    }
+                }
+            }
+        }
+      else if (strcmp (name, "cups-sides") == 0)
+        {
+          option = gtk_printer_option_set_lookup (set, "gtk-duplex");
+          if (option && opts[i].value)
+            {
+              if (!option_is_ipp_option (option))
+                {
+                  if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
+                    gtk_printer_option_set (option, "DuplexTumble");
+                  else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
+                    gtk_printer_option_set (option, "DuplexNoTumble");
+                }
+              else
+                {
+                  gtk_printer_option_set (option, opts[i].value);
+                }
+            }
+        }
+      else
+        {
+          option = gtk_printer_option_set_lookup (set, name);
+          if (option)
+            gtk_printer_option_set (option, opts[i].value);
+        }
+      g_free (name);
+    }
+
+  cupsFreeOptions (num_opts, opts);
+
+#ifdef HAVE_COLORD
+  option = gtk_printer_option_new ("colord-profile",
+                                   /* TRANSLATORS: this this the ICC color profile to use for this job */
+                                   C_("printer option", "Printer Profile"),
+                                   GTK_PRINTER_OPTION_TYPE_INFO);
+
+  /* assign it to the color page */
+  option->group = g_strdup ("ColorPage");
+
+  /* TRANSLATORS: this is when color profile information is unavailable */
+  gtk_printer_option_set (option, C_("printer option value", "Unavailable"));
+  gtk_printer_option_set_add (set, option);
+
+  /* watch to see if the user changed the options */
+  helper = g_new (GtkPrintBackendCupsColordHelper, 1);
+  helper->printer = printer;
+  helper->settings = settings;
+  g_signal_connect_data (set, "changed",
+                         G_CALLBACK (colord_printer_option_set_changed_cb),
+                         helper,
+                         (GClosureNotify) g_free,
+                         0);
+
+  /* initial coldplug */
+  gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
+                                    settings, set);
+  g_object_bind_property (printer, "profile-title",
+                          option, "value",
+                          G_BINDING_DEFAULT);
+
+#endif
+
+  return set;
+}
+
+
+static void
+mark_option_from_set (GtkPrinterOptionSet *set,
+                     ppd_file_t          *ppd_file,
+                     ppd_option_t        *ppd_option)
+{
+  GtkPrinterOption *option;
+  char *name = get_ppd_option_name (ppd_option->keyword);
+
+  option = gtk_printer_option_set_lookup (set, name);
+
+  if (option)
+    ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
+
+  g_free (name);
+}
+
+
+static void
+mark_group_from_set (GtkPrinterOptionSet *set,
+                    ppd_file_t          *ppd_file,
+                    ppd_group_t         *group)
+{
+  int i;
+
+  for (i = 0; i < group->num_options; i++)
+    mark_option_from_set (set, ppd_file, &group->options[i]);
+
+  for (i = 0; i < group->num_subgroups; i++)
+    mark_group_from_set (set, ppd_file, &group->subgroups[i]);
+}
+
+static void
+set_conflicts_from_option (GtkPrinterOptionSet *set,
+                          ppd_file_t          *ppd_file,
+                          ppd_option_t        *ppd_option)
+{
+  GtkPrinterOption *option;
+  char *name;
+
+  if (ppd_option->conflicted)
+    {
+      name = get_ppd_option_name (ppd_option->keyword);
+      option = gtk_printer_option_set_lookup (set, name);
+
+      if (option)
+       gtk_printer_option_set_has_conflict (option, TRUE);
+#ifdef PRINT_IGNORED_OPTIONS
+      else
+       g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
+#endif
+
+      g_free (name);
+    }
+}
+
+static void
+set_conflicts_from_group (GtkPrinterOptionSet *set,
+                         ppd_file_t          *ppd_file,
+                         ppd_group_t         *group)
+{
+  int i;
+
+  for (i = 0; i < group->num_options; i++)
+    set_conflicts_from_option (set, ppd_file, &group->options[i]);
+
+  for (i = 0; i < group->num_subgroups; i++)
+    set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
+}
+
+static gboolean
+cups_printer_mark_conflicts (GtkPrinter          *printer,
+                            GtkPrinterOptionSet *options)
+{
+  ppd_file_t *ppd_file;
+  int num_conflicts;
+  int i;
+
+  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+
+  if (ppd_file == NULL)
+    return FALSE;
+
+  ppdMarkDefaults (ppd_file);
+
+  for (i = 0; i < ppd_file->num_groups; i++)
+    mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
+
+  num_conflicts = ppdConflicts (ppd_file);
+
+  if (num_conflicts > 0)
+    {
+      for (i = 0; i < ppd_file->num_groups; i++)
+       set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
+    }
+
+  return num_conflicts > 0;
+}
+
+struct OptionData {
+  GtkPrinter *printer;
+  GtkPrinterOptionSet *options;
+  GtkPrintSettings *settings;
+  ppd_file_t *ppd_file;
+};
+
+typedef struct {
+  const char *cups;
+  const char *standard;
+} NameMapping;
+
+static void
+map_settings_to_option (GtkPrinterOption  *option,
+                       const NameMapping  table[],
+                       gint               n_elements,
+                       GtkPrintSettings  *settings,
+                       const gchar       *standard_name,
+                       const gchar       *cups_name,
+                       const gchar       *ipp_name)
+{
+  int i;
+  char *name;
+  const char *cups_value;
+  const char *ipp_value;
+  const char *standard_value;
+
+  /* If the cups-specific setting is set, always use that */
+  name = g_strdup_printf ("cups-%s", cups_name);
+  cups_value = gtk_print_settings_get (settings, name);
+  g_free (name);
+
+  if (cups_value != NULL)
+    {
+      gtk_printer_option_set (option, cups_value);
+      return;
+    }
+
+  /* If the IPP-specific setting is set, use that */
+  name = g_strdup_printf ("cups-%s", ipp_name);
+  ipp_value = gtk_print_settings_get (settings, name);
+  g_free (name);
+
+  if (ipp_value != NULL)
+    {
+      gtk_printer_option_set (option, ipp_value);
+      return;
+    }
+
+  /* Otherwise we try to convert from the general setting */
+  standard_value = gtk_print_settings_get (settings, standard_name);
+  if (standard_value == NULL)
+    return;
+
+  for (i = 0; i < n_elements; i++)
+    {
+      if (table[i].cups == NULL && table[i].standard == NULL)
+       {
+         gtk_printer_option_set (option, standard_value);
+         break;
+       }
+      else if (table[i].cups == NULL &&
+              strcmp (table[i].standard, standard_value) == 0)
+       {
+         set_option_off (option);
+         break;
+       }
+      else if (strcmp (table[i].standard, standard_value) == 0)
+       {
+         gtk_printer_option_set (option, table[i].cups);
+         break;
+       }
+    }
+}
+
+static void
+map_option_to_settings (const gchar       *value,
+                       const NameMapping  table[],
+                       gint               n_elements,
+                       GtkPrintSettings  *settings,
+                       const gchar       *standard_name,
+                       const gchar       *cups_name,
+                       const gchar       *ipp_name,
+                       gboolean           is_ipp_option)
+{
+  int i;
+  char *name;
+
+  for (i = 0; i < n_elements; i++)
+    {
+      if (table[i].cups == NULL && table[i].standard == NULL)
+       {
+         gtk_print_settings_set (settings,
+                                 standard_name,
+                                 value);
+         break;
+       }
+      else if (table[i].cups == NULL && table[i].standard != NULL)
+       {
+         if (value_is_off (value))
+           {
+             gtk_print_settings_set (settings,
+                                     standard_name,
+                                     table[i].standard);
+             break;
+           }
+       }
+      else if (strcmp (table[i].cups, value) == 0)
+       {
+         gtk_print_settings_set (settings,
+                                 standard_name,
+                                 table[i].standard);
+         break;
+       }
+    }
+
+  /* Always set the corresponding cups-specific setting */
+  if (is_ipp_option)
+    name = g_strdup_printf ("cups-%s", ipp_name);
+  else
+    name = g_strdup_printf ("cups-%s", cups_name);
+
+  gtk_print_settings_set (settings, name, value);
+
+  g_free (name);
+}
+
+
+static const NameMapping paper_source_map[] = {
+  { "Lower", "lower"},
+  { "Middle", "middle"},
+  { "Upper", "upper"},
+  { "Rear", "rear"},
+  { "Envelope", "envelope"},
+  { "Cassette", "cassette"},
+  { "LargeCapacity", "large-capacity"},
+  { "AnySmallFormat", "small-format"},
+  { "AnyLargeFormat", "large-format"},
+  { NULL, NULL}
+};
+
+static const NameMapping output_tray_map[] = {
+  { "Upper", "upper"},
+  { "Lower", "lower"},
+  { "Rear", "rear"},
+  { NULL, NULL}
+};
+
+static const NameMapping duplex_map[] = {
+  { "DuplexTumble", "vertical" },
+  { "DuplexNoTumble", "horizontal" },
+  { NULL, "simplex" }
+};
+
+static const NameMapping output_mode_map[] = {
+  { "Standard", "normal" },
+  { "Normal", "normal" },
+  { "Draft", "draft" },
+  { "Fast", "draft" },
+};
+
+static const NameMapping media_type_map[] = {
+  { "Transparency", "transparency"},
+  { "Standard", "stationery"},
+  { NULL, NULL}
+};
+
+static const NameMapping all_map[] = {
+  { NULL, NULL}
+};
+
+
+static void
+set_option_from_settings (GtkPrinterOption *option,
+                         GtkPrintSettings *settings)
+{
+  const char *cups_value;
+  char *value;
+
+  if (settings == NULL)
+    return;
+
+  if (strcmp (option->name, "gtk-paper-source") == 0)
+    map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
+                            settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
+                            "InputSlot", NULL);
+  else if (strcmp (option->name, "gtk-output-tray") == 0)
+    map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
+                           settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
+                           "OutputBin", "output-bin");
+  else if (strcmp (option->name, "gtk-duplex") == 0)
+    map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
+                           settings, GTK_PRINT_SETTINGS_DUPLEX,
+                           "Duplex", "sides");
+  else if (strcmp (option->name, "cups-OutputMode") == 0)
+    map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
+                           settings, GTK_PRINT_SETTINGS_QUALITY,
+                           "OutputMode", NULL);
+  else if (strcmp (option->name, "cups-Resolution") == 0)
+    {
+      cups_value = gtk_print_settings_get (settings, option->name);
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+      else
+       {
+         if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
+             gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
+             gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
+             option->value == NULL || option->value[0] == '\0')
+           {
+              int res = gtk_print_settings_get_resolution (settings);
+              int res_x = gtk_print_settings_get_resolution_x (settings);
+              int res_y = gtk_print_settings_get_resolution_y (settings);
+
+              if (res_x != res_y)
+                {
+                  value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
+                  gtk_printer_option_set (option, value);
+                  g_free (value);
+                }
+              else if (res != 0)
+                {
+                  value = g_strdup_printf ("%ddpi", res);
+                  gtk_printer_option_set (option, value);
+                  g_free (value);
+                }
+            }
+        }
+    }
+  else if (strcmp (option->name, "gtk-paper-type") == 0)
+    map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
+                           settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
+                           "MediaType", NULL);
+  else if (strcmp (option->name, "gtk-n-up") == 0)
+    {
+      map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
+                             settings, GTK_PRINT_SETTINGS_NUMBER_UP,
+                             "number-up", NULL);
+    }
+  else if (strcmp (option->name, "gtk-n-up-layout") == 0)
+    {
+      map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
+                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
+                             "number-up-layout", NULL);
+    }
+  else if (strcmp (option->name, "gtk-billing-info") == 0)
+    {
+      cups_value = gtk_print_settings_get (settings, "cups-job-billing");
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+    }
+  else if (strcmp (option->name, "gtk-job-prio") == 0)
+    {
+      cups_value = gtk_print_settings_get (settings, "cups-job-priority");
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+    }
+  else if (strcmp (option->name, "gtk-cover-before") == 0)
+    {
+      cups_value = gtk_print_settings_get (settings, "cover-before");
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+    }
+  else if (strcmp (option->name, "gtk-cover-after") == 0)
+    {
+      cups_value = gtk_print_settings_get (settings, "cover-after");
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+    }
+  else if (strcmp (option->name, "gtk-print-time") == 0)
+    {
+      cups_value = gtk_print_settings_get (settings, "print-at");
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+    }
+  else if (strcmp (option->name, "gtk-print-time-text") == 0)
+    {
+      cups_value = gtk_print_settings_get (settings, "print-at-time");
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+    }
+  else if (g_str_has_prefix (option->name, "cups-"))
+    {
+      cups_value = gtk_print_settings_get (settings, option->name);
+      if (cups_value)
+       gtk_printer_option_set (option, cups_value);
+    }
+}
+
+static void
+foreach_option_get_settings (GtkPrinterOption *option,
+                            gpointer          user_data)
+{
+  struct OptionData *data = user_data;
+  GtkPrintSettings *settings = data->settings;
+  const char *value;
+
+  value = option->value;
+
+  if (strcmp (option->name, "gtk-paper-source") == 0)
+    map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
+                           settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE,
+                           "InputSlot", NULL, FALSE);
+  else if (strcmp (option->name, "gtk-output-tray") == 0)
+    map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
+                           settings, GTK_PRINT_SETTINGS_OUTPUT_BIN,
+                           "OutputBin", "output-bin", option_is_ipp_option (option));
+  else if (strcmp (option->name, "gtk-duplex") == 0)
+    map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
+                           settings, GTK_PRINT_SETTINGS_DUPLEX,
+                           "Duplex", "sides", option_is_ipp_option (option));
+  else if (strcmp (option->name, "cups-OutputMode") == 0)
+    map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
+                           settings, GTK_PRINT_SETTINGS_QUALITY,
+                           "OutputMode", NULL, FALSE);
+  else if (strcmp (option->name, "cups-Resolution") == 0)
+    {
+      int res, res_x, res_y;
+
+      if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
+        {
+          if (res_x > 0 && res_y > 0)
+            gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
+        }
+      else if (sscanf (value, "%ddpi", &res) == 1)
+        {
+          if (res > 0)
+            gtk_print_settings_set_resolution (settings, res);
+        }
+
+      gtk_print_settings_set (settings, option->name, value);
+    }
+  else if (strcmp (option->name, "gtk-paper-type") == 0)
+    map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
+                           settings, GTK_PRINT_SETTINGS_MEDIA_TYPE,
+                           "MediaType", NULL, FALSE);
+  else if (strcmp (option->name, "gtk-n-up") == 0)
+    map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
+                           settings, GTK_PRINT_SETTINGS_NUMBER_UP,
+                           "number-up", NULL, FALSE);
+  else if (strcmp (option->name, "gtk-n-up-layout") == 0)
+    map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
+                           settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT,
+                           "number-up-layout", NULL, FALSE);
+  else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
+    gtk_print_settings_set (settings, "cups-job-billing", value);
+  else if (strcmp (option->name, "gtk-job-prio") == 0)
+    gtk_print_settings_set (settings, "cups-job-priority", value);
+  else if (strcmp (option->name, "gtk-cover-before") == 0)
+    gtk_print_settings_set (settings, "cover-before", value);
+  else if (strcmp (option->name, "gtk-cover-after") == 0)
+    gtk_print_settings_set (settings, "cover-after", value);
+  else if (strcmp (option->name, "gtk-print-time") == 0)
+    gtk_print_settings_set (settings, "print-at", value);
+  else if (strcmp (option->name, "gtk-print-time-text") == 0)
+    gtk_print_settings_set (settings, "print-at-time", value);
+  else if (g_str_has_prefix (option->name, "cups-"))
+    gtk_print_settings_set (settings, option->name, value);
+}
+
+static gboolean
+supports_am_pm (void)
+{
+  struct tm tmp_tm = { 0 };
+  char   time[8];
+  int    length;
+
+  length = strftime (time, sizeof (time), "%p", &tmp_tm);
+
+  return length != 0;
+}
+
+/* Converts local time to UTC time. Local time has to be in one of these
+ * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
+ * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
+ * Returns a newly allocated string holding UTC time in HH:MM:SS format
+ * or NULL.
+ */
+gchar *
+localtime_to_utctime (const char *local_time)
+{
+  const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
+                             " %H : %M : %S ",
+                             " %I : %M %p ", " %p %I : %M ",
+                             " %H : %M ",
+                             " %I %p ", " %p %I "};
+  const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
+  const char *end = NULL;
+  struct tm  *actual_local_time;
+  struct tm  *actual_utc_time;
+  struct tm   local_print_time;
+  struct tm   utc_print_time;
+  struct tm   diff_time;
+  gchar      *utc_time = NULL;
+  int         i, n;
+
+  if (local_time == NULL || local_time[0] == '\0')
+    return NULL;
+
+  n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
+
+  for (i = 0; i < n; i++)
+    {
+      local_print_time.tm_hour = 0;
+      local_print_time.tm_min  = 0;
+      local_print_time.tm_sec  = 0;
+
+      if (supports_am_pm ())
+        end = strptime (local_time, formats_0[i], &local_print_time);
+      else
+        end = strptime (local_time, formats_1[i], &local_print_time);
+
+      if (end != NULL && end[0] == '\0')
+        break;
+    }
+
+  if (end != NULL && end[0] == '\0')
+    {
+      time_t rawtime;
+      time (&rawtime);
+
+      actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
+      actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
+
+      diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
+      diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
+      diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
+
+      utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
+      utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
+      utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
+
+      utc_time = g_strdup_printf ("%02d:%02d:%02d",
+                                  utc_print_time.tm_hour,
+                                  utc_print_time.tm_min,
+                                  utc_print_time.tm_sec);
+    }
+
+  return utc_time;
+}
+
+static void
+cups_printer_get_settings_from_options (GtkPrinter          *printer,
+                                       GtkPrinterOptionSet *options,
+                                       GtkPrintSettings    *settings)
+{
+  struct OptionData data;
+  const char *print_at, *print_at_time;
+
+  data.printer = printer;
+  data.options = options;
+  data.settings = settings;
+  data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+
+  gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
+  if (data.ppd_file != NULL)
+    {
+      GtkPrinterOption *cover_before, *cover_after;
+
+      cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
+      cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
+      if (cover_before && cover_after)
+       {
+         char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
+         gtk_print_settings_set (settings, "cups-job-sheets", value);
+         g_free (value);
+       }
+
+      print_at = gtk_print_settings_get (settings, "print-at");
+      print_at_time = gtk_print_settings_get (settings, "print-at-time");
+
+      if (strcmp (print_at, "at") == 0)
+        {
+          gchar *utc_time = NULL;
+
+          utc_time = localtime_to_utctime (print_at_time);
+
+          if (utc_time != NULL)
+            {
+              gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
+              g_free (utc_time);
+            }
+          else
+            gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
+        }
+      else if (strcmp (print_at, "on-hold") == 0)
+       gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
+    }
+}
+
+static void
+cups_printer_prepare_for_print (GtkPrinter       *printer,
+                               GtkPrintJob      *print_job,
+                               GtkPrintSettings *settings,
+                               GtkPageSetup     *page_setup)
+{
+  GtkPrintPages pages;
+  GtkPageRange *ranges;
+  gint n_ranges;
+  GtkPageSet page_set;
+  GtkPaperSize *paper_size;
+  const char *ppd_paper_name;
+  double scale;
+  GtkPrintCapabilities  capabilities;
+
+  capabilities = cups_printer_get_capabilities (printer);
+  pages = gtk_print_settings_get_print_pages (settings);
+  gtk_print_job_set_pages (print_job, pages);
+
+  if (pages == GTK_PRINT_PAGES_RANGES)
+    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
+  else
+    {
+      ranges = NULL;
+      n_ranges = 0;
+    }
+
+  gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
+
+  if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
+    {
+      if (gtk_print_settings_get_collate (settings))
+        gtk_print_settings_set (settings, "cups-Collate", "True");
+      else
+        gtk_print_settings_set (settings, "cups-Collate", "False");
+      gtk_print_job_set_collate (print_job, FALSE);
+    }
+  else
+    {
+      gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
+    }
+
+  if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
+    {
+      if (gtk_print_settings_get_reverse (settings))
+        gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
+      gtk_print_job_set_reverse (print_job, FALSE);
+    }
+  else
+    {
+      gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
+    }
+
+  if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
+    {
+      if (gtk_print_settings_get_n_copies (settings) > 1)
+        gtk_print_settings_set_int (settings, "cups-copies",
+                                    gtk_print_settings_get_n_copies (settings));
+      gtk_print_job_set_num_copies (print_job, 1);
+    }
+  else
+    {
+      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);
+
+  page_set = gtk_print_settings_get_page_set (settings);
+  if (page_set == GTK_PAGE_SET_EVEN)
+    gtk_print_settings_set (settings, "cups-page-set", "even");
+  else if (page_set == GTK_PAGE_SET_ODD)
+    gtk_print_settings_set (settings, "cups-page-set", "odd");
+  gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
+
+  paper_size = gtk_page_setup_get_paper_size (page_setup);
+  ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
+  if (ppd_paper_name != NULL)
+    gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
+  else if (gtk_paper_size_is_ipp (paper_size))
+    gtk_print_settings_set (settings, "cups-media", gtk_paper_size_get_name (paper_size));
+  else
+    {
+      char width[G_ASCII_DTOSTR_BUF_SIZE];
+      char height[G_ASCII_DTOSTR_BUF_SIZE];
+      char *custom_name;
+
+      g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
+      g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
+      custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
+      gtk_print_settings_set (settings, "cups-PageSize", custom_name);
+      g_free (custom_name);
+    }
+
+  if (gtk_print_settings_get_number_up (settings) > 1)
+    {
+      GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
+      GEnumClass        *enum_class;
+      GEnumValue        *enum_value;
+
+      switch (gtk_page_setup_get_orientation (page_setup))
+        {
+          case GTK_PAGE_ORIENTATION_PORTRAIT:
+            break;
+          case GTK_PAGE_ORIENTATION_LANDSCAPE:
+            if (layout < 4)
+              layout = layout + 2 + 4 * (1 - layout / 2);
+            else
+              layout = layout - 3 - 2 * (layout % 2);
+            break;
+          case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
+            layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
+            break;
+          case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
+            if (layout < 4)
+              layout = layout + 5 - 2 * (layout % 2);
+            else
+              layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
+            break;
+        }
+
+      enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
+      enum_value = g_enum_get_value (enum_class, layout);
+      gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
+      g_type_class_unref (enum_class);
+
+      if (!(capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP))
+        {
+          gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
+          gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings));
+        }
+    }
+
+  gtk_print_job_set_rotate (print_job, TRUE);
+}
+
+static GtkPageSetup *
+create_page_setup (ppd_file_t *ppd_file,
+                  ppd_size_t *size)
+ {
+   char *display_name;
+   GtkPageSetup *page_setup;
+   GtkPaperSize *paper_size;
+   ppd_option_t *option;
+   ppd_choice_t *choice;
+
+  display_name = NULL;
+  option = ppdFindOption (ppd_file, "PageSize");
+  if (option)
+    {
+      choice = ppdFindChoice (option, size->name);
+      if (choice)
+       display_name = ppd_text_to_utf8 (ppd_file, choice->text);
+    }
+
+  if (display_name == NULL)
+    display_name = g_strdup (size->name);
+
+  page_setup = gtk_page_setup_new ();
+  paper_size = gtk_paper_size_new_from_ppd (size->name,
+                                           display_name,
+                                           size->width,
+                                           size->length);
+  gtk_page_setup_set_paper_size (page_setup, paper_size);
+  gtk_paper_size_free (paper_size);
+
+  gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
+  gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
+  gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
+  gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
+
+  g_free (display_name);
+
+  return page_setup;
+}
+
+static GtkPageSetup *
+create_page_setup_from_media (gchar     *media,
+                              MediaSize *media_size,
+                              gboolean   media_margin_default_set,
+                              gint       media_bottom_margin_default,
+                              gint       media_top_margin_default,
+                              gint       media_left_margin_default,
+                              gint       media_right_margin_default)
+{
+  GtkPageSetup *page_setup;
+  GtkPaperSize *paper_size;
+
+  page_setup = gtk_page_setup_new ();
+  paper_size = gtk_paper_size_new_from_ipp (media,
+                                            POINTS_PER_INCH * (media_size->x_dimension / MM_PER_INCH),
+                                            POINTS_PER_INCH * (media_size->y_dimension / MM_PER_INCH));
+  gtk_page_setup_set_paper_size (page_setup, paper_size);
+  gtk_paper_size_free (paper_size);
+
+  if (media_margin_default_set)
+    {
+      gtk_page_setup_set_bottom_margin (page_setup, media_bottom_margin_default, GTK_UNIT_MM);
+      gtk_page_setup_set_top_margin (page_setup, media_top_margin_default, GTK_UNIT_MM);
+      gtk_page_setup_set_left_margin (page_setup, media_left_margin_default, GTK_UNIT_MM);
+      gtk_page_setup_set_right_margin (page_setup, media_right_margin_default, GTK_UNIT_MM);
+    }
+
+  return page_setup;
+}
+
+static GList *
+cups_printer_list_papers (GtkPrinter *printer)
+{
+  ppd_file_t *ppd_file;
+  ppd_size_t *size;
+  GtkPageSetup *page_setup;
+  GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
+  GList *result = NULL;
+  int i;
+
+  ppd_file = gtk_printer_cups_get_ppd (cups_printer);
+  if (ppd_file != NULL)
+    {
+      for (i = 0; i < ppd_file->num_sizes; i++)
+        {
+          size = &ppd_file->sizes[i];
+
+          page_setup = create_page_setup (ppd_file, size);
+
+          result = g_list_prepend (result, page_setup);
+        }
+    }
+  else if (cups_printer->media_supported != NULL &&
+           cups_printer->media_size_supported != NULL &&
+           /*
+            * 'media_supported' list can contain names of minimal and maximal sizes
+            * for which we don't create item in 'media_size_supported' list.
+            */
+           g_list_length (cups_printer->media_supported) >=
+           g_list_length (cups_printer->media_size_supported))
+    {
+      MediaSize *media_size;
+      GList     *media_iter;
+      GList     *media_size_iter;
+      gchar     *media;
+
+      for (media_iter = cups_printer->media_supported,
+           media_size_iter = cups_printer->media_size_supported;
+           media_size_iter != NULL;
+           media_iter = media_iter->next,
+           media_size_iter = media_size_iter->next)
+        {
+          media = (gchar *) media_iter->data;
+          media_size = (MediaSize *) media_size_iter->data;
+
+          page_setup = create_page_setup_from_media (media,
+                                                     media_size,
+                                                     cups_printer->media_margin_default_set,
+                                                     cups_printer->media_bottom_margin_default,
+                                                     cups_printer->media_top_margin_default,
+                                                     cups_printer->media_left_margin_default,
+                                                     cups_printer->media_right_margin_default);
+
+          result = g_list_prepend (result, page_setup);
+        }
+    }
+
+  result = g_list_reverse (result);
+
+  return result;
+}
+
+static GtkPageSetup *
+cups_printer_get_default_page_size (GtkPrinter *printer)
+{
+  GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
+  GtkPageSetup   *result = NULL;
+  ppd_option_t   *option;
+  ppd_file_t     *ppd_file;
+  ppd_size_t     *size;
+
+  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+  if (ppd_file != NULL)
+    {
+      option = ppdFindOption (ppd_file, "PageSize");
+      if (option == NULL)
+        return NULL;
+
+      size = ppdPageSize (ppd_file, option->defchoice);
+      if (size == NULL)
+        return NULL;
+
+      result = create_page_setup (ppd_file, size);
+    }
+  else if (cups_printer->media_default != NULL)
+    {
+      MediaSize *media_size;
+      GList     *media_iter;
+      GList     *media_size_iter;
+      gchar     *media;
+
+      for (media_iter = cups_printer->media_supported,
+           media_size_iter = cups_printer->media_size_supported;
+           media_size_iter != NULL;
+           media_iter = media_iter->next,
+           media_size_iter = media_size_iter->next)
+        {
+          media = (gchar *) media_iter->data;
+          media_size = (MediaSize *) media_size_iter->data;
+
+          if (g_strcmp0 (cups_printer->media_default, media) == 0)
+            {
+              result = create_page_setup_from_media (media,
+                                                     media_size,
+                                                     cups_printer->media_margin_default_set,
+                                                     cups_printer->media_bottom_margin_default,
+                                                     cups_printer->media_top_margin_default,
+                                                     cups_printer->media_left_margin_default,
+                                                     cups_printer->media_right_margin_default);
+            }
+        }
+    }
+
+  return result;
+}
+
+static gboolean
+cups_printer_get_hard_margins (GtkPrinter *printer,
+                              gdouble    *top,
+                              gdouble    *bottom,
+                              gdouble    *left,
+                              gdouble    *right)
+{
+  GtkPrinterCups *cups_printer = GTK_PRINTER_CUPS (printer);
+  ppd_file_t     *ppd_file;
+  gboolean        result = FALSE;
+
+  ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+  if (ppd_file != NULL)
+    {
+      *left = ppd_file->custom_margins[0];
+      *bottom = ppd_file->custom_margins[1];
+      *right = ppd_file->custom_margins[2];
+      *top = ppd_file->custom_margins[3];
+      result = TRUE;
+    }
+  else if (cups_printer->media_margin_default_set)
+    {
+      *left = POINTS_PER_INCH * cups_printer->media_left_margin_default / MM_PER_INCH;
+      *bottom = POINTS_PER_INCH * cups_printer->media_bottom_margin_default / MM_PER_INCH;
+      *right = POINTS_PER_INCH * cups_printer->media_right_margin_default / MM_PER_INCH;
+      *top = POINTS_PER_INCH * cups_printer->media_top_margin_default / MM_PER_INCH;
+      result = TRUE;
+    }
+
+  return result;
+}
+
+static GtkPrintCapabilities
+cups_printer_get_capabilities (GtkPrinter *printer)
+{
+  GtkPrintCapabilities  capabilities = 0;
+  GtkPrinterCups       *cups_printer = GTK_PRINTER_CUPS (printer);
+
+  if (gtk_printer_cups_get_ppd (cups_printer))
+    {
+      capabilities = GTK_PRINT_CAPABILITY_REVERSE;
+    }
+
+  if (cups_printer->supports_copies)
+    {
+      capabilities |= GTK_PRINT_CAPABILITY_COPIES;
+    }
+
+  if (cups_printer->supports_collate)
+    {
+      capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
+    }
+
+  if (cups_printer->supports_number_up)
+    {
+      capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
+                      GTK_PRINT_CAPABILITY_NUMBER_UP;
+    }
+
+  return capabilities;
+}
+
+static void
+secrets_service_appeared_cb (GDBusConnection *connection,
+                             const gchar     *name,
+                             const gchar     *name_owner,
+                             gpointer         user_data)
+{
+  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+
+  backend->secrets_service_available = TRUE;
+}
+
+static void
+secrets_service_vanished_cb (GDBusConnection *connection,
+                             const gchar     *name,
+                             gpointer         user_data)
+{
+  GtkPrintBackendCups *backend = GTK_PRINT_BACKEND_CUPS (user_data);
+
+  backend->secrets_service_available = FALSE;
+}
diff --git a/modules/printbackends/gtkprintbackendcups.h b/modules/printbackends/gtkprintbackendcups.h
new file mode 100644 (file)
index 0000000..d4e35dd
--- /dev/null
@@ -0,0 +1,40 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcups.h: Default implementation of GtkPrintBackend for the Common Unix Print System (CUPS)
+ * Copyright (C) 2006, 2007 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_CUPS_H__
+#define __GTK_PRINT_BACKEND_CUPS_H__
+
+#include <glib-object.h>
+#include "gtkprintbackend.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINT_BACKEND_CUPS             (gtk_print_backend_cups_get_type ())
+#define GTK_PRINT_BACKEND_CUPS(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCups))
+#define GTK_IS_PRINT_BACKEND_CUPS(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CUPS))
+
+typedef struct _GtkPrintBackendCups      GtkPrintBackendCups;
+
+GtkPrintBackend *gtk_print_backend_cups_new      (void);
+GType          gtk_print_backend_cups_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_PRINT_BACKEND_CUPS_H__ */
+
+
diff --git a/modules/printbackends/gtkprintbackendfile.c b/modules/printbackends/gtkprintbackendfile.c
new file mode 100644 (file)
index 0000000..b568e01
--- /dev/null
@@ -0,0 +1,846 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendfile.c: Default implementation of GtkPrintBackend 
+ * for printing to a file
+ * Copyright (C) 2003, 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 <cairo-svg.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "gtk/gtk.h"
+#include "gtk/gtkprinter-private.h"
+
+#include "gtkprintbackendfile.h"
+
+typedef struct _GtkPrintBackendFileClass GtkPrintBackendFileClass;
+
+#define GTK_PRINT_BACKEND_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass))
+#define GTK_IS_PRINT_BACKEND_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_FILE))
+#define GTK_PRINT_BACKEND_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass))
+
+#define _STREAM_MAX_CHUNK_SIZE 8192
+
+struct _GtkPrintBackendFileClass
+{
+  GtkPrintBackendClass parent_class;
+};
+
+struct _GtkPrintBackendFile
+{
+  GtkPrintBackend parent_instance;
+};
+
+typedef enum
+{
+  FORMAT_PDF,
+  FORMAT_PS,
+  FORMAT_SVG,
+  N_FORMATS
+} OutputFormat;
+
+static const gchar* formats[N_FORMATS] =
+{
+  "pdf",
+  "ps",
+  "svg"
+};
+
+static GObjectClass *backend_parent_class;
+
+static void                 gtk_print_backend_file_class_init      (GtkPrintBackendFileClass *class);
+static void                 gtk_print_backend_file_init            (GtkPrintBackendFile      *impl);
+static void                 file_printer_get_settings_from_options (GtkPrinter              *printer,
+                                                                   GtkPrinterOptionSet     *options,
+                                                                   GtkPrintSettings        *settings);
+static GtkPrinterOptionSet *file_printer_get_options               (GtkPrinter              *printer,
+                                                                   GtkPrintSettings        *settings,
+                                                                   GtkPageSetup            *page_setup,
+                                                                   GtkPrintCapabilities     capabilities);
+static void                 file_printer_prepare_for_print         (GtkPrinter              *printer,
+                                                                   GtkPrintJob             *print_job,
+                                                                   GtkPrintSettings        *settings,
+                                                                   GtkPageSetup            *page_setup);
+static void                 gtk_print_backend_file_print_stream    (GtkPrintBackend         *print_backend,
+                                                                   GtkPrintJob             *job,
+                                                                   GIOChannel              *data_io,
+                                                                   GtkPrintJobCompleteFunc  callback,
+                                                                   gpointer                 user_data,
+                                                                   GDestroyNotify           dnotify);
+static cairo_surface_t *    file_printer_create_cairo_surface      (GtkPrinter              *printer,
+                                                                   GtkPrintSettings        *settings,
+                                                                   gdouble                  width,
+                                                                   gdouble                  height,
+                                                                   GIOChannel              *cache_io);
+
+static GList *              file_printer_list_papers               (GtkPrinter              *printer);
+static GtkPageSetup *       file_printer_get_default_page_size     (GtkPrinter              *printer);
+
+G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendFile, gtk_print_backend_file, GTK_TYPE_PRINT_BACKEND)
+
+void
+g_io_module_load (GIOModule *module)
+{
+  g_type_module_use (G_TYPE_MODULE (module));
+
+  gtk_print_backend_file_register_type (G_TYPE_MODULE (module));
+
+  g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
+                                  GTK_TYPE_PRINT_BACKEND_FILE,
+                                  "file",
+                                  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_file_new:
+ *
+ * Creates a new #GtkPrintBackendFile object. #GtkPrintBackendFile
+ * implements the #GtkPrintBackend interface with direct access to
+ * the filesystem using Unix/Linux API calls
+ *
+ * Returns: the new #GtkPrintBackendFile object
+ **/
+GtkPrintBackend *
+gtk_print_backend_file_new (void)
+{
+  return g_object_new (GTK_TYPE_PRINT_BACKEND_FILE, NULL);
+}
+
+static void
+gtk_print_backend_file_class_init (GtkPrintBackendFileClass *class)
+{
+  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
+
+  backend_parent_class = g_type_class_peek_parent (class);
+
+  backend_class->print_stream = gtk_print_backend_file_print_stream;
+  backend_class->printer_create_cairo_surface = file_printer_create_cairo_surface;
+  backend_class->printer_get_options = file_printer_get_options;
+  backend_class->printer_get_settings_from_options = file_printer_get_settings_from_options;
+  backend_class->printer_prepare_for_print = file_printer_prepare_for_print;
+  backend_class->printer_list_papers = file_printer_list_papers;
+  backend_class->printer_get_default_page_size = file_printer_get_default_page_size;
+}
+
+static void
+gtk_print_backend_file_class_finalize (GtkPrintBackendFileClass *class)
+{
+}
+
+/* return N_FORMATS if no explicit format in the settings */
+static OutputFormat
+format_from_settings (GtkPrintSettings *settings)
+{
+  const gchar *value;
+  gint i;
+
+  if (settings == NULL)
+    return N_FORMATS;
+
+  value = gtk_print_settings_get (settings,
+                                  GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
+  if (value == NULL)
+    return N_FORMATS;
+
+  for (i = 0; i < N_FORMATS; ++i)
+    if (strcmp (value, formats[i]) == 0)
+      break;
+
+  g_assert (i < N_FORMATS);
+
+  return (OutputFormat) i;
+}
+
+static gchar *
+output_file_from_settings (GtkPrintSettings *settings,
+                          const gchar      *default_format)
+{
+  gchar *uri = NULL;
+
+  if (settings)
+    uri = g_strdup (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI));
+
+  if (uri == NULL)
+    { 
+      const gchar *extension, *basename = NULL, *output_dir = NULL;
+      gchar *name, *locale_name, *path;
+
+      if (default_format)
+        extension = default_format;
+      else
+        {
+          OutputFormat format;
+
+          format = format_from_settings (settings);
+          switch (format)
+            {
+              default:
+              case FORMAT_PDF:
+                extension = "pdf";
+                break;
+              case FORMAT_PS:
+                extension = "ps";
+                break;
+              case FORMAT_SVG:
+                extension = "svg";
+                break;
+            }
+        }
+
+      if (settings)
+        basename = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME);
+      if (basename == NULL)
+        basename = _("output");
+
+      name = g_strconcat (basename, ".", extension, NULL);
+
+      locale_name = g_filename_from_utf8 (name, -1, NULL, NULL, NULL);
+      g_free (name);
+
+      if (locale_name != NULL)
+        {
+          if (settings)
+            output_dir = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_DIR);
+          if (output_dir == NULL)
+            {
+              const gchar *document_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+
+              if (document_dir == NULL)
+                {
+                  gchar *current_dir = g_get_current_dir ();
+                  path = g_build_filename (current_dir, locale_name, NULL);
+                  g_free (current_dir);
+                }
+              else
+                path = g_build_filename (document_dir, locale_name, NULL);
+
+              uri = g_filename_to_uri (path, NULL, NULL); 
+           }
+          else
+            {
+              path = g_build_filename (output_dir, locale_name, NULL);
+              uri = g_filename_to_uri (path, NULL, NULL);
+            }
+
+          g_free (path); 
+          g_free (locale_name);
+        }
+    }
+
+  return uri;
+}
+
+static cairo_status_t
+_cairo_write (void                *closure,
+              const unsigned char *data,
+              unsigned int         length)
+{
+  GIOChannel *io = (GIOChannel *)closure;
+  gsize written = 0;
+  GError *error;
+
+  error = NULL;
+
+  GTK_NOTE (PRINTING,
+            g_print ("FILE Backend: Writting %u byte chunk to temp file\n", length));
+
+  while (length > 0) 
+    {
+      GIOStatus status;
+
+      status = g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error);
+
+      if (status == G_IO_STATUS_ERROR)
+        {
+          if (error != NULL)
+            {
+              GTK_NOTE (PRINTING,
+                        g_print ("FILE Backend: Error writting to temp file, %s\n", error->message));
+
+              g_error_free (error);
+            }
+
+         return CAIRO_STATUS_WRITE_ERROR;
+       }    
+
+      GTK_NOTE (PRINTING,
+                g_print ("FILE Backend: Wrote %zd bytes to temp file\n", written));
+      
+      data += written;
+      length -= written;
+    }
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_surface_t *
+file_printer_create_cairo_surface (GtkPrinter       *printer,
+                                  GtkPrintSettings *settings,
+                                  gdouble           width, 
+                                  gdouble           height,
+                                  GIOChannel       *cache_io)
+{
+  cairo_surface_t *surface;
+  OutputFormat format;
+  const cairo_svg_version_t *versions;
+  int num_versions = 0;
+
+  format = format_from_settings (settings);
+
+  switch (format)
+    {
+      default:
+      case FORMAT_PDF:
+        surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height);
+        break;
+      case FORMAT_PS:
+        surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height);
+        break;
+      case FORMAT_SVG:
+        surface = cairo_svg_surface_create_for_stream (_cairo_write, cache_io, width, height);
+        cairo_svg_get_versions (&versions, &num_versions);
+        if (num_versions > 0)
+          cairo_svg_surface_restrict_to_version (surface, versions[num_versions - 1]);
+        break;
+    }
+
+  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;
+  GFileOutputStream *target_io_stream;
+  gpointer user_data;
+  GDestroyNotify dnotify;
+} _PrintStreamData;
+
+static void
+file_print_cb (GtkPrintBackendFile *print_backend,
+               GError              *error,
+               gpointer            user_data)
+{
+  gchar *uri;
+
+  _PrintStreamData *ps = (_PrintStreamData *) user_data;
+  GtkRecentManager *recent_manager;
+
+  if (ps->target_io_stream != NULL)
+    (void)g_output_stream_close (G_OUTPUT_STREAM (ps->target_io_stream), NULL, NULL);
+
+  if (ps->callback)
+    ps->callback (ps->job, ps->user_data, error);
+
+  if (ps->dnotify)
+    ps->dnotify (ps->user_data);
+
+  gtk_print_job_set_status (ps->job,
+                           (error != NULL)
+                              ? GTK_PRINT_STATUS_FINISHED_ABORTED
+                              : GTK_PRINT_STATUS_FINISHED);
+
+  recent_manager = gtk_recent_manager_get_default ();
+  uri = output_file_from_settings (gtk_print_job_get_settings (ps->job), NULL);
+  gtk_recent_manager_add_item (recent_manager, uri);
+  g_free (uri);
+
+  if (ps->job)
+    g_object_unref (ps->job);
+
+  g_free (ps);
+}
+
+static gboolean
+file_write (GIOChannel   *source,
+            GIOCondition  con,
+            gpointer      user_data)
+{
+  gchar buf[_STREAM_MAX_CHUNK_SIZE];
+  gsize bytes_read;
+  GError *error;
+  GIOStatus read_status;
+  _PrintStreamData *ps = (_PrintStreamData *) user_data;
+
+  error = NULL;
+
+  read_status =
+    g_io_channel_read_chars (source,
+                             buf,
+                             _STREAM_MAX_CHUNK_SIZE,
+                             &bytes_read,
+                             &error);
+
+  if (read_status != G_IO_STATUS_ERROR)
+    {
+      gsize bytes_written;
+
+      g_output_stream_write_all (G_OUTPUT_STREAM (ps->target_io_stream),
+                                 buf,
+                                 bytes_read,
+                                 &bytes_written,
+                                 NULL,
+                                 &error);
+    }
+
+  if (error != NULL || read_status == G_IO_STATUS_EOF)
+    {
+      file_print_cb (GTK_PRINT_BACKEND_FILE (ps->backend), error, user_data);
+
+      if (error != NULL)
+        {
+          GTK_NOTE (PRINTING,
+                    g_print ("FILE Backend: %s\n", error->message));
+
+          g_error_free (error);
+        }
+
+      return FALSE;
+    }
+
+  GTK_NOTE (PRINTING,
+            g_print ("FILE Backend: Writting %lu byte chunk to target file\n", bytes_read));
+
+  return TRUE;
+}
+
+static void
+gtk_print_backend_file_print_stream (GtkPrintBackend        *print_backend,
+                                    GtkPrintJob            *job,
+                                    GIOChannel             *data_io,
+                                    GtkPrintJobCompleteFunc callback,
+                                    gpointer                user_data,
+                                    GDestroyNotify          dnotify)
+{
+  GError *internal_error = NULL;
+  _PrintStreamData *ps;
+  GtkPrintSettings *settings;
+  gchar *uri;
+  GFile *file = NULL;
+
+  settings = gtk_print_job_get_settings (job);
+
+  ps = g_new0 (_PrintStreamData, 1);
+  ps->callback = callback;
+  ps->user_data = user_data;
+  ps->dnotify = dnotify;
+  ps->job = g_object_ref (job);
+  ps->backend = print_backend;
+
+  internal_error = NULL;
+  uri = output_file_from_settings (settings, NULL);
+
+  if (uri == NULL)
+    goto error;
+
+  file = g_file_new_for_uri (uri);
+  ps->target_io_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &internal_error);
+
+  g_object_unref (file);
+  g_free (uri);
+
+error:
+  if (internal_error != NULL)
+    {
+      file_print_cb (GTK_PRINT_BACKEND_FILE (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) file_write,
+                  ps);
+}
+
+static void
+gtk_print_backend_file_init (GtkPrintBackendFile *backend)
+{
+  GtkPrinter *printer;
+  
+  printer = g_object_new (GTK_TYPE_PRINTER,
+                         "name", _("Print to File"),
+                         "backend", backend,
+                         "is-virtual", TRUE,
+                         "accepts-pdf", TRUE,
+                         NULL); 
+
+  gtk_printer_set_has_details (printer, TRUE);
+  gtk_printer_set_icon_name (printer, "document-save");
+  gtk_printer_set_is_active (printer, TRUE);
+
+  gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer);
+  g_object_unref (printer);
+
+  gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
+}
+
+typedef struct {
+  GtkPrinter          *printer;
+  GtkPrinterOptionSet *set;
+} _OutputFormatChangedData;
+
+static void
+set_printer_format_from_option_set (GtkPrinter          *printer,
+                                   GtkPrinterOptionSet *set)
+{
+  GtkPrinterOption *format_option;
+  const gchar *value;
+  gint i;
+
+  format_option = gtk_printer_option_set_lookup (set, "output-file-format");
+  if (format_option && format_option->value)
+    {
+      value = format_option->value;
+      if (value)
+        {
+         for (i = 0; i < N_FORMATS; ++i)
+           if (strcmp (value, formats[i]) == 0)
+             break;
+
+         g_assert (i < N_FORMATS);
+
+         switch (i)
+           {
+             case FORMAT_PDF:
+               gtk_printer_set_accepts_pdf (printer, TRUE);
+               gtk_printer_set_accepts_ps (printer, FALSE);
+               break;
+             case FORMAT_PS:
+               gtk_printer_set_accepts_pdf (printer, FALSE);
+               gtk_printer_set_accepts_ps (printer, TRUE);
+               break;
+             case FORMAT_SVG:
+             default:
+               gtk_printer_set_accepts_pdf (printer, FALSE);
+               gtk_printer_set_accepts_ps (printer, FALSE);
+               break;
+           }
+       }
+    }
+}
+
+static void
+file_printer_output_file_format_changed (GtkPrinterOption    *format_option,
+                                        gpointer             user_data)
+{
+  GtkPrinterOption *uri_option;
+  gchar            *base = NULL;
+  _OutputFormatChangedData *data = (_OutputFormatChangedData *) user_data;
+
+  if (! format_option->value)
+    return;
+
+  uri_option = gtk_printer_option_set_lookup (data->set,
+                                              "gtk-main-page-custom-input");
+
+  if (uri_option && uri_option->value)
+    {
+      const gchar *uri = uri_option->value;
+      const gchar *dot = strrchr (uri, '.');
+
+      if (dot)
+        {
+          gint i;
+
+          /*  check if the file extension matches one of the known ones  */
+          for (i = 0; i < N_FORMATS; i++)
+            if (strcmp (dot + 1, formats[i]) == 0)
+              break;
+
+          if (i < N_FORMATS && strcmp (formats[i], format_option->value))
+            {
+              /*  the file extension is known but doesn't match the
+               *  selected one, strip it away
+               */
+              base = g_strndup (uri, dot - uri);
+            }
+        }
+      else
+        {
+          /*  there's no file extension  */
+          base = g_strdup (uri);
+        }
+    }
+
+  if (base)
+    {
+      gchar *tmp = g_strdup_printf ("%s.%s", base, format_option->value);
+
+      gtk_printer_option_set (uri_option, tmp);
+      g_free (tmp);
+      g_free (base);
+    }
+
+  set_printer_format_from_option_set (data->printer, data->set);
+}
+
+static GtkPrinterOptionSet *
+file_printer_get_options (GtkPrinter           *printer,
+                         GtkPrintSettings     *settings,
+                         GtkPageSetup         *page_setup,
+                         GtkPrintCapabilities  capabilities)
+{
+  GtkPrinterOptionSet *set;
+  GtkPrinterOption *option;
+  const gchar *n_up[] = {"1", "2", "4", "6", "9", "16" };
+  const gchar *pages_per_sheet = NULL;
+  const gchar *format_names[N_FORMATS] = { N_("PDF"), N_("PostScript"), N_("SVG") };
+  const gchar *supported_formats[N_FORMATS];
+  gchar *display_format_names[N_FORMATS];
+  gint n_formats = 0;
+  OutputFormat format;
+  gchar *uri;
+  gint current_format = 0;
+  _OutputFormatChangedData *format_changed_data;
+
+  format = format_from_settings (settings);
+
+  set = gtk_printer_option_set_new ();
+
+  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)! */);
+  if (settings)
+    pages_per_sheet = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_NUMBER_UP);
+  if (pages_per_sheet)
+    gtk_printer_option_set (option, pages_per_sheet);
+  else
+    gtk_printer_option_set (option, "1");
+  gtk_printer_option_set_add (set, option);
+  g_object_unref (option);
+
+  if (capabilities & (GTK_PRINT_CAPABILITY_GENERATE_PDF | GTK_PRINT_CAPABILITY_GENERATE_PS))
+    {
+      if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PDF)
+        {
+         if (format == FORMAT_PDF || format == N_FORMATS)
+            {
+              format = FORMAT_PDF;
+             current_format = n_formats;
+            }
+          supported_formats[n_formats] = formats[FORMAT_PDF];
+         display_format_names[n_formats] = _(format_names[FORMAT_PDF]);
+         n_formats++;
+       }
+      if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PS)
+        {
+         if (format == FORMAT_PS || format == N_FORMATS)
+           current_format = n_formats;
+          supported_formats[n_formats] = formats[FORMAT_PS];
+          display_format_names[n_formats] = _(format_names[FORMAT_PS]);
+         n_formats++;
+       }
+    }
+  else
+    {
+      switch (format)
+        {
+          default:
+          case FORMAT_PDF:
+            current_format = FORMAT_PDF;
+            break;
+          case FORMAT_PS:
+            current_format = FORMAT_PS;
+            break;
+          case FORMAT_SVG:
+            current_format = FORMAT_SVG;            
+            break;
+        }
+
+      for (n_formats = 0; n_formats < N_FORMATS; ++n_formats)
+        {
+         supported_formats[n_formats] = formats[n_formats];
+          display_format_names[n_formats] = _(format_names[n_formats]);
+       }
+    }
+
+  uri = output_file_from_settings (settings, supported_formats[current_format]);
+
+  option = gtk_printer_option_new ("gtk-main-page-custom-input", _("File"), 
+                                  GTK_PRINTER_OPTION_TYPE_FILESAVE);
+  gtk_printer_option_set_activates_default (option, TRUE);
+  gtk_printer_option_set (option, uri);
+  g_free (uri);
+  option->group = g_strdup ("GtkPrintDialogExtension");
+  gtk_printer_option_set_add (set, option);
+
+  if (n_formats > 1)
+    {
+      option = gtk_printer_option_new ("output-file-format", _("_Output format"), 
+                                      GTK_PRINTER_OPTION_TYPE_ALTERNATIVE);
+      option->group = g_strdup ("GtkPrintDialogExtension");
+
+      gtk_printer_option_choices_from_array (option, n_formats,
+                                            (char **) supported_formats,
+                                            display_format_names);
+      gtk_printer_option_set (option, supported_formats[current_format]);
+      gtk_printer_option_set_add (set, option);
+
+      set_printer_format_from_option_set (printer, set);
+      format_changed_data = g_new (_OutputFormatChangedData, 1);
+      format_changed_data->printer = printer;
+      format_changed_data->set = set;
+      g_signal_connect_data (option, "changed",
+                            G_CALLBACK (file_printer_output_file_format_changed),
+                            format_changed_data, (GClosureNotify)g_free, 0);
+
+      g_object_unref (option);
+    }
+
+  return set;
+}
+
+static void
+file_printer_get_settings_from_options (GtkPrinter          *printer,
+                                       GtkPrinterOptionSet *options,
+                                       GtkPrintSettings    *settings)
+{
+  GtkPrinterOption *option;
+
+  option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input");
+  gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, option->value);
+
+  option = gtk_printer_option_set_lookup (options, "output-file-format");
+  if (option)
+    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT, option->value);
+
+  option = gtk_printer_option_set_lookup (options, "gtk-n-up");
+  if (option)
+    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value);
+
+  option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout");
+  if (option)
+    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value);
+}
+
+static void
+file_printer_prepare_for_print (GtkPrinter       *printer,
+                               GtkPrintJob      *print_job,
+                               GtkPrintSettings *settings,
+                               GtkPageSetup     *page_setup)
+{
+  gdouble scale;
+  GtkPrintPages pages;
+  GtkPageRange *ranges;
+  gint n_ranges;
+  OutputFormat format;
+
+  pages = gtk_print_settings_get_print_pages (settings);
+  gtk_print_job_set_pages (print_job, pages);
+
+  if (pages == GTK_PRINT_PAGES_RANGES)
+    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
+  else
+    {
+      ranges = NULL;
+      n_ranges = 0;
+    }
+
+  gtk_print_job_set_page_ranges (print_job, ranges, n_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));
+  gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
+  gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (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));
+
+  format = format_from_settings (settings);
+  switch (format)
+    {
+      case FORMAT_PDF:
+       gtk_print_job_set_rotate (print_job, FALSE);
+        break;
+      default:
+      case FORMAT_PS:
+      case FORMAT_SVG:
+       gtk_print_job_set_rotate (print_job, TRUE);
+        break;
+    }
+}
+
+static GList *
+file_printer_list_papers (GtkPrinter *printer)
+{
+  GList *result = NULL;
+  GList *papers, *p;
+  GtkPageSetup *page_setup;
+
+  papers = gtk_paper_size_get_paper_sizes (FALSE);
+
+  for (p = papers; p; p = p->next)
+    {
+      GtkPaperSize *paper_size = p->data;
+
+      page_setup = gtk_page_setup_new ();
+      gtk_page_setup_set_paper_size (page_setup, paper_size);
+      gtk_paper_size_free (paper_size);
+      result = g_list_prepend (result, page_setup);
+    }
+
+  g_list_free (papers);
+
+  return g_list_reverse (result);
+}
+
+static GtkPageSetup *
+file_printer_get_default_page_size (GtkPrinter *printer)
+{
+  GtkPageSetup *result = NULL;
+
+  return result;
+}
diff --git a/modules/printbackends/gtkprintbackendfile.h b/modules/printbackends/gtkprintbackendfile.h
new file mode 100644 (file)
index 0000000..b4ae41a
--- /dev/null
@@ -0,0 +1,39 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendpdf.h: Default implementation of GtkPrintBackend 
+ * for printing to a file
+ * Copyright (C) 2003, 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_FILE_H__
+#define __GTK_PRINT_BACKEND_FILE_H__
+
+#include <glib-object.h>
+#include "gtkprintbackend.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINT_BACKEND_FILE    (gtk_print_backend_file_get_type ())
+#define GTK_PRINT_BACKEND_FILE(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFile))
+#define GTK_IS_PRINT_BACKEND_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_FILE))
+
+typedef struct _GtkPrintBackendFile    GtkPrintBackendFile;
+
+GtkPrintBackend *gtk_print_backend_file_new      (void);
+GType            gtk_print_backend_file_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_PRINT_BACKEND_FILE_H__ */
diff --git a/modules/printbackends/gtkprintbackendlpr.c b/modules/printbackends/gtkprintbackendlpr.c
new file mode 100644 (file)
index 0000000..aefa921
--- /dev/null
@@ -0,0 +1,479 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendlpr.c: LPR implementation of GtkPrintBackend 
+ * for printing to lpr 
+ * Copyright (C) 2006, 2007 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-ps.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtk.h>
+#include "gtkprinter-private.h"
+
+#include "gtkprintbackendlpr.h"
+
+typedef struct _GtkPrintBackendLprClass GtkPrintBackendLprClass;
+
+#define GTK_PRINT_BACKEND_LPR_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass))
+#define GTK_IS_PRINT_BACKEND_LPR_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_LPR))
+#define GTK_PRINT_BACKEND_LPR_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass))
+
+#define _LPR_MAX_CHUNK_SIZE 8192
+
+struct _GtkPrintBackendLprClass
+{
+  GtkPrintBackendClass parent_class;
+};
+
+struct _GtkPrintBackendLpr
+{
+  GtkPrintBackend parent_instance;
+};
+
+static GObjectClass *backend_parent_class;
+
+static void                 gtk_print_backend_lpr_class_init      (GtkPrintBackendLprClass *class);
+static void                 gtk_print_backend_lpr_init            (GtkPrintBackendLpr      *impl);
+static void                 lpr_printer_get_settings_from_options (GtkPrinter              *printer,
+                                                                  GtkPrinterOptionSet     *options,
+                                                                  GtkPrintSettings        *settings);
+static GtkPrinterOptionSet *lpr_printer_get_options               (GtkPrinter              *printer,
+                                                                  GtkPrintSettings        *settings,
+                                                                  GtkPageSetup            *page_setup,
+                                                                  GtkPrintCapabilities     capabilities);
+static void                 lpr_printer_prepare_for_print         (GtkPrinter              *printer,
+                                                                  GtkPrintJob             *print_job,
+                                                                  GtkPrintSettings        *settings,
+                                                                  GtkPageSetup            *page_setup);
+static cairo_surface_t *    lpr_printer_create_cairo_surface      (GtkPrinter              *printer,
+                                                                  GtkPrintSettings        *settings,
+                                                                  gdouble                  width,
+                                                                  gdouble                  height,
+                                                                  GIOChannel              *cache_io);
+static void                 gtk_print_backend_lpr_print_stream    (GtkPrintBackend         *print_backend,
+                                                                  GtkPrintJob             *job,
+                                                                  GIOChannel              *data_io,
+                                                                  GtkPrintJobCompleteFunc  callback,
+                                                                  gpointer                 user_data,
+                                                                  GDestroyNotify           dnotify);
+
+G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendLpr, gtk_print_backend_lpr, GTK_TYPE_PRINT_BACKEND)
+
+void
+g_io_module_load (GIOModule *module)
+{
+  g_type_module_use (G_TYPE_MODULE (module));
+
+  gtk_print_backend_lpr_register_type (G_TYPE_MODULE (module));
+
+  g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
+                                  GTK_TYPE_PRINT_BACKEND_LPR,
+                                  "lpr",
+                                  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_lpr_new:
+ *
+ * Creates a new #GtkPrintBackendLpr object. #GtkPrintBackendLpr
+ * implements the #GtkPrintBackend interface with direct access to
+ * the filesystem using Unix/Linux API calls
+ *
+ * Returns: the new #GtkPrintBackendLpr object
+ **/
+GtkPrintBackend *
+gtk_print_backend_lpr_new (void)
+{
+  return g_object_new (GTK_TYPE_PRINT_BACKEND_LPR, NULL);
+}
+
+static void
+gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class)
+{
+  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
+  
+  backend_parent_class = g_type_class_peek_parent (class);
+
+  backend_class->print_stream = gtk_print_backend_lpr_print_stream;
+  backend_class->printer_create_cairo_surface = lpr_printer_create_cairo_surface;
+  backend_class->printer_get_options = lpr_printer_get_options;
+  backend_class->printer_get_settings_from_options = lpr_printer_get_settings_from_options;
+  backend_class->printer_prepare_for_print = lpr_printer_prepare_for_print;
+}
+
+static void
+gtk_print_backend_lpr_class_finalize (GtkPrintBackendLprClass *class)
+{
+}
+
+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;
+
+  GTK_NOTE (PRINTING,
+            g_print ("LPR Backend: Writting %i byte chunk to temp file\n", length));
+
+  while (length > 0) 
+    {
+      g_io_channel_write_chars (io, (const gchar*)data, length, &written, &error);
+
+      if (error != NULL)
+       {
+         GTK_NOTE (PRINTING,
+                     g_print ("LPR Backend: Error writting to temp file, %s\n", error->message));
+
+          g_error_free (error);
+         return CAIRO_STATUS_WRITE_ERROR;
+       }    
+
+      GTK_NOTE (PRINTING,
+                g_print ("LPR Backend: Wrote %" G_GSIZE_FORMAT " bytes to temp file\n", written));
+
+      data += written;
+      length -= written;
+    }
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+lpr_printer_create_cairo_surface (GtkPrinter       *printer,
+                                 GtkPrintSettings *settings,
+                                 gdouble           width, 
+                                 gdouble           height,
+                                 GIOChannel       *cache_io)
+{
+  cairo_surface_t *surface;
+  
+  surface = cairo_ps_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;
+  gpointer user_data;
+  GDestroyNotify dnotify;
+
+  GIOChannel *in;
+} _PrintStreamData;
+
+static void
+lpr_print_cb (GtkPrintBackendLpr *print_backend,
+              GError             *error,
+              gpointer            user_data)
+{
+  _PrintStreamData *ps = (_PrintStreamData *) user_data;
+
+  if (ps->in != NULL) 
+    g_io_channel_unref (ps->in);
+
+  if (ps->callback)
+    ps->callback (ps->job, ps->user_data, error);
+
+  if (ps->dnotify)
+    ps->dnotify (ps->user_data);
+
+  gtk_print_job_set_status (ps->job, 
+                           error ? GTK_PRINT_STATUS_FINISHED_ABORTED 
+                                 : GTK_PRINT_STATUS_FINISHED);
+
+  if (ps->job)
+    g_object_unref (ps->job);
+  
+  g_free (ps);
+}
+
+static gboolean
+lpr_write (GIOChannel   *source,
+           GIOCondition  con,
+           gpointer      user_data)
+{
+  gchar buf[_LPR_MAX_CHUNK_SIZE];
+  gsize bytes_read;
+  GError *error;
+  GIOStatus status;
+  _PrintStreamData *ps = (_PrintStreamData *) user_data;
+
+  error = NULL;
+
+  status = 
+    g_io_channel_read_chars (source,
+                             buf,
+                             _LPR_MAX_CHUNK_SIZE,
+                             &bytes_read,
+                             &error);
+
+  if (status != G_IO_STATUS_ERROR)
+    {
+      gsize bytes_written;
+
+      g_io_channel_write_chars (ps->in,
+                                buf, 
+                               bytes_read, 
+                               &bytes_written, 
+                               &error);
+    }
+
+  if (error != NULL || status == G_IO_STATUS_EOF)
+    {
+      lpr_print_cb (GTK_PRINT_BACKEND_LPR (ps->backend), 
+                   error, user_data);
+
+
+      if (error != NULL)
+        {
+          GTK_NOTE (PRINTING,
+                    g_print ("LPR Backend: %s\n", error->message));
+
+          g_error_free (error);
+        } 
+
+      return FALSE;
+    }
+
+  GTK_NOTE (PRINTING,
+            g_print ("LPR Backend: Writting %" G_GSIZE_FORMAT " byte chunk to lpr pipe\n", bytes_read));
+
+
+  return TRUE;
+}
+
+#define LPR_COMMAND "lpr"
+
+static void
+gtk_print_backend_lpr_print_stream (GtkPrintBackend        *print_backend,
+                                   GtkPrintJob            *job,
+                                   GIOChannel             *data_io,
+                                   GtkPrintJobCompleteFunc callback,
+                                   gpointer                user_data,
+                                   GDestroyNotify          dnotify)
+{
+  GError *print_error = NULL;
+  _PrintStreamData *ps;
+  GtkPrintSettings *settings;
+  gint argc;
+  gint in_fd;
+  gchar **argv = NULL;
+  const char *cmd_line;
+
+  settings = gtk_print_job_get_settings (job);
+
+  cmd_line = gtk_print_settings_get (settings, "lpr-commandline");
+  if (cmd_line == NULL)
+    cmd_line = LPR_COMMAND;
+
+  ps = g_new0 (_PrintStreamData, 1);
+  ps->callback = callback;
+  ps->user_data = user_data;
+  ps->dnotify = dnotify;
+  ps->job = g_object_ref (job);
+  ps->in = NULL;
+
+ /* spawn lpr with pipes and pipe ps file to lpr */
+  if (!g_shell_parse_argv (cmd_line, &argc, &argv, &print_error))
+    goto out;
+
+  if (!g_spawn_async_with_pipes (NULL,
+                                 argv,
+                                 NULL,
+                                 G_SPAWN_SEARCH_PATH,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 &in_fd,
+                                 NULL,
+                                 NULL,
+                                 &print_error))
+      goto out;
+
+  ps->in = g_io_channel_unix_new (in_fd);
+
+  g_io_channel_set_encoding (ps->in, NULL, &print_error);
+  if (print_error != NULL)
+    {
+      if (ps->in != NULL)
+        g_io_channel_unref (ps->in);
+
+      goto out;
+    }
+
+  g_io_channel_set_close_on_unref (ps->in, TRUE);
+
+  g_io_add_watch (data_io,
+                  G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
+                  (GIOFunc) lpr_write,
+                  ps);
+
+ out:
+  if (argv != NULL)
+    g_strfreev (argv);
+
+  if (print_error != NULL)
+    {
+      lpr_print_cb (GTK_PRINT_BACKEND_LPR (print_backend),
+                   print_error, ps);
+      g_error_free (print_error);
+    }
+}
+
+static void
+gtk_print_backend_lpr_init (GtkPrintBackendLpr *backend)
+{
+  GtkPrinter *printer;
+
+  printer = gtk_printer_new (_("Print to LPR"),
+                            GTK_PRINT_BACKEND (backend),
+                            TRUE); 
+  gtk_printer_set_has_details (printer, TRUE);
+  gtk_printer_set_icon_name (printer, "printer");
+  gtk_printer_set_is_active (printer, TRUE);
+  gtk_printer_set_is_default (printer, TRUE);
+
+  gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer);
+  g_object_unref (printer);
+  gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
+}
+
+static GtkPrinterOptionSet *
+lpr_printer_get_options (GtkPrinter           *printer,
+                        GtkPrintSettings     *settings,
+                        GtkPageSetup         *page_setup,
+                        GtkPrintCapabilities  capabilities)
+{
+  GtkPrinterOptionSet *set;
+  GtkPrinterOption *option;
+  const char *command;
+  char *n_up[] = {"1", "2", "4", "6", "9", "16" };
+
+  set = gtk_printer_option_set_new ();
+
+  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),
+                                        n_up, n_up);
+  gtk_printer_option_set (option, "1");
+  gtk_printer_option_set_add (set, option);
+  g_object_unref (option);
+
+  option = gtk_printer_option_new ("gtk-main-page-custom-input", _("Command Line"), GTK_PRINTER_OPTION_TYPE_STRING);
+  gtk_printer_option_set_activates_default (option, TRUE);
+  option->group = g_strdup ("GtkPrintDialogExtension");
+  if (settings != NULL &&
+      (command = gtk_print_settings_get (settings, "lpr-commandline"))!= NULL)
+    gtk_printer_option_set (option, command);
+  else
+    gtk_printer_option_set (option, LPR_COMMAND);
+  gtk_printer_option_set_add (set, option);
+    
+  return set;
+}
+
+static void
+lpr_printer_get_settings_from_options (GtkPrinter          *printer,
+                                      GtkPrinterOptionSet *options,
+                                      GtkPrintSettings    *settings)
+{
+  GtkPrinterOption *option;
+
+  option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input");
+  if (option)
+    gtk_print_settings_set (settings, "lpr-commandline", option->value);
+
+  option = gtk_printer_option_set_lookup (options, "gtk-n-up");
+  if (option)
+    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value);
+
+  option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout");
+  if (option)
+    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value);
+}
+
+static void
+lpr_printer_prepare_for_print (GtkPrinter       *printer,
+                              GtkPrintJob      *print_job,
+                              GtkPrintSettings *settings,
+                              GtkPageSetup     *page_setup)
+{
+  double scale;
+  GtkPrintPages pages;
+  GtkPageRange *ranges;
+  gint n_ranges;
+
+  pages = gtk_print_settings_get_print_pages (settings);
+  gtk_print_job_set_pages (print_job, pages);
+
+  if (pages == GTK_PRINT_PAGES_RANGES)
+    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
+  else
+    {
+      ranges = NULL;
+      n_ranges = 0;
+    }
+
+  gtk_print_job_set_page_ranges (print_job, ranges, n_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));
+  gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
+  gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (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);
+}
diff --git a/modules/printbackends/gtkprintbackendlpr.h b/modules/printbackends/gtkprintbackendlpr.h
new file mode 100644 (file)
index 0000000..0ba195b
--- /dev/null
@@ -0,0 +1,41 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendlpr.h: LPR implementation of GtkPrintBackend 
+ * for printing to lpr 
+ * Copyright (C) 2006, 2007 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_LPR_H__
+#define __GTK_PRINT_BACKEND_LPR_H__
+
+#include <glib-object.h>
+#include "gtkprintbackend.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINT_BACKEND_LPR            (gtk_print_backend_lpr_get_type ())
+#define GTK_PRINT_BACKEND_LPR(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLpr))
+#define GTK_IS_PRINT_BACKEND_LPR(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_LPR))
+
+typedef struct _GtkPrintBackendLpr      GtkPrintBackendLpr;
+
+GtkPrintBackend *gtk_print_backend_lpr_new      (void);
+GType          gtk_print_backend_lpr_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_PRINT_BACKEND_LPR_H__ */
+
+
diff --git a/modules/printbackends/gtkprintercloudprint.c b/modules/printbackends/gtkprintercloudprint.c
new file mode 100644 (file)
index 0000000..4f299fd
--- /dev/null
@@ -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
+ *
+ * Returns: 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/gtkprintercloudprint.h b/modules/printbackends/gtkprintercloudprint.h
new file mode 100644 (file)
index 0000000..564fb15
--- /dev/null
@@ -0,0 +1,43 @@
+/* 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))
+
+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__ */
diff --git a/modules/printbackends/gtkprintercups.c b/modules/printbackends/gtkprintercups.c
new file mode 100644 (file)
index 0000000..525382a
--- /dev/null
@@ -0,0 +1,650 @@
+/* GtkPrinterCupsCups
+ * Copyright (C) 2006 John (J5) Palmieri  <johnp@redhat.com>
+ * Copyright (C) 2011 Richard Hughes <rhughes@redhat.com>
+ *
+ * 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>
+
+#ifdef HAVE_COLORD
+#include <colord.h>
+#endif
+
+#include "gtkintl.h"
+#include "gtkprintercups.h"
+
+enum {
+  PROP_0,
+  PROP_PROFILE_TITLE
+};
+
+static void gtk_printer_cups_init       (GtkPrinterCups      *printer);
+static void gtk_printer_cups_class_init (GtkPrinterCupsClass *class);
+static void gtk_printer_cups_finalize   (GObject             *object);
+
+static GtkPrinterClass *gtk_printer_cups_parent_class;
+static GType gtk_printer_cups_type = 0;
+
+static void gtk_printer_cups_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec);
+static void gtk_printer_cups_get_property (GObject      *object,
+                                           guint         prop_id,
+                                           GValue       *value,
+                                           GParamSpec   *pspec);
+
+void 
+gtk_printer_cups_register_type (GTypeModule *module)
+{
+  const GTypeInfo object_info =
+  {
+    sizeof (GtkPrinterCupsClass),
+    (GBaseInitFunc) NULL,
+    (GBaseFinalizeFunc) NULL,
+    (GClassInitFunc) gtk_printer_cups_class_init,
+    NULL,           /* class_finalize */
+    NULL,           /* class_data */
+    sizeof (GtkPrinterCups),
+    0,              /* n_preallocs */
+    (GInstanceInitFunc) gtk_printer_cups_init,
+  };
+
+ gtk_printer_cups_type = g_type_module_register_type (module,
+                                                      GTK_TYPE_PRINTER,
+                                                      "GtkPrinterCups",
+                                                      &object_info, 0);
+}
+
+GType
+gtk_printer_cups_get_type (void)
+{
+  return gtk_printer_cups_type;
+}
+
+static void
+gtk_printer_cups_class_init (GtkPrinterCupsClass *class)
+{
+  GObjectClass *object_class = (GObjectClass *) class;
+
+  object_class->finalize = gtk_printer_cups_finalize;
+  object_class->set_property = gtk_printer_cups_set_property;
+  object_class->get_property = gtk_printer_cups_get_property;
+
+  gtk_printer_cups_parent_class = g_type_class_peek_parent (class);
+
+  g_object_class_install_property (G_OBJECT_CLASS (class),
+                                   PROP_PROFILE_TITLE,
+                                   g_param_spec_string ("profile-title",
+                                                        P_("Color Profile Title"),
+                                                        P_("The title of the color profile to use"),
+                                                        "",
+                                                        G_PARAM_READABLE));
+}
+
+static void
+gtk_printer_cups_init (GtkPrinterCups *printer)
+{
+  printer->device_uri = NULL;
+  printer->original_device_uri = NULL;
+  printer->printer_uri = NULL;
+  printer->state = 0;
+  printer->hostname = NULL;
+  printer->port = 0;
+  printer->ppd_name = NULL;
+  printer->ppd_file = NULL;
+  printer->default_cover_before = NULL;
+  printer->default_cover_after = NULL;
+  printer->remote = FALSE;
+  printer->get_remote_ppd_poll = 0;
+  printer->get_remote_ppd_attempts = 0;
+  printer->remote_cups_connection_test = NULL;
+  printer->auth_info_required = NULL;
+  printer->default_number_up = 1;
+#ifdef HAVE_CUPS_API_1_6
+  printer->avahi_browsed = FALSE;
+  printer->avahi_name = NULL;
+  printer->avahi_type = NULL;
+  printer->avahi_domain = NULL;
+#endif
+  printer->ipp_version_major = 1;
+  printer->ipp_version_minor = 1;
+  printer->supports_copies = FALSE;
+  printer->supports_collate = FALSE;
+  printer->supports_number_up = FALSE;
+  printer->media_default = NULL;
+  printer->media_supported = NULL;
+  printer->media_size_supported = NULL;
+  printer->media_bottom_margin_default = 0;
+  printer->media_top_margin_default = 0;
+  printer->media_left_margin_default = 0;
+  printer->media_right_margin_default = 0;
+  printer->media_margin_default_set = FALSE;
+  printer->sides_default = NULL;
+  printer->sides_supported = NULL;
+  printer->number_of_covers = 0;
+  printer->covers = NULL;
+  printer->output_bin_default = NULL;
+  printer->output_bin_supported = NULL;
+}
+
+static void
+gtk_printer_cups_finalize (GObject *object)
+{
+  GtkPrinterCups *printer;
+
+  g_return_if_fail (object != NULL);
+
+  printer = GTK_PRINTER_CUPS (object);
+
+  g_free (printer->device_uri);
+  g_free (printer->original_device_uri);
+  g_free (printer->printer_uri);
+  g_free (printer->hostname);
+  g_free (printer->ppd_name);
+  g_free (printer->default_cover_before);
+  g_free (printer->default_cover_after);
+  g_strfreev (printer->auth_info_required);
+
+#ifdef HAVE_COLORD
+  if (printer->colord_cancellable)
+    {
+      g_cancellable_cancel (printer->colord_cancellable);
+      g_object_unref (printer->colord_cancellable);
+    }
+  g_free (printer->colord_title);
+  g_free (printer->colord_qualifier);
+  if (printer->colord_client)
+    g_object_unref (printer->colord_client);
+  if (printer->colord_device)
+    g_object_unref (printer->colord_device);
+  if (printer->colord_profile)
+    g_object_unref (printer->colord_profile);
+#endif
+
+#ifdef HAVE_CUPS_API_1_6
+  g_free (printer->avahi_name);
+  g_free (printer->avahi_type);
+  g_free (printer->avahi_domain);
+#endif
+
+  g_strfreev (printer->covers);
+
+  if (printer->ppd_file)
+    ppdClose (printer->ppd_file);
+
+  g_free (printer->media_default);
+  g_list_free_full (printer->media_supported, g_free);
+  g_list_free_full (printer->media_size_supported, g_free);
+
+  g_free (printer->sides_default);
+  g_list_free_full (printer->sides_supported, g_free);
+
+  g_free (printer->output_bin_default);
+  g_list_free_full (printer->output_bin_supported, g_free);
+
+  if (printer->get_remote_ppd_poll > 0)
+    g_source_remove (printer->get_remote_ppd_poll);
+  printer->get_remote_ppd_attempts = 0;
+
+  gtk_cups_connection_test_free (printer->remote_cups_connection_test);
+
+  G_OBJECT_CLASS (gtk_printer_cups_parent_class)->finalize (object);
+}
+
+static void
+gtk_printer_cups_set_property (GObject         *object,
+                               guint            prop_id,
+                               const GValue    *value,
+                               GParamSpec      *pspec)
+{
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_printer_cups_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+#ifdef HAVE_COLORD
+  GtkPrinterCups *printer = GTK_PRINTER_CUPS (object);
+#endif
+
+  switch (prop_id)
+    {
+    case PROP_PROFILE_TITLE:
+#ifdef HAVE_COLORD
+      if (printer->colord_title)
+        g_value_set_string (value, printer->colord_title);
+      else
+        g_value_set_static_string (value, "");
+#else
+      g_value_set_static_string (value, NULL);
+#endif
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+#ifdef HAVE_COLORD
+
+static void
+colord_update_ui_from_settings (GtkPrinterCups *printer)
+{
+  const gchar *title = NULL;
+
+  /* not yet connected to colord */
+  if (printer->colord_client == NULL)
+    goto out;
+  if (!cd_client_get_connected (printer->colord_client))
+    goto out;
+
+  /* failed to get a colord device for the printer */
+  if (printer->colord_device == NULL)
+    {
+      /* TRANSLATORS: when we're running an old CUPS, and
+       * it hasn't registered the device with colord */
+      title = _("Color management unavailable");
+      goto out;
+    }
+
+  /* when colord prevents us from connecting (should not happen) */
+  if (!cd_device_get_connected (printer->colord_device))
+    goto out;
+
+  /* failed to get a colord device for the printer */
+  if (printer->colord_profile == NULL)
+    {
+      /* TRANSLATORS: when there is no color profile available */
+      title = _("No profile available");
+      goto out;
+    }
+
+  /* when colord prevents us from connecting (should not happen) */
+  if (!cd_profile_get_connected (printer->colord_profile))
+    goto out;
+  title = cd_profile_get_title (printer->colord_profile);
+  if (title == NULL)
+    {
+      /* TRANSLATORS: when the color profile has no title */
+      title = _("Unspecified profile");
+      goto out;
+    }
+
+out:
+  /* SUCCESS! */
+  if (g_strcmp0 (title, printer->colord_title) != 0)
+    {
+      g_free (printer->colord_title);
+      printer->colord_title = g_strdup (title);
+      g_object_notify (G_OBJECT (printer), "profile-title");
+    }
+  return;
+}
+
+static void
+colord_client_profile_connect_cb (GObject *source_object,
+                                  GAsyncResult *res,
+                                  gpointer user_data)
+{
+  gboolean ret;
+  GError *error = NULL;
+  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
+
+  ret = cd_profile_connect_finish (CD_PROFILE (source_object),
+                                   res,
+                                   &error);
+  if (!ret)
+    {
+      g_warning ("failed to get properties from the profile: %s",
+                 error->message);
+      g_error_free (error);
+    }
+
+  /* update the UI */
+  colord_update_ui_from_settings (printer);
+
+  g_object_unref (printer);
+}
+
+static void
+colord_client_device_get_profile_for_qualifiers_cb (GObject *source_object,
+                                                    GAsyncResult *res,
+                                                    gpointer user_data)
+{
+  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
+  GError *error = NULL;
+
+  printer->colord_profile = cd_device_get_profile_for_qualifiers_finish (printer->colord_device,
+                                                                         res,
+                                                                         &error);
+  if (printer->colord_profile == NULL)
+    {
+      /* not having a profile for a qualifier is not a warning */
+      g_debug ("no profile for device %s: %s",
+               cd_device_get_id (printer->colord_device),
+               error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  /* get details about the profile */
+  cd_profile_connect (printer->colord_profile,
+                      printer->colord_cancellable,
+                      colord_client_profile_connect_cb,
+                      g_object_ref (printer));
+out:
+  /* update the UI */
+  colord_update_ui_from_settings (printer);
+
+  g_object_unref (printer);
+}
+
+void
+gtk_printer_cups_update_settings (GtkPrinterCups *printer,
+                                  GtkPrintSettings *settings,
+                                  GtkPrinterOptionSet *set)
+{
+  gchar *qualifier = NULL;
+  gchar **qualifiers = NULL;
+  GtkPrinterOption *option;
+  const gchar *format[3];
+
+  /* nothing set yet */
+  if (printer->colord_device == NULL)
+    goto out;
+  if (!cd_device_get_connected (printer->colord_device))
+    goto out;
+
+  /* cupsICCQualifier1 */
+  option = gtk_printer_option_set_lookup (set, "cups-ColorSpace");
+  if (option == NULL)
+    option = gtk_printer_option_set_lookup (set, "cups-ColorModel");
+  if (option != NULL)
+    format[0] = option->value;
+  else
+    format[0] = "*";
+
+  /* cupsICCQualifier2 */
+  option = gtk_printer_option_set_lookup (set, "cups-OutputMode");
+  if (option != NULL)
+    format[1] = option->value;
+  else
+    format[1] = "*";
+
+  /* cupsICCQualifier3 */
+  option = gtk_printer_option_set_lookup (set, "cups-Resolution");
+  if (option != NULL)
+    format[2] = option->value;
+  else
+    format[2] = "*";
+
+  /* get profile for the device given the qualifier */
+  qualifier = g_strdup_printf ("%s.%s.%s,%s.%s.*,%s.*.*",
+                               format[0], format[1], format[2],
+                               format[0], format[1],
+                               format[0]);
+
+  /* only requery colord if the option that was changed would give
+   * us a different profile result */
+  if (g_strcmp0 (qualifier, printer->colord_qualifier) == 0)
+    goto out;
+
+  qualifiers = g_strsplit (qualifier, ",", -1);
+  cd_device_get_profile_for_qualifiers (printer->colord_device,
+                                        (const gchar **) qualifiers,
+                                        printer->colord_cancellable,
+                                        colord_client_device_get_profile_for_qualifiers_cb,
+                                        g_object_ref (printer));
+
+  /* save for the future */
+  g_free (printer->colord_qualifier);
+  printer->colord_qualifier = g_strdup (qualifier);
+
+  /* update the UI */
+  colord_update_ui_from_settings (printer);
+out:
+  g_free (qualifier);
+  g_strfreev (qualifiers);
+}
+
+static void
+colord_client_device_connect_cb (GObject *source_object,
+                                 GAsyncResult *res,
+                                 gpointer user_data)
+{
+  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
+  gboolean ret;
+  GError *error = NULL;
+
+  /* get details about the device */
+  ret = cd_device_connect_finish (CD_DEVICE (source_object), res, &error);
+  if (!ret)
+    {
+      g_warning ("failed to get properties from the colord device: %s",
+                 error->message);
+      g_error_free (error);
+      goto out;
+    }
+out:
+  /* update the UI */
+  colord_update_ui_from_settings (printer);
+
+  g_object_unref (printer);
+}
+
+static void
+colord_client_find_device_cb (GObject *source_object,
+                              GAsyncResult *res,
+                              gpointer user_data)
+{
+  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
+  GError *error = NULL;
+
+  /* get the new device */
+  printer->colord_device = cd_client_find_device_finish (printer->colord_client,
+                                               res,
+                                               &error);
+  if (printer->colord_device == NULL)
+    {
+      g_warning ("failed to get find a colord device: %s",
+                 error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  /* get details about the device */
+  g_cancellable_reset (printer->colord_cancellable);
+  cd_device_connect (printer->colord_device,
+                     printer->colord_cancellable,
+                     colord_client_device_connect_cb,
+                     g_object_ref (printer));
+out:
+  /* update the UI */
+  colord_update_ui_from_settings (printer);
+
+  g_object_unref (printer);
+}
+
+static void
+colord_update_device (GtkPrinterCups *printer)
+{
+  gchar *colord_device_id = NULL;
+
+  /* not yet connected to the daemon */
+  if (!cd_client_get_connected (printer->colord_client))
+    goto out;
+
+  /* not yet assigned a printer */
+  if (printer->ppd_file == NULL)
+    goto out;
+
+  /* old cached profile no longer valid */
+  if (printer->colord_profile)
+    {
+      g_object_unref (printer->colord_profile);
+      printer->colord_profile = NULL;
+    }
+
+  /* old cached device no longer valid */
+  if (printer->colord_device)
+    {
+      g_object_unref (printer->colord_device);
+      printer->colord_device = NULL;
+    }
+
+  /* generate a known ID */
+  colord_device_id = g_strdup_printf ("cups-%s", gtk_printer_get_name (GTK_PRINTER (printer)));
+
+  g_cancellable_reset (printer->colord_cancellable);
+  cd_client_find_device (printer->colord_client,
+                         colord_device_id,
+                         printer->colord_cancellable,
+                         colord_client_find_device_cb,
+                         g_object_ref (printer));
+out:
+  g_free (colord_device_id);
+
+  /* update the UI */
+  colord_update_ui_from_settings (printer);
+}
+
+static void
+colord_client_connect_cb (GObject *source_object,
+                          GAsyncResult *res,
+                          gpointer user_data)
+{
+  gboolean ret;
+  GError *error = NULL;
+  GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data);
+  static gboolean colord_warned = FALSE;
+
+  ret = cd_client_connect_finish (CD_CLIENT (source_object),
+                                  res, &error);
+  if (!ret)
+    {
+      if (!colord_warned)
+        {
+          g_warning ("failed to contact colord: %s", error->message);
+          colord_warned = TRUE;
+        }
+      g_error_free (error);
+    }
+
+  /* refresh the device */
+  colord_update_device (printer);
+
+  g_object_unref (printer);
+}
+
+static void
+colord_printer_details_aquired_cb (GtkPrinterCups *printer,
+                                   gboolean success,
+                                   gpointer user_data)
+{
+  /* refresh the device */
+  if (printer->colord_client)
+    colord_update_device (printer);
+}
+#endif
+
+/**
+ * gtk_printer_cups_new:
+ *
+ * Creates a new #GtkPrinterCups.
+ *
+ * Returns: a new #GtkPrinterCups
+ *
+ * Since: 2.10
+ **/
+GtkPrinterCups *
+gtk_printer_cups_new (const char      *name,
+                      GtkPrintBackend *backend,
+                      gpointer         colord_client)
+{
+  GObject *result;
+  gboolean accepts_pdf;
+  GtkPrinterCups *printer;
+
+#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
+  accepts_pdf = TRUE;
+#else
+  accepts_pdf = FALSE;
+#endif
+
+  result = g_object_new (GTK_TYPE_PRINTER_CUPS,
+                        "name", name,
+                        "backend", backend,
+                        "is-virtual", FALSE,
+                        "accepts-pdf", accepts_pdf,
+                         NULL);
+  printer = GTK_PRINTER_CUPS (result);
+
+#ifdef HAVE_COLORD
+  /* connect to colord */
+  if (colord_client != NULL)
+    {
+      printer->colord_cancellable = g_cancellable_new ();
+      printer->colord_client = g_object_ref (CD_CLIENT (colord_client));
+      cd_client_connect (printer->colord_client,
+                         printer->colord_cancellable,
+                         colord_client_connect_cb,
+                         g_object_ref (printer));
+    }
+
+    /* update the device when we read the PPD */
+    g_signal_connect (printer, "details-acquired",
+                      G_CALLBACK (colord_printer_details_aquired_cb),
+                      printer);
+#endif
+
+  /*
+   * IPP version 1.1 has to be supported
+   * by all implementations according to rfc 2911
+   */
+  printer->ipp_version_major = 1;
+  printer->ipp_version_minor = 1;
+
+  return printer;
+}
+
+ppd_file_t *
+gtk_printer_cups_get_ppd (GtkPrinterCups *printer)
+{
+  return printer->ppd_file;
+}
+
+const gchar *
+gtk_printer_cups_get_ppd_name (GtkPrinterCups  *printer)
+{
+  const gchar *result;
+
+  result = printer->ppd_name;
+
+  if (result == NULL)
+    result = gtk_printer_get_name (GTK_PRINTER (printer));
+
+  return result;
+}
diff --git a/modules/printbackends/gtkprintercups.h b/modules/printbackends/gtkprintercups.h
new file mode 100644 (file)
index 0000000..f26bbab
--- /dev/null
@@ -0,0 +1,130 @@
+/* GtkPrinterCups
+ * Copyright (C) 2006 John (J5) Palmieri <johnp@redhat.com>
+ *
+ * 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_CUPS_H__
+#define __GTK_PRINTER_CUPS_H__
+
+#include <glib-object.h>
+#include <cups/cups.h>
+#include <cups/ppd.h>
+#include "gtkcupsutils.h"
+
+#include <gtk/gtkunixprint.h>
+#include <gtk/gtkprinter-private.h>
+
+#ifdef HAVE_COLORD
+#include <colord.h>
+#endif
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINTER_CUPS                  (gtk_printer_cups_get_type ())
+#define GTK_PRINTER_CUPS(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCups))
+#define GTK_PRINTER_CUPS_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass))
+#define GTK_IS_PRINTER_CUPS(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CUPS))
+#define GTK_IS_PRINTER_CUPS_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CUPS))
+#define GTK_PRINTER_CUPS_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass))
+
+typedef struct _GtkPrinterCups         GtkPrinterCups;
+typedef struct _GtkPrinterCupsClass     GtkPrinterCupsClass;
+typedef struct _GtkPrinterCupsPrivate   GtkPrinterCupsPrivate;
+
+struct _GtkPrinterCups
+{
+  GtkPrinter parent_instance;
+
+  gchar *device_uri;
+  gchar *original_device_uri;
+  gchar *printer_uri;
+  gchar *hostname;
+  gint port;
+  gchar **auth_info_required;
+
+  ipp_pstate_t state;
+  gboolean reading_ppd;
+  gchar      *ppd_name;
+  ppd_file_t *ppd_file;
+
+  gchar    *media_default;
+  GList    *media_supported;
+  GList    *media_size_supported;
+  gint      media_bottom_margin_default;
+  gint      media_top_margin_default;
+  gint      media_left_margin_default;
+  gint      media_right_margin_default;
+  gboolean  media_margin_default_set;
+  gchar    *sides_default;
+  GList    *sides_supported;
+  gchar    *output_bin_default;
+  GList    *output_bin_supported;
+
+  gchar  *default_cover_before;
+  gchar  *default_cover_after;
+
+  gint    default_number_up;
+
+  gboolean remote;
+  guint get_remote_ppd_poll;
+  gint  get_remote_ppd_attempts;
+  GtkCupsConnectionTest *remote_cups_connection_test;
+#ifdef HAVE_COLORD
+  CdClient     *colord_client;
+  CdDevice     *colord_device;
+  CdProfile    *colord_profile;
+  GCancellable *colord_cancellable;
+  gchar        *colord_title;
+  gchar        *colord_qualifier;
+#endif
+#ifdef HAVE_CUPS_API_1_6
+  gboolean  avahi_browsed;
+  gchar    *avahi_name;
+  gchar    *avahi_type;
+  gchar    *avahi_domain;
+#endif
+  guchar ipp_version_major;
+  guchar ipp_version_minor;
+  gboolean supports_copies;
+  gboolean supports_collate;
+  gboolean supports_number_up;
+  char   **covers;
+  int      number_of_covers;
+};
+
+struct _GtkPrinterCupsClass
+{
+  GtkPrinterClass parent_class;
+
+};
+
+GType                    gtk_printer_cups_get_type      (void) G_GNUC_CONST;
+void                     gtk_printer_cups_register_type (GTypeModule     *module);
+
+GtkPrinterCups          *gtk_printer_cups_new           (const char      *name,
+                                                         GtkPrintBackend *backend,
+                                                         gpointer         colord_client);
+ppd_file_t             *gtk_printer_cups_get_ppd       (GtkPrinterCups  *printer);
+const gchar            *gtk_printer_cups_get_ppd_name  (GtkPrinterCups  *printer);
+
+#ifdef HAVE_COLORD
+void                     gtk_printer_cups_update_settings (GtkPrinterCups *printer,
+                                                         GtkPrintSettings *settings,
+                                                         GtkPrinterOptionSet *set);
+#endif
+
+G_END_DECLS
+
+#endif /* __GTK_PRINTER_CUPS_H__ */
diff --git a/modules/printbackends/lpr/gtkprintbackendlpr.c b/modules/printbackends/lpr/gtkprintbackendlpr.c
deleted file mode 100644 (file)
index aefa921..0000000
+++ /dev/null
@@ -1,479 +0,0 @@
-/* GTK - The GIMP Toolkit
- * gtkprintbackendlpr.c: LPR implementation of GtkPrintBackend 
- * for printing to lpr 
- * Copyright (C) 2006, 2007 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-ps.h>
-
-#include <glib/gi18n-lib.h>
-
-#include <gtk/gtk.h>
-#include "gtkprinter-private.h"
-
-#include "gtkprintbackendlpr.h"
-
-typedef struct _GtkPrintBackendLprClass GtkPrintBackendLprClass;
-
-#define GTK_PRINT_BACKEND_LPR_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass))
-#define GTK_IS_PRINT_BACKEND_LPR_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_LPR))
-#define GTK_PRINT_BACKEND_LPR_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass))
-
-#define _LPR_MAX_CHUNK_SIZE 8192
-
-struct _GtkPrintBackendLprClass
-{
-  GtkPrintBackendClass parent_class;
-};
-
-struct _GtkPrintBackendLpr
-{
-  GtkPrintBackend parent_instance;
-};
-
-static GObjectClass *backend_parent_class;
-
-static void                 gtk_print_backend_lpr_class_init      (GtkPrintBackendLprClass *class);
-static void                 gtk_print_backend_lpr_init            (GtkPrintBackendLpr      *impl);
-static void                 lpr_printer_get_settings_from_options (GtkPrinter              *printer,
-                                                                  GtkPrinterOptionSet     *options,
-                                                                  GtkPrintSettings        *settings);
-static GtkPrinterOptionSet *lpr_printer_get_options               (GtkPrinter              *printer,
-                                                                  GtkPrintSettings        *settings,
-                                                                  GtkPageSetup            *page_setup,
-                                                                  GtkPrintCapabilities     capabilities);
-static void                 lpr_printer_prepare_for_print         (GtkPrinter              *printer,
-                                                                  GtkPrintJob             *print_job,
-                                                                  GtkPrintSettings        *settings,
-                                                                  GtkPageSetup            *page_setup);
-static cairo_surface_t *    lpr_printer_create_cairo_surface      (GtkPrinter              *printer,
-                                                                  GtkPrintSettings        *settings,
-                                                                  gdouble                  width,
-                                                                  gdouble                  height,
-                                                                  GIOChannel              *cache_io);
-static void                 gtk_print_backend_lpr_print_stream    (GtkPrintBackend         *print_backend,
-                                                                  GtkPrintJob             *job,
-                                                                  GIOChannel              *data_io,
-                                                                  GtkPrintJobCompleteFunc  callback,
-                                                                  gpointer                 user_data,
-                                                                  GDestroyNotify           dnotify);
-
-G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendLpr, gtk_print_backend_lpr, GTK_TYPE_PRINT_BACKEND)
-
-void
-g_io_module_load (GIOModule *module)
-{
-  g_type_module_use (G_TYPE_MODULE (module));
-
-  gtk_print_backend_lpr_register_type (G_TYPE_MODULE (module));
-
-  g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
-                                  GTK_TYPE_PRINT_BACKEND_LPR,
-                                  "lpr",
-                                  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_lpr_new:
- *
- * Creates a new #GtkPrintBackendLpr object. #GtkPrintBackendLpr
- * implements the #GtkPrintBackend interface with direct access to
- * the filesystem using Unix/Linux API calls
- *
- * Returns: the new #GtkPrintBackendLpr object
- **/
-GtkPrintBackend *
-gtk_print_backend_lpr_new (void)
-{
-  return g_object_new (GTK_TYPE_PRINT_BACKEND_LPR, NULL);
-}
-
-static void
-gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class)
-{
-  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
-  
-  backend_parent_class = g_type_class_peek_parent (class);
-
-  backend_class->print_stream = gtk_print_backend_lpr_print_stream;
-  backend_class->printer_create_cairo_surface = lpr_printer_create_cairo_surface;
-  backend_class->printer_get_options = lpr_printer_get_options;
-  backend_class->printer_get_settings_from_options = lpr_printer_get_settings_from_options;
-  backend_class->printer_prepare_for_print = lpr_printer_prepare_for_print;
-}
-
-static void
-gtk_print_backend_lpr_class_finalize (GtkPrintBackendLprClass *class)
-{
-}
-
-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;
-
-  GTK_NOTE (PRINTING,
-            g_print ("LPR Backend: Writting %i byte chunk to temp file\n", length));
-
-  while (length > 0) 
-    {
-      g_io_channel_write_chars (io, (const gchar*)data, length, &written, &error);
-
-      if (error != NULL)
-       {
-         GTK_NOTE (PRINTING,
-                     g_print ("LPR Backend: Error writting to temp file, %s\n", error->message));
-
-          g_error_free (error);
-         return CAIRO_STATUS_WRITE_ERROR;
-       }    
-
-      GTK_NOTE (PRINTING,
-                g_print ("LPR Backend: Wrote %" G_GSIZE_FORMAT " bytes to temp file\n", written));
-
-      data += written;
-      length -= written;
-    }
-
-  return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_surface_t *
-lpr_printer_create_cairo_surface (GtkPrinter       *printer,
-                                 GtkPrintSettings *settings,
-                                 gdouble           width, 
-                                 gdouble           height,
-                                 GIOChannel       *cache_io)
-{
-  cairo_surface_t *surface;
-  
-  surface = cairo_ps_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;
-  gpointer user_data;
-  GDestroyNotify dnotify;
-
-  GIOChannel *in;
-} _PrintStreamData;
-
-static void
-lpr_print_cb (GtkPrintBackendLpr *print_backend,
-              GError             *error,
-              gpointer            user_data)
-{
-  _PrintStreamData *ps = (_PrintStreamData *) user_data;
-
-  if (ps->in != NULL) 
-    g_io_channel_unref (ps->in);
-
-  if (ps->callback)
-    ps->callback (ps->job, ps->user_data, error);
-
-  if (ps->dnotify)
-    ps->dnotify (ps->user_data);
-
-  gtk_print_job_set_status (ps->job, 
-                           error ? GTK_PRINT_STATUS_FINISHED_ABORTED 
-                                 : GTK_PRINT_STATUS_FINISHED);
-
-  if (ps->job)
-    g_object_unref (ps->job);
-  
-  g_free (ps);
-}
-
-static gboolean
-lpr_write (GIOChannel   *source,
-           GIOCondition  con,
-           gpointer      user_data)
-{
-  gchar buf[_LPR_MAX_CHUNK_SIZE];
-  gsize bytes_read;
-  GError *error;
-  GIOStatus status;
-  _PrintStreamData *ps = (_PrintStreamData *) user_data;
-
-  error = NULL;
-
-  status = 
-    g_io_channel_read_chars (source,
-                             buf,
-                             _LPR_MAX_CHUNK_SIZE,
-                             &bytes_read,
-                             &error);
-
-  if (status != G_IO_STATUS_ERROR)
-    {
-      gsize bytes_written;
-
-      g_io_channel_write_chars (ps->in,
-                                buf, 
-                               bytes_read, 
-                               &bytes_written, 
-                               &error);
-    }
-
-  if (error != NULL || status == G_IO_STATUS_EOF)
-    {
-      lpr_print_cb (GTK_PRINT_BACKEND_LPR (ps->backend), 
-                   error, user_data);
-
-
-      if (error != NULL)
-        {
-          GTK_NOTE (PRINTING,
-                    g_print ("LPR Backend: %s\n", error->message));
-
-          g_error_free (error);
-        } 
-
-      return FALSE;
-    }
-
-  GTK_NOTE (PRINTING,
-            g_print ("LPR Backend: Writting %" G_GSIZE_FORMAT " byte chunk to lpr pipe\n", bytes_read));
-
-
-  return TRUE;
-}
-
-#define LPR_COMMAND "lpr"
-
-static void
-gtk_print_backend_lpr_print_stream (GtkPrintBackend        *print_backend,
-                                   GtkPrintJob            *job,
-                                   GIOChannel             *data_io,
-                                   GtkPrintJobCompleteFunc callback,
-                                   gpointer                user_data,
-                                   GDestroyNotify          dnotify)
-{
-  GError *print_error = NULL;
-  _PrintStreamData *ps;
-  GtkPrintSettings *settings;
-  gint argc;
-  gint in_fd;
-  gchar **argv = NULL;
-  const char *cmd_line;
-
-  settings = gtk_print_job_get_settings (job);
-
-  cmd_line = gtk_print_settings_get (settings, "lpr-commandline");
-  if (cmd_line == NULL)
-    cmd_line = LPR_COMMAND;
-
-  ps = g_new0 (_PrintStreamData, 1);
-  ps->callback = callback;
-  ps->user_data = user_data;
-  ps->dnotify = dnotify;
-  ps->job = g_object_ref (job);
-  ps->in = NULL;
-
- /* spawn lpr with pipes and pipe ps file to lpr */
-  if (!g_shell_parse_argv (cmd_line, &argc, &argv, &print_error))
-    goto out;
-
-  if (!g_spawn_async_with_pipes (NULL,
-                                 argv,
-                                 NULL,
-                                 G_SPAWN_SEARCH_PATH,
-                                 NULL,
-                                 NULL,
-                                 NULL,
-                                 &in_fd,
-                                 NULL,
-                                 NULL,
-                                 &print_error))
-      goto out;
-
-  ps->in = g_io_channel_unix_new (in_fd);
-
-  g_io_channel_set_encoding (ps->in, NULL, &print_error);
-  if (print_error != NULL)
-    {
-      if (ps->in != NULL)
-        g_io_channel_unref (ps->in);
-
-      goto out;
-    }
-
-  g_io_channel_set_close_on_unref (ps->in, TRUE);
-
-  g_io_add_watch (data_io,
-                  G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
-                  (GIOFunc) lpr_write,
-                  ps);
-
- out:
-  if (argv != NULL)
-    g_strfreev (argv);
-
-  if (print_error != NULL)
-    {
-      lpr_print_cb (GTK_PRINT_BACKEND_LPR (print_backend),
-                   print_error, ps);
-      g_error_free (print_error);
-    }
-}
-
-static void
-gtk_print_backend_lpr_init (GtkPrintBackendLpr *backend)
-{
-  GtkPrinter *printer;
-
-  printer = gtk_printer_new (_("Print to LPR"),
-                            GTK_PRINT_BACKEND (backend),
-                            TRUE); 
-  gtk_printer_set_has_details (printer, TRUE);
-  gtk_printer_set_icon_name (printer, "printer");
-  gtk_printer_set_is_active (printer, TRUE);
-  gtk_printer_set_is_default (printer, TRUE);
-
-  gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer);
-  g_object_unref (printer);
-  gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
-}
-
-static GtkPrinterOptionSet *
-lpr_printer_get_options (GtkPrinter           *printer,
-                        GtkPrintSettings     *settings,
-                        GtkPageSetup         *page_setup,
-                        GtkPrintCapabilities  capabilities)
-{
-  GtkPrinterOptionSet *set;
-  GtkPrinterOption *option;
-  const char *command;
-  char *n_up[] = {"1", "2", "4", "6", "9", "16" };
-
-  set = gtk_printer_option_set_new ();
-
-  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),
-                                        n_up, n_up);
-  gtk_printer_option_set (option, "1");
-  gtk_printer_option_set_add (set, option);
-  g_object_unref (option);
-
-  option = gtk_printer_option_new ("gtk-main-page-custom-input", _("Command Line"), GTK_PRINTER_OPTION_TYPE_STRING);
-  gtk_printer_option_set_activates_default (option, TRUE);
-  option->group = g_strdup ("GtkPrintDialogExtension");
-  if (settings != NULL &&
-      (command = gtk_print_settings_get (settings, "lpr-commandline"))!= NULL)
-    gtk_printer_option_set (option, command);
-  else
-    gtk_printer_option_set (option, LPR_COMMAND);
-  gtk_printer_option_set_add (set, option);
-    
-  return set;
-}
-
-static void
-lpr_printer_get_settings_from_options (GtkPrinter          *printer,
-                                      GtkPrinterOptionSet *options,
-                                      GtkPrintSettings    *settings)
-{
-  GtkPrinterOption *option;
-
-  option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input");
-  if (option)
-    gtk_print_settings_set (settings, "lpr-commandline", option->value);
-
-  option = gtk_printer_option_set_lookup (options, "gtk-n-up");
-  if (option)
-    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value);
-
-  option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout");
-  if (option)
-    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value);
-}
-
-static void
-lpr_printer_prepare_for_print (GtkPrinter       *printer,
-                              GtkPrintJob      *print_job,
-                              GtkPrintSettings *settings,
-                              GtkPageSetup     *page_setup)
-{
-  double scale;
-  GtkPrintPages pages;
-  GtkPageRange *ranges;
-  gint n_ranges;
-
-  pages = gtk_print_settings_get_print_pages (settings);
-  gtk_print_job_set_pages (print_job, pages);
-
-  if (pages == GTK_PRINT_PAGES_RANGES)
-    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
-  else
-    {
-      ranges = NULL;
-      n_ranges = 0;
-    }
-
-  gtk_print_job_set_page_ranges (print_job, ranges, n_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));
-  gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
-  gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (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);
-}
diff --git a/modules/printbackends/lpr/gtkprintbackendlpr.h b/modules/printbackends/lpr/gtkprintbackendlpr.h
deleted file mode 100644 (file)
index 0ba195b..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* GTK - The GIMP Toolkit
- * gtkprintbackendlpr.h: LPR implementation of GtkPrintBackend 
- * for printing to lpr 
- * Copyright (C) 2006, 2007 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_LPR_H__
-#define __GTK_PRINT_BACKEND_LPR_H__
-
-#include <glib-object.h>
-#include "gtkprintbackend.h"
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_PRINT_BACKEND_LPR            (gtk_print_backend_lpr_get_type ())
-#define GTK_PRINT_BACKEND_LPR(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLpr))
-#define GTK_IS_PRINT_BACKEND_LPR(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_LPR))
-
-typedef struct _GtkPrintBackendLpr      GtkPrintBackendLpr;
-
-GtkPrintBackend *gtk_print_backend_lpr_new      (void);
-GType          gtk_print_backend_lpr_get_type (void) G_GNUC_CONST;
-
-G_END_DECLS
-
-#endif /* __GTK_PRINT_BACKEND_LPR_H__ */
-
-
diff --git a/modules/printbackends/lpr/meson.build b/modules/printbackends/lpr/meson.build
deleted file mode 100644 (file)
index 581ca77..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-shared_module('printbackend-lpr',
-              'gtkprintbackendlpr.c',
-              c_args: [
-                '-DGTK_COMPILATION',
-                '-DGTK_DISABLE_DEPRECATION_WARNINGS',
-                '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
-              ],
-              dependencies: libgtk_dep,
-              install_dir: printbackends_install_dir,
-              install : true)
index f8a363a668dafc3b273d89b7787041cab87a70bd..76b5c3789e075973500c618e86b63d6b83bf53e2 100644 (file)
@@ -84,6 +84,68 @@ printbackends_install_dir = join_paths(get_option('libdir'), printbackends_subdi
 
 cdata.set_quoted('GTK_PRINT_BACKENDS', ','.join(print_backends))
 
-foreach print_backend : print_backends
-  subdir(print_backend)
-endforeach
+
+enable_colord = get_option('colord')
+if enable_colord != 'no'
+  want_colord = enable_colord == 'yes'
+  colord_dep = dependency('colord', version: '>= 0.1.9', required: want_colord)
+  cdata.set('HAVE_COLORD', colord_dep.found())
+else
+  colord_dep = []
+endif
+
+if print_backends.contains('cups')
+  shared_module('printbackend-cups',
+                'gtkprintbackendcups.c',
+                'gtkprintercups.c',
+                'gtkcupsutils.c',
+                'gtkcupssecretsutils.c',
+                c_args: [
+                  '-DGTK_COMPILATION',
+                  '-DGTK_DISABLE_DEPRECATION_WARNINGS',
+                  '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
+                ],
+                dependencies: [libgtk_dep, libcups, colord_dep],
+                install_dir: printbackends_install_dir,
+                install : true)
+endif
+
+if print_backends.contains('cloudprint')
+  shared_module('printbackend-cloudprint',
+                'gtkprintbackendcloudprint.c',
+                'gtkprintercloudprint.c',
+                'gtkcloudprintaccount.c',
+                c_args: [
+                  '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
+                 '-DGTK_DISABLE_DEPRECATION_WARNINGS',
+                ],
+                dependencies: [ libgtk_dep, rest_dep, json_glib_dep ],
+                install_dir: printbackends_install_dir,
+                install : true)
+endif
+
+if print_backends.contains('file')
+  shared_module('printbackend-file',
+                'gtkprintbackendfile.c',
+                c_args: [
+                  '-DGTK_COMPILATION',
+                  '-DGTK_DISABLE_DEPRECATION_WARNINGS',
+                  '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
+                ],
+                dependencies: libgtk_dep,
+                install_dir: printbackends_install_dir,
+                install : true)
+endif
+
+if print_backends.contains('lpr')
+  shared_module('printbackend-lpr',
+                'gtkprintbackendlpr.c',
+                c_args: [
+                  '-DGTK_COMPILATION',
+                  '-DGTK_DISABLE_DEPRECATION_WARNINGS',
+                  '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
+                ],
+                dependencies: libgtk_dep,
+                install_dir: printbackends_install_dir,
+                install : true)
+endif
\ No newline at end of file