From: Matthias Clasen Date: Sun, 11 Mar 2018 16:18:16 +0000 (-0400) Subject: Consolidate print backends into one directory X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~22^2~1024^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=a78e9f2261687c6f0b48107db717877f063371c8;p=gtk4.git Consolidate print backends into one directory This will let us use GTK_PATH to load them uninstalled, which is useful for in-tree tests. --- diff --git a/modules/printbackends/cloudprint/gtkcloudprintaccount.c b/modules/printbackends/cloudprint/gtkcloudprintaccount.c deleted file mode 100644 index ee65a18287..0000000000 --- a/modules/printbackends/cloudprint/gtkcloudprintaccount.c +++ /dev/null @@ -1,662 +0,0 @@ -/* gtkcloudprintaccount.c: Google Cloud Print account class - * Copyright (C) 2014, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include "gtkcloudprintaccount.h" -#include "gtkprintercloudprint.h" - -#define CLOUDPRINT_PROXY "GTK+" - -#define ACCOUNT_IFACE "org.gnome.OnlineAccounts.Account" -#define O_AUTH2_BASED_IFACE "org.gnome.OnlineAccounts.OAuth2Based" - -#define GTK_CLOUDPRINT_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccountClass)) -#define GTK_IS_CLOUDPRINT_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLOUDPRINT_ACCOUNT)) -#define GTK_CLOUDPRINT_ACCOUNT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccountClass)) - -static GObjectClass *gtk_cloudprint_account_parent_class; -static GType gtk_cloudprint_account_type = 0; - -typedef struct _GtkCloudprintAccountClass GtkCloudprintAccountClass; - -struct _GtkCloudprintAccountClass -{ - GObjectClass parent_class; -}; - -struct _GtkCloudprintAccount -{ - GObject parent_instance; - - gchar *printer_id; - gchar *goa_path; - gchar *presentation_identity; - RestProxy *rest_proxy; - gchar *oauth2_access_token; -}; - -static void gtk_cloudprint_account_class_init (GtkCloudprintAccountClass *class); -static void gtk_cloudprint_account_init (GtkCloudprintAccount *impl); -static void gtk_cloudprint_account_finalize (GObject *object); - -void -gtk_cloudprint_account_register_type (GTypeModule *module) -{ - const GTypeInfo cloudprint_account_info = - { - sizeof (GtkCloudprintAccountClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_cloudprint_account_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkCloudprintAccount), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_cloudprint_account_init, - }; - - gtk_cloudprint_account_type = g_type_module_register_type (module, - G_TYPE_OBJECT, - "GtkCloudprintAccount", - &cloudprint_account_info, 0); -} - -/* - * GtkCloudprintAccount - */ -GType -gtk_cloudprint_account_get_type (void) -{ - return gtk_cloudprint_account_type; -} - -/** - * gtk_cloudprint_account_new: - * - * Creates a new #GtkCloudprintAccount object, representing a Google - * Cloud Print account and its state data. - * - * Returns: the new #GtkCloudprintAccount object - **/ -GtkCloudprintAccount * -gtk_cloudprint_account_new (const gchar *id, - const gchar *path, - const gchar *presentation_identity) -{ - GtkCloudprintAccount *account = g_object_new (GTK_TYPE_CLOUDPRINT_ACCOUNT, - NULL); - account->printer_id = g_strdup (id); - account->goa_path = g_strdup (path); - account->presentation_identity = g_strdup (presentation_identity); - return account; -} - -static void -gtk_cloudprint_account_class_init (GtkCloudprintAccountClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - gtk_cloudprint_account_parent_class = g_type_class_peek_parent (klass); - gobject_class->finalize = gtk_cloudprint_account_finalize; -} - -static void -gtk_cloudprint_account_init (GtkCloudprintAccount *account) -{ - account->printer_id = NULL; - account->goa_path = NULL; - account->presentation_identity = NULL; - account->rest_proxy = NULL; - account->oauth2_access_token = NULL; - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: +GtkCloudprintAccount(%p)\n", - account)); -} - -static void -gtk_cloudprint_account_finalize (GObject *object) -{ - GtkCloudprintAccount *account; - - account = GTK_CLOUDPRINT_ACCOUNT (object); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: -GtkCloudprintAccount(%p)\n", - account)); - - g_clear_object (&(account->rest_proxy)); - g_clear_pointer (&(account->printer_id), g_free); - g_clear_pointer (&(account->goa_path), g_free); - g_clear_pointer (&(account->presentation_identity), g_free); - g_clear_pointer (&(account->oauth2_access_token), g_free); - - G_OBJECT_CLASS (gtk_cloudprint_account_parent_class)->finalize (object); -} - -static JsonParser * -cloudprint_json_parse (RestProxyCall *call, JsonObject **result, GError **error) -{ - JsonParser *json_parser = json_parser_new (); - JsonNode *root; - JsonObject *json_object; - gboolean success = FALSE; - - if (!json_parser_load_from_data (json_parser, - rest_proxy_call_get_payload (call), - rest_proxy_call_get_payload_length (call), - error)) - { - g_object_unref (json_parser); - return NULL; - } - - root = json_parser_get_root (json_parser); - if (JSON_NODE_TYPE (root) != JSON_NODE_OBJECT) - { - if (error != NULL) - *error = g_error_new_literal (gtk_print_error_quark (), - GTK_PRINT_ERROR_INTERNAL_ERROR, - "Bad reply"); - - g_object_unref (json_parser); - return NULL; - } - - json_object = json_node_get_object (root); - if (json_object_has_member (json_object, "success")) - success = json_object_get_boolean_member (json_object, "success"); - - if (!success) - { - const gchar *message = "(no message)"; - - if (json_object_has_member (json_object, "message")) - message = json_object_get_string_member (json_object, "message"); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: unsuccessful submit: %s\n", - message)); - - if (error != NULL) - *error = g_error_new_literal (gtk_print_error_quark (), - GTK_PRINT_ERROR_INTERNAL_ERROR, - message); - - g_object_unref (json_parser); - return NULL; - } - - if (result != NULL) - *result = json_node_dup_object (root); - - return json_parser; -} - -static void -gtk_cloudprint_account_search_rest_call_cb (RestProxyCall *call, - const GError *cb_error, - GObject *weak_object, - gpointer user_data) -{ - GTask *task = user_data; - GtkCloudprintAccount *account = g_task_get_task_data (task); - JsonParser *json_parser = NULL; - JsonObject *result; - JsonNode *printers = NULL; - GError *error = NULL; - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) 'search' REST call " - "returned\n", account)); - - if (cb_error != NULL) - { - error = g_error_copy (cb_error); - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - if (g_task_return_error_if_cancelled (task)) - { - g_object_unref (task); - return; - } - - if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_object_unref (json_parser); - - if (json_object_has_member (result, "printers")) - printers = json_object_dup_member (result, "printers"); - - json_object_unref (result); - if (printers == NULL) - { - g_task_return_new_error (task, - gtk_print_error_quark (), - GTK_PRINT_ERROR_INTERNAL_ERROR, - "Bad reply to 'search' request"); - return; - } - - g_task_return_pointer (task, - printers, - (GDestroyNotify) json_node_free); - g_object_unref (task); -} - -static void -gtk_cloudprint_account_got_oauth2_access_token_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GTask *task = user_data; - GtkCloudprintAccount *account = g_task_get_task_data (task); - RestProxyCall *call; - RestProxy *rest; - GVariant *output; - gint expires_in = 0; - GError *error = NULL; - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), - result, - &error); - g_object_unref (source); - - if (output == NULL) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_variant_get (output, "(si)", - &account->oauth2_access_token, - &expires_in); - g_variant_unref (output); - - rest = oauth2_proxy_new_with_token (account->printer_id, - account->oauth2_access_token, - "https://accounts.google.com/o/oauth2/token", - "https://www.google.com/cloudprint/", - FALSE); - - if (rest == NULL) - { - g_task_return_new_error (task, - gtk_print_error_quark (), - GTK_PRINT_ERROR_INTERNAL_ERROR, - "REST proxy creation failed"); - g_object_unref (task); - return; - } - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) 'search' REST call\n", - account)); - - account->rest_proxy = g_object_ref (rest); - - call = rest_proxy_new_call (REST_PROXY (rest)); - g_object_unref (rest); - rest_proxy_call_set_function (call, "search"); - rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY); - rest_proxy_call_add_param (call, "connection_status", "ALL"); - if (!rest_proxy_call_async (call, - gtk_cloudprint_account_search_rest_call_cb, - NULL, - task, - &error)) - { - g_task_return_error (task, error); - g_object_unref (task); - } - - g_object_unref (call); -} - -static void -gtk_cloudprint_account_ensure_credentials_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GTask *task = user_data; - GtkCloudprintAccount *account = g_task_get_task_data (task); - GVariant *output; - gint expires_in = 0; - GError *error = NULL; - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), - result, - &error); - - if (output == NULL) - { - g_object_unref (source); - if (error->domain != G_DBUS_ERROR || - (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN && - error->code != G_DBUS_ERROR_UNKNOWN_METHOD)) - g_task_return_error (task, error); - else - /* Return an empty list. */ - g_task_return_pointer (task, - json_node_new (JSON_NODE_ARRAY), - (GDestroyNotify) json_node_free); - - g_object_unref (task); - return; - } - - g_variant_get (output, "(i)", - &expires_in); - g_variant_unref (output); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) getting access token\n", - account)); - - g_dbus_connection_call (G_DBUS_CONNECTION (source), - ONLINE_ACCOUNTS_BUS, - account->goa_path, - O_AUTH2_BASED_IFACE, - "GetAccessToken", - NULL, - G_VARIANT_TYPE ("(si)"), - G_DBUS_CALL_FLAGS_NONE, - -1, - g_task_get_cancellable (task), - gtk_cloudprint_account_got_oauth2_access_token_cb, - task); -} - -void -gtk_cloudprint_account_search (GtkCloudprintAccount *account, - GDBusConnection *dbus_connection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task = g_task_new (G_OBJECT (account), - cancellable, - callback, - user_data); - g_task_set_task_data (task, - g_object_ref (account), - (GDestroyNotify) g_object_unref); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) ensuring credentials\n", - account)); - - g_dbus_connection_call (g_object_ref (dbus_connection), - ONLINE_ACCOUNTS_BUS, - account->goa_path, - ACCOUNT_IFACE, - "EnsureCredentials", - NULL, - G_VARIANT_TYPE ("(i)"), - G_DBUS_CALL_FLAGS_NONE, - -1, - cancellable, - gtk_cloudprint_account_ensure_credentials_cb, - task); -} - -JsonNode * -gtk_cloudprint_account_search_finish (GtkCloudprintAccount *account, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_task_is_valid (result, account), NULL); - return g_task_propagate_pointer (G_TASK (result), error); -} - -static void -gtk_cloudprint_account_printer_rest_call_cb (RestProxyCall *call, - const GError *cb_error, - GObject *weak_object, - gpointer user_data) -{ - GTask *task = user_data; - GtkCloudprintAccount *account = g_task_get_task_data (task); - JsonParser *json_parser = NULL; - JsonObject *result; - GError *error = NULL; - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) 'printer' REST call " - "returned\n", account)); - - if (cb_error != NULL) - { - error = g_error_copy (cb_error); - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - if (g_task_return_error_if_cancelled (task)) - { - g_object_unref (task); - return; - } - - if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_object_unref (json_parser); - g_task_return_pointer (task, - result, - (GDestroyNotify) json_object_unref); - g_object_unref (task); -} - -void -gtk_cloudprint_account_printer (GtkCloudprintAccount *account, - const gchar *printerid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - RestProxyCall *call; - GTask *task; - GError *error = NULL; - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) 'printer' REST call for " - "printer id %s", account, printerid)); - - task = g_task_new (G_OBJECT (account), cancellable, callback, user_data); - - g_task_set_task_data (task, - g_object_ref (account), - (GDestroyNotify) g_object_unref); - - call = rest_proxy_new_call (REST_PROXY (account->rest_proxy)); - rest_proxy_call_set_function (call, "printer"); - rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY); - rest_proxy_call_add_param (call, "printerid", printerid); - if (!rest_proxy_call_async (call, - gtk_cloudprint_account_printer_rest_call_cb, - NULL, - task, - &error)) - { - g_task_return_error (task, error); - g_object_unref (task); - } - - g_object_unref (call); -} - -JsonObject * -gtk_cloudprint_account_printer_finish (GtkCloudprintAccount *account, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_task_is_valid (result, account), NULL); - return g_task_propagate_pointer (G_TASK (result), error); -} - -static void -gtk_cloudprint_account_submit_rest_call_cb (RestProxyCall *call, - const GError *cb_error, - GObject *weak_object, - gpointer user_data) -{ - GTask *task = user_data; - GtkCloudprintAccount *account = g_task_get_task_data (task); - JsonParser *json_parser = NULL; - JsonObject *result; - GError *error = NULL; - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) 'submit' REST call " - "returned\n", account)); - - if (cb_error != NULL) - { - error = g_error_copy (cb_error); - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - if (g_task_return_error_if_cancelled (task)) - { - g_object_unref (task); - return; - } - - if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL) - { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_object_unref (json_parser); - g_task_return_pointer (task, - result, - (GDestroyNotify) json_object_unref); - g_object_unref (task); -} - -void -gtk_cloudprint_account_submit (GtkCloudprintAccount *account, - GtkPrinterCloudprint *printer, - GMappedFile *file, - const gchar *title, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task; - RestProxyCall *call; - gchar *printerid = NULL; - RestParam *param; - GError *error = NULL; - gchar *auth; - - g_object_get (printer, - "printer-id", &printerid, - NULL); - - g_warn_if_fail (printerid != NULL); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: (%p) 'submit' REST call for " - "printer id %s\n", account, printerid)); - - task = g_task_new (G_OBJECT (account), - cancellable, - callback, - user_data); - - g_task_set_task_data (task, - g_object_ref (account), - (GDestroyNotify) g_object_unref); - - call = rest_proxy_new_call (REST_PROXY (account->rest_proxy)); - rest_proxy_call_set_method (call, "POST"); - rest_proxy_call_set_function (call, "submit"); - - auth = g_strdup_printf ("Bearer %s", account->oauth2_access_token); - rest_proxy_call_add_header (call, "Authorization", auth); - g_free (auth); - rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY); - - rest_proxy_call_add_param (call, "printerid", printerid); - g_free (printerid); - - rest_proxy_call_add_param (call, "contentType", "dataUrl"); - rest_proxy_call_add_param (call, "title", title); - param = rest_param_new_with_owner ("content", - g_mapped_file_get_contents (file), - g_mapped_file_get_length (file), - "dataUrl", - NULL, - file, - (GDestroyNotify) g_mapped_file_unref); - rest_proxy_call_add_param_full (call, param); - - if (!rest_proxy_call_async (call, - gtk_cloudprint_account_submit_rest_call_cb, - NULL, - task, - &error)) - { - g_task_return_error (task, error); - g_object_unref (call); - g_object_unref (task); - return; - } - - g_object_unref (call); -} - -JsonObject * -gtk_cloudprint_account_submit_finish (GtkCloudprintAccount *account, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_task_is_valid (result, account), NULL); - return g_task_propagate_pointer (G_TASK (result), error); -} - -const gchar * -gtk_cloudprint_account_get_presentation_identity (GtkCloudprintAccount *account) -{ - return account->presentation_identity; -} diff --git a/modules/printbackends/cloudprint/gtkcloudprintaccount.h b/modules/printbackends/cloudprint/gtkcloudprintaccount.h deleted file mode 100644 index ef788743d3..0000000000 --- a/modules/printbackends/cloudprint/gtkcloudprintaccount.h +++ /dev/null @@ -1,74 +0,0 @@ -/* gtkcloudprintaccount.h: Google Cloud Print account class - * Copyright (C) 2014, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_CLOUDPRINT_ACCOUNT_H__ -#define __GTK_CLOUDPRINT_ACCOUNT_H__ - -#include -#include - -#include "gtkprintbackendcloudprint.h" - -G_BEGIN_DECLS - -#define GTK_TYPE_CLOUDPRINT_ACCOUNT (gtk_cloudprint_account_get_type ()) -#define GTK_CLOUDPRINT_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccount)) -#define GTK_IS_CLOUDPRINT_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT)) - -typedef struct _GtkPrinterCloudprint GtkPrinterCloudprint; -typedef struct _GtkCloudprintAccount GtkCloudprintAccount; - -void gtk_cloudprint_account_register_type (GTypeModule *module); -GtkCloudprintAccount *gtk_cloudprint_account_new (const gchar *id, - const gchar *path, - const gchar *presentation_identity); -GType gtk_cloudprint_account_get_type (void) G_GNUC_CONST; - -void gtk_cloudprint_account_search (GtkCloudprintAccount *account, - GDBusConnection *connection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -JsonNode *gtk_cloudprint_account_search_finish (GtkCloudprintAccount *account, - GAsyncResult *result, - GError **error); - -void gtk_cloudprint_account_printer (GtkCloudprintAccount *account, - const gchar *printerid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -JsonObject *gtk_cloudprint_account_printer_finish (GtkCloudprintAccount *account, - GAsyncResult *result, - GError **error); - -void gtk_cloudprint_account_submit (GtkCloudprintAccount *account, - GtkPrinterCloudprint *printer, - GMappedFile *file, - const gchar *title, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -JsonObject *gtk_cloudprint_account_submit_finish (GtkCloudprintAccount *account, - GAsyncResult *result, - GError **error); - -const gchar *gtk_cloudprint_account_get_presentation_identity (GtkCloudprintAccount *account); - -G_END_DECLS - -#endif /* __GTK_CLOUDPRINT_ACCOUNT_H__ */ diff --git a/modules/printbackends/cloudprint/gtkprintbackendcloudprint.c b/modules/printbackends/cloudprint/gtkprintbackendcloudprint.c deleted file mode 100644 index b5560343a2..0000000000 --- a/modules/printbackends/cloudprint/gtkprintbackendcloudprint.c +++ /dev/null @@ -1,1036 +0,0 @@ -/* gtkprintbackendcloudprint.c: Google Cloud Print implementation of - * GtkPrintBackend - * Copyright (C) 2014, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "gtkprintbackendcloudprint.h" -#include "gtkcloudprintaccount.h" -#include "gtkprintercloudprint.h" - -typedef struct _GtkPrintBackendCloudprintClass GtkPrintBackendCloudprintClass; - -#define GTK_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass)) -#define GTK_IS_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT)) -#define GTK_PRINT_BACKEND_CLOUDPRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass)) - -#define _STREAM_MAX_CHUNK_SIZE 8192 - -#define ONLINE_ACCOUNTS_PATH "/org/gnome/OnlineAccounts" -#define OBJECT_MANAGER_IFACE "org.freedesktop.DBus.ObjectManager" - -struct _GtkPrintBackendCloudprintClass -{ - GtkPrintBackendClass parent_class; -}; - -struct _GtkPrintBackendCloudprint -{ - GtkPrintBackend parent_instance; - GCancellable *cancellable; - guint accounts_searching; -}; - -struct -{ - gchar *id; - gchar *path; - gchar *presentation_identity; -} typedef TGOAAccount; - -static GObjectClass *backend_parent_class; -static void gtk_print_backend_cloudprint_class_init (GtkPrintBackendCloudprintClass *class); -static void gtk_print_backend_cloudprint_init (GtkPrintBackendCloudprint *impl); -static void gtk_print_backend_cloudprint_finalize (GObject *object); -static void cloudprint_printer_get_settings_from_options (GtkPrinter *printer, - GtkPrinterOptionSet *options, - GtkPrintSettings *settings); -static GtkPrinterOptionSet *cloudprint_printer_get_options (GtkPrinter *printer, - GtkPrintSettings *settings, - GtkPageSetup *page_setup, - GtkPrintCapabilities capabilities); -static void cloudprint_printer_prepare_for_print (GtkPrinter *printer, - GtkPrintJob *print_job, - GtkPrintSettings *settings, - GtkPageSetup *page_setup); -static void cloudprint_request_printer_list (GtkPrintBackend *print_backend); -static void gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend, - GtkPrintJob *job, - GIOChannel *data_io, - GtkPrintJobCompleteFunc callback, - gpointer user_data, - GDestroyNotify dnotify); -static cairo_surface_t * cloudprint_printer_create_cairo_surface (GtkPrinter *printer, - GtkPrintSettings *settings, - gdouble width, - gdouble height, - GIOChannel *cache_io); -static void cloudprint_printer_request_details (GtkPrinter *printer); -TGOAAccount * t_goa_account_copy (TGOAAccount *account); -void t_goa_account_free (gpointer data); - -G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendCloudprint, gtk_print_backend_cloudprint, GTK_TYPE_PRINT_BACKEND) - -void -g_io_module_load (GIOModule *module) -{ - g_type_module_use (G_TYPE_MODULE (module)); - - gtk_print_backend_cloudprint_register_type (G_TYPE_MODULE (module)); - gtk_cloudprint_account_register_type (G_TYPE_MODULE (module)); - gtk_printer_cloudprint_register_type (G_TYPE_MODULE (module)); - - g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, - GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, - "cloudprint", - 10); -} - -void -g_io_module_unload (GIOModule *module) -{ -} - -char ** -g_io_module_query (void) -{ - char *eps[] = { - GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, - NULL - }; - - return g_strdupv (eps); -} - -/** - * gtk_print_backend_cloudprint_new: - * - * Creates a new #GtkPrintBackendCloudprint - * object. #GtkPrintBackendCloudprint implements the #GtkPrintBackend - * interface using REST API calls to the Google Cloud Print service. - * - * Returns: the new #GtkPrintBackendCloudprint object - **/ -GtkPrintBackend * -gtk_print_backend_cloudprint_new (void) -{ - return g_object_new (GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, NULL); -} - -static void -gtk_print_backend_cloudprint_class_init (GtkPrintBackendCloudprintClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (klass); - - backend_parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gtk_print_backend_cloudprint_finalize; - - backend_class->request_printer_list = cloudprint_request_printer_list; - backend_class->print_stream = gtk_print_backend_cloudprint_print_stream; - backend_class->printer_create_cairo_surface = cloudprint_printer_create_cairo_surface; - backend_class->printer_get_options = cloudprint_printer_get_options; - backend_class->printer_get_settings_from_options = cloudprint_printer_get_settings_from_options; - backend_class->printer_prepare_for_print = cloudprint_printer_prepare_for_print; - backend_class->printer_request_details = cloudprint_printer_request_details; -} - -static void -gtk_print_backend_cloudprint_class_finalize (GtkPrintBackendCloudprintClass *class) -{ -} - -static void -gtk_print_backend_cloudprint_init (GtkPrintBackendCloudprint *backend) -{ - backend->cancellable = g_cancellable_new (); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: +GtkPrintBackendCloudprint(%p)\n", - backend)); -} - -static void -gtk_print_backend_cloudprint_finalize (GObject *object) -{ - GtkPrintBackendCloudprint *backend; - - backend = GTK_PRINT_BACKEND_CLOUDPRINT (object); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: -GtkPrintBackendCloudprint(%p)\n", - backend)); - - g_cancellable_cancel (backend->cancellable); - g_clear_object (&(backend->cancellable)); - - backend_parent_class->finalize (object); -} - -static cairo_status_t -_cairo_write (void *closure, - const unsigned char *data, - unsigned int length) -{ - GIOChannel *io = (GIOChannel *)closure; - gsize written; - GError *error; - - error = NULL; - - while (length > 0) - { - g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error); - - if (error != NULL) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: Error writing to temp file, %s\n", error->message)); - - g_error_free (error); - return CAIRO_STATUS_WRITE_ERROR; - } - - data += written; - length -= written; - } - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_surface_t * -cloudprint_printer_create_cairo_surface (GtkPrinter *printer, - GtkPrintSettings *settings, - gdouble width, - gdouble height, - GIOChannel *cache_io) -{ - cairo_surface_t *surface; - - surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height); - - cairo_surface_set_fallback_resolution (surface, - 2.0 * gtk_print_settings_get_printer_lpi (settings), - 2.0 * gtk_print_settings_get_printer_lpi (settings)); - - return surface; -} - -typedef struct { - GtkPrintBackend *backend; - GtkPrintJobCompleteFunc callback; - GtkPrintJob *job; - GIOChannel *target_io; - gpointer user_data; - GDestroyNotify dnotify; - gchar *path; - - /* Base64 encoding state */ - gint b64state; - gint b64save; -} _PrintStreamData; - -static void -cloudprint_submit_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); - _PrintStreamData *ps = (_PrintStreamData *) user_data; - JsonObject *result; - GError *error = NULL; - gboolean success = FALSE; - - result = gtk_cloudprint_account_submit_finish (account, res, &error); - g_object_unref (account); - if (result == NULL) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: submit REST reply: %s\n", - error->message)); - goto done; - } - - json_object_unref (result); - success = TRUE; - - done: - if (ps->callback != NULL) - ps->callback (ps->job, ps->user_data, error); - - if (ps->dnotify != NULL) - ps->dnotify (ps->user_data); - - gtk_print_job_set_status (ps->job, - (success ? - GTK_PRINT_STATUS_FINISHED : - GTK_PRINT_STATUS_FINISHED_ABORTED)); - - g_clear_object (&(ps->job)); - g_clear_object (&(ps->backend)); - g_clear_pointer (&error, g_error_free); - - g_free (ps->path); - g_free (ps); -} - -static void -cloudprint_print_cb (GtkPrintBackendCloudprint *print_backend, - GError *cb_error, - gpointer user_data) -{ - _PrintStreamData *ps = (_PrintStreamData *) user_data; - gsize encodedlen; - gchar encoded[4]; /* Up to 4 bytes are needed to finish encoding */ - GError *error = NULL; - - encodedlen = g_base64_encode_close (FALSE, - encoded, - &ps->b64state, - &ps->b64save); - - if (encodedlen > 0) - g_io_channel_write_chars (ps->target_io, - encoded, - encodedlen, - NULL, - &error); - - if (ps->target_io != NULL) - g_io_channel_unref (ps->target_io); - - if (cb_error == NULL) - { - GMappedFile *map = g_mapped_file_new (ps->path, FALSE, &error); - GtkPrinter *printer = gtk_print_job_get_printer (ps->job); - GtkCloudprintAccount *account = NULL; - - if (map == NULL) - { - GTK_NOTE (PRINTING, - g_printerr ("Cloud Print Backend: failed to map file: %s\n", - error->message)); - g_error_free (error); - goto out; - } - - g_object_get (printer, - "cloudprint-account", &account, - NULL); - - g_warn_if_fail (account != NULL); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: submitting job\n")); - gtk_cloudprint_account_submit (account, - GTK_PRINTER_CLOUDPRINT (printer), - map, - gtk_print_job_get_title (ps->job), - print_backend->cancellable, - cloudprint_submit_cb, - ps); - } - - out: - if (ps->path != NULL) - unlink (ps->path); - - if (cb_error != NULL || error != NULL) - { - if (ps->callback != NULL) - ps->callback (ps->job, ps->user_data, error); - - if (ps->dnotify != NULL) - ps->dnotify (ps->user_data); - - gtk_print_job_set_status (ps->job, - GTK_PRINT_STATUS_FINISHED_ABORTED); - - g_clear_object (&(ps->job)); - g_free (ps->path); - g_free (ps); - } -} - -static gboolean -cloudprint_write (GIOChannel *source, - GIOCondition con, - gpointer user_data) -{ - gchar buf[_STREAM_MAX_CHUNK_SIZE]; - /* Base64 converts 24 bits into 32 bits, so divide the number of - * bytes by 3 (rounding up) and multiply by 4. Also, if the previous - * call left a non-zero state we may need an extra 4 bytes. */ - gchar encoded[(_STREAM_MAX_CHUNK_SIZE / 3 + 1) * 4 + 4]; - gsize bytes_read; - GError *error = NULL; - GIOStatus read_status; - _PrintStreamData *ps = (_PrintStreamData *) user_data; - - read_status = - g_io_channel_read_chars (source, - buf, - _STREAM_MAX_CHUNK_SIZE, - &bytes_read, - &error); - - if (read_status != G_IO_STATUS_ERROR) - { - gsize encodedlen = g_base64_encode_step ((guchar *) buf, - bytes_read, - FALSE, - encoded, - &ps->b64state, - &ps->b64save); - - g_io_channel_write_chars (ps->target_io, - encoded, - encodedlen, - NULL, - &error); - } - - if (error != NULL || read_status == G_IO_STATUS_EOF) - { - cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (ps->backend), - error, user_data); - - if (error != NULL) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: %s\n", error->message)); - - g_error_free (error); - } - - return FALSE; - } - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: Writing %i byte chunk to tempfile\n", (int)bytes_read)); - - return TRUE; -} - -static void -gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend, - GtkPrintJob *job, - GIOChannel *data_io, - GtkPrintJobCompleteFunc callback, - gpointer user_data, - GDestroyNotify dnotify) -{ - const gchar *prefix = "data:application/pdf;base64,"; - GError *internal_error = NULL; - _PrintStreamData *ps; - int tmpfd; - - ps = g_new0 (_PrintStreamData, 1); - ps->callback = callback; - ps->user_data = user_data; - ps->dnotify = dnotify; - ps->job = g_object_ref (job); - ps->backend = g_object_ref (print_backend); - ps->path = g_strdup_printf ("%s/cloudprintXXXXXX.pdf.b64", - g_get_tmp_dir ()); - ps->b64state = 0; - ps->b64save = 0; - - internal_error = NULL; - - if (ps->path == NULL) - goto error; - - tmpfd = g_mkstemp (ps->path); - if (tmpfd == -1) - { - int err = errno; - internal_error = g_error_new (gtk_print_error_quark (), - GTK_PRINT_ERROR_INTERNAL_ERROR, - "Error creating temporary file: %s", - g_strerror (err)); - goto error; - } - - ps->target_io = g_io_channel_unix_new (tmpfd); - - if (ps->target_io != NULL) - { - g_io_channel_set_close_on_unref (ps->target_io, TRUE); - g_io_channel_set_encoding (ps->target_io, NULL, &internal_error); - } - - g_io_channel_write_chars (ps->target_io, - prefix, - strlen (prefix), - NULL, - &internal_error); - -error: - if (internal_error != NULL) - { - cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (print_backend), - internal_error, ps); - - g_error_free (internal_error); - return; - } - - g_io_add_watch (data_io, - G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, - (GIOFunc) cloudprint_write, - ps); -} - -TGOAAccount * -t_goa_account_copy (TGOAAccount *account) -{ - TGOAAccount *result = NULL; - - if (account != NULL) - { - result = g_new0 (TGOAAccount, 1); - result->id = g_strdup (account->id); - result->path = g_strdup (account->path); - result->presentation_identity = g_strdup (account->presentation_identity); - } - - return result; -} - -void -t_goa_account_free (gpointer data) -{ - TGOAAccount *account = (TGOAAccount *) data; - - if (account != NULL) - { - g_free (account->id); - g_free (account->path); - g_free (account->presentation_identity); - g_free (account); - } -} - -static GList * -get_accounts (GVariant *output) -{ - GVariant *objects; - GList *result = NULL; - gint i, j, k; - - g_variant_get (output, "(@a{oa{sa{sv}}})", - &objects); - - if (objects) - { - for (i = 0; i < g_variant_n_children (objects); i++) - { - const gchar *object_name; - GVariant *object_variant; - - g_variant_get_child (objects, i, "{&o@a{sa{sv}}}", - &object_name, - &object_variant); - - if (g_str_has_prefix (object_name, "/org/gnome/OnlineAccounts/Accounts/")) - { - for (j = 0; j < g_variant_n_children (object_variant); j++) - { - const gchar *service_name; - GVariant *service_variant; - - g_variant_get_child (object_variant, j, "{&s@a{sv}}", - &service_name, - &service_variant); - - if (g_str_has_prefix (service_name, "org.gnome.OnlineAccounts.Account")) - { - TGOAAccount *account; - gboolean printers_disabled = FALSE; - gchar *provider_type = NULL; - - account = g_new0 (TGOAAccount, 1); - - account->path = g_strdup (object_name); - for (k = 0; k < g_variant_n_children (service_variant); k++) - { - const gchar *property_name; - GVariant *property_variant; - GVariant *value; - - g_variant_get_child (service_variant, k, "{&s@v}", - &property_name, - &property_variant); - - g_variant_get (property_variant, "v", - &value); - - if (g_strcmp0 (property_name, "Id") == 0) - account->id = g_variant_dup_string (value, NULL); - else if (g_strcmp0 (property_name, "ProviderType") == 0) - provider_type = g_variant_dup_string (value, NULL); - else if (g_strcmp0 (property_name, "PrintersDisabled") == 0) - printers_disabled = g_variant_get_boolean (value); - else if (g_strcmp0 (property_name, "PresentationIdentity") == 0) - account->presentation_identity = g_variant_dup_string (value, NULL); - - g_variant_unref (property_variant); - g_variant_unref (value); - } - - if (!printers_disabled && - g_strcmp0 (provider_type, "google") == 0 && - account->presentation_identity != NULL) - result = g_list_append (result, account); - else - t_goa_account_free (account); - - g_free (provider_type); - } - - g_variant_unref (service_variant); - } - } - - g_variant_unref (object_variant); - } - - g_variant_unref (objects); - } - - return result; -} - -static void -cloudprint_search_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); - GtkPrintBackendCloudprint *backend = NULL; - JsonNode *node; - JsonArray *printers; - guint i; - GError *error = NULL; - - node = gtk_cloudprint_account_search_finish (account, res, &error); - g_object_unref (account); - if (node == NULL) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: search failed: %s\n", - error->message)); - - if (error->domain != G_IO_ERROR || - error->code != G_IO_ERROR_CANCELLED) - backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); - - g_error_free (error); - goto done; - } - - backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); - printers = json_node_get_array (node); - for (i = 0; i < json_array_get_length (printers); i++) - { - GtkPrinterCloudprint *printer; - JsonObject *json_printer = json_array_get_object_element (printers, i); - const char *name = NULL; - const char *id = NULL; - const char *type = NULL; - const char *desc = NULL; - const char *status = NULL; - gboolean is_virtual; - - if (json_object_has_member (json_printer, "displayName")) - name = json_object_get_string_member (json_printer, "displayName"); - - if (json_object_has_member (json_printer, "id")) - id = json_object_get_string_member (json_printer, "id"); - - if (name == NULL || id == NULL) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: ignoring incomplete " - "printer description\n")); - continue; - } - - if (json_object_has_member (json_printer, "type")) - type = json_object_get_string_member (json_printer, "type"); - - if (json_object_has_member (json_printer, "description")) - desc = json_object_get_string_member (json_printer, "description"); - - if (json_object_has_member (json_printer, "connectionStatus")) - status = json_object_get_string_member (json_printer, - "connectionStatus"); - - is_virtual = (type != NULL && !strcmp (type, "DOCS")); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: Adding printer %s\n", name)); - - printer = gtk_printer_cloudprint_new (name, - is_virtual, - GTK_PRINT_BACKEND (backend), - account, - id); - gtk_printer_set_has_details (GTK_PRINTER (printer), FALSE); - gtk_printer_set_icon_name (GTK_PRINTER (printer), "printer"); - gtk_printer_set_location (GTK_PRINTER (printer), - gtk_cloudprint_account_get_presentation_identity (account)); - - if (desc != NULL) - gtk_printer_set_description (GTK_PRINTER (printer), desc); - - if (status != NULL) - { - if (!strcmp (status, "ONLINE")) - /* Translators: The printer status is online, i.e. it is - * ready to print. */ - gtk_printer_set_state_message (GTK_PRINTER (printer), _("Online")); - else if (!strcmp (status, "UNKNOWN")) - /* Translators: We don't know whether this printer is - * available to print to. */ - gtk_printer_set_state_message (GTK_PRINTER (printer), _("Unknown")); - else if (!strcmp (status, "OFFLINE")) - /* Translators: The printer is offline. */ - gtk_printer_set_state_message (GTK_PRINTER (printer), _("Offline")); - else if (!strcmp (status, "DORMANT")) - /* We shouldn't get here because the query omits dormant - * printers by default. */ - - /* Translators: Printer has been offline for a long time. */ - gtk_printer_set_state_message (GTK_PRINTER (printer), _("Dormant")); - } - - gtk_printer_set_is_active (GTK_PRINTER (printer), TRUE); - - gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), - GTK_PRINTER (printer)); - g_signal_emit_by_name (GTK_PRINT_BACKEND (backend), - "printer-added", GTK_PRINTER (printer)); - g_object_unref (printer); - } - - json_node_free (node); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: 'search' finished for account %p\n", - account)); - - done: - if (backend != NULL && --backend->accounts_searching == 0) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: 'search' finished for " - "all accounts\n")); - - gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); - } -} - -static void -cloudprint_get_managed_objects_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - GtkPrintBackendCloudprint *backend; - GVariant *output; - GError *error = NULL; - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); - - if (output != NULL) - { - TGOAAccount *goa_account; - GList *accounts = NULL; - GList *iter; - guint searching; - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: got objects managed by goa\n")); - - backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); - - accounts = get_accounts (output); - g_variant_unref (output); - searching = backend->accounts_searching = g_list_length (accounts); - - for (iter = accounts; iter != NULL; iter = iter->next) - { - GtkCloudprintAccount *account; - goa_account = (TGOAAccount *) iter->data; - account = gtk_cloudprint_account_new (goa_account->id, - goa_account->path, - goa_account->presentation_identity); - if (account == NULL) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: error constructing " - "account object")); - backend->accounts_searching--; - searching--; - continue; - } - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: issuing 'search' for %p\n", - account)); - - gtk_cloudprint_account_search (account, - G_DBUS_CONNECTION (source), - backend->cancellable, - cloudprint_search_cb, - GTK_PRINT_BACKEND (backend)); - } - - if (searching == 0) - gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); - - g_list_free_full (accounts, t_goa_account_free); - } - else - { - if (error->domain != G_IO_ERROR || - error->code != G_IO_ERROR_CANCELLED) - { - if (error->domain != G_DBUS_ERROR || - (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN && - error->code != G_DBUS_ERROR_UNKNOWN_METHOD)) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: failed to get objects managed by goa: %s\n", - error->message)); - g_warning ("%s", error->message); - } - - gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data)); - } - - g_error_free (error); - } - - g_object_unref (source); -} - -static void -cloudprint_bus_get_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - GtkPrintBackendCloudprint *backend; - GDBusConnection *connection; - GError *error = NULL; - - connection = g_bus_get_finish (res, &error); - - if (connection != NULL) - { - backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: got connection to session bus\n")); - - g_dbus_connection_call (connection, - ONLINE_ACCOUNTS_BUS, - ONLINE_ACCOUNTS_PATH, - OBJECT_MANAGER_IFACE, - "GetManagedObjects", - NULL, - G_VARIANT_TYPE ("(a{oa{sa{sv}}})"), - G_DBUS_CALL_FLAGS_NONE, - -1, - backend->cancellable, - cloudprint_get_managed_objects_cb, - backend); - } - else - { - if (error->domain != G_IO_ERROR || - error->code != G_IO_ERROR_CANCELLED) - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: failed getting session bus: %s\n", - error->message)); - g_warning ("%s", error->message); - - gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data)); - } - g_error_free (error); - } -} - -static void -cloudprint_request_printer_list (GtkPrintBackend *print_backend) -{ - GtkPrintBackendCloudprint *backend = GTK_PRINT_BACKEND_CLOUDPRINT (print_backend); - - g_cancellable_reset (backend->cancellable); - g_bus_get (G_BUS_TYPE_SESSION, backend->cancellable, cloudprint_bus_get_cb, backend); -} - -static GtkPrinterOptionSet * -cloudprint_printer_get_options (GtkPrinter *printer, - GtkPrintSettings *settings, - GtkPageSetup *page_setup, - GtkPrintCapabilities capabilities) -{ - GtkPrinterOptionSet *set; - GtkPrinterOption *option; - const gchar *n_up[] = { "1" }; - - set = gtk_printer_option_set_new (); - - /* How many document pages to go onto one side of paper. */ - option = gtk_printer_option_new ("gtk-n-up", _("Pages per _sheet:"), GTK_PRINTER_OPTION_TYPE_PICKONE); - gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), - (char **) n_up, (char **) n_up /* FIXME i18n (localised digits)! */); - gtk_printer_option_set (option, "1"); - gtk_printer_option_set_add (set, option); - g_object_unref (option); - - return set; -} - -static void -cloudprint_printer_get_settings_from_options (GtkPrinter *printer, - GtkPrinterOptionSet *options, - GtkPrintSettings *settings) -{ -} - -static void -cloudprint_printer_prepare_for_print (GtkPrinter *printer, - GtkPrintJob *print_job, - GtkPrintSettings *settings, - GtkPageSetup *page_setup) -{ - gdouble scale; - - gtk_print_job_set_pages (print_job, gtk_print_settings_get_print_pages (settings)); - gtk_print_job_set_page_ranges (print_job, NULL, 0); - - if (gtk_print_job_get_pages (print_job) == GTK_PRINT_PAGES_RANGES) - { - GtkPageRange *page_ranges; - gint num_page_ranges; - page_ranges = gtk_print_settings_get_page_ranges (settings, &num_page_ranges); - gtk_print_job_set_page_ranges (print_job, page_ranges, num_page_ranges); - } - - gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings)); - gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings)); - gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings)); - - scale = gtk_print_settings_get_scale (settings); - if (scale != 100.0) - gtk_print_job_set_scale (print_job, scale/100.0); - - gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings)); - gtk_print_job_set_rotate (print_job, TRUE); -} - -static void -cloudprint_printer_cb (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); - GtkPrinter *printer = GTK_PRINTER (user_data); - JsonObject *result; - GError *error = NULL; - gboolean success = FALSE; - - result = gtk_cloudprint_account_printer_finish (account, res, &error); - if (result != NULL) - { - /* Ignore capabilities for now. */ - json_object_unref (result); - success = TRUE; - } - else - { - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: failure getting details: %s\n", - error->message)); - - if (error->domain == G_IO_ERROR && - error->code == G_IO_ERROR_CANCELLED) - { - g_error_free (error); - return; - } - - g_error_free (error); - } - - gtk_printer_set_has_details (printer, success); - g_signal_emit_by_name (printer, "details-acquired", success); -} - -static void -cloudprint_printer_request_details (GtkPrinter *printer) -{ - GtkPrintBackendCloudprint *backend; - GtkCloudprintAccount *account = NULL; - gchar *printerid = NULL; - - g_object_get (printer, - "cloudprint-account", &account, - "printer-id", &printerid, - NULL); - - g_warn_if_fail (account != NULL); - g_warn_if_fail (printerid != NULL); - - backend = GTK_PRINT_BACKEND_CLOUDPRINT (gtk_printer_get_backend (printer)); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: Getting details for printer id %s\n", - printerid)); - - gtk_cloudprint_account_printer (account, - printerid, - backend->cancellable, - cloudprint_printer_cb, - printer); - g_object_unref (account); - g_free (printerid); -} diff --git a/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h b/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h deleted file mode 100644 index 044ad4b32b..0000000000 --- a/modules/printbackends/cloudprint/gtkprintbackendcloudprint.h +++ /dev/null @@ -1,40 +0,0 @@ -/* gtkprintbackendcloudprint.h: Google Cloud Print implementation of - * GtkPrintBackend - * Copyright (C) 2014, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_PRINT_BACKEND_CLOUDPRINT_H__ -#define __GTK_PRINT_BACKEND_CLOUDPRINT_H__ - -#include -#include "gtkprintbackend.h" - -G_BEGIN_DECLS - -#define GTK_TYPE_PRINT_BACKEND_CLOUDPRINT (gtk_print_backend_cloudprint_get_type ()) -#define GTK_PRINT_BACKEND_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprint)) -#define GTK_IS_PRINT_BACKEND_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT)) - -#define ONLINE_ACCOUNTS_BUS "org.gnome.OnlineAccounts" - -typedef struct _GtkPrintBackendCloudprint GtkPrintBackendCloudprint; - -GtkPrintBackend *gtk_print_backend_cloudprint_new (void); -GType gtk_print_backend_cloudprint_get_type (void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GTK_PRINT_BACKEND_CLOUDPRINT_H__ */ diff --git a/modules/printbackends/cloudprint/gtkprintercloudprint.c b/modules/printbackends/cloudprint/gtkprintercloudprint.c deleted file mode 100644 index 4f299fdcab..0000000000 --- a/modules/printbackends/cloudprint/gtkprintercloudprint.c +++ /dev/null @@ -1,231 +0,0 @@ -/* gtkprintercloudprint.c: Google Cloud Print -specific Printer class, - * GtkPrinterCloudprint - * Copyright (C) 2014, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include -#include - -#include "gtkprintercloudprint.h" -#include "gtkcloudprintaccount.h" - -typedef struct _GtkPrinterCloudprintClass GtkPrinterCloudprintClass; - -#define GTK_PRINTER_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprintClass)) -#define GTK_IS_PRINTER_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CLOUDPRINT)) -#define GTK_PRINTER_CLOUDPRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprintClass)) - -static GtkPrinterClass *gtk_printer_cloudprint_parent_class; -static GType printer_cloudprint_type = 0; - -struct _GtkPrinterCloudprintClass -{ - GtkPrinterClass parent_class; -}; - -struct _GtkPrinterCloudprint -{ - GtkPrinter parent_instance; - - GtkCloudprintAccount *account; - gchar *id; -}; - -enum { - PROP_0, - PROP_CLOUDPRINT_ACCOUNT, - PROP_PRINTER_ID -}; - -static void gtk_printer_cloudprint_class_init (GtkPrinterCloudprintClass *class); -static void gtk_printer_cloudprint_init (GtkPrinterCloudprint *impl); -static void gtk_printer_cloudprint_finalize (GObject *object); -static void gtk_printer_cloudprint_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_printer_cloudprint_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -void -gtk_printer_cloudprint_register_type (GTypeModule *module) -{ - const GTypeInfo printer_cloudprint_info = - { - sizeof (GtkPrinterCloudprintClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_printer_cloudprint_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkPrinterCloudprint), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_printer_cloudprint_init, - }; - - printer_cloudprint_type = g_type_module_register_type (module, - GTK_TYPE_PRINTER, - "GtkPrinterCloudprint", - &printer_cloudprint_info, 0); -} - -/* - * GtkPrinterCloudprint - */ -GType -gtk_printer_cloudprint_get_type (void) -{ - return printer_cloudprint_type; -} - -/** - * gtk_printer_cloudprint_new: - * - * Creates a new #GtkPrinterCloudprint object. #GtkPrinterCloudprint - * implements the #GtkPrinter interface and stores a reference to the - * #GtkCloudprintAccount object and the printer-id to use - * - * Returns: the new #GtkPrinterCloudprint object - **/ -GtkPrinterCloudprint * -gtk_printer_cloudprint_new (const char *name, - gboolean is_virtual, - GtkPrintBackend *backend, - GtkCloudprintAccount *account, - const gchar *id) -{ - return g_object_new (GTK_TYPE_PRINTER_CLOUDPRINT, - "name", name, - "backend", backend, - "is-virtual", is_virtual, - "accepts-pdf", TRUE, - "cloudprint-account", account, - "printer-id", id, - NULL); -} - -static void -gtk_printer_cloudprint_class_init (GtkPrinterCloudprintClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gtk_printer_cloudprint_parent_class = g_type_class_peek_parent (klass); - gobject_class->finalize = gtk_printer_cloudprint_finalize; - gobject_class->set_property = gtk_printer_cloudprint_set_property; - gobject_class->get_property = gtk_printer_cloudprint_get_property; - - g_object_class_install_property (G_OBJECT_CLASS (klass), - PROP_CLOUDPRINT_ACCOUNT, - g_param_spec_object ("cloudprint-account", - P_("Cloud Print account"), - P_("GtkCloudprintAccount instance"), - GTK_TYPE_CLOUDPRINT_ACCOUNT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS | - G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), - PROP_PRINTER_ID, - g_param_spec_string ("printer-id", - P_("Printer ID"), - P_("Cloud Print printer ID"), - "", - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS | - G_PARAM_CONSTRUCT_ONLY)); -} - -static void -gtk_printer_cloudprint_init (GtkPrinterCloudprint *printer) -{ - printer->account = NULL; - printer->id = NULL; - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: +GtkPrinterCloudprint(%p)\n", - printer)); -} - -static void -gtk_printer_cloudprint_finalize (GObject *object) -{ - GtkPrinterCloudprint *printer; - - printer = GTK_PRINTER_CLOUDPRINT (object); - - GTK_NOTE (PRINTING, - g_print ("Cloud Print Backend: -GtkPrinterCloudprint(%p)\n", - printer)); - - if (printer->account != NULL) - g_object_unref (printer->account); - - g_free (printer->id); - - G_OBJECT_CLASS (gtk_printer_cloudprint_parent_class)->finalize (object); -} - -static void -gtk_printer_cloudprint_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkPrinterCloudprint *printer = GTK_PRINTER_CLOUDPRINT (object); - - switch (prop_id) - { - case PROP_CLOUDPRINT_ACCOUNT: - printer->account = g_value_dup_object (value); - break; - - case PROP_PRINTER_ID: - printer->id = g_value_dup_string (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_printer_cloudprint_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkPrinterCloudprint *printer = GTK_PRINTER_CLOUDPRINT (object); - - switch (prop_id) - { - case PROP_CLOUDPRINT_ACCOUNT: - g_value_set_object (value, printer->account); - break; - - case PROP_PRINTER_ID: - g_value_set_string (value, printer->id); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/modules/printbackends/cloudprint/gtkprintercloudprint.h b/modules/printbackends/cloudprint/gtkprintercloudprint.h deleted file mode 100644 index 564fb150be..0000000000 --- a/modules/printbackends/cloudprint/gtkprintercloudprint.h +++ /dev/null @@ -1,43 +0,0 @@ -/* gtkprintercloudprint.h: Google Cloud Print -specific Printer class - * GtkPrinterCloudprint - * Copyright (C) 2014, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_PRINTER_CLOUDPRINT_H__ -#define __GTK_PRINTER_CLOUDPRINT_H__ - -#include -#include - -#include "gtkcloudprintaccount.h" - -G_BEGIN_DECLS - -#define GTK_TYPE_PRINTER_CLOUDPRINT (gtk_printer_cloudprint_get_type ()) -#define GTK_PRINTER_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprint)) -#define GTK_IS_PRINTER_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CLOUDPRINT)) - -void gtk_printer_cloudprint_register_type (GTypeModule *module); -GtkPrinterCloudprint *gtk_printer_cloudprint_new (const char *name, - gboolean is_virtual, - GtkPrintBackend *backend, - GtkCloudprintAccount *account, - const gchar *id); -GType gtk_printer_cloudprint_get_type (void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GTK_PRINTER_CLOUDPRINT_H__ */ diff --git a/modules/printbackends/cloudprint/meson.build b/modules/printbackends/cloudprint/meson.build deleted file mode 100644 index f5217cbc46..0000000000 --- a/modules/printbackends/cloudprint/meson.build +++ /dev/null @@ -1,11 +0,0 @@ -shared_module('printbackend-cloudprint', - 'gtkprintbackendcloudprint.c', - 'gtkprintercloudprint.c', - 'gtkcloudprintaccount.c', - c_args: [ - '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', - '-DGTK_DISABLE_DEPRECATION_WARNINGS', - ], - dependencies: [ libgtk_dep, rest_dep, json_glib_dep ], - install_dir: printbackends_install_dir, - install : true) diff --git a/modules/printbackends/cups/gtkcupssecretsutils.c b/modules/printbackends/cups/gtkcupssecretsutils.c deleted file mode 100644 index 925f7d561c..0000000000 --- a/modules/printbackends/cups/gtkcupssecretsutils.c +++ /dev/null @@ -1,1041 +0,0 @@ -/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords - * Copyright (C) 2014, Intevation GmbH - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include -#include -#include - -#include - -#include "gtkcupssecretsutils.h" - -#define SECRETS_BUS "org.freedesktop.secrets" -#define SECRETS_IFACE(interface) "org.freedesktop.Secret."interface -#define SECRETS_PATH "/org/freedesktop/secrets" -#define SECRETS_TIMEOUT 5000 - -typedef enum -{ - SECRETS_SERVICE_ACTION_QUERY, - SECRETS_SERVICE_ACTION_STORE -} SecretsServiceAction; - -typedef struct -{ - GDBusConnection *dbus_connection; - SecretsServiceAction action; - gchar **auth_info, - **auth_info_labels, - **auth_info_required, - *printer_uri, - *session_path, - *collection_path; - GDBusProxy *item_proxy; - guint prompt_subscription; -} SecretsServiceData; - -/** - * create_attributes: - * @printer_uri: URI for the printer - * @additional_labels: Optional labels for additional attributes - * @additional_attrs: Optional additional attributes - * - * Creates a GVariant dictionary with key / value pairs that - * can be used to identify a secret item. - * - * Returns: A GVariant dictionary of string pairs or NULL on error. - */ -static GVariant * -create_attributes (const gchar *printer_uri, - gchar **additional_attrs, - gchar **additional_labels) -{ - GVariantBuilder *attr_builder = NULL; - GVariant *ret = NULL; - - if (printer_uri == NULL) - { - GTK_NOTE (PRINTING, - g_print ("create_attributes called with invalid parameters.\n")); - return NULL; - } - - attr_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY); - /* The printer uri is the main identifying part */ - g_variant_builder_add (attr_builder, "{ss}", "uri", printer_uri); - - if (additional_labels != NULL) - { - int i; - for (i = 0; additional_labels[i] != NULL; i++) - { - g_variant_builder_add (attr_builder, "{ss}", - additional_labels[i], - additional_attrs[i]); - } - } - - ret = g_variant_builder_end (attr_builder); - g_variant_builder_unref (attr_builder); - - return ret; -} - -static void -get_secret_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GError *error = NULL; - GVariant *output, - *attributes; - gchar **auth_info = NULL, - *key = NULL, - *value = NULL; - GVariantIter *iter = NULL; - guint i, required_len; - gint pw_field = -1; - - task = user_data; - task_data = g_task_get_task_data (task); - - output = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), - res, - &error); - if (output == NULL) - { - g_task_return_error (task, error); - return; - } - - attributes = g_dbus_proxy_get_cached_property (task_data->item_proxy, - "Attributes"); - if (attributes == NULL) - { - GTK_NOTE (PRINTING, g_print ("Failed to lookup attributes.\n")); - g_variant_unref (output); - g_task_return_pointer (task, NULL, NULL); - return; - } - - /* Iterate over the attributes to fill the auth info */ - g_variant_get (attributes, "a{ss}", &iter); - - auth_info = g_new0 (gchar *, - g_strv_length (task_data->auth_info_required) + 1); - - while (g_variant_iter_loop (iter, "{ss}", &key, &value)) - { - /* Match attributes with required auth info */ - for (i = 0; task_data->auth_info_required[i] != NULL; i++) - { - if ((strcmp (key, "user") == 0 || - strcmp (key, "username") == 0) && - strcmp (task_data->auth_info_required[i], - "username") == 0) - { - auth_info[i] = g_strdup (value); - } - else if (strcmp (key, "domain") == 0 && - strcmp (task_data->auth_info_required[i], "domain") == 0) - { - auth_info[i] = g_strdup (value); - } - else if ((strcmp (key, "hostname") == 0 || - strcmp (key, "server") == 0 ) && - strcmp (task_data->auth_info_required[i], "hostname") == 0) - { - auth_info[i] = g_strdup (value); - } - else if (strcmp (task_data->auth_info_required[i], "password") == 0) - { - pw_field = i; - } - } - } - - if (pw_field == -1) - { - /* should not happen... */ - GTK_NOTE (PRINTING, g_print ("No password required?.\n")); - g_variant_unref (output); - goto fail; - } - else - { - GVariant *secret, - *s_value; - gconstpointer ba_passwd = NULL; - gsize len = 0; - - secret = g_variant_get_child_value (output, 0); - g_variant_unref (output); - if (secret == NULL || g_variant_n_children (secret) != 4) - { - GTK_NOTE (PRINTING, g_print ("Get secret response invalid.\n")); - if (secret != NULL) - g_variant_unref (secret); - goto fail; - } - s_value = g_variant_get_child_value (secret, 2); - ba_passwd = g_variant_get_fixed_array (s_value, - &len, - sizeof (guchar)); - - g_variant_unref (secret); - - if (ba_passwd == NULL) - { - GTK_NOTE (PRINTING, g_print ("Invalid / no secret found.\n")); - g_variant_unref (s_value); - goto fail; - } - - auth_info[pw_field] = g_strndup (ba_passwd, len); - g_variant_unref (s_value); - } - - for (i = 0; task_data->auth_info_required[i] != NULL; i++) - { - if (auth_info[i] == NULL) - { - /* Error out if we did not find everything */ - GTK_NOTE (PRINTING, g_print ("Failed to lookup required attribute: %s.\n", - task_data->auth_info_required[i])); - goto fail; - } - } - - g_task_return_pointer (task, auth_info, NULL); - return; - -fail: - /* Error out */ - GTK_NOTE (PRINTING, g_print ("Failed to lookup secret.\n")); - required_len = g_strv_length (task_data->auth_info_required); - for (i = 0; i < required_len; i++) - { - /* Not all fields of auth_info are neccessarily written so we can not - use strfreev here */ - g_free (auth_info[i]); - } - g_free (auth_info); - g_task_return_pointer (task, NULL, NULL); -} - -static void -create_item_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - GError *error = NULL; - GVariant *output; - gchar *item = NULL; - - task = user_data; - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), - res, - &error); - if (output == NULL) - { - g_task_return_error (task, error); - return; - } - - g_variant_get (output, "(&o&o)", &item, NULL); - if (item != NULL && strlen (item) > 1) - { - GTK_NOTE (PRINTING, g_print ("Successfully stored auth info.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - g_variant_unref (output); -} - -static void -do_store_auth_info (GTask *task) -{ - GVariant *attributes = NULL, - *properties = NULL, - *secret = NULL; - gchar **additional_attrs = NULL, - **additional_labels = NULL, - *password = NULL; - SecretsServiceData *task_data = g_task_get_task_data (task); - guint i, - length, - additional_count = 0; - GVariantBuilder *prop_builder = NULL; - - length = g_strv_length (task_data->auth_info_labels); - - additional_attrs = g_new0 (gchar *, length + 1); - additional_labels = g_new0 (gchar *, length + 1); - /* The labels user and server are chosen to be compatible with - the attributes used by system-config-printer */ - for (i = 0; task_data->auth_info_labels[i] != NULL; i++) - { - if (g_strcmp0 (task_data->auth_info_labels[i], "username") == 0) - { - additional_attrs[additional_count] = task_data->auth_info[i]; - additional_labels[additional_count++] = "user"; - } - else if (g_strcmp0 (task_data->auth_info_labels[i], "hostname") == 0) - { - additional_attrs[additional_count] = task_data->auth_info[i]; - additional_labels[additional_count++] = "server"; - } - else if (g_strcmp0 (task_data->auth_info_labels[i], "password") == 0) - { - password = task_data->auth_info[i]; - } - } - - attributes = create_attributes (task_data->printer_uri, - additional_attrs, - additional_labels); - g_free (additional_labels); - g_free (additional_attrs); - if (attributes == NULL) - { - GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - - if (password == NULL) - { - GTK_NOTE (PRINTING, g_print ("No secret to store.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - - prop_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY); - - g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Label"), - g_variant_new_string (task_data->printer_uri)); - g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Attributes"), - attributes); - - properties = g_variant_builder_end (prop_builder); - - g_variant_builder_unref (prop_builder); - - secret = g_variant_new ("(oay@ays)", - task_data->session_path, - NULL, - g_variant_new_bytestring (password), - "text/plain"); - - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - task_data->collection_path, - SECRETS_IFACE ("Collection"), - "CreateItem", - g_variant_new ("(@a{sv}@(oayays)b)", - properties, - secret, - TRUE), - G_VARIANT_TYPE ("(oo)"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - create_item_cb, - task); -} - -static void -prompt_completed_cb (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GVariant *dismissed; - gboolean is_dismissed = TRUE; - - task = user_data; - task_data = g_task_get_task_data (task); - - g_dbus_connection_signal_unsubscribe (task_data->dbus_connection, - task_data->prompt_subscription); - task_data->prompt_subscription = 0; - - dismissed = g_variant_get_child_value (parameters, 0); - - if (dismissed == NULL) - { - GTK_NOTE (PRINTING, g_print ("Invalid prompt signal.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - - g_variant_get (dismissed, "b", &is_dismissed); - g_variant_unref (dismissed); - - if (is_dismissed) - { - GTK_NOTE (PRINTING, g_print ("Collection unlock dismissed.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - - /* Prompt successfull, proceed to get or store secret */ - switch (task_data->action) - { - case SECRETS_SERVICE_ACTION_STORE: - do_store_auth_info (task); - break; - - case SECRETS_SERVICE_ACTION_QUERY: - g_dbus_proxy_call (task_data->item_proxy, - "GetSecret", - g_variant_new ("(o)", - task_data->session_path), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - get_secret_cb, - task); - break; - } -} - -static void -prompt_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GError *error = NULL; - GVariant *output; - - task = user_data; - task_data = g_task_get_task_data (task); - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), - res, - &error); - if (output == NULL) - { - g_task_return_error (task, error); - return; - } - - g_variant_unref (output); - - /* Connect to the prompt's completed signal */ - task_data->prompt_subscription = - g_dbus_connection_signal_subscribe (task_data->dbus_connection, - NULL, - SECRETS_IFACE ("Prompt"), - "Completed", - NULL, - NULL, - G_DBUS_SIGNAL_FLAGS_NONE, - prompt_completed_cb, - task, - NULL); -} - -static void -unlock_collection_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GError *error = NULL; - GVariant *output; - const gchar *prompt_path; - - task = user_data; - task_data = g_task_get_task_data (task); - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), - res, - &error); - if (output == NULL) - { - g_task_return_error (task, error); - return; - } - - g_variant_get (output, "(@ao&o)", NULL, &prompt_path); - - if (prompt_path != NULL && strlen (prompt_path) > 1) - { - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - prompt_path, - SECRETS_IFACE ("Prompt"), - "Prompt", - g_variant_new ("(s)", "0"), - G_VARIANT_TYPE ("()"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - prompt_cb, - task); - } - else - { - switch (task_data->action) - { - case SECRETS_SERVICE_ACTION_STORE: - do_store_auth_info (task); - break; - - case SECRETS_SERVICE_ACTION_QUERY: - /* Prompt successfull proceed to get secret */ - g_dbus_proxy_call (task_data->item_proxy, - "GetSecret", - g_variant_new ("(o)", - task_data->session_path), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - get_secret_cb, - task); - break; - } - } - g_variant_unref (output); -} - -static void -unlock_read_alias_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GError *error = NULL; - GVariant *output, *subresult; - gsize path_len = 0; - const gchar *collection_path; - const gchar *to_unlock[2]; - - task = user_data; - task_data = g_task_get_task_data (task); - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), - res, - &error); - if (output == NULL) - { - g_task_return_error (task, error); - return; - } - - subresult = g_variant_get_child_value (output, 0); - g_variant_unref (output); - - if (subresult == NULL) - { - GTK_NOTE (PRINTING, g_print ("Invalid ReadAlias response.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - - collection_path = g_variant_get_string (subresult, &path_len); - to_unlock[0] = collection_path; - to_unlock[1] = NULL; - - task_data->collection_path = g_strdup (collection_path); - - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - SECRETS_PATH, - SECRETS_IFACE ("Service"), - "Unlock", - g_variant_new ("(^ao)", to_unlock), - G_VARIANT_TYPE ("(aoo)"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - unlock_collection_cb, - task); - - g_variant_unref (subresult); -} - -static void -item_proxy_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GError *error = NULL; - GDBusProxy *item_proxy; - GVariant *locked; - gboolean is_locked; - - task = user_data; - task_data = g_task_get_task_data (task); - - item_proxy = g_dbus_proxy_new_finish (res, - &error); - if (item_proxy == NULL) - { - g_task_return_error (task, error); - return; - } - - task_data->item_proxy = item_proxy; - - locked = g_dbus_proxy_get_cached_property (item_proxy, "Locked"); - - if (locked == NULL) - { - GTK_NOTE (PRINTING, g_print ("Failed to look up \"Locked\" property on item.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - - g_variant_get (locked, "b", &is_locked); - g_variant_unref (locked); - - if (is_locked) - { - /* Go down the unlock -> lookup path */ - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - SECRETS_PATH, - SECRETS_IFACE ("Service"), - "ReadAlias", - g_variant_new ("(s)", "default"), - G_VARIANT_TYPE ("(o)"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - unlock_read_alias_cb, - task); - return; - } - - /* Unlocked proceed to get or store secret */ - switch (task_data->action) - { - case SECRETS_SERVICE_ACTION_STORE: - do_store_auth_info (task); - break; - - case SECRETS_SERVICE_ACTION_QUERY: - g_dbus_proxy_call (item_proxy, - "GetSecret", - g_variant_new ("(o)", - task_data->session_path), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - get_secret_cb, - task); - break; - } -} - -static void -search_items_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GError *error = NULL; - GVariant *output; - gsize array_cnt, - i; - gboolean found_item = FALSE; - - task = user_data; - task_data = g_task_get_task_data (task); - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), - res, - &error); - if (output == NULL) - { - g_task_return_error (task, error); - return; - } - - array_cnt = g_variant_n_children (output); - - for (i = 0; i < array_cnt; i++) - { - GVariant * const item_paths = g_variant_get_child_value (output, i); - const gchar **items = NULL; - - if (item_paths == NULL) - { - GTK_NOTE (PRINTING, - g_print ("SearchItems returned invalid result.\n")); - continue; - } - - items = g_variant_get_objv (item_paths, NULL); - - if (*items == NULL) - { - g_variant_unref (item_paths); - g_free ((gpointer) items); - continue; - } - - /* Access the first found item. */ - found_item = TRUE; - g_dbus_proxy_new (task_data->dbus_connection, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - SECRETS_BUS, - *items, - SECRETS_IFACE ("Item"), - g_task_get_cancellable (task), - item_proxy_cb, - task); - g_free ((gpointer) items); - g_variant_unref (item_paths); - break; - } - g_variant_unref (output); - - if (!found_item) - { - GTK_NOTE (PRINTING, g_print ("No match found in secrets service.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } -} - -static void -open_session_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - GVariant *output, - *session_variant; - SecretsServiceData *task_data; - GError *error = NULL; - - task = user_data; - task_data = g_task_get_task_data (task); - - output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), - res, - &error); - if (output == NULL) - { - g_task_return_error (task, error); - return; - } - - session_variant = g_variant_get_child_value (output, 1); - - if (session_variant == NULL) - { - GTK_NOTE (PRINTING, g_print ("Invalid session path response.\n")); - g_variant_unref (output); - g_task_return_pointer (task, NULL, NULL); - return; - } - - task_data->session_path = g_variant_dup_string (session_variant, NULL); - - if (task_data->session_path == NULL) - { - GTK_NOTE (PRINTING, g_print ("Invalid session path string value.\n")); - g_variant_unref (session_variant); - g_variant_unref (output); - g_task_return_pointer (task, NULL, NULL); - return; - } - - g_variant_unref (session_variant); - g_variant_unref (output); - - switch (task_data->action) - { - case SECRETS_SERVICE_ACTION_QUERY: - { - /* Search for the secret item */ - GVariant *secrets_attrs; - - secrets_attrs = create_attributes (task_data->printer_uri, NULL, NULL); - if (secrets_attrs == NULL) - { - GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n")); - g_task_return_pointer (task, NULL, NULL); - return; - } - - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - SECRETS_PATH, - SECRETS_IFACE ("Service"), - "SearchItems", - g_variant_new ("(@a{ss})", secrets_attrs), - G_VARIANT_TYPE ("(aoao)"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - search_items_cb, - task); - break; - } - - case SECRETS_SERVICE_ACTION_STORE: - { - /* Look up / unlock the default collection for storing */ - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - SECRETS_PATH, - SECRETS_IFACE ("Service"), - "ReadAlias", - g_variant_new ("(s)", "default"), - G_VARIANT_TYPE ("(o)"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - unlock_read_alias_cb, - task); - break; - } - } -} - -static void -get_connection_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task; - SecretsServiceData *task_data; - GError *error = NULL; - - task = user_data; - task_data = g_task_get_task_data (task); - - task_data->dbus_connection = g_bus_get_finish (res, &error); - if (task_data->dbus_connection == NULL) - { - g_task_return_error (task, error); - return; - } - - /* Now open a session */ - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - SECRETS_PATH, - SECRETS_IFACE ("Service"), - "OpenSession", - g_variant_new ("(sv)", "plain", - g_variant_new_string ("")), - G_VARIANT_TYPE ("(vo)"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - g_task_get_cancellable (task), - open_session_cb, - task); -} - -/** - * gtk_cups_secrets_service_watch: - * @appeared: The callback to call when the service interface appears - * @vanished: The callback to call when the service interface vanishes - * @user_data: A reference to the watching printbackend - * - * Registers a watch for the secrets service interface. - * - * Returns: The watcher id - */ -guint -gtk_cups_secrets_service_watch (GBusNameAppearedCallback appeared, - GBusNameVanishedCallback vanished, - gpointer user_data) -{ - return g_bus_watch_name (G_BUS_TYPE_SESSION, - SECRETS_BUS, - G_BUS_NAME_WATCHER_FLAGS_AUTO_START, - appeared, - vanished, - user_data, - NULL); -} - -void -cleanup_task_data (gpointer data) -{ - gint i; - SecretsServiceData *task_data = data; - - g_free (task_data->collection_path); - g_strfreev (task_data->auth_info_labels); - g_strfreev (task_data->auth_info_required); - g_free (task_data->printer_uri); - - if (task_data->auth_info != NULL) - { - for (i = 0; task_data->auth_info[i] != NULL; i++) - { - memset (task_data->auth_info[i], 0, strlen (task_data->auth_info[i])); - g_clear_pointer (&task_data->auth_info[i], g_free); - } - g_clear_pointer (&task_data->auth_info, g_free); - } - - if (task_data->prompt_subscription != 0) - { - g_dbus_connection_signal_unsubscribe (task_data->dbus_connection, - task_data->prompt_subscription); - task_data->prompt_subscription = 0; - } - - if (task_data->session_path != NULL) - { - g_dbus_connection_call (task_data->dbus_connection, - SECRETS_BUS, - task_data->session_path, - SECRETS_IFACE ("Session"), - "Close", - NULL, - G_VARIANT_TYPE ("()"), - G_DBUS_CALL_FLAGS_NONE, - SECRETS_TIMEOUT, - NULL, - NULL, - NULL); - } - - g_clear_object (&task_data->dbus_connection); - g_clear_pointer (&task_data->session_path, g_free); - g_clear_object (&task_data->item_proxy); -} - -/** - * gtk_cups_secrets_service_query_task: - * @source_object: Source object for this task - * @cancellable: Cancellable to cancel this task - * @callback: Callback to call once the query is finished - * @user_data: The user_data passed to the callback - * @printer_uri: URI of the printer - * @auth_info_required: Info required for authentication - * - * Checks if a secrets service as described by the secrets-service standard - * is available and if so it tries to find the authentication info in the - * default collection of the service. - * - * This is the entry point to a chain of async calls to open a session, - * search the secret, unlock the collection (if necessary) and finally - * to lookup the secret. - * - * See: http://standards.freedesktop.org/secret-service/ for documentation - * of the used API. - */ -void -gtk_cups_secrets_service_query_task (gpointer source_object, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - const gchar *printer_uri, - gchar **auth_info_required) -{ - GTask *task; - SecretsServiceData *task_data; - - task_data = g_new0 (SecretsServiceData, 1); - task_data->action = SECRETS_SERVICE_ACTION_QUERY; - task_data->printer_uri = g_strdup (printer_uri); - task_data->auth_info_required = g_strdupv (auth_info_required); - - task = g_task_new (source_object, cancellable, callback, user_data); - - g_task_set_task_data (task, task_data, cleanup_task_data); - - g_bus_get (G_BUS_TYPE_SESSION, cancellable, - get_connection_cb, task); -} - -static void -store_done_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GTask *task = (GTask *) res; - GError *error = NULL; - - g_task_propagate_pointer (task, &error); - - if (error != NULL) - { - GTK_NOTE (PRINTING, - g_print ("Failed to store auth info: %s\n", error->message)); - g_error_free (error); - } - - g_object_unref (task); - GTK_NOTE (PRINTING, - g_print ("gtk_cups_secrets_service_store finished.\n")); -} - -/** - * gtk_cups_secrets_service_store: - * @auth_info: Auth info that should be stored - * @auth_info_labels: The keys to use for the auth info - * @printer_uri: URI of the printer - * - * Tries to store the auth_info in a secrets service. - */ -void -gtk_cups_secrets_service_store (gchar **auth_info, - gchar **auth_info_labels, - const gchar *printer_uri) -{ - GTask *task; - SecretsServiceData *task_data; - - if (auth_info == NULL || auth_info_labels == NULL || printer_uri == NULL) - { - GTK_NOTE (PRINTING, - g_print ("Invalid call to gtk_cups_secrets_service_store.\n")); - return; - } - - task_data = g_new0 (SecretsServiceData, 1); - task_data->action = SECRETS_SERVICE_ACTION_STORE; - task_data->printer_uri = g_strdup (printer_uri); - task_data->auth_info = g_strdupv (auth_info); - task_data->auth_info_labels = g_strdupv (auth_info_labels); - - task = g_task_new (NULL, NULL, store_done_cb, NULL); - - g_task_set_task_data (task, task_data, cleanup_task_data); - - g_bus_get (G_BUS_TYPE_SESSION, NULL, - get_connection_cb, task); -} diff --git a/modules/printbackends/cups/gtkcupssecretsutils.h b/modules/printbackends/cups/gtkcupssecretsutils.h deleted file mode 100644 index 1a0424a3bf..0000000000 --- a/modules/printbackends/cups/gtkcupssecretsutils.h +++ /dev/null @@ -1,41 +0,0 @@ -/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords - * Copyright (C) 2014 Intevation GmbH - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ -#ifndef __GTK_SECRETS_UTILS_H__ -#define __GTK_SECRETS_UTILS_H__ - -#include - -#include "gtkcupsutils.h" - -G_BEGIN_DECLS - -void gtk_cups_secrets_service_query_task (gpointer source_object, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - const gchar *printer_uri, - gchar **auth_info_required); -guint gtk_cups_secrets_service_watch (GBusNameAppearedCallback appeared, - GBusNameVanishedCallback vanished, - gpointer user_data); -void gtk_cups_secrets_service_store (gchar **auth_info, - gchar **auth_info_labels, - const gchar *printer_uri); - -G_END_DECLS - -#endif /* __GTK_SECRETS_UTILS_H__ */ diff --git a/modules/printbackends/cups/gtkcupsutils.c b/modules/printbackends/cups/gtkcupsutils.c deleted file mode 100644 index 54239cad07..0000000000 --- a/modules/printbackends/cups/gtkcupsutils.c +++ /dev/null @@ -1,1663 +0,0 @@ -/* GTK - The GIMP Toolkit - * gtkcupsutils.h: Statemachine implementation of POST and GET - * cups calls which can be used to create a non-blocking cups API - * Copyright (C) 2006, 2007 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include "gtkcupsutils.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request); - -static void _connect (GtkCupsRequest *request); -static void _post_send (GtkCupsRequest *request); -static void _post_write_request (GtkCupsRequest *request); -static void _post_write_data (GtkCupsRequest *request); -static void _post_check (GtkCupsRequest *request); -static void _post_auth (GtkCupsRequest *request); -static void _post_read_response (GtkCupsRequest *request); - -static void _get_send (GtkCupsRequest *request); -static void _get_check (GtkCupsRequest *request); -static void _get_auth (GtkCupsRequest *request); -static void _get_read_data (GtkCupsRequest *request); - -struct _GtkCupsResult -{ - gchar *error_msg; - ipp_t *ipp_response; - GtkCupsErrorType error_type; - - /* some error types like HTTP_ERROR have a status and a code */ - int error_status; - int error_code; - - guint is_error : 1; - guint is_ipp_response : 1; -}; - - -#define _GTK_CUPS_MAX_ATTEMPTS 10 -#define _GTK_CUPS_MAX_CHUNK_SIZE 8192 - -static GtkCupsRequestStateFunc post_states[] = { - _connect, - _post_send, - _post_write_request, - _post_write_data, - _post_check, - _post_auth, - _post_read_response -}; - -static GtkCupsRequestStateFunc get_states[] = { - _connect, - _get_send, - _get_check, - _get_auth, - _get_read_data -}; - -#ifndef HAVE_CUPS_API_1_6 -#define ippSetOperation(ipp_request, ipp_op_id) ipp_request->request.op.operation_id = ipp_op_id -#define ippSetRequestId(ipp_request, ipp_rq_id) ipp_request->request.op.request_id = ipp_rq_id -#define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state -#define ippGetString(attr, index, foo) attr->values[index].string.text -#define ippGetCount(attr) attr->num_values - -int -ippSetVersion (ipp_t *ipp, - int major, - int minor) -{ - if (!ipp || major < 0 || minor < 0) - return 0; - - ipp->request.any.version[0] = major; - ipp->request.any.version[1] = minor; - - return 1; -} -#endif - -static void -gtk_cups_result_set_error (GtkCupsResult *result, - GtkCupsErrorType error_type, - int error_status, - int error_code, - const char *error_msg, - ...) -{ - va_list args; - - result->is_ipp_response = FALSE; - result->is_error = TRUE; - result->error_type = error_type; - result->error_status = error_status; - result->error_code = error_code; - - va_start (args, error_msg); - result->error_msg = g_strdup_vprintf (error_msg, args); - va_end (args); -} - -GtkCupsRequest * -gtk_cups_request_new_with_username (http_t *connection, - GtkCupsRequestType req_type, - gint operation_id, - GIOChannel *data_io, - const char *server, - const char *resource, - const char *username) -{ - GtkCupsRequest *request; - cups_lang_t *language; - - request = g_new0 (GtkCupsRequest, 1); - request->result = g_new0 (GtkCupsResult, 1); - - request->result->error_msg = NULL; - request->result->ipp_response = NULL; - - request->result->is_error = FALSE; - request->result->is_ipp_response = FALSE; - - request->type = req_type; - request->state = GTK_CUPS_REQUEST_START; - - request->password_state = GTK_CUPS_PASSWORD_NONE; - - if (server) - request->server = g_strdup (server); - else - request->server = g_strdup (cupsServer ()); - - - if (resource) - request->resource = g_strdup (resource); - else - request->resource = g_strdup ("/"); - - if (connection != NULL) - { - request->http = connection; - request->own_http = FALSE; - } - else - { - request->http = NULL; - request->http = httpConnectEncrypt (request->server, - ippPort (), - cupsEncryption ()); - - if (request->http) - httpBlocking (request->http, 0); - - request->own_http = TRUE; - } - - request->last_status = HTTP_CONTINUE; - - request->attempts = 0; - request->data_io = data_io; - - request->ipp_request = ippNew (); - ippSetOperation (request->ipp_request, operation_id); - ippSetRequestId (request->ipp_request, 1); - - language = cupsLangDefault (); - - gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, - "attributes-charset", - NULL, "utf-8"); - - gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, - "attributes-natural-language", - NULL, language->language); - - if (username != NULL) - gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", - NULL, username); - else - gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", - NULL, cupsUser ()); - - request->auth_info_required = NULL; - request->auth_info = NULL; - request->need_auth_info = FALSE; - - cupsLangFree (language); - - return request; -} - -GtkCupsRequest * -gtk_cups_request_new (http_t *connection, - GtkCupsRequestType req_type, - gint operation_id, - GIOChannel *data_io, - const char *server, - const char *resource) -{ - return gtk_cups_request_new_with_username (connection, - req_type, - operation_id, - data_io, - server, - resource, - NULL); -} - -static void -gtk_cups_result_free (GtkCupsResult *result) -{ - g_free (result->error_msg); - - if (result->ipp_response) - ippDelete (result->ipp_response); - - g_free (result); -} - -void -gtk_cups_request_free (GtkCupsRequest *request) -{ - if (request->own_http) - { - if (request->http) - httpClose (request->http); - } - - if (request->ipp_request) - ippDelete (request->ipp_request); - - g_free (request->server); - g_free (request->resource); - if (request->password != NULL) - { - memset (request->password, 0, strlen (request->password)); - g_free (request->password); - } - - g_free (request->username); - g_strfreev (request->auth_info_required); - - gtk_cups_result_free (request->result); - - g_free (request); -} - -gboolean -gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only) -{ - if (connect_only && request->state != GTK_CUPS_REQUEST_START) - return FALSE; - - do - { - if (request->type == GTK_CUPS_POST) - post_states[request->state] (request); - else if (request->type == GTK_CUPS_GET) - get_states[request->state] (request); - - if (gtk_cups_result_is_error (request->result)) - request->state = GTK_CUPS_REQUEST_DONE; - - if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && - request->state != GTK_CUPS_REQUEST_DONE) - { - /* TODO: should add a status or error code for too many failed attempts */ - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_GENERAL, - 0, - 0, - "Too many failed attempts"); - - request->state = GTK_CUPS_REQUEST_DONE; - } - - if (request->state == GTK_CUPS_REQUEST_DONE) - { - request->poll_state = GTK_CUPS_HTTP_IDLE; - return TRUE; - } - } - /* We need to recheck using httpCheck if the poll_state is read, because - * Cups has an internal read buffer. And if this buffer is filled, we may - * never get a poll event again. */ - while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http)); - - return FALSE; -} - -GtkCupsPollState -gtk_cups_request_get_poll_state (GtkCupsRequest *request) -{ - return request->poll_state; -} - - - -GtkCupsResult * -gtk_cups_request_get_result (GtkCupsRequest *request) -{ - return request->result; -} - -void -gtk_cups_request_ipp_add_string (GtkCupsRequest *request, - ipp_tag_t group, - ipp_tag_t tag, - const char *name, - const char *charset, - const char *value) -{ - ippAddString (request->ipp_request, - group, - tag, - name, - charset, - value); -} - -void -gtk_cups_request_ipp_add_strings (GtkCupsRequest *request, - ipp_tag_t group, - ipp_tag_t tag, - const char *name, - int num_values, - const char *charset, - const char *const *values) -{ - ippAddStrings (request->ipp_request, - group, - tag, - name, - num_values, - charset, - values); -} - -const char * -gtk_cups_request_ipp_get_string (GtkCupsRequest *request, - ipp_tag_t tag, - const char *name) -{ - ipp_attribute_t *attribute = NULL; - - if (request != NULL && request->ipp_request != NULL) - attribute = ippFindAttribute (request->ipp_request, - name, - tag); - - if (attribute != NULL && ippGetCount (attribute) > 0) - return ippGetString (attribute, 0, NULL); - else - return NULL; -} - - -typedef struct -{ - const char *name; - ipp_tag_t value_tag; -} ipp_option_t; - -static const ipp_option_t ipp_options[] = { - { "blackplot", IPP_TAG_BOOLEAN }, - { "brightness", IPP_TAG_INTEGER }, - { "columns", IPP_TAG_INTEGER }, - { "copies", IPP_TAG_INTEGER }, - { "finishings", IPP_TAG_ENUM }, - { "fitplot", IPP_TAG_BOOLEAN }, - { "gamma", IPP_TAG_INTEGER }, - { "hue", IPP_TAG_INTEGER }, - { "job-k-limit", IPP_TAG_INTEGER }, - { "job-page-limit", IPP_TAG_INTEGER }, - { "job-priority", IPP_TAG_INTEGER }, - { "job-quota-period", IPP_TAG_INTEGER }, - { "landscape", IPP_TAG_BOOLEAN }, - { "media", IPP_TAG_KEYWORD }, - { "mirror", IPP_TAG_BOOLEAN }, - { "natural-scaling", IPP_TAG_INTEGER }, - { "number-up", IPP_TAG_INTEGER }, - { "orientation-requested", IPP_TAG_ENUM }, - { "page-bottom", IPP_TAG_INTEGER }, - { "page-left", IPP_TAG_INTEGER }, - { "page-ranges", IPP_TAG_RANGE }, - { "page-right", IPP_TAG_INTEGER }, - { "page-top", IPP_TAG_INTEGER }, - { "penwidth", IPP_TAG_INTEGER }, - { "ppi", IPP_TAG_INTEGER }, - { "prettyprint", IPP_TAG_BOOLEAN }, - { "printer-resolution", IPP_TAG_RESOLUTION }, - { "print-quality", IPP_TAG_ENUM }, - { "saturation", IPP_TAG_INTEGER }, - { "scaling", IPP_TAG_INTEGER }, - { "sides", IPP_TAG_KEYWORD }, - { "wrap", IPP_TAG_BOOLEAN }, - { "number-up-layout", IPP_TAG_INTEGER } -}; - - -static ipp_tag_t -_find_option_tag (const gchar *option) -{ - int lower_bound, upper_bound, num_options; - int current_option; - ipp_tag_t result; - - result = IPP_TAG_ZERO; - - lower_bound = 0; - upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1; - - while (1) - { - int match; - current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound); - - match = strcasecmp (option, ipp_options[current_option].name); - if (match == 0) - { - result = ipp_options[current_option].value_tag; - return result; - } - else if (match < 0) - { - upper_bound = current_option - 1; - } - else - { - lower_bound = current_option + 1; - } - - if (upper_bound == lower_bound && upper_bound == current_option) - return result; - - if (upper_bound < 0) - return result; - - if (lower_bound > num_options) - return result; - - if (upper_bound < lower_bound) - return result; - } -} - -/* - * Note that this function uses IPP_TAG_JOB, so it is - * only suitable for IPP Group 2 attributes. - * See RFC 2911. - */ -void -gtk_cups_request_encode_option (GtkCupsRequest *request, - const gchar *option, - const gchar *value) -{ - ipp_tag_t option_tag; - - g_return_if_fail (option != NULL); - g_return_if_fail (value != NULL); - - option_tag = _find_option_tag (option); - - if (option_tag == IPP_TAG_ZERO) - { - option_tag = IPP_TAG_NAME; - if (strcasecmp (value, "true") == 0 || - strcasecmp (value, "false") == 0) - { - option_tag = IPP_TAG_BOOLEAN; - } - } - - switch (option_tag) - { - case IPP_TAG_INTEGER: - case IPP_TAG_ENUM: - ippAddInteger (request->ipp_request, - IPP_TAG_JOB, - option_tag, - option, - strtol (value, NULL, 0)); - break; - - case IPP_TAG_BOOLEAN: - { - char b; - - if (strcasecmp (value, "true") == 0 || - strcasecmp (value, "on") == 0 || - strcasecmp (value, "yes") == 0) - b = 1; - else - b = 0; - - ippAddBoolean (request->ipp_request, - IPP_TAG_JOB, - option, - b); - - break; - } - - case IPP_TAG_RANGE: - { - char *s; - int lower; - int upper; - - if (*value == '-') - { - lower = 1; - s = (char *)value; - } - else - lower = strtol (value, &s, 0); - - if (*s == '-') - { - if (s[1]) - upper = strtol (s + 1, NULL, 0); - else - upper = 2147483647; - } - else - upper = lower; - - ippAddRange (request->ipp_request, - IPP_TAG_JOB, - option, - lower, - upper); - - break; - } - - case IPP_TAG_RESOLUTION: - { - char *s; - int xres; - int yres; - ipp_res_t units; - - xres = strtol (value, &s, 0); - - if (*s == 'x') - yres = strtol (s + 1, &s, 0); - else - yres = xres; - - if (strcasecmp (s, "dpc") == 0) - units = IPP_RES_PER_CM; - else - units = IPP_RES_PER_INCH; - - ippAddResolution (request->ipp_request, - IPP_TAG_JOB, - option, - units, - xres, - yres); - - break; - } - - default: - { - char *values; - char *s; - int in_quotes; - char *next; - GPtrArray *strings; - - values = g_strdup (value); - strings = NULL; - in_quotes = 0; - - for (s = values, next = s; *s != '\0'; s++) - { - if (in_quotes != 2 && *s == '\'') - { - /* skip quoted value */ - if (in_quotes == 0) - in_quotes = 1; - else - in_quotes = 0; - } - else if (in_quotes != 1 && *s == '\"') - { - /* skip quoted value */ - if (in_quotes == 0) - in_quotes = 2; - else - in_quotes = 0; - } - else if (in_quotes == 0 && *s == ',') - { - /* found delimiter, add to value array */ - *s = '\0'; - if (strings == NULL) - strings = g_ptr_array_new (); - g_ptr_array_add (strings, next); - next = s + 1; - } - else if (in_quotes == 0 && *s == '\\' && s[1] != '\0') - { - /* skip escaped character */ - s++; - } - } - - if (strings == NULL) - { - /* single value */ - ippAddString (request->ipp_request, - IPP_TAG_JOB, - option_tag, - option, - NULL, - value); - } - else - { - /* multiple values */ - - /* add last value */ - g_ptr_array_add (strings, next); - - ippAddStrings (request->ipp_request, - IPP_TAG_JOB, - option_tag, - option, - strings->len, - NULL, - (const char **) strings->pdata); - g_ptr_array_free (strings, TRUE); - } - - g_free (values); - } - - break; - } -} - -void -gtk_cups_request_set_ipp_version (GtkCupsRequest *request, - gint major, - gint minor) -{ - ippSetVersion (request->ipp_request, major, minor); -} - -static void -_connect (GtkCupsRequest *request) -{ - request->poll_state = GTK_CUPS_HTTP_IDLE; - request->bytes_received = 0; - - if (request->http == NULL) - { - request->http = httpConnectEncrypt (request->server, - ippPort (), - cupsEncryption ()); - - if (request->http == NULL) - request->attempts++; - - if (request->http) - httpBlocking (request->http, 0); - - request->own_http = TRUE; - } - else - { - request->attempts = 0; - request->state++; - - /* we always write to the socket after we get - the connection */ - request->poll_state = GTK_CUPS_HTTP_WRITE; - } -} - -static void -_post_send (GtkCupsRequest *request) -{ - gchar length[255]; - struct stat data_info; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); - - request->poll_state = GTK_CUPS_HTTP_WRITE; - - if (request->data_io != NULL) - { - fstat (g_io_channel_unix_get_fd (request->data_io), &data_info); - sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size)); - } - else - sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request)); - - httpClearFields (request->http); - httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length); - httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); -#ifdef HAVE_HTTPGETAUTHSTRING - httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http)); -#else -#ifdef HAVE_HTTP_AUTHSTRING - httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring); -#endif -#endif - - if (httpPost (request->http, request->resource)) - { - if (httpReconnect (request->http)) - { - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - /* TODO: should add a status or error code for failed post */ - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_GENERAL, - 0, - 0, - "Failed Post"); - } - - request->attempts++; - return; - } - - request->attempts = 0; - - request->state = GTK_CUPS_POST_WRITE_REQUEST; - ippSetState (request->ipp_request, IPP_IDLE); -} - -static void -_post_write_request (GtkCupsRequest *request) -{ - ipp_state_t ipp_status; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); - - request->poll_state = GTK_CUPS_HTTP_WRITE; - - ipp_status = ippWrite (request->http, request->ipp_request); - - if (ipp_status == IPP_ERROR) - { - int cups_error = cupsLastError (); - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_IPP, - ipp_status, - cups_error, - "%s", - ippErrorString (cups_error)); - return; - } - - if (ipp_status == IPP_DATA) - { - if (request->data_io != NULL) - request->state = GTK_CUPS_POST_WRITE_DATA; - else - { - request->state = GTK_CUPS_POST_CHECK; - request->poll_state = GTK_CUPS_HTTP_READ; - } - } -} - -static void -_post_write_data (GtkCupsRequest *request) -{ - gsize bytes; - char buffer[_GTK_CUPS_MAX_CHUNK_SIZE]; - http_status_t http_status; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); - - request->poll_state = GTK_CUPS_HTTP_WRITE; - - if (httpCheck (request->http)) - http_status = httpUpdate (request->http); - else - http_status = request->last_status; - - request->last_status = http_status; - - - if (http_status == HTTP_CONTINUE || http_status == HTTP_OK) - { - GIOStatus io_status; - GError *error; - - error = NULL; - - /* send data */ - io_status = - g_io_channel_read_chars (request->data_io, - buffer, - _GTK_CUPS_MAX_CHUNK_SIZE, - &bytes, - &error); - - if (io_status == G_IO_STATUS_ERROR) - { - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_IO, - io_status, - error->code, - "Error reading from cache file: %s", - error->message); - - g_error_free (error); - return; - } - else if (bytes == 0 && io_status == G_IO_STATUS_EOF) - { - request->state = GTK_CUPS_POST_CHECK; - request->poll_state = GTK_CUPS_HTTP_READ; - - request->attempts = 0; - return; - } - - - if (httpWrite2 (request->http, buffer, bytes) < bytes) - { - int http_errno; - - http_errno = httpError (request->http); - - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_HTTP, - http_status, - http_errno, - "Error writing to socket in Post %s", - g_strerror (http_errno)); - return; - } - } - else if (http_status == HTTP_UNAUTHORIZED) - { - request->state = GTK_CUPS_POST_CHECK; - request->poll_state = GTK_CUPS_HTTP_READ; - - request->attempts = 0; - return; - } - else - { - request->attempts++; - } -} - -static void -_post_auth (GtkCupsRequest *request) -{ - if (request->password_state == GTK_CUPS_PASSWORD_HAS) - { - if (request->password == NULL) - { - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_AUTH, - 0, - 1, - "Canceled by user"); - } - else - request->state = GTK_CUPS_POST_CHECK; - } -} - -static void -_get_auth (GtkCupsRequest *request) -{ - if (request->password_state == GTK_CUPS_PASSWORD_HAS) - { - if (request->password == NULL) - { - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_AUTH, - 0, - 1, - "Canceled by user"); - } - else - request->state = GTK_CUPS_GET_CHECK; - } -} - -/* Very ugly hack: cups has a stupid synchronous password callback - * that doesn't even take the request or user data parameters, so - * we have to use a static variable to pass the password to it. - * Not threadsafe ! - * The callback sets cups_password to NULL to signal that the - * password has been used. - */ -static char *cups_password = NULL; -static char *cups_username = NULL; - -static const char * -passwordCB (const char *prompt) -{ - char *pwd = cups_password; - cups_password = NULL; - - cupsSetUser (cups_username); - - return pwd; -} - -static void -_post_check (GtkCupsRequest *request) -{ - http_status_t http_status; - - http_status = request->last_status; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status)); - - request->poll_state = GTK_CUPS_HTTP_READ; - - if (http_status == HTTP_CONTINUE) - { - goto again; - } - else if (http_status == HTTP_UNAUTHORIZED) - { - int auth_result = -1; - httpFlush (request->http); - - if (request->password_state == GTK_CUPS_PASSWORD_APPLIED) - { - request->poll_state = GTK_CUPS_HTTP_IDLE; - request->password_state = GTK_CUPS_PASSWORD_NOT_VALID; - request->state = GTK_CUPS_POST_AUTH; - request->need_password = TRUE; - - return; - } - - /* Negotiate */ - if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0) - { - auth_result = cupsDoAuthentication (request->http, "POST", request->resource); - } - /* Basic, BasicDigest, Digest and PeerCred */ - else - { - if (request->password_state == GTK_CUPS_PASSWORD_NONE) - { - cups_username = request->username; - cupsSetPasswordCB (passwordCB); - - /* This call success for PeerCred authentication */ - auth_result = cupsDoAuthentication (request->http, "POST", request->resource); - - if (auth_result != 0) - { - /* move to AUTH state to let the backend - * ask for a password - */ - request->poll_state = GTK_CUPS_HTTP_IDLE; - request->state = GTK_CUPS_POST_AUTH; - request->need_password = TRUE; - - return; - } - } - else - { - cups_password = request->password; - cups_username = request->username; - - auth_result = cupsDoAuthentication (request->http, "POST", request->resource); - - if (cups_password != NULL) - return; - - if (request->password != NULL) - { - memset (request->password, 0, strlen (request->password)); - g_free (request->password); - request->password = NULL; - } - - request->password_state = GTK_CUPS_PASSWORD_APPLIED; - } - } - - if (auth_result || - httpReconnect (request->http)) - { - /* if the password has been used, reset password_state - * so that we ask for a new one next time around - */ - if (cups_password == NULL) - request->password_state = GTK_CUPS_PASSWORD_NONE; - - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_AUTH, - 0, - 0, - "Not authorized"); - return; - } - - if (request->data_io != NULL) - g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL); - - request->state = GTK_CUPS_POST_CONNECT; - request->poll_state = GTK_CUPS_HTTP_WRITE; - } - else if (http_status == HTTP_ERROR) - { - int error = httpError (request->http); -#ifdef G_OS_WIN32 - if (error != WSAENETDOWN && error != WSAENETUNREACH) -#else - if (error != ENETDOWN && error != ENETUNREACH) -#endif /* G_OS_WIN32 */ - { - request->attempts++; - goto again; - } - else - { - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_HTTP, - http_status, - error, - "Unknown HTTP error"); - - return; - } - } - else if (http_status == HTTP_UPGRADE_REQUIRED) - { - /* Flush any error message... */ - httpFlush (request->http); - - cupsSetEncryption (HTTP_ENCRYPT_REQUIRED); - request->state = GTK_CUPS_POST_CONNECT; - - /* Reconnect... */ - httpReconnect (request->http); - - /* Upgrade with encryption... */ - httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); - - request->attempts++; - goto again; - } - else if (http_status != HTTP_OK) - { - int http_errno; - - http_errno = httpError (request->http); - - if (http_errno == EPIPE) - request->state = GTK_CUPS_POST_CONNECT; - else - { - request->state = GTK_CUPS_POST_DONE; - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_HTTP, - http_status, - http_errno, - "HTTP Error in POST %s", - g_strerror (http_errno)); - request->poll_state = GTK_CUPS_HTTP_IDLE; - - httpFlush (request->http); - return; - } - - request->poll_state = GTK_CUPS_HTTP_IDLE; - request->last_status = HTTP_CONTINUE; - - httpFlush (request->http); - if (request->own_http) - httpClose (request->http); - request->http = NULL; - - return; - } - else - { - request->state = GTK_CUPS_POST_READ_RESPONSE; - return; - } - - again: - http_status = HTTP_CONTINUE; - - if (httpCheck (request->http)) - http_status = httpUpdate (request->http); - - request->last_status = http_status; -} - -static void -_post_read_response (GtkCupsRequest *request) -{ - ipp_state_t ipp_status; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); - - request->poll_state = GTK_CUPS_HTTP_READ; - - if (request->result->ipp_response == NULL) - request->result->ipp_response = ippNew(); - - ipp_status = ippRead (request->http, - request->result->ipp_response); - - if (ipp_status == IPP_ERROR) - { - int ipp_error = cupsLastError (); - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_IPP, - ipp_status, - ipp_error, - "%s", - ippErrorString (ipp_error)); - - ippDelete (request->result->ipp_response); - request->result->ipp_response = NULL; - - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - } - else if (ipp_status == IPP_DATA) - { - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - } -} - -static void -_get_send (GtkCupsRequest *request) -{ - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); - - request->poll_state = GTK_CUPS_HTTP_WRITE; - - if (request->data_io == NULL) - { - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_IO, - G_IO_STATUS_ERROR, - G_IO_CHANNEL_ERROR_FAILED, - "Get requires an open io channel"); - - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - return; - } - - httpClearFields (request->http); -#ifdef HAVE_HTTPGETAUTHSTRING - httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http)); -#else -#ifdef HAVE_HTTP_AUTHSTRING - httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring); -#endif -#endif - - if (httpGet (request->http, request->resource)) - { - if (httpReconnect (request->http)) - { - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - /* TODO: should add a status or error code for failed GET */ - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_GENERAL, - 0, - 0, - "Failed Get"); - } - - request->attempts++; - return; - } - - if (httpCheck (request->http)) - request->last_status = httpUpdate (request->http); - - request->attempts = 0; - - request->state = GTK_CUPS_GET_CHECK; - request->poll_state = GTK_CUPS_HTTP_READ; - - ippSetState (request->ipp_request, IPP_IDLE); -} - -static void -_get_check (GtkCupsRequest *request) -{ - http_status_t http_status; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); - - http_status = request->last_status; - - request->poll_state = GTK_CUPS_HTTP_READ; - - if (http_status == HTTP_CONTINUE) - { - goto again; - } - else if (http_status == HTTP_UNAUTHORIZED) - { - int auth_result = -1; - httpFlush (request->http); - - if (request->password_state == GTK_CUPS_PASSWORD_APPLIED) - { - request->poll_state = GTK_CUPS_HTTP_IDLE; - request->password_state = GTK_CUPS_PASSWORD_NOT_VALID; - request->state = GTK_CUPS_GET_AUTH; - request->need_password = TRUE; - - return; - } - - /* Negotiate */ - if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0) - { - auth_result = cupsDoAuthentication (request->http, "GET", request->resource); - } - /* Basic, BasicDigest, Digest and PeerCred */ - else - { - if (request->password_state == GTK_CUPS_PASSWORD_NONE) - { - cups_username = request->username; - cupsSetPasswordCB (passwordCB); - - /* This call success for PeerCred authentication */ - auth_result = cupsDoAuthentication (request->http, "GET", request->resource); - - if (auth_result != 0) - { - /* move to AUTH state to let the backend - * ask for a password - */ - request->poll_state = GTK_CUPS_HTTP_IDLE; - request->state = GTK_CUPS_GET_AUTH; - request->need_password = TRUE; - - return; - } - } - else - { - cups_password = request->password; - cups_username = request->username; - - auth_result = cupsDoAuthentication (request->http, "GET", request->resource); - - if (cups_password != NULL) - return; - - if (request->password != NULL) - { - memset (request->password, 0, strlen (request->password)); - g_free (request->password); - request->password = NULL; - } - - request->password_state = GTK_CUPS_PASSWORD_APPLIED; - } - } - - if (auth_result || - httpReconnect (request->http)) - { - /* if the password has been used, reset password_state - * so that we ask for a new one next time around - */ - if (cups_password == NULL) - request->password_state = GTK_CUPS_PASSWORD_NONE; - - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_AUTH, - 0, - 0, - "Not authorized"); - return; - } - - request->state = GTK_CUPS_GET_CONNECT; - request->last_status = HTTP_CONTINUE; - - return; - } - else if (http_status == HTTP_UPGRADE_REQUIRED) - { - /* Flush any error message... */ - httpFlush (request->http); - - cupsSetEncryption (HTTP_ENCRYPT_REQUIRED); - request->state = GTK_CUPS_GET_CONNECT; - - /* Reconnect... */ - httpReconnect (request->http); - - /* Upgrade with encryption... */ - httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); - - request->attempts++; - goto again; - } - else if (http_status != HTTP_OK) - { - int http_errno; - - http_errno = httpError (request->http); - - if (http_errno == EPIPE) - request->state = GTK_CUPS_GET_CONNECT; - else - { - request->state = GTK_CUPS_GET_DONE; - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_HTTP, - http_status, - http_errno, - "HTTP Error in GET %s", - g_strerror (http_errno)); - request->poll_state = GTK_CUPS_HTTP_IDLE; - httpFlush (request->http); - - return; - } - - request->poll_state = GTK_CUPS_HTTP_IDLE; - request->last_status = HTTP_CONTINUE; - - httpFlush (request->http); - if (request->own_http) - httpClose (request->http); - request->http = NULL; - - return; - } - else - { - request->state = GTK_CUPS_GET_READ_DATA; - return; - } - - again: - http_status = HTTP_CONTINUE; - - if (httpCheck (request->http)) - http_status = httpUpdate (request->http); - - request->last_status = http_status; - -} - -static void -_get_read_data (GtkCupsRequest *request) -{ - char buffer[_GTK_CUPS_MAX_CHUNK_SIZE]; - gsize bytes; - gsize bytes_written; - GIOStatus io_status; - GError *error; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); - - error = NULL; - - request->poll_state = GTK_CUPS_HTTP_READ; - - bytes = httpRead2 (request->http, buffer, sizeof (buffer)); - request->bytes_received += bytes; - - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes)); - - io_status = - g_io_channel_write_chars (request->data_io, - buffer, - bytes, - &bytes_written, - &error); - - if (io_status == G_IO_STATUS_ERROR) - { - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - gtk_cups_result_set_error (request->result, - GTK_CUPS_ERROR_IO, - io_status, - error->code, - error->message); - g_error_free (error); - } - - /* Stop if we do not expect any more data or EOF was received. */ - if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0) - { - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - - return; - } -} - -gboolean -gtk_cups_request_is_done (GtkCupsRequest *request) -{ - return (request->state == GTK_CUPS_REQUEST_DONE); -} - -gboolean -gtk_cups_result_is_error (GtkCupsResult *result) -{ - return result->is_error; -} - -ipp_t * -gtk_cups_result_get_response (GtkCupsResult *result) -{ - return result->ipp_response; -} - -GtkCupsErrorType -gtk_cups_result_get_error_type (GtkCupsResult *result) -{ - return result->error_type; -} - -int -gtk_cups_result_get_error_status (GtkCupsResult *result) -{ - return result->error_status; -} - -int -gtk_cups_result_get_error_code (GtkCupsResult *result) -{ - return result->error_code; -} - -const char * -gtk_cups_result_get_error_string (GtkCupsResult *result) -{ - return result->error_msg; -} - -/* This function allocates new instance of GtkCupsConnectionTest() and creates - * a socket for communication with a CUPS server 'server'. - */ -GtkCupsConnectionTest * -gtk_cups_connection_test_new (const char *server, - const int port) -{ - GtkCupsConnectionTest *result = NULL; - gchar *port_str = NULL; - - result = g_new (GtkCupsConnectionTest, 1); - - if (port >= 0) - port_str = g_strdup_printf ("%d", port); - else - port_str = g_strdup_printf ("%d", ippPort ()); - - if (server != NULL) - result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str); - else - result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str); - - g_free (port_str); - - result->socket = -1; - result->current_addr = NULL; - result->last_wrong_addr = NULL; - result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE; - - result->at_init = gtk_cups_connection_test_get_state (result); - - return result; -} - - -/* A non-blocking test whether it is possible to connect to a CUPS server specified - * inside of GtkCupsConnectionTest structure. - * - you need to check it more then once. - * The connection is closed after a successful connection. - */ -GtkCupsConnectionState -gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test) -{ - GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE; - http_addrlist_t *iter; - gint error_code; - gint flags; - gint code; - - if (test == NULL) - return GTK_CUPS_CONNECTION_NOT_AVAILABLE; - - if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE) - { - test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE; - return GTK_CUPS_CONNECTION_AVAILABLE; - } - else - { - if (test->socket == -1) - { - if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL) - iter = test->last_wrong_addr->next; - else - { - test->last_wrong_addr = NULL; - iter = test->addrlist; - } - - while (iter) - { - test->socket = socket (iter->addr.addr.sa_family, - SOCK_STREAM, - 0); - - if (test->socket >= 0) - { - flags = fcntl (test->socket, F_GETFL); - - if (flags != -1) - flags |= O_NONBLOCK; - - fcntl (test->socket, F_SETFL, flags); - - test->current_addr = iter; - - break; - } - iter = iter->next; - } - } - - if (test->socket >= 0) - { - code = connect (test->socket, - &test->current_addr->addr.addr, - httpAddrLength (&test->current_addr->addr)); - - error_code = errno; - - if (code == 0 || error_code == EISCONN) - { - close (test->socket); - test->socket = -1; - test->current_addr = NULL; - result = GTK_CUPS_CONNECTION_AVAILABLE; - } - else - { - if (error_code == EALREADY || error_code == EINPROGRESS) - result = GTK_CUPS_CONNECTION_IN_PROGRESS; - else - { - close (test->socket); - test->socket = -1; - test->last_wrong_addr = test->current_addr; - result = GTK_CUPS_CONNECTION_NOT_AVAILABLE; - } - } - } - - return result; - } -} - -/* This function frees memory used by the GtkCupsConnectionTest structure. - */ -void -gtk_cups_connection_test_free (GtkCupsConnectionTest *test) -{ - if (test == NULL) - return; - - test->current_addr = NULL; - test->last_wrong_addr = NULL; - httpAddrFreeList (test->addrlist); - if (test->socket != -1) - { - close (test->socket); - test->socket = -1; - } - g_free (test); -} diff --git a/modules/printbackends/cups/gtkcupsutils.h b/modules/printbackends/cups/gtkcupsutils.h deleted file mode 100644 index 500bb4fecf..0000000000 --- a/modules/printbackends/cups/gtkcupsutils.h +++ /dev/null @@ -1,196 +0,0 @@ -/* gtkcupsutils.h - * Copyright (C) 2006 John (J5) Palmieri - * - * 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 . - */ - -#ifndef __GTK_CUPS_UTILS_H__ -#define __GTK_CUPS_UTILS_H__ - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -typedef struct _GtkCupsRequest GtkCupsRequest; -typedef struct _GtkCupsResult GtkCupsResult; -typedef struct _GtkCupsConnectionTest GtkCupsConnectionTest; - -typedef enum -{ - GTK_CUPS_ERROR_HTTP, - GTK_CUPS_ERROR_IPP, - GTK_CUPS_ERROR_IO, - GTK_CUPS_ERROR_AUTH, - GTK_CUPS_ERROR_GENERAL -} GtkCupsErrorType; - -typedef enum -{ - GTK_CUPS_POST, - GTK_CUPS_GET -} GtkCupsRequestType; - - -/** - * Direction we should be polling the http socket on. - * We are either reading or writting at each state. - * This makes it easy for mainloops to connect to poll. - */ -typedef enum -{ - GTK_CUPS_HTTP_IDLE, - GTK_CUPS_HTTP_READ, - GTK_CUPS_HTTP_WRITE -} GtkCupsPollState; - -typedef enum -{ - GTK_CUPS_CONNECTION_AVAILABLE, - GTK_CUPS_CONNECTION_NOT_AVAILABLE, - GTK_CUPS_CONNECTION_IN_PROGRESS -} GtkCupsConnectionState; - -typedef enum -{ - GTK_CUPS_PASSWORD_NONE, - GTK_CUPS_PASSWORD_REQUESTED, - GTK_CUPS_PASSWORD_HAS, - GTK_CUPS_PASSWORD_APPLIED, - GTK_CUPS_PASSWORD_NOT_VALID -} GtkCupsPasswordState; - -struct _GtkCupsRequest -{ - GtkCupsRequestType type; - - http_t *http; - http_status_t last_status; - ipp_t *ipp_request; - - gchar *server; - gchar *resource; - GIOChannel *data_io; - gint attempts; - - GtkCupsResult *result; - - gint state; - GtkCupsPollState poll_state; - guint64 bytes_received; - - gchar *password; - gchar *username; - - gint own_http : 1; - gint need_password : 1; - gint need_auth_info : 1; - gchar **auth_info_required; - gchar **auth_info; - GtkCupsPasswordState password_state; -}; - -struct _GtkCupsConnectionTest -{ - GtkCupsConnectionState at_init; - http_addrlist_t *addrlist; - http_addrlist_t *current_addr; - http_addrlist_t *last_wrong_addr; - gint socket; -}; - -#define GTK_CUPS_REQUEST_START 0 -#define GTK_CUPS_REQUEST_DONE 500 - -/* POST states */ -enum -{ - GTK_CUPS_POST_CONNECT = GTK_CUPS_REQUEST_START, - GTK_CUPS_POST_SEND, - GTK_CUPS_POST_WRITE_REQUEST, - GTK_CUPS_POST_WRITE_DATA, - GTK_CUPS_POST_CHECK, - GTK_CUPS_POST_AUTH, - GTK_CUPS_POST_READ_RESPONSE, - GTK_CUPS_POST_DONE = GTK_CUPS_REQUEST_DONE -}; - -/* GET states */ -enum -{ - GTK_CUPS_GET_CONNECT = GTK_CUPS_REQUEST_START, - GTK_CUPS_GET_SEND, - GTK_CUPS_GET_CHECK, - GTK_CUPS_GET_AUTH, - GTK_CUPS_GET_READ_DATA, - GTK_CUPS_GET_DONE = GTK_CUPS_REQUEST_DONE -}; - -GtkCupsRequest * gtk_cups_request_new_with_username (http_t *connection, - GtkCupsRequestType req_type, - gint operation_id, - GIOChannel *data_io, - const char *server, - const char *resource, - const char *username); -GtkCupsRequest * gtk_cups_request_new (http_t *connection, - GtkCupsRequestType req_type, - gint operation_id, - GIOChannel *data_io, - const char *server, - const char *resource); -void gtk_cups_request_ipp_add_string (GtkCupsRequest *request, - ipp_tag_t group, - ipp_tag_t tag, - const char *name, - const char *charset, - const char *value); -void gtk_cups_request_ipp_add_strings (GtkCupsRequest *request, - ipp_tag_t group, - ipp_tag_t tag, - const char *name, - int num_values, - const char *charset, - const char * const *values); -const char * gtk_cups_request_ipp_get_string (GtkCupsRequest *request, - ipp_tag_t tag, - const char *name); -gboolean gtk_cups_request_read_write (GtkCupsRequest *request, - gboolean connect_only); -GtkCupsPollState gtk_cups_request_get_poll_state (GtkCupsRequest *request); -void gtk_cups_request_free (GtkCupsRequest *request); -GtkCupsResult * gtk_cups_request_get_result (GtkCupsRequest *request); -gboolean gtk_cups_request_is_done (GtkCupsRequest *request); -void gtk_cups_request_encode_option (GtkCupsRequest *request, - const gchar *option, - const gchar *value); -void gtk_cups_request_set_ipp_version (GtkCupsRequest *request, - gint major, - gint minor); -gboolean gtk_cups_result_is_error (GtkCupsResult *result); -ipp_t * gtk_cups_result_get_response (GtkCupsResult *result); -GtkCupsErrorType gtk_cups_result_get_error_type (GtkCupsResult *result); -int gtk_cups_result_get_error_status (GtkCupsResult *result); -int gtk_cups_result_get_error_code (GtkCupsResult *result); -const char * gtk_cups_result_get_error_string (GtkCupsResult *result); -GtkCupsConnectionTest * gtk_cups_connection_test_new (const char *server, - const int port); -GtkCupsConnectionState gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test); -void gtk_cups_connection_test_free (GtkCupsConnectionTest *test); - -G_END_DECLS -#endif diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c deleted file mode 100644 index 09e18b170e..0000000000 --- a/modules/printbackends/cups/gtkprintbackendcups.c +++ /dev/null @@ -1,6745 +0,0 @@ -/* GTK - The GIMP Toolkit - * gtkprintbackendcups.h: Default implementation of GtkPrintBackend - * for the Common Unix Print System (CUPS) - * Copyright (C) 2006, 2007 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include -/* 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "gtkprintbackendcups.h" -#include "gtkprintercups.h" - -#include "gtkcupsutils.h" -#include "gtkcupssecretsutils.h" - -#include - -#ifdef HAVE_COLORD -#include -#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 \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 \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 \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 \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 - 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; -} diff --git a/modules/printbackends/cups/gtkprintbackendcups.h b/modules/printbackends/cups/gtkprintbackendcups.h deleted file mode 100644 index d4e35ddf23..0000000000 --- a/modules/printbackends/cups/gtkprintbackendcups.h +++ /dev/null @@ -1,40 +0,0 @@ -/* GTK - The GIMP Toolkit - * gtkprintbackendcups.h: Default implementation of GtkPrintBackend for the Common Unix Print System (CUPS) - * Copyright (C) 2006, 2007 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_PRINT_BACKEND_CUPS_H__ -#define __GTK_PRINT_BACKEND_CUPS_H__ - -#include -#include "gtkprintbackend.h" - -G_BEGIN_DECLS - -#define GTK_TYPE_PRINT_BACKEND_CUPS (gtk_print_backend_cups_get_type ()) -#define GTK_PRINT_BACKEND_CUPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCups)) -#define GTK_IS_PRINT_BACKEND_CUPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CUPS)) - -typedef struct _GtkPrintBackendCups GtkPrintBackendCups; - -GtkPrintBackend *gtk_print_backend_cups_new (void); -GType gtk_print_backend_cups_get_type (void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GTK_PRINT_BACKEND_CUPS_H__ */ - - diff --git a/modules/printbackends/cups/gtkprintercups.c b/modules/printbackends/cups/gtkprintercups.c deleted file mode 100644 index 525382aa8a..0000000000 --- a/modules/printbackends/cups/gtkprintercups.c +++ /dev/null @@ -1,650 +0,0 @@ -/* GtkPrinterCupsCups - * Copyright (C) 2006 John (J5) Palmieri - * Copyright (C) 2011 Richard Hughes - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include - -#ifdef HAVE_COLORD -#include -#endif - -#include "gtkintl.h" -#include "gtkprintercups.h" - -enum { - PROP_0, - PROP_PROFILE_TITLE -}; - -static void gtk_printer_cups_init (GtkPrinterCups *printer); -static void gtk_printer_cups_class_init (GtkPrinterCupsClass *class); -static void gtk_printer_cups_finalize (GObject *object); - -static GtkPrinterClass *gtk_printer_cups_parent_class; -static GType gtk_printer_cups_type = 0; - -static void gtk_printer_cups_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_printer_cups_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -void -gtk_printer_cups_register_type (GTypeModule *module) -{ - const GTypeInfo object_info = - { - sizeof (GtkPrinterCupsClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gtk_printer_cups_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkPrinterCups), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_printer_cups_init, - }; - - gtk_printer_cups_type = g_type_module_register_type (module, - GTK_TYPE_PRINTER, - "GtkPrinterCups", - &object_info, 0); -} - -GType -gtk_printer_cups_get_type (void) -{ - return gtk_printer_cups_type; -} - -static void -gtk_printer_cups_class_init (GtkPrinterCupsClass *class) -{ - GObjectClass *object_class = (GObjectClass *) class; - - object_class->finalize = gtk_printer_cups_finalize; - object_class->set_property = gtk_printer_cups_set_property; - object_class->get_property = gtk_printer_cups_get_property; - - gtk_printer_cups_parent_class = g_type_class_peek_parent (class); - - g_object_class_install_property (G_OBJECT_CLASS (class), - PROP_PROFILE_TITLE, - g_param_spec_string ("profile-title", - P_("Color Profile Title"), - P_("The title of the color profile to use"), - "", - G_PARAM_READABLE)); -} - -static void -gtk_printer_cups_init (GtkPrinterCups *printer) -{ - printer->device_uri = NULL; - printer->original_device_uri = NULL; - printer->printer_uri = NULL; - printer->state = 0; - printer->hostname = NULL; - printer->port = 0; - printer->ppd_name = NULL; - printer->ppd_file = NULL; - printer->default_cover_before = NULL; - printer->default_cover_after = NULL; - printer->remote = FALSE; - printer->get_remote_ppd_poll = 0; - printer->get_remote_ppd_attempts = 0; - printer->remote_cups_connection_test = NULL; - printer->auth_info_required = NULL; - printer->default_number_up = 1; -#ifdef HAVE_CUPS_API_1_6 - printer->avahi_browsed = FALSE; - printer->avahi_name = NULL; - printer->avahi_type = NULL; - printer->avahi_domain = NULL; -#endif - printer->ipp_version_major = 1; - printer->ipp_version_minor = 1; - printer->supports_copies = FALSE; - printer->supports_collate = FALSE; - printer->supports_number_up = FALSE; - printer->media_default = NULL; - printer->media_supported = NULL; - printer->media_size_supported = NULL; - printer->media_bottom_margin_default = 0; - printer->media_top_margin_default = 0; - printer->media_left_margin_default = 0; - printer->media_right_margin_default = 0; - printer->media_margin_default_set = FALSE; - printer->sides_default = NULL; - printer->sides_supported = NULL; - printer->number_of_covers = 0; - printer->covers = NULL; - printer->output_bin_default = NULL; - printer->output_bin_supported = NULL; -} - -static void -gtk_printer_cups_finalize (GObject *object) -{ - GtkPrinterCups *printer; - - g_return_if_fail (object != NULL); - - printer = GTK_PRINTER_CUPS (object); - - g_free (printer->device_uri); - g_free (printer->original_device_uri); - g_free (printer->printer_uri); - g_free (printer->hostname); - g_free (printer->ppd_name); - g_free (printer->default_cover_before); - g_free (printer->default_cover_after); - g_strfreev (printer->auth_info_required); - -#ifdef HAVE_COLORD - if (printer->colord_cancellable) - { - g_cancellable_cancel (printer->colord_cancellable); - g_object_unref (printer->colord_cancellable); - } - g_free (printer->colord_title); - g_free (printer->colord_qualifier); - if (printer->colord_client) - g_object_unref (printer->colord_client); - if (printer->colord_device) - g_object_unref (printer->colord_device); - if (printer->colord_profile) - g_object_unref (printer->colord_profile); -#endif - -#ifdef HAVE_CUPS_API_1_6 - g_free (printer->avahi_name); - g_free (printer->avahi_type); - g_free (printer->avahi_domain); -#endif - - g_strfreev (printer->covers); - - if (printer->ppd_file) - ppdClose (printer->ppd_file); - - g_free (printer->media_default); - g_list_free_full (printer->media_supported, g_free); - g_list_free_full (printer->media_size_supported, g_free); - - g_free (printer->sides_default); - g_list_free_full (printer->sides_supported, g_free); - - g_free (printer->output_bin_default); - g_list_free_full (printer->output_bin_supported, g_free); - - if (printer->get_remote_ppd_poll > 0) - g_source_remove (printer->get_remote_ppd_poll); - printer->get_remote_ppd_attempts = 0; - - gtk_cups_connection_test_free (printer->remote_cups_connection_test); - - G_OBJECT_CLASS (gtk_printer_cups_parent_class)->finalize (object); -} - -static void -gtk_printer_cups_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gtk_printer_cups_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ -#ifdef HAVE_COLORD - GtkPrinterCups *printer = GTK_PRINTER_CUPS (object); -#endif - - switch (prop_id) - { - case PROP_PROFILE_TITLE: -#ifdef HAVE_COLORD - if (printer->colord_title) - g_value_set_string (value, printer->colord_title); - else - g_value_set_static_string (value, ""); -#else - g_value_set_static_string (value, NULL); -#endif - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -#ifdef HAVE_COLORD - -static void -colord_update_ui_from_settings (GtkPrinterCups *printer) -{ - const gchar *title = NULL; - - /* not yet connected to colord */ - if (printer->colord_client == NULL) - goto out; - if (!cd_client_get_connected (printer->colord_client)) - goto out; - - /* failed to get a colord device for the printer */ - if (printer->colord_device == NULL) - { - /* TRANSLATORS: when we're running an old CUPS, and - * it hasn't registered the device with colord */ - title = _("Color management unavailable"); - goto out; - } - - /* when colord prevents us from connecting (should not happen) */ - if (!cd_device_get_connected (printer->colord_device)) - goto out; - - /* failed to get a colord device for the printer */ - if (printer->colord_profile == NULL) - { - /* TRANSLATORS: when there is no color profile available */ - title = _("No profile available"); - goto out; - } - - /* when colord prevents us from connecting (should not happen) */ - if (!cd_profile_get_connected (printer->colord_profile)) - goto out; - title = cd_profile_get_title (printer->colord_profile); - if (title == NULL) - { - /* TRANSLATORS: when the color profile has no title */ - title = _("Unspecified profile"); - goto out; - } - -out: - /* SUCCESS! */ - if (g_strcmp0 (title, printer->colord_title) != 0) - { - g_free (printer->colord_title); - printer->colord_title = g_strdup (title); - g_object_notify (G_OBJECT (printer), "profile-title"); - } - return; -} - -static void -colord_client_profile_connect_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - gboolean ret; - GError *error = NULL; - GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); - - ret = cd_profile_connect_finish (CD_PROFILE (source_object), - res, - &error); - if (!ret) - { - g_warning ("failed to get properties from the profile: %s", - error->message); - g_error_free (error); - } - - /* update the UI */ - colord_update_ui_from_settings (printer); - - g_object_unref (printer); -} - -static void -colord_client_device_get_profile_for_qualifiers_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); - GError *error = NULL; - - printer->colord_profile = cd_device_get_profile_for_qualifiers_finish (printer->colord_device, - res, - &error); - if (printer->colord_profile == NULL) - { - /* not having a profile for a qualifier is not a warning */ - g_debug ("no profile for device %s: %s", - cd_device_get_id (printer->colord_device), - error->message); - g_error_free (error); - goto out; - } - - /* get details about the profile */ - cd_profile_connect (printer->colord_profile, - printer->colord_cancellable, - colord_client_profile_connect_cb, - g_object_ref (printer)); -out: - /* update the UI */ - colord_update_ui_from_settings (printer); - - g_object_unref (printer); -} - -void -gtk_printer_cups_update_settings (GtkPrinterCups *printer, - GtkPrintSettings *settings, - GtkPrinterOptionSet *set) -{ - gchar *qualifier = NULL; - gchar **qualifiers = NULL; - GtkPrinterOption *option; - const gchar *format[3]; - - /* nothing set yet */ - if (printer->colord_device == NULL) - goto out; - if (!cd_device_get_connected (printer->colord_device)) - goto out; - - /* cupsICCQualifier1 */ - option = gtk_printer_option_set_lookup (set, "cups-ColorSpace"); - if (option == NULL) - option = gtk_printer_option_set_lookup (set, "cups-ColorModel"); - if (option != NULL) - format[0] = option->value; - else - format[0] = "*"; - - /* cupsICCQualifier2 */ - option = gtk_printer_option_set_lookup (set, "cups-OutputMode"); - if (option != NULL) - format[1] = option->value; - else - format[1] = "*"; - - /* cupsICCQualifier3 */ - option = gtk_printer_option_set_lookup (set, "cups-Resolution"); - if (option != NULL) - format[2] = option->value; - else - format[2] = "*"; - - /* get profile for the device given the qualifier */ - qualifier = g_strdup_printf ("%s.%s.%s,%s.%s.*,%s.*.*", - format[0], format[1], format[2], - format[0], format[1], - format[0]); - - /* only requery colord if the option that was changed would give - * us a different profile result */ - if (g_strcmp0 (qualifier, printer->colord_qualifier) == 0) - goto out; - - qualifiers = g_strsplit (qualifier, ",", -1); - cd_device_get_profile_for_qualifiers (printer->colord_device, - (const gchar **) qualifiers, - printer->colord_cancellable, - colord_client_device_get_profile_for_qualifiers_cb, - g_object_ref (printer)); - - /* save for the future */ - g_free (printer->colord_qualifier); - printer->colord_qualifier = g_strdup (qualifier); - - /* update the UI */ - colord_update_ui_from_settings (printer); -out: - g_free (qualifier); - g_strfreev (qualifiers); -} - -static void -colord_client_device_connect_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); - gboolean ret; - GError *error = NULL; - - /* get details about the device */ - ret = cd_device_connect_finish (CD_DEVICE (source_object), res, &error); - if (!ret) - { - g_warning ("failed to get properties from the colord device: %s", - error->message); - g_error_free (error); - goto out; - } -out: - /* update the UI */ - colord_update_ui_from_settings (printer); - - g_object_unref (printer); -} - -static void -colord_client_find_device_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); - GError *error = NULL; - - /* get the new device */ - printer->colord_device = cd_client_find_device_finish (printer->colord_client, - res, - &error); - if (printer->colord_device == NULL) - { - g_warning ("failed to get find a colord device: %s", - error->message); - g_error_free (error); - goto out; - } - - /* get details about the device */ - g_cancellable_reset (printer->colord_cancellable); - cd_device_connect (printer->colord_device, - printer->colord_cancellable, - colord_client_device_connect_cb, - g_object_ref (printer)); -out: - /* update the UI */ - colord_update_ui_from_settings (printer); - - g_object_unref (printer); -} - -static void -colord_update_device (GtkPrinterCups *printer) -{ - gchar *colord_device_id = NULL; - - /* not yet connected to the daemon */ - if (!cd_client_get_connected (printer->colord_client)) - goto out; - - /* not yet assigned a printer */ - if (printer->ppd_file == NULL) - goto out; - - /* old cached profile no longer valid */ - if (printer->colord_profile) - { - g_object_unref (printer->colord_profile); - printer->colord_profile = NULL; - } - - /* old cached device no longer valid */ - if (printer->colord_device) - { - g_object_unref (printer->colord_device); - printer->colord_device = NULL; - } - - /* generate a known ID */ - colord_device_id = g_strdup_printf ("cups-%s", gtk_printer_get_name (GTK_PRINTER (printer))); - - g_cancellable_reset (printer->colord_cancellable); - cd_client_find_device (printer->colord_client, - colord_device_id, - printer->colord_cancellable, - colord_client_find_device_cb, - g_object_ref (printer)); -out: - g_free (colord_device_id); - - /* update the UI */ - colord_update_ui_from_settings (printer); -} - -static void -colord_client_connect_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - gboolean ret; - GError *error = NULL; - GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); - static gboolean colord_warned = FALSE; - - ret = cd_client_connect_finish (CD_CLIENT (source_object), - res, &error); - if (!ret) - { - if (!colord_warned) - { - g_warning ("failed to contact colord: %s", error->message); - colord_warned = TRUE; - } - g_error_free (error); - } - - /* refresh the device */ - colord_update_device (printer); - - g_object_unref (printer); -} - -static void -colord_printer_details_aquired_cb (GtkPrinterCups *printer, - gboolean success, - gpointer user_data) -{ - /* refresh the device */ - if (printer->colord_client) - colord_update_device (printer); -} -#endif - -/** - * gtk_printer_cups_new: - * - * Creates a new #GtkPrinterCups. - * - * Returns: a new #GtkPrinterCups - * - * Since: 2.10 - **/ -GtkPrinterCups * -gtk_printer_cups_new (const char *name, - GtkPrintBackend *backend, - gpointer colord_client) -{ - GObject *result; - gboolean accepts_pdf; - GtkPrinterCups *printer; - -#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1 - accepts_pdf = TRUE; -#else - accepts_pdf = FALSE; -#endif - - result = g_object_new (GTK_TYPE_PRINTER_CUPS, - "name", name, - "backend", backend, - "is-virtual", FALSE, - "accepts-pdf", accepts_pdf, - NULL); - printer = GTK_PRINTER_CUPS (result); - -#ifdef HAVE_COLORD - /* connect to colord */ - if (colord_client != NULL) - { - printer->colord_cancellable = g_cancellable_new (); - printer->colord_client = g_object_ref (CD_CLIENT (colord_client)); - cd_client_connect (printer->colord_client, - printer->colord_cancellable, - colord_client_connect_cb, - g_object_ref (printer)); - } - - /* update the device when we read the PPD */ - g_signal_connect (printer, "details-acquired", - G_CALLBACK (colord_printer_details_aquired_cb), - printer); -#endif - - /* - * IPP version 1.1 has to be supported - * by all implementations according to rfc 2911 - */ - printer->ipp_version_major = 1; - printer->ipp_version_minor = 1; - - return printer; -} - -ppd_file_t * -gtk_printer_cups_get_ppd (GtkPrinterCups *printer) -{ - return printer->ppd_file; -} - -const gchar * -gtk_printer_cups_get_ppd_name (GtkPrinterCups *printer) -{ - const gchar *result; - - result = printer->ppd_name; - - if (result == NULL) - result = gtk_printer_get_name (GTK_PRINTER (printer)); - - return result; -} diff --git a/modules/printbackends/cups/gtkprintercups.h b/modules/printbackends/cups/gtkprintercups.h deleted file mode 100644 index f26bbab677..0000000000 --- a/modules/printbackends/cups/gtkprintercups.h +++ /dev/null @@ -1,130 +0,0 @@ -/* GtkPrinterCups - * Copyright (C) 2006 John (J5) Palmieri - * - * 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 . - */ - -#ifndef __GTK_PRINTER_CUPS_H__ -#define __GTK_PRINTER_CUPS_H__ - -#include -#include -#include -#include "gtkcupsutils.h" - -#include -#include - -#ifdef HAVE_COLORD -#include -#endif - -G_BEGIN_DECLS - -#define GTK_TYPE_PRINTER_CUPS (gtk_printer_cups_get_type ()) -#define GTK_PRINTER_CUPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCups)) -#define GTK_PRINTER_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass)) -#define GTK_IS_PRINTER_CUPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CUPS)) -#define GTK_IS_PRINTER_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CUPS)) -#define GTK_PRINTER_CUPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass)) - -typedef struct _GtkPrinterCups GtkPrinterCups; -typedef struct _GtkPrinterCupsClass GtkPrinterCupsClass; -typedef struct _GtkPrinterCupsPrivate GtkPrinterCupsPrivate; - -struct _GtkPrinterCups -{ - GtkPrinter parent_instance; - - gchar *device_uri; - gchar *original_device_uri; - gchar *printer_uri; - gchar *hostname; - gint port; - gchar **auth_info_required; - - ipp_pstate_t state; - gboolean reading_ppd; - gchar *ppd_name; - ppd_file_t *ppd_file; - - gchar *media_default; - GList *media_supported; - GList *media_size_supported; - gint media_bottom_margin_default; - gint media_top_margin_default; - gint media_left_margin_default; - gint media_right_margin_default; - gboolean media_margin_default_set; - gchar *sides_default; - GList *sides_supported; - gchar *output_bin_default; - GList *output_bin_supported; - - gchar *default_cover_before; - gchar *default_cover_after; - - gint default_number_up; - - gboolean remote; - guint get_remote_ppd_poll; - gint get_remote_ppd_attempts; - GtkCupsConnectionTest *remote_cups_connection_test; -#ifdef HAVE_COLORD - CdClient *colord_client; - CdDevice *colord_device; - CdProfile *colord_profile; - GCancellable *colord_cancellable; - gchar *colord_title; - gchar *colord_qualifier; -#endif -#ifdef HAVE_CUPS_API_1_6 - gboolean avahi_browsed; - gchar *avahi_name; - gchar *avahi_type; - gchar *avahi_domain; -#endif - guchar ipp_version_major; - guchar ipp_version_minor; - gboolean supports_copies; - gboolean supports_collate; - gboolean supports_number_up; - char **covers; - int number_of_covers; -}; - -struct _GtkPrinterCupsClass -{ - GtkPrinterClass parent_class; - -}; - -GType gtk_printer_cups_get_type (void) G_GNUC_CONST; -void gtk_printer_cups_register_type (GTypeModule *module); - -GtkPrinterCups *gtk_printer_cups_new (const char *name, - GtkPrintBackend *backend, - gpointer colord_client); -ppd_file_t *gtk_printer_cups_get_ppd (GtkPrinterCups *printer); -const gchar *gtk_printer_cups_get_ppd_name (GtkPrinterCups *printer); - -#ifdef HAVE_COLORD -void gtk_printer_cups_update_settings (GtkPrinterCups *printer, - GtkPrintSettings *settings, - GtkPrinterOptionSet *set); -#endif - -G_END_DECLS - -#endif /* __GTK_PRINTER_CUPS_H__ */ diff --git a/modules/printbackends/cups/meson.build b/modules/printbackends/cups/meson.build deleted file mode 100644 index ef7a30ee9a..0000000000 --- a/modules/printbackends/cups/meson.build +++ /dev/null @@ -1,22 +0,0 @@ -enable_colord = get_option('colord') -if enable_colord != 'no' - want_colord = enable_colord == 'yes' - colord_dep = dependency('colord', version: '>= 0.1.9', required: want_colord) - cdata.set('HAVE_COLORD', colord_dep.found()) -else - colord_dep = [] -endif - -shared_module('printbackend-cups', - 'gtkprintbackendcups.c', - 'gtkprintercups.c', - 'gtkcupsutils.c', - 'gtkcupssecretsutils.c', - c_args: [ - '-DGTK_COMPILATION', - '-DGTK_DISABLE_DEPRECATION_WARNINGS', - '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', - ], - dependencies: [libgtk_dep, libcups, colord_dep], - install_dir: printbackends_install_dir, - install : true) diff --git a/modules/printbackends/file/gtkprintbackendfile.c b/modules/printbackends/file/gtkprintbackendfile.c deleted file mode 100644 index b568e01395..0000000000 --- a/modules/printbackends/file/gtkprintbackendfile.c +++ /dev/null @@ -1,846 +0,0 @@ -/* GTK - The GIMP Toolkit - * gtkprintbackendfile.c: Default implementation of GtkPrintBackend - * for printing to a file - * Copyright (C) 2003, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "gtk/gtk.h" -#include "gtk/gtkprinter-private.h" - -#include "gtkprintbackendfile.h" - -typedef struct _GtkPrintBackendFileClass GtkPrintBackendFileClass; - -#define GTK_PRINT_BACKEND_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass)) -#define GTK_IS_PRINT_BACKEND_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_FILE)) -#define GTK_PRINT_BACKEND_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass)) - -#define _STREAM_MAX_CHUNK_SIZE 8192 - -struct _GtkPrintBackendFileClass -{ - GtkPrintBackendClass parent_class; -}; - -struct _GtkPrintBackendFile -{ - GtkPrintBackend parent_instance; -}; - -typedef enum -{ - FORMAT_PDF, - FORMAT_PS, - FORMAT_SVG, - N_FORMATS -} OutputFormat; - -static const gchar* formats[N_FORMATS] = -{ - "pdf", - "ps", - "svg" -}; - -static GObjectClass *backend_parent_class; - -static void gtk_print_backend_file_class_init (GtkPrintBackendFileClass *class); -static void gtk_print_backend_file_init (GtkPrintBackendFile *impl); -static void file_printer_get_settings_from_options (GtkPrinter *printer, - GtkPrinterOptionSet *options, - GtkPrintSettings *settings); -static GtkPrinterOptionSet *file_printer_get_options (GtkPrinter *printer, - GtkPrintSettings *settings, - GtkPageSetup *page_setup, - GtkPrintCapabilities capabilities); -static void file_printer_prepare_for_print (GtkPrinter *printer, - GtkPrintJob *print_job, - GtkPrintSettings *settings, - GtkPageSetup *page_setup); -static void gtk_print_backend_file_print_stream (GtkPrintBackend *print_backend, - GtkPrintJob *job, - GIOChannel *data_io, - GtkPrintJobCompleteFunc callback, - gpointer user_data, - GDestroyNotify dnotify); -static cairo_surface_t * file_printer_create_cairo_surface (GtkPrinter *printer, - GtkPrintSettings *settings, - gdouble width, - gdouble height, - GIOChannel *cache_io); - -static GList * file_printer_list_papers (GtkPrinter *printer); -static GtkPageSetup * file_printer_get_default_page_size (GtkPrinter *printer); - -G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendFile, gtk_print_backend_file, GTK_TYPE_PRINT_BACKEND) - -void -g_io_module_load (GIOModule *module) -{ - g_type_module_use (G_TYPE_MODULE (module)); - - gtk_print_backend_file_register_type (G_TYPE_MODULE (module)); - - g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, - GTK_TYPE_PRINT_BACKEND_FILE, - "file", - 10); -} - -void -g_io_module_unload (GIOModule *module) -{ -} - -char ** -g_io_module_query (void) -{ - char *eps[] = { - GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, - NULL - }; - - return g_strdupv (eps); -} - -/** - * gtk_print_backend_file_new: - * - * Creates a new #GtkPrintBackendFile object. #GtkPrintBackendFile - * implements the #GtkPrintBackend interface with direct access to - * the filesystem using Unix/Linux API calls - * - * Returns: the new #GtkPrintBackendFile object - **/ -GtkPrintBackend * -gtk_print_backend_file_new (void) -{ - return g_object_new (GTK_TYPE_PRINT_BACKEND_FILE, NULL); -} - -static void -gtk_print_backend_file_class_init (GtkPrintBackendFileClass *class) -{ - GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class); - - backend_parent_class = g_type_class_peek_parent (class); - - backend_class->print_stream = gtk_print_backend_file_print_stream; - backend_class->printer_create_cairo_surface = file_printer_create_cairo_surface; - backend_class->printer_get_options = file_printer_get_options; - backend_class->printer_get_settings_from_options = file_printer_get_settings_from_options; - backend_class->printer_prepare_for_print = file_printer_prepare_for_print; - backend_class->printer_list_papers = file_printer_list_papers; - backend_class->printer_get_default_page_size = file_printer_get_default_page_size; -} - -static void -gtk_print_backend_file_class_finalize (GtkPrintBackendFileClass *class) -{ -} - -/* return N_FORMATS if no explicit format in the settings */ -static OutputFormat -format_from_settings (GtkPrintSettings *settings) -{ - const gchar *value; - gint i; - - if (settings == NULL) - return N_FORMATS; - - value = gtk_print_settings_get (settings, - GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT); - if (value == NULL) - return N_FORMATS; - - for (i = 0; i < N_FORMATS; ++i) - if (strcmp (value, formats[i]) == 0) - break; - - g_assert (i < N_FORMATS); - - return (OutputFormat) i; -} - -static gchar * -output_file_from_settings (GtkPrintSettings *settings, - const gchar *default_format) -{ - gchar *uri = NULL; - - if (settings) - uri = g_strdup (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); - - if (uri == NULL) - { - const gchar *extension, *basename = NULL, *output_dir = NULL; - gchar *name, *locale_name, *path; - - if (default_format) - extension = default_format; - else - { - OutputFormat format; - - format = format_from_settings (settings); - switch (format) - { - default: - case FORMAT_PDF: - extension = "pdf"; - break; - case FORMAT_PS: - extension = "ps"; - break; - case FORMAT_SVG: - extension = "svg"; - break; - } - } - - if (settings) - basename = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME); - if (basename == NULL) - basename = _("output"); - - name = g_strconcat (basename, ".", extension, NULL); - - locale_name = g_filename_from_utf8 (name, -1, NULL, NULL, NULL); - g_free (name); - - if (locale_name != NULL) - { - if (settings) - output_dir = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_DIR); - if (output_dir == NULL) - { - const gchar *document_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS); - - if (document_dir == NULL) - { - gchar *current_dir = g_get_current_dir (); - path = g_build_filename (current_dir, locale_name, NULL); - g_free (current_dir); - } - else - path = g_build_filename (document_dir, locale_name, NULL); - - uri = g_filename_to_uri (path, NULL, NULL); - } - else - { - path = g_build_filename (output_dir, locale_name, NULL); - uri = g_filename_to_uri (path, NULL, NULL); - } - - g_free (path); - g_free (locale_name); - } - } - - return uri; -} - -static cairo_status_t -_cairo_write (void *closure, - const unsigned char *data, - unsigned int length) -{ - GIOChannel *io = (GIOChannel *)closure; - gsize written = 0; - GError *error; - - error = NULL; - - GTK_NOTE (PRINTING, - g_print ("FILE Backend: Writting %u byte chunk to temp file\n", length)); - - while (length > 0) - { - GIOStatus status; - - status = g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error); - - if (status == G_IO_STATUS_ERROR) - { - if (error != NULL) - { - GTK_NOTE (PRINTING, - g_print ("FILE Backend: Error writting to temp file, %s\n", error->message)); - - g_error_free (error); - } - - return CAIRO_STATUS_WRITE_ERROR; - } - - GTK_NOTE (PRINTING, - g_print ("FILE Backend: Wrote %zd bytes to temp file\n", written)); - - data += written; - length -= written; - } - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_surface_t * -file_printer_create_cairo_surface (GtkPrinter *printer, - GtkPrintSettings *settings, - gdouble width, - gdouble height, - GIOChannel *cache_io) -{ - cairo_surface_t *surface; - OutputFormat format; - const cairo_svg_version_t *versions; - int num_versions = 0; - - format = format_from_settings (settings); - - switch (format) - { - default: - case FORMAT_PDF: - surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height); - break; - case FORMAT_PS: - surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height); - break; - case FORMAT_SVG: - surface = cairo_svg_surface_create_for_stream (_cairo_write, cache_io, width, height); - cairo_svg_get_versions (&versions, &num_versions); - if (num_versions > 0) - cairo_svg_surface_restrict_to_version (surface, versions[num_versions - 1]); - break; - } - - cairo_surface_set_fallback_resolution (surface, - 2.0 * gtk_print_settings_get_printer_lpi (settings), - 2.0 * gtk_print_settings_get_printer_lpi (settings)); - - return surface; -} - -typedef struct { - GtkPrintBackend *backend; - GtkPrintJobCompleteFunc callback; - GtkPrintJob *job; - GFileOutputStream *target_io_stream; - gpointer user_data; - GDestroyNotify dnotify; -} _PrintStreamData; - -static void -file_print_cb (GtkPrintBackendFile *print_backend, - GError *error, - gpointer user_data) -{ - gchar *uri; - - _PrintStreamData *ps = (_PrintStreamData *) user_data; - GtkRecentManager *recent_manager; - - if (ps->target_io_stream != NULL) - (void)g_output_stream_close (G_OUTPUT_STREAM (ps->target_io_stream), NULL, NULL); - - if (ps->callback) - ps->callback (ps->job, ps->user_data, error); - - if (ps->dnotify) - ps->dnotify (ps->user_data); - - gtk_print_job_set_status (ps->job, - (error != NULL) - ? GTK_PRINT_STATUS_FINISHED_ABORTED - : GTK_PRINT_STATUS_FINISHED); - - recent_manager = gtk_recent_manager_get_default (); - uri = output_file_from_settings (gtk_print_job_get_settings (ps->job), NULL); - gtk_recent_manager_add_item (recent_manager, uri); - g_free (uri); - - if (ps->job) - g_object_unref (ps->job); - - g_free (ps); -} - -static gboolean -file_write (GIOChannel *source, - GIOCondition con, - gpointer user_data) -{ - gchar buf[_STREAM_MAX_CHUNK_SIZE]; - gsize bytes_read; - GError *error; - GIOStatus read_status; - _PrintStreamData *ps = (_PrintStreamData *) user_data; - - error = NULL; - - read_status = - g_io_channel_read_chars (source, - buf, - _STREAM_MAX_CHUNK_SIZE, - &bytes_read, - &error); - - if (read_status != G_IO_STATUS_ERROR) - { - gsize bytes_written; - - g_output_stream_write_all (G_OUTPUT_STREAM (ps->target_io_stream), - buf, - bytes_read, - &bytes_written, - NULL, - &error); - } - - if (error != NULL || read_status == G_IO_STATUS_EOF) - { - file_print_cb (GTK_PRINT_BACKEND_FILE (ps->backend), error, user_data); - - if (error != NULL) - { - GTK_NOTE (PRINTING, - g_print ("FILE Backend: %s\n", error->message)); - - g_error_free (error); - } - - return FALSE; - } - - GTK_NOTE (PRINTING, - g_print ("FILE Backend: Writting %lu byte chunk to target file\n", bytes_read)); - - return TRUE; -} - -static void -gtk_print_backend_file_print_stream (GtkPrintBackend *print_backend, - GtkPrintJob *job, - GIOChannel *data_io, - GtkPrintJobCompleteFunc callback, - gpointer user_data, - GDestroyNotify dnotify) -{ - GError *internal_error = NULL; - _PrintStreamData *ps; - GtkPrintSettings *settings; - gchar *uri; - GFile *file = NULL; - - settings = gtk_print_job_get_settings (job); - - ps = g_new0 (_PrintStreamData, 1); - ps->callback = callback; - ps->user_data = user_data; - ps->dnotify = dnotify; - ps->job = g_object_ref (job); - ps->backend = print_backend; - - internal_error = NULL; - uri = output_file_from_settings (settings, NULL); - - if (uri == NULL) - goto error; - - file = g_file_new_for_uri (uri); - ps->target_io_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &internal_error); - - g_object_unref (file); - g_free (uri); - -error: - if (internal_error != NULL) - { - file_print_cb (GTK_PRINT_BACKEND_FILE (print_backend), - internal_error, ps); - - g_error_free (internal_error); - return; - } - - g_io_add_watch (data_io, - G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, - (GIOFunc) file_write, - ps); -} - -static void -gtk_print_backend_file_init (GtkPrintBackendFile *backend) -{ - GtkPrinter *printer; - - printer = g_object_new (GTK_TYPE_PRINTER, - "name", _("Print to File"), - "backend", backend, - "is-virtual", TRUE, - "accepts-pdf", TRUE, - NULL); - - gtk_printer_set_has_details (printer, TRUE); - gtk_printer_set_icon_name (printer, "document-save"); - gtk_printer_set_is_active (printer, TRUE); - - gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer); - g_object_unref (printer); - - gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); -} - -typedef struct { - GtkPrinter *printer; - GtkPrinterOptionSet *set; -} _OutputFormatChangedData; - -static void -set_printer_format_from_option_set (GtkPrinter *printer, - GtkPrinterOptionSet *set) -{ - GtkPrinterOption *format_option; - const gchar *value; - gint i; - - format_option = gtk_printer_option_set_lookup (set, "output-file-format"); - if (format_option && format_option->value) - { - value = format_option->value; - if (value) - { - for (i = 0; i < N_FORMATS; ++i) - if (strcmp (value, formats[i]) == 0) - break; - - g_assert (i < N_FORMATS); - - switch (i) - { - case FORMAT_PDF: - gtk_printer_set_accepts_pdf (printer, TRUE); - gtk_printer_set_accepts_ps (printer, FALSE); - break; - case FORMAT_PS: - gtk_printer_set_accepts_pdf (printer, FALSE); - gtk_printer_set_accepts_ps (printer, TRUE); - break; - case FORMAT_SVG: - default: - gtk_printer_set_accepts_pdf (printer, FALSE); - gtk_printer_set_accepts_ps (printer, FALSE); - break; - } - } - } -} - -static void -file_printer_output_file_format_changed (GtkPrinterOption *format_option, - gpointer user_data) -{ - GtkPrinterOption *uri_option; - gchar *base = NULL; - _OutputFormatChangedData *data = (_OutputFormatChangedData *) user_data; - - if (! format_option->value) - return; - - uri_option = gtk_printer_option_set_lookup (data->set, - "gtk-main-page-custom-input"); - - if (uri_option && uri_option->value) - { - const gchar *uri = uri_option->value; - const gchar *dot = strrchr (uri, '.'); - - if (dot) - { - gint i; - - /* check if the file extension matches one of the known ones */ - for (i = 0; i < N_FORMATS; i++) - if (strcmp (dot + 1, formats[i]) == 0) - break; - - if (i < N_FORMATS && strcmp (formats[i], format_option->value)) - { - /* the file extension is known but doesn't match the - * selected one, strip it away - */ - base = g_strndup (uri, dot - uri); - } - } - else - { - /* there's no file extension */ - base = g_strdup (uri); - } - } - - if (base) - { - gchar *tmp = g_strdup_printf ("%s.%s", base, format_option->value); - - gtk_printer_option_set (uri_option, tmp); - g_free (tmp); - g_free (base); - } - - set_printer_format_from_option_set (data->printer, data->set); -} - -static GtkPrinterOptionSet * -file_printer_get_options (GtkPrinter *printer, - GtkPrintSettings *settings, - GtkPageSetup *page_setup, - GtkPrintCapabilities capabilities) -{ - GtkPrinterOptionSet *set; - GtkPrinterOption *option; - const gchar *n_up[] = {"1", "2", "4", "6", "9", "16" }; - const gchar *pages_per_sheet = NULL; - const gchar *format_names[N_FORMATS] = { N_("PDF"), N_("PostScript"), N_("SVG") }; - const gchar *supported_formats[N_FORMATS]; - gchar *display_format_names[N_FORMATS]; - gint n_formats = 0; - OutputFormat format; - gchar *uri; - gint current_format = 0; - _OutputFormatChangedData *format_changed_data; - - format = format_from_settings (settings); - - set = gtk_printer_option_set_new (); - - option = gtk_printer_option_new ("gtk-n-up", _("Pages per _sheet:"), GTK_PRINTER_OPTION_TYPE_PICKONE); - gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), - (char **) n_up, (char **) n_up /* FIXME i18n (localised digits)! */); - if (settings) - pages_per_sheet = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_NUMBER_UP); - if (pages_per_sheet) - gtk_printer_option_set (option, pages_per_sheet); - else - gtk_printer_option_set (option, "1"); - gtk_printer_option_set_add (set, option); - g_object_unref (option); - - if (capabilities & (GTK_PRINT_CAPABILITY_GENERATE_PDF | GTK_PRINT_CAPABILITY_GENERATE_PS)) - { - if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PDF) - { - if (format == FORMAT_PDF || format == N_FORMATS) - { - format = FORMAT_PDF; - current_format = n_formats; - } - supported_formats[n_formats] = formats[FORMAT_PDF]; - display_format_names[n_formats] = _(format_names[FORMAT_PDF]); - n_formats++; - } - if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PS) - { - if (format == FORMAT_PS || format == N_FORMATS) - current_format = n_formats; - supported_formats[n_formats] = formats[FORMAT_PS]; - display_format_names[n_formats] = _(format_names[FORMAT_PS]); - n_formats++; - } - } - else - { - switch (format) - { - default: - case FORMAT_PDF: - current_format = FORMAT_PDF; - break; - case FORMAT_PS: - current_format = FORMAT_PS; - break; - case FORMAT_SVG: - current_format = FORMAT_SVG; - break; - } - - for (n_formats = 0; n_formats < N_FORMATS; ++n_formats) - { - supported_formats[n_formats] = formats[n_formats]; - display_format_names[n_formats] = _(format_names[n_formats]); - } - } - - uri = output_file_from_settings (settings, supported_formats[current_format]); - - option = gtk_printer_option_new ("gtk-main-page-custom-input", _("File"), - GTK_PRINTER_OPTION_TYPE_FILESAVE); - gtk_printer_option_set_activates_default (option, TRUE); - gtk_printer_option_set (option, uri); - g_free (uri); - option->group = g_strdup ("GtkPrintDialogExtension"); - gtk_printer_option_set_add (set, option); - - if (n_formats > 1) - { - option = gtk_printer_option_new ("output-file-format", _("_Output format"), - GTK_PRINTER_OPTION_TYPE_ALTERNATIVE); - option->group = g_strdup ("GtkPrintDialogExtension"); - - gtk_printer_option_choices_from_array (option, n_formats, - (char **) supported_formats, - display_format_names); - gtk_printer_option_set (option, supported_formats[current_format]); - gtk_printer_option_set_add (set, option); - - set_printer_format_from_option_set (printer, set); - format_changed_data = g_new (_OutputFormatChangedData, 1); - format_changed_data->printer = printer; - format_changed_data->set = set; - g_signal_connect_data (option, "changed", - G_CALLBACK (file_printer_output_file_format_changed), - format_changed_data, (GClosureNotify)g_free, 0); - - g_object_unref (option); - } - - return set; -} - -static void -file_printer_get_settings_from_options (GtkPrinter *printer, - GtkPrinterOptionSet *options, - GtkPrintSettings *settings) -{ - GtkPrinterOption *option; - - option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input"); - gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, option->value); - - option = gtk_printer_option_set_lookup (options, "output-file-format"); - if (option) - gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT, option->value); - - option = gtk_printer_option_set_lookup (options, "gtk-n-up"); - if (option) - gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value); - - option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout"); - if (option) - gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value); -} - -static void -file_printer_prepare_for_print (GtkPrinter *printer, - GtkPrintJob *print_job, - GtkPrintSettings *settings, - GtkPageSetup *page_setup) -{ - gdouble scale; - GtkPrintPages pages; - GtkPageRange *ranges; - gint n_ranges; - OutputFormat format; - - pages = gtk_print_settings_get_print_pages (settings); - gtk_print_job_set_pages (print_job, pages); - - if (pages == GTK_PRINT_PAGES_RANGES) - ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges); - else - { - ranges = NULL; - n_ranges = 0; - } - - gtk_print_job_set_page_ranges (print_job, ranges, n_ranges); - gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings)); - gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings)); - gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings)); - gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings)); - gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings)); - - scale = gtk_print_settings_get_scale (settings); - if (scale != 100.0) - gtk_print_job_set_scale (print_job, scale / 100.0); - - gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings)); - - format = format_from_settings (settings); - switch (format) - { - case FORMAT_PDF: - gtk_print_job_set_rotate (print_job, FALSE); - break; - default: - case FORMAT_PS: - case FORMAT_SVG: - gtk_print_job_set_rotate (print_job, TRUE); - break; - } -} - -static GList * -file_printer_list_papers (GtkPrinter *printer) -{ - GList *result = NULL; - GList *papers, *p; - GtkPageSetup *page_setup; - - papers = gtk_paper_size_get_paper_sizes (FALSE); - - for (p = papers; p; p = p->next) - { - GtkPaperSize *paper_size = p->data; - - page_setup = gtk_page_setup_new (); - gtk_page_setup_set_paper_size (page_setup, paper_size); - gtk_paper_size_free (paper_size); - result = g_list_prepend (result, page_setup); - } - - g_list_free (papers); - - return g_list_reverse (result); -} - -static GtkPageSetup * -file_printer_get_default_page_size (GtkPrinter *printer) -{ - GtkPageSetup *result = NULL; - - return result; -} diff --git a/modules/printbackends/file/gtkprintbackendfile.h b/modules/printbackends/file/gtkprintbackendfile.h deleted file mode 100644 index b4ae41a15c..0000000000 --- a/modules/printbackends/file/gtkprintbackendfile.h +++ /dev/null @@ -1,39 +0,0 @@ -/* GTK - The GIMP Toolkit - * gtkprintbackendpdf.h: Default implementation of GtkPrintBackend - * for printing to a file - * Copyright (C) 2003, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_PRINT_BACKEND_FILE_H__ -#define __GTK_PRINT_BACKEND_FILE_H__ - -#include -#include "gtkprintbackend.h" - -G_BEGIN_DECLS - -#define GTK_TYPE_PRINT_BACKEND_FILE (gtk_print_backend_file_get_type ()) -#define GTK_PRINT_BACKEND_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFile)) -#define GTK_IS_PRINT_BACKEND_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_FILE)) - -typedef struct _GtkPrintBackendFile GtkPrintBackendFile; - -GtkPrintBackend *gtk_print_backend_file_new (void); -GType gtk_print_backend_file_get_type (void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GTK_PRINT_BACKEND_FILE_H__ */ diff --git a/modules/printbackends/file/meson.build b/modules/printbackends/file/meson.build deleted file mode 100644 index b1604be0f6..0000000000 --- a/modules/printbackends/file/meson.build +++ /dev/null @@ -1,10 +0,0 @@ -shared_module('printbackend-file', - 'gtkprintbackendfile.c', - c_args: [ - '-DGTK_COMPILATION', - '-DGTK_DISABLE_DEPRECATION_WARNINGS', - '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', - ], - dependencies: libgtk_dep, - install_dir: printbackends_install_dir, - install : true) diff --git a/modules/printbackends/gtkcloudprintaccount.c b/modules/printbackends/gtkcloudprintaccount.c new file mode 100644 index 0000000000..ee65a18287 --- /dev/null +++ b/modules/printbackends/gtkcloudprintaccount.c @@ -0,0 +1,662 @@ +/* gtkcloudprintaccount.c: Google Cloud Print account class + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include "gtkcloudprintaccount.h" +#include "gtkprintercloudprint.h" + +#define CLOUDPRINT_PROXY "GTK+" + +#define ACCOUNT_IFACE "org.gnome.OnlineAccounts.Account" +#define O_AUTH2_BASED_IFACE "org.gnome.OnlineAccounts.OAuth2Based" + +#define GTK_CLOUDPRINT_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccountClass)) +#define GTK_IS_CLOUDPRINT_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLOUDPRINT_ACCOUNT)) +#define GTK_CLOUDPRINT_ACCOUNT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccountClass)) + +static GObjectClass *gtk_cloudprint_account_parent_class; +static GType gtk_cloudprint_account_type = 0; + +typedef struct _GtkCloudprintAccountClass GtkCloudprintAccountClass; + +struct _GtkCloudprintAccountClass +{ + GObjectClass parent_class; +}; + +struct _GtkCloudprintAccount +{ + GObject parent_instance; + + gchar *printer_id; + gchar *goa_path; + gchar *presentation_identity; + RestProxy *rest_proxy; + gchar *oauth2_access_token; +}; + +static void gtk_cloudprint_account_class_init (GtkCloudprintAccountClass *class); +static void gtk_cloudprint_account_init (GtkCloudprintAccount *impl); +static void gtk_cloudprint_account_finalize (GObject *object); + +void +gtk_cloudprint_account_register_type (GTypeModule *module) +{ + const GTypeInfo cloudprint_account_info = + { + sizeof (GtkCloudprintAccountClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_cloudprint_account_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkCloudprintAccount), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_cloudprint_account_init, + }; + + gtk_cloudprint_account_type = g_type_module_register_type (module, + G_TYPE_OBJECT, + "GtkCloudprintAccount", + &cloudprint_account_info, 0); +} + +/* + * GtkCloudprintAccount + */ +GType +gtk_cloudprint_account_get_type (void) +{ + return gtk_cloudprint_account_type; +} + +/** + * gtk_cloudprint_account_new: + * + * Creates a new #GtkCloudprintAccount object, representing a Google + * Cloud Print account and its state data. + * + * Returns: the new #GtkCloudprintAccount object + **/ +GtkCloudprintAccount * +gtk_cloudprint_account_new (const gchar *id, + const gchar *path, + const gchar *presentation_identity) +{ + GtkCloudprintAccount *account = g_object_new (GTK_TYPE_CLOUDPRINT_ACCOUNT, + NULL); + account->printer_id = g_strdup (id); + account->goa_path = g_strdup (path); + account->presentation_identity = g_strdup (presentation_identity); + return account; +} + +static void +gtk_cloudprint_account_class_init (GtkCloudprintAccountClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gtk_cloudprint_account_parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = gtk_cloudprint_account_finalize; +} + +static void +gtk_cloudprint_account_init (GtkCloudprintAccount *account) +{ + account->printer_id = NULL; + account->goa_path = NULL; + account->presentation_identity = NULL; + account->rest_proxy = NULL; + account->oauth2_access_token = NULL; + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: +GtkCloudprintAccount(%p)\n", + account)); +} + +static void +gtk_cloudprint_account_finalize (GObject *object) +{ + GtkCloudprintAccount *account; + + account = GTK_CLOUDPRINT_ACCOUNT (object); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: -GtkCloudprintAccount(%p)\n", + account)); + + g_clear_object (&(account->rest_proxy)); + g_clear_pointer (&(account->printer_id), g_free); + g_clear_pointer (&(account->goa_path), g_free); + g_clear_pointer (&(account->presentation_identity), g_free); + g_clear_pointer (&(account->oauth2_access_token), g_free); + + G_OBJECT_CLASS (gtk_cloudprint_account_parent_class)->finalize (object); +} + +static JsonParser * +cloudprint_json_parse (RestProxyCall *call, JsonObject **result, GError **error) +{ + JsonParser *json_parser = json_parser_new (); + JsonNode *root; + JsonObject *json_object; + gboolean success = FALSE; + + if (!json_parser_load_from_data (json_parser, + rest_proxy_call_get_payload (call), + rest_proxy_call_get_payload_length (call), + error)) + { + g_object_unref (json_parser); + return NULL; + } + + root = json_parser_get_root (json_parser); + if (JSON_NODE_TYPE (root) != JSON_NODE_OBJECT) + { + if (error != NULL) + *error = g_error_new_literal (gtk_print_error_quark (), + GTK_PRINT_ERROR_INTERNAL_ERROR, + "Bad reply"); + + g_object_unref (json_parser); + return NULL; + } + + json_object = json_node_get_object (root); + if (json_object_has_member (json_object, "success")) + success = json_object_get_boolean_member (json_object, "success"); + + if (!success) + { + const gchar *message = "(no message)"; + + if (json_object_has_member (json_object, "message")) + message = json_object_get_string_member (json_object, "message"); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: unsuccessful submit: %s\n", + message)); + + if (error != NULL) + *error = g_error_new_literal (gtk_print_error_quark (), + GTK_PRINT_ERROR_INTERNAL_ERROR, + message); + + g_object_unref (json_parser); + return NULL; + } + + if (result != NULL) + *result = json_node_dup_object (root); + + return json_parser; +} + +static void +gtk_cloudprint_account_search_rest_call_cb (RestProxyCall *call, + const GError *cb_error, + GObject *weak_object, + gpointer user_data) +{ + GTask *task = user_data; + GtkCloudprintAccount *account = g_task_get_task_data (task); + JsonParser *json_parser = NULL; + JsonObject *result; + JsonNode *printers = NULL; + GError *error = NULL; + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) 'search' REST call " + "returned\n", account)); + + if (cb_error != NULL) + { + error = g_error_copy (cb_error); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (g_task_return_error_if_cancelled (task)) + { + g_object_unref (task); + return; + } + + if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_object_unref (json_parser); + + if (json_object_has_member (result, "printers")) + printers = json_object_dup_member (result, "printers"); + + json_object_unref (result); + if (printers == NULL) + { + g_task_return_new_error (task, + gtk_print_error_quark (), + GTK_PRINT_ERROR_INTERNAL_ERROR, + "Bad reply to 'search' request"); + return; + } + + g_task_return_pointer (task, + printers, + (GDestroyNotify) json_node_free); + g_object_unref (task); +} + +static void +gtk_cloudprint_account_got_oauth2_access_token_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + GtkCloudprintAccount *account = g_task_get_task_data (task); + RestProxyCall *call; + RestProxy *rest; + GVariant *output; + gint expires_in = 0; + GError *error = NULL; + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), + result, + &error); + g_object_unref (source); + + if (output == NULL) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_variant_get (output, "(si)", + &account->oauth2_access_token, + &expires_in); + g_variant_unref (output); + + rest = oauth2_proxy_new_with_token (account->printer_id, + account->oauth2_access_token, + "https://accounts.google.com/o/oauth2/token", + "https://www.google.com/cloudprint/", + FALSE); + + if (rest == NULL) + { + g_task_return_new_error (task, + gtk_print_error_quark (), + GTK_PRINT_ERROR_INTERNAL_ERROR, + "REST proxy creation failed"); + g_object_unref (task); + return; + } + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) 'search' REST call\n", + account)); + + account->rest_proxy = g_object_ref (rest); + + call = rest_proxy_new_call (REST_PROXY (rest)); + g_object_unref (rest); + rest_proxy_call_set_function (call, "search"); + rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY); + rest_proxy_call_add_param (call, "connection_status", "ALL"); + if (!rest_proxy_call_async (call, + gtk_cloudprint_account_search_rest_call_cb, + NULL, + task, + &error)) + { + g_task_return_error (task, error); + g_object_unref (task); + } + + g_object_unref (call); +} + +static void +gtk_cloudprint_account_ensure_credentials_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + GtkCloudprintAccount *account = g_task_get_task_data (task); + GVariant *output; + gint expires_in = 0; + GError *error = NULL; + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), + result, + &error); + + if (output == NULL) + { + g_object_unref (source); + if (error->domain != G_DBUS_ERROR || + (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN && + error->code != G_DBUS_ERROR_UNKNOWN_METHOD)) + g_task_return_error (task, error); + else + /* Return an empty list. */ + g_task_return_pointer (task, + json_node_new (JSON_NODE_ARRAY), + (GDestroyNotify) json_node_free); + + g_object_unref (task); + return; + } + + g_variant_get (output, "(i)", + &expires_in); + g_variant_unref (output); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) getting access token\n", + account)); + + g_dbus_connection_call (G_DBUS_CONNECTION (source), + ONLINE_ACCOUNTS_BUS, + account->goa_path, + O_AUTH2_BASED_IFACE, + "GetAccessToken", + NULL, + G_VARIANT_TYPE ("(si)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + g_task_get_cancellable (task), + gtk_cloudprint_account_got_oauth2_access_token_cb, + task); +} + +void +gtk_cloudprint_account_search (GtkCloudprintAccount *account, + GDBusConnection *dbus_connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task = g_task_new (G_OBJECT (account), + cancellable, + callback, + user_data); + g_task_set_task_data (task, + g_object_ref (account), + (GDestroyNotify) g_object_unref); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) ensuring credentials\n", + account)); + + g_dbus_connection_call (g_object_ref (dbus_connection), + ONLINE_ACCOUNTS_BUS, + account->goa_path, + ACCOUNT_IFACE, + "EnsureCredentials", + NULL, + G_VARIANT_TYPE ("(i)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + gtk_cloudprint_account_ensure_credentials_cb, + task); +} + +JsonNode * +gtk_cloudprint_account_search_finish (GtkCloudprintAccount *account, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, account), NULL); + return g_task_propagate_pointer (G_TASK (result), error); +} + +static void +gtk_cloudprint_account_printer_rest_call_cb (RestProxyCall *call, + const GError *cb_error, + GObject *weak_object, + gpointer user_data) +{ + GTask *task = user_data; + GtkCloudprintAccount *account = g_task_get_task_data (task); + JsonParser *json_parser = NULL; + JsonObject *result; + GError *error = NULL; + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) 'printer' REST call " + "returned\n", account)); + + if (cb_error != NULL) + { + error = g_error_copy (cb_error); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (g_task_return_error_if_cancelled (task)) + { + g_object_unref (task); + return; + } + + if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_object_unref (json_parser); + g_task_return_pointer (task, + result, + (GDestroyNotify) json_object_unref); + g_object_unref (task); +} + +void +gtk_cloudprint_account_printer (GtkCloudprintAccount *account, + const gchar *printerid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RestProxyCall *call; + GTask *task; + GError *error = NULL; + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) 'printer' REST call for " + "printer id %s", account, printerid)); + + task = g_task_new (G_OBJECT (account), cancellable, callback, user_data); + + g_task_set_task_data (task, + g_object_ref (account), + (GDestroyNotify) g_object_unref); + + call = rest_proxy_new_call (REST_PROXY (account->rest_proxy)); + rest_proxy_call_set_function (call, "printer"); + rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY); + rest_proxy_call_add_param (call, "printerid", printerid); + if (!rest_proxy_call_async (call, + gtk_cloudprint_account_printer_rest_call_cb, + NULL, + task, + &error)) + { + g_task_return_error (task, error); + g_object_unref (task); + } + + g_object_unref (call); +} + +JsonObject * +gtk_cloudprint_account_printer_finish (GtkCloudprintAccount *account, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, account), NULL); + return g_task_propagate_pointer (G_TASK (result), error); +} + +static void +gtk_cloudprint_account_submit_rest_call_cb (RestProxyCall *call, + const GError *cb_error, + GObject *weak_object, + gpointer user_data) +{ + GTask *task = user_data; + GtkCloudprintAccount *account = g_task_get_task_data (task); + JsonParser *json_parser = NULL; + JsonObject *result; + GError *error = NULL; + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) 'submit' REST call " + "returned\n", account)); + + if (cb_error != NULL) + { + error = g_error_copy (cb_error); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + if (g_task_return_error_if_cancelled (task)) + { + g_object_unref (task); + return; + } + + if ((json_parser = cloudprint_json_parse (call, &result, &error)) == NULL) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_object_unref (json_parser); + g_task_return_pointer (task, + result, + (GDestroyNotify) json_object_unref); + g_object_unref (task); +} + +void +gtk_cloudprint_account_submit (GtkCloudprintAccount *account, + GtkPrinterCloudprint *printer, + GMappedFile *file, + const gchar *title, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + RestProxyCall *call; + gchar *printerid = NULL; + RestParam *param; + GError *error = NULL; + gchar *auth; + + g_object_get (printer, + "printer-id", &printerid, + NULL); + + g_warn_if_fail (printerid != NULL); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: (%p) 'submit' REST call for " + "printer id %s\n", account, printerid)); + + task = g_task_new (G_OBJECT (account), + cancellable, + callback, + user_data); + + g_task_set_task_data (task, + g_object_ref (account), + (GDestroyNotify) g_object_unref); + + call = rest_proxy_new_call (REST_PROXY (account->rest_proxy)); + rest_proxy_call_set_method (call, "POST"); + rest_proxy_call_set_function (call, "submit"); + + auth = g_strdup_printf ("Bearer %s", account->oauth2_access_token); + rest_proxy_call_add_header (call, "Authorization", auth); + g_free (auth); + rest_proxy_call_add_header (call, "X-CloudPrint-Proxy", CLOUDPRINT_PROXY); + + rest_proxy_call_add_param (call, "printerid", printerid); + g_free (printerid); + + rest_proxy_call_add_param (call, "contentType", "dataUrl"); + rest_proxy_call_add_param (call, "title", title); + param = rest_param_new_with_owner ("content", + g_mapped_file_get_contents (file), + g_mapped_file_get_length (file), + "dataUrl", + NULL, + file, + (GDestroyNotify) g_mapped_file_unref); + rest_proxy_call_add_param_full (call, param); + + if (!rest_proxy_call_async (call, + gtk_cloudprint_account_submit_rest_call_cb, + NULL, + task, + &error)) + { + g_task_return_error (task, error); + g_object_unref (call); + g_object_unref (task); + return; + } + + g_object_unref (call); +} + +JsonObject * +gtk_cloudprint_account_submit_finish (GtkCloudprintAccount *account, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, account), NULL); + return g_task_propagate_pointer (G_TASK (result), error); +} + +const gchar * +gtk_cloudprint_account_get_presentation_identity (GtkCloudprintAccount *account) +{ + return account->presentation_identity; +} diff --git a/modules/printbackends/gtkcloudprintaccount.h b/modules/printbackends/gtkcloudprintaccount.h new file mode 100644 index 0000000000..ef788743d3 --- /dev/null +++ b/modules/printbackends/gtkcloudprintaccount.h @@ -0,0 +1,74 @@ +/* gtkcloudprintaccount.h: Google Cloud Print account class + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_CLOUDPRINT_ACCOUNT_H__ +#define __GTK_CLOUDPRINT_ACCOUNT_H__ + +#include +#include + +#include "gtkprintbackendcloudprint.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_CLOUDPRINT_ACCOUNT (gtk_cloudprint_account_get_type ()) +#define GTK_CLOUDPRINT_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT, GtkCloudprintAccount)) +#define GTK_IS_CLOUDPRINT_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLOUDPRINT_ACCOUNT)) + +typedef struct _GtkPrinterCloudprint GtkPrinterCloudprint; +typedef struct _GtkCloudprintAccount GtkCloudprintAccount; + +void gtk_cloudprint_account_register_type (GTypeModule *module); +GtkCloudprintAccount *gtk_cloudprint_account_new (const gchar *id, + const gchar *path, + const gchar *presentation_identity); +GType gtk_cloudprint_account_get_type (void) G_GNUC_CONST; + +void gtk_cloudprint_account_search (GtkCloudprintAccount *account, + GDBusConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +JsonNode *gtk_cloudprint_account_search_finish (GtkCloudprintAccount *account, + GAsyncResult *result, + GError **error); + +void gtk_cloudprint_account_printer (GtkCloudprintAccount *account, + const gchar *printerid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +JsonObject *gtk_cloudprint_account_printer_finish (GtkCloudprintAccount *account, + GAsyncResult *result, + GError **error); + +void gtk_cloudprint_account_submit (GtkCloudprintAccount *account, + GtkPrinterCloudprint *printer, + GMappedFile *file, + const gchar *title, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +JsonObject *gtk_cloudprint_account_submit_finish (GtkCloudprintAccount *account, + GAsyncResult *result, + GError **error); + +const gchar *gtk_cloudprint_account_get_presentation_identity (GtkCloudprintAccount *account); + +G_END_DECLS + +#endif /* __GTK_CLOUDPRINT_ACCOUNT_H__ */ diff --git a/modules/printbackends/gtkcupssecretsutils.c b/modules/printbackends/gtkcupssecretsutils.c new file mode 100644 index 0000000000..925f7d561c --- /dev/null +++ b/modules/printbackends/gtkcupssecretsutils.c @@ -0,0 +1,1041 @@ +/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords + * Copyright (C) 2014, Intevation GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include +#include +#include + +#include + +#include "gtkcupssecretsutils.h" + +#define SECRETS_BUS "org.freedesktop.secrets" +#define SECRETS_IFACE(interface) "org.freedesktop.Secret."interface +#define SECRETS_PATH "/org/freedesktop/secrets" +#define SECRETS_TIMEOUT 5000 + +typedef enum +{ + SECRETS_SERVICE_ACTION_QUERY, + SECRETS_SERVICE_ACTION_STORE +} SecretsServiceAction; + +typedef struct +{ + GDBusConnection *dbus_connection; + SecretsServiceAction action; + gchar **auth_info, + **auth_info_labels, + **auth_info_required, + *printer_uri, + *session_path, + *collection_path; + GDBusProxy *item_proxy; + guint prompt_subscription; +} SecretsServiceData; + +/** + * create_attributes: + * @printer_uri: URI for the printer + * @additional_labels: Optional labels for additional attributes + * @additional_attrs: Optional additional attributes + * + * Creates a GVariant dictionary with key / value pairs that + * can be used to identify a secret item. + * + * Returns: A GVariant dictionary of string pairs or NULL on error. + */ +static GVariant * +create_attributes (const gchar *printer_uri, + gchar **additional_attrs, + gchar **additional_labels) +{ + GVariantBuilder *attr_builder = NULL; + GVariant *ret = NULL; + + if (printer_uri == NULL) + { + GTK_NOTE (PRINTING, + g_print ("create_attributes called with invalid parameters.\n")); + return NULL; + } + + attr_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY); + /* The printer uri is the main identifying part */ + g_variant_builder_add (attr_builder, "{ss}", "uri", printer_uri); + + if (additional_labels != NULL) + { + int i; + for (i = 0; additional_labels[i] != NULL; i++) + { + g_variant_builder_add (attr_builder, "{ss}", + additional_labels[i], + additional_attrs[i]); + } + } + + ret = g_variant_builder_end (attr_builder); + g_variant_builder_unref (attr_builder); + + return ret; +} + +static void +get_secret_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GError *error = NULL; + GVariant *output, + *attributes; + gchar **auth_info = NULL, + *key = NULL, + *value = NULL; + GVariantIter *iter = NULL; + guint i, required_len; + gint pw_field = -1; + + task = user_data; + task_data = g_task_get_task_data (task); + + output = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), + res, + &error); + if (output == NULL) + { + g_task_return_error (task, error); + return; + } + + attributes = g_dbus_proxy_get_cached_property (task_data->item_proxy, + "Attributes"); + if (attributes == NULL) + { + GTK_NOTE (PRINTING, g_print ("Failed to lookup attributes.\n")); + g_variant_unref (output); + g_task_return_pointer (task, NULL, NULL); + return; + } + + /* Iterate over the attributes to fill the auth info */ + g_variant_get (attributes, "a{ss}", &iter); + + auth_info = g_new0 (gchar *, + g_strv_length (task_data->auth_info_required) + 1); + + while (g_variant_iter_loop (iter, "{ss}", &key, &value)) + { + /* Match attributes with required auth info */ + for (i = 0; task_data->auth_info_required[i] != NULL; i++) + { + if ((strcmp (key, "user") == 0 || + strcmp (key, "username") == 0) && + strcmp (task_data->auth_info_required[i], + "username") == 0) + { + auth_info[i] = g_strdup (value); + } + else if (strcmp (key, "domain") == 0 && + strcmp (task_data->auth_info_required[i], "domain") == 0) + { + auth_info[i] = g_strdup (value); + } + else if ((strcmp (key, "hostname") == 0 || + strcmp (key, "server") == 0 ) && + strcmp (task_data->auth_info_required[i], "hostname") == 0) + { + auth_info[i] = g_strdup (value); + } + else if (strcmp (task_data->auth_info_required[i], "password") == 0) + { + pw_field = i; + } + } + } + + if (pw_field == -1) + { + /* should not happen... */ + GTK_NOTE (PRINTING, g_print ("No password required?.\n")); + g_variant_unref (output); + goto fail; + } + else + { + GVariant *secret, + *s_value; + gconstpointer ba_passwd = NULL; + gsize len = 0; + + secret = g_variant_get_child_value (output, 0); + g_variant_unref (output); + if (secret == NULL || g_variant_n_children (secret) != 4) + { + GTK_NOTE (PRINTING, g_print ("Get secret response invalid.\n")); + if (secret != NULL) + g_variant_unref (secret); + goto fail; + } + s_value = g_variant_get_child_value (secret, 2); + ba_passwd = g_variant_get_fixed_array (s_value, + &len, + sizeof (guchar)); + + g_variant_unref (secret); + + if (ba_passwd == NULL) + { + GTK_NOTE (PRINTING, g_print ("Invalid / no secret found.\n")); + g_variant_unref (s_value); + goto fail; + } + + auth_info[pw_field] = g_strndup (ba_passwd, len); + g_variant_unref (s_value); + } + + for (i = 0; task_data->auth_info_required[i] != NULL; i++) + { + if (auth_info[i] == NULL) + { + /* Error out if we did not find everything */ + GTK_NOTE (PRINTING, g_print ("Failed to lookup required attribute: %s.\n", + task_data->auth_info_required[i])); + goto fail; + } + } + + g_task_return_pointer (task, auth_info, NULL); + return; + +fail: + /* Error out */ + GTK_NOTE (PRINTING, g_print ("Failed to lookup secret.\n")); + required_len = g_strv_length (task_data->auth_info_required); + for (i = 0; i < required_len; i++) + { + /* Not all fields of auth_info are neccessarily written so we can not + use strfreev here */ + g_free (auth_info[i]); + } + g_free (auth_info); + g_task_return_pointer (task, NULL, NULL); +} + +static void +create_item_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + GError *error = NULL; + GVariant *output; + gchar *item = NULL; + + task = user_data; + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (output == NULL) + { + g_task_return_error (task, error); + return; + } + + g_variant_get (output, "(&o&o)", &item, NULL); + if (item != NULL && strlen (item) > 1) + { + GTK_NOTE (PRINTING, g_print ("Successfully stored auth info.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + g_variant_unref (output); +} + +static void +do_store_auth_info (GTask *task) +{ + GVariant *attributes = NULL, + *properties = NULL, + *secret = NULL; + gchar **additional_attrs = NULL, + **additional_labels = NULL, + *password = NULL; + SecretsServiceData *task_data = g_task_get_task_data (task); + guint i, + length, + additional_count = 0; + GVariantBuilder *prop_builder = NULL; + + length = g_strv_length (task_data->auth_info_labels); + + additional_attrs = g_new0 (gchar *, length + 1); + additional_labels = g_new0 (gchar *, length + 1); + /* The labels user and server are chosen to be compatible with + the attributes used by system-config-printer */ + for (i = 0; task_data->auth_info_labels[i] != NULL; i++) + { + if (g_strcmp0 (task_data->auth_info_labels[i], "username") == 0) + { + additional_attrs[additional_count] = task_data->auth_info[i]; + additional_labels[additional_count++] = "user"; + } + else if (g_strcmp0 (task_data->auth_info_labels[i], "hostname") == 0) + { + additional_attrs[additional_count] = task_data->auth_info[i]; + additional_labels[additional_count++] = "server"; + } + else if (g_strcmp0 (task_data->auth_info_labels[i], "password") == 0) + { + password = task_data->auth_info[i]; + } + } + + attributes = create_attributes (task_data->printer_uri, + additional_attrs, + additional_labels); + g_free (additional_labels); + g_free (additional_attrs); + if (attributes == NULL) + { + GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + + if (password == NULL) + { + GTK_NOTE (PRINTING, g_print ("No secret to store.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + + prop_builder = g_variant_builder_new (G_VARIANT_TYPE_DICTIONARY); + + g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Label"), + g_variant_new_string (task_data->printer_uri)); + g_variant_builder_add (prop_builder, "{sv}", SECRETS_IFACE ("Item.Attributes"), + attributes); + + properties = g_variant_builder_end (prop_builder); + + g_variant_builder_unref (prop_builder); + + secret = g_variant_new ("(oay@ays)", + task_data->session_path, + NULL, + g_variant_new_bytestring (password), + "text/plain"); + + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + task_data->collection_path, + SECRETS_IFACE ("Collection"), + "CreateItem", + g_variant_new ("(@a{sv}@(oayays)b)", + properties, + secret, + TRUE), + G_VARIANT_TYPE ("(oo)"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + create_item_cb, + task); +} + +static void +prompt_completed_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GVariant *dismissed; + gboolean is_dismissed = TRUE; + + task = user_data; + task_data = g_task_get_task_data (task); + + g_dbus_connection_signal_unsubscribe (task_data->dbus_connection, + task_data->prompt_subscription); + task_data->prompt_subscription = 0; + + dismissed = g_variant_get_child_value (parameters, 0); + + if (dismissed == NULL) + { + GTK_NOTE (PRINTING, g_print ("Invalid prompt signal.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + + g_variant_get (dismissed, "b", &is_dismissed); + g_variant_unref (dismissed); + + if (is_dismissed) + { + GTK_NOTE (PRINTING, g_print ("Collection unlock dismissed.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + + /* Prompt successfull, proceed to get or store secret */ + switch (task_data->action) + { + case SECRETS_SERVICE_ACTION_STORE: + do_store_auth_info (task); + break; + + case SECRETS_SERVICE_ACTION_QUERY: + g_dbus_proxy_call (task_data->item_proxy, + "GetSecret", + g_variant_new ("(o)", + task_data->session_path), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + get_secret_cb, + task); + break; + } +} + +static void +prompt_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GError *error = NULL; + GVariant *output; + + task = user_data; + task_data = g_task_get_task_data (task); + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (output == NULL) + { + g_task_return_error (task, error); + return; + } + + g_variant_unref (output); + + /* Connect to the prompt's completed signal */ + task_data->prompt_subscription = + g_dbus_connection_signal_subscribe (task_data->dbus_connection, + NULL, + SECRETS_IFACE ("Prompt"), + "Completed", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + prompt_completed_cb, + task, + NULL); +} + +static void +unlock_collection_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GError *error = NULL; + GVariant *output; + const gchar *prompt_path; + + task = user_data; + task_data = g_task_get_task_data (task); + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (output == NULL) + { + g_task_return_error (task, error); + return; + } + + g_variant_get (output, "(@ao&o)", NULL, &prompt_path); + + if (prompt_path != NULL && strlen (prompt_path) > 1) + { + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + prompt_path, + SECRETS_IFACE ("Prompt"), + "Prompt", + g_variant_new ("(s)", "0"), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + prompt_cb, + task); + } + else + { + switch (task_data->action) + { + case SECRETS_SERVICE_ACTION_STORE: + do_store_auth_info (task); + break; + + case SECRETS_SERVICE_ACTION_QUERY: + /* Prompt successfull proceed to get secret */ + g_dbus_proxy_call (task_data->item_proxy, + "GetSecret", + g_variant_new ("(o)", + task_data->session_path), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + get_secret_cb, + task); + break; + } + } + g_variant_unref (output); +} + +static void +unlock_read_alias_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GError *error = NULL; + GVariant *output, *subresult; + gsize path_len = 0; + const gchar *collection_path; + const gchar *to_unlock[2]; + + task = user_data; + task_data = g_task_get_task_data (task); + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (output == NULL) + { + g_task_return_error (task, error); + return; + } + + subresult = g_variant_get_child_value (output, 0); + g_variant_unref (output); + + if (subresult == NULL) + { + GTK_NOTE (PRINTING, g_print ("Invalid ReadAlias response.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + + collection_path = g_variant_get_string (subresult, &path_len); + to_unlock[0] = collection_path; + to_unlock[1] = NULL; + + task_data->collection_path = g_strdup (collection_path); + + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + SECRETS_PATH, + SECRETS_IFACE ("Service"), + "Unlock", + g_variant_new ("(^ao)", to_unlock), + G_VARIANT_TYPE ("(aoo)"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + unlock_collection_cb, + task); + + g_variant_unref (subresult); +} + +static void +item_proxy_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GError *error = NULL; + GDBusProxy *item_proxy; + GVariant *locked; + gboolean is_locked; + + task = user_data; + task_data = g_task_get_task_data (task); + + item_proxy = g_dbus_proxy_new_finish (res, + &error); + if (item_proxy == NULL) + { + g_task_return_error (task, error); + return; + } + + task_data->item_proxy = item_proxy; + + locked = g_dbus_proxy_get_cached_property (item_proxy, "Locked"); + + if (locked == NULL) + { + GTK_NOTE (PRINTING, g_print ("Failed to look up \"Locked\" property on item.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + + g_variant_get (locked, "b", &is_locked); + g_variant_unref (locked); + + if (is_locked) + { + /* Go down the unlock -> lookup path */ + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + SECRETS_PATH, + SECRETS_IFACE ("Service"), + "ReadAlias", + g_variant_new ("(s)", "default"), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + unlock_read_alias_cb, + task); + return; + } + + /* Unlocked proceed to get or store secret */ + switch (task_data->action) + { + case SECRETS_SERVICE_ACTION_STORE: + do_store_auth_info (task); + break; + + case SECRETS_SERVICE_ACTION_QUERY: + g_dbus_proxy_call (item_proxy, + "GetSecret", + g_variant_new ("(o)", + task_data->session_path), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + get_secret_cb, + task); + break; + } +} + +static void +search_items_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GError *error = NULL; + GVariant *output; + gsize array_cnt, + i; + gboolean found_item = FALSE; + + task = user_data; + task_data = g_task_get_task_data (task); + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (output == NULL) + { + g_task_return_error (task, error); + return; + } + + array_cnt = g_variant_n_children (output); + + for (i = 0; i < array_cnt; i++) + { + GVariant * const item_paths = g_variant_get_child_value (output, i); + const gchar **items = NULL; + + if (item_paths == NULL) + { + GTK_NOTE (PRINTING, + g_print ("SearchItems returned invalid result.\n")); + continue; + } + + items = g_variant_get_objv (item_paths, NULL); + + if (*items == NULL) + { + g_variant_unref (item_paths); + g_free ((gpointer) items); + continue; + } + + /* Access the first found item. */ + found_item = TRUE; + g_dbus_proxy_new (task_data->dbus_connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + SECRETS_BUS, + *items, + SECRETS_IFACE ("Item"), + g_task_get_cancellable (task), + item_proxy_cb, + task); + g_free ((gpointer) items); + g_variant_unref (item_paths); + break; + } + g_variant_unref (output); + + if (!found_item) + { + GTK_NOTE (PRINTING, g_print ("No match found in secrets service.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } +} + +static void +open_session_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + GVariant *output, + *session_variant; + SecretsServiceData *task_data; + GError *error = NULL; + + task = user_data; + task_data = g_task_get_task_data (task); + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + if (output == NULL) + { + g_task_return_error (task, error); + return; + } + + session_variant = g_variant_get_child_value (output, 1); + + if (session_variant == NULL) + { + GTK_NOTE (PRINTING, g_print ("Invalid session path response.\n")); + g_variant_unref (output); + g_task_return_pointer (task, NULL, NULL); + return; + } + + task_data->session_path = g_variant_dup_string (session_variant, NULL); + + if (task_data->session_path == NULL) + { + GTK_NOTE (PRINTING, g_print ("Invalid session path string value.\n")); + g_variant_unref (session_variant); + g_variant_unref (output); + g_task_return_pointer (task, NULL, NULL); + return; + } + + g_variant_unref (session_variant); + g_variant_unref (output); + + switch (task_data->action) + { + case SECRETS_SERVICE_ACTION_QUERY: + { + /* Search for the secret item */ + GVariant *secrets_attrs; + + secrets_attrs = create_attributes (task_data->printer_uri, NULL, NULL); + if (secrets_attrs == NULL) + { + GTK_NOTE (PRINTING, g_print ("Failed to create attributes.\n")); + g_task_return_pointer (task, NULL, NULL); + return; + } + + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + SECRETS_PATH, + SECRETS_IFACE ("Service"), + "SearchItems", + g_variant_new ("(@a{ss})", secrets_attrs), + G_VARIANT_TYPE ("(aoao)"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + search_items_cb, + task); + break; + } + + case SECRETS_SERVICE_ACTION_STORE: + { + /* Look up / unlock the default collection for storing */ + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + SECRETS_PATH, + SECRETS_IFACE ("Service"), + "ReadAlias", + g_variant_new ("(s)", "default"), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + unlock_read_alias_cb, + task); + break; + } + } +} + +static void +get_connection_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task; + SecretsServiceData *task_data; + GError *error = NULL; + + task = user_data; + task_data = g_task_get_task_data (task); + + task_data->dbus_connection = g_bus_get_finish (res, &error); + if (task_data->dbus_connection == NULL) + { + g_task_return_error (task, error); + return; + } + + /* Now open a session */ + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + SECRETS_PATH, + SECRETS_IFACE ("Service"), + "OpenSession", + g_variant_new ("(sv)", "plain", + g_variant_new_string ("")), + G_VARIANT_TYPE ("(vo)"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + g_task_get_cancellable (task), + open_session_cb, + task); +} + +/** + * gtk_cups_secrets_service_watch: + * @appeared: The callback to call when the service interface appears + * @vanished: The callback to call when the service interface vanishes + * @user_data: A reference to the watching printbackend + * + * Registers a watch for the secrets service interface. + * + * Returns: The watcher id + */ +guint +gtk_cups_secrets_service_watch (GBusNameAppearedCallback appeared, + GBusNameVanishedCallback vanished, + gpointer user_data) +{ + return g_bus_watch_name (G_BUS_TYPE_SESSION, + SECRETS_BUS, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + appeared, + vanished, + user_data, + NULL); +} + +void +cleanup_task_data (gpointer data) +{ + gint i; + SecretsServiceData *task_data = data; + + g_free (task_data->collection_path); + g_strfreev (task_data->auth_info_labels); + g_strfreev (task_data->auth_info_required); + g_free (task_data->printer_uri); + + if (task_data->auth_info != NULL) + { + for (i = 0; task_data->auth_info[i] != NULL; i++) + { + memset (task_data->auth_info[i], 0, strlen (task_data->auth_info[i])); + g_clear_pointer (&task_data->auth_info[i], g_free); + } + g_clear_pointer (&task_data->auth_info, g_free); + } + + if (task_data->prompt_subscription != 0) + { + g_dbus_connection_signal_unsubscribe (task_data->dbus_connection, + task_data->prompt_subscription); + task_data->prompt_subscription = 0; + } + + if (task_data->session_path != NULL) + { + g_dbus_connection_call (task_data->dbus_connection, + SECRETS_BUS, + task_data->session_path, + SECRETS_IFACE ("Session"), + "Close", + NULL, + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + SECRETS_TIMEOUT, + NULL, + NULL, + NULL); + } + + g_clear_object (&task_data->dbus_connection); + g_clear_pointer (&task_data->session_path, g_free); + g_clear_object (&task_data->item_proxy); +} + +/** + * gtk_cups_secrets_service_query_task: + * @source_object: Source object for this task + * @cancellable: Cancellable to cancel this task + * @callback: Callback to call once the query is finished + * @user_data: The user_data passed to the callback + * @printer_uri: URI of the printer + * @auth_info_required: Info required for authentication + * + * Checks if a secrets service as described by the secrets-service standard + * is available and if so it tries to find the authentication info in the + * default collection of the service. + * + * This is the entry point to a chain of async calls to open a session, + * search the secret, unlock the collection (if necessary) and finally + * to lookup the secret. + * + * See: http://standards.freedesktop.org/secret-service/ for documentation + * of the used API. + */ +void +gtk_cups_secrets_service_query_task (gpointer source_object, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + const gchar *printer_uri, + gchar **auth_info_required) +{ + GTask *task; + SecretsServiceData *task_data; + + task_data = g_new0 (SecretsServiceData, 1); + task_data->action = SECRETS_SERVICE_ACTION_QUERY; + task_data->printer_uri = g_strdup (printer_uri); + task_data->auth_info_required = g_strdupv (auth_info_required); + + task = g_task_new (source_object, cancellable, callback, user_data); + + g_task_set_task_data (task, task_data, cleanup_task_data); + + g_bus_get (G_BUS_TYPE_SESSION, cancellable, + get_connection_cb, task); +} + +static void +store_done_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = (GTask *) res; + GError *error = NULL; + + g_task_propagate_pointer (task, &error); + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("Failed to store auth info: %s\n", error->message)); + g_error_free (error); + } + + g_object_unref (task); + GTK_NOTE (PRINTING, + g_print ("gtk_cups_secrets_service_store finished.\n")); +} + +/** + * gtk_cups_secrets_service_store: + * @auth_info: Auth info that should be stored + * @auth_info_labels: The keys to use for the auth info + * @printer_uri: URI of the printer + * + * Tries to store the auth_info in a secrets service. + */ +void +gtk_cups_secrets_service_store (gchar **auth_info, + gchar **auth_info_labels, + const gchar *printer_uri) +{ + GTask *task; + SecretsServiceData *task_data; + + if (auth_info == NULL || auth_info_labels == NULL || printer_uri == NULL) + { + GTK_NOTE (PRINTING, + g_print ("Invalid call to gtk_cups_secrets_service_store.\n")); + return; + } + + task_data = g_new0 (SecretsServiceData, 1); + task_data->action = SECRETS_SERVICE_ACTION_STORE; + task_data->printer_uri = g_strdup (printer_uri); + task_data->auth_info = g_strdupv (auth_info); + task_data->auth_info_labels = g_strdupv (auth_info_labels); + + task = g_task_new (NULL, NULL, store_done_cb, NULL); + + g_task_set_task_data (task, task_data, cleanup_task_data); + + g_bus_get (G_BUS_TYPE_SESSION, NULL, + get_connection_cb, task); +} diff --git a/modules/printbackends/gtkcupssecretsutils.h b/modules/printbackends/gtkcupssecretsutils.h new file mode 100644 index 0000000000..1a0424a3bf --- /dev/null +++ b/modules/printbackends/gtkcupssecretsutils.h @@ -0,0 +1,41 @@ +/* gtkcupssecretsutils.h: Helper to use a secrets service for printer passwords + * Copyright (C) 2014 Intevation GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ +#ifndef __GTK_SECRETS_UTILS_H__ +#define __GTK_SECRETS_UTILS_H__ + +#include + +#include "gtkcupsutils.h" + +G_BEGIN_DECLS + +void gtk_cups_secrets_service_query_task (gpointer source_object, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + const gchar *printer_uri, + gchar **auth_info_required); +guint gtk_cups_secrets_service_watch (GBusNameAppearedCallback appeared, + GBusNameVanishedCallback vanished, + gpointer user_data); +void gtk_cups_secrets_service_store (gchar **auth_info, + gchar **auth_info_labels, + const gchar *printer_uri); + +G_END_DECLS + +#endif /* __GTK_SECRETS_UTILS_H__ */ diff --git a/modules/printbackends/gtkcupsutils.c b/modules/printbackends/gtkcupsutils.c new file mode 100644 index 0000000000..54239cad07 --- /dev/null +++ b/modules/printbackends/gtkcupsutils.c @@ -0,0 +1,1663 @@ +/* GTK - The GIMP Toolkit + * gtkcupsutils.h: Statemachine implementation of POST and GET + * cups calls which can be used to create a non-blocking cups API + * Copyright (C) 2006, 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include "gtkcupsutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request); + +static void _connect (GtkCupsRequest *request); +static void _post_send (GtkCupsRequest *request); +static void _post_write_request (GtkCupsRequest *request); +static void _post_write_data (GtkCupsRequest *request); +static void _post_check (GtkCupsRequest *request); +static void _post_auth (GtkCupsRequest *request); +static void _post_read_response (GtkCupsRequest *request); + +static void _get_send (GtkCupsRequest *request); +static void _get_check (GtkCupsRequest *request); +static void _get_auth (GtkCupsRequest *request); +static void _get_read_data (GtkCupsRequest *request); + +struct _GtkCupsResult +{ + gchar *error_msg; + ipp_t *ipp_response; + GtkCupsErrorType error_type; + + /* some error types like HTTP_ERROR have a status and a code */ + int error_status; + int error_code; + + guint is_error : 1; + guint is_ipp_response : 1; +}; + + +#define _GTK_CUPS_MAX_ATTEMPTS 10 +#define _GTK_CUPS_MAX_CHUNK_SIZE 8192 + +static GtkCupsRequestStateFunc post_states[] = { + _connect, + _post_send, + _post_write_request, + _post_write_data, + _post_check, + _post_auth, + _post_read_response +}; + +static GtkCupsRequestStateFunc get_states[] = { + _connect, + _get_send, + _get_check, + _get_auth, + _get_read_data +}; + +#ifndef HAVE_CUPS_API_1_6 +#define ippSetOperation(ipp_request, ipp_op_id) ipp_request->request.op.operation_id = ipp_op_id +#define ippSetRequestId(ipp_request, ipp_rq_id) ipp_request->request.op.request_id = ipp_rq_id +#define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state +#define ippGetString(attr, index, foo) attr->values[index].string.text +#define ippGetCount(attr) attr->num_values + +int +ippSetVersion (ipp_t *ipp, + int major, + int minor) +{ + if (!ipp || major < 0 || minor < 0) + return 0; + + ipp->request.any.version[0] = major; + ipp->request.any.version[1] = minor; + + return 1; +} +#endif + +static void +gtk_cups_result_set_error (GtkCupsResult *result, + GtkCupsErrorType error_type, + int error_status, + int error_code, + const char *error_msg, + ...) +{ + va_list args; + + result->is_ipp_response = FALSE; + result->is_error = TRUE; + result->error_type = error_type; + result->error_status = error_status; + result->error_code = error_code; + + va_start (args, error_msg); + result->error_msg = g_strdup_vprintf (error_msg, args); + va_end (args); +} + +GtkCupsRequest * +gtk_cups_request_new_with_username (http_t *connection, + GtkCupsRequestType req_type, + gint operation_id, + GIOChannel *data_io, + const char *server, + const char *resource, + const char *username) +{ + GtkCupsRequest *request; + cups_lang_t *language; + + request = g_new0 (GtkCupsRequest, 1); + request->result = g_new0 (GtkCupsResult, 1); + + request->result->error_msg = NULL; + request->result->ipp_response = NULL; + + request->result->is_error = FALSE; + request->result->is_ipp_response = FALSE; + + request->type = req_type; + request->state = GTK_CUPS_REQUEST_START; + + request->password_state = GTK_CUPS_PASSWORD_NONE; + + if (server) + request->server = g_strdup (server); + else + request->server = g_strdup (cupsServer ()); + + + if (resource) + request->resource = g_strdup (resource); + else + request->resource = g_strdup ("/"); + + if (connection != NULL) + { + request->http = connection; + request->own_http = FALSE; + } + else + { + request->http = NULL; + request->http = httpConnectEncrypt (request->server, + ippPort (), + cupsEncryption ()); + + if (request->http) + httpBlocking (request->http, 0); + + request->own_http = TRUE; + } + + request->last_status = HTTP_CONTINUE; + + request->attempts = 0; + request->data_io = data_io; + + request->ipp_request = ippNew (); + ippSetOperation (request->ipp_request, operation_id); + ippSetRequestId (request->ipp_request, 1); + + language = cupsLangDefault (); + + gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", + NULL, "utf-8"); + + gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", + NULL, language->language); + + if (username != NULL) + gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requesting-user-name", + NULL, username); + else + gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requesting-user-name", + NULL, cupsUser ()); + + request->auth_info_required = NULL; + request->auth_info = NULL; + request->need_auth_info = FALSE; + + cupsLangFree (language); + + return request; +} + +GtkCupsRequest * +gtk_cups_request_new (http_t *connection, + GtkCupsRequestType req_type, + gint operation_id, + GIOChannel *data_io, + const char *server, + const char *resource) +{ + return gtk_cups_request_new_with_username (connection, + req_type, + operation_id, + data_io, + server, + resource, + NULL); +} + +static void +gtk_cups_result_free (GtkCupsResult *result) +{ + g_free (result->error_msg); + + if (result->ipp_response) + ippDelete (result->ipp_response); + + g_free (result); +} + +void +gtk_cups_request_free (GtkCupsRequest *request) +{ + if (request->own_http) + { + if (request->http) + httpClose (request->http); + } + + if (request->ipp_request) + ippDelete (request->ipp_request); + + g_free (request->server); + g_free (request->resource); + if (request->password != NULL) + { + memset (request->password, 0, strlen (request->password)); + g_free (request->password); + } + + g_free (request->username); + g_strfreev (request->auth_info_required); + + gtk_cups_result_free (request->result); + + g_free (request); +} + +gboolean +gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only) +{ + if (connect_only && request->state != GTK_CUPS_REQUEST_START) + return FALSE; + + do + { + if (request->type == GTK_CUPS_POST) + post_states[request->state] (request); + else if (request->type == GTK_CUPS_GET) + get_states[request->state] (request); + + if (gtk_cups_result_is_error (request->result)) + request->state = GTK_CUPS_REQUEST_DONE; + + if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && + request->state != GTK_CUPS_REQUEST_DONE) + { + /* TODO: should add a status or error code for too many failed attempts */ + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_GENERAL, + 0, + 0, + "Too many failed attempts"); + + request->state = GTK_CUPS_REQUEST_DONE; + } + + if (request->state == GTK_CUPS_REQUEST_DONE) + { + request->poll_state = GTK_CUPS_HTTP_IDLE; + return TRUE; + } + } + /* We need to recheck using httpCheck if the poll_state is read, because + * Cups has an internal read buffer. And if this buffer is filled, we may + * never get a poll event again. */ + while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http)); + + return FALSE; +} + +GtkCupsPollState +gtk_cups_request_get_poll_state (GtkCupsRequest *request) +{ + return request->poll_state; +} + + + +GtkCupsResult * +gtk_cups_request_get_result (GtkCupsRequest *request) +{ + return request->result; +} + +void +gtk_cups_request_ipp_add_string (GtkCupsRequest *request, + ipp_tag_t group, + ipp_tag_t tag, + const char *name, + const char *charset, + const char *value) +{ + ippAddString (request->ipp_request, + group, + tag, + name, + charset, + value); +} + +void +gtk_cups_request_ipp_add_strings (GtkCupsRequest *request, + ipp_tag_t group, + ipp_tag_t tag, + const char *name, + int num_values, + const char *charset, + const char *const *values) +{ + ippAddStrings (request->ipp_request, + group, + tag, + name, + num_values, + charset, + values); +} + +const char * +gtk_cups_request_ipp_get_string (GtkCupsRequest *request, + ipp_tag_t tag, + const char *name) +{ + ipp_attribute_t *attribute = NULL; + + if (request != NULL && request->ipp_request != NULL) + attribute = ippFindAttribute (request->ipp_request, + name, + tag); + + if (attribute != NULL && ippGetCount (attribute) > 0) + return ippGetString (attribute, 0, NULL); + else + return NULL; +} + + +typedef struct +{ + const char *name; + ipp_tag_t value_tag; +} ipp_option_t; + +static const ipp_option_t ipp_options[] = { + { "blackplot", IPP_TAG_BOOLEAN }, + { "brightness", IPP_TAG_INTEGER }, + { "columns", IPP_TAG_INTEGER }, + { "copies", IPP_TAG_INTEGER }, + { "finishings", IPP_TAG_ENUM }, + { "fitplot", IPP_TAG_BOOLEAN }, + { "gamma", IPP_TAG_INTEGER }, + { "hue", IPP_TAG_INTEGER }, + { "job-k-limit", IPP_TAG_INTEGER }, + { "job-page-limit", IPP_TAG_INTEGER }, + { "job-priority", IPP_TAG_INTEGER }, + { "job-quota-period", IPP_TAG_INTEGER }, + { "landscape", IPP_TAG_BOOLEAN }, + { "media", IPP_TAG_KEYWORD }, + { "mirror", IPP_TAG_BOOLEAN }, + { "natural-scaling", IPP_TAG_INTEGER }, + { "number-up", IPP_TAG_INTEGER }, + { "orientation-requested", IPP_TAG_ENUM }, + { "page-bottom", IPP_TAG_INTEGER }, + { "page-left", IPP_TAG_INTEGER }, + { "page-ranges", IPP_TAG_RANGE }, + { "page-right", IPP_TAG_INTEGER }, + { "page-top", IPP_TAG_INTEGER }, + { "penwidth", IPP_TAG_INTEGER }, + { "ppi", IPP_TAG_INTEGER }, + { "prettyprint", IPP_TAG_BOOLEAN }, + { "printer-resolution", IPP_TAG_RESOLUTION }, + { "print-quality", IPP_TAG_ENUM }, + { "saturation", IPP_TAG_INTEGER }, + { "scaling", IPP_TAG_INTEGER }, + { "sides", IPP_TAG_KEYWORD }, + { "wrap", IPP_TAG_BOOLEAN }, + { "number-up-layout", IPP_TAG_INTEGER } +}; + + +static ipp_tag_t +_find_option_tag (const gchar *option) +{ + int lower_bound, upper_bound, num_options; + int current_option; + ipp_tag_t result; + + result = IPP_TAG_ZERO; + + lower_bound = 0; + upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1; + + while (1) + { + int match; + current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound); + + match = strcasecmp (option, ipp_options[current_option].name); + if (match == 0) + { + result = ipp_options[current_option].value_tag; + return result; + } + else if (match < 0) + { + upper_bound = current_option - 1; + } + else + { + lower_bound = current_option + 1; + } + + if (upper_bound == lower_bound && upper_bound == current_option) + return result; + + if (upper_bound < 0) + return result; + + if (lower_bound > num_options) + return result; + + if (upper_bound < lower_bound) + return result; + } +} + +/* + * Note that this function uses IPP_TAG_JOB, so it is + * only suitable for IPP Group 2 attributes. + * See RFC 2911. + */ +void +gtk_cups_request_encode_option (GtkCupsRequest *request, + const gchar *option, + const gchar *value) +{ + ipp_tag_t option_tag; + + g_return_if_fail (option != NULL); + g_return_if_fail (value != NULL); + + option_tag = _find_option_tag (option); + + if (option_tag == IPP_TAG_ZERO) + { + option_tag = IPP_TAG_NAME; + if (strcasecmp (value, "true") == 0 || + strcasecmp (value, "false") == 0) + { + option_tag = IPP_TAG_BOOLEAN; + } + } + + switch (option_tag) + { + case IPP_TAG_INTEGER: + case IPP_TAG_ENUM: + ippAddInteger (request->ipp_request, + IPP_TAG_JOB, + option_tag, + option, + strtol (value, NULL, 0)); + break; + + case IPP_TAG_BOOLEAN: + { + char b; + + if (strcasecmp (value, "true") == 0 || + strcasecmp (value, "on") == 0 || + strcasecmp (value, "yes") == 0) + b = 1; + else + b = 0; + + ippAddBoolean (request->ipp_request, + IPP_TAG_JOB, + option, + b); + + break; + } + + case IPP_TAG_RANGE: + { + char *s; + int lower; + int upper; + + if (*value == '-') + { + lower = 1; + s = (char *)value; + } + else + lower = strtol (value, &s, 0); + + if (*s == '-') + { + if (s[1]) + upper = strtol (s + 1, NULL, 0); + else + upper = 2147483647; + } + else + upper = lower; + + ippAddRange (request->ipp_request, + IPP_TAG_JOB, + option, + lower, + upper); + + break; + } + + case IPP_TAG_RESOLUTION: + { + char *s; + int xres; + int yres; + ipp_res_t units; + + xres = strtol (value, &s, 0); + + if (*s == 'x') + yres = strtol (s + 1, &s, 0); + else + yres = xres; + + if (strcasecmp (s, "dpc") == 0) + units = IPP_RES_PER_CM; + else + units = IPP_RES_PER_INCH; + + ippAddResolution (request->ipp_request, + IPP_TAG_JOB, + option, + units, + xres, + yres); + + break; + } + + default: + { + char *values; + char *s; + int in_quotes; + char *next; + GPtrArray *strings; + + values = g_strdup (value); + strings = NULL; + in_quotes = 0; + + for (s = values, next = s; *s != '\0'; s++) + { + if (in_quotes != 2 && *s == '\'') + { + /* skip quoted value */ + if (in_quotes == 0) + in_quotes = 1; + else + in_quotes = 0; + } + else if (in_quotes != 1 && *s == '\"') + { + /* skip quoted value */ + if (in_quotes == 0) + in_quotes = 2; + else + in_quotes = 0; + } + else if (in_quotes == 0 && *s == ',') + { + /* found delimiter, add to value array */ + *s = '\0'; + if (strings == NULL) + strings = g_ptr_array_new (); + g_ptr_array_add (strings, next); + next = s + 1; + } + else if (in_quotes == 0 && *s == '\\' && s[1] != '\0') + { + /* skip escaped character */ + s++; + } + } + + if (strings == NULL) + { + /* single value */ + ippAddString (request->ipp_request, + IPP_TAG_JOB, + option_tag, + option, + NULL, + value); + } + else + { + /* multiple values */ + + /* add last value */ + g_ptr_array_add (strings, next); + + ippAddStrings (request->ipp_request, + IPP_TAG_JOB, + option_tag, + option, + strings->len, + NULL, + (const char **) strings->pdata); + g_ptr_array_free (strings, TRUE); + } + + g_free (values); + } + + break; + } +} + +void +gtk_cups_request_set_ipp_version (GtkCupsRequest *request, + gint major, + gint minor) +{ + ippSetVersion (request->ipp_request, major, minor); +} + +static void +_connect (GtkCupsRequest *request) +{ + request->poll_state = GTK_CUPS_HTTP_IDLE; + request->bytes_received = 0; + + if (request->http == NULL) + { + request->http = httpConnectEncrypt (request->server, + ippPort (), + cupsEncryption ()); + + if (request->http == NULL) + request->attempts++; + + if (request->http) + httpBlocking (request->http, 0); + + request->own_http = TRUE; + } + else + { + request->attempts = 0; + request->state++; + + /* we always write to the socket after we get + the connection */ + request->poll_state = GTK_CUPS_HTTP_WRITE; + } +} + +static void +_post_send (GtkCupsRequest *request) +{ + gchar length[255]; + struct stat data_info; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s\n", G_STRFUNC)); + + request->poll_state = GTK_CUPS_HTTP_WRITE; + + if (request->data_io != NULL) + { + fstat (g_io_channel_unix_get_fd (request->data_io), &data_info); + sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size)); + } + else + sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request)); + + httpClearFields (request->http); + httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length); + httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); +#ifdef HAVE_HTTPGETAUTHSTRING + httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http)); +#else +#ifdef HAVE_HTTP_AUTHSTRING + httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring); +#endif +#endif + + if (httpPost (request->http, request->resource)) + { + if (httpReconnect (request->http)) + { + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + /* TODO: should add a status or error code for failed post */ + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_GENERAL, + 0, + 0, + "Failed Post"); + } + + request->attempts++; + return; + } + + request->attempts = 0; + + request->state = GTK_CUPS_POST_WRITE_REQUEST; + ippSetState (request->ipp_request, IPP_IDLE); +} + +static void +_post_write_request (GtkCupsRequest *request) +{ + ipp_state_t ipp_status; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s\n", G_STRFUNC)); + + request->poll_state = GTK_CUPS_HTTP_WRITE; + + ipp_status = ippWrite (request->http, request->ipp_request); + + if (ipp_status == IPP_ERROR) + { + int cups_error = cupsLastError (); + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_IPP, + ipp_status, + cups_error, + "%s", + ippErrorString (cups_error)); + return; + } + + if (ipp_status == IPP_DATA) + { + if (request->data_io != NULL) + request->state = GTK_CUPS_POST_WRITE_DATA; + else + { + request->state = GTK_CUPS_POST_CHECK; + request->poll_state = GTK_CUPS_HTTP_READ; + } + } +} + +static void +_post_write_data (GtkCupsRequest *request) +{ + gsize bytes; + char buffer[_GTK_CUPS_MAX_CHUNK_SIZE]; + http_status_t http_status; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s\n", G_STRFUNC)); + + request->poll_state = GTK_CUPS_HTTP_WRITE; + + if (httpCheck (request->http)) + http_status = httpUpdate (request->http); + else + http_status = request->last_status; + + request->last_status = http_status; + + + if (http_status == HTTP_CONTINUE || http_status == HTTP_OK) + { + GIOStatus io_status; + GError *error; + + error = NULL; + + /* send data */ + io_status = + g_io_channel_read_chars (request->data_io, + buffer, + _GTK_CUPS_MAX_CHUNK_SIZE, + &bytes, + &error); + + if (io_status == G_IO_STATUS_ERROR) + { + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_IO, + io_status, + error->code, + "Error reading from cache file: %s", + error->message); + + g_error_free (error); + return; + } + else if (bytes == 0 && io_status == G_IO_STATUS_EOF) + { + request->state = GTK_CUPS_POST_CHECK; + request->poll_state = GTK_CUPS_HTTP_READ; + + request->attempts = 0; + return; + } + + + if (httpWrite2 (request->http, buffer, bytes) < bytes) + { + int http_errno; + + http_errno = httpError (request->http); + + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_HTTP, + http_status, + http_errno, + "Error writing to socket in Post %s", + g_strerror (http_errno)); + return; + } + } + else if (http_status == HTTP_UNAUTHORIZED) + { + request->state = GTK_CUPS_POST_CHECK; + request->poll_state = GTK_CUPS_HTTP_READ; + + request->attempts = 0; + return; + } + else + { + request->attempts++; + } +} + +static void +_post_auth (GtkCupsRequest *request) +{ + if (request->password_state == GTK_CUPS_PASSWORD_HAS) + { + if (request->password == NULL) + { + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_AUTH, + 0, + 1, + "Canceled by user"); + } + else + request->state = GTK_CUPS_POST_CHECK; + } +} + +static void +_get_auth (GtkCupsRequest *request) +{ + if (request->password_state == GTK_CUPS_PASSWORD_HAS) + { + if (request->password == NULL) + { + request->state = GTK_CUPS_GET_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_AUTH, + 0, + 1, + "Canceled by user"); + } + else + request->state = GTK_CUPS_GET_CHECK; + } +} + +/* Very ugly hack: cups has a stupid synchronous password callback + * that doesn't even take the request or user data parameters, so + * we have to use a static variable to pass the password to it. + * Not threadsafe ! + * The callback sets cups_password to NULL to signal that the + * password has been used. + */ +static char *cups_password = NULL; +static char *cups_username = NULL; + +static const char * +passwordCB (const char *prompt) +{ + char *pwd = cups_password; + cups_password = NULL; + + cupsSetUser (cups_username); + + return pwd; +} + +static void +_post_check (GtkCupsRequest *request) +{ + http_status_t http_status; + + http_status = request->last_status; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s - status %i\n", G_STRFUNC, http_status)); + + request->poll_state = GTK_CUPS_HTTP_READ; + + if (http_status == HTTP_CONTINUE) + { + goto again; + } + else if (http_status == HTTP_UNAUTHORIZED) + { + int auth_result = -1; + httpFlush (request->http); + + if (request->password_state == GTK_CUPS_PASSWORD_APPLIED) + { + request->poll_state = GTK_CUPS_HTTP_IDLE; + request->password_state = GTK_CUPS_PASSWORD_NOT_VALID; + request->state = GTK_CUPS_POST_AUTH; + request->need_password = TRUE; + + return; + } + + /* Negotiate */ + if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0) + { + auth_result = cupsDoAuthentication (request->http, "POST", request->resource); + } + /* Basic, BasicDigest, Digest and PeerCred */ + else + { + if (request->password_state == GTK_CUPS_PASSWORD_NONE) + { + cups_username = request->username; + cupsSetPasswordCB (passwordCB); + + /* This call success for PeerCred authentication */ + auth_result = cupsDoAuthentication (request->http, "POST", request->resource); + + if (auth_result != 0) + { + /* move to AUTH state to let the backend + * ask for a password + */ + request->poll_state = GTK_CUPS_HTTP_IDLE; + request->state = GTK_CUPS_POST_AUTH; + request->need_password = TRUE; + + return; + } + } + else + { + cups_password = request->password; + cups_username = request->username; + + auth_result = cupsDoAuthentication (request->http, "POST", request->resource); + + if (cups_password != NULL) + return; + + if (request->password != NULL) + { + memset (request->password, 0, strlen (request->password)); + g_free (request->password); + request->password = NULL; + } + + request->password_state = GTK_CUPS_PASSWORD_APPLIED; + } + } + + if (auth_result || + httpReconnect (request->http)) + { + /* if the password has been used, reset password_state + * so that we ask for a new one next time around + */ + if (cups_password == NULL) + request->password_state = GTK_CUPS_PASSWORD_NONE; + + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_AUTH, + 0, + 0, + "Not authorized"); + return; + } + + if (request->data_io != NULL) + g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL); + + request->state = GTK_CUPS_POST_CONNECT; + request->poll_state = GTK_CUPS_HTTP_WRITE; + } + else if (http_status == HTTP_ERROR) + { + int error = httpError (request->http); +#ifdef G_OS_WIN32 + if (error != WSAENETDOWN && error != WSAENETUNREACH) +#else + if (error != ENETDOWN && error != ENETUNREACH) +#endif /* G_OS_WIN32 */ + { + request->attempts++; + goto again; + } + else + { + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_HTTP, + http_status, + error, + "Unknown HTTP error"); + + return; + } + } + else if (http_status == HTTP_UPGRADE_REQUIRED) + { + /* Flush any error message... */ + httpFlush (request->http); + + cupsSetEncryption (HTTP_ENCRYPT_REQUIRED); + request->state = GTK_CUPS_POST_CONNECT; + + /* Reconnect... */ + httpReconnect (request->http); + + /* Upgrade with encryption... */ + httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); + + request->attempts++; + goto again; + } + else if (http_status != HTTP_OK) + { + int http_errno; + + http_errno = httpError (request->http); + + if (http_errno == EPIPE) + request->state = GTK_CUPS_POST_CONNECT; + else + { + request->state = GTK_CUPS_POST_DONE; + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_HTTP, + http_status, + http_errno, + "HTTP Error in POST %s", + g_strerror (http_errno)); + request->poll_state = GTK_CUPS_HTTP_IDLE; + + httpFlush (request->http); + return; + } + + request->poll_state = GTK_CUPS_HTTP_IDLE; + request->last_status = HTTP_CONTINUE; + + httpFlush (request->http); + if (request->own_http) + httpClose (request->http); + request->http = NULL; + + return; + } + else + { + request->state = GTK_CUPS_POST_READ_RESPONSE; + return; + } + + again: + http_status = HTTP_CONTINUE; + + if (httpCheck (request->http)) + http_status = httpUpdate (request->http); + + request->last_status = http_status; +} + +static void +_post_read_response (GtkCupsRequest *request) +{ + ipp_state_t ipp_status; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s\n", G_STRFUNC)); + + request->poll_state = GTK_CUPS_HTTP_READ; + + if (request->result->ipp_response == NULL) + request->result->ipp_response = ippNew(); + + ipp_status = ippRead (request->http, + request->result->ipp_response); + + if (ipp_status == IPP_ERROR) + { + int ipp_error = cupsLastError (); + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_IPP, + ipp_status, + ipp_error, + "%s", + ippErrorString (ipp_error)); + + ippDelete (request->result->ipp_response); + request->result->ipp_response = NULL; + + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + } + else if (ipp_status == IPP_DATA) + { + request->state = GTK_CUPS_POST_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + } +} + +static void +_get_send (GtkCupsRequest *request) +{ + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s\n", G_STRFUNC)); + + request->poll_state = GTK_CUPS_HTTP_WRITE; + + if (request->data_io == NULL) + { + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_IO, + G_IO_STATUS_ERROR, + G_IO_CHANNEL_ERROR_FAILED, + "Get requires an open io channel"); + + request->state = GTK_CUPS_GET_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + return; + } + + httpClearFields (request->http); +#ifdef HAVE_HTTPGETAUTHSTRING + httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http)); +#else +#ifdef HAVE_HTTP_AUTHSTRING + httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring); +#endif +#endif + + if (httpGet (request->http, request->resource)) + { + if (httpReconnect (request->http)) + { + request->state = GTK_CUPS_GET_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + /* TODO: should add a status or error code for failed GET */ + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_GENERAL, + 0, + 0, + "Failed Get"); + } + + request->attempts++; + return; + } + + if (httpCheck (request->http)) + request->last_status = httpUpdate (request->http); + + request->attempts = 0; + + request->state = GTK_CUPS_GET_CHECK; + request->poll_state = GTK_CUPS_HTTP_READ; + + ippSetState (request->ipp_request, IPP_IDLE); +} + +static void +_get_check (GtkCupsRequest *request) +{ + http_status_t http_status; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s\n", G_STRFUNC)); + + http_status = request->last_status; + + request->poll_state = GTK_CUPS_HTTP_READ; + + if (http_status == HTTP_CONTINUE) + { + goto again; + } + else if (http_status == HTTP_UNAUTHORIZED) + { + int auth_result = -1; + httpFlush (request->http); + + if (request->password_state == GTK_CUPS_PASSWORD_APPLIED) + { + request->poll_state = GTK_CUPS_HTTP_IDLE; + request->password_state = GTK_CUPS_PASSWORD_NOT_VALID; + request->state = GTK_CUPS_GET_AUTH; + request->need_password = TRUE; + + return; + } + + /* Negotiate */ + if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0) + { + auth_result = cupsDoAuthentication (request->http, "GET", request->resource); + } + /* Basic, BasicDigest, Digest and PeerCred */ + else + { + if (request->password_state == GTK_CUPS_PASSWORD_NONE) + { + cups_username = request->username; + cupsSetPasswordCB (passwordCB); + + /* This call success for PeerCred authentication */ + auth_result = cupsDoAuthentication (request->http, "GET", request->resource); + + if (auth_result != 0) + { + /* move to AUTH state to let the backend + * ask for a password + */ + request->poll_state = GTK_CUPS_HTTP_IDLE; + request->state = GTK_CUPS_GET_AUTH; + request->need_password = TRUE; + + return; + } + } + else + { + cups_password = request->password; + cups_username = request->username; + + auth_result = cupsDoAuthentication (request->http, "GET", request->resource); + + if (cups_password != NULL) + return; + + if (request->password != NULL) + { + memset (request->password, 0, strlen (request->password)); + g_free (request->password); + request->password = NULL; + } + + request->password_state = GTK_CUPS_PASSWORD_APPLIED; + } + } + + if (auth_result || + httpReconnect (request->http)) + { + /* if the password has been used, reset password_state + * so that we ask for a new one next time around + */ + if (cups_password == NULL) + request->password_state = GTK_CUPS_PASSWORD_NONE; + + request->state = GTK_CUPS_GET_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_AUTH, + 0, + 0, + "Not authorized"); + return; + } + + request->state = GTK_CUPS_GET_CONNECT; + request->last_status = HTTP_CONTINUE; + + return; + } + else if (http_status == HTTP_UPGRADE_REQUIRED) + { + /* Flush any error message... */ + httpFlush (request->http); + + cupsSetEncryption (HTTP_ENCRYPT_REQUIRED); + request->state = GTK_CUPS_GET_CONNECT; + + /* Reconnect... */ + httpReconnect (request->http); + + /* Upgrade with encryption... */ + httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED); + + request->attempts++; + goto again; + } + else if (http_status != HTTP_OK) + { + int http_errno; + + http_errno = httpError (request->http); + + if (http_errno == EPIPE) + request->state = GTK_CUPS_GET_CONNECT; + else + { + request->state = GTK_CUPS_GET_DONE; + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_HTTP, + http_status, + http_errno, + "HTTP Error in GET %s", + g_strerror (http_errno)); + request->poll_state = GTK_CUPS_HTTP_IDLE; + httpFlush (request->http); + + return; + } + + request->poll_state = GTK_CUPS_HTTP_IDLE; + request->last_status = HTTP_CONTINUE; + + httpFlush (request->http); + if (request->own_http) + httpClose (request->http); + request->http = NULL; + + return; + } + else + { + request->state = GTK_CUPS_GET_READ_DATA; + return; + } + + again: + http_status = HTTP_CONTINUE; + + if (httpCheck (request->http)) + http_status = httpUpdate (request->http); + + request->last_status = http_status; + +} + +static void +_get_read_data (GtkCupsRequest *request) +{ + char buffer[_GTK_CUPS_MAX_CHUNK_SIZE]; + gsize bytes; + gsize bytes_written; + GIOStatus io_status; + GError *error; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %s\n", G_STRFUNC)); + + error = NULL; + + request->poll_state = GTK_CUPS_HTTP_READ; + + bytes = httpRead2 (request->http, buffer, sizeof (buffer)); + request->bytes_received += bytes; + + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes)); + + io_status = + g_io_channel_write_chars (request->data_io, + buffer, + bytes, + &bytes_written, + &error); + + if (io_status == G_IO_STATUS_ERROR) + { + request->state = GTK_CUPS_GET_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + gtk_cups_result_set_error (request->result, + GTK_CUPS_ERROR_IO, + io_status, + error->code, + error->message); + g_error_free (error); + } + + /* Stop if we do not expect any more data or EOF was received. */ + if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0) + { + request->state = GTK_CUPS_GET_DONE; + request->poll_state = GTK_CUPS_HTTP_IDLE; + + return; + } +} + +gboolean +gtk_cups_request_is_done (GtkCupsRequest *request) +{ + return (request->state == GTK_CUPS_REQUEST_DONE); +} + +gboolean +gtk_cups_result_is_error (GtkCupsResult *result) +{ + return result->is_error; +} + +ipp_t * +gtk_cups_result_get_response (GtkCupsResult *result) +{ + return result->ipp_response; +} + +GtkCupsErrorType +gtk_cups_result_get_error_type (GtkCupsResult *result) +{ + return result->error_type; +} + +int +gtk_cups_result_get_error_status (GtkCupsResult *result) +{ + return result->error_status; +} + +int +gtk_cups_result_get_error_code (GtkCupsResult *result) +{ + return result->error_code; +} + +const char * +gtk_cups_result_get_error_string (GtkCupsResult *result) +{ + return result->error_msg; +} + +/* This function allocates new instance of GtkCupsConnectionTest() and creates + * a socket for communication with a CUPS server 'server'. + */ +GtkCupsConnectionTest * +gtk_cups_connection_test_new (const char *server, + const int port) +{ + GtkCupsConnectionTest *result = NULL; + gchar *port_str = NULL; + + result = g_new (GtkCupsConnectionTest, 1); + + if (port >= 0) + port_str = g_strdup_printf ("%d", port); + else + port_str = g_strdup_printf ("%d", ippPort ()); + + if (server != NULL) + result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str); + else + result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str); + + g_free (port_str); + + result->socket = -1; + result->current_addr = NULL; + result->last_wrong_addr = NULL; + result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE; + + result->at_init = gtk_cups_connection_test_get_state (result); + + return result; +} + + +/* A non-blocking test whether it is possible to connect to a CUPS server specified + * inside of GtkCupsConnectionTest structure. + * - you need to check it more then once. + * The connection is closed after a successful connection. + */ +GtkCupsConnectionState +gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test) +{ + GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE; + http_addrlist_t *iter; + gint error_code; + gint flags; + gint code; + + if (test == NULL) + return GTK_CUPS_CONNECTION_NOT_AVAILABLE; + + if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE) + { + test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE; + return GTK_CUPS_CONNECTION_AVAILABLE; + } + else + { + if (test->socket == -1) + { + if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL) + iter = test->last_wrong_addr->next; + else + { + test->last_wrong_addr = NULL; + iter = test->addrlist; + } + + while (iter) + { + test->socket = socket (iter->addr.addr.sa_family, + SOCK_STREAM, + 0); + + if (test->socket >= 0) + { + flags = fcntl (test->socket, F_GETFL); + + if (flags != -1) + flags |= O_NONBLOCK; + + fcntl (test->socket, F_SETFL, flags); + + test->current_addr = iter; + + break; + } + iter = iter->next; + } + } + + if (test->socket >= 0) + { + code = connect (test->socket, + &test->current_addr->addr.addr, + httpAddrLength (&test->current_addr->addr)); + + error_code = errno; + + if (code == 0 || error_code == EISCONN) + { + close (test->socket); + test->socket = -1; + test->current_addr = NULL; + result = GTK_CUPS_CONNECTION_AVAILABLE; + } + else + { + if (error_code == EALREADY || error_code == EINPROGRESS) + result = GTK_CUPS_CONNECTION_IN_PROGRESS; + else + { + close (test->socket); + test->socket = -1; + test->last_wrong_addr = test->current_addr; + result = GTK_CUPS_CONNECTION_NOT_AVAILABLE; + } + } + } + + return result; + } +} + +/* This function frees memory used by the GtkCupsConnectionTest structure. + */ +void +gtk_cups_connection_test_free (GtkCupsConnectionTest *test) +{ + if (test == NULL) + return; + + test->current_addr = NULL; + test->last_wrong_addr = NULL; + httpAddrFreeList (test->addrlist); + if (test->socket != -1) + { + close (test->socket); + test->socket = -1; + } + g_free (test); +} diff --git a/modules/printbackends/gtkcupsutils.h b/modules/printbackends/gtkcupsutils.h new file mode 100644 index 0000000000..500bb4fecf --- /dev/null +++ b/modules/printbackends/gtkcupsutils.h @@ -0,0 +1,196 @@ +/* gtkcupsutils.h + * Copyright (C) 2006 John (J5) Palmieri + * + * 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 . + */ + +#ifndef __GTK_CUPS_UTILS_H__ +#define __GTK_CUPS_UTILS_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GtkCupsRequest GtkCupsRequest; +typedef struct _GtkCupsResult GtkCupsResult; +typedef struct _GtkCupsConnectionTest GtkCupsConnectionTest; + +typedef enum +{ + GTK_CUPS_ERROR_HTTP, + GTK_CUPS_ERROR_IPP, + GTK_CUPS_ERROR_IO, + GTK_CUPS_ERROR_AUTH, + GTK_CUPS_ERROR_GENERAL +} GtkCupsErrorType; + +typedef enum +{ + GTK_CUPS_POST, + GTK_CUPS_GET +} GtkCupsRequestType; + + +/** + * Direction we should be polling the http socket on. + * We are either reading or writting at each state. + * This makes it easy for mainloops to connect to poll. + */ +typedef enum +{ + GTK_CUPS_HTTP_IDLE, + GTK_CUPS_HTTP_READ, + GTK_CUPS_HTTP_WRITE +} GtkCupsPollState; + +typedef enum +{ + GTK_CUPS_CONNECTION_AVAILABLE, + GTK_CUPS_CONNECTION_NOT_AVAILABLE, + GTK_CUPS_CONNECTION_IN_PROGRESS +} GtkCupsConnectionState; + +typedef enum +{ + GTK_CUPS_PASSWORD_NONE, + GTK_CUPS_PASSWORD_REQUESTED, + GTK_CUPS_PASSWORD_HAS, + GTK_CUPS_PASSWORD_APPLIED, + GTK_CUPS_PASSWORD_NOT_VALID +} GtkCupsPasswordState; + +struct _GtkCupsRequest +{ + GtkCupsRequestType type; + + http_t *http; + http_status_t last_status; + ipp_t *ipp_request; + + gchar *server; + gchar *resource; + GIOChannel *data_io; + gint attempts; + + GtkCupsResult *result; + + gint state; + GtkCupsPollState poll_state; + guint64 bytes_received; + + gchar *password; + gchar *username; + + gint own_http : 1; + gint need_password : 1; + gint need_auth_info : 1; + gchar **auth_info_required; + gchar **auth_info; + GtkCupsPasswordState password_state; +}; + +struct _GtkCupsConnectionTest +{ + GtkCupsConnectionState at_init; + http_addrlist_t *addrlist; + http_addrlist_t *current_addr; + http_addrlist_t *last_wrong_addr; + gint socket; +}; + +#define GTK_CUPS_REQUEST_START 0 +#define GTK_CUPS_REQUEST_DONE 500 + +/* POST states */ +enum +{ + GTK_CUPS_POST_CONNECT = GTK_CUPS_REQUEST_START, + GTK_CUPS_POST_SEND, + GTK_CUPS_POST_WRITE_REQUEST, + GTK_CUPS_POST_WRITE_DATA, + GTK_CUPS_POST_CHECK, + GTK_CUPS_POST_AUTH, + GTK_CUPS_POST_READ_RESPONSE, + GTK_CUPS_POST_DONE = GTK_CUPS_REQUEST_DONE +}; + +/* GET states */ +enum +{ + GTK_CUPS_GET_CONNECT = GTK_CUPS_REQUEST_START, + GTK_CUPS_GET_SEND, + GTK_CUPS_GET_CHECK, + GTK_CUPS_GET_AUTH, + GTK_CUPS_GET_READ_DATA, + GTK_CUPS_GET_DONE = GTK_CUPS_REQUEST_DONE +}; + +GtkCupsRequest * gtk_cups_request_new_with_username (http_t *connection, + GtkCupsRequestType req_type, + gint operation_id, + GIOChannel *data_io, + const char *server, + const char *resource, + const char *username); +GtkCupsRequest * gtk_cups_request_new (http_t *connection, + GtkCupsRequestType req_type, + gint operation_id, + GIOChannel *data_io, + const char *server, + const char *resource); +void gtk_cups_request_ipp_add_string (GtkCupsRequest *request, + ipp_tag_t group, + ipp_tag_t tag, + const char *name, + const char *charset, + const char *value); +void gtk_cups_request_ipp_add_strings (GtkCupsRequest *request, + ipp_tag_t group, + ipp_tag_t tag, + const char *name, + int num_values, + const char *charset, + const char * const *values); +const char * gtk_cups_request_ipp_get_string (GtkCupsRequest *request, + ipp_tag_t tag, + const char *name); +gboolean gtk_cups_request_read_write (GtkCupsRequest *request, + gboolean connect_only); +GtkCupsPollState gtk_cups_request_get_poll_state (GtkCupsRequest *request); +void gtk_cups_request_free (GtkCupsRequest *request); +GtkCupsResult * gtk_cups_request_get_result (GtkCupsRequest *request); +gboolean gtk_cups_request_is_done (GtkCupsRequest *request); +void gtk_cups_request_encode_option (GtkCupsRequest *request, + const gchar *option, + const gchar *value); +void gtk_cups_request_set_ipp_version (GtkCupsRequest *request, + gint major, + gint minor); +gboolean gtk_cups_result_is_error (GtkCupsResult *result); +ipp_t * gtk_cups_result_get_response (GtkCupsResult *result); +GtkCupsErrorType gtk_cups_result_get_error_type (GtkCupsResult *result); +int gtk_cups_result_get_error_status (GtkCupsResult *result); +int gtk_cups_result_get_error_code (GtkCupsResult *result); +const char * gtk_cups_result_get_error_string (GtkCupsResult *result); +GtkCupsConnectionTest * gtk_cups_connection_test_new (const char *server, + const int port); +GtkCupsConnectionState gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test); +void gtk_cups_connection_test_free (GtkCupsConnectionTest *test); + +G_END_DECLS +#endif diff --git a/modules/printbackends/gtkprintbackendcloudprint.c b/modules/printbackends/gtkprintbackendcloudprint.c new file mode 100644 index 0000000000..b5560343a2 --- /dev/null +++ b/modules/printbackends/gtkprintbackendcloudprint.c @@ -0,0 +1,1036 @@ +/* gtkprintbackendcloudprint.c: Google Cloud Print implementation of + * GtkPrintBackend + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "gtkprintbackendcloudprint.h" +#include "gtkcloudprintaccount.h" +#include "gtkprintercloudprint.h" + +typedef struct _GtkPrintBackendCloudprintClass GtkPrintBackendCloudprintClass; + +#define GTK_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass)) +#define GTK_IS_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT)) +#define GTK_PRINT_BACKEND_CLOUDPRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass)) + +#define _STREAM_MAX_CHUNK_SIZE 8192 + +#define ONLINE_ACCOUNTS_PATH "/org/gnome/OnlineAccounts" +#define OBJECT_MANAGER_IFACE "org.freedesktop.DBus.ObjectManager" + +struct _GtkPrintBackendCloudprintClass +{ + GtkPrintBackendClass parent_class; +}; + +struct _GtkPrintBackendCloudprint +{ + GtkPrintBackend parent_instance; + GCancellable *cancellable; + guint accounts_searching; +}; + +struct +{ + gchar *id; + gchar *path; + gchar *presentation_identity; +} typedef TGOAAccount; + +static GObjectClass *backend_parent_class; +static void gtk_print_backend_cloudprint_class_init (GtkPrintBackendCloudprintClass *class); +static void gtk_print_backend_cloudprint_init (GtkPrintBackendCloudprint *impl); +static void gtk_print_backend_cloudprint_finalize (GObject *object); +static void cloudprint_printer_get_settings_from_options (GtkPrinter *printer, + GtkPrinterOptionSet *options, + GtkPrintSettings *settings); +static GtkPrinterOptionSet *cloudprint_printer_get_options (GtkPrinter *printer, + GtkPrintSettings *settings, + GtkPageSetup *page_setup, + GtkPrintCapabilities capabilities); +static void cloudprint_printer_prepare_for_print (GtkPrinter *printer, + GtkPrintJob *print_job, + GtkPrintSettings *settings, + GtkPageSetup *page_setup); +static void cloudprint_request_printer_list (GtkPrintBackend *print_backend); +static void gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend, + GtkPrintJob *job, + GIOChannel *data_io, + GtkPrintJobCompleteFunc callback, + gpointer user_data, + GDestroyNotify dnotify); +static cairo_surface_t * cloudprint_printer_create_cairo_surface (GtkPrinter *printer, + GtkPrintSettings *settings, + gdouble width, + gdouble height, + GIOChannel *cache_io); +static void cloudprint_printer_request_details (GtkPrinter *printer); +TGOAAccount * t_goa_account_copy (TGOAAccount *account); +void t_goa_account_free (gpointer data); + +G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendCloudprint, gtk_print_backend_cloudprint, GTK_TYPE_PRINT_BACKEND) + +void +g_io_module_load (GIOModule *module) +{ + g_type_module_use (G_TYPE_MODULE (module)); + + gtk_print_backend_cloudprint_register_type (G_TYPE_MODULE (module)); + gtk_cloudprint_account_register_type (G_TYPE_MODULE (module)); + gtk_printer_cloudprint_register_type (G_TYPE_MODULE (module)); + + g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, + GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, + "cloudprint", + 10); +} + +void +g_io_module_unload (GIOModule *module) +{ +} + +char ** +g_io_module_query (void) +{ + char *eps[] = { + GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, + NULL + }; + + return g_strdupv (eps); +} + +/** + * gtk_print_backend_cloudprint_new: + * + * Creates a new #GtkPrintBackendCloudprint + * object. #GtkPrintBackendCloudprint implements the #GtkPrintBackend + * interface using REST API calls to the Google Cloud Print service. + * + * Returns: the new #GtkPrintBackendCloudprint object + **/ +GtkPrintBackend * +gtk_print_backend_cloudprint_new (void) +{ + return g_object_new (GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, NULL); +} + +static void +gtk_print_backend_cloudprint_class_init (GtkPrintBackendCloudprintClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (klass); + + backend_parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gtk_print_backend_cloudprint_finalize; + + backend_class->request_printer_list = cloudprint_request_printer_list; + backend_class->print_stream = gtk_print_backend_cloudprint_print_stream; + backend_class->printer_create_cairo_surface = cloudprint_printer_create_cairo_surface; + backend_class->printer_get_options = cloudprint_printer_get_options; + backend_class->printer_get_settings_from_options = cloudprint_printer_get_settings_from_options; + backend_class->printer_prepare_for_print = cloudprint_printer_prepare_for_print; + backend_class->printer_request_details = cloudprint_printer_request_details; +} + +static void +gtk_print_backend_cloudprint_class_finalize (GtkPrintBackendCloudprintClass *class) +{ +} + +static void +gtk_print_backend_cloudprint_init (GtkPrintBackendCloudprint *backend) +{ + backend->cancellable = g_cancellable_new (); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: +GtkPrintBackendCloudprint(%p)\n", + backend)); +} + +static void +gtk_print_backend_cloudprint_finalize (GObject *object) +{ + GtkPrintBackendCloudprint *backend; + + backend = GTK_PRINT_BACKEND_CLOUDPRINT (object); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: -GtkPrintBackendCloudprint(%p)\n", + backend)); + + g_cancellable_cancel (backend->cancellable); + g_clear_object (&(backend->cancellable)); + + backend_parent_class->finalize (object); +} + +static cairo_status_t +_cairo_write (void *closure, + const unsigned char *data, + unsigned int length) +{ + GIOChannel *io = (GIOChannel *)closure; + gsize written; + GError *error; + + error = NULL; + + while (length > 0) + { + g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error); + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: Error writing to temp file, %s\n", error->message)); + + g_error_free (error); + return CAIRO_STATUS_WRITE_ERROR; + } + + data += written; + length -= written; + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_surface_t * +cloudprint_printer_create_cairo_surface (GtkPrinter *printer, + GtkPrintSettings *settings, + gdouble width, + gdouble height, + GIOChannel *cache_io) +{ + cairo_surface_t *surface; + + surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height); + + cairo_surface_set_fallback_resolution (surface, + 2.0 * gtk_print_settings_get_printer_lpi (settings), + 2.0 * gtk_print_settings_get_printer_lpi (settings)); + + return surface; +} + +typedef struct { + GtkPrintBackend *backend; + GtkPrintJobCompleteFunc callback; + GtkPrintJob *job; + GIOChannel *target_io; + gpointer user_data; + GDestroyNotify dnotify; + gchar *path; + + /* Base64 encoding state */ + gint b64state; + gint b64save; +} _PrintStreamData; + +static void +cloudprint_submit_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); + _PrintStreamData *ps = (_PrintStreamData *) user_data; + JsonObject *result; + GError *error = NULL; + gboolean success = FALSE; + + result = gtk_cloudprint_account_submit_finish (account, res, &error); + g_object_unref (account); + if (result == NULL) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: submit REST reply: %s\n", + error->message)); + goto done; + } + + json_object_unref (result); + success = TRUE; + + done: + if (ps->callback != NULL) + ps->callback (ps->job, ps->user_data, error); + + if (ps->dnotify != NULL) + ps->dnotify (ps->user_data); + + gtk_print_job_set_status (ps->job, + (success ? + GTK_PRINT_STATUS_FINISHED : + GTK_PRINT_STATUS_FINISHED_ABORTED)); + + g_clear_object (&(ps->job)); + g_clear_object (&(ps->backend)); + g_clear_pointer (&error, g_error_free); + + g_free (ps->path); + g_free (ps); +} + +static void +cloudprint_print_cb (GtkPrintBackendCloudprint *print_backend, + GError *cb_error, + gpointer user_data) +{ + _PrintStreamData *ps = (_PrintStreamData *) user_data; + gsize encodedlen; + gchar encoded[4]; /* Up to 4 bytes are needed to finish encoding */ + GError *error = NULL; + + encodedlen = g_base64_encode_close (FALSE, + encoded, + &ps->b64state, + &ps->b64save); + + if (encodedlen > 0) + g_io_channel_write_chars (ps->target_io, + encoded, + encodedlen, + NULL, + &error); + + if (ps->target_io != NULL) + g_io_channel_unref (ps->target_io); + + if (cb_error == NULL) + { + GMappedFile *map = g_mapped_file_new (ps->path, FALSE, &error); + GtkPrinter *printer = gtk_print_job_get_printer (ps->job); + GtkCloudprintAccount *account = NULL; + + if (map == NULL) + { + GTK_NOTE (PRINTING, + g_printerr ("Cloud Print Backend: failed to map file: %s\n", + error->message)); + g_error_free (error); + goto out; + } + + g_object_get (printer, + "cloudprint-account", &account, + NULL); + + g_warn_if_fail (account != NULL); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: submitting job\n")); + gtk_cloudprint_account_submit (account, + GTK_PRINTER_CLOUDPRINT (printer), + map, + gtk_print_job_get_title (ps->job), + print_backend->cancellable, + cloudprint_submit_cb, + ps); + } + + out: + if (ps->path != NULL) + unlink (ps->path); + + if (cb_error != NULL || error != NULL) + { + if (ps->callback != NULL) + ps->callback (ps->job, ps->user_data, error); + + if (ps->dnotify != NULL) + ps->dnotify (ps->user_data); + + gtk_print_job_set_status (ps->job, + GTK_PRINT_STATUS_FINISHED_ABORTED); + + g_clear_object (&(ps->job)); + g_free (ps->path); + g_free (ps); + } +} + +static gboolean +cloudprint_write (GIOChannel *source, + GIOCondition con, + gpointer user_data) +{ + gchar buf[_STREAM_MAX_CHUNK_SIZE]; + /* Base64 converts 24 bits into 32 bits, so divide the number of + * bytes by 3 (rounding up) and multiply by 4. Also, if the previous + * call left a non-zero state we may need an extra 4 bytes. */ + gchar encoded[(_STREAM_MAX_CHUNK_SIZE / 3 + 1) * 4 + 4]; + gsize bytes_read; + GError *error = NULL; + GIOStatus read_status; + _PrintStreamData *ps = (_PrintStreamData *) user_data; + + read_status = + g_io_channel_read_chars (source, + buf, + _STREAM_MAX_CHUNK_SIZE, + &bytes_read, + &error); + + if (read_status != G_IO_STATUS_ERROR) + { + gsize encodedlen = g_base64_encode_step ((guchar *) buf, + bytes_read, + FALSE, + encoded, + &ps->b64state, + &ps->b64save); + + g_io_channel_write_chars (ps->target_io, + encoded, + encodedlen, + NULL, + &error); + } + + if (error != NULL || read_status == G_IO_STATUS_EOF) + { + cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (ps->backend), + error, user_data); + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: %s\n", error->message)); + + g_error_free (error); + } + + return FALSE; + } + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: Writing %i byte chunk to tempfile\n", (int)bytes_read)); + + return TRUE; +} + +static void +gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend, + GtkPrintJob *job, + GIOChannel *data_io, + GtkPrintJobCompleteFunc callback, + gpointer user_data, + GDestroyNotify dnotify) +{ + const gchar *prefix = "data:application/pdf;base64,"; + GError *internal_error = NULL; + _PrintStreamData *ps; + int tmpfd; + + ps = g_new0 (_PrintStreamData, 1); + ps->callback = callback; + ps->user_data = user_data; + ps->dnotify = dnotify; + ps->job = g_object_ref (job); + ps->backend = g_object_ref (print_backend); + ps->path = g_strdup_printf ("%s/cloudprintXXXXXX.pdf.b64", + g_get_tmp_dir ()); + ps->b64state = 0; + ps->b64save = 0; + + internal_error = NULL; + + if (ps->path == NULL) + goto error; + + tmpfd = g_mkstemp (ps->path); + if (tmpfd == -1) + { + int err = errno; + internal_error = g_error_new (gtk_print_error_quark (), + GTK_PRINT_ERROR_INTERNAL_ERROR, + "Error creating temporary file: %s", + g_strerror (err)); + goto error; + } + + ps->target_io = g_io_channel_unix_new (tmpfd); + + if (ps->target_io != NULL) + { + g_io_channel_set_close_on_unref (ps->target_io, TRUE); + g_io_channel_set_encoding (ps->target_io, NULL, &internal_error); + } + + g_io_channel_write_chars (ps->target_io, + prefix, + strlen (prefix), + NULL, + &internal_error); + +error: + if (internal_error != NULL) + { + cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (print_backend), + internal_error, ps); + + g_error_free (internal_error); + return; + } + + g_io_add_watch (data_io, + G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, + (GIOFunc) cloudprint_write, + ps); +} + +TGOAAccount * +t_goa_account_copy (TGOAAccount *account) +{ + TGOAAccount *result = NULL; + + if (account != NULL) + { + result = g_new0 (TGOAAccount, 1); + result->id = g_strdup (account->id); + result->path = g_strdup (account->path); + result->presentation_identity = g_strdup (account->presentation_identity); + } + + return result; +} + +void +t_goa_account_free (gpointer data) +{ + TGOAAccount *account = (TGOAAccount *) data; + + if (account != NULL) + { + g_free (account->id); + g_free (account->path); + g_free (account->presentation_identity); + g_free (account); + } +} + +static GList * +get_accounts (GVariant *output) +{ + GVariant *objects; + GList *result = NULL; + gint i, j, k; + + g_variant_get (output, "(@a{oa{sa{sv}}})", + &objects); + + if (objects) + { + for (i = 0; i < g_variant_n_children (objects); i++) + { + const gchar *object_name; + GVariant *object_variant; + + g_variant_get_child (objects, i, "{&o@a{sa{sv}}}", + &object_name, + &object_variant); + + if (g_str_has_prefix (object_name, "/org/gnome/OnlineAccounts/Accounts/")) + { + for (j = 0; j < g_variant_n_children (object_variant); j++) + { + const gchar *service_name; + GVariant *service_variant; + + g_variant_get_child (object_variant, j, "{&s@a{sv}}", + &service_name, + &service_variant); + + if (g_str_has_prefix (service_name, "org.gnome.OnlineAccounts.Account")) + { + TGOAAccount *account; + gboolean printers_disabled = FALSE; + gchar *provider_type = NULL; + + account = g_new0 (TGOAAccount, 1); + + account->path = g_strdup (object_name); + for (k = 0; k < g_variant_n_children (service_variant); k++) + { + const gchar *property_name; + GVariant *property_variant; + GVariant *value; + + g_variant_get_child (service_variant, k, "{&s@v}", + &property_name, + &property_variant); + + g_variant_get (property_variant, "v", + &value); + + if (g_strcmp0 (property_name, "Id") == 0) + account->id = g_variant_dup_string (value, NULL); + else if (g_strcmp0 (property_name, "ProviderType") == 0) + provider_type = g_variant_dup_string (value, NULL); + else if (g_strcmp0 (property_name, "PrintersDisabled") == 0) + printers_disabled = g_variant_get_boolean (value); + else if (g_strcmp0 (property_name, "PresentationIdentity") == 0) + account->presentation_identity = g_variant_dup_string (value, NULL); + + g_variant_unref (property_variant); + g_variant_unref (value); + } + + if (!printers_disabled && + g_strcmp0 (provider_type, "google") == 0 && + account->presentation_identity != NULL) + result = g_list_append (result, account); + else + t_goa_account_free (account); + + g_free (provider_type); + } + + g_variant_unref (service_variant); + } + } + + g_variant_unref (object_variant); + } + + g_variant_unref (objects); + } + + return result; +} + +static void +cloudprint_search_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); + GtkPrintBackendCloudprint *backend = NULL; + JsonNode *node; + JsonArray *printers; + guint i; + GError *error = NULL; + + node = gtk_cloudprint_account_search_finish (account, res, &error); + g_object_unref (account); + if (node == NULL) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: search failed: %s\n", + error->message)); + + if (error->domain != G_IO_ERROR || + error->code != G_IO_ERROR_CANCELLED) + backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); + + g_error_free (error); + goto done; + } + + backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); + printers = json_node_get_array (node); + for (i = 0; i < json_array_get_length (printers); i++) + { + GtkPrinterCloudprint *printer; + JsonObject *json_printer = json_array_get_object_element (printers, i); + const char *name = NULL; + const char *id = NULL; + const char *type = NULL; + const char *desc = NULL; + const char *status = NULL; + gboolean is_virtual; + + if (json_object_has_member (json_printer, "displayName")) + name = json_object_get_string_member (json_printer, "displayName"); + + if (json_object_has_member (json_printer, "id")) + id = json_object_get_string_member (json_printer, "id"); + + if (name == NULL || id == NULL) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: ignoring incomplete " + "printer description\n")); + continue; + } + + if (json_object_has_member (json_printer, "type")) + type = json_object_get_string_member (json_printer, "type"); + + if (json_object_has_member (json_printer, "description")) + desc = json_object_get_string_member (json_printer, "description"); + + if (json_object_has_member (json_printer, "connectionStatus")) + status = json_object_get_string_member (json_printer, + "connectionStatus"); + + is_virtual = (type != NULL && !strcmp (type, "DOCS")); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: Adding printer %s\n", name)); + + printer = gtk_printer_cloudprint_new (name, + is_virtual, + GTK_PRINT_BACKEND (backend), + account, + id); + gtk_printer_set_has_details (GTK_PRINTER (printer), FALSE); + gtk_printer_set_icon_name (GTK_PRINTER (printer), "printer"); + gtk_printer_set_location (GTK_PRINTER (printer), + gtk_cloudprint_account_get_presentation_identity (account)); + + if (desc != NULL) + gtk_printer_set_description (GTK_PRINTER (printer), desc); + + if (status != NULL) + { + if (!strcmp (status, "ONLINE")) + /* Translators: The printer status is online, i.e. it is + * ready to print. */ + gtk_printer_set_state_message (GTK_PRINTER (printer), _("Online")); + else if (!strcmp (status, "UNKNOWN")) + /* Translators: We don't know whether this printer is + * available to print to. */ + gtk_printer_set_state_message (GTK_PRINTER (printer), _("Unknown")); + else if (!strcmp (status, "OFFLINE")) + /* Translators: The printer is offline. */ + gtk_printer_set_state_message (GTK_PRINTER (printer), _("Offline")); + else if (!strcmp (status, "DORMANT")) + /* We shouldn't get here because the query omits dormant + * printers by default. */ + + /* Translators: Printer has been offline for a long time. */ + gtk_printer_set_state_message (GTK_PRINTER (printer), _("Dormant")); + } + + gtk_printer_set_is_active (GTK_PRINTER (printer), TRUE); + + gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), + GTK_PRINTER (printer)); + g_signal_emit_by_name (GTK_PRINT_BACKEND (backend), + "printer-added", GTK_PRINTER (printer)); + g_object_unref (printer); + } + + json_node_free (node); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: 'search' finished for account %p\n", + account)); + + done: + if (backend != NULL && --backend->accounts_searching == 0) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: 'search' finished for " + "all accounts\n")); + + gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); + } +} + +static void +cloudprint_get_managed_objects_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GtkPrintBackendCloudprint *backend; + GVariant *output; + GError *error = NULL; + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); + + if (output != NULL) + { + TGOAAccount *goa_account; + GList *accounts = NULL; + GList *iter; + guint searching; + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: got objects managed by goa\n")); + + backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); + + accounts = get_accounts (output); + g_variant_unref (output); + searching = backend->accounts_searching = g_list_length (accounts); + + for (iter = accounts; iter != NULL; iter = iter->next) + { + GtkCloudprintAccount *account; + goa_account = (TGOAAccount *) iter->data; + account = gtk_cloudprint_account_new (goa_account->id, + goa_account->path, + goa_account->presentation_identity); + if (account == NULL) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: error constructing " + "account object")); + backend->accounts_searching--; + searching--; + continue; + } + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: issuing 'search' for %p\n", + account)); + + gtk_cloudprint_account_search (account, + G_DBUS_CONNECTION (source), + backend->cancellable, + cloudprint_search_cb, + GTK_PRINT_BACKEND (backend)); + } + + if (searching == 0) + gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); + + g_list_free_full (accounts, t_goa_account_free); + } + else + { + if (error->domain != G_IO_ERROR || + error->code != G_IO_ERROR_CANCELLED) + { + if (error->domain != G_DBUS_ERROR || + (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN && + error->code != G_DBUS_ERROR_UNKNOWN_METHOD)) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: failed to get objects managed by goa: %s\n", + error->message)); + g_warning ("%s", error->message); + } + + gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data)); + } + + g_error_free (error); + } + + g_object_unref (source); +} + +static void +cloudprint_bus_get_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GtkPrintBackendCloudprint *backend; + GDBusConnection *connection; + GError *error = NULL; + + connection = g_bus_get_finish (res, &error); + + if (connection != NULL) + { + backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: got connection to session bus\n")); + + g_dbus_connection_call (connection, + ONLINE_ACCOUNTS_BUS, + ONLINE_ACCOUNTS_PATH, + OBJECT_MANAGER_IFACE, + "GetManagedObjects", + NULL, + G_VARIANT_TYPE ("(a{oa{sa{sv}}})"), + G_DBUS_CALL_FLAGS_NONE, + -1, + backend->cancellable, + cloudprint_get_managed_objects_cb, + backend); + } + else + { + if (error->domain != G_IO_ERROR || + error->code != G_IO_ERROR_CANCELLED) + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: failed getting session bus: %s\n", + error->message)); + g_warning ("%s", error->message); + + gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data)); + } + g_error_free (error); + } +} + +static void +cloudprint_request_printer_list (GtkPrintBackend *print_backend) +{ + GtkPrintBackendCloudprint *backend = GTK_PRINT_BACKEND_CLOUDPRINT (print_backend); + + g_cancellable_reset (backend->cancellable); + g_bus_get (G_BUS_TYPE_SESSION, backend->cancellable, cloudprint_bus_get_cb, backend); +} + +static GtkPrinterOptionSet * +cloudprint_printer_get_options (GtkPrinter *printer, + GtkPrintSettings *settings, + GtkPageSetup *page_setup, + GtkPrintCapabilities capabilities) +{ + GtkPrinterOptionSet *set; + GtkPrinterOption *option; + const gchar *n_up[] = { "1" }; + + set = gtk_printer_option_set_new (); + + /* How many document pages to go onto one side of paper. */ + option = gtk_printer_option_new ("gtk-n-up", _("Pages per _sheet:"), GTK_PRINTER_OPTION_TYPE_PICKONE); + gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), + (char **) n_up, (char **) n_up /* FIXME i18n (localised digits)! */); + gtk_printer_option_set (option, "1"); + gtk_printer_option_set_add (set, option); + g_object_unref (option); + + return set; +} + +static void +cloudprint_printer_get_settings_from_options (GtkPrinter *printer, + GtkPrinterOptionSet *options, + GtkPrintSettings *settings) +{ +} + +static void +cloudprint_printer_prepare_for_print (GtkPrinter *printer, + GtkPrintJob *print_job, + GtkPrintSettings *settings, + GtkPageSetup *page_setup) +{ + gdouble scale; + + gtk_print_job_set_pages (print_job, gtk_print_settings_get_print_pages (settings)); + gtk_print_job_set_page_ranges (print_job, NULL, 0); + + if (gtk_print_job_get_pages (print_job) == GTK_PRINT_PAGES_RANGES) + { + GtkPageRange *page_ranges; + gint num_page_ranges; + page_ranges = gtk_print_settings_get_page_ranges (settings, &num_page_ranges); + gtk_print_job_set_page_ranges (print_job, page_ranges, num_page_ranges); + } + + gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings)); + gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings)); + gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings)); + + scale = gtk_print_settings_get_scale (settings); + if (scale != 100.0) + gtk_print_job_set_scale (print_job, scale/100.0); + + gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings)); + gtk_print_job_set_rotate (print_job, TRUE); +} + +static void +cloudprint_printer_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); + GtkPrinter *printer = GTK_PRINTER (user_data); + JsonObject *result; + GError *error = NULL; + gboolean success = FALSE; + + result = gtk_cloudprint_account_printer_finish (account, res, &error); + if (result != NULL) + { + /* Ignore capabilities for now. */ + json_object_unref (result); + success = TRUE; + } + else + { + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: failure getting details: %s\n", + error->message)); + + if (error->domain == G_IO_ERROR && + error->code == G_IO_ERROR_CANCELLED) + { + g_error_free (error); + return; + } + + g_error_free (error); + } + + gtk_printer_set_has_details (printer, success); + g_signal_emit_by_name (printer, "details-acquired", success); +} + +static void +cloudprint_printer_request_details (GtkPrinter *printer) +{ + GtkPrintBackendCloudprint *backend; + GtkCloudprintAccount *account = NULL; + gchar *printerid = NULL; + + g_object_get (printer, + "cloudprint-account", &account, + "printer-id", &printerid, + NULL); + + g_warn_if_fail (account != NULL); + g_warn_if_fail (printerid != NULL); + + backend = GTK_PRINT_BACKEND_CLOUDPRINT (gtk_printer_get_backend (printer)); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: Getting details for printer id %s\n", + printerid)); + + gtk_cloudprint_account_printer (account, + printerid, + backend->cancellable, + cloudprint_printer_cb, + printer); + g_object_unref (account); + g_free (printerid); +} diff --git a/modules/printbackends/gtkprintbackendcloudprint.h b/modules/printbackends/gtkprintbackendcloudprint.h new file mode 100644 index 0000000000..044ad4b32b --- /dev/null +++ b/modules/printbackends/gtkprintbackendcloudprint.h @@ -0,0 +1,40 @@ +/* gtkprintbackendcloudprint.h: Google Cloud Print implementation of + * GtkPrintBackend + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_PRINT_BACKEND_CLOUDPRINT_H__ +#define __GTK_PRINT_BACKEND_CLOUDPRINT_H__ + +#include +#include "gtkprintbackend.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_PRINT_BACKEND_CLOUDPRINT (gtk_print_backend_cloudprint_get_type ()) +#define GTK_PRINT_BACKEND_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprint)) +#define GTK_IS_PRINT_BACKEND_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT)) + +#define ONLINE_ACCOUNTS_BUS "org.gnome.OnlineAccounts" + +typedef struct _GtkPrintBackendCloudprint GtkPrintBackendCloudprint; + +GtkPrintBackend *gtk_print_backend_cloudprint_new (void); +GType gtk_print_backend_cloudprint_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GTK_PRINT_BACKEND_CLOUDPRINT_H__ */ diff --git a/modules/printbackends/gtkprintbackendcups.c b/modules/printbackends/gtkprintbackendcups.c new file mode 100644 index 0000000000..09e18b170e --- /dev/null +++ b/modules/printbackends/gtkprintbackendcups.c @@ -0,0 +1,6745 @@ +/* GTK - The GIMP Toolkit + * gtkprintbackendcups.h: Default implementation of GtkPrintBackend + * for the Common Unix Print System (CUPS) + * Copyright (C) 2006, 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +/* 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "gtkprintbackendcups.h" +#include "gtkprintercups.h" + +#include "gtkcupsutils.h" +#include "gtkcupssecretsutils.h" + +#include + +#ifdef HAVE_COLORD +#include +#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 \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 \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 \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 \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 - 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; +} diff --git a/modules/printbackends/gtkprintbackendcups.h b/modules/printbackends/gtkprintbackendcups.h new file mode 100644 index 0000000000..d4e35ddf23 --- /dev/null +++ b/modules/printbackends/gtkprintbackendcups.h @@ -0,0 +1,40 @@ +/* GTK - The GIMP Toolkit + * gtkprintbackendcups.h: Default implementation of GtkPrintBackend for the Common Unix Print System (CUPS) + * Copyright (C) 2006, 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_PRINT_BACKEND_CUPS_H__ +#define __GTK_PRINT_BACKEND_CUPS_H__ + +#include +#include "gtkprintbackend.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_PRINT_BACKEND_CUPS (gtk_print_backend_cups_get_type ()) +#define GTK_PRINT_BACKEND_CUPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCups)) +#define GTK_IS_PRINT_BACKEND_CUPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CUPS)) + +typedef struct _GtkPrintBackendCups GtkPrintBackendCups; + +GtkPrintBackend *gtk_print_backend_cups_new (void); +GType gtk_print_backend_cups_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GTK_PRINT_BACKEND_CUPS_H__ */ + + diff --git a/modules/printbackends/gtkprintbackendfile.c b/modules/printbackends/gtkprintbackendfile.c new file mode 100644 index 0000000000..b568e01395 --- /dev/null +++ b/modules/printbackends/gtkprintbackendfile.c @@ -0,0 +1,846 @@ +/* GTK - The GIMP Toolkit + * gtkprintbackendfile.c: Default implementation of GtkPrintBackend + * for printing to a file + * Copyright (C) 2003, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "gtk/gtk.h" +#include "gtk/gtkprinter-private.h" + +#include "gtkprintbackendfile.h" + +typedef struct _GtkPrintBackendFileClass GtkPrintBackendFileClass; + +#define GTK_PRINT_BACKEND_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass)) +#define GTK_IS_PRINT_BACKEND_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_FILE)) +#define GTK_PRINT_BACKEND_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass)) + +#define _STREAM_MAX_CHUNK_SIZE 8192 + +struct _GtkPrintBackendFileClass +{ + GtkPrintBackendClass parent_class; +}; + +struct _GtkPrintBackendFile +{ + GtkPrintBackend parent_instance; +}; + +typedef enum +{ + FORMAT_PDF, + FORMAT_PS, + FORMAT_SVG, + N_FORMATS +} OutputFormat; + +static const gchar* formats[N_FORMATS] = +{ + "pdf", + "ps", + "svg" +}; + +static GObjectClass *backend_parent_class; + +static void gtk_print_backend_file_class_init (GtkPrintBackendFileClass *class); +static void gtk_print_backend_file_init (GtkPrintBackendFile *impl); +static void file_printer_get_settings_from_options (GtkPrinter *printer, + GtkPrinterOptionSet *options, + GtkPrintSettings *settings); +static GtkPrinterOptionSet *file_printer_get_options (GtkPrinter *printer, + GtkPrintSettings *settings, + GtkPageSetup *page_setup, + GtkPrintCapabilities capabilities); +static void file_printer_prepare_for_print (GtkPrinter *printer, + GtkPrintJob *print_job, + GtkPrintSettings *settings, + GtkPageSetup *page_setup); +static void gtk_print_backend_file_print_stream (GtkPrintBackend *print_backend, + GtkPrintJob *job, + GIOChannel *data_io, + GtkPrintJobCompleteFunc callback, + gpointer user_data, + GDestroyNotify dnotify); +static cairo_surface_t * file_printer_create_cairo_surface (GtkPrinter *printer, + GtkPrintSettings *settings, + gdouble width, + gdouble height, + GIOChannel *cache_io); + +static GList * file_printer_list_papers (GtkPrinter *printer); +static GtkPageSetup * file_printer_get_default_page_size (GtkPrinter *printer); + +G_DEFINE_DYNAMIC_TYPE(GtkPrintBackendFile, gtk_print_backend_file, GTK_TYPE_PRINT_BACKEND) + +void +g_io_module_load (GIOModule *module) +{ + g_type_module_use (G_TYPE_MODULE (module)); + + gtk_print_backend_file_register_type (G_TYPE_MODULE (module)); + + g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, + GTK_TYPE_PRINT_BACKEND_FILE, + "file", + 10); +} + +void +g_io_module_unload (GIOModule *module) +{ +} + +char ** +g_io_module_query (void) +{ + char *eps[] = { + GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, + NULL + }; + + return g_strdupv (eps); +} + +/** + * gtk_print_backend_file_new: + * + * Creates a new #GtkPrintBackendFile object. #GtkPrintBackendFile + * implements the #GtkPrintBackend interface with direct access to + * the filesystem using Unix/Linux API calls + * + * Returns: the new #GtkPrintBackendFile object + **/ +GtkPrintBackend * +gtk_print_backend_file_new (void) +{ + return g_object_new (GTK_TYPE_PRINT_BACKEND_FILE, NULL); +} + +static void +gtk_print_backend_file_class_init (GtkPrintBackendFileClass *class) +{ + GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class); + + backend_parent_class = g_type_class_peek_parent (class); + + backend_class->print_stream = gtk_print_backend_file_print_stream; + backend_class->printer_create_cairo_surface = file_printer_create_cairo_surface; + backend_class->printer_get_options = file_printer_get_options; + backend_class->printer_get_settings_from_options = file_printer_get_settings_from_options; + backend_class->printer_prepare_for_print = file_printer_prepare_for_print; + backend_class->printer_list_papers = file_printer_list_papers; + backend_class->printer_get_default_page_size = file_printer_get_default_page_size; +} + +static void +gtk_print_backend_file_class_finalize (GtkPrintBackendFileClass *class) +{ +} + +/* return N_FORMATS if no explicit format in the settings */ +static OutputFormat +format_from_settings (GtkPrintSettings *settings) +{ + const gchar *value; + gint i; + + if (settings == NULL) + return N_FORMATS; + + value = gtk_print_settings_get (settings, + GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT); + if (value == NULL) + return N_FORMATS; + + for (i = 0; i < N_FORMATS; ++i) + if (strcmp (value, formats[i]) == 0) + break; + + g_assert (i < N_FORMATS); + + return (OutputFormat) i; +} + +static gchar * +output_file_from_settings (GtkPrintSettings *settings, + const gchar *default_format) +{ + gchar *uri = NULL; + + if (settings) + uri = g_strdup (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI)); + + if (uri == NULL) + { + const gchar *extension, *basename = NULL, *output_dir = NULL; + gchar *name, *locale_name, *path; + + if (default_format) + extension = default_format; + else + { + OutputFormat format; + + format = format_from_settings (settings); + switch (format) + { + default: + case FORMAT_PDF: + extension = "pdf"; + break; + case FORMAT_PS: + extension = "ps"; + break; + case FORMAT_SVG: + extension = "svg"; + break; + } + } + + if (settings) + basename = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME); + if (basename == NULL) + basename = _("output"); + + name = g_strconcat (basename, ".", extension, NULL); + + locale_name = g_filename_from_utf8 (name, -1, NULL, NULL, NULL); + g_free (name); + + if (locale_name != NULL) + { + if (settings) + output_dir = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_DIR); + if (output_dir == NULL) + { + const gchar *document_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS); + + if (document_dir == NULL) + { + gchar *current_dir = g_get_current_dir (); + path = g_build_filename (current_dir, locale_name, NULL); + g_free (current_dir); + } + else + path = g_build_filename (document_dir, locale_name, NULL); + + uri = g_filename_to_uri (path, NULL, NULL); + } + else + { + path = g_build_filename (output_dir, locale_name, NULL); + uri = g_filename_to_uri (path, NULL, NULL); + } + + g_free (path); + g_free (locale_name); + } + } + + return uri; +} + +static cairo_status_t +_cairo_write (void *closure, + const unsigned char *data, + unsigned int length) +{ + GIOChannel *io = (GIOChannel *)closure; + gsize written = 0; + GError *error; + + error = NULL; + + GTK_NOTE (PRINTING, + g_print ("FILE Backend: Writting %u byte chunk to temp file\n", length)); + + while (length > 0) + { + GIOStatus status; + + status = g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error); + + if (status == G_IO_STATUS_ERROR) + { + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("FILE Backend: Error writting to temp file, %s\n", error->message)); + + g_error_free (error); + } + + return CAIRO_STATUS_WRITE_ERROR; + } + + GTK_NOTE (PRINTING, + g_print ("FILE Backend: Wrote %zd bytes to temp file\n", written)); + + data += written; + length -= written; + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_surface_t * +file_printer_create_cairo_surface (GtkPrinter *printer, + GtkPrintSettings *settings, + gdouble width, + gdouble height, + GIOChannel *cache_io) +{ + cairo_surface_t *surface; + OutputFormat format; + const cairo_svg_version_t *versions; + int num_versions = 0; + + format = format_from_settings (settings); + + switch (format) + { + default: + case FORMAT_PDF: + surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height); + break; + case FORMAT_PS: + surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height); + break; + case FORMAT_SVG: + surface = cairo_svg_surface_create_for_stream (_cairo_write, cache_io, width, height); + cairo_svg_get_versions (&versions, &num_versions); + if (num_versions > 0) + cairo_svg_surface_restrict_to_version (surface, versions[num_versions - 1]); + break; + } + + cairo_surface_set_fallback_resolution (surface, + 2.0 * gtk_print_settings_get_printer_lpi (settings), + 2.0 * gtk_print_settings_get_printer_lpi (settings)); + + return surface; +} + +typedef struct { + GtkPrintBackend *backend; + GtkPrintJobCompleteFunc callback; + GtkPrintJob *job; + GFileOutputStream *target_io_stream; + gpointer user_data; + GDestroyNotify dnotify; +} _PrintStreamData; + +static void +file_print_cb (GtkPrintBackendFile *print_backend, + GError *error, + gpointer user_data) +{ + gchar *uri; + + _PrintStreamData *ps = (_PrintStreamData *) user_data; + GtkRecentManager *recent_manager; + + if (ps->target_io_stream != NULL) + (void)g_output_stream_close (G_OUTPUT_STREAM (ps->target_io_stream), NULL, NULL); + + if (ps->callback) + ps->callback (ps->job, ps->user_data, error); + + if (ps->dnotify) + ps->dnotify (ps->user_data); + + gtk_print_job_set_status (ps->job, + (error != NULL) + ? GTK_PRINT_STATUS_FINISHED_ABORTED + : GTK_PRINT_STATUS_FINISHED); + + recent_manager = gtk_recent_manager_get_default (); + uri = output_file_from_settings (gtk_print_job_get_settings (ps->job), NULL); + gtk_recent_manager_add_item (recent_manager, uri); + g_free (uri); + + if (ps->job) + g_object_unref (ps->job); + + g_free (ps); +} + +static gboolean +file_write (GIOChannel *source, + GIOCondition con, + gpointer user_data) +{ + gchar buf[_STREAM_MAX_CHUNK_SIZE]; + gsize bytes_read; + GError *error; + GIOStatus read_status; + _PrintStreamData *ps = (_PrintStreamData *) user_data; + + error = NULL; + + read_status = + g_io_channel_read_chars (source, + buf, + _STREAM_MAX_CHUNK_SIZE, + &bytes_read, + &error); + + if (read_status != G_IO_STATUS_ERROR) + { + gsize bytes_written; + + g_output_stream_write_all (G_OUTPUT_STREAM (ps->target_io_stream), + buf, + bytes_read, + &bytes_written, + NULL, + &error); + } + + if (error != NULL || read_status == G_IO_STATUS_EOF) + { + file_print_cb (GTK_PRINT_BACKEND_FILE (ps->backend), error, user_data); + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("FILE Backend: %s\n", error->message)); + + g_error_free (error); + } + + return FALSE; + } + + GTK_NOTE (PRINTING, + g_print ("FILE Backend: Writting %lu byte chunk to target file\n", bytes_read)); + + return TRUE; +} + +static void +gtk_print_backend_file_print_stream (GtkPrintBackend *print_backend, + GtkPrintJob *job, + GIOChannel *data_io, + GtkPrintJobCompleteFunc callback, + gpointer user_data, + GDestroyNotify dnotify) +{ + GError *internal_error = NULL; + _PrintStreamData *ps; + GtkPrintSettings *settings; + gchar *uri; + GFile *file = NULL; + + settings = gtk_print_job_get_settings (job); + + ps = g_new0 (_PrintStreamData, 1); + ps->callback = callback; + ps->user_data = user_data; + ps->dnotify = dnotify; + ps->job = g_object_ref (job); + ps->backend = print_backend; + + internal_error = NULL; + uri = output_file_from_settings (settings, NULL); + + if (uri == NULL) + goto error; + + file = g_file_new_for_uri (uri); + ps->target_io_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &internal_error); + + g_object_unref (file); + g_free (uri); + +error: + if (internal_error != NULL) + { + file_print_cb (GTK_PRINT_BACKEND_FILE (print_backend), + internal_error, ps); + + g_error_free (internal_error); + return; + } + + g_io_add_watch (data_io, + G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, + (GIOFunc) file_write, + ps); +} + +static void +gtk_print_backend_file_init (GtkPrintBackendFile *backend) +{ + GtkPrinter *printer; + + printer = g_object_new (GTK_TYPE_PRINTER, + "name", _("Print to File"), + "backend", backend, + "is-virtual", TRUE, + "accepts-pdf", TRUE, + NULL); + + gtk_printer_set_has_details (printer, TRUE); + gtk_printer_set_icon_name (printer, "document-save"); + gtk_printer_set_is_active (printer, TRUE); + + gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer); + g_object_unref (printer); + + gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); +} + +typedef struct { + GtkPrinter *printer; + GtkPrinterOptionSet *set; +} _OutputFormatChangedData; + +static void +set_printer_format_from_option_set (GtkPrinter *printer, + GtkPrinterOptionSet *set) +{ + GtkPrinterOption *format_option; + const gchar *value; + gint i; + + format_option = gtk_printer_option_set_lookup (set, "output-file-format"); + if (format_option && format_option->value) + { + value = format_option->value; + if (value) + { + for (i = 0; i < N_FORMATS; ++i) + if (strcmp (value, formats[i]) == 0) + break; + + g_assert (i < N_FORMATS); + + switch (i) + { + case FORMAT_PDF: + gtk_printer_set_accepts_pdf (printer, TRUE); + gtk_printer_set_accepts_ps (printer, FALSE); + break; + case FORMAT_PS: + gtk_printer_set_accepts_pdf (printer, FALSE); + gtk_printer_set_accepts_ps (printer, TRUE); + break; + case FORMAT_SVG: + default: + gtk_printer_set_accepts_pdf (printer, FALSE); + gtk_printer_set_accepts_ps (printer, FALSE); + break; + } + } + } +} + +static void +file_printer_output_file_format_changed (GtkPrinterOption *format_option, + gpointer user_data) +{ + GtkPrinterOption *uri_option; + gchar *base = NULL; + _OutputFormatChangedData *data = (_OutputFormatChangedData *) user_data; + + if (! format_option->value) + return; + + uri_option = gtk_printer_option_set_lookup (data->set, + "gtk-main-page-custom-input"); + + if (uri_option && uri_option->value) + { + const gchar *uri = uri_option->value; + const gchar *dot = strrchr (uri, '.'); + + if (dot) + { + gint i; + + /* check if the file extension matches one of the known ones */ + for (i = 0; i < N_FORMATS; i++) + if (strcmp (dot + 1, formats[i]) == 0) + break; + + if (i < N_FORMATS && strcmp (formats[i], format_option->value)) + { + /* the file extension is known but doesn't match the + * selected one, strip it away + */ + base = g_strndup (uri, dot - uri); + } + } + else + { + /* there's no file extension */ + base = g_strdup (uri); + } + } + + if (base) + { + gchar *tmp = g_strdup_printf ("%s.%s", base, format_option->value); + + gtk_printer_option_set (uri_option, tmp); + g_free (tmp); + g_free (base); + } + + set_printer_format_from_option_set (data->printer, data->set); +} + +static GtkPrinterOptionSet * +file_printer_get_options (GtkPrinter *printer, + GtkPrintSettings *settings, + GtkPageSetup *page_setup, + GtkPrintCapabilities capabilities) +{ + GtkPrinterOptionSet *set; + GtkPrinterOption *option; + const gchar *n_up[] = {"1", "2", "4", "6", "9", "16" }; + const gchar *pages_per_sheet = NULL; + const gchar *format_names[N_FORMATS] = { N_("PDF"), N_("PostScript"), N_("SVG") }; + const gchar *supported_formats[N_FORMATS]; + gchar *display_format_names[N_FORMATS]; + gint n_formats = 0; + OutputFormat format; + gchar *uri; + gint current_format = 0; + _OutputFormatChangedData *format_changed_data; + + format = format_from_settings (settings); + + set = gtk_printer_option_set_new (); + + option = gtk_printer_option_new ("gtk-n-up", _("Pages per _sheet:"), GTK_PRINTER_OPTION_TYPE_PICKONE); + gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), + (char **) n_up, (char **) n_up /* FIXME i18n (localised digits)! */); + if (settings) + pages_per_sheet = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_NUMBER_UP); + if (pages_per_sheet) + gtk_printer_option_set (option, pages_per_sheet); + else + gtk_printer_option_set (option, "1"); + gtk_printer_option_set_add (set, option); + g_object_unref (option); + + if (capabilities & (GTK_PRINT_CAPABILITY_GENERATE_PDF | GTK_PRINT_CAPABILITY_GENERATE_PS)) + { + if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PDF) + { + if (format == FORMAT_PDF || format == N_FORMATS) + { + format = FORMAT_PDF; + current_format = n_formats; + } + supported_formats[n_formats] = formats[FORMAT_PDF]; + display_format_names[n_formats] = _(format_names[FORMAT_PDF]); + n_formats++; + } + if (capabilities & GTK_PRINT_CAPABILITY_GENERATE_PS) + { + if (format == FORMAT_PS || format == N_FORMATS) + current_format = n_formats; + supported_formats[n_formats] = formats[FORMAT_PS]; + display_format_names[n_formats] = _(format_names[FORMAT_PS]); + n_formats++; + } + } + else + { + switch (format) + { + default: + case FORMAT_PDF: + current_format = FORMAT_PDF; + break; + case FORMAT_PS: + current_format = FORMAT_PS; + break; + case FORMAT_SVG: + current_format = FORMAT_SVG; + break; + } + + for (n_formats = 0; n_formats < N_FORMATS; ++n_formats) + { + supported_formats[n_formats] = formats[n_formats]; + display_format_names[n_formats] = _(format_names[n_formats]); + } + } + + uri = output_file_from_settings (settings, supported_formats[current_format]); + + option = gtk_printer_option_new ("gtk-main-page-custom-input", _("File"), + GTK_PRINTER_OPTION_TYPE_FILESAVE); + gtk_printer_option_set_activates_default (option, TRUE); + gtk_printer_option_set (option, uri); + g_free (uri); + option->group = g_strdup ("GtkPrintDialogExtension"); + gtk_printer_option_set_add (set, option); + + if (n_formats > 1) + { + option = gtk_printer_option_new ("output-file-format", _("_Output format"), + GTK_PRINTER_OPTION_TYPE_ALTERNATIVE); + option->group = g_strdup ("GtkPrintDialogExtension"); + + gtk_printer_option_choices_from_array (option, n_formats, + (char **) supported_formats, + display_format_names); + gtk_printer_option_set (option, supported_formats[current_format]); + gtk_printer_option_set_add (set, option); + + set_printer_format_from_option_set (printer, set); + format_changed_data = g_new (_OutputFormatChangedData, 1); + format_changed_data->printer = printer; + format_changed_data->set = set; + g_signal_connect_data (option, "changed", + G_CALLBACK (file_printer_output_file_format_changed), + format_changed_data, (GClosureNotify)g_free, 0); + + g_object_unref (option); + } + + return set; +} + +static void +file_printer_get_settings_from_options (GtkPrinter *printer, + GtkPrinterOptionSet *options, + GtkPrintSettings *settings) +{ + GtkPrinterOption *option; + + option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input"); + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, option->value); + + option = gtk_printer_option_set_lookup (options, "output-file-format"); + if (option) + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT, option->value); + + option = gtk_printer_option_set_lookup (options, "gtk-n-up"); + if (option) + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value); + + option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout"); + if (option) + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value); +} + +static void +file_printer_prepare_for_print (GtkPrinter *printer, + GtkPrintJob *print_job, + GtkPrintSettings *settings, + GtkPageSetup *page_setup) +{ + gdouble scale; + GtkPrintPages pages; + GtkPageRange *ranges; + gint n_ranges; + OutputFormat format; + + pages = gtk_print_settings_get_print_pages (settings); + gtk_print_job_set_pages (print_job, pages); + + if (pages == GTK_PRINT_PAGES_RANGES) + ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges); + else + { + ranges = NULL; + n_ranges = 0; + } + + gtk_print_job_set_page_ranges (print_job, ranges, n_ranges); + gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings)); + gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings)); + gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings)); + gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings)); + gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings)); + + scale = gtk_print_settings_get_scale (settings); + if (scale != 100.0) + gtk_print_job_set_scale (print_job, scale / 100.0); + + gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings)); + + format = format_from_settings (settings); + switch (format) + { + case FORMAT_PDF: + gtk_print_job_set_rotate (print_job, FALSE); + break; + default: + case FORMAT_PS: + case FORMAT_SVG: + gtk_print_job_set_rotate (print_job, TRUE); + break; + } +} + +static GList * +file_printer_list_papers (GtkPrinter *printer) +{ + GList *result = NULL; + GList *papers, *p; + GtkPageSetup *page_setup; + + papers = gtk_paper_size_get_paper_sizes (FALSE); + + for (p = papers; p; p = p->next) + { + GtkPaperSize *paper_size = p->data; + + page_setup = gtk_page_setup_new (); + gtk_page_setup_set_paper_size (page_setup, paper_size); + gtk_paper_size_free (paper_size); + result = g_list_prepend (result, page_setup); + } + + g_list_free (papers); + + return g_list_reverse (result); +} + +static GtkPageSetup * +file_printer_get_default_page_size (GtkPrinter *printer) +{ + GtkPageSetup *result = NULL; + + return result; +} diff --git a/modules/printbackends/gtkprintbackendfile.h b/modules/printbackends/gtkprintbackendfile.h new file mode 100644 index 0000000000..b4ae41a15c --- /dev/null +++ b/modules/printbackends/gtkprintbackendfile.h @@ -0,0 +1,39 @@ +/* GTK - The GIMP Toolkit + * gtkprintbackendpdf.h: Default implementation of GtkPrintBackend + * for printing to a file + * Copyright (C) 2003, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_PRINT_BACKEND_FILE_H__ +#define __GTK_PRINT_BACKEND_FILE_H__ + +#include +#include "gtkprintbackend.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_PRINT_BACKEND_FILE (gtk_print_backend_file_get_type ()) +#define GTK_PRINT_BACKEND_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFile)) +#define GTK_IS_PRINT_BACKEND_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_FILE)) + +typedef struct _GtkPrintBackendFile GtkPrintBackendFile; + +GtkPrintBackend *gtk_print_backend_file_new (void); +GType gtk_print_backend_file_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GTK_PRINT_BACKEND_FILE_H__ */ diff --git a/modules/printbackends/gtkprintbackendlpr.c b/modules/printbackends/gtkprintbackendlpr.c new file mode 100644 index 0000000000..aefa921515 --- /dev/null +++ b/modules/printbackends/gtkprintbackendlpr.c @@ -0,0 +1,479 @@ +/* GTK - The GIMP Toolkit + * gtkprintbackendlpr.c: LPR implementation of GtkPrintBackend + * for printing to lpr + * Copyright (C) 2006, 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include "gtkprinter-private.h" + +#include "gtkprintbackendlpr.h" + +typedef struct _GtkPrintBackendLprClass GtkPrintBackendLprClass; + +#define GTK_PRINT_BACKEND_LPR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass)) +#define GTK_IS_PRINT_BACKEND_LPR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_LPR)) +#define GTK_PRINT_BACKEND_LPR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass)) + +#define _LPR_MAX_CHUNK_SIZE 8192 + +struct _GtkPrintBackendLprClass +{ + GtkPrintBackendClass parent_class; +}; + +struct _GtkPrintBackendLpr +{ + GtkPrintBackend parent_instance; +}; + +static GObjectClass *backend_parent_class; + +static void gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class); +static void gtk_print_backend_lpr_init (GtkPrintBackendLpr *impl); +static void lpr_printer_get_settings_from_options (GtkPrinter *printer, + GtkPrinterOptionSet *options, + GtkPrintSettings *settings); +static GtkPrinterOptionSet *lpr_printer_get_options (GtkPrinter *printer, + GtkPrintSettings *settings, + GtkPageSetup *page_setup, + GtkPrintCapabilities capabilities); +static void lpr_printer_prepare_for_print (GtkPrinter *printer, + GtkPrintJob *print_job, + GtkPrintSettings *settings, + GtkPageSetup *page_setup); +static cairo_surface_t * lpr_printer_create_cairo_surface (GtkPrinter *printer, + GtkPrintSettings *settings, + gdouble width, + gdouble height, + GIOChannel *cache_io); +static void gtk_print_backend_lpr_print_stream (GtkPrintBackend *print_backend, + GtkPrintJob *job, + GIOChannel *data_io, + GtkPrintJobCompleteFunc callback, + gpointer user_data, + GDestroyNotify dnotify); + +G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendLpr, gtk_print_backend_lpr, GTK_TYPE_PRINT_BACKEND) + +void +g_io_module_load (GIOModule *module) +{ + g_type_module_use (G_TYPE_MODULE (module)); + + gtk_print_backend_lpr_register_type (G_TYPE_MODULE (module)); + + g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, + GTK_TYPE_PRINT_BACKEND_LPR, + "lpr", + 10); +} + +void +g_io_module_unload (GIOModule *module) +{ +} + +char ** +g_io_module_query (void) +{ + char *eps[] = { + GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, + NULL + }; + + return g_strdupv (eps); +} + +/** + * gtk_print_backend_lpr_new: + * + * Creates a new #GtkPrintBackendLpr object. #GtkPrintBackendLpr + * implements the #GtkPrintBackend interface with direct access to + * the filesystem using Unix/Linux API calls + * + * Returns: the new #GtkPrintBackendLpr object + **/ +GtkPrintBackend * +gtk_print_backend_lpr_new (void) +{ + return g_object_new (GTK_TYPE_PRINT_BACKEND_LPR, NULL); +} + +static void +gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class) +{ + GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class); + + backend_parent_class = g_type_class_peek_parent (class); + + backend_class->print_stream = gtk_print_backend_lpr_print_stream; + backend_class->printer_create_cairo_surface = lpr_printer_create_cairo_surface; + backend_class->printer_get_options = lpr_printer_get_options; + backend_class->printer_get_settings_from_options = lpr_printer_get_settings_from_options; + backend_class->printer_prepare_for_print = lpr_printer_prepare_for_print; +} + +static void +gtk_print_backend_lpr_class_finalize (GtkPrintBackendLprClass *class) +{ +} + +static cairo_status_t +_cairo_write (void *closure, + const unsigned char *data, + unsigned int length) +{ + GIOChannel *io = (GIOChannel *)closure; + gsize written; + GError *error; + + error = NULL; + + GTK_NOTE (PRINTING, + g_print ("LPR Backend: Writting %i byte chunk to temp file\n", length)); + + while (length > 0) + { + g_io_channel_write_chars (io, (const gchar*)data, length, &written, &error); + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("LPR Backend: Error writting to temp file, %s\n", error->message)); + + g_error_free (error); + return CAIRO_STATUS_WRITE_ERROR; + } + + GTK_NOTE (PRINTING, + g_print ("LPR Backend: Wrote %" G_GSIZE_FORMAT " bytes to temp file\n", written)); + + data += written; + length -= written; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +lpr_printer_create_cairo_surface (GtkPrinter *printer, + GtkPrintSettings *settings, + gdouble width, + gdouble height, + GIOChannel *cache_io) +{ + cairo_surface_t *surface; + + surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height); + + cairo_surface_set_fallback_resolution (surface, + 2.0 * gtk_print_settings_get_printer_lpi (settings), + 2.0 * gtk_print_settings_get_printer_lpi (settings)); + + return surface; +} + +typedef struct { + GtkPrintBackend *backend; + GtkPrintJobCompleteFunc callback; + GtkPrintJob *job; + gpointer user_data; + GDestroyNotify dnotify; + + GIOChannel *in; +} _PrintStreamData; + +static void +lpr_print_cb (GtkPrintBackendLpr *print_backend, + GError *error, + gpointer user_data) +{ + _PrintStreamData *ps = (_PrintStreamData *) user_data; + + if (ps->in != NULL) + g_io_channel_unref (ps->in); + + if (ps->callback) + ps->callback (ps->job, ps->user_data, error); + + if (ps->dnotify) + ps->dnotify (ps->user_data); + + gtk_print_job_set_status (ps->job, + error ? GTK_PRINT_STATUS_FINISHED_ABORTED + : GTK_PRINT_STATUS_FINISHED); + + if (ps->job) + g_object_unref (ps->job); + + g_free (ps); +} + +static gboolean +lpr_write (GIOChannel *source, + GIOCondition con, + gpointer user_data) +{ + gchar buf[_LPR_MAX_CHUNK_SIZE]; + gsize bytes_read; + GError *error; + GIOStatus status; + _PrintStreamData *ps = (_PrintStreamData *) user_data; + + error = NULL; + + status = + g_io_channel_read_chars (source, + buf, + _LPR_MAX_CHUNK_SIZE, + &bytes_read, + &error); + + if (status != G_IO_STATUS_ERROR) + { + gsize bytes_written; + + g_io_channel_write_chars (ps->in, + buf, + bytes_read, + &bytes_written, + &error); + } + + if (error != NULL || status == G_IO_STATUS_EOF) + { + lpr_print_cb (GTK_PRINT_BACKEND_LPR (ps->backend), + error, user_data); + + + if (error != NULL) + { + GTK_NOTE (PRINTING, + g_print ("LPR Backend: %s\n", error->message)); + + g_error_free (error); + } + + return FALSE; + } + + GTK_NOTE (PRINTING, + g_print ("LPR Backend: Writting %" G_GSIZE_FORMAT " byte chunk to lpr pipe\n", bytes_read)); + + + return TRUE; +} + +#define LPR_COMMAND "lpr" + +static void +gtk_print_backend_lpr_print_stream (GtkPrintBackend *print_backend, + GtkPrintJob *job, + GIOChannel *data_io, + GtkPrintJobCompleteFunc callback, + gpointer user_data, + GDestroyNotify dnotify) +{ + GError *print_error = NULL; + _PrintStreamData *ps; + GtkPrintSettings *settings; + gint argc; + gint in_fd; + gchar **argv = NULL; + const char *cmd_line; + + settings = gtk_print_job_get_settings (job); + + cmd_line = gtk_print_settings_get (settings, "lpr-commandline"); + if (cmd_line == NULL) + cmd_line = LPR_COMMAND; + + ps = g_new0 (_PrintStreamData, 1); + ps->callback = callback; + ps->user_data = user_data; + ps->dnotify = dnotify; + ps->job = g_object_ref (job); + ps->in = NULL; + + /* spawn lpr with pipes and pipe ps file to lpr */ + if (!g_shell_parse_argv (cmd_line, &argc, &argv, &print_error)) + goto out; + + if (!g_spawn_async_with_pipes (NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, + NULL, + NULL, + &in_fd, + NULL, + NULL, + &print_error)) + goto out; + + ps->in = g_io_channel_unix_new (in_fd); + + g_io_channel_set_encoding (ps->in, NULL, &print_error); + if (print_error != NULL) + { + if (ps->in != NULL) + g_io_channel_unref (ps->in); + + goto out; + } + + g_io_channel_set_close_on_unref (ps->in, TRUE); + + g_io_add_watch (data_io, + G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, + (GIOFunc) lpr_write, + ps); + + out: + if (argv != NULL) + g_strfreev (argv); + + if (print_error != NULL) + { + lpr_print_cb (GTK_PRINT_BACKEND_LPR (print_backend), + print_error, ps); + g_error_free (print_error); + } +} + +static void +gtk_print_backend_lpr_init (GtkPrintBackendLpr *backend) +{ + GtkPrinter *printer; + + printer = gtk_printer_new (_("Print to LPR"), + GTK_PRINT_BACKEND (backend), + TRUE); + gtk_printer_set_has_details (printer, TRUE); + gtk_printer_set_icon_name (printer, "printer"); + gtk_printer_set_is_active (printer, TRUE); + gtk_printer_set_is_default (printer, TRUE); + + gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer); + g_object_unref (printer); + gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); +} + +static GtkPrinterOptionSet * +lpr_printer_get_options (GtkPrinter *printer, + GtkPrintSettings *settings, + GtkPageSetup *page_setup, + GtkPrintCapabilities capabilities) +{ + GtkPrinterOptionSet *set; + GtkPrinterOption *option; + const char *command; + char *n_up[] = {"1", "2", "4", "6", "9", "16" }; + + set = gtk_printer_option_set_new (); + + option = gtk_printer_option_new ("gtk-n-up", _("Pages Per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE); + gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), + n_up, n_up); + gtk_printer_option_set (option, "1"); + gtk_printer_option_set_add (set, option); + g_object_unref (option); + + option = gtk_printer_option_new ("gtk-main-page-custom-input", _("Command Line"), GTK_PRINTER_OPTION_TYPE_STRING); + gtk_printer_option_set_activates_default (option, TRUE); + option->group = g_strdup ("GtkPrintDialogExtension"); + if (settings != NULL && + (command = gtk_print_settings_get (settings, "lpr-commandline"))!= NULL) + gtk_printer_option_set (option, command); + else + gtk_printer_option_set (option, LPR_COMMAND); + gtk_printer_option_set_add (set, option); + + return set; +} + +static void +lpr_printer_get_settings_from_options (GtkPrinter *printer, + GtkPrinterOptionSet *options, + GtkPrintSettings *settings) +{ + GtkPrinterOption *option; + + option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input"); + if (option) + gtk_print_settings_set (settings, "lpr-commandline", option->value); + + option = gtk_printer_option_set_lookup (options, "gtk-n-up"); + if (option) + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value); + + option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout"); + if (option) + gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value); +} + +static void +lpr_printer_prepare_for_print (GtkPrinter *printer, + GtkPrintJob *print_job, + GtkPrintSettings *settings, + GtkPageSetup *page_setup) +{ + double scale; + GtkPrintPages pages; + GtkPageRange *ranges; + gint n_ranges; + + pages = gtk_print_settings_get_print_pages (settings); + gtk_print_job_set_pages (print_job, pages); + + if (pages == GTK_PRINT_PAGES_RANGES) + ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges); + else + { + ranges = NULL; + n_ranges = 0; + } + + gtk_print_job_set_page_ranges (print_job, ranges, n_ranges); + gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings)); + gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings)); + gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings)); + gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings)); + gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings)); + + scale = gtk_print_settings_get_scale (settings); + if (scale != 100.0) + gtk_print_job_set_scale (print_job, scale / 100.0); + + gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings)); + gtk_print_job_set_rotate (print_job, TRUE); +} diff --git a/modules/printbackends/gtkprintbackendlpr.h b/modules/printbackends/gtkprintbackendlpr.h new file mode 100644 index 0000000000..0ba195bebd --- /dev/null +++ b/modules/printbackends/gtkprintbackendlpr.h @@ -0,0 +1,41 @@ +/* GTK - The GIMP Toolkit + * gtkprintbackendlpr.h: LPR implementation of GtkPrintBackend + * for printing to lpr + * Copyright (C) 2006, 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_PRINT_BACKEND_LPR_H__ +#define __GTK_PRINT_BACKEND_LPR_H__ + +#include +#include "gtkprintbackend.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_PRINT_BACKEND_LPR (gtk_print_backend_lpr_get_type ()) +#define GTK_PRINT_BACKEND_LPR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLpr)) +#define GTK_IS_PRINT_BACKEND_LPR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_LPR)) + +typedef struct _GtkPrintBackendLpr GtkPrintBackendLpr; + +GtkPrintBackend *gtk_print_backend_lpr_new (void); +GType gtk_print_backend_lpr_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GTK_PRINT_BACKEND_LPR_H__ */ + + diff --git a/modules/printbackends/gtkprintercloudprint.c b/modules/printbackends/gtkprintercloudprint.c new file mode 100644 index 0000000000..4f299fdcab --- /dev/null +++ b/modules/printbackends/gtkprintercloudprint.c @@ -0,0 +1,231 @@ +/* gtkprintercloudprint.c: Google Cloud Print -specific Printer class, + * GtkPrinterCloudprint + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "gtkprintercloudprint.h" +#include "gtkcloudprintaccount.h" + +typedef struct _GtkPrinterCloudprintClass GtkPrinterCloudprintClass; + +#define GTK_PRINTER_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprintClass)) +#define GTK_IS_PRINTER_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CLOUDPRINT)) +#define GTK_PRINTER_CLOUDPRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprintClass)) + +static GtkPrinterClass *gtk_printer_cloudprint_parent_class; +static GType printer_cloudprint_type = 0; + +struct _GtkPrinterCloudprintClass +{ + GtkPrinterClass parent_class; +}; + +struct _GtkPrinterCloudprint +{ + GtkPrinter parent_instance; + + GtkCloudprintAccount *account; + gchar *id; +}; + +enum { + PROP_0, + PROP_CLOUDPRINT_ACCOUNT, + PROP_PRINTER_ID +}; + +static void gtk_printer_cloudprint_class_init (GtkPrinterCloudprintClass *class); +static void gtk_printer_cloudprint_init (GtkPrinterCloudprint *impl); +static void gtk_printer_cloudprint_finalize (GObject *object); +static void gtk_printer_cloudprint_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_printer_cloudprint_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +void +gtk_printer_cloudprint_register_type (GTypeModule *module) +{ + const GTypeInfo printer_cloudprint_info = + { + sizeof (GtkPrinterCloudprintClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_printer_cloudprint_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkPrinterCloudprint), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_printer_cloudprint_init, + }; + + printer_cloudprint_type = g_type_module_register_type (module, + GTK_TYPE_PRINTER, + "GtkPrinterCloudprint", + &printer_cloudprint_info, 0); +} + +/* + * GtkPrinterCloudprint + */ +GType +gtk_printer_cloudprint_get_type (void) +{ + return printer_cloudprint_type; +} + +/** + * gtk_printer_cloudprint_new: + * + * Creates a new #GtkPrinterCloudprint object. #GtkPrinterCloudprint + * implements the #GtkPrinter interface and stores a reference to the + * #GtkCloudprintAccount object and the printer-id to use + * + * Returns: the new #GtkPrinterCloudprint object + **/ +GtkPrinterCloudprint * +gtk_printer_cloudprint_new (const char *name, + gboolean is_virtual, + GtkPrintBackend *backend, + GtkCloudprintAccount *account, + const gchar *id) +{ + return g_object_new (GTK_TYPE_PRINTER_CLOUDPRINT, + "name", name, + "backend", backend, + "is-virtual", is_virtual, + "accepts-pdf", TRUE, + "cloudprint-account", account, + "printer-id", id, + NULL); +} + +static void +gtk_printer_cloudprint_class_init (GtkPrinterCloudprintClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gtk_printer_cloudprint_parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = gtk_printer_cloudprint_finalize; + gobject_class->set_property = gtk_printer_cloudprint_set_property; + gobject_class->get_property = gtk_printer_cloudprint_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_CLOUDPRINT_ACCOUNT, + g_param_spec_object ("cloudprint-account", + P_("Cloud Print account"), + P_("GtkCloudprintAccount instance"), + GTK_TYPE_CLOUDPRINT_ACCOUNT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_PRINTER_ID, + g_param_spec_string ("printer-id", + P_("Printer ID"), + P_("Cloud Print printer ID"), + "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gtk_printer_cloudprint_init (GtkPrinterCloudprint *printer) +{ + printer->account = NULL; + printer->id = NULL; + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: +GtkPrinterCloudprint(%p)\n", + printer)); +} + +static void +gtk_printer_cloudprint_finalize (GObject *object) +{ + GtkPrinterCloudprint *printer; + + printer = GTK_PRINTER_CLOUDPRINT (object); + + GTK_NOTE (PRINTING, + g_print ("Cloud Print Backend: -GtkPrinterCloudprint(%p)\n", + printer)); + + if (printer->account != NULL) + g_object_unref (printer->account); + + g_free (printer->id); + + G_OBJECT_CLASS (gtk_printer_cloudprint_parent_class)->finalize (object); +} + +static void +gtk_printer_cloudprint_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkPrinterCloudprint *printer = GTK_PRINTER_CLOUDPRINT (object); + + switch (prop_id) + { + case PROP_CLOUDPRINT_ACCOUNT: + printer->account = g_value_dup_object (value); + break; + + case PROP_PRINTER_ID: + printer->id = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_printer_cloudprint_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkPrinterCloudprint *printer = GTK_PRINTER_CLOUDPRINT (object); + + switch (prop_id) + { + case PROP_CLOUDPRINT_ACCOUNT: + g_value_set_object (value, printer->account); + break; + + case PROP_PRINTER_ID: + g_value_set_string (value, printer->id); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} diff --git a/modules/printbackends/gtkprintercloudprint.h b/modules/printbackends/gtkprintercloudprint.h new file mode 100644 index 0000000000..564fb150be --- /dev/null +++ b/modules/printbackends/gtkprintercloudprint.h @@ -0,0 +1,43 @@ +/* gtkprintercloudprint.h: Google Cloud Print -specific Printer class + * GtkPrinterCloudprint + * Copyright (C) 2014, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_PRINTER_CLOUDPRINT_H__ +#define __GTK_PRINTER_CLOUDPRINT_H__ + +#include +#include + +#include "gtkcloudprintaccount.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_PRINTER_CLOUDPRINT (gtk_printer_cloudprint_get_type ()) +#define GTK_PRINTER_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CLOUDPRINT, GtkPrinterCloudprint)) +#define GTK_IS_PRINTER_CLOUDPRINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CLOUDPRINT)) + +void gtk_printer_cloudprint_register_type (GTypeModule *module); +GtkPrinterCloudprint *gtk_printer_cloudprint_new (const char *name, + gboolean is_virtual, + GtkPrintBackend *backend, + GtkCloudprintAccount *account, + const gchar *id); +GType gtk_printer_cloudprint_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GTK_PRINTER_CLOUDPRINT_H__ */ diff --git a/modules/printbackends/gtkprintercups.c b/modules/printbackends/gtkprintercups.c new file mode 100644 index 0000000000..525382aa8a --- /dev/null +++ b/modules/printbackends/gtkprintercups.c @@ -0,0 +1,650 @@ +/* GtkPrinterCupsCups + * Copyright (C) 2006 John (J5) Palmieri + * Copyright (C) 2011 Richard Hughes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include + +#ifdef HAVE_COLORD +#include +#endif + +#include "gtkintl.h" +#include "gtkprintercups.h" + +enum { + PROP_0, + PROP_PROFILE_TITLE +}; + +static void gtk_printer_cups_init (GtkPrinterCups *printer); +static void gtk_printer_cups_class_init (GtkPrinterCupsClass *class); +static void gtk_printer_cups_finalize (GObject *object); + +static GtkPrinterClass *gtk_printer_cups_parent_class; +static GType gtk_printer_cups_type = 0; + +static void gtk_printer_cups_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_printer_cups_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +void +gtk_printer_cups_register_type (GTypeModule *module) +{ + const GTypeInfo object_info = + { + sizeof (GtkPrinterCupsClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gtk_printer_cups_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkPrinterCups), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_printer_cups_init, + }; + + gtk_printer_cups_type = g_type_module_register_type (module, + GTK_TYPE_PRINTER, + "GtkPrinterCups", + &object_info, 0); +} + +GType +gtk_printer_cups_get_type (void) +{ + return gtk_printer_cups_type; +} + +static void +gtk_printer_cups_class_init (GtkPrinterCupsClass *class) +{ + GObjectClass *object_class = (GObjectClass *) class; + + object_class->finalize = gtk_printer_cups_finalize; + object_class->set_property = gtk_printer_cups_set_property; + object_class->get_property = gtk_printer_cups_get_property; + + gtk_printer_cups_parent_class = g_type_class_peek_parent (class); + + g_object_class_install_property (G_OBJECT_CLASS (class), + PROP_PROFILE_TITLE, + g_param_spec_string ("profile-title", + P_("Color Profile Title"), + P_("The title of the color profile to use"), + "", + G_PARAM_READABLE)); +} + +static void +gtk_printer_cups_init (GtkPrinterCups *printer) +{ + printer->device_uri = NULL; + printer->original_device_uri = NULL; + printer->printer_uri = NULL; + printer->state = 0; + printer->hostname = NULL; + printer->port = 0; + printer->ppd_name = NULL; + printer->ppd_file = NULL; + printer->default_cover_before = NULL; + printer->default_cover_after = NULL; + printer->remote = FALSE; + printer->get_remote_ppd_poll = 0; + printer->get_remote_ppd_attempts = 0; + printer->remote_cups_connection_test = NULL; + printer->auth_info_required = NULL; + printer->default_number_up = 1; +#ifdef HAVE_CUPS_API_1_6 + printer->avahi_browsed = FALSE; + printer->avahi_name = NULL; + printer->avahi_type = NULL; + printer->avahi_domain = NULL; +#endif + printer->ipp_version_major = 1; + printer->ipp_version_minor = 1; + printer->supports_copies = FALSE; + printer->supports_collate = FALSE; + printer->supports_number_up = FALSE; + printer->media_default = NULL; + printer->media_supported = NULL; + printer->media_size_supported = NULL; + printer->media_bottom_margin_default = 0; + printer->media_top_margin_default = 0; + printer->media_left_margin_default = 0; + printer->media_right_margin_default = 0; + printer->media_margin_default_set = FALSE; + printer->sides_default = NULL; + printer->sides_supported = NULL; + printer->number_of_covers = 0; + printer->covers = NULL; + printer->output_bin_default = NULL; + printer->output_bin_supported = NULL; +} + +static void +gtk_printer_cups_finalize (GObject *object) +{ + GtkPrinterCups *printer; + + g_return_if_fail (object != NULL); + + printer = GTK_PRINTER_CUPS (object); + + g_free (printer->device_uri); + g_free (printer->original_device_uri); + g_free (printer->printer_uri); + g_free (printer->hostname); + g_free (printer->ppd_name); + g_free (printer->default_cover_before); + g_free (printer->default_cover_after); + g_strfreev (printer->auth_info_required); + +#ifdef HAVE_COLORD + if (printer->colord_cancellable) + { + g_cancellable_cancel (printer->colord_cancellable); + g_object_unref (printer->colord_cancellable); + } + g_free (printer->colord_title); + g_free (printer->colord_qualifier); + if (printer->colord_client) + g_object_unref (printer->colord_client); + if (printer->colord_device) + g_object_unref (printer->colord_device); + if (printer->colord_profile) + g_object_unref (printer->colord_profile); +#endif + +#ifdef HAVE_CUPS_API_1_6 + g_free (printer->avahi_name); + g_free (printer->avahi_type); + g_free (printer->avahi_domain); +#endif + + g_strfreev (printer->covers); + + if (printer->ppd_file) + ppdClose (printer->ppd_file); + + g_free (printer->media_default); + g_list_free_full (printer->media_supported, g_free); + g_list_free_full (printer->media_size_supported, g_free); + + g_free (printer->sides_default); + g_list_free_full (printer->sides_supported, g_free); + + g_free (printer->output_bin_default); + g_list_free_full (printer->output_bin_supported, g_free); + + if (printer->get_remote_ppd_poll > 0) + g_source_remove (printer->get_remote_ppd_poll); + printer->get_remote_ppd_attempts = 0; + + gtk_cups_connection_test_free (printer->remote_cups_connection_test); + + G_OBJECT_CLASS (gtk_printer_cups_parent_class)->finalize (object); +} + +static void +gtk_printer_cups_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_printer_cups_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ +#ifdef HAVE_COLORD + GtkPrinterCups *printer = GTK_PRINTER_CUPS (object); +#endif + + switch (prop_id) + { + case PROP_PROFILE_TITLE: +#ifdef HAVE_COLORD + if (printer->colord_title) + g_value_set_string (value, printer->colord_title); + else + g_value_set_static_string (value, ""); +#else + g_value_set_static_string (value, NULL); +#endif + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +#ifdef HAVE_COLORD + +static void +colord_update_ui_from_settings (GtkPrinterCups *printer) +{ + const gchar *title = NULL; + + /* not yet connected to colord */ + if (printer->colord_client == NULL) + goto out; + if (!cd_client_get_connected (printer->colord_client)) + goto out; + + /* failed to get a colord device for the printer */ + if (printer->colord_device == NULL) + { + /* TRANSLATORS: when we're running an old CUPS, and + * it hasn't registered the device with colord */ + title = _("Color management unavailable"); + goto out; + } + + /* when colord prevents us from connecting (should not happen) */ + if (!cd_device_get_connected (printer->colord_device)) + goto out; + + /* failed to get a colord device for the printer */ + if (printer->colord_profile == NULL) + { + /* TRANSLATORS: when there is no color profile available */ + title = _("No profile available"); + goto out; + } + + /* when colord prevents us from connecting (should not happen) */ + if (!cd_profile_get_connected (printer->colord_profile)) + goto out; + title = cd_profile_get_title (printer->colord_profile); + if (title == NULL) + { + /* TRANSLATORS: when the color profile has no title */ + title = _("Unspecified profile"); + goto out; + } + +out: + /* SUCCESS! */ + if (g_strcmp0 (title, printer->colord_title) != 0) + { + g_free (printer->colord_title); + printer->colord_title = g_strdup (title); + g_object_notify (G_OBJECT (printer), "profile-title"); + } + return; +} + +static void +colord_client_profile_connect_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + gboolean ret; + GError *error = NULL; + GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); + + ret = cd_profile_connect_finish (CD_PROFILE (source_object), + res, + &error); + if (!ret) + { + g_warning ("failed to get properties from the profile: %s", + error->message); + g_error_free (error); + } + + /* update the UI */ + colord_update_ui_from_settings (printer); + + g_object_unref (printer); +} + +static void +colord_client_device_get_profile_for_qualifiers_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); + GError *error = NULL; + + printer->colord_profile = cd_device_get_profile_for_qualifiers_finish (printer->colord_device, + res, + &error); + if (printer->colord_profile == NULL) + { + /* not having a profile for a qualifier is not a warning */ + g_debug ("no profile for device %s: %s", + cd_device_get_id (printer->colord_device), + error->message); + g_error_free (error); + goto out; + } + + /* get details about the profile */ + cd_profile_connect (printer->colord_profile, + printer->colord_cancellable, + colord_client_profile_connect_cb, + g_object_ref (printer)); +out: + /* update the UI */ + colord_update_ui_from_settings (printer); + + g_object_unref (printer); +} + +void +gtk_printer_cups_update_settings (GtkPrinterCups *printer, + GtkPrintSettings *settings, + GtkPrinterOptionSet *set) +{ + gchar *qualifier = NULL; + gchar **qualifiers = NULL; + GtkPrinterOption *option; + const gchar *format[3]; + + /* nothing set yet */ + if (printer->colord_device == NULL) + goto out; + if (!cd_device_get_connected (printer->colord_device)) + goto out; + + /* cupsICCQualifier1 */ + option = gtk_printer_option_set_lookup (set, "cups-ColorSpace"); + if (option == NULL) + option = gtk_printer_option_set_lookup (set, "cups-ColorModel"); + if (option != NULL) + format[0] = option->value; + else + format[0] = "*"; + + /* cupsICCQualifier2 */ + option = gtk_printer_option_set_lookup (set, "cups-OutputMode"); + if (option != NULL) + format[1] = option->value; + else + format[1] = "*"; + + /* cupsICCQualifier3 */ + option = gtk_printer_option_set_lookup (set, "cups-Resolution"); + if (option != NULL) + format[2] = option->value; + else + format[2] = "*"; + + /* get profile for the device given the qualifier */ + qualifier = g_strdup_printf ("%s.%s.%s,%s.%s.*,%s.*.*", + format[0], format[1], format[2], + format[0], format[1], + format[0]); + + /* only requery colord if the option that was changed would give + * us a different profile result */ + if (g_strcmp0 (qualifier, printer->colord_qualifier) == 0) + goto out; + + qualifiers = g_strsplit (qualifier, ",", -1); + cd_device_get_profile_for_qualifiers (printer->colord_device, + (const gchar **) qualifiers, + printer->colord_cancellable, + colord_client_device_get_profile_for_qualifiers_cb, + g_object_ref (printer)); + + /* save for the future */ + g_free (printer->colord_qualifier); + printer->colord_qualifier = g_strdup (qualifier); + + /* update the UI */ + colord_update_ui_from_settings (printer); +out: + g_free (qualifier); + g_strfreev (qualifiers); +} + +static void +colord_client_device_connect_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); + gboolean ret; + GError *error = NULL; + + /* get details about the device */ + ret = cd_device_connect_finish (CD_DEVICE (source_object), res, &error); + if (!ret) + { + g_warning ("failed to get properties from the colord device: %s", + error->message); + g_error_free (error); + goto out; + } +out: + /* update the UI */ + colord_update_ui_from_settings (printer); + + g_object_unref (printer); +} + +static void +colord_client_find_device_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); + GError *error = NULL; + + /* get the new device */ + printer->colord_device = cd_client_find_device_finish (printer->colord_client, + res, + &error); + if (printer->colord_device == NULL) + { + g_warning ("failed to get find a colord device: %s", + error->message); + g_error_free (error); + goto out; + } + + /* get details about the device */ + g_cancellable_reset (printer->colord_cancellable); + cd_device_connect (printer->colord_device, + printer->colord_cancellable, + colord_client_device_connect_cb, + g_object_ref (printer)); +out: + /* update the UI */ + colord_update_ui_from_settings (printer); + + g_object_unref (printer); +} + +static void +colord_update_device (GtkPrinterCups *printer) +{ + gchar *colord_device_id = NULL; + + /* not yet connected to the daemon */ + if (!cd_client_get_connected (printer->colord_client)) + goto out; + + /* not yet assigned a printer */ + if (printer->ppd_file == NULL) + goto out; + + /* old cached profile no longer valid */ + if (printer->colord_profile) + { + g_object_unref (printer->colord_profile); + printer->colord_profile = NULL; + } + + /* old cached device no longer valid */ + if (printer->colord_device) + { + g_object_unref (printer->colord_device); + printer->colord_device = NULL; + } + + /* generate a known ID */ + colord_device_id = g_strdup_printf ("cups-%s", gtk_printer_get_name (GTK_PRINTER (printer))); + + g_cancellable_reset (printer->colord_cancellable); + cd_client_find_device (printer->colord_client, + colord_device_id, + printer->colord_cancellable, + colord_client_find_device_cb, + g_object_ref (printer)); +out: + g_free (colord_device_id); + + /* update the UI */ + colord_update_ui_from_settings (printer); +} + +static void +colord_client_connect_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + gboolean ret; + GError *error = NULL; + GtkPrinterCups *printer = GTK_PRINTER_CUPS (user_data); + static gboolean colord_warned = FALSE; + + ret = cd_client_connect_finish (CD_CLIENT (source_object), + res, &error); + if (!ret) + { + if (!colord_warned) + { + g_warning ("failed to contact colord: %s", error->message); + colord_warned = TRUE; + } + g_error_free (error); + } + + /* refresh the device */ + colord_update_device (printer); + + g_object_unref (printer); +} + +static void +colord_printer_details_aquired_cb (GtkPrinterCups *printer, + gboolean success, + gpointer user_data) +{ + /* refresh the device */ + if (printer->colord_client) + colord_update_device (printer); +} +#endif + +/** + * gtk_printer_cups_new: + * + * Creates a new #GtkPrinterCups. + * + * Returns: a new #GtkPrinterCups + * + * Since: 2.10 + **/ +GtkPrinterCups * +gtk_printer_cups_new (const char *name, + GtkPrintBackend *backend, + gpointer colord_client) +{ + GObject *result; + gboolean accepts_pdf; + GtkPrinterCups *printer; + +#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1 + accepts_pdf = TRUE; +#else + accepts_pdf = FALSE; +#endif + + result = g_object_new (GTK_TYPE_PRINTER_CUPS, + "name", name, + "backend", backend, + "is-virtual", FALSE, + "accepts-pdf", accepts_pdf, + NULL); + printer = GTK_PRINTER_CUPS (result); + +#ifdef HAVE_COLORD + /* connect to colord */ + if (colord_client != NULL) + { + printer->colord_cancellable = g_cancellable_new (); + printer->colord_client = g_object_ref (CD_CLIENT (colord_client)); + cd_client_connect (printer->colord_client, + printer->colord_cancellable, + colord_client_connect_cb, + g_object_ref (printer)); + } + + /* update the device when we read the PPD */ + g_signal_connect (printer, "details-acquired", + G_CALLBACK (colord_printer_details_aquired_cb), + printer); +#endif + + /* + * IPP version 1.1 has to be supported + * by all implementations according to rfc 2911 + */ + printer->ipp_version_major = 1; + printer->ipp_version_minor = 1; + + return printer; +} + +ppd_file_t * +gtk_printer_cups_get_ppd (GtkPrinterCups *printer) +{ + return printer->ppd_file; +} + +const gchar * +gtk_printer_cups_get_ppd_name (GtkPrinterCups *printer) +{ + const gchar *result; + + result = printer->ppd_name; + + if (result == NULL) + result = gtk_printer_get_name (GTK_PRINTER (printer)); + + return result; +} diff --git a/modules/printbackends/gtkprintercups.h b/modules/printbackends/gtkprintercups.h new file mode 100644 index 0000000000..f26bbab677 --- /dev/null +++ b/modules/printbackends/gtkprintercups.h @@ -0,0 +1,130 @@ +/* GtkPrinterCups + * Copyright (C) 2006 John (J5) Palmieri + * + * 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 . + */ + +#ifndef __GTK_PRINTER_CUPS_H__ +#define __GTK_PRINTER_CUPS_H__ + +#include +#include +#include +#include "gtkcupsutils.h" + +#include +#include + +#ifdef HAVE_COLORD +#include +#endif + +G_BEGIN_DECLS + +#define GTK_TYPE_PRINTER_CUPS (gtk_printer_cups_get_type ()) +#define GTK_PRINTER_CUPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCups)) +#define GTK_PRINTER_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass)) +#define GTK_IS_PRINTER_CUPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CUPS)) +#define GTK_IS_PRINTER_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CUPS)) +#define GTK_PRINTER_CUPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass)) + +typedef struct _GtkPrinterCups GtkPrinterCups; +typedef struct _GtkPrinterCupsClass GtkPrinterCupsClass; +typedef struct _GtkPrinterCupsPrivate GtkPrinterCupsPrivate; + +struct _GtkPrinterCups +{ + GtkPrinter parent_instance; + + gchar *device_uri; + gchar *original_device_uri; + gchar *printer_uri; + gchar *hostname; + gint port; + gchar **auth_info_required; + + ipp_pstate_t state; + gboolean reading_ppd; + gchar *ppd_name; + ppd_file_t *ppd_file; + + gchar *media_default; + GList *media_supported; + GList *media_size_supported; + gint media_bottom_margin_default; + gint media_top_margin_default; + gint media_left_margin_default; + gint media_right_margin_default; + gboolean media_margin_default_set; + gchar *sides_default; + GList *sides_supported; + gchar *output_bin_default; + GList *output_bin_supported; + + gchar *default_cover_before; + gchar *default_cover_after; + + gint default_number_up; + + gboolean remote; + guint get_remote_ppd_poll; + gint get_remote_ppd_attempts; + GtkCupsConnectionTest *remote_cups_connection_test; +#ifdef HAVE_COLORD + CdClient *colord_client; + CdDevice *colord_device; + CdProfile *colord_profile; + GCancellable *colord_cancellable; + gchar *colord_title; + gchar *colord_qualifier; +#endif +#ifdef HAVE_CUPS_API_1_6 + gboolean avahi_browsed; + gchar *avahi_name; + gchar *avahi_type; + gchar *avahi_domain; +#endif + guchar ipp_version_major; + guchar ipp_version_minor; + gboolean supports_copies; + gboolean supports_collate; + gboolean supports_number_up; + char **covers; + int number_of_covers; +}; + +struct _GtkPrinterCupsClass +{ + GtkPrinterClass parent_class; + +}; + +GType gtk_printer_cups_get_type (void) G_GNUC_CONST; +void gtk_printer_cups_register_type (GTypeModule *module); + +GtkPrinterCups *gtk_printer_cups_new (const char *name, + GtkPrintBackend *backend, + gpointer colord_client); +ppd_file_t *gtk_printer_cups_get_ppd (GtkPrinterCups *printer); +const gchar *gtk_printer_cups_get_ppd_name (GtkPrinterCups *printer); + +#ifdef HAVE_COLORD +void gtk_printer_cups_update_settings (GtkPrinterCups *printer, + GtkPrintSettings *settings, + GtkPrinterOptionSet *set); +#endif + +G_END_DECLS + +#endif /* __GTK_PRINTER_CUPS_H__ */ diff --git a/modules/printbackends/lpr/gtkprintbackendlpr.c b/modules/printbackends/lpr/gtkprintbackendlpr.c deleted file mode 100644 index aefa921515..0000000000 --- a/modules/printbackends/lpr/gtkprintbackendlpr.c +++ /dev/null @@ -1,479 +0,0 @@ -/* GTK - The GIMP Toolkit - * gtkprintbackendlpr.c: LPR implementation of GtkPrintBackend - * for printing to lpr - * Copyright (C) 2006, 2007 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include "gtkprinter-private.h" - -#include "gtkprintbackendlpr.h" - -typedef struct _GtkPrintBackendLprClass GtkPrintBackendLprClass; - -#define GTK_PRINT_BACKEND_LPR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass)) -#define GTK_IS_PRINT_BACKEND_LPR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_LPR)) -#define GTK_PRINT_BACKEND_LPR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass)) - -#define _LPR_MAX_CHUNK_SIZE 8192 - -struct _GtkPrintBackendLprClass -{ - GtkPrintBackendClass parent_class; -}; - -struct _GtkPrintBackendLpr -{ - GtkPrintBackend parent_instance; -}; - -static GObjectClass *backend_parent_class; - -static void gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class); -static void gtk_print_backend_lpr_init (GtkPrintBackendLpr *impl); -static void lpr_printer_get_settings_from_options (GtkPrinter *printer, - GtkPrinterOptionSet *options, - GtkPrintSettings *settings); -static GtkPrinterOptionSet *lpr_printer_get_options (GtkPrinter *printer, - GtkPrintSettings *settings, - GtkPageSetup *page_setup, - GtkPrintCapabilities capabilities); -static void lpr_printer_prepare_for_print (GtkPrinter *printer, - GtkPrintJob *print_job, - GtkPrintSettings *settings, - GtkPageSetup *page_setup); -static cairo_surface_t * lpr_printer_create_cairo_surface (GtkPrinter *printer, - GtkPrintSettings *settings, - gdouble width, - gdouble height, - GIOChannel *cache_io); -static void gtk_print_backend_lpr_print_stream (GtkPrintBackend *print_backend, - GtkPrintJob *job, - GIOChannel *data_io, - GtkPrintJobCompleteFunc callback, - gpointer user_data, - GDestroyNotify dnotify); - -G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendLpr, gtk_print_backend_lpr, GTK_TYPE_PRINT_BACKEND) - -void -g_io_module_load (GIOModule *module) -{ - g_type_module_use (G_TYPE_MODULE (module)); - - gtk_print_backend_lpr_register_type (G_TYPE_MODULE (module)); - - g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, - GTK_TYPE_PRINT_BACKEND_LPR, - "lpr", - 10); -} - -void -g_io_module_unload (GIOModule *module) -{ -} - -char ** -g_io_module_query (void) -{ - char *eps[] = { - GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, - NULL - }; - - return g_strdupv (eps); -} - -/** - * gtk_print_backend_lpr_new: - * - * Creates a new #GtkPrintBackendLpr object. #GtkPrintBackendLpr - * implements the #GtkPrintBackend interface with direct access to - * the filesystem using Unix/Linux API calls - * - * Returns: the new #GtkPrintBackendLpr object - **/ -GtkPrintBackend * -gtk_print_backend_lpr_new (void) -{ - return g_object_new (GTK_TYPE_PRINT_BACKEND_LPR, NULL); -} - -static void -gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class) -{ - GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class); - - backend_parent_class = g_type_class_peek_parent (class); - - backend_class->print_stream = gtk_print_backend_lpr_print_stream; - backend_class->printer_create_cairo_surface = lpr_printer_create_cairo_surface; - backend_class->printer_get_options = lpr_printer_get_options; - backend_class->printer_get_settings_from_options = lpr_printer_get_settings_from_options; - backend_class->printer_prepare_for_print = lpr_printer_prepare_for_print; -} - -static void -gtk_print_backend_lpr_class_finalize (GtkPrintBackendLprClass *class) -{ -} - -static cairo_status_t -_cairo_write (void *closure, - const unsigned char *data, - unsigned int length) -{ - GIOChannel *io = (GIOChannel *)closure; - gsize written; - GError *error; - - error = NULL; - - GTK_NOTE (PRINTING, - g_print ("LPR Backend: Writting %i byte chunk to temp file\n", length)); - - while (length > 0) - { - g_io_channel_write_chars (io, (const gchar*)data, length, &written, &error); - - if (error != NULL) - { - GTK_NOTE (PRINTING, - g_print ("LPR Backend: Error writting to temp file, %s\n", error->message)); - - g_error_free (error); - return CAIRO_STATUS_WRITE_ERROR; - } - - GTK_NOTE (PRINTING, - g_print ("LPR Backend: Wrote %" G_GSIZE_FORMAT " bytes to temp file\n", written)); - - data += written; - length -= written; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -lpr_printer_create_cairo_surface (GtkPrinter *printer, - GtkPrintSettings *settings, - gdouble width, - gdouble height, - GIOChannel *cache_io) -{ - cairo_surface_t *surface; - - surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height); - - cairo_surface_set_fallback_resolution (surface, - 2.0 * gtk_print_settings_get_printer_lpi (settings), - 2.0 * gtk_print_settings_get_printer_lpi (settings)); - - return surface; -} - -typedef struct { - GtkPrintBackend *backend; - GtkPrintJobCompleteFunc callback; - GtkPrintJob *job; - gpointer user_data; - GDestroyNotify dnotify; - - GIOChannel *in; -} _PrintStreamData; - -static void -lpr_print_cb (GtkPrintBackendLpr *print_backend, - GError *error, - gpointer user_data) -{ - _PrintStreamData *ps = (_PrintStreamData *) user_data; - - if (ps->in != NULL) - g_io_channel_unref (ps->in); - - if (ps->callback) - ps->callback (ps->job, ps->user_data, error); - - if (ps->dnotify) - ps->dnotify (ps->user_data); - - gtk_print_job_set_status (ps->job, - error ? GTK_PRINT_STATUS_FINISHED_ABORTED - : GTK_PRINT_STATUS_FINISHED); - - if (ps->job) - g_object_unref (ps->job); - - g_free (ps); -} - -static gboolean -lpr_write (GIOChannel *source, - GIOCondition con, - gpointer user_data) -{ - gchar buf[_LPR_MAX_CHUNK_SIZE]; - gsize bytes_read; - GError *error; - GIOStatus status; - _PrintStreamData *ps = (_PrintStreamData *) user_data; - - error = NULL; - - status = - g_io_channel_read_chars (source, - buf, - _LPR_MAX_CHUNK_SIZE, - &bytes_read, - &error); - - if (status != G_IO_STATUS_ERROR) - { - gsize bytes_written; - - g_io_channel_write_chars (ps->in, - buf, - bytes_read, - &bytes_written, - &error); - } - - if (error != NULL || status == G_IO_STATUS_EOF) - { - lpr_print_cb (GTK_PRINT_BACKEND_LPR (ps->backend), - error, user_data); - - - if (error != NULL) - { - GTK_NOTE (PRINTING, - g_print ("LPR Backend: %s\n", error->message)); - - g_error_free (error); - } - - return FALSE; - } - - GTK_NOTE (PRINTING, - g_print ("LPR Backend: Writting %" G_GSIZE_FORMAT " byte chunk to lpr pipe\n", bytes_read)); - - - return TRUE; -} - -#define LPR_COMMAND "lpr" - -static void -gtk_print_backend_lpr_print_stream (GtkPrintBackend *print_backend, - GtkPrintJob *job, - GIOChannel *data_io, - GtkPrintJobCompleteFunc callback, - gpointer user_data, - GDestroyNotify dnotify) -{ - GError *print_error = NULL; - _PrintStreamData *ps; - GtkPrintSettings *settings; - gint argc; - gint in_fd; - gchar **argv = NULL; - const char *cmd_line; - - settings = gtk_print_job_get_settings (job); - - cmd_line = gtk_print_settings_get (settings, "lpr-commandline"); - if (cmd_line == NULL) - cmd_line = LPR_COMMAND; - - ps = g_new0 (_PrintStreamData, 1); - ps->callback = callback; - ps->user_data = user_data; - ps->dnotify = dnotify; - ps->job = g_object_ref (job); - ps->in = NULL; - - /* spawn lpr with pipes and pipe ps file to lpr */ - if (!g_shell_parse_argv (cmd_line, &argc, &argv, &print_error)) - goto out; - - if (!g_spawn_async_with_pipes (NULL, - argv, - NULL, - G_SPAWN_SEARCH_PATH, - NULL, - NULL, - NULL, - &in_fd, - NULL, - NULL, - &print_error)) - goto out; - - ps->in = g_io_channel_unix_new (in_fd); - - g_io_channel_set_encoding (ps->in, NULL, &print_error); - if (print_error != NULL) - { - if (ps->in != NULL) - g_io_channel_unref (ps->in); - - goto out; - } - - g_io_channel_set_close_on_unref (ps->in, TRUE); - - g_io_add_watch (data_io, - G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, - (GIOFunc) lpr_write, - ps); - - out: - if (argv != NULL) - g_strfreev (argv); - - if (print_error != NULL) - { - lpr_print_cb (GTK_PRINT_BACKEND_LPR (print_backend), - print_error, ps); - g_error_free (print_error); - } -} - -static void -gtk_print_backend_lpr_init (GtkPrintBackendLpr *backend) -{ - GtkPrinter *printer; - - printer = gtk_printer_new (_("Print to LPR"), - GTK_PRINT_BACKEND (backend), - TRUE); - gtk_printer_set_has_details (printer, TRUE); - gtk_printer_set_icon_name (printer, "printer"); - gtk_printer_set_is_active (printer, TRUE); - gtk_printer_set_is_default (printer, TRUE); - - gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer); - g_object_unref (printer); - gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); -} - -static GtkPrinterOptionSet * -lpr_printer_get_options (GtkPrinter *printer, - GtkPrintSettings *settings, - GtkPageSetup *page_setup, - GtkPrintCapabilities capabilities) -{ - GtkPrinterOptionSet *set; - GtkPrinterOption *option; - const char *command; - char *n_up[] = {"1", "2", "4", "6", "9", "16" }; - - set = gtk_printer_option_set_new (); - - option = gtk_printer_option_new ("gtk-n-up", _("Pages Per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE); - gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), - n_up, n_up); - gtk_printer_option_set (option, "1"); - gtk_printer_option_set_add (set, option); - g_object_unref (option); - - option = gtk_printer_option_new ("gtk-main-page-custom-input", _("Command Line"), GTK_PRINTER_OPTION_TYPE_STRING); - gtk_printer_option_set_activates_default (option, TRUE); - option->group = g_strdup ("GtkPrintDialogExtension"); - if (settings != NULL && - (command = gtk_print_settings_get (settings, "lpr-commandline"))!= NULL) - gtk_printer_option_set (option, command); - else - gtk_printer_option_set (option, LPR_COMMAND); - gtk_printer_option_set_add (set, option); - - return set; -} - -static void -lpr_printer_get_settings_from_options (GtkPrinter *printer, - GtkPrinterOptionSet *options, - GtkPrintSettings *settings) -{ - GtkPrinterOption *option; - - option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input"); - if (option) - gtk_print_settings_set (settings, "lpr-commandline", option->value); - - option = gtk_printer_option_set_lookup (options, "gtk-n-up"); - if (option) - gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value); - - option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout"); - if (option) - gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value); -} - -static void -lpr_printer_prepare_for_print (GtkPrinter *printer, - GtkPrintJob *print_job, - GtkPrintSettings *settings, - GtkPageSetup *page_setup) -{ - double scale; - GtkPrintPages pages; - GtkPageRange *ranges; - gint n_ranges; - - pages = gtk_print_settings_get_print_pages (settings); - gtk_print_job_set_pages (print_job, pages); - - if (pages == GTK_PRINT_PAGES_RANGES) - ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges); - else - { - ranges = NULL; - n_ranges = 0; - } - - gtk_print_job_set_page_ranges (print_job, ranges, n_ranges); - gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings)); - gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings)); - gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings)); - gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings)); - gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings)); - - scale = gtk_print_settings_get_scale (settings); - if (scale != 100.0) - gtk_print_job_set_scale (print_job, scale / 100.0); - - gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings)); - gtk_print_job_set_rotate (print_job, TRUE); -} diff --git a/modules/printbackends/lpr/gtkprintbackendlpr.h b/modules/printbackends/lpr/gtkprintbackendlpr.h deleted file mode 100644 index 0ba195bebd..0000000000 --- a/modules/printbackends/lpr/gtkprintbackendlpr.h +++ /dev/null @@ -1,41 +0,0 @@ -/* GTK - The GIMP Toolkit - * gtkprintbackendlpr.h: LPR implementation of GtkPrintBackend - * for printing to lpr - * Copyright (C) 2006, 2007 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -#ifndef __GTK_PRINT_BACKEND_LPR_H__ -#define __GTK_PRINT_BACKEND_LPR_H__ - -#include -#include "gtkprintbackend.h" - -G_BEGIN_DECLS - -#define GTK_TYPE_PRINT_BACKEND_LPR (gtk_print_backend_lpr_get_type ()) -#define GTK_PRINT_BACKEND_LPR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLpr)) -#define GTK_IS_PRINT_BACKEND_LPR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_LPR)) - -typedef struct _GtkPrintBackendLpr GtkPrintBackendLpr; - -GtkPrintBackend *gtk_print_backend_lpr_new (void); -GType gtk_print_backend_lpr_get_type (void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GTK_PRINT_BACKEND_LPR_H__ */ - - diff --git a/modules/printbackends/lpr/meson.build b/modules/printbackends/lpr/meson.build deleted file mode 100644 index 581ca77a48..0000000000 --- a/modules/printbackends/lpr/meson.build +++ /dev/null @@ -1,10 +0,0 @@ -shared_module('printbackend-lpr', - 'gtkprintbackendlpr.c', - c_args: [ - '-DGTK_COMPILATION', - '-DGTK_DISABLE_DEPRECATION_WARNINGS', - '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', - ], - dependencies: libgtk_dep, - install_dir: printbackends_install_dir, - install : true) diff --git a/modules/printbackends/meson.build b/modules/printbackends/meson.build index f8a363a668..76b5c3789e 100644 --- a/modules/printbackends/meson.build +++ b/modules/printbackends/meson.build @@ -84,6 +84,68 @@ printbackends_install_dir = join_paths(get_option('libdir'), printbackends_subdi cdata.set_quoted('GTK_PRINT_BACKENDS', ','.join(print_backends)) -foreach print_backend : print_backends - subdir(print_backend) -endforeach + +enable_colord = get_option('colord') +if enable_colord != 'no' + want_colord = enable_colord == 'yes' + colord_dep = dependency('colord', version: '>= 0.1.9', required: want_colord) + cdata.set('HAVE_COLORD', colord_dep.found()) +else + colord_dep = [] +endif + +if print_backends.contains('cups') + shared_module('printbackend-cups', + 'gtkprintbackendcups.c', + 'gtkprintercups.c', + 'gtkcupsutils.c', + 'gtkcupssecretsutils.c', + c_args: [ + '-DGTK_COMPILATION', + '-DGTK_DISABLE_DEPRECATION_WARNINGS', + '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', + ], + dependencies: [libgtk_dep, libcups, colord_dep], + install_dir: printbackends_install_dir, + install : true) +endif + +if print_backends.contains('cloudprint') + shared_module('printbackend-cloudprint', + 'gtkprintbackendcloudprint.c', + 'gtkprintercloudprint.c', + 'gtkcloudprintaccount.c', + c_args: [ + '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', + '-DGTK_DISABLE_DEPRECATION_WARNINGS', + ], + dependencies: [ libgtk_dep, rest_dep, json_glib_dep ], + install_dir: printbackends_install_dir, + install : true) +endif + +if print_backends.contains('file') + shared_module('printbackend-file', + 'gtkprintbackendfile.c', + c_args: [ + '-DGTK_COMPILATION', + '-DGTK_DISABLE_DEPRECATION_WARNINGS', + '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', + ], + dependencies: libgtk_dep, + install_dir: printbackends_install_dir, + install : true) +endif + +if print_backends.contains('lpr') + shared_module('printbackend-lpr', + 'gtkprintbackendlpr.c', + c_args: [ + '-DGTK_COMPILATION', + '-DGTK_DISABLE_DEPRECATION_WARNINGS', + '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED', + ], + dependencies: libgtk_dep, + install_dir: printbackends_install_dir, + install : true) +endif \ No newline at end of file