+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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__ */
+++ /dev/null
-/* 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);
-}
+++ /dev/null
-/* 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__ */
+++ /dev/null
-/* 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;
- }
-}
+++ /dev/null
-/* 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__ */
+++ /dev/null
-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)
+++ /dev/null
-/* 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);
-}
+++ /dev/null
-/* 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__ */
+++ /dev/null
-/* 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);
-}
+++ /dev/null
-/* 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
+++ /dev/null
-/* 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,
- >k_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;
-}
+++ /dev/null
-/* 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__ */
-
-
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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__ */
+++ /dev/null
-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)
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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__ */
+++ /dev/null
-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)
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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,
+ >k_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;
+}
--- /dev/null
+/* 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__ */
+
+
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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__ */
+
+
--- /dev/null
+/* 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;
+ }
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
+++ /dev/null
-/* 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);
-}
+++ /dev/null
-/* 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__ */
-
-
+++ /dev/null
-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)
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