X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=modules%2Fprintbackends%2Fcups%2Fgtkprintbackendcups.c;h=96f4be410339f04b3511a8c49b127278aad3a007;hb=9d36dbb44ba14adeb29135de01a936eb623375f2;hp=113388a142e1e9716d865b19ca68b1d8acf699ac;hpb=1b97ef3e147577ae1e745051e4df76bcce6ff6fa;p=~andy%2Fgtk diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c index 113388a14..96f4be410 100644 --- a/modules/printbackends/cups/gtkprintbackendcups.c +++ b/modules/printbackends/cups/gtkprintbackendcups.c @@ -1,5 +1,5 @@ /* GTK - The GIMP Toolkit - * gtkprintbackendcups.h: Default implementation of GtkPrintBackend + * gtkprintbackendcups.h: Default implementation of GtkPrintBackend * for the Common Unix Print System (CUPS) * Copyright (C) 2006, 2007 Red Hat, Inc. * @@ -14,17 +14,27 @@ * 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, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ -#include +#ifdef __linux__ +#define _GNU_SOURCE +#endif + +#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 @@ -39,18 +49,19 @@ #include #include -#include -#include +#include #include -#include +#include #include #include "gtkprintbackendcups.h" #include "gtkprintercups.h" #include "gtkcupsutils.h" -#include "gtkdebug.h" +#ifdef HAVE_COLORD +#include +#endif typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass; @@ -58,7 +69,7 @@ typedef struct _GtkPrintBackendCupsClass 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_ATTEMPTS 10 #define _CUPS_MAX_CHUNK_SIZE 8192 /* define this to see warnings about ignored ppd options */ @@ -70,10 +81,10 @@ typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass; static GType print_backend_cups_type = 0; typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend, - GtkCupsResult *result, + GtkCupsResult *result, gpointer user_data); -typedef enum +typedef enum { DISPATCH_SETUP, DISPATCH_REQUEST, @@ -83,14 +94,17 @@ typedef enum DISPATCH_ERROR } GtkPrintCupsDispatchState; -typedef struct +typedef struct { GSource source; http_t *http; GtkCupsRequest *request; + GtkCupsPollState poll_state; GPollFD *data_poll; GtkPrintBackendCups *backend; + GtkPrintCupsResponseCallbackFunc callback; + gpointer callback_data; } GtkPrintCupsDispatchWatch; @@ -104,10 +118,25 @@ 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; + + char **covers; + int number_of_covers; + + GList *requests; + GHashTable *auth; + gchar *username; + gboolean authentication_lock; +#ifdef HAVE_COLORD + CdClient *colord_client; +#endif }; static GObjectClass *backend_parent_class; @@ -117,6 +146,8 @@ static void gtk_print_backend_cups_init (GtkPrintBack 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, @@ -136,14 +167,15 @@ static void cups_printer_prepare_for_print (GtkPrinter 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 void cups_request_default_printer (GtkPrintBackendCups *print_backend); -static void cups_request_ppd (GtkPrinter *printer); -static void cups_printer_get_hard_margins (GtkPrinter *printer, - double *top, - double *bottom, - double *left, - double *right); +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); @@ -163,11 +195,18 @@ static cairo_surface_t * cups_printer_create_cairo_surface (GtkPrinter gdouble height, GIOChannel *cache_io); +static void gtk_print_backend_cups_set_password (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **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 gtk_print_backend_cups_register_type (GTypeModule *module) { - static const GTypeInfo print_backend_cups_info = + const GTypeInfo print_backend_cups_info = { sizeof (GtkPrintBackendCupsClass), NULL, /* base_init */ @@ -186,28 +225,43 @@ gtk_print_backend_cups_register_type (GTypeModule *module) &print_backend_cups_info, 0); } -G_MODULE_EXPORT void +G_MODULE_EXPORT void pb_module_init (GTypeModule *module) { GTK_NOTE (PRINTING, - g_print ("CUPS Backend: Initializing the CUPS print backend module\n")); + g_print ("CUPS Backend: Initializing the CUPS print backend module\n")); gtk_print_backend_cups_register_type (module); gtk_printer_cups_register_type (module); } -G_MODULE_EXPORT void +G_MODULE_EXPORT void pb_module_exit (void) { } - -G_MODULE_EXPORT GtkPrintBackend * + +G_MODULE_EXPORT GtkPrintBackend * pb_module_create (void) { return gtk_print_backend_cups_new (); } - +/* 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 ippGetInteger(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 +#endif /* * GtkPrintBackendCups */ @@ -246,7 +300,7 @@ gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *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->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; @@ -255,8 +309,10 @@ gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class) 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 cairo_status_t @@ -273,22 +329,22 @@ _cairo_write_to_cups (void *closure, GTK_NOTE (PRINTING, g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length)); - while (length > 0) + 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", + 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 %i bytes to temp file\n", written)); + g_print ("CUPS Backend: Wrote %"G_GSIZE_FORMAT" bytes to temp file\n", written)); data += written; length -= written; @@ -300,18 +356,88 @@ _cairo_write_to_cups (void *closure, static cairo_surface_t * cups_printer_create_cairo_surface (GtkPrinter *printer, GtkPrintSettings *settings, - gdouble width, + gdouble width, gdouble height, GIOChannel *cache_io) { - cairo_surface_t *surface; - - /* TODO: check if it is a ps or pdf printer */ - - surface = cairo_ps_surface_create_for_stream (_cairo_write_to_cups, cache_io, width, height); + 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); + } - /* TODO: DPI from settings object? */ - cairo_surface_set_fallback_resolution (surface, 300, 300); + 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; } @@ -343,10 +469,10 @@ cups_print_cb (GtkPrintBackendCups *print_backend, GError *error = NULL; CupsPrintStreamData *ps = user_data; - GDK_THREADS_ENTER (); + gdk_threads_enter (); GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); + g_print ("CUPS Backend: %s\n", G_STRFUNC)); if (gtk_cups_result_is_error (result)) error = g_error_new_literal (gtk_print_error_quark (), @@ -363,7 +489,7 @@ cups_print_cb (GtkPrintBackendCups *print_backend, ipp_t *response = gtk_cups_result_get_response (result); if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL) - job_id = attr->values[0].integer; + 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); @@ -372,33 +498,79 @@ cups_print_cb (GtkPrintBackendCups *print_backend, 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); - GDK_THREADS_LEAVE (); + gdk_threads_leave (); } +typedef struct { + GtkCupsRequest *request; + GtkPrinterCups *printer; +} CupsOptionsData; + static void add_cups_options (const gchar *key, const gchar *value, gpointer user_data) { - GtkCupsRequest *request = 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-"); - gtk_cups_request_encode_option (request, key, value); + 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 @@ -411,41 +583,61 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend, { GtkPrinterCups *cups_printer; CupsPrintStreamData *ps; + CupsOptionsData *options_data; GtkCupsRequest *request; GtkPrintSettings *settings; const gchar *title; + char printer_absolute_uri[HTTP_MAX_URI]; GTK_NOTE (PRINTING, - g_print ("CUPS Backend: %s\n", G_STRFUNC)); + 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); - request = gtk_cups_request_new (NULL, - GTK_CUPS_POST, - IPP_PRINT_JOB, - data_io, - NULL, - cups_printer->device_uri); - - gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, + 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_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, cups_printer->printer_uri); + NULL, printer_absolute_uri); title = gtk_print_job_get_title (job); if (title) - gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, - IPP_TAG_NAME, "job-name", + gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, + IPP_TAG_NAME, "job-name", NULL, title); - gtk_print_settings_foreach (settings, add_cups_options, request); - + 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); + request->need_auth_info = cups_printer->auth_info_required != NULL; + 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, @@ -453,22 +645,50 @@ gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend, (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) { - backend_cups->list_printers_poll = FALSE; - backend_cups->got_default_printer = FALSE; + 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->covers = NULL; + backend_cups->number_of_covers = 0; + + 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 - cups_request_default_printer (backend_cups); + cups_get_local_default_printer (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")); @@ -476,7 +696,21 @@ gtk_print_backend_cups_finalize (GObject *object) g_free (backend_cups->default_printer); backend_cups->default_printer = NULL; - + + g_strfreev (backend_cups->covers); + backend_cups->number_of_covers = 0; + + 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 + backend_parent_class->finalize (object); } @@ -493,61 +727,439 @@ gtk_print_backend_cups_dispose (GObject *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; + 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) +{ + 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: storing 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]); + } + 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 -cups_dispatch_watch_check (GSource *source) +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); + + 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; - 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); + /* 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); - g_source_add_poll (source, dispatch->data_poll); - } - else - { + 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; - } -#ifdef HAVE_CUPS_API_1_2 - dispatch->data_poll->fd = httpGetFd (dispatch->request->http); -#else - dispatch->data_poll->fd = dispatch->request->http->fd; -#endif + 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 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); + + for (i = 0; i < length; i++) + { + g_free (auth_info_default[i]); + g_free (auth_info_display[i]); } - - if (poll_state != GTK_CUPS_HTTP_IDLE) - if (!(dispatch->data_poll->revents & dispatch->data_poll->events)) + + g_free (auth_info_default); + g_free (auth_info_display); + g_free (printer_name); + g_free (prompt); + + g_idle_add (check_auth_info, user_data); + + 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); + + 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; } @@ -556,6 +1168,7 @@ cups_dispatch_watch_prepare (GSource *source, gint *timeout_) { GtkPrintCupsDispatchWatch *dispatch; + gboolean result; dispatch = (GtkPrintCupsDispatchWatch *) source; @@ -563,8 +1176,12 @@ cups_dispatch_watch_prepare (GSource *source, g_print ("CUPS Backend: %s \n", G_STRFUNC, source)); *timeout_ = -1; - - return gtk_cups_request_read_write (dispatch->request); + + result = gtk_cups_request_read_write (dispatch->request, TRUE); + + cups_dispatch_add_poll (source); + + return result; } static gboolean @@ -573,13 +1190,13 @@ cups_dispatch_watch_dispatch (GSource *source, gpointer user_data) { GtkPrintCupsDispatchWatch *dispatch; - GtkPrintCupsResponseCallbackFunc ep_callback; + GtkPrintCupsResponseCallbackFunc ep_callback; GtkCupsResult *result; - + g_assert (callback != NULL); ep_callback = (GtkPrintCupsResponseCallbackFunc) callback; - + dispatch = (GtkPrintCupsDispatchWatch *) source; result = gtk_cups_request_get_result (dispatch->request); @@ -589,8 +1206,8 @@ cups_dispatch_watch_dispatch (GSource *source, if (gtk_cups_result_is_error (result)) { - GTK_NOTE (PRINTING, - g_print("Error result: %s (type %i, status %i, code %i)\n", + 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), @@ -598,7 +1215,7 @@ cups_dispatch_watch_dispatch (GSource *source, } ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data); - + return FALSE; } @@ -606,12 +1223,39 @@ 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) @@ -625,11 +1269,20 @@ cups_dispatch_watch_finalize (GSource *source) * 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; } - g_free (dispatch->data_poll); + 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 = { @@ -649,20 +1302,35 @@ cups_request_execute (GtkPrintBackendCups *print_backend, { GtkPrintCupsDispatchWatch *dispatch; - dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs, + 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); - g_source_attach ((GSource *) dispatch, NULL); - g_source_unref ((GSource *) dispatch); + if (request->need_auth_info) + { + dispatch->callback = callback; + dispatch->callback_data = user_data; + request_auth_info (dispatch); + } + else + { + g_source_attach ((GSource *) dispatch, NULL); + g_source_unref ((GSource *) dispatch); + } } #if 0 @@ -680,7 +1348,7 @@ cups_request_printer_info_cb (GtkPrintBackendCups *backend, gchar *desc; gchar *state_msg; int job_count; - gboolean status_changed; + gboolean status_changed; g_assert (GTK_IS_PRINT_BACKEND_CUPS (backend)); @@ -699,7 +1367,7 @@ cups_request_printer_info_cb (GtkPrintBackendCups *backend, } cups_printer = GTK_PRINTER_CUPS (printer); - + if (gtk_cups_result_is_error (result)) { if (gtk_printer_is_new (printer)) @@ -715,13 +1383,13 @@ cups_request_printer_info_cb (GtkPrintBackendCups *backend, response = gtk_cups_result_get_response (result); /* TODO: determine printer type and use correct icon */ - gtk_printer_set_icon_name (printer, "gtk-print"); - + gtk_printer_set_icon_name (printer, "printer"); + state_msg = ""; loc = ""; desc = ""; job_count = 0; - for (attr = response->attrs; attr != NULL; attr = attr->next) + for (attr = response->attrs; attr != NULL; attr = attr->next) { if (!attr->name) continue; @@ -734,14 +1402,14 @@ cups_request_printer_info_cb (GtkPrintBackendCups *backend, } status_changed = gtk_printer_set_job_count (printer, job_count); - + status_changed |= gtk_printer_set_location (printer, loc); status_changed |= gtk_printer_set_description (printer, desc); status_changed |= gtk_printer_set_state_message (printer, state_msg); if (status_changed) g_signal_emit_by_name (GTK_PRINT_BACKEND (backend), - "printer-status-changed", printer); + "printer-status-changed", printer); } static void @@ -756,15 +1424,18 @@ cups_request_printer_info (GtkPrintBackendCups *print_backend, "printer-info", "printer-state-message", "printer-state", - "queued-job-count" + "queued-job-count", + "job-sheets-supported", + "job-sheets-default" }; - request = gtk_cups_request_new (NULL, - GTK_CUPS_POST, - IPP_GET_PRINTER_ATTRIBUTES, - NULL, - NULL, - NULL); + request = gtk_cups_request_new_with_username (NULL, + GTK_CUPS_POST, + IPP_GET_PRINTER_ATTRIBUTES, + NULL, + NULL, + NULL, + print_backend->username); printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", printer_name); @@ -779,7 +1450,7 @@ cups_request_printer_info (GtkPrintBackendCups *print_backend, gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", G_N_ELEMENTS (pattrs), NULL, pattrs); - + cups_request_execute (print_backend, request, (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb, @@ -808,7 +1479,7 @@ 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); } @@ -823,7 +1494,7 @@ cups_request_job_info_cb (GtkPrintBackendCups *print_backend, int state; gboolean done; - GDK_THREADS_ENTER (); + gdk_threads_enter (); if (data->job == NULL) { @@ -832,18 +1503,24 @@ cups_request_job_info_cb (GtkPrintBackendCups *print_backend, } data->counter++; - + response = gtk_cups_result_get_response (result); state = 0; - for (attr = response->attrs; attr != NULL; attr = attr->next) + +#ifdef HAVE_CUPS_API_1_6 + attr = ippFindAttribute (response, "job-state", IPP_TAG_INTEGER); + 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) { @@ -882,14 +1559,14 @@ cups_request_job_info_cb (GtkPrintBackendCups *print_backend, timeout = 500; else timeout = 1000; - + g_timeout_add (timeout, cups_job_info_poll_timeout, data); } else - cups_job_poll_data_free (data); + cups_job_poll_data_free (data); done: - GDK_THREADS_LEAVE (); + gdk_threads_leave (); } static void @@ -898,12 +1575,13 @@ cups_request_job_info (CupsJobPollData *data) GtkCupsRequest *request; gchar *job_uri; - request = gtk_cups_request_new (NULL, - GTK_CUPS_POST, - IPP_GET_JOB_ATTRIBUTES, - NULL, - NULL, - NULL); + 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, @@ -921,13 +1599,13 @@ 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 FALSE; + + return G_SOURCE_REMOVE; } static void @@ -950,7 +1628,7 @@ cups_begin_polling_info (GtkPrintBackendCups *print_backend, } static void -mark_printer_inactive (GtkPrinter *printer, +mark_printer_inactive (GtkPrinter *printer, GtkPrintBackend *backend) { gtk_printer_set_is_active (printer, FALSE); @@ -958,7 +1636,7 @@ mark_printer_inactive (GtkPrinter *printer, } static gint -find_printer (GtkPrinter *printer, +find_printer (GtkPrinter *printer, const gchar *find_name) { const gchar *printer_name; @@ -966,6 +1644,302 @@ find_printer (GtkPrinter *printer, 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" + }; +/* Our translatable versions of the printer messages */ +static const char * printer_strings[] = + { + N_("Printer '%s' is low on toner."), + N_("Printer '%s' has no toner left."), + /* Translators: "Developer" like on photo development context */ + N_("Printer '%s' is low on developer."), + /* Translators: "Developer" like on photo development context */ + N_("Printer '%s' is out of developer."), + /* Translators: "marker" is one color bin of the printer */ + N_("Printer '%s' is low on at least one marker supply."), + /* Translators: "marker" is one color bin of the printer */ + N_("Printer '%s' is out of at least one marker supply."), + N_("The cover is open on printer '%s'."), + N_("The door is open on printer '%s'."), + N_("Printer '%s' is low on paper."), + N_("Printer '%s' is out of paper."), + N_("Printer '%s' is currently offline."), + N_("There is a problem on printer '%s'.") + }; + +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 +{ + 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; + gchar **auth_info_required; + gint default_number_up; +} PrinterSetupInfo; + +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) + { + if (cups_backend->covers == NULL) + { + cups_backend->number_of_covers = ippGetCount (attr); + cups_backend->covers = g_new (char *, cups_backend->number_of_covers + 1); + for (i = 0; i < cups_backend->number_of_covers; i++) + cups_backend->covers[i] = g_strdup (ippGetString (attr, i, NULL)); + cups_backend->covers[cups_backend->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 + { + GTK_NOTE (PRINTING, + g_print ("CUPS Backend: Attribute %s ignored", 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 + 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->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); + + + gtk_print_backend_add_printer (backend, printer); + return printer; +} + static void cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, @@ -977,8 +1951,9 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, ipp_t *response; gboolean list_has_changed; GList *removed_printer_checklist; + gchar *remote_default_printer = NULL; - GDK_THREADS_ENTER (); + gdk_threads_enter (); list_has_changed = FALSE; @@ -989,159 +1964,130 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, if (gtk_cups_result_is_error (result)) { - GTK_NOTE (PRINTING, - g_warning ("CUPS Backend: Error getting printer list: %s", - gtk_cups_result_get_error_string (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 + * 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; + gint i; + 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; - const gchar *printer_name = NULL; - const gchar *printer_uri = NULL; - const gchar *member_uris = NULL; - const gchar *location = NULL; - const gchar *description = NULL; - const gchar *state_msg = NULL; - gint state = 0; - gint job_count = 0; gboolean status_changed = FALSE; GList *node; - + gint i; + PrinterSetupInfo *info = g_slice_new0 (PrinterSetupInfo); + info->default_number_up = 1; + /* Skip leading attributes until we hit a printer... */ - while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER) + while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER) attr = attr->next; if (attr == NULL) break; - - while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) + while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER) { - if (strcmp (attr->name, "printer-name") == 0 && - attr->value_tag == IPP_TAG_NAME) - printer_name = attr->values[0].string.text; - else if (strcmp (attr->name, "printer-uri-supported") == 0 && - attr->value_tag == IPP_TAG_URI) - printer_uri = attr->values[0].string.text; - else if (strcmp (attr->name, "member-uris") == 0 && - attr->value_tag == IPP_TAG_URI) - member_uris = attr->values[0].string.text; - else if (strcmp (attr->name, "printer-location") == 0) - location = attr->values[0].string.text; - else if (strcmp (attr->name, "printer-info") == 0) - description = attr->values[0].string.text; - else if (strcmp (attr->name, "printer-state-message") == 0) - state_msg = attr->values[0].string.text; - else if (strcmp (attr->name, "printer-state") == 0) - state = attr->values[0].integer; - else if (strcmp (attr->name, "queued-job-count") == 0) - job_count = attr->values[0].integer; - else - { - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: Attribute %s ignored", attr->name)); - } - + cups_printer_handle_attribute (cups_backend, attr, info); attr = attr->next; } +#endif - if (printer_name == NULL || - (printer_uri == NULL && member_uris == NULL)) + if (info->printer_name == NULL || + (info->printer_uri == NULL && info->member_uris == NULL)) { if (attr == NULL) break; else continue; } - - /* remove name from checklist if it was found */ - node = g_list_find_custom (removed_printer_checklist, printer_name, (GCompareFunc) find_printer); - removed_printer_checklist = g_list_delete_link (removed_printer_checklist, node); - - printer = gtk_print_backend_find_printer (backend, printer_name); - if (!printer) - { - GtkPrinterCups *cups_printer; - 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 */ - - list_has_changed = TRUE; - cups_printer = gtk_printer_cups_new (printer_name, backend); - cups_printer->device_uri = g_strdup_printf ("/printers/%s", printer_name); - - /* Check to see if we are looking at a class */ - if (member_uris) - { - cups_printer->printer_uri = g_strdup (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", member_uris)); - } - else - { - cups_printer->printer_uri = g_strdup (printer_uri); - GTK_NOTE (PRINTING, - g_print ("CUPS Backend: Found printer %s\n", printer_uri)); + 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); + } -#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1 - httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri, - method, sizeof (method), - username, sizeof (username), - hostname, sizeof (hostname), - &port, - resource, sizeof (resource)); - -#else - httpSeparate (cups_printer->printer_uri, - method, - username, - hostname, - &port, - resource); -#endif + /* 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); - 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, printer_name)); - } + printer = gtk_print_backend_find_printer (backend, info->printer_name); + if (!printer) + { + printer = cups_create_printer (cups_backend, info); + list_has_changed = TRUE; + } - gethostname (uri, sizeof (uri)); - if (strcasecmp (uri, hostname) == 0) - strcpy (hostname, "localhost"); - - cups_printer->hostname = g_strdup (hostname); - cups_printer->port = port; - - 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); - - - gtk_print_backend_add_printer (backend, printer); - } 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); @@ -1163,11 +2109,89 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, cups_request_printer_info (cups_backend, gtk_printer_get_name (printer)); #endif - GTK_PRINTER_CUPS (printer)->state = state; - status_changed = gtk_printer_set_job_count (printer, job_count); - status_changed |= gtk_printer_set_location (printer, location); - status_changed |= gtk_printer_set_description (printer, description); - status_changed |= gtk_printer_set_state_message (printer, state_msg); + GTK_PRINTER_CUPS (printer)->state = info->state; + 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); + + 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 ( N_("Paused ; Rejecting Jobs")); + if (info->is_paused && info->is_accepting_jobs) + /* Translators: this is a printer status. */ + tmp_msg2 = g_strdup ( N_("Paused")); + if (!info->is_paused && !info->is_accepting_jobs) + /* Translators: this is a printer status. */ + tmp_msg2 = g_strdup ( N_("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 != NULL)) + { + 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 = g_strdup_printf (printer_strings[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 (strlen (info->state_msg) == 0) + { + g_free (info->state_msg); + info->state_msg = reason_msg_desc; + reason_msg_desc = NULL; + } + else + { + gchar *tmp_msg = NULL; + tmp_msg = g_strjoin (" ; ", info->state_msg, + reason_msg_desc, NULL); + g_free (info->state_msg); + info->state_msg = tmp_msg; + } + } + if (reason_msg_desc != NULL) + g_free (reason_msg_desc); + } + + 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 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"); if (status_changed) g_signal_emit_by_name (GTK_PRINT_BACKEND (backend), @@ -1175,7 +2199,9 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, /* The ref is held by GtkPrintBackend, in add_printer() */ g_object_unref (printer); - + g_free (info->state_msg); + g_slice_free (PrinterSetupInfo, info); + if (attr == NULL) break; } @@ -1188,19 +2214,56 @@ cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend, g_list_free (removed_printer_checklist); list_has_changed = TRUE; } - + done: if (list_has_changed) g_signal_emit_by_name (backend, "printer-list-changed"); - + gtk_print_backend_set_list_done (backend); - GDK_THREADS_LEAVE (); + if (!cups_backend->got_default_printer && remote_default_printer != NULL) + { + cups_backend->default_printer = g_strdup (remote_default_printer); + cups_backend->got_default_printer = TRUE; + g_free (remote_default_printer); + + 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); + } + } + } + + gdk_threads_leave (); +} + +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; static const char * const pattrs[] = /* Attributes we're interested in */ { @@ -1210,22 +2273,50 @@ cups_request_printer_list (GtkPrintBackendCups *cups_backend) "printer-location", "printer-info", "printer-state-message", + "printer-state-reasons", "printer-state", - "queued-job-count" + "queued-job-count", + "printer-is-accepting-jobs", + "job-sheets-supported", + "job-sheets-default", + "printer-type", + "auth-info-required", + "number-up-default" }; - - if (cups_backend->list_printers_pending || - !cups_backend->got_default_printer) + + 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 = gdk_threads_add_timeout (200, + (GSourceFunc) cups_request_printer_list, + cups_backend); + } + 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 (NULL, - GTK_CUPS_POST, - CUPS_GET_PRINTERS, - NULL, - NULL, - NULL); + 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 (pattrs), @@ -1246,12 +2337,16 @@ 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); + if (cups_backend->list_printers_poll == 0) { - cups_request_printer_list (cups_backend); - cups_backend->list_printers_poll = gdk_threads_add_timeout (3000, - (GSourceFunc) cups_request_printer_list, - backend); + if (cups_request_printer_list (cups_backend)) + cups_backend->list_printers_poll = gdk_threads_add_timeout (50, + (GSourceFunc) cups_request_printer_list, + backend); } } @@ -1277,16 +2372,16 @@ cups_request_ppd_cb (GtkPrintBackendCups *print_backend, GtkCupsResult *result, GetPPDData *data) { - ipp_t *response; GtkPrinter *printer; - GDK_THREADS_ENTER (); + gdk_threads_enter (); 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--; if (gtk_cups_result_is_error (result)) { @@ -1299,26 +2394,26 @@ cups_request_ppd_cb (GtkPrintBackendCups *print_backend, { gtk_printer_set_has_details (printer, TRUE); success = TRUE; - } - + } + g_signal_emit_by_name (printer, "details-acquired", success); goto done; } - response = gtk_cups_result_get_response (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); + gtk_printer_set_has_details (printer, TRUE); g_signal_emit_by_name (printer, "details-acquired", TRUE); done: - GDK_THREADS_LEAVE (); + gdk_threads_leave (); } -static void +static gboolean cups_request_ppd (GtkPrinter *printer) { GError *error; @@ -1338,19 +2433,54 @@ cups_request_ppd (GtkPrinter *printer) GTK_NOTE (PRINTING, g_print ("CUPS Backend: %s\n", G_STRFUNC)); - http = httpConnectEncrypt (cups_printer->hostname, + if (cups_printer->remote) + { + 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 = gdk_threads_add_timeout (200, + (GSourceFunc) cups_request_ppd, + printer); + } + 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, + fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", + &ppd_filename, &error); -#ifdef G_ENABLE_DEBUG +#ifdef G_ENABLE_DEBUG /* If we are debugging printing don't delete the tmp files */ - if (!(gtk_debug_flags & GTK_DEBUG_PRINTING)) + if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING)) unlink (ppd_filename); #else unlink (ppd_filename); @@ -1358,8 +2488,8 @@ cups_request_ppd (GtkPrinter *printer) if (error != NULL) { - GTK_NOTE (PRINTING, - g_warning ("CUPS Backend: Failed to create temp file, %s\n", + GTK_NOTE (PRINTING, + g_warning ("CUPS Backend: Failed to create temp file, %s\n", error->message)); g_error_free (error); httpClose (http); @@ -1367,9 +2497,9 @@ cups_request_ppd (GtkPrinter *printer) g_free (data); g_signal_emit_by_name (printer, "details-acquired", FALSE); - return; + return FALSE; } - + data->http = http; fchmod (fd, S_IRUSR | S_IWUSR); data->ppd_io = g_io_channel_unix_new (fd); @@ -1378,37 +2508,42 @@ cups_request_ppd (GtkPrinter *printer) data->printer = g_object_ref (printer); - resource = g_strdup_printf ("/printers/%s.ppd", + resource = g_strdup_printf ("/printers/%s.ppd", gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer))); - request = gtk_cups_request_new (data->http, - GTK_CUPS_GET, - 0, - data->ppd_io, - cups_printer->hostname, - resource); + 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_NOTE (PRINTING, g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename)); - g_free (resource); - g_free (ppd_filename); cups_printer->reading_ppd = TRUE; - - print_backend = gtk_printer_get_backend (printer); + 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", + ".lpoptions", ".cups/lpoptions" }; @@ -1418,7 +2553,7 @@ cups_parse_user_default_printer (const char *filename, { FILE *fp; char line[1024], *lineptr, *defname = NULL; - + if ((fp = g_fopen (filename, "r")) == NULL) return; @@ -1461,11 +2596,11 @@ cups_get_user_default_printer (char **printer_name) cups_parse_user_default_printer (lpoptions_locations[i], printer_name); } - else + else { char *filename; - filename = g_build_filename (g_get_home_dir (), + filename = g_build_filename (g_get_home_dir (), lpoptions_locations[i], NULL); cups_parse_user_default_printer (filename, printer_name); g_free (filename); @@ -1537,7 +2672,7 @@ cups_get_user_options (const char *printer_name, 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, @@ -1547,7 +2682,7 @@ cups_get_user_options (const char *printer_name, { char *filename; - filename = g_build_filename (g_get_home_dir (), + filename = g_build_filename (g_get_home_dir (), lpoptions_locations[i], NULL); num_options = cups_parse_user_options (filename, printer_name, num_options, options); @@ -1558,70 +2693,138 @@ cups_get_user_options (const char *printer_name, 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_request_default_printer_cb (GtkPrintBackendCups *print_backend, - GtkCupsResult *result, - gpointer user_data) +cups_get_default_printer (GtkPrintBackendCups *backend) { - ipp_t *response; - ipp_attribute_t *attr; + GtkPrintBackendCups *cups_backend; - response = gtk_cups_result_get_response (result); - - if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL) - print_backend->default_printer = g_strdup (attr->values[0].string.text); + cups_backend = backend; - print_backend->got_default_printer = TRUE; + if (cups_backend->cups_connection_test == NULL) + cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL); - /* 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); + if (cups_backend->default_printer_poll == 0) + { + if (cups_request_default_printer (cups_backend)) + cups_backend->default_printer_poll = gdk_threads_add_timeout (200, + (GSourceFunc) cups_request_default_printer, + backend); + } } +/* This function gets default printer from local settings.*/ static void -cups_request_default_printer (GtkPrintBackendCups *print_backend) +cups_get_local_default_printer (GtkPrintBackendCups *backend) { - GtkCupsRequest *request; const char *str; char *name = NULL; if ((str = g_getenv ("LPDEST")) != NULL) { - print_backend->default_printer = g_strdup (str); - print_backend->got_default_printer = TRUE; + backend->default_printer = g_strdup (str); + backend->got_default_printer = TRUE; return; } else if ((str = g_getenv ("PRINTER")) != NULL && strcmp (str, "lp") != 0) { - print_backend->default_printer = g_strdup (str); - print_backend->got_default_printer = TRUE; + backend->default_printer = g_strdup (str); + backend->got_default_printer = TRUE; return; } - - /* Figure out user setting for default printer */ + + /* Figure out user setting for default printer */ cups_get_user_default_printer (&name); if (name != NULL) { - print_backend->default_printer = name; - print_backend->got_default_printer = TRUE; - return; + 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; + + gdk_threads_enter (); + + 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); + } } - request = gtk_cups_request_new (NULL, - GTK_CUPS_POST, - CUPS_GET_DEFAULT, - NULL, - NULL, - NULL); - + /* 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); + + gdk_threads_leave (); +} + +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 @@ -1630,18 +2833,33 @@ cups_printer_request_details (GtkPrinter *printer) GtkPrinterCups *cups_printer; cups_printer = GTK_PRINTER_CUPS (printer); - if (!cups_printer->reading_ppd && + if (!cups_printer->reading_ppd && gtk_printer_cups_get_ppd (cups_printer) == NULL) - cups_request_ppd (printer); + { + if (cups_printer->remote) + { + if (cups_printer->get_remote_ppd_poll == 0) + { + cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname); + + if (cups_request_ppd (printer)) + cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50, + (GSourceFunc) cups_request_ppd, + printer); + } + } + else + cups_request_ppd (printer); + } } static char * -ppd_text_to_utf8 (ppd_file_t *ppd_file, +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); @@ -1670,7 +2888,7 @@ ppd_text_to_utf8 (ppd_file_t *ppd_file, { encoding = "WINDOWS-1252"; } - else + else { /* Fallback, try iso-8859-1... */ encoding = "ISO-8859-1"; @@ -1684,7 +2902,7 @@ ppd_text_to_utf8 (ppd_file_t *ppd_file, g_warning ("CUPS Backend: Unable to convert PPD text\n")); res = g_strdup ("???"); } - + return res; } @@ -1698,6 +2916,8 @@ static const struct { { "MediaType", N_("Paper Type") }, { "InputSlot", N_("Paper Source") }, { "OutputBin", N_("Output Tray") }, + { "Resolution", N_("Resolution") }, + { "PreFilter", N_("GhostScript pre-filtering") }, }; @@ -1707,24 +2927,63 @@ static const struct { const char *translation; } cups_choice_translations[] = { { "Duplex", "None", N_("One Sided") }, + /* Translators: this is an option of "Two Sided" */ + { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") }, + /* Translators: this is an option of "Two Sided" */ + { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") }, + /* Translators: this is an option of "Paper Source" */ { "InputSlot", "Auto", N_("Auto Select") }, + /* Translators: this is an option of "Paper Source" */ { "InputSlot", "AutoSelect", N_("Auto Select") }, + /* Translators: this is an option of "Paper Source" */ { "InputSlot", "Default", N_("Printer Default") }, + /* Translators: this is an option of "Paper Source" */ { "InputSlot", "None", N_("Printer Default") }, + /* Translators: this is an option of "Paper Source" */ { "InputSlot", "PrinterDefault", N_("Printer Default") }, + /* Translators: this is an option of "Paper Source" */ { "InputSlot", "Unspecified", N_("Auto Select") }, + /* Translators: this is an option of "Resolution" */ + { "Resolution", "default", N_("Printer Default") }, + /* Translators: this is an option of "GhostScript" */ + { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") }, + /* Translators: this is an option of "GhostScript" */ + { "PreFilter", "Level1", N_("Convert to PS level 1") }, + /* Translators: this is an option of "GhostScript" */ + { "PreFilter", "Level2", N_("Convert to PS level 2") }, + /* Translators: this is an option of "GhostScript" */ + { "PreFilter", "No", N_("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", N_("Miscellaneous") }, }; static const struct { const char *ppd_keyword; const char *name; -} option_names[] = { +} ppd_option_names[] = { {"Duplex", "gtk-duplex" }, {"MediaType", "gtk-paper-type"}, {"InputSlot", "gtk-paper-source"}, {"OutputBin", "gtk-output-tray"}, }; +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", @@ -1755,7 +3014,7 @@ static const char *color_group_whitelist[] = { "FPColorWise5", "HPColorOptionsPanel", }; - + /* keep sorted when changing */ static const char *image_quality_option_whitelist[] = { "BRDocument", @@ -1825,19 +3084,19 @@ static const char *finishing_group_whitelist[] = { /* keep sorted when changing */ static const char *cups_option_blacklist[] = { "Collate", - "Copies", + "Copies", "OutputOrder", "PageRegion", "PageSize", }; static char * -get_option_text (ppd_file_t *ppd_file, +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) @@ -1848,18 +3107,18 @@ get_option_text (ppd_file_t *ppd_file, /* 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, +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 && @@ -1870,18 +3129,18 @@ get_choice_text (ppd_file_t *ppd_file, } static gboolean -group_has_option (ppd_group_t *group, +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)) @@ -1911,11 +3170,7 @@ value_is_off (const char *value) static char * ppd_group_name (ppd_group_t *group) { -#if CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR == 1 && CUPS_VERSION_PATCH >= 18) return group->name; -#else - return group->text; -#endif } static int @@ -1943,7 +3198,7 @@ available_choices (ppd_file_t *ppd, installed_options = NULL; for (i = 0; i < ppd->num_groups; i++) { - char *name; + char *name; name = ppd_group_name (&ppd->groups[i]); if (strcmp (name, "InstallableOptions") == 0) @@ -2065,7 +3320,7 @@ available_choices (ppd_file_t *ppd, if (!found_auto) add_auto = 1; } - + if (available) { *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto); @@ -2077,12 +3332,12 @@ available_choices (ppd_file_t *ppd, (*available)[i++] = &option->choices[j]; } - if (add_auto) + if (add_auto) (*available)[i++] = NULL; } g_free (conflicts); - + return option->num_choices - num_conflicts + add_auto; } @@ -2096,25 +3351,22 @@ create_pickone_option (ppd_file_t *ppd_file, char *label; int n_choices; int i; -#ifdef HAVE_CUPS_API_1_2 ppd_coption_t *coption; -#endif 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 + + /* 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); -#ifdef HAVE_CUPS_API_1_2 coption = ppdFindCustomOption (ppd_file, ppd_option->keyword); if (coption) @@ -2148,17 +3400,17 @@ create_pickone_option (ppd_file_t *ppd_file, GTK_PRINTER_OPTION_TYPE_PICKONE_STRING); break; #ifdef PRINT_IGNORED_OPTIONS - case PPD_CUSTOM_POINTS: + 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: + case PPD_CUSTOM_INVCURVE: g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported"); break; #endif - default: + default: break; } } @@ -2167,13 +3419,12 @@ create_pickone_option (ppd_file_t *ppd_file, g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported"); #endif } -#endif /* HAVE_CUPS_API_1_2 */ 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++) { @@ -2189,7 +3440,18 @@ create_pickone_option (ppd_file_t *ppd_file, option->choices_display[i] = get_choice_text (ppd_file, available[i]); } } - gtk_printer_option_set (option, ppd_option->defchoice); + + 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 @@ -2211,7 +3473,7 @@ create_boolean_option (ppd_file_t *ppd_file, 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-")); @@ -2221,13 +3483,13 @@ create_boolean_option (ppd_file_t *ppd_file, 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 @@ -2240,19 +3502,35 @@ create_boolean_option (ppd_file_t *ppd_file, } static gchar * -get_option_name (const gchar *keyword) +get_ppd_option_name (const gchar *keyword) { int i; - for (i = 0; i < G_N_ELEMENTS (option_names); i++) - if (strcmp (option_names[i].ppd_keyword, keyword) == 0) - return g_strdup (option_names[i].name); + 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, +strptr_cmp (const void *a, const void *b) { char **aa = (char **)a; @@ -2262,8 +3540,8 @@ strptr_cmp (const void *a, static gboolean -string_in_table (gchar *str, - const gchar *table[], +string_in_table (gchar *str, + const gchar *table[], gint table_len) { return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL; @@ -2280,11 +3558,12 @@ handle_option (GtkPrinterOptionSet *set, { GtkPrinterOption *option; char *name; + int i; if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist)) return; - name = get_option_name (ppd_option->keyword); + name = get_ppd_option_name (ppd_option->keyword); option = NULL; if (ppd_option->ui == PPD_UI_PICKONE) @@ -2298,8 +3577,8 @@ handle_option (GtkPrinterOptionSet *set, #ifdef PRINT_IGNORED_OPTIONS else g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text); -#endif - +#endif + if (option) { char *name; @@ -2328,14 +3607,24 @@ handle_option (GtkPrinterOptionSet *set, } else { - option->group = g_strdup (toplevel_group->text); + 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 (_(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 (name); } @@ -2348,12 +3637,12 @@ handle_group (GtkPrinterOptionSet *set, { gint i; 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); @@ -2362,6 +3651,23 @@ handle_group (GtkPrinterOptionSet *set, } +#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 + static GtkPrinterOptionSet * cups_printer_get_options (GtkPrinter *printer, GtkPrintSettings *settings, @@ -2375,30 +3681,40 @@ cups_printer_get_options (GtkPrinter *printer, 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 *cover[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" }; - char *cover_display[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),}; + 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 */ - 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"); - set_option_from_settings (option, settings); - gtk_printer_option_set_add (set, option); - g_object_unref (option); - for (i = 0; i < G_N_ELEMENTS(prio_display); i++) prio_display[i] = _(prio_display[i]); - - option = gtk_printer_option_new ("gtk-job-prio", "Job Priority", GTK_PRINTER_OPTION_TYPE_PICKONE); + + /* 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"); @@ -2406,45 +3722,147 @@ cups_printer_get_options (GtkPrinter *printer, gtk_printer_option_set_add (set, option); g_object_unref (option); - option = gtk_printer_option_new ("gtk-billing-info", "Billing Info", GTK_PRINTER_OPTION_TYPE_STRING); + /* 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); - for (i = 0; i < G_N_ELEMENTS(cover_display); i++) - cover_display[i] = _(cover_display[i]); - - option = gtk_printer_option_new ("gtk-cover-before", "Before", GTK_PRINTER_OPTION_TYPE_PICKONE); - gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover), - cover, cover_display); - gtk_printer_option_set (option, "none"); - 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); - option = gtk_printer_option_new ("gtk-cover-after", "After", GTK_PRINTER_OPTION_TYPE_PICKONE); - gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover), - cover, cover_display); - gtk_printer_option_set (option, "none"); - set_option_from_settings (option, settings); - gtk_printer_option_set_add (set, option); - g_object_unref (option); + if (backend != NULL && printer != NULL) + { + char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" }; + /* Translators, these strings are names for various 'standard' cover + * pages that the printing system may support. + */ + char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("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", _("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", _("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); + } - option = gtk_printer_option_new ("gtk-print-time", "Print at", GTK_PRINTER_OPTION_TYPE_PICKONE); + num_of_covers = backend->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 (backend->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 (backend->covers[i]); + } + + for (i = 0; i < num_of_covers; i++) + cover_display_translated[i] = _(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", _("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", _("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", _("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); - - option = gtk_printer_option_new ("gtk-print-time-text", "Print at time", GTK_PRINTER_OPTION_TYPE_STRING); + + /* 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", _("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) @@ -2459,7 +3877,7 @@ cups_printer_get_options (GtkPrinter *printer, option = ppdFindOption (ppd_file, "PageSize"); ppd_name = gtk_paper_size_get_ppd_name (paper_size); - + if (ppd_name) strncpy (option->defchoice, ppd_name, PPD_MAX_NAME); else @@ -2470,6 +3888,10 @@ cups_printer_get_options (GtkPrinter *printer, 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 %sx%s"), width, height); strncpy (option->defchoice, custom_name, PPD_MAX_NAME); g_free (custom_name); @@ -2484,18 +3906,102 @@ cups_printer_get_options (GtkPrinter *printer, for (i = 0; i < num_opts; i++) { - if (STRING_IN_TABLE (opts->name, cups_option_blacklist)) + if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist)) continue; - name = get_option_name (opts[i].name); - option = gtk_printer_option_set_lookup (set, name); - if (option) - gtk_printer_option_set (option, opts[i].value); + 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 (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 + { + 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 + /* TRANSLATORS: this this the ICC color profile to use for this job */ + option = gtk_printer_option_new ("colord-profile", + _("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, _("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; } @@ -2506,13 +4012,13 @@ mark_option_from_set (GtkPrinterOptionSet *set, ppd_option_t *ppd_option) { GtkPrinterOption *option; - char *name = get_option_name (ppd_option->keyword); + 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); } @@ -2541,7 +4047,7 @@ set_conflicts_from_option (GtkPrinterOptionSet *set, if (ppd_option->conflicted) { - name = get_option_name (ppd_option->keyword); + name = get_ppd_option_name (ppd_option->keyword); option = gtk_printer_option_set_lookup (set, name); if (option) @@ -2550,7 +4056,7 @@ set_conflicts_from_option (GtkPrinterOptionSet *set, else g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword); #endif - + g_free (name); } } @@ -2576,7 +4082,7 @@ cups_printer_mark_conflicts (GtkPrinter *printer, 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) @@ -2594,7 +4100,7 @@ cups_printer_mark_conflicts (GtkPrinter *printer, for (i = 0; i < ppd_file->num_groups; i++) set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]); } - + return num_conflicts > 0; } @@ -2627,8 +4133,8 @@ map_settings_to_option (GtkPrinterOption *option, name = g_strdup_printf ("cups-%s", cups_name); cups_value = gtk_print_settings_get (settings, name); g_free (name); - - if (cups_value != NULL) + + if (cups_value != NULL) { gtk_printer_option_set (option, cups_value); return; @@ -2756,7 +4262,7 @@ set_option_from_settings (GtkPrinterOption *option, { const char *cups_value; char *value; - + if (settings == NULL) return; @@ -2779,14 +4285,29 @@ set_option_from_settings (GtkPrinterOption *option, gtk_printer_option_set (option, cups_value); else { - int res = gtk_print_settings_get_resolution (settings); - if (res != 0) + 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') { - value = g_strdup_printf ("%ddpi", res); - gtk_printer_option_set (option, value); - g_free (value); - } - } + 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), @@ -2796,48 +4317,53 @@ set_option_from_settings (GtkPrinterOption *option, map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map), settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up"); } + 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"); + } 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 @@ -2864,10 +4390,19 @@ foreach_option_get_settings (GtkPrinterOption *option, settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode"); else if (strcmp (option->name, "cups-Resolution") == 0) { - int res = atoi (value); - /* TODO: What if resolution is on XXXxYYYdpi form? */ - if (res != 0) - gtk_print_settings_set_resolution (settings, res); + 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) @@ -2876,6 +4411,9 @@ foreach_option_get_settings (GtkPrinterOption *option, 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"); + 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"); 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) @@ -2892,6 +4430,87 @@ foreach_option_get_settings (GtkPrinterOption *option, 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, @@ -2904,11 +4523,11 @@ cups_printer_get_settings_from_options (GtkPrinter *printer, data.options = options; data.settings = settings; data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer)); - + if (data.ppd_file != NULL) { GtkPrinterOption *cover_before, *cover_after; - + gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data); cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before"); @@ -2922,8 +4541,21 @@ cups_printer_get_settings_from_options (GtkPrinter *printer, 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) - gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time); + { + 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"); } @@ -2935,44 +4567,49 @@ cups_printer_prepare_for_print (GtkPrinter *printer, 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; - print_job->print_pages = gtk_print_settings_get_print_pages (settings); - print_job->page_ranges = NULL; - print_job->num_page_ranges = 0; - - if (print_job->print_pages == GTK_PRINT_PAGES_RANGES) - print_job->page_ranges = - gtk_print_settings_get_page_ranges (settings, - &print_job->num_page_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); if (gtk_print_settings_get_collate (settings)) gtk_print_settings_set (settings, "cups-Collate", "True"); - print_job->collate = FALSE; + gtk_print_job_set_collate (print_job, FALSE); if (gtk_print_settings_get_reverse (settings)) gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse"); - print_job->reverse = FALSE; + gtk_print_job_set_reverse (print_job, FALSE); if (gtk_print_settings_get_n_copies (settings) > 1) gtk_print_settings_set_int (settings, "cups-copies", - gtk_print_settings_get_n_copies (settings)); - print_job->num_copies = 1; + gtk_print_settings_get_n_copies (settings)); + gtk_print_job_set_num_copies (print_job, 1); scale = gtk_print_settings_get_scale (settings); - print_job->scale = 1.0; if (scale != 100.0) - print_job->scale = 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"); - print_job->page_set = GTK_PAGE_SET_ALL; + 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); @@ -2991,7 +4628,80 @@ cups_printer_prepare_for_print (GtkPrinter *printer, g_free (custom_name); } - print_job->rotate_to_orientation = TRUE; + 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); + } + + 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 GList * @@ -2999,11 +4709,7 @@ cups_printer_list_papers (GtkPrinter *printer) { 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; GList *l; int i; @@ -3012,36 +4718,12 @@ cups_printer_list_papers (GtkPrinter *printer) return NULL; l = NULL; - + for (i = 0; i < ppd_file->num_sizes; i++) { size = &ppd_file->sizes[i]; - 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); + page_setup = create_page_setup (ppd_file, size); l = g_list_prepend (l, page_setup); } @@ -3049,7 +4731,30 @@ cups_printer_list_papers (GtkPrinter *printer) return g_list_reverse (l); } -static void +static GtkPageSetup * +cups_printer_get_default_page_size (GtkPrinter *printer) +{ + ppd_file_t *ppd_file; + ppd_size_t *size; + ppd_option_t *option; + + + ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer)); + if (ppd_file == NULL) + return NULL; + + option = ppdFindOption (ppd_file, "PageSize"); + if (option == NULL) + return NULL; + + size = ppdPageSize (ppd_file, option->defchoice); + if (size == NULL) + return NULL; + + return create_page_setup (ppd_file, size); +} + +static gboolean cups_printer_get_hard_margins (GtkPrinter *printer, gdouble *top, gdouble *bottom, @@ -3060,12 +4765,14 @@ cups_printer_get_hard_margins (GtkPrinter *printer, ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer)); if (ppd_file == NULL) - return; + return FALSE; *left = ppd_file->custom_margins[0]; *bottom = ppd_file->custom_margins[1]; *right = ppd_file->custom_margins[2]; *top = ppd_file->custom_margins[3]; + + return TRUE; } static GtkPrintCapabilities @@ -3075,5 +4782,6 @@ cups_printer_get_capabilities (GtkPrinter *printer) GTK_PRINT_CAPABILITY_COPIES | GTK_PRINT_CAPABILITY_COLLATE | GTK_PRINT_CAPABILITY_REVERSE | + GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT | GTK_PRINT_CAPABILITY_NUMBER_UP; }