]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Remove sealed members from GtkPrintJob
[~andy/gtk] / modules / printbackends / cups / gtkprintbackendcups.c
1 /* GTK - The GIMP Toolkit
2  * gtkprintbackendcups.h: Default implementation of GtkPrintBackend 
3  * for the Common Unix Print System (CUPS)
4  * Copyright (C) 2006, 2007 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef __linux__
23 #define _GNU_SOURCE
24 #endif
25
26 #include "config.h"
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <stdlib.h>
32 #include <time.h>
33
34 #include <cups/cups.h>
35 #include <cups/language.h>
36 #include <cups/http.h>
37 #include <cups/ipp.h>
38 #include <errno.h>
39 #include <cairo.h>
40 #include <cairo-pdf.h>
41 #include <cairo-ps.h>
42
43 #include <glib/gstdio.h>
44 #include <glib/gi18n-lib.h>
45 #include <gmodule.h>
46
47 #include <gtk/gtk.h>
48 #include <gtk/gtkprintbackend.h>
49 #include <gtk/gtkunixprint.h>
50 #include <gtk/gtkprinter-private.h>
51
52 #include "gtkprintbackendcups.h"
53 #include "gtkprintercups.h"
54
55 #include "gtkcupsutils.h"
56
57
58 typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
59
60 #define GTK_PRINT_BACKEND_CUPS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
61 #define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
62 #define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
63
64 #define _CUPS_MAX_ATTEMPTS 10 
65 #define _CUPS_MAX_CHUNK_SIZE 8192
66
67 /* define this to see warnings about ignored ppd options */
68 #undef PRINT_IGNORED_OPTIONS
69
70 #define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
71 #define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].string.text;}
72
73 static GType print_backend_cups_type = 0;
74
75 typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
76                                                    GtkCupsResult   *result, 
77                                                    gpointer         user_data);
78
79 typedef enum 
80 {
81   DISPATCH_SETUP,
82   DISPATCH_REQUEST,
83   DISPATCH_SEND,
84   DISPATCH_CHECK,
85   DISPATCH_READ,
86   DISPATCH_ERROR
87 } GtkPrintCupsDispatchState;
88
89 typedef struct 
90 {
91   GSource source;
92
93   http_t *http;
94   GtkCupsRequest *request;
95   GPollFD *data_poll;
96   GtkPrintBackendCups *backend;
97   GtkPrintCupsResponseCallbackFunc callback;
98   gpointer                         callback_data;
99
100 } GtkPrintCupsDispatchWatch;
101
102 struct _GtkPrintBackendCupsClass
103 {
104   GtkPrintBackendClass parent_class;
105 };
106
107 struct _GtkPrintBackendCups
108 {
109   GtkPrintBackend parent_instance;
110
111   char *default_printer;
112   
113   guint list_printers_poll;
114   guint list_printers_pending : 1;
115   gint  list_printers_attempts;
116   guint got_default_printer   : 1;
117   guint default_printer_poll;
118   GtkCupsConnectionTest *cups_connection_test;
119   gint  reading_ppds;
120
121   char **covers;
122   int    number_of_covers;
123
124   GList      *requests;
125   GHashTable *auth;
126   gchar      *username;
127   gboolean    authentication_lock;
128 };
129
130 static GObjectClass *backend_parent_class;
131
132 static void                 gtk_print_backend_cups_class_init      (GtkPrintBackendCupsClass          *class);
133 static void                 gtk_print_backend_cups_init            (GtkPrintBackendCups               *impl);
134 static void                 gtk_print_backend_cups_finalize        (GObject                           *object);
135 static void                 gtk_print_backend_cups_dispose         (GObject                           *object);
136 static void                 cups_get_printer_list                  (GtkPrintBackend                   *print_backend);
137 static void                 cups_get_default_printer               (GtkPrintBackendCups               *print_backend);
138 static void                 cups_get_local_default_printer         (GtkPrintBackendCups               *print_backend);
139 static void                 cups_request_execute                   (GtkPrintBackendCups               *print_backend,
140                                                                     GtkCupsRequest                    *request,
141                                                                     GtkPrintCupsResponseCallbackFunc   callback,
142                                                                     gpointer                           user_data,
143                                                                     GDestroyNotify                     notify);
144 static void                 cups_printer_get_settings_from_options (GtkPrinter                        *printer,
145                                                                     GtkPrinterOptionSet               *options,
146                                                                     GtkPrintSettings                  *settings);
147 static gboolean             cups_printer_mark_conflicts            (GtkPrinter                        *printer,
148                                                                     GtkPrinterOptionSet               *options);
149 static GtkPrinterOptionSet *cups_printer_get_options               (GtkPrinter                        *printer,
150                                                                     GtkPrintSettings                  *settings,
151                                                                     GtkPageSetup                      *page_setup,
152                                                                     GtkPrintCapabilities               capabilities);
153 static void                 cups_printer_prepare_for_print         (GtkPrinter                        *printer,
154                                                                     GtkPrintJob                       *print_job,
155                                                                     GtkPrintSettings                  *settings,
156                                                                     GtkPageSetup                      *page_setup);
157 static GList *              cups_printer_list_papers               (GtkPrinter                        *printer);
158 static GtkPageSetup *       cups_printer_get_default_page_size     (GtkPrinter                        *printer);
159 static void                 cups_printer_request_details           (GtkPrinter                        *printer);
160 static gboolean             cups_request_default_printer           (GtkPrintBackendCups               *print_backend);
161 static gboolean             cups_request_ppd                       (GtkPrinter                        *printer);
162 static gboolean             cups_printer_get_hard_margins          (GtkPrinter                        *printer,
163                                                                     gdouble                           *top,
164                                                                     gdouble                           *bottom,
165                                                                     gdouble                           *left,
166                                                                     gdouble                           *right);
167 static GtkPrintCapabilities cups_printer_get_capabilities          (GtkPrinter                        *printer);
168 static void                 set_option_from_settings               (GtkPrinterOption                  *option,
169                                                                     GtkPrintSettings                  *setting);
170 static void                 cups_begin_polling_info                (GtkPrintBackendCups               *print_backend,
171                                                                     GtkPrintJob                       *job,
172                                                                     int                                job_id);
173 static gboolean             cups_job_info_poll_timeout             (gpointer                           user_data);
174 static void                 gtk_print_backend_cups_print_stream    (GtkPrintBackend                   *backend,
175                                                                     GtkPrintJob                       *job,
176                                                                     GIOChannel                        *data_io,
177                                                                     GtkPrintJobCompleteFunc            callback,
178                                                                     gpointer                           user_data,
179                                                                     GDestroyNotify                     dnotify);
180 static cairo_surface_t *    cups_printer_create_cairo_surface      (GtkPrinter                        *printer,
181                                                                     GtkPrintSettings                  *settings,
182                                                                     gdouble                            width,
183                                                                     gdouble                            height,
184                                                                     GIOChannel                        *cache_io);
185
186 static void                 gtk_print_backend_cups_set_password    (GtkPrintBackend                   *backend, 
187                                                                     gchar                            **auth_info_required,
188                                                                     gchar                            **auth_info);
189
190 void                        overwrite_and_free                      (gpointer                          data);
191 static gboolean             is_address_local                        (const gchar                      *address);
192 static gboolean             request_auth_info                       (gpointer                          data);
193
194 static void
195 gtk_print_backend_cups_register_type (GTypeModule *module)
196 {
197   const GTypeInfo print_backend_cups_info =
198   {
199     sizeof (GtkPrintBackendCupsClass),
200     NULL,               /* base_init */
201     NULL,               /* base_finalize */
202     (GClassInitFunc) gtk_print_backend_cups_class_init,
203     NULL,               /* class_finalize */
204     NULL,               /* class_data */
205     sizeof (GtkPrintBackendCups),
206     0,                  /* n_preallocs */
207     (GInstanceInitFunc) gtk_print_backend_cups_init
208   };
209
210   print_backend_cups_type = g_type_module_register_type (module,
211                                                          GTK_TYPE_PRINT_BACKEND,
212                                                          "GtkPrintBackendCups",
213                                                          &print_backend_cups_info, 0);
214 }
215
216 G_MODULE_EXPORT void 
217 pb_module_init (GTypeModule *module)
218 {
219   GTK_NOTE (PRINTING,
220             g_print ("CUPS Backend: Initializing the CUPS print backend module\n")); 
221
222   gtk_print_backend_cups_register_type (module);
223   gtk_printer_cups_register_type (module);
224 }
225
226 G_MODULE_EXPORT void 
227 pb_module_exit (void)
228 {
229
230 }
231   
232 G_MODULE_EXPORT GtkPrintBackend * 
233 pb_module_create (void)
234 {
235   return gtk_print_backend_cups_new ();
236 }
237
238 /*
239  * GtkPrintBackendCups
240  */
241 GType
242 gtk_print_backend_cups_get_type (void)
243 {
244   return print_backend_cups_type;
245 }
246
247 /**
248  * gtk_print_backend_cups_new:
249  *
250  * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
251  * implements the #GtkPrintBackend interface with direct access to
252  * the filesystem using Unix/Linux API calls
253  *
254  * Return value: the new #GtkPrintBackendCups object
255  */
256 GtkPrintBackend *
257 gtk_print_backend_cups_new (void)
258 {
259   GTK_NOTE (PRINTING,
260             g_print ("CUPS Backend: Creating a new CUPS print backend object\n"));
261
262   return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
263 }
264
265 static void
266 gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
267 {
268   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
269   GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
270
271   backend_parent_class = g_type_class_peek_parent (class);
272
273   gobject_class->finalize = gtk_print_backend_cups_finalize;
274   gobject_class->dispose = gtk_print_backend_cups_dispose;
275
276   backend_class->request_printer_list = cups_get_printer_list; 
277   backend_class->print_stream = gtk_print_backend_cups_print_stream;
278   backend_class->printer_request_details = cups_printer_request_details;
279   backend_class->printer_create_cairo_surface = cups_printer_create_cairo_surface;
280   backend_class->printer_get_options = cups_printer_get_options;
281   backend_class->printer_mark_conflicts = cups_printer_mark_conflicts;
282   backend_class->printer_get_settings_from_options = cups_printer_get_settings_from_options;
283   backend_class->printer_prepare_for_print = cups_printer_prepare_for_print;
284   backend_class->printer_list_papers = cups_printer_list_papers;
285   backend_class->printer_get_default_page_size = cups_printer_get_default_page_size;
286   backend_class->printer_get_hard_margins = cups_printer_get_hard_margins;
287   backend_class->printer_get_capabilities = cups_printer_get_capabilities;
288   backend_class->set_password = gtk_print_backend_cups_set_password;
289 }
290
291 static cairo_status_t
292 _cairo_write_to_cups (void                *closure,
293                       const unsigned char *data,
294                       unsigned int         length)
295 {
296   GIOChannel *io = (GIOChannel *)closure;
297   gsize written;
298   GError *error;
299
300   error = NULL;
301
302   GTK_NOTE (PRINTING,
303             g_print ("CUPS Backend: Writing %i byte chunk to temp file\n", length));
304
305   while (length > 0) 
306     {
307       g_io_channel_write_chars (io, (gchar *)data, length, &written, &error);
308
309       if (error != NULL)
310         {
311           GTK_NOTE (PRINTING,
312                     g_print ("CUPS Backend: Error writing to temp file, %s\n", 
313                              error->message));
314
315           g_error_free (error);
316           return CAIRO_STATUS_WRITE_ERROR;
317         }    
318
319       GTK_NOTE (PRINTING,
320                 g_print ("CUPS Backend: Wrote %"G_GSIZE_FORMAT" bytes to temp file\n", written));
321
322       data += written;
323       length -= written;
324     }
325
326   return CAIRO_STATUS_SUCCESS;
327 }
328
329 static cairo_surface_t *
330 cups_printer_create_cairo_surface (GtkPrinter       *printer,
331                                    GtkPrintSettings *settings,
332                                    gdouble           width, 
333                                    gdouble           height,
334                                    GIOChannel       *cache_io)
335 {
336   cairo_surface_t *surface; 
337   ppd_file_t      *ppd_file = NULL;
338   ppd_attr_t      *ppd_attr = NULL;
339   ppd_attr_t      *ppd_attr_res = NULL;
340   ppd_attr_t      *ppd_attr_screen_freq = NULL;
341   ppd_attr_t      *ppd_attr_res_screen_freq = NULL;
342   gchar           *res_string = NULL;
343   int              level = 2;
344  
345   /* TODO: check if it is a ps or pdf printer */
346   
347   surface = cairo_ps_surface_create_for_stream  (_cairo_write_to_cups, cache_io, width, height);
348
349   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
350
351   if (ppd_file != NULL)
352     {
353       ppd_attr = ppdFindAttr (ppd_file, "LanguageLevel", NULL);
354
355       if (ppd_attr != NULL)
356         level = atoi (ppd_attr->value);
357
358       if (gtk_print_settings_get_resolution (settings) == 0)
359         {
360           ppd_attr_res = ppdFindAttr (ppd_file, "DefaultResolution", NULL);
361
362           if (ppd_attr_res != NULL)
363             {
364               int res, res_x, res_y;
365
366               if (sscanf (ppd_attr_res->value, "%dx%ddpi", &res_x, &res_y) == 2)
367                 {
368                   if (res_x > 0 && res_y > 0)
369                     gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
370                 }
371               else if (sscanf (ppd_attr_res->value, "%ddpi", &res) == 1)
372                 {
373                   if (res > 0)
374                     gtk_print_settings_set_resolution (settings, res);
375                 }
376             }
377         }
378
379       res_string = g_strdup_printf ("%ddpi", 
380                                     gtk_print_settings_get_resolution (settings));
381       ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
382       g_free (res_string);
383
384       if (ppd_attr_res_screen_freq == NULL)
385         {
386           res_string = g_strdup_printf ("%dx%ddpi", 
387                                         gtk_print_settings_get_resolution_x (settings),
388                                         gtk_print_settings_get_resolution_y (settings));
389           ppd_attr_res_screen_freq = ppdFindAttr (ppd_file, "ResScreenFreq", res_string);
390           g_free (res_string);
391         }
392
393       ppd_attr_screen_freq = ppdFindAttr (ppd_file, "ScreenFreq", NULL);
394
395       if (ppd_attr_res_screen_freq != NULL && atof (ppd_attr_res_screen_freq->value) > 0.0)
396         gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_res_screen_freq->value));
397       else if (ppd_attr_screen_freq != NULL && atof (ppd_attr_screen_freq->value) > 0.0)
398         gtk_print_settings_set_printer_lpi (settings, atof (ppd_attr_screen_freq->value));
399     }
400
401   if (level == 2)
402     cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_2);
403
404   if (level == 3)
405     cairo_ps_surface_restrict_to_level (surface, CAIRO_PS_LEVEL_3);
406
407   cairo_surface_set_fallback_resolution (surface,
408                                          2.0 * gtk_print_settings_get_printer_lpi (settings),
409                                          2.0 * gtk_print_settings_get_printer_lpi (settings));
410
411   return surface;
412 }
413
414 typedef struct {
415   GtkPrintJobCompleteFunc callback;
416   GtkPrintJob *job;
417   gpointer user_data;
418   GDestroyNotify dnotify;
419 } CupsPrintStreamData;
420
421 static void
422 cups_free_print_stream_data (CupsPrintStreamData *data)
423 {
424   GTK_NOTE (PRINTING,
425             g_print ("CUPS Backend: %s\n", G_STRFUNC));
426
427   if (data->dnotify)
428     data->dnotify (data->user_data);
429   g_object_unref (data->job);
430   g_free (data);
431 }
432
433 static void
434 cups_print_cb (GtkPrintBackendCups *print_backend,
435                GtkCupsResult       *result,
436                gpointer             user_data)
437 {
438   GError *error = NULL;
439   CupsPrintStreamData *ps = user_data;
440
441   GDK_THREADS_ENTER ();
442
443   GTK_NOTE (PRINTING,
444             g_print ("CUPS Backend: %s\n", G_STRFUNC)); 
445
446   if (gtk_cups_result_is_error (result))
447     error = g_error_new_literal (gtk_print_error_quark (),
448                                  GTK_PRINT_ERROR_INTERNAL_ERROR,
449                                  gtk_cups_result_get_error_string (result));
450
451   if (ps->callback)
452     ps->callback (ps->job, ps->user_data, error);
453
454   if (error == NULL)
455     {
456       int job_id = 0;
457       ipp_attribute_t *attr;            /* IPP job-id attribute */
458       ipp_t *response = gtk_cups_result_get_response (result);
459
460       if ((attr = ippFindAttribute (response, "job-id", IPP_TAG_INTEGER)) != NULL)
461         job_id = attr->values[0].integer;
462
463       if (!gtk_print_job_get_track_print_status (ps->job) || job_id == 0)
464         gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
465       else
466         {
467           gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
468           cups_begin_polling_info (print_backend, ps->job, job_id);
469         }
470     } 
471   else
472     gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
473
474   
475   if (error)
476     g_error_free (error);
477
478   GDK_THREADS_LEAVE ();  
479 }
480
481 static void
482 add_cups_options (const gchar *key,
483                   const gchar *value,
484                   gpointer     user_data)
485 {
486   GtkCupsRequest *request = user_data;
487
488   if (!g_str_has_prefix (key, "cups-"))
489     return;
490
491   if (strcmp (value, "gtk-ignore-value") == 0)
492     return;
493   
494   key = key + strlen ("cups-");
495
496   gtk_cups_request_encode_option (request, key, value);
497 }
498
499 static void
500 gtk_print_backend_cups_print_stream (GtkPrintBackend         *print_backend,
501                                      GtkPrintJob             *job,
502                                      GIOChannel              *data_io,
503                                      GtkPrintJobCompleteFunc  callback,
504                                      gpointer                 user_data,
505                                      GDestroyNotify           dnotify)
506 {
507   GtkPrinterCups *cups_printer;
508   CupsPrintStreamData *ps;
509   GtkCupsRequest *request;
510   GtkPrintSettings *settings;
511   const gchar *title;
512   char  printer_absolute_uri[HTTP_MAX_URI];
513
514   GTK_NOTE (PRINTING,
515             g_print ("CUPS Backend: %s\n", G_STRFUNC));   
516
517   cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
518   settings = gtk_print_job_get_settings (job);
519
520   request = gtk_cups_request_new_with_username (NULL,
521                                                 GTK_CUPS_POST,
522                                                 IPP_PRINT_JOB,
523                                                 data_io,
524                                                 NULL,
525                                                 cups_printer->device_uri,
526                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
527
528 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
529   httpAssembleURIf (HTTP_URI_CODING_ALL,
530                     printer_absolute_uri,
531                     sizeof (printer_absolute_uri),
532                     "ipp",
533                     NULL,
534                     "localhost",
535                     ippPort (),
536                     "/printers/%s",
537                     gtk_printer_get_name (gtk_print_job_get_printer (job)));
538 #else
539   g_snprintf (printer_absolute_uri,
540               sizeof (printer_absolute_uri),
541               "ipp://localhost:%d/printers/%s",
542               ippPort (),
543               gtk_printer_get_name (gtk_print_job_get_printer (job)));
544 #endif
545
546   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, 
547                                    IPP_TAG_URI, "printer-uri",
548                                    NULL, printer_absolute_uri);
549
550   title = gtk_print_job_get_title (job);
551   if (title)
552     gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, 
553                                      IPP_TAG_NAME, "job-name", 
554                                      NULL, title);
555
556   gtk_print_settings_foreach (settings, add_cups_options, request);
557   
558   ps = g_new0 (CupsPrintStreamData, 1);
559   ps->callback = callback;
560   ps->user_data = user_data;
561   ps->dnotify = dnotify;
562   ps->job = g_object_ref (job);
563
564   request->need_auth_info = cups_printer->auth_info_required != NULL;
565   request->auth_info_required = g_strdupv (cups_printer->auth_info_required);
566
567   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
568                         request,
569                         (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
570                         ps,
571                         (GDestroyNotify)cups_free_print_stream_data);
572 }
573
574 void overwrite_and_free (gpointer data)
575 {
576   gchar *password = (gchar *) data;
577
578   if (password != NULL)
579     {
580       memset (password, 0, strlen (password));
581       g_free (password);
582     }
583 }
584
585 static void
586 gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
587 {
588   backend_cups->list_printers_poll = FALSE;  
589   backend_cups->got_default_printer = FALSE;  
590   backend_cups->list_printers_pending = FALSE;
591   backend_cups->list_printers_attempts = 0;
592   backend_cups->reading_ppds = 0;
593
594   backend_cups->requests = NULL;
595   backend_cups->auth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, overwrite_and_free);
596   backend_cups->authentication_lock = FALSE;
597
598   backend_cups->covers = NULL;
599   backend_cups->number_of_covers = 0;
600
601   backend_cups->default_printer_poll = 0;
602   backend_cups->cups_connection_test = NULL;
603
604   backend_cups->username = NULL;
605
606   cups_get_local_default_printer (backend_cups);
607 }
608
609 static void
610 gtk_print_backend_cups_finalize (GObject *object)
611 {
612   GtkPrintBackendCups *backend_cups;
613   
614   GTK_NOTE (PRINTING,
615             g_print ("CUPS Backend: finalizing CUPS backend module\n"));
616
617   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
618
619   g_free (backend_cups->default_printer);
620   backend_cups->default_printer = NULL;
621
622   g_strfreev (backend_cups->covers);
623   backend_cups->number_of_covers = 0;
624
625   gtk_cups_connection_test_free (backend_cups->cups_connection_test);
626   backend_cups->cups_connection_test = NULL;
627
628   g_hash_table_destroy (backend_cups->auth);
629
630   g_free (backend_cups->username);
631
632   backend_parent_class->finalize (object);
633 }
634
635 static void
636 gtk_print_backend_cups_dispose (GObject *object)
637 {
638   GtkPrintBackendCups *backend_cups;
639
640   GTK_NOTE (PRINTING,
641             g_print ("CUPS Backend: %s\n", G_STRFUNC));
642
643   backend_cups = GTK_PRINT_BACKEND_CUPS (object);
644
645   if (backend_cups->list_printers_poll > 0)
646     g_source_remove (backend_cups->list_printers_poll);
647   backend_cups->list_printers_poll = 0;
648   backend_cups->list_printers_attempts = 0;
649   
650   if (backend_cups->default_printer_poll > 0)
651     g_source_remove (backend_cups->default_printer_poll);
652   backend_cups->default_printer_poll = 0;
653
654   backend_parent_class->dispose (object);
655 }
656
657 static gboolean
658 is_address_local (const gchar *address)
659 {
660   if (address[0] == '/' ||
661       strcmp (address, "127.0.0.1") == 0 ||
662       strcmp (address, "[::1]") == 0)
663     return TRUE;
664   else
665     return FALSE;
666 }
667
668 #ifndef HAVE_CUPS_API_1_2
669 /* Included from CUPS library because of backward compatibility */
670 const char *
671 httpGetHostname(http_t *http,
672                 char   *s,
673                 int    slen)
674 {
675   struct hostent *host;
676
677   if (!s || slen <= 1)
678     return (NULL);
679
680   if (http)
681     {
682       if (http->hostname[0] == '/')
683         g_strlcpy (s, "localhost", slen);
684       else
685         g_strlcpy (s, http->hostname, slen);
686     }
687   else
688     {
689       if (gethostname (s, slen) < 0)
690         g_strlcpy (s, "localhost", slen);
691
692       if (!strchr (s, '.'))
693         {
694           if ((host = gethostbyname (s)) != NULL && host->h_name)
695             g_strlcpy (s, host->h_name, slen);
696         }
697     }
698   return (s);
699 }
700 #endif
701
702 static void
703 gtk_print_backend_cups_set_password (GtkPrintBackend  *backend,
704                                      gchar           **auth_info_required,
705                                      gchar           **auth_info)
706 {
707   GtkPrintBackendCups *cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
708   GList *l;
709   char   dispatch_hostname[HTTP_MAX_URI];
710   gchar *username = NULL;
711   gchar *hostname = NULL;
712   gchar *password = NULL;
713   gint   length;
714   gint   i;
715
716   length = g_strv_length (auth_info_required);
717
718   if (auth_info != NULL)
719     for (i = 0; i < length; i++)
720       {
721         if (g_strcmp0 (auth_info_required[i], "username") == 0)
722           username = g_strdup (auth_info[i]);
723         else if (g_strcmp0 (auth_info_required[i], "hostname") == 0)
724           hostname = g_strdup (auth_info[i]);
725         else if (g_strcmp0 (auth_info_required[i], "password") == 0)
726           password = g_strdup (auth_info[i]);
727       }
728
729   if (hostname != NULL && username != NULL && password != NULL)
730     {
731       gchar *key = g_strconcat (username, "@", hostname, NULL);
732       g_hash_table_insert (cups_backend->auth, key, g_strdup (password));
733       GTK_NOTE (PRINTING,
734                 g_print ("CUPS backend: storing password for %s\n", key));
735     }
736
737   g_free (cups_backend->username);
738   cups_backend->username = g_strdup (username);
739
740
741   for (l = cups_backend->requests; l; l = l->next)
742     {
743       GtkPrintCupsDispatchWatch *dispatch = l->data;
744
745       httpGetHostname (dispatch->request->http, dispatch_hostname, sizeof (dispatch_hostname));
746       if (is_address_local (dispatch_hostname))
747         strcpy (dispatch_hostname, "localhost");
748
749       if (dispatch->request->need_auth_info)
750         {
751           if (auth_info != NULL)
752             {
753               dispatch->request->auth_info = g_new0 (gchar *, length + 1);
754               for (i = 0; i < length; i++)
755                 dispatch->request->auth_info[i] = g_strdup (auth_info[i]);
756             }
757           dispatch->backend->authentication_lock = FALSE;
758           dispatch->request->need_auth_info = FALSE;
759         }
760       else if (dispatch->request->password_state == GTK_CUPS_PASSWORD_REQUESTED || auth_info == NULL)
761         {
762           overwrite_and_free (dispatch->request->password);
763           dispatch->request->password = g_strdup (password);
764           g_free (dispatch->request->username);
765           dispatch->request->username = g_strdup (username);
766           dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
767           dispatch->backend->authentication_lock = FALSE;
768         }
769     }
770 }
771
772 static gboolean
773 request_password (gpointer data)
774 {
775   GtkPrintCupsDispatchWatch *dispatch = data;
776   const gchar               *username;
777   gchar                     *password;
778   gchar                     *prompt = NULL;
779   gchar                     *key = NULL;
780   char                       hostname[HTTP_MAX_URI];
781   gchar                    **auth_info_required;
782   gchar                    **auth_info_default;
783   gchar                    **auth_info_display;
784   gboolean                  *auth_info_visible;
785   gint                       length = 3;
786   gint                       i;
787
788   if (dispatch->backend->authentication_lock)
789     return FALSE;
790
791   httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
792   if (is_address_local (hostname))
793     strcpy (hostname, "localhost");
794
795   if (dispatch->backend->username != NULL)
796     username = dispatch->backend->username;
797   else
798     username = cupsUser ();
799
800   auth_info_required = g_new0 (gchar*, length + 1);
801   auth_info_required[0] = g_strdup ("hostname");
802   auth_info_required[1] = g_strdup ("username");
803   auth_info_required[2] = g_strdup ("password");
804
805   auth_info_default = g_new0 (gchar*, length + 1);
806   auth_info_default[0] = g_strdup (hostname);
807   auth_info_default[1] = g_strdup (username);
808
809   auth_info_display = g_new0 (gchar*, length + 1);
810   auth_info_display[1] = g_strdup (_("Username:"));
811   auth_info_display[2] = g_strdup (_("Password:"));
812
813   auth_info_visible = g_new0 (gboolean, length + 1);
814   auth_info_visible[1] = TRUE;
815
816   key = g_strconcat (username, "@", hostname, NULL);
817   password = g_hash_table_lookup (dispatch->backend->auth, key);
818
819   if (password && dispatch->request->password_state != GTK_CUPS_PASSWORD_NOT_VALID)
820     {
821       GTK_NOTE (PRINTING,
822                 g_print ("CUPS backend: using stored password for %s\n", key));
823
824       overwrite_and_free (dispatch->request->password);
825       dispatch->request->password = g_strdup (password);
826       g_free (dispatch->request->username);
827       dispatch->request->username = g_strdup (username);
828       dispatch->request->password_state = GTK_CUPS_PASSWORD_HAS;
829     }
830   else
831     {
832       const char *job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
833       const char *printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
834       char *printer_name = NULL;
835
836       if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
837         printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
838
839       if (dispatch->request->password_state == GTK_CUPS_PASSWORD_NOT_VALID)
840         g_hash_table_remove (dispatch->backend->auth, key);
841
842       dispatch->request->password_state = GTK_CUPS_PASSWORD_REQUESTED;
843
844       dispatch->backend->authentication_lock = TRUE;
845
846       switch (dispatch->request->ipp_request->request.op.operation_id)
847         {
848           case IPP_PRINT_JOB:
849             if (job_title != NULL && printer_name != NULL)
850               prompt = g_strdup_printf ( _("Authentication is required to print document '%s' on printer %s"), job_title, printer_name);
851             else
852               prompt = g_strdup_printf ( _("Authentication is required to print a document on %s"), hostname);
853             break;
854           case IPP_GET_JOB_ATTRIBUTES:
855             if (job_title != NULL)
856               prompt = g_strdup_printf ( _("Authentication is required to get attributes of job '%s'"), job_title);
857             else
858               prompt = g_strdup ( _("Authentication is required to get attributes of a job"));
859             break;
860           case IPP_GET_PRINTER_ATTRIBUTES:
861             if (printer_name != NULL)
862               prompt = g_strdup_printf ( _("Authentication is required to get attributes of printer %s"), printer_name);
863             else
864               prompt = g_strdup ( _("Authentication is required to get attributes of a printer"));
865             break;
866           case CUPS_GET_DEFAULT:
867             prompt = g_strdup_printf ( _("Authentication is required to get default printer of %s"), hostname);
868             break;
869           case CUPS_GET_PRINTERS:
870             prompt = g_strdup_printf ( _("Authentication is required to get printers from %s"), hostname);
871             break;
872           default:
873             /* work around gcc warning about 0 not being a value for this enum */
874             if (dispatch->request->ipp_request->request.op.operation_id == 0)
875               prompt = g_strdup_printf ( _("Authentication is required to get a file from %s"), hostname);
876             else
877               prompt = g_strdup_printf ( _("Authentication is required on %s"), hostname);
878             break;
879         }
880
881       g_free (printer_name);
882
883       g_signal_emit_by_name (dispatch->backend, "request-password", 
884                              auth_info_required, auth_info_default, auth_info_display, auth_info_visible, prompt);
885
886       g_free (prompt);
887     }
888
889   for (i = 0; i < length; i++)
890     {
891       g_free (auth_info_required[i]);
892       g_free (auth_info_default[i]);
893       g_free (auth_info_display[i]);
894     }
895
896   g_free (auth_info_required);
897   g_free (auth_info_default);
898   g_free (auth_info_display);
899   g_free (auth_info_visible);
900   g_free (key);
901
902   return FALSE;
903 }
904
905 static void
906 cups_dispatch_add_poll (GSource *source)
907 {
908   GtkPrintCupsDispatchWatch *dispatch;
909   GtkCupsPollState poll_state;
910
911   dispatch = (GtkPrintCupsDispatchWatch *) source;
912
913   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
914
915   if (dispatch->request->http != NULL)
916     {
917       if (dispatch->data_poll == NULL)
918         {
919           dispatch->data_poll = g_new0 (GPollFD, 1);
920
921           if (poll_state == GTK_CUPS_HTTP_READ)
922             dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
923           else if (poll_state == GTK_CUPS_HTTP_WRITE)
924             dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
925           else
926             dispatch->data_poll->events = 0;
927
928 #ifdef HAVE_CUPS_API_1_2
929           dispatch->data_poll->fd = httpGetFd (dispatch->request->http);
930 #else
931           dispatch->data_poll->fd = dispatch->request->http->fd;
932 #endif
933           g_source_add_poll (source, dispatch->data_poll);
934         }
935     }
936 }
937
938 static gboolean
939 check_auth_info (gpointer user_data)
940 {
941   GtkPrintCupsDispatchWatch *dispatch;
942   dispatch = (GtkPrintCupsDispatchWatch *) user_data;
943
944   if (!dispatch->request->need_auth_info)
945     {
946       if (dispatch->request->auth_info == NULL)
947         {
948           dispatch->callback (GTK_PRINT_BACKEND (dispatch->backend),
949                               gtk_cups_request_get_result (dispatch->request),
950                               dispatch->callback_data);
951           g_source_destroy ((GSource *) dispatch);
952         }
953       else
954         {
955           gint length;
956           gint i;
957
958           length = g_strv_length (dispatch->request->auth_info_required);
959
960           gtk_cups_request_ipp_add_strings (dispatch->request,
961                                             IPP_TAG_JOB,
962                                             IPP_TAG_TEXT,
963                                             "auth-info",
964                                             length,
965                                             NULL,
966                                             (const char * const *) dispatch->request->auth_info);
967
968           g_source_attach ((GSource *) dispatch, NULL);
969           g_source_unref ((GSource *) dispatch);
970
971           for (i = 0; i < length; i++)
972             overwrite_and_free (dispatch->request->auth_info[i]);
973           g_free (dispatch->request->auth_info);
974           dispatch->request->auth_info = NULL;
975         }
976
977       return FALSE;
978     }
979
980   return TRUE;
981 }
982
983 static gboolean
984 request_auth_info (gpointer user_data)
985 {
986   GtkPrintCupsDispatchWatch  *dispatch;
987   const char                 *job_title;
988   const char                 *printer_uri;
989   gchar                      *prompt = NULL;
990   char                       *printer_name = NULL;
991   gint                        length;
992   gint                        i;
993   gboolean                   *auth_info_visible = NULL;
994   gchar                     **auth_info_default = NULL;
995   gchar                     **auth_info_display = NULL;
996
997   dispatch = (GtkPrintCupsDispatchWatch *) user_data;
998
999   if (dispatch->backend->authentication_lock)
1000     return FALSE;
1001
1002   job_title = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_NAME, "job-name");
1003   printer_uri = gtk_cups_request_ipp_get_string (dispatch->request, IPP_TAG_URI, "printer-uri");
1004   length = g_strv_length (dispatch->request->auth_info_required);
1005
1006   auth_info_visible = g_new0 (gboolean, length);
1007   auth_info_default = g_new0 (gchar *, length + 1);
1008   auth_info_display = g_new0 (gchar *, length + 1);
1009
1010   for (i = 0; i < length; i++)
1011     {
1012       if (g_strcmp0 (dispatch->request->auth_info_required[i], "domain") == 0)
1013         {
1014           auth_info_display[i] = g_strdup (_("Domain:"));
1015           auth_info_default[i] = g_strdup ("WORKGROUP");
1016           auth_info_visible[i] = TRUE;
1017         }
1018       else if (g_strcmp0 (dispatch->request->auth_info_required[i], "username") == 0)
1019         {
1020           auth_info_display[i] = g_strdup (_("Username:"));
1021           if (dispatch->backend->username != NULL)
1022             auth_info_default[i] = g_strdup (dispatch->backend->username);
1023           else
1024             auth_info_default[i] = g_strdup (cupsUser ());
1025           auth_info_visible[i] = TRUE;
1026         }
1027       else if (g_strcmp0 (dispatch->request->auth_info_required[i], "password") == 0)
1028         {
1029           auth_info_display[i] = g_strdup (_("Password:"));
1030           auth_info_visible[i] = FALSE;
1031         }
1032     }
1033
1034   if (printer_uri != NULL && strrchr (printer_uri, '/') != NULL)
1035     printer_name = g_strdup (strrchr (printer_uri, '/') + 1);
1036
1037   dispatch->backend->authentication_lock = TRUE;
1038
1039   if (job_title != NULL)
1040     {
1041       if (printer_name != NULL)
1042         prompt = g_strdup_printf ( _("Authentication is required to print document '%s' on printer %s"), job_title, printer_name);
1043       else
1044         prompt = g_strdup_printf ( _("Authentication is required to print document '%s'"), job_title);
1045     }
1046   else
1047     {
1048       if (printer_name != NULL)
1049         prompt = g_strdup_printf ( _("Authentication is required to print this document on printer %s"), printer_name);
1050       else
1051         prompt = g_strdup ( _("Authentication is required to print this document"));
1052     }
1053
1054   g_signal_emit_by_name (dispatch->backend, "request-password",
1055                          dispatch->request->auth_info_required,
1056                          auth_info_default,
1057                          auth_info_display,
1058                          auth_info_visible,
1059                          prompt);
1060
1061   for (i = 0; i < length; i++)
1062     {
1063       g_free (auth_info_default[i]);
1064       g_free (auth_info_display[i]);
1065     }
1066
1067   g_free (auth_info_default);
1068   g_free (auth_info_display);
1069   g_free (printer_name);
1070   g_free (prompt);
1071
1072   g_idle_add (check_auth_info, user_data);
1073
1074   return FALSE;
1075 }
1076
1077 static gboolean
1078 cups_dispatch_watch_check (GSource *source)
1079 {
1080   GtkPrintCupsDispatchWatch *dispatch;
1081   GtkCupsPollState poll_state;
1082   gboolean result;
1083
1084   GTK_NOTE (PRINTING,
1085             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source)); 
1086
1087   dispatch = (GtkPrintCupsDispatchWatch *) source;
1088
1089   poll_state = gtk_cups_request_get_poll_state (dispatch->request);
1090
1091   cups_dispatch_add_poll (source);
1092     
1093   if (poll_state != GTK_CUPS_HTTP_IDLE && !dispatch->request->need_password)
1094     if (!(dispatch->data_poll->revents & dispatch->data_poll->events)) 
1095        return FALSE;
1096   
1097   result = gtk_cups_request_read_write (dispatch->request);
1098   if (result && dispatch->data_poll != NULL)
1099     {
1100       g_source_remove_poll (source, dispatch->data_poll);
1101       g_free (dispatch->data_poll);
1102       dispatch->data_poll = NULL;
1103     }
1104
1105   if (dispatch->request->need_password && dispatch->request->password_state != GTK_CUPS_PASSWORD_REQUESTED)
1106     {
1107       dispatch->request->need_password = FALSE;
1108       g_idle_add (request_password, dispatch);
1109       result = FALSE;
1110     }
1111   
1112   return result;
1113 }
1114
1115 static gboolean
1116 cups_dispatch_watch_prepare (GSource *source,
1117                              gint    *timeout_)
1118 {
1119   GtkPrintCupsDispatchWatch *dispatch;
1120   gboolean result;
1121
1122   dispatch = (GtkPrintCupsDispatchWatch *) source;
1123
1124   GTK_NOTE (PRINTING,
1125             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1126
1127   *timeout_ = -1;
1128   
1129   result = gtk_cups_request_read_write (dispatch->request);
1130
1131   cups_dispatch_add_poll (source);
1132
1133   return result;
1134 }
1135
1136 static gboolean
1137 cups_dispatch_watch_dispatch (GSource     *source,
1138                               GSourceFunc  callback,
1139                               gpointer     user_data)
1140 {
1141   GtkPrintCupsDispatchWatch *dispatch;
1142   GtkPrintCupsResponseCallbackFunc ep_callback;  
1143   GtkCupsResult *result;
1144   
1145   g_assert (callback != NULL);
1146
1147   ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
1148   
1149   dispatch = (GtkPrintCupsDispatchWatch *) source;
1150
1151   result = gtk_cups_request_get_result (dispatch->request);
1152
1153   GTK_NOTE (PRINTING,
1154             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1155
1156   if (gtk_cups_result_is_error (result))
1157     {
1158       GTK_NOTE (PRINTING, 
1159                 g_print("Error result: %s (type %i, status %i, code %i)\n", 
1160                         gtk_cups_result_get_error_string (result),
1161                         gtk_cups_result_get_error_type (result),
1162                         gtk_cups_result_get_error_status (result),
1163                         gtk_cups_result_get_error_code (result)));
1164      }
1165
1166   ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
1167     
1168   return FALSE;
1169 }
1170
1171 static void
1172 cups_dispatch_watch_finalize (GSource *source)
1173 {
1174   GtkPrintCupsDispatchWatch *dispatch;
1175   GtkCupsResult *result;
1176
1177   GTK_NOTE (PRINTING,
1178             g_print ("CUPS Backend: %s <source %p>\n", G_STRFUNC, source));
1179
1180   dispatch = (GtkPrintCupsDispatchWatch *) source;
1181
1182   result = gtk_cups_request_get_result (dispatch->request);
1183   if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH)
1184     {
1185       const gchar *username;
1186       gchar        hostname[HTTP_MAX_URI];
1187       gchar       *key;
1188     
1189       httpGetHostname (dispatch->request->http, hostname, sizeof (hostname));
1190       if (is_address_local (hostname))
1191         strcpy (hostname, "localhost");
1192
1193       if (dispatch->backend->username != NULL)
1194         username = dispatch->backend->username;
1195       else
1196         username = cupsUser ();
1197
1198       key = g_strconcat (username, "@", hostname, NULL);
1199       GTK_NOTE (PRINTING,
1200                 g_print ("CUPS backend: removing stored password for %s\n", key));
1201       g_hash_table_remove (dispatch->backend->auth, key);
1202       g_free (key);
1203       
1204       if (dispatch->backend)
1205         dispatch->backend->authentication_lock = FALSE;
1206     }
1207
1208   gtk_cups_request_free (dispatch->request);
1209
1210   if (dispatch->backend)
1211     {
1212       /* We need to unref this at idle time, because it might be the
1213        * last reference to this module causing the code to be
1214        * unloaded (including this particular function!)
1215        * Update: Doing this at idle caused a deadlock taking the
1216        * mainloop context lock while being in a GSource callout for
1217        * multithreaded apps. So, for now we just disable unloading
1218        * of print backends. See _gtk_print_backend_create for the
1219        * disabling.
1220        */
1221
1222       dispatch->backend->requests = g_list_remove (dispatch->backend->requests, dispatch);
1223
1224       
1225       g_object_unref (dispatch->backend);
1226       dispatch->backend = NULL;
1227     }
1228
1229   g_free (dispatch->data_poll);
1230 }
1231
1232 static GSourceFuncs _cups_dispatch_watch_funcs = {
1233   cups_dispatch_watch_prepare,
1234   cups_dispatch_watch_check,
1235   cups_dispatch_watch_dispatch,
1236   cups_dispatch_watch_finalize
1237 };
1238
1239
1240 static void
1241 cups_request_execute (GtkPrintBackendCups              *print_backend,
1242                       GtkCupsRequest                   *request,
1243                       GtkPrintCupsResponseCallbackFunc  callback,
1244                       gpointer                          user_data,
1245                       GDestroyNotify                    notify)
1246 {
1247   GtkPrintCupsDispatchWatch *dispatch;
1248
1249   dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs, 
1250                                                          sizeof (GtkPrintCupsDispatchWatch));
1251   g_source_set_name (&dispatch->source, "GTK+ CUPS backend");
1252
1253   GTK_NOTE (PRINTING,
1254             g_print ("CUPS Backend: %s <source %p> - Executing cups request on server '%s' and resource '%s'\n", G_STRFUNC, dispatch, request->server, request->resource));
1255
1256   dispatch->request = request;
1257   dispatch->backend = g_object_ref (print_backend);
1258   dispatch->data_poll = NULL;
1259   dispatch->callback = NULL;
1260   dispatch->callback_data = NULL;
1261
1262   print_backend->requests = g_list_prepend (print_backend->requests, dispatch);
1263
1264   g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
1265
1266   if (request->need_auth_info)
1267     {
1268       dispatch->callback = callback;
1269       dispatch->callback_data = user_data;
1270       request_auth_info (dispatch);
1271     }
1272   else
1273     {
1274       g_source_attach ((GSource *) dispatch, NULL);
1275       g_source_unref ((GSource *) dispatch);
1276     }
1277 }
1278
1279 #if 0
1280 static void
1281 cups_request_printer_info_cb (GtkPrintBackendCups *backend,
1282                               GtkCupsResult       *result,
1283                               gpointer             user_data)
1284 {
1285   ipp_attribute_t *attr;
1286   ipp_t *response;
1287   gchar *printer_name;
1288   GtkPrinterCups *cups_printer;
1289   GtkPrinter *printer;
1290   gchar *loc;
1291   gchar *desc;
1292   gchar *state_msg;
1293   int job_count;
1294   gboolean status_changed;  
1295
1296   g_assert (GTK_IS_PRINT_BACKEND_CUPS (backend));
1297
1298   printer_name = (gchar *)user_data;
1299   printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (backend),
1300                                             printer_name);
1301
1302   GTK_NOTE (PRINTING,
1303             g_print ("CUPS Backend: %s - Got printer info for printer '%s'\n", G_STRFUNC, printer_name));
1304
1305   if (!printer)
1306     {
1307       GTK_NOTE (PRINTING,
1308             g_print ("CUPS Backend: Could not find printer called '%s'\n", printer_name));
1309       return;
1310     }
1311
1312   cups_printer = GTK_PRINTER_CUPS (printer);
1313   
1314   if (gtk_cups_result_is_error (result))
1315     {
1316       if (gtk_printer_is_new (printer))
1317         {
1318           gtk_print_backend_remove_printer (GTK_PRINT_BACKEND (backend),
1319                                             printer);
1320           return;
1321         }
1322       else
1323         return; /* TODO: mark as inactive printer */
1324     }
1325
1326   response = gtk_cups_result_get_response (result);
1327
1328   /* TODO: determine printer type and use correct icon */
1329   gtk_printer_set_icon_name (printer, "printer");
1330  
1331   state_msg = "";
1332   loc = "";
1333   desc = "";
1334   job_count = 0;
1335   for (attr = response->attrs; attr != NULL; attr = attr->next) 
1336     {
1337       if (!attr->name)
1338         continue;
1339
1340       _CUPS_MAP_ATTR_STR (attr, loc, "printer-location");
1341       _CUPS_MAP_ATTR_STR (attr, desc, "printer-info");
1342       _CUPS_MAP_ATTR_STR (attr, state_msg, "printer-state-message");
1343       _CUPS_MAP_ATTR_INT (attr, cups_printer->state, "printer-state");
1344       _CUPS_MAP_ATTR_INT (attr, job_count, "queued-job-count");
1345     }
1346
1347   status_changed = gtk_printer_set_job_count (printer, job_count);
1348   
1349   status_changed |= gtk_printer_set_location (printer, loc);
1350   status_changed |= gtk_printer_set_description (printer, desc);
1351   status_changed |= gtk_printer_set_state_message (printer, state_msg);
1352
1353   if (status_changed)
1354     g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
1355                            "printer-status-changed", printer); 
1356 }
1357
1358 static void
1359 cups_request_printer_info (GtkPrintBackendCups *print_backend,
1360                            const gchar         *printer_name)
1361 {
1362   GtkCupsRequest *request;
1363   gchar *printer_uri;
1364   static const char * const pattrs[] =  /* Attributes we're interested in */
1365     {
1366       "printer-location",
1367       "printer-info",
1368       "printer-state-message",
1369       "printer-state",
1370       "queued-job-count",
1371       "job-sheets-supported",
1372       "job-sheets-default"
1373     };
1374
1375   request = gtk_cups_request_new_with_username (NULL,
1376                                                 GTK_CUPS_POST,
1377                                                 IPP_GET_PRINTER_ATTRIBUTES,
1378                                                 NULL,
1379                                                 NULL,
1380                                                 NULL,
1381                                                 print_backend->username);
1382
1383   printer_uri = g_strdup_printf ("ipp://localhost/printers/%s",
1384                                   printer_name);
1385   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
1386                                    "printer-uri", NULL, printer_uri);
1387
1388   GTK_NOTE (PRINTING,
1389             g_print ("CUPS Backend: %s - Requesting printer info for URI '%s'\n", G_STRFUNC, printer_uri));
1390
1391   g_free (printer_uri);
1392
1393   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1394                                     "requested-attributes", G_N_ELEMENTS (pattrs),
1395                                     NULL, pattrs);
1396  
1397   cups_request_execute (print_backend,
1398                         request,
1399                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
1400                         g_strdup (printer_name),
1401                         (GDestroyNotify) g_free);
1402 }
1403 #endif
1404
1405 typedef struct {
1406   GtkPrintBackendCups *print_backend;
1407   GtkPrintJob *job;
1408   int job_id;
1409   int counter;
1410 } CupsJobPollData;
1411
1412 static void
1413 job_object_died (gpointer  user_data,
1414                  GObject  *where_the_object_was)
1415 {
1416   CupsJobPollData *data = user_data;
1417   data->job = NULL;
1418 }
1419
1420 static void
1421 cups_job_poll_data_free (CupsJobPollData *data)
1422 {
1423   if (data->job)
1424     g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
1425     
1426   g_free (data);
1427 }
1428
1429 static void
1430 cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
1431                           GtkCupsResult       *result,
1432                           gpointer             user_data)
1433 {
1434   CupsJobPollData *data = user_data;
1435   ipp_attribute_t *attr;
1436   ipp_t *response;
1437   int state;
1438   gboolean done;
1439
1440   GDK_THREADS_ENTER ();
1441
1442   if (data->job == NULL)
1443     {
1444       cups_job_poll_data_free (data);
1445       goto done;
1446     }
1447
1448   data->counter++;
1449   
1450   response = gtk_cups_result_get_response (result);
1451
1452   state = 0;
1453   for (attr = response->attrs; attr != NULL; attr = attr->next) 
1454     {
1455       if (!attr->name)
1456         continue;
1457       
1458       _CUPS_MAP_ATTR_INT (attr, state, "job-state");
1459     }
1460   
1461   done = FALSE;
1462   switch (state)
1463     {
1464     case IPP_JOB_PENDING:
1465     case IPP_JOB_HELD:
1466     case IPP_JOB_STOPPED:
1467       gtk_print_job_set_status (data->job,
1468                                 GTK_PRINT_STATUS_PENDING);
1469       break;
1470     case IPP_JOB_PROCESSING:
1471       gtk_print_job_set_status (data->job,
1472                                 GTK_PRINT_STATUS_PRINTING);
1473       break;
1474     default:
1475     case IPP_JOB_CANCELLED:
1476     case IPP_JOB_ABORTED:
1477       gtk_print_job_set_status (data->job,
1478                                 GTK_PRINT_STATUS_FINISHED_ABORTED);
1479       done = TRUE;
1480       break;
1481     case 0:
1482     case IPP_JOB_COMPLETED:
1483       gtk_print_job_set_status (data->job,
1484                                 GTK_PRINT_STATUS_FINISHED);
1485       done = TRUE;
1486       break;
1487     }
1488
1489   if (!done && data->job != NULL)
1490     {
1491       guint32 timeout;
1492
1493       if (data->counter < 5)
1494         timeout = 100;
1495       else if (data->counter < 10)
1496         timeout = 500;
1497       else
1498         timeout = 1000;
1499       
1500       g_timeout_add (timeout, cups_job_info_poll_timeout, data);
1501     }
1502   else
1503     cups_job_poll_data_free (data);    
1504
1505 done:
1506   GDK_THREADS_LEAVE ();
1507 }
1508
1509 static void
1510 cups_request_job_info (CupsJobPollData *data)
1511 {
1512   GtkCupsRequest *request;
1513   gchar *job_uri;
1514
1515   request = gtk_cups_request_new_with_username (NULL,
1516                                                 GTK_CUPS_POST,
1517                                                 IPP_GET_JOB_ATTRIBUTES,
1518                                                 NULL,
1519                                                 NULL,
1520                                                 NULL,
1521                                                 data->print_backend->username);
1522
1523   job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
1524   gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
1525                                    "job-uri", NULL, job_uri);
1526   g_free (job_uri);
1527
1528   cups_request_execute (data->print_backend,
1529                         request,
1530                         (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
1531                         data,
1532                         NULL);
1533 }
1534
1535 static gboolean
1536 cups_job_info_poll_timeout (gpointer user_data)
1537 {
1538   CupsJobPollData *data = user_data;
1539   
1540   if (data->job == NULL)
1541     cups_job_poll_data_free (data);
1542   else
1543     cups_request_job_info (data);
1544   
1545   return FALSE;
1546 }
1547
1548 static void
1549 cups_begin_polling_info (GtkPrintBackendCups *print_backend,
1550                          GtkPrintJob         *job,
1551                          gint                 job_id)
1552 {
1553   CupsJobPollData *data;
1554
1555   data = g_new0 (CupsJobPollData, 1);
1556
1557   data->print_backend = print_backend;
1558   data->job = job;
1559   data->job_id = job_id;
1560   data->counter = 0;
1561
1562   g_object_weak_ref (G_OBJECT (job), job_object_died, data);
1563
1564   cups_request_job_info (data);
1565 }
1566
1567 static void
1568 mark_printer_inactive (GtkPrinter      *printer, 
1569                        GtkPrintBackend *backend)
1570 {
1571   gtk_printer_set_is_active (printer, FALSE);
1572   g_signal_emit_by_name (backend, "printer-removed", printer);
1573 }
1574
1575 static gint
1576 find_printer (GtkPrinter  *printer, 
1577               const gchar *find_name)
1578 {
1579   const gchar *printer_name;
1580
1581   printer_name = gtk_printer_get_name (printer);
1582   return g_ascii_strcasecmp (printer_name, find_name);
1583 }
1584
1585 static void
1586 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
1587                               GtkCupsResult       *result,
1588                               gpointer             user_data)
1589 {
1590   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
1591   ipp_attribute_t *attr;
1592   ipp_t *response;
1593   gboolean list_has_changed;
1594   GList *removed_printer_checklist;
1595   gchar *remote_default_printer = NULL;
1596
1597   GDK_THREADS_ENTER ();
1598
1599   list_has_changed = FALSE;
1600
1601   GTK_NOTE (PRINTING,
1602             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1603
1604   cups_backend->list_printers_pending = FALSE;
1605
1606   if (gtk_cups_result_is_error (result))
1607     {
1608       GTK_NOTE (PRINTING, 
1609                 g_warning ("CUPS Backend: Error getting printer list: %s %d %d", 
1610                            gtk_cups_result_get_error_string (result),
1611                            gtk_cups_result_get_error_type (result),
1612                            gtk_cups_result_get_error_code (result)));
1613
1614       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
1615           gtk_cups_result_get_error_code (result) == 1)
1616         {
1617           /* Canceled by user, stop popping up more password dialogs */
1618           if (cups_backend->list_printers_poll > 0)
1619             g_source_remove (cups_backend->list_printers_poll);
1620           cups_backend->list_printers_poll = 0;
1621           cups_backend->list_printers_attempts = 0;
1622         }
1623
1624       goto done;
1625     }
1626   
1627   /* Gather the names of the printers in the current queue
1628    * so we may check to see if they were removed 
1629    */
1630   removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
1631                                                                   
1632   response = gtk_cups_result_get_response (result);
1633
1634   for (attr = response->attrs; attr != NULL; attr = attr->next)
1635     {
1636       GtkPrinter *printer;
1637       const gchar *printer_name = NULL;
1638       const gchar *printer_uri = NULL;
1639       const gchar *member_uris = NULL;
1640       const gchar *location = NULL;
1641       const gchar *description = NULL;
1642       const gchar *state_msg = NULL;
1643       gint state = 0;
1644       gint job_count = 0;
1645       gboolean status_changed = FALSE;
1646       GList *node;
1647       gint i,j;
1648       const gchar *reason_msg = NULL;
1649       gchar *reason_msg_desc = NULL;
1650       gchar *tmp_msg = NULL;
1651       gchar *tmp_msg2 = NULL;
1652       gint printer_state_reason_level = 0; /* 0 - none, 1 - report, 2 - warning, 3 - error */
1653       gboolean interested_in = FALSE;
1654       gboolean found = FALSE;
1655       static const char * const reasons[] =     /* Reasons we're interested in */
1656         {
1657           "toner-low",
1658           "toner-empty",
1659           "developer-low",
1660           "developer-empty",
1661           "marker-supply-low",
1662           "marker-supply-empty",
1663           "cover-open",
1664           "door-open",
1665           "media-low",
1666           "media-empty",
1667           "offline",
1668           "other"
1669         };
1670       static const char * reasons_descs[] =
1671         {
1672           N_("Printer '%s' is low on toner."),
1673           N_("Printer '%s' has no toner left."),
1674           /* Translators: "Developer" like on photo development context */
1675           N_("Printer '%s' is low on developer."),
1676           /* Translators: "Developer" like on photo development context */
1677           N_("Printer '%s' is out of developer."),
1678           /* Translators: "marker" is one color bin of the printer */
1679           N_("Printer '%s' is low on at least one marker supply."),
1680           /* Translators: "marker" is one color bin of the printer */
1681           N_("Printer '%s' is out of at least one marker supply."),
1682           N_("The cover is open on printer '%s'."),
1683           N_("The door is open on printer '%s'."),
1684           N_("Printer '%s' is low on paper."),
1685           N_("Printer '%s' is out of paper."),
1686           N_("Printer '%s' is currently offline."),
1687           N_("There is a problem on printer '%s'.")
1688         };
1689       gboolean is_paused = FALSE;
1690       gboolean is_accepting_jobs = TRUE;
1691       gboolean default_printer = FALSE;
1692       gboolean got_printer_type = FALSE;
1693       gchar   *default_cover_before = NULL;
1694       gchar   *default_cover_after = NULL;
1695       gboolean remote_printer = FALSE;
1696       gchar  **auth_info_required = NULL;
1697       
1698       /* Skip leading attributes until we hit a printer...
1699        */
1700       while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1701         attr = attr->next;
1702
1703       if (attr == NULL)
1704         break;
1705
1706       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1707       {
1708         if (strcmp (attr->name, "printer-name") == 0 &&
1709             attr->value_tag == IPP_TAG_NAME)
1710           printer_name = attr->values[0].string.text;
1711         else if (strcmp (attr->name, "printer-uri-supported") == 0 &&
1712                  attr->value_tag == IPP_TAG_URI)
1713           printer_uri = attr->values[0].string.text;
1714         else if (strcmp (attr->name, "member-uris") == 0 &&
1715                  attr->value_tag == IPP_TAG_URI)
1716           member_uris = attr->values[0].string.text;
1717         else if (strcmp (attr->name, "printer-location") == 0)
1718           location = attr->values[0].string.text;
1719         else if (strcmp (attr->name, "printer-info") == 0)
1720           description = attr->values[0].string.text;
1721         else if (strcmp (attr->name, "printer-state-message") == 0)
1722           state_msg = attr->values[0].string.text;
1723         else if (strcmp (attr->name, "printer-state-reasons") == 0)
1724           /* Store most important reason to reason_msg and set
1725              its importance at printer_state_reason_level */
1726           {
1727             for (i = 0; i < attr->num_values; i++)
1728               {
1729                 if (strcmp (attr->values[i].string.text, "none") != 0)
1730                   {
1731                     /* Sets is_paused flag for paused printer. */
1732                     if (strcmp (attr->values[i].string.text, "paused") == 0)
1733                       {
1734                         is_paused = TRUE;
1735                       }
1736
1737                     interested_in = FALSE;
1738                     for (j = 0; j < G_N_ELEMENTS (reasons); j++)
1739                         if (strncmp (attr->values[i].string.text, reasons[j], strlen (reasons[j])) == 0)
1740                           {
1741                             interested_in = TRUE;
1742                             break;
1743                           }
1744
1745                     if (interested_in)
1746                       {
1747                         if (g_str_has_suffix (attr->values[i].string.text, "-report"))
1748                           {
1749                             if (printer_state_reason_level <= 1)
1750                               {
1751                                 reason_msg = attr->values[i].string.text;
1752                                 printer_state_reason_level = 1;
1753                               }
1754                           }
1755                         else if (g_str_has_suffix (attr->values[i].string.text, "-warning"))
1756                           {
1757                             if (printer_state_reason_level <= 2)
1758                               {
1759                                 reason_msg = attr->values[i].string.text;
1760                                 printer_state_reason_level = 2;
1761                               }
1762                           }
1763                         else  /* It is error in the case of no suffix. */
1764                           {
1765                             reason_msg = attr->values[i].string.text;
1766                             printer_state_reason_level = 3;
1767                           }
1768                       }
1769                   }
1770               }
1771           }
1772         else if (strcmp (attr->name, "printer-state") == 0)
1773           state = attr->values[0].integer;
1774         else if (strcmp (attr->name, "queued-job-count") == 0)
1775           job_count = attr->values[0].integer;
1776         else if (strcmp (attr->name, "printer-is-accepting-jobs") == 0)
1777           {
1778             if (attr->values[0].boolean == 1)
1779               is_accepting_jobs = TRUE;
1780             else
1781               is_accepting_jobs = FALSE;
1782           }
1783         else if (strcmp (attr->name, "job-sheets-supported") == 0)
1784           {
1785             if (cups_backend->covers == NULL)
1786               {
1787                 cups_backend->number_of_covers = attr->num_values;
1788                 cups_backend->covers = g_new (char *, cups_backend->number_of_covers + 1);
1789                 for (i = 0; i < cups_backend->number_of_covers; i++)
1790                   cups_backend->covers[i] = g_strdup (attr->values[i].string.text);
1791                 cups_backend->covers[cups_backend->number_of_covers] = NULL;
1792               }
1793           }
1794         else if (strcmp (attr->name, "job-sheets-default") == 0)
1795           {
1796             if (attr->num_values == 2)
1797               {
1798                 default_cover_before = attr->values[0].string.text;
1799                 default_cover_after = attr->values[1].string.text;
1800               }
1801           }
1802         else if (strcmp (attr->name, "printer-type") == 0)
1803           {
1804             got_printer_type = TRUE;
1805             if (attr->values[0].integer & 0x00020000)
1806               default_printer = TRUE;
1807             else
1808               default_printer = FALSE;
1809
1810             if (attr->values[0].integer & 0x00000002)
1811               remote_printer = TRUE;
1812             else
1813               remote_printer = FALSE;
1814           }
1815         else if (strcmp (attr->name, "auth-info-required") == 0)
1816           {
1817             if (strcmp (attr->values[0].string.text, "none") != 0)
1818               {
1819                 auth_info_required = g_new0 (gchar *, attr->num_values + 1);
1820                 for (i = 0; i < attr->num_values; i++)
1821                   auth_info_required[i] = g_strdup (attr->values[i].string.text);
1822               }
1823           }
1824         else
1825           {
1826             GTK_NOTE (PRINTING,
1827                       g_print ("CUPS Backend: Attribute %s ignored", attr->name));
1828           }
1829
1830         attr = attr->next;
1831       }
1832
1833       if (printer_name == NULL ||
1834           (printer_uri == NULL && member_uris == NULL))
1835       {
1836         if (attr == NULL)
1837           break;
1838         else
1839           continue;
1840       }
1841
1842       if (got_printer_type)
1843         {
1844           if (default_printer && !cups_backend->got_default_printer)
1845             {
1846               if (!remote_printer)
1847                 {
1848                   cups_backend->got_default_printer = TRUE;
1849                   cups_backend->default_printer = g_strdup (printer_name);
1850                 }
1851               else
1852                 {
1853                   if (remote_default_printer == NULL)
1854                     remote_default_printer = g_strdup (printer_name);
1855                 }
1856             }
1857         }
1858       else
1859         {
1860           if (!cups_backend->got_default_printer)
1861             cups_get_default_printer (cups_backend);
1862         }
1863
1864       /* remove name from checklist if it was found */
1865       node = g_list_find_custom (removed_printer_checklist, printer_name, (GCompareFunc) find_printer);
1866       removed_printer_checklist = g_list_delete_link (removed_printer_checklist, node);
1867  
1868       printer = gtk_print_backend_find_printer (backend, printer_name);
1869       if (!printer)
1870         {
1871           GtkPrinterCups *cups_printer;
1872           char uri[HTTP_MAX_URI];       /* Printer URI */
1873           char method[HTTP_MAX_URI];    /* Method/scheme name */
1874           char username[HTTP_MAX_URI];  /* Username:password */
1875           char hostname[HTTP_MAX_URI];  /* Hostname */
1876           char resource[HTTP_MAX_URI];  /* Resource name */
1877           int  port;                    /* Port number */
1878           char *cups_server;            /* CUPS server */
1879           
1880           list_has_changed = TRUE;
1881           cups_printer = gtk_printer_cups_new (printer_name, backend);
1882
1883           cups_printer->device_uri = g_strdup_printf ("/printers/%s", printer_name);
1884
1885           /* Check to see if we are looking at a class */
1886           if (member_uris)
1887             {
1888               cups_printer->printer_uri = g_strdup (member_uris);
1889               /* TODO if member_uris is a class we need to recursivly find a printer */
1890               GTK_NOTE (PRINTING,
1891                         g_print ("CUPS Backend: Found class with printer %s\n", member_uris));
1892             }
1893           else
1894             {
1895               cups_printer->printer_uri = g_strdup (printer_uri);
1896               GTK_NOTE (PRINTING,
1897                         g_print ("CUPS Backend: Found printer %s\n", printer_uri));
1898             }
1899
1900 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
1901           httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri, 
1902                            method, sizeof (method), 
1903                            username, sizeof (username),
1904                            hostname, sizeof (hostname),
1905                            &port, 
1906                            resource, sizeof (resource));
1907
1908 #else
1909           httpSeparate (cups_printer->printer_uri, 
1910                         method, 
1911                         username, 
1912                         hostname,
1913                         &port, 
1914                         resource);
1915 #endif
1916
1917           if (strncmp (resource, "/printers/", 10) == 0)
1918             {
1919               cups_printer->ppd_name = g_strdup (resource + 10);
1920               GTK_NOTE (PRINTING,
1921                         g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, printer_name));
1922             }
1923
1924           gethostname (uri, sizeof (uri));
1925           cups_server = g_strdup (cupsServer());
1926
1927           if (strcasecmp (uri, hostname) == 0)
1928             strcpy (hostname, "localhost");
1929
1930           /* if the cups server is local and listening at a unix domain socket 
1931            * then use the socket connection
1932            */
1933           if ((strstr (hostname, "localhost") != NULL) &&
1934               (cups_server[0] == '/'))
1935             strcpy (hostname, cups_server);
1936
1937           g_free (cups_server);
1938
1939           cups_printer->default_cover_before = g_strdup (default_cover_before);
1940           cups_printer->default_cover_after = g_strdup (default_cover_after);
1941
1942           cups_printer->hostname = g_strdup (hostname);
1943           cups_printer->port = port;
1944           
1945           cups_printer->auth_info_required = g_strdupv (auth_info_required);
1946           g_strfreev (auth_info_required);
1947
1948           printer = GTK_PRINTER (cups_printer);
1949           
1950           if (cups_backend->default_printer != NULL &&
1951               strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
1952             gtk_printer_set_is_default (printer, TRUE);
1953
1954           
1955           gtk_print_backend_add_printer (backend, printer);
1956         }
1957       else
1958         g_object_ref (printer);
1959
1960       GTK_PRINTER_CUPS (printer)->remote = remote_printer;
1961
1962       gtk_printer_set_is_paused (printer, is_paused);
1963       gtk_printer_set_is_accepting_jobs (printer, is_accepting_jobs);
1964
1965       if (!gtk_printer_is_active (printer))
1966         {
1967           gtk_printer_set_is_active (printer, TRUE);
1968           gtk_printer_set_is_new (printer, TRUE);
1969           list_has_changed = TRUE;
1970         }
1971
1972       if (gtk_printer_is_new (printer))
1973         {
1974           g_signal_emit_by_name (backend, "printer-added", printer);
1975
1976           gtk_printer_set_is_new (printer, FALSE);
1977         }
1978
1979 #if 0
1980       /* Getting printer info with separate requests overwhelms cups
1981        * when the printer list has more than a handful of printers.
1982        */
1983       cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
1984 #endif
1985
1986       GTK_PRINTER_CUPS (printer)->state = state;
1987       status_changed = gtk_printer_set_job_count (printer, job_count);
1988       status_changed |= gtk_printer_set_location (printer, location);
1989       status_changed |= gtk_printer_set_description (printer, description);
1990
1991       if (state_msg != NULL && strlen (state_msg) == 0)
1992         {
1993           if (is_paused && !is_accepting_jobs)
1994                   /* Translators: this is a printer status. */
1995             tmp_msg2 = g_strdup ( N_("Paused ; Rejecting Jobs"));
1996           if (is_paused && is_accepting_jobs)
1997                   /* Translators: this is a printer status. */
1998             tmp_msg2 = g_strdup ( N_("Paused"));
1999           if (!is_paused && !is_accepting_jobs)
2000                   /* Translators: this is a printer status. */
2001             tmp_msg2 = g_strdup ( N_("Rejecting Jobs"));
2002
2003           if (tmp_msg2 != NULL)
2004             state_msg = tmp_msg2;
2005         }
2006
2007       /* Set description of the reason and combine it with printer-state-message. */
2008       if ( (reason_msg != NULL))
2009         {
2010           for (i = 0; i < G_N_ELEMENTS (reasons); i++)
2011             {
2012               if (strncmp (reason_msg, reasons[i], strlen (reasons[i])) == 0)
2013                 {
2014                   reason_msg_desc = g_strdup_printf (reasons_descs[i], printer_name);
2015                   found = TRUE;
2016                   break;
2017                 }
2018             }
2019
2020           if (!found)
2021             printer_state_reason_level = 0;
2022
2023           if (printer_state_reason_level >= 2)
2024             {
2025               if (strlen (state_msg) == 0)
2026                 state_msg = reason_msg_desc;
2027               else
2028                 {
2029                   tmp_msg = g_strjoin (" ; ", state_msg, reason_msg_desc, NULL);
2030                   state_msg = tmp_msg;
2031                 }
2032             }
2033         }
2034
2035       status_changed |= gtk_printer_set_state_message (printer, state_msg);
2036       status_changed |= gtk_printer_set_is_accepting_jobs (printer, is_accepting_jobs);
2037
2038       if (tmp_msg != NULL)
2039         g_free (tmp_msg);
2040
2041       if (tmp_msg2 != NULL)
2042         g_free (tmp_msg2);
2043
2044       if (reason_msg_desc != NULL)
2045         g_free (reason_msg_desc);
2046
2047       /* Set printer icon according to importance
2048          (none, report, warning, error - report is omitted). */
2049       if (printer_state_reason_level == 3)
2050         gtk_printer_set_icon_name (printer, "printer-error");
2051       else if (printer_state_reason_level == 2)
2052         gtk_printer_set_icon_name (printer, "printer-warning");
2053       else if (gtk_printer_is_paused (printer))
2054         gtk_printer_set_icon_name (printer, "printer-paused");
2055       else
2056         gtk_printer_set_icon_name (printer, "printer");
2057
2058       if (status_changed)
2059         g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
2060                                "printer-status-changed", printer);
2061
2062       /* The ref is held by GtkPrintBackend, in add_printer() */
2063       g_object_unref (printer);
2064       
2065       if (attr == NULL)
2066         break;
2067     }
2068
2069   /* look at the removed printers checklist and mark any printer
2070      as inactive if it is in the list, emitting a printer_removed signal */
2071   if (removed_printer_checklist != NULL)
2072     {
2073       g_list_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, backend);
2074       g_list_free (removed_printer_checklist);
2075       list_has_changed = TRUE;
2076     }
2077   
2078 done:
2079   if (list_has_changed)
2080     g_signal_emit_by_name (backend, "printer-list-changed");
2081   
2082   gtk_print_backend_set_list_done (backend);
2083
2084   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
2085     {
2086       cups_backend->default_printer = g_strdup (remote_default_printer);
2087       cups_backend->got_default_printer = TRUE;
2088       g_free (remote_default_printer);
2089
2090       if (cups_backend->default_printer != NULL)
2091         {
2092           GtkPrinter *default_printer = NULL;
2093           default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
2094                                                             cups_backend->default_printer);
2095           if (default_printer != NULL)
2096             {
2097               gtk_printer_set_is_default (default_printer, TRUE);
2098               g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
2099                                      "printer-status-changed", default_printer);
2100             }
2101         }
2102     }
2103
2104   GDK_THREADS_LEAVE ();
2105 }
2106
2107 static void
2108 update_backend_status (GtkPrintBackendCups    *cups_backend,
2109                        GtkCupsConnectionState  state)
2110 {
2111   switch (state)
2112     {
2113     case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
2114       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
2115       break;
2116     case GTK_CUPS_CONNECTION_AVAILABLE:
2117       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
2118       break;
2119     default: ;
2120     }
2121 }
2122
2123 static gboolean
2124 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
2125 {
2126   GtkCupsConnectionState state;
2127   GtkCupsRequest *request;
2128   static const char * const pattrs[] =  /* Attributes we're interested in */
2129     {
2130       "printer-name",
2131       "printer-uri-supported",
2132       "member-uris",
2133       "printer-location",
2134       "printer-info",
2135       "printer-state-message",
2136       "printer-state-reasons",
2137       "printer-state",
2138       "queued-job-count",
2139       "printer-is-accepting-jobs",
2140       "job-sheets-supported",
2141       "job-sheets-default",
2142       "printer-type",
2143       "auth-info-required"
2144     };
2145
2146   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
2147     return TRUE;
2148
2149   state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
2150   update_backend_status (cups_backend, state);
2151
2152   if (cups_backend->list_printers_attempts == 60)
2153     {
2154       cups_backend->list_printers_attempts = -1;
2155       if (cups_backend->list_printers_poll > 0)
2156         g_source_remove (cups_backend->list_printers_poll);
2157       cups_backend->list_printers_poll = gdk_threads_add_timeout (200,
2158                                            (GSourceFunc) cups_request_printer_list,
2159                                            cups_backend);
2160     }
2161   else if (cups_backend->list_printers_attempts != -1)
2162     cups_backend->list_printers_attempts++;
2163
2164   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2165     return TRUE;
2166   else
2167     if (cups_backend->list_printers_attempts > 0)
2168       cups_backend->list_printers_attempts = 60;
2169
2170   cups_backend->list_printers_pending = TRUE;
2171
2172   request = gtk_cups_request_new_with_username (NULL,
2173                                                 GTK_CUPS_POST,
2174                                                 CUPS_GET_PRINTERS,
2175                                                 NULL,
2176                                                 NULL,
2177                                                 NULL,
2178                                                 cups_backend->username);
2179
2180   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2181                                     "requested-attributes", G_N_ELEMENTS (pattrs),
2182                                     NULL, pattrs);
2183
2184   cups_request_execute (cups_backend,
2185                         request,
2186                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
2187                         request,
2188                         NULL);
2189
2190   return TRUE;
2191 }
2192
2193 static void
2194 cups_get_printer_list (GtkPrintBackend *backend)
2195 {
2196   GtkPrintBackendCups *cups_backend;
2197
2198   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
2199
2200   if (cups_backend->cups_connection_test == NULL)
2201     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2202
2203   if (cups_backend->list_printers_poll == 0)
2204     {
2205       if (cups_request_printer_list (cups_backend))
2206         cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
2207                                              (GSourceFunc) cups_request_printer_list,
2208                                              backend);
2209     }
2210 }
2211
2212 typedef struct {
2213   GtkPrinterCups *printer;
2214   GIOChannel *ppd_io;
2215   http_t *http;
2216 } GetPPDData;
2217
2218 static void
2219 get_ppd_data_free (GetPPDData *data)
2220 {
2221   GTK_NOTE (PRINTING,
2222             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2223   httpClose (data->http);
2224   g_io_channel_unref (data->ppd_io);
2225   g_object_unref (data->printer);
2226   g_free (data);
2227 }
2228
2229 static void
2230 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
2231                      GtkCupsResult       *result,
2232                      GetPPDData          *data)
2233 {
2234   ipp_t *response;
2235   GtkPrinter *printer;
2236
2237   GDK_THREADS_ENTER ();
2238
2239   GTK_NOTE (PRINTING,
2240             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2241
2242   printer = GTK_PRINTER (data->printer);
2243   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
2244   print_backend->reading_ppds--;
2245
2246   if (gtk_cups_result_is_error (result))
2247     {
2248       gboolean success = FALSE;
2249
2250       /* if we get a 404 then it is just a raw printer without a ppd
2251          and not an error */
2252       if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
2253           (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
2254         {
2255           gtk_printer_set_has_details (printer, TRUE);
2256           success = TRUE;
2257         } 
2258         
2259       g_signal_emit_by_name (printer, "details-acquired", success);
2260       goto done;
2261     }
2262
2263   response = gtk_cups_result_get_response (result);
2264
2265   /* let ppdOpenFd take over the ownership of the open file */
2266   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
2267   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
2268
2269   ppdMarkDefaults (data->printer->ppd_file);
2270   
2271   gtk_printer_set_has_details (printer, TRUE);
2272   g_signal_emit_by_name (printer, "details-acquired", TRUE);
2273
2274 done:
2275   GDK_THREADS_LEAVE ();
2276 }
2277
2278 static gboolean
2279 cups_request_ppd (GtkPrinter *printer)
2280 {
2281   GError *error;
2282   GtkPrintBackend *print_backend;
2283   GtkPrinterCups *cups_printer;
2284   GtkCupsRequest *request;
2285   char *ppd_filename;
2286   gchar *resource;
2287   http_t *http;
2288   GetPPDData *data;
2289   int fd;
2290
2291   cups_printer = GTK_PRINTER_CUPS (printer);
2292
2293   error = NULL;
2294
2295   GTK_NOTE (PRINTING,
2296             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2297
2298   if (cups_printer->remote)
2299     {
2300       GtkCupsConnectionState state;
2301
2302       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
2303
2304       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
2305         {
2306           if (cups_printer->get_remote_ppd_attempts == 60)
2307             {
2308               cups_printer->get_remote_ppd_attempts = -1;
2309               if (cups_printer->get_remote_ppd_poll > 0)
2310                 g_source_remove (cups_printer->get_remote_ppd_poll);
2311               cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
2312                                                     (GSourceFunc) cups_request_ppd,
2313                                                     printer);
2314             }
2315           else if (cups_printer->get_remote_ppd_attempts != -1)
2316             cups_printer->get_remote_ppd_attempts++;
2317
2318           return TRUE;
2319         }
2320
2321       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
2322       cups_printer->remote_cups_connection_test = NULL;
2323       cups_printer->get_remote_ppd_poll = 0;
2324       cups_printer->get_remote_ppd_attempts = 0;
2325
2326       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2327         {
2328           g_signal_emit_by_name (printer, "details-acquired", FALSE);
2329           return FALSE;
2330         }
2331     }
2332
2333   http = httpConnectEncrypt (cups_printer->hostname, 
2334                              cups_printer->port,
2335                              cupsEncryption ());
2336   
2337   data = g_new0 (GetPPDData, 1);
2338
2339   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", 
2340                         &ppd_filename, 
2341                         &error);
2342
2343 #ifdef G_ENABLE_DEBUG 
2344   /* If we are debugging printing don't delete the tmp files */
2345   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
2346     unlink (ppd_filename);
2347 #else
2348   unlink (ppd_filename);
2349 #endif /* G_ENABLE_DEBUG */
2350
2351   if (error != NULL)
2352     {
2353       GTK_NOTE (PRINTING, 
2354                 g_warning ("CUPS Backend: Failed to create temp file, %s\n", 
2355                            error->message));
2356       g_error_free (error);
2357       httpClose (http);
2358       g_free (ppd_filename);
2359       g_free (data);
2360
2361       g_signal_emit_by_name (printer, "details-acquired", FALSE);
2362       return FALSE;
2363     }
2364     
2365   data->http = http;
2366   fchmod (fd, S_IRUSR | S_IWUSR);
2367   data->ppd_io = g_io_channel_unix_new (fd);
2368   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
2369   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
2370
2371   data->printer = g_object_ref (printer);
2372
2373   resource = g_strdup_printf ("/printers/%s.ppd", 
2374                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
2375
2376   print_backend = gtk_printer_get_backend (printer);
2377
2378   request = gtk_cups_request_new_with_username (data->http,
2379                                                 GTK_CUPS_GET,
2380                                                 0,
2381                                                 data->ppd_io,
2382                                                 cups_printer->hostname,
2383                                                 resource,
2384                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
2385
2386   GTK_NOTE (PRINTING,
2387             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
2388
2389
2390   cups_printer->reading_ppd = TRUE;
2391   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
2392
2393   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
2394                         request,
2395                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
2396                         data,
2397                         (GDestroyNotify)get_ppd_data_free);
2398
2399   g_free (resource);
2400   g_free (ppd_filename);
2401
2402   return FALSE;
2403 }
2404
2405 /* Ordering matters for default preference */
2406 static const char *lpoptions_locations[] = {
2407   "/etc/cups/lpoptions",
2408   ".lpoptions", 
2409   ".cups/lpoptions"
2410 };
2411
2412 static void
2413 cups_parse_user_default_printer (const char  *filename,
2414                                  char       **printer_name)
2415 {
2416   FILE *fp;
2417   char line[1024], *lineptr, *defname = NULL;
2418   
2419   if ((fp = g_fopen (filename, "r")) == NULL)
2420     return;
2421
2422   while (fgets (line, sizeof (line), fp) != NULL)
2423     {
2424       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
2425         continue;
2426
2427       lineptr = line + 8;
2428       while (isspace (*lineptr))
2429         lineptr++;
2430
2431       if (!*lineptr)
2432         continue;
2433
2434       defname = lineptr;
2435       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
2436         lineptr++;
2437
2438       *lineptr = '\0';
2439
2440       if (*printer_name != NULL)
2441         g_free (*printer_name);
2442
2443       *printer_name = g_strdup (defname);
2444     }
2445
2446   fclose (fp);
2447 }
2448
2449 static void
2450 cups_get_user_default_printer (char **printer_name)
2451 {
2452   int i;
2453
2454   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2455     {
2456       if (g_path_is_absolute (lpoptions_locations[i]))
2457         {
2458           cups_parse_user_default_printer (lpoptions_locations[i],
2459                                            printer_name);
2460         }
2461       else 
2462         {
2463           char *filename;
2464
2465           filename = g_build_filename (g_get_home_dir (), 
2466                                        lpoptions_locations[i], NULL);
2467           cups_parse_user_default_printer (filename, printer_name);
2468           g_free (filename);
2469         }
2470     }
2471 }
2472
2473 static int
2474 cups_parse_user_options (const char     *filename,
2475                          const char     *printer_name,
2476                          int             num_options,
2477                          cups_option_t **options)
2478 {
2479   FILE *fp;
2480   gchar line[1024], *lineptr, *name;
2481
2482   if ((fp = g_fopen (filename, "r")) == NULL)
2483     return num_options;
2484
2485   while (fgets (line, sizeof (line), fp) != NULL)
2486     {
2487       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
2488         lineptr = line + 4;
2489       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
2490         lineptr = line + 7;
2491       else
2492         continue;
2493
2494       /* Skip leading whitespace */
2495       while (isspace (*lineptr))
2496         lineptr++;
2497
2498       if (!*lineptr)
2499         continue;
2500
2501       /* NUL-terminate the name, stripping the instance name */
2502       name = lineptr;
2503       while (!isspace (*lineptr) && *lineptr)
2504         {
2505           if (*lineptr == '/')
2506             *lineptr = '\0';
2507           lineptr++;
2508         }
2509
2510       if (!*lineptr)
2511         continue;
2512
2513       *lineptr++ = '\0';
2514
2515       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
2516           continue;
2517
2518       /* We found our printer, parse the options */
2519       num_options = cupsParseOptions (lineptr, num_options, options);
2520     }
2521
2522   fclose (fp);
2523
2524   return num_options;
2525 }
2526
2527 static int
2528 cups_get_user_options (const char     *printer_name,
2529                        int             num_options,
2530                        cups_option_t **options)
2531 {
2532   int i;
2533
2534   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2535     {
2536       if (g_path_is_absolute (lpoptions_locations[i]))
2537         { 
2538            num_options = cups_parse_user_options (lpoptions_locations[i],
2539                                                   printer_name,
2540                                                   num_options,
2541                                                   options);
2542         }
2543       else
2544         {
2545           char *filename;
2546
2547           filename = g_build_filename (g_get_home_dir (), 
2548                                        lpoptions_locations[i], NULL);
2549           num_options = cups_parse_user_options (filename, printer_name,
2550                                                  num_options, options);
2551           g_free (filename);
2552         }
2553     }
2554
2555   return num_options;
2556 }
2557
2558 /* This function requests default printer from a CUPS server in regular intervals.
2559  * In the case of unreachable CUPS server the request is repeated later.
2560  * The default printer is not requested in the case of previous success.
2561  */
2562 static void
2563 cups_get_default_printer (GtkPrintBackendCups *backend)
2564 {
2565   GtkPrintBackendCups *cups_backend;
2566
2567   cups_backend = backend;
2568
2569   if (cups_backend->cups_connection_test == NULL)
2570     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2571
2572   if (cups_backend->default_printer_poll == 0)
2573     {
2574       if (cups_request_default_printer (cups_backend))
2575         cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
2576                                                (GSourceFunc) cups_request_default_printer,
2577                                                backend);
2578     }
2579 }
2580
2581 /* This function gets default printer from local settings.*/
2582 static void
2583 cups_get_local_default_printer (GtkPrintBackendCups *backend)
2584 {
2585   const char *str;
2586   char *name = NULL;
2587
2588   if ((str = g_getenv ("LPDEST")) != NULL)
2589     {
2590       backend->default_printer = g_strdup (str);
2591       backend->got_default_printer = TRUE;
2592       return;
2593     }
2594   else if ((str = g_getenv ("PRINTER")) != NULL &&
2595            strcmp (str, "lp") != 0)
2596     {
2597       backend->default_printer = g_strdup (str);
2598       backend->got_default_printer = TRUE;
2599       return;
2600     }
2601   
2602   /* Figure out user setting for default printer */  
2603   cups_get_user_default_printer (&name);
2604   if (name != NULL)
2605     {
2606       backend->default_printer = name;
2607       backend->got_default_printer = TRUE;
2608       return;
2609     }
2610 }
2611
2612 static void
2613 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
2614                                  GtkCupsResult       *result,
2615                                  gpointer             user_data)
2616 {
2617   ipp_t *response;
2618   ipp_attribute_t *attr;
2619   GtkPrinter *printer;
2620
2621   GDK_THREADS_ENTER ();
2622
2623   if (gtk_cups_result_is_error (result))
2624     {
2625       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
2626           gtk_cups_result_get_error_code (result) == 1)
2627         {
2628           /* Canceled by user, stop popping up more password dialogs */
2629           if (print_backend->list_printers_poll > 0)
2630             g_source_remove (print_backend->list_printers_poll);
2631           print_backend->list_printers_poll = 0;
2632         }
2633
2634       return;
2635     }
2636
2637   response = gtk_cups_result_get_response (result);
2638   
2639   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
2640     print_backend->default_printer = g_strdup (attr->values[0].string.text);
2641
2642   print_backend->got_default_printer = TRUE;
2643
2644   if (print_backend->default_printer != NULL)
2645     {
2646       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
2647       if (printer != NULL)
2648         {
2649           gtk_printer_set_is_default (printer, TRUE);
2650           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
2651         }
2652     }
2653
2654   /* Make sure to kick off get_printers if we are polling it, 
2655    * as we could have blocked this reading the default printer 
2656    */
2657   if (print_backend->list_printers_poll != 0)
2658     cups_request_printer_list (print_backend);
2659
2660   GDK_THREADS_LEAVE ();
2661 }
2662
2663 static gboolean
2664 cups_request_default_printer (GtkPrintBackendCups *print_backend)
2665 {
2666   GtkCupsConnectionState state;
2667   GtkCupsRequest *request;
2668
2669   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
2670   update_backend_status (print_backend, state);
2671
2672   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2673     return TRUE;
2674
2675   request = gtk_cups_request_new_with_username (NULL,
2676                                                 GTK_CUPS_POST,
2677                                                 CUPS_GET_DEFAULT,
2678                                                 NULL,
2679                                                 NULL,
2680                                                 NULL,
2681                                                 print_backend->username);
2682   
2683   cups_request_execute (print_backend,
2684                         request,
2685                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
2686                         g_object_ref (print_backend),
2687                         g_object_unref);
2688
2689   return FALSE;
2690 }
2691
2692 static void
2693 cups_printer_request_details (GtkPrinter *printer)
2694 {
2695   GtkPrinterCups *cups_printer;
2696
2697   cups_printer = GTK_PRINTER_CUPS (printer);
2698   if (!cups_printer->reading_ppd && 
2699       gtk_printer_cups_get_ppd (cups_printer) == NULL)
2700     {
2701       if (cups_printer->remote)
2702         {
2703           if (cups_printer->get_remote_ppd_poll == 0)
2704             {
2705               cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname);
2706
2707               if (cups_request_ppd (printer))
2708                 cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
2709                                                     (GSourceFunc) cups_request_ppd,
2710                                                     printer);
2711             }
2712         }
2713       else
2714         cups_request_ppd (printer);
2715     }
2716 }
2717
2718 static char *
2719 ppd_text_to_utf8 (ppd_file_t *ppd_file, 
2720                   const char *text)
2721 {
2722   const char *encoding = NULL;
2723   char *res;
2724   
2725   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
2726     {
2727       return g_strdup (text);
2728     }
2729   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
2730     {
2731       encoding = "ISO-8859-1";
2732     }
2733   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
2734     {
2735       encoding = "ISO-8859-2";
2736     }
2737   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
2738     {
2739       encoding = "ISO-8859-5";
2740     }
2741   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
2742     {
2743       encoding = "SHIFT-JIS";
2744     }
2745   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
2746     {
2747       encoding = "MACINTOSH";
2748     }
2749   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
2750     {
2751       encoding = "WINDOWS-1252";
2752     }
2753   else 
2754     {
2755       /* Fallback, try iso-8859-1... */
2756       encoding = "ISO-8859-1";
2757     }
2758
2759   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
2760
2761   if (res == NULL)
2762     {
2763       GTK_NOTE (PRINTING,
2764                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
2765       res = g_strdup ("???");
2766     }
2767   
2768   return res;
2769 }
2770
2771 /* TODO: Add more translations for common settings here */
2772
2773 static const struct {
2774   const char *keyword;
2775   const char *translation;
2776 } cups_option_translations[] = {
2777   { "Duplex", N_("Two Sided") },
2778   { "MediaType", N_("Paper Type") },
2779   { "InputSlot", N_("Paper Source") },
2780   { "OutputBin", N_("Output Tray") },
2781   { "Resolution", N_("Resolution") },
2782   { "PreFilter", N_("GhostScript pre-filtering") },
2783 };
2784
2785
2786 static const struct {
2787   const char *keyword;
2788   const char *choice;
2789   const char *translation;
2790 } cups_choice_translations[] = {
2791   { "Duplex", "None", N_("One Sided") },
2792   /* Translators: this is an option of "Two Sided" */
2793   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
2794   /* Translators: this is an option of "Two Sided" */
2795   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
2796   /* Translators: this is an option of "Paper Source" */
2797   { "InputSlot", "Auto", N_("Auto Select") },
2798   /* Translators: this is an option of "Paper Source" */
2799   { "InputSlot", "AutoSelect", N_("Auto Select") },
2800   /* Translators: this is an option of "Paper Source" */
2801   { "InputSlot", "Default", N_("Printer Default") },
2802   /* Translators: this is an option of "Paper Source" */
2803   { "InputSlot", "None", N_("Printer Default") },
2804   /* Translators: this is an option of "Paper Source" */
2805   { "InputSlot", "PrinterDefault", N_("Printer Default") },
2806   /* Translators: this is an option of "Paper Source" */
2807   { "InputSlot", "Unspecified", N_("Auto Select") },
2808   /* Translators: this is an option of "Resolution" */
2809   { "Resolution", "default", N_("Printer Default") },
2810   /* Translators: this is an option of "GhostScript" */
2811   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
2812   /* Translators: this is an option of "GhostScript" */
2813   { "PreFilter", "Level1", N_("Convert to PS level 1") },
2814   /* Translators: this is an option of "GhostScript" */
2815   { "PreFilter", "Level2", N_("Convert to PS level 2") },
2816   /* Translators: this is an option of "GhostScript" */
2817   { "PreFilter", "No", N_("No pre-filtering") },
2818 };
2819
2820 static const struct {
2821   const char *name;
2822   const char *translation;
2823 } cups_group_translations[] = {
2824 /* Translators: "Miscellaneous" is the label for a button, that opens
2825    up an extra panel of settings in a print dialog. */
2826   { "Miscellaneous", N_("Miscellaneous") },
2827 };
2828
2829 static const struct {
2830   const char *ppd_keyword;
2831   const char *name;
2832 } ppd_option_names[] = {
2833   {"Duplex", "gtk-duplex" },
2834   {"MediaType", "gtk-paper-type"},
2835   {"InputSlot", "gtk-paper-source"},
2836   {"OutputBin", "gtk-output-tray"},
2837 };
2838
2839 static const struct {
2840   const char *lpoption;
2841   const char *name;
2842 } lpoption_names[] = {
2843   {"number-up", "gtk-n-up" },
2844   {"number-up-layout", "gtk-n-up-layout"},
2845   {"job-billing", "gtk-billing-info"},
2846   {"job-priority", "gtk-job-prio"},
2847 };
2848
2849 /* keep sorted when changing */
2850 static const char *color_option_whitelist[] = {
2851   "BRColorEnhancement",
2852   "BRColorMatching",
2853   "BRColorMatching",
2854   "BRColorMode",
2855   "BRGammaValue",
2856   "BRImprovedGray",
2857   "BlackSubstitution",
2858   "ColorModel",
2859   "HPCMYKInks",
2860   "HPCSGraphics",
2861   "HPCSImages",
2862   "HPCSText",
2863   "HPColorSmart",
2864   "RPSBlackMode",
2865   "RPSBlackOverPrint",
2866   "Rcmyksimulation",
2867 };
2868
2869 /* keep sorted when changing */
2870 static const char *color_group_whitelist[] = {
2871   "ColorPage",
2872   "FPColorWise1",
2873   "FPColorWise2",
2874   "FPColorWise3",
2875   "FPColorWise4",
2876   "FPColorWise5",
2877   "HPColorOptionsPanel",
2878 };
2879   
2880 /* keep sorted when changing */
2881 static const char *image_quality_option_whitelist[] = {
2882   "BRDocument",
2883   "BRHalfTonePattern",
2884   "BRNormalPrt",
2885   "BRPrintQuality",
2886   "BitsPerPixel",
2887   "Darkness",
2888   "Dithering",
2889   "EconoMode",
2890   "Economode",
2891   "HPEconoMode",
2892   "HPEdgeControl",
2893   "HPGraphicsHalftone",
2894   "HPHalftone",
2895   "HPLJDensity",
2896   "HPPhotoHalftone",
2897   "OutputMode",
2898   "REt",
2899   "RPSBitsPerPixel",
2900   "RPSDitherType",
2901   "Resolution",
2902   "ScreenLock",
2903   "Smoothing",
2904   "TonerSaveMode",
2905   "UCRGCRForImage",
2906 };
2907
2908 /* keep sorted when changing */
2909 static const char *image_quality_group_whitelist[] = {
2910   "FPImageQuality1",
2911   "FPImageQuality2",
2912   "FPImageQuality3",
2913   "ImageQualityPage",
2914 };
2915
2916 /* keep sorted when changing */
2917 static const char * finishing_option_whitelist[] = {
2918   "BindColor",
2919   "BindEdge",
2920   "BindType",
2921   "BindWhen",
2922   "Booklet",
2923   "FoldType",
2924   "FoldWhen",
2925   "HPStaplerOptions",
2926   "Jog",
2927   "Slipsheet",
2928   "Sorter",
2929   "StapleLocation",
2930   "StapleOrientation",
2931   "StapleWhen",
2932   "StapleX",
2933   "StapleY",
2934 };
2935
2936 /* keep sorted when changing */
2937 static const char *finishing_group_whitelist[] = {
2938   "FPFinishing1",
2939   "FPFinishing2",
2940   "FPFinishing3",
2941   "FPFinishing4",
2942   "FinishingPage",
2943   "HPFinishingPanel",
2944 };
2945
2946 /* keep sorted when changing */
2947 static const char *cups_option_blacklist[] = {
2948   "Collate",
2949   "Copies", 
2950   "OutputOrder",
2951   "PageRegion",
2952   "PageSize",
2953 };
2954
2955 static char *
2956 get_option_text (ppd_file_t   *ppd_file, 
2957                  ppd_option_t *option)
2958 {
2959   int i;
2960   char *utf8;
2961   
2962   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
2963     {
2964       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
2965         return g_strdup (_(cups_option_translations[i].translation));
2966     }
2967
2968   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
2969
2970   /* Some ppd files have spaces in the text before the colon */
2971   g_strchomp (utf8);
2972   
2973   return utf8;
2974 }
2975
2976 static char *
2977 get_choice_text (ppd_file_t   *ppd_file, 
2978                  ppd_choice_t *choice)
2979 {
2980   int i;
2981   ppd_option_t *option = choice->option;
2982   const char *keyword = option->keyword;
2983   
2984   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
2985     {
2986       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
2987           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
2988         return g_strdup (_(cups_choice_translations[i].translation));
2989     }
2990   return ppd_text_to_utf8 (ppd_file, choice->text);
2991 }
2992
2993 static gboolean
2994 group_has_option (ppd_group_t  *group, 
2995                   ppd_option_t *option)
2996 {
2997   int i;
2998
2999   if (group == NULL)
3000     return FALSE;
3001   
3002   if (group->num_options > 0 &&
3003       option >= group->options && option < group->options + group->num_options)
3004     return TRUE;
3005   
3006   for (i = 0; i < group->num_subgroups; i++)
3007     {
3008       if (group_has_option (&group->subgroups[i],option))
3009         return TRUE;
3010     }
3011   return FALSE;
3012 }
3013
3014 static void
3015 set_option_off (GtkPrinterOption *option)
3016 {
3017   /* Any of these will do, _set only applies the value
3018    * if its allowed of the option */
3019   gtk_printer_option_set (option, "False");
3020   gtk_printer_option_set (option, "Off");
3021   gtk_printer_option_set (option, "None");
3022 }
3023
3024 static gboolean
3025 value_is_off (const char *value)
3026 {
3027   return  (strcasecmp (value, "None") == 0 ||
3028            strcasecmp (value, "Off") == 0 ||
3029            strcasecmp (value, "False") == 0);
3030 }
3031
3032 static char *
3033 ppd_group_name (ppd_group_t *group)
3034 {
3035 #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) 
3036   return group->name;
3037 #else
3038   return group->text;
3039 #endif
3040 }
3041
3042 static int
3043 available_choices (ppd_file_t     *ppd,
3044                    ppd_option_t   *option,
3045                    ppd_choice_t ***available,
3046                    gboolean        keep_if_only_one_option)
3047 {
3048   ppd_option_t *other_option;
3049   int i, j;
3050   gchar *conflicts;
3051   ppd_const_t *constraint;
3052   const char *choice, *other_choice;
3053   ppd_option_t *option1, *option2;
3054   ppd_group_t *installed_options;
3055   int num_conflicts;
3056   gboolean all_default;
3057   int add_auto;
3058
3059   if (available)
3060     *available = NULL;
3061
3062   conflicts = g_new0 (char, option->num_choices);
3063
3064   installed_options = NULL;
3065   for (i = 0; i < ppd->num_groups; i++)
3066     {
3067       char *name; 
3068
3069       name = ppd_group_name (&ppd->groups[i]);
3070       if (strcmp (name, "InstallableOptions") == 0)
3071         {
3072           installed_options = &ppd->groups[i];
3073           break;
3074         }
3075     }
3076
3077   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
3078     {
3079       option1 = ppdFindOption (ppd, constraint->option1);
3080       if (option1 == NULL)
3081         continue;
3082
3083       option2 = ppdFindOption (ppd, constraint->option2);
3084       if (option2 == NULL)
3085         continue;
3086
3087       if (option == option1)
3088         {
3089           choice = constraint->choice1;
3090           other_option = option2;
3091           other_choice = constraint->choice2;
3092         }
3093       else if (option == option2)
3094         {
3095           choice = constraint->choice2;
3096           other_option = option1;
3097           other_choice = constraint->choice1;
3098         }
3099       else
3100         continue;
3101
3102       /* We only care of conflicts with installed_options and
3103          PageSize */
3104       if (!group_has_option (installed_options, other_option) &&
3105           (strcmp (other_option->keyword, "PageSize") != 0))
3106         continue;
3107
3108       if (*other_choice == 0)
3109         {
3110           /* Conflict only if the installed option is not off */
3111           if (value_is_off (other_option->defchoice))
3112             continue;
3113         }
3114       /* Conflict if the installed option has the specified default */
3115       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
3116         continue;
3117
3118       if (*choice == 0)
3119         {
3120           /* Conflict with all non-off choices */
3121           for (j = 0; j < option->num_choices; j++)
3122             {
3123               if (!value_is_off (option->choices[j].choice))
3124                 conflicts[j] = 1;
3125             }
3126         }
3127       else
3128         {
3129           for (j = 0; j < option->num_choices; j++)
3130             {
3131               if (strcasecmp (option->choices[j].choice, choice) == 0)
3132                 conflicts[j] = 1;
3133             }
3134         }
3135     }
3136
3137   num_conflicts = 0;
3138   all_default = TRUE;
3139   for (j = 0; j < option->num_choices; j++)
3140     {
3141       if (conflicts[j])
3142         num_conflicts++;
3143       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
3144         all_default = FALSE;
3145     }
3146
3147   if ((all_default && !keep_if_only_one_option) ||
3148       (num_conflicts == option->num_choices))
3149     {
3150       g_free (conflicts);
3151
3152       return 0;
3153     }
3154
3155   /* Some ppds don't have a "use printer default" option for
3156    * InputSlot. This means you always have to select a particular slot,
3157    * and you can't auto-pick source based on the paper size. To support
3158    * this we always add an auto option if there isn't one already. If
3159    * the user chooses the generated option we don't send any InputSlot
3160    * value when printing. The way we detect existing auto-cases is based
3161    * on feedback from Michael Sweet of cups fame.
3162    */
3163   add_auto = 0;
3164   if (strcmp (option->keyword, "InputSlot") == 0)
3165     {
3166       gboolean found_auto = FALSE;
3167       for (j = 0; j < option->num_choices; j++)
3168         {
3169           if (!conflicts[j])
3170             {
3171               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
3172                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
3173                   strcmp (option->choices[j].choice, "Default") == 0 ||
3174                   strcmp (option->choices[j].choice, "None") == 0 ||
3175                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
3176                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
3177                   option->choices[j].code == NULL ||
3178                   option->choices[j].code[0] == 0)
3179                 {
3180                   found_auto = TRUE;
3181                   break;
3182                 }
3183             }
3184         }
3185
3186       if (!found_auto)
3187         add_auto = 1;
3188     }
3189   
3190   if (available)
3191     {
3192       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
3193
3194       i = 0;
3195       for (j = 0; j < option->num_choices; j++)
3196         {
3197           if (!conflicts[j])
3198             (*available)[i++] = &option->choices[j];
3199         }
3200
3201       if (add_auto) 
3202         (*available)[i++] = NULL;
3203     }
3204
3205   g_free (conflicts);
3206   
3207   return option->num_choices - num_conflicts + add_auto;
3208 }
3209
3210 static GtkPrinterOption *
3211 create_pickone_option (ppd_file_t   *ppd_file,
3212                        ppd_option_t *ppd_option,
3213                        const gchar  *gtk_name)
3214 {
3215   GtkPrinterOption *option;
3216   ppd_choice_t **available;
3217   char *label;
3218   int n_choices;
3219   int i;
3220 #ifdef HAVE_CUPS_API_1_2
3221   ppd_coption_t *coption;
3222 #endif
3223
3224   g_assert (ppd_option->ui == PPD_UI_PICKONE);
3225   
3226   option = NULL;
3227
3228   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3229   if (n_choices > 0)
3230     {
3231       
3232       /* right now only support one parameter per custom option 
3233        * if more than one print warning and only offer the default choices
3234        */
3235
3236       label = get_option_text (ppd_file, ppd_option);
3237
3238 #ifdef HAVE_CUPS_API_1_2
3239       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
3240
3241       if (coption)
3242         {
3243           ppd_cparam_t *cparam;
3244
3245           cparam = ppdFirstCustomParam (coption);
3246
3247           if (ppdNextCustomParam (coption) == NULL)
3248             {
3249               switch (cparam->type)
3250                 {
3251                 case PPD_CUSTOM_INT:
3252                   option = gtk_printer_option_new (gtk_name, label,
3253                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
3254                   break;
3255                 case PPD_CUSTOM_PASSCODE:
3256                   option = gtk_printer_option_new (gtk_name, label,
3257                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
3258                   break;
3259                 case PPD_CUSTOM_PASSWORD:
3260                     option = gtk_printer_option_new (gtk_name, label,
3261                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
3262                   break;
3263                case PPD_CUSTOM_REAL:
3264                     option = gtk_printer_option_new (gtk_name, label,
3265                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
3266                   break;
3267                 case PPD_CUSTOM_STRING:
3268                   option = gtk_printer_option_new (gtk_name, label,
3269                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
3270                   break;
3271 #ifdef PRINT_IGNORED_OPTIONS
3272                 case PPD_CUSTOM_POINTS: 
3273                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
3274                   break;
3275                 case PPD_CUSTOM_CURVE:
3276                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
3277                   break;
3278                 case PPD_CUSTOM_INVCURVE:       
3279                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
3280                   break;
3281 #endif
3282                 default: 
3283                   break;
3284                 }
3285             }
3286 #ifdef PRINT_IGNORED_OPTIONS
3287           else
3288             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
3289 #endif
3290         }
3291 #endif /* HAVE_CUPS_API_1_2 */
3292
3293       if (!option)
3294         option = gtk_printer_option_new (gtk_name, label,
3295                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
3296       g_free (label);
3297       
3298       gtk_printer_option_allocate_choices (option, n_choices);
3299       for (i = 0; i < n_choices; i++)
3300         {
3301           if (available[i] == NULL)
3302             {
3303               /* This was auto-added */
3304               option->choices[i] = g_strdup ("gtk-ignore-value");
3305               option->choices_display[i] = g_strdup (_("Printer Default"));
3306             }
3307           else
3308             {
3309               option->choices[i] = g_strdup (available[i]->choice);
3310               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
3311             }
3312         }
3313       gtk_printer_option_set (option, ppd_option->defchoice);
3314     }
3315 #ifdef PRINT_IGNORED_OPTIONS
3316   else
3317     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
3318 #endif
3319   g_free (available);
3320
3321   return option;
3322 }
3323
3324 static GtkPrinterOption *
3325 create_boolean_option (ppd_file_t   *ppd_file,
3326                        ppd_option_t *ppd_option,
3327                        const gchar  *gtk_name)
3328 {
3329   GtkPrinterOption *option;
3330   ppd_choice_t **available;
3331   char *label;
3332   int n_choices;
3333
3334   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
3335   
3336   option = NULL;
3337
3338   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3339   if (n_choices == 2)
3340     {
3341       label = get_option_text (ppd_file, ppd_option);
3342       option = gtk_printer_option_new (gtk_name, label,
3343                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
3344       g_free (label);
3345       
3346       gtk_printer_option_allocate_choices (option, 2);
3347       option->choices[0] = g_strdup ("True");
3348       option->choices_display[0] = g_strdup ("True");
3349       option->choices[1] = g_strdup ("False");
3350       option->choices_display[1] = g_strdup ("False");
3351       
3352       gtk_printer_option_set (option, ppd_option->defchoice);
3353     }
3354 #ifdef PRINT_IGNORED_OPTIONS
3355   else
3356     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
3357 #endif
3358   g_free (available);
3359
3360   return option;
3361 }
3362
3363 static gchar *
3364 get_ppd_option_name (const gchar *keyword)
3365 {
3366   int i;
3367
3368   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3369     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
3370       return g_strdup (ppd_option_names[i].name);
3371
3372   return g_strdup_printf ("cups-%s", keyword);
3373 }
3374
3375 static gchar *
3376 get_lpoption_name (const gchar *lpoption)
3377 {
3378   int i;
3379
3380   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3381     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
3382       return g_strdup (ppd_option_names[i].name);
3383
3384   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
3385     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
3386       return g_strdup (lpoption_names[i].name);
3387
3388   return g_strdup_printf ("cups-%s", lpoption);
3389 }
3390
3391 static int
3392 strptr_cmp (const void *a, 
3393             const void *b)
3394 {
3395   char **aa = (char **)a;
3396   char **bb = (char **)b;
3397   return strcmp (*aa, *bb);
3398 }
3399
3400
3401 static gboolean
3402 string_in_table (gchar       *str, 
3403                  const gchar *table[], 
3404                  gint         table_len)
3405 {
3406   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
3407 }
3408
3409 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
3410
3411 static void
3412 handle_option (GtkPrinterOptionSet *set,
3413                ppd_file_t          *ppd_file,
3414                ppd_option_t        *ppd_option,
3415                ppd_group_t         *toplevel_group,
3416                GtkPrintSettings    *settings)
3417 {
3418   GtkPrinterOption *option;
3419   char *name;
3420   int i;
3421
3422   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
3423     return;
3424
3425   name = get_ppd_option_name (ppd_option->keyword);
3426
3427   option = NULL;
3428   if (ppd_option->ui == PPD_UI_PICKONE)
3429     {
3430       option = create_pickone_option (ppd_file, ppd_option, name);
3431     }
3432   else if (ppd_option->ui == PPD_UI_BOOLEAN)
3433     {
3434       option = create_boolean_option (ppd_file, ppd_option, name);
3435     }
3436 #ifdef PRINT_IGNORED_OPTIONS
3437   else
3438     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
3439 #endif  
3440   
3441   if (option)
3442     {
3443       char *name;
3444
3445       name = ppd_group_name (toplevel_group);
3446       if (STRING_IN_TABLE (name,
3447                            color_group_whitelist) ||
3448           STRING_IN_TABLE (ppd_option->keyword,
3449                            color_option_whitelist))
3450         {
3451           option->group = g_strdup ("ColorPage");
3452         }
3453       else if (STRING_IN_TABLE (name,
3454                                 image_quality_group_whitelist) ||
3455                STRING_IN_TABLE (ppd_option->keyword,
3456                                 image_quality_option_whitelist))
3457         {
3458           option->group = g_strdup ("ImageQualityPage");
3459         }
3460       else if (STRING_IN_TABLE (name,
3461                                 finishing_group_whitelist) ||
3462                STRING_IN_TABLE (ppd_option->keyword,
3463                                 finishing_option_whitelist))
3464         {
3465           option->group = g_strdup ("FinishingPage");
3466         }
3467       else
3468         {
3469           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
3470             {
3471               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
3472                 {
3473                   option->group = g_strdup (_(cups_group_translations[i].translation));
3474                   break;
3475                 }
3476             }
3477
3478           if (i == G_N_ELEMENTS (cups_group_translations))
3479             option->group = g_strdup (toplevel_group->text);
3480         }
3481
3482       set_option_from_settings (option, settings);
3483       
3484       gtk_printer_option_set_add (set, option);
3485     }
3486   
3487   g_free (name);
3488 }
3489
3490 static void
3491 handle_group (GtkPrinterOptionSet *set,
3492               ppd_file_t          *ppd_file,
3493               ppd_group_t         *group,
3494               ppd_group_t         *toplevel_group,
3495               GtkPrintSettings    *settings)
3496 {
3497   gint i;
3498   gchar *name;
3499   
3500   /* Ignore installable options */
3501   name = ppd_group_name (toplevel_group);
3502   if (strcmp (name, "InstallableOptions") == 0)
3503     return;
3504   
3505   for (i = 0; i < group->num_options; i++)
3506     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
3507
3508   for (i = 0; i < group->num_subgroups; i++)
3509     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
3510
3511 }
3512
3513 static GtkPrinterOptionSet *
3514 cups_printer_get_options (GtkPrinter           *printer,
3515                           GtkPrintSettings     *settings,
3516                           GtkPageSetup         *page_setup,
3517                           GtkPrintCapabilities  capabilities)
3518 {
3519   GtkPrinterOptionSet *set;
3520   GtkPrinterOption *option;
3521   ppd_file_t *ppd_file;
3522   int i;
3523   char *print_at[] = { "now", "at", "on-hold" };
3524   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
3525   char *prio[] = {"100", "80", "50", "30" };
3526   /* Translators: These strings name the possible values of the 
3527    * job priority option in the print dialog
3528    */
3529   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
3530   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
3531   /* Translators: These strings name the possible arrangements of
3532    * multiple pages on a sheet when printing
3533    */
3534   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"), 
3535                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"), 
3536                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"), 
3537                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
3538   char *name;
3539   int num_opts;
3540   cups_option_t *opts = NULL;
3541   GtkPrintBackendCups *backend;
3542   GtkTextDirection text_direction;
3543   GtkPrinterCups *cups_printer = NULL;
3544
3545
3546   set = gtk_printer_option_set_new ();
3547
3548   /* Cups specific, non-ppd related settings */
3549
3550    /* Translators, this string is used to label the pages-per-sheet option 
3551     * in the print dialog 
3552     */
3553   option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3554   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
3555                                          n_up, n_up);
3556   gtk_printer_option_set (option, "1");
3557   set_option_from_settings (option, settings);
3558   gtk_printer_option_set_add (set, option);
3559   g_object_unref (option);
3560
3561   if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
3562     {
3563       for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
3564         n_up_layout_display[i] = _(n_up_layout_display[i]);
3565   
3566        /* Translators, this string is used to label the option in the print 
3567         * dialog that controls in what order multiple pages are arranged 
3568         */
3569       option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3570       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
3571                                              n_up_layout, n_up_layout_display);
3572
3573       text_direction = gtk_widget_get_default_direction ();
3574       if (text_direction == GTK_TEXT_DIR_LTR)
3575         gtk_printer_option_set (option, "lrtb");
3576       else
3577         gtk_printer_option_set (option, "rltb");
3578
3579       set_option_from_settings (option, settings);
3580       gtk_printer_option_set_add (set, option);
3581       g_object_unref (option);
3582     }
3583
3584   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
3585     prio_display[i] = _(prio_display[i]);
3586   
3587   /* Translators, this string is used to label the job priority option 
3588    * in the print dialog 
3589    */
3590   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3591   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
3592                                          prio, prio_display);
3593   gtk_printer_option_set (option, "50");
3594   set_option_from_settings (option, settings);
3595   gtk_printer_option_set_add (set, option);
3596   g_object_unref (option);
3597
3598   /* Translators, this string is used to label the billing info entry
3599    * in the print dialog 
3600    */
3601   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
3602   gtk_printer_option_set (option, "");
3603   set_option_from_settings (option, settings);
3604   gtk_printer_option_set_add (set, option);
3605   g_object_unref (option);
3606
3607   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
3608   cups_printer = GTK_PRINTER_CUPS (printer);
3609
3610   if (backend != NULL && printer != NULL)
3611     {
3612       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
3613       /* Translators, these strings are names for various 'standard' cover 
3614        * pages that the printing system may support.
3615        */
3616       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
3617       char **cover = NULL;
3618       char **cover_display = NULL;
3619       char **cover_display_translated = NULL;
3620       gint num_of_covers = 0;
3621       gpointer value;
3622       gint j;
3623
3624       num_of_covers = backend->number_of_covers;
3625       cover = g_new (char *, num_of_covers + 1);
3626       cover[num_of_covers] = NULL;
3627       cover_display = g_new (char *, num_of_covers + 1);
3628       cover_display[num_of_covers] = NULL;
3629       cover_display_translated = g_new (char *, num_of_covers + 1);
3630       cover_display_translated[num_of_covers] = NULL;
3631
3632       for (i = 0; i < num_of_covers; i++)
3633         {
3634           cover[i] = g_strdup (backend->covers[i]);
3635           value = NULL;
3636           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
3637             if (strcmp (cover_default[j], cover[i]) == 0)
3638               {
3639                 value = cover_display_default[j];
3640                 break;
3641               }
3642           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
3643         }
3644
3645       for (i = 0; i < num_of_covers; i++)
3646         cover_display_translated[i] = _(cover_display[i]);
3647   
3648       /* Translators, this is the label used for the option in the print 
3649        * dialog that controls the front cover page.
3650        */
3651       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3652       gtk_printer_option_choices_from_array (option, num_of_covers,
3653                                          cover, cover_display_translated);
3654
3655       if (cups_printer->default_cover_before != NULL)
3656         gtk_printer_option_set (option, cups_printer->default_cover_before);
3657       else
3658         gtk_printer_option_set (option, "none");
3659       set_option_from_settings (option, settings);
3660       gtk_printer_option_set_add (set, option);
3661       g_object_unref (option);
3662
3663       /* Translators, this is the label used for the option in the print 
3664        * dialog that controls the back cover page.
3665        */
3666       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3667       gtk_printer_option_choices_from_array (option, num_of_covers,
3668                                          cover, cover_display_translated);
3669       if (cups_printer->default_cover_after != NULL)
3670         gtk_printer_option_set (option, cups_printer->default_cover_after);
3671       else
3672         gtk_printer_option_set (option, "none");
3673       set_option_from_settings (option, settings);
3674       gtk_printer_option_set_add (set, option);
3675       g_object_unref (option);
3676
3677       g_strfreev (cover);
3678       g_strfreev (cover_display);
3679       g_free (cover_display_translated);
3680     }
3681
3682   /* Translators: this is the name of the option that controls when
3683    * a print job is printed. Possible values are 'now', a specified time,
3684    * or 'on hold'
3685    */
3686   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3687   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
3688                                          print_at, print_at);
3689   gtk_printer_option_set (option, "now");
3690   set_option_from_settings (option, settings);
3691   gtk_printer_option_set_add (set, option);
3692   g_object_unref (option);
3693   
3694   /* Translators: this is the name of the option that allows the user
3695    * to specify a time when a print job will be printed.
3696    */
3697   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
3698   gtk_printer_option_set (option, "");
3699   set_option_from_settings (option, settings);
3700   gtk_printer_option_set_add (set, option);
3701   g_object_unref (option);
3702   
3703   /* Printer (ppd) specific settings */
3704   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3705   if (ppd_file)
3706     {
3707       GtkPaperSize *paper_size;
3708       ppd_option_t *option;
3709       const gchar  *ppd_name;
3710
3711       ppdMarkDefaults (ppd_file);
3712
3713       paper_size = gtk_page_setup_get_paper_size (page_setup);
3714
3715       option = ppdFindOption (ppd_file, "PageSize");
3716       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
3717       
3718       if (ppd_name)
3719         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
3720       else
3721         {
3722           gchar *custom_name;
3723           char width[G_ASCII_DTOSTR_BUF_SIZE];
3724           char height[G_ASCII_DTOSTR_BUF_SIZE];
3725
3726           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3727           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3728           /* Translators: this format is used to display a custom paper
3729            * size. The two placeholders are replaced with the width and height
3730            * in points. E.g: "Custom 230.4x142.9"
3731            */
3732           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
3733           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
3734           g_free (custom_name);
3735         }
3736
3737       for (i = 0; i < ppd_file->num_groups; i++)
3738         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
3739     }
3740
3741   /* Now honor the user set defaults for this printer */
3742   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
3743
3744   for (i = 0; i < num_opts; i++)
3745     {
3746       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
3747         continue;
3748
3749       name = get_lpoption_name (opts[i].name);
3750       if (strcmp (name, "cups-job-sheets") == 0)
3751         {
3752           gchar **values;
3753           gint    num_values;
3754           
3755           values = g_strsplit (opts[i].value, ",", 2);
3756           num_values = g_strv_length (values);
3757
3758           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
3759           if (option && num_values > 0)
3760             gtk_printer_option_set (option, g_strstrip (values[0]));
3761
3762           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
3763           if (option && num_values > 1)
3764             gtk_printer_option_set (option, g_strstrip (values[1]));
3765
3766           g_strfreev (values);
3767         }
3768       else if (strcmp (name, "cups-job-hold-until") == 0)
3769         {
3770           GtkPrinterOption *option2 = NULL;
3771
3772           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
3773           if (option && opts[i].value)
3774             {
3775               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
3776               if (option2)
3777                 {
3778                   if (strcmp (opts[i].value, "indefinite") == 0)
3779                     gtk_printer_option_set (option2, "on-hold");
3780                   else
3781                     {
3782                       gtk_printer_option_set (option2, "at");
3783                       gtk_printer_option_set (option, opts[i].value);
3784                     }
3785                 }
3786             }
3787         }
3788       else if (strcmp (name, "cups-sides") == 0)
3789         {
3790           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
3791           if (option && opts[i].value)
3792             {
3793               if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
3794                 gtk_printer_option_set (option, "DuplexTumble");
3795               else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
3796                 gtk_printer_option_set (option, "DuplexNoTumble");
3797             }
3798         }
3799       else
3800         {
3801           option = gtk_printer_option_set_lookup (set, name);
3802           if (option)
3803             gtk_printer_option_set (option, opts[i].value);
3804         }
3805       g_free (name);
3806     }
3807
3808   cupsFreeOptions (num_opts, opts);
3809
3810   return set;
3811 }
3812
3813
3814 static void
3815 mark_option_from_set (GtkPrinterOptionSet *set,
3816                       ppd_file_t          *ppd_file,
3817                       ppd_option_t        *ppd_option)
3818 {
3819   GtkPrinterOption *option;
3820   char *name = get_ppd_option_name (ppd_option->keyword);
3821
3822   option = gtk_printer_option_set_lookup (set, name);
3823
3824   if (option)
3825     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
3826   
3827   g_free (name);
3828 }
3829
3830
3831 static void
3832 mark_group_from_set (GtkPrinterOptionSet *set,
3833                      ppd_file_t          *ppd_file,
3834                      ppd_group_t         *group)
3835 {
3836   int i;
3837
3838   for (i = 0; i < group->num_options; i++)
3839     mark_option_from_set (set, ppd_file, &group->options[i]);
3840
3841   for (i = 0; i < group->num_subgroups; i++)
3842     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
3843 }
3844
3845 static void
3846 set_conflicts_from_option (GtkPrinterOptionSet *set,
3847                            ppd_file_t          *ppd_file,
3848                            ppd_option_t        *ppd_option)
3849 {
3850   GtkPrinterOption *option;
3851   char *name;
3852
3853   if (ppd_option->conflicted)
3854     {
3855       name = get_ppd_option_name (ppd_option->keyword);
3856       option = gtk_printer_option_set_lookup (set, name);
3857
3858       if (option)
3859         gtk_printer_option_set_has_conflict (option, TRUE);
3860 #ifdef PRINT_IGNORED_OPTIONS
3861       else
3862         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
3863 #endif
3864       
3865       g_free (name);
3866     }
3867 }
3868
3869 static void
3870 set_conflicts_from_group (GtkPrinterOptionSet *set,
3871                           ppd_file_t          *ppd_file,
3872                           ppd_group_t         *group)
3873 {
3874   int i;
3875
3876   for (i = 0; i < group->num_options; i++)
3877     set_conflicts_from_option (set, ppd_file, &group->options[i]);
3878
3879   for (i = 0; i < group->num_subgroups; i++)
3880     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
3881 }
3882
3883 static gboolean
3884 cups_printer_mark_conflicts (GtkPrinter          *printer,
3885                              GtkPrinterOptionSet *options)
3886 {
3887   ppd_file_t *ppd_file;
3888   int num_conflicts;
3889   int i;
3890  
3891   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3892
3893   if (ppd_file == NULL)
3894     return FALSE;
3895
3896   ppdMarkDefaults (ppd_file);
3897
3898   for (i = 0; i < ppd_file->num_groups; i++)
3899     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
3900
3901   num_conflicts = ppdConflicts (ppd_file);
3902
3903   if (num_conflicts > 0)
3904     {
3905       for (i = 0; i < ppd_file->num_groups; i++)
3906         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
3907     }
3908  
3909   return num_conflicts > 0;
3910 }
3911
3912 struct OptionData {
3913   GtkPrinter *printer;
3914   GtkPrinterOptionSet *options;
3915   GtkPrintSettings *settings;
3916   ppd_file_t *ppd_file;
3917 };
3918
3919 typedef struct {
3920   const char *cups;
3921   const char *standard;
3922 } NameMapping;
3923
3924 static void
3925 map_settings_to_option (GtkPrinterOption  *option,
3926                         const NameMapping  table[],
3927                         gint               n_elements,
3928                         GtkPrintSettings  *settings,
3929                         const gchar       *standard_name,
3930                         const gchar       *cups_name)
3931 {
3932   int i;
3933   char *name;
3934   const char *cups_value;
3935   const char *standard_value;
3936
3937   /* If the cups-specific setting is set, always use that */
3938   name = g_strdup_printf ("cups-%s", cups_name);
3939   cups_value = gtk_print_settings_get (settings, name);
3940   g_free (name);
3941   
3942   if (cups_value != NULL) 
3943     {
3944       gtk_printer_option_set (option, cups_value);
3945       return;
3946     }
3947
3948   /* Otherwise we try to convert from the general setting */
3949   standard_value = gtk_print_settings_get (settings, standard_name);
3950   if (standard_value == NULL)
3951     return;
3952
3953   for (i = 0; i < n_elements; i++)
3954     {
3955       if (table[i].cups == NULL && table[i].standard == NULL)
3956         {
3957           gtk_printer_option_set (option, standard_value);
3958           break;
3959         }
3960       else if (table[i].cups == NULL &&
3961                strcmp (table[i].standard, standard_value) == 0)
3962         {
3963           set_option_off (option);
3964           break;
3965         }
3966       else if (strcmp (table[i].standard, standard_value) == 0)
3967         {
3968           gtk_printer_option_set (option, table[i].cups);
3969           break;
3970         }
3971     }
3972 }
3973
3974 static void
3975 map_option_to_settings (const gchar       *value,
3976                         const NameMapping  table[],
3977                         gint               n_elements,
3978                         GtkPrintSettings  *settings,
3979                         const gchar       *standard_name,
3980                         const gchar       *cups_name)
3981 {
3982   int i;
3983   char *name;
3984
3985   for (i = 0; i < n_elements; i++)
3986     {
3987       if (table[i].cups == NULL && table[i].standard == NULL)
3988         {
3989           gtk_print_settings_set (settings,
3990                                   standard_name,
3991                                   value);
3992           break;
3993         }
3994       else if (table[i].cups == NULL && table[i].standard != NULL)
3995         {
3996           if (value_is_off (value))
3997             {
3998               gtk_print_settings_set (settings,
3999                                       standard_name,
4000                                       table[i].standard);
4001               break;
4002             }
4003         }
4004       else if (strcmp (table[i].cups, value) == 0)
4005         {
4006           gtk_print_settings_set (settings,
4007                                   standard_name,
4008                                   table[i].standard);
4009           break;
4010         }
4011     }
4012
4013   /* Always set the corresponding cups-specific setting */
4014   name = g_strdup_printf ("cups-%s", cups_name);
4015   gtk_print_settings_set (settings, name, value);
4016   g_free (name);
4017 }
4018
4019
4020 static const NameMapping paper_source_map[] = {
4021   { "Lower", "lower"},
4022   { "Middle", "middle"},
4023   { "Upper", "upper"},
4024   { "Rear", "rear"},
4025   { "Envelope", "envelope"},
4026   { "Cassette", "cassette"},
4027   { "LargeCapacity", "large-capacity"},
4028   { "AnySmallFormat", "small-format"},
4029   { "AnyLargeFormat", "large-format"},
4030   { NULL, NULL}
4031 };
4032
4033 static const NameMapping output_tray_map[] = {
4034   { "Upper", "upper"},
4035   { "Lower", "lower"},
4036   { "Rear", "rear"},
4037   { NULL, NULL}
4038 };
4039
4040 static const NameMapping duplex_map[] = {
4041   { "DuplexTumble", "vertical" },
4042   { "DuplexNoTumble", "horizontal" },
4043   { NULL, "simplex" }
4044 };
4045
4046 static const NameMapping output_mode_map[] = {
4047   { "Standard", "normal" },
4048   { "Normal", "normal" },
4049   { "Draft", "draft" },
4050   { "Fast", "draft" },
4051 };
4052
4053 static const NameMapping media_type_map[] = {
4054   { "Transparency", "transparency"},
4055   { "Standard", "stationery"},
4056   { NULL, NULL}
4057 };
4058
4059 static const NameMapping all_map[] = {
4060   { NULL, NULL}
4061 };
4062
4063
4064 static void
4065 set_option_from_settings (GtkPrinterOption *option,
4066                           GtkPrintSettings *settings)
4067 {
4068   const char *cups_value;
4069   char *value;
4070   
4071   if (settings == NULL)
4072     return;
4073
4074   if (strcmp (option->name, "gtk-paper-source") == 0)
4075     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
4076                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4077   else if (strcmp (option->name, "gtk-output-tray") == 0)
4078     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
4079                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4080   else if (strcmp (option->name, "gtk-duplex") == 0)
4081     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
4082                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4083   else if (strcmp (option->name, "cups-OutputMode") == 0)
4084     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
4085                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4086   else if (strcmp (option->name, "cups-Resolution") == 0)
4087     {
4088       cups_value = gtk_print_settings_get (settings, option->name);
4089       if (cups_value)
4090         gtk_printer_option_set (option, cups_value);
4091       else
4092         {
4093           int res = gtk_print_settings_get_resolution (settings);
4094           int res_x = gtk_print_settings_get_resolution_x (settings);
4095           int res_y = gtk_print_settings_get_resolution_y (settings);
4096
4097           if (res_x != res_y)
4098             {
4099               value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
4100               gtk_printer_option_set (option, value);
4101               g_free (value);
4102             }
4103           else if (res != 0)
4104             {
4105               value = g_strdup_printf ("%ddpi", res);
4106               gtk_printer_option_set (option, value);
4107               g_free (value);
4108             }
4109         }
4110     }
4111   else if (strcmp (option->name, "gtk-paper-type") == 0)
4112     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
4113                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4114   else if (strcmp (option->name, "gtk-n-up") == 0)
4115     {
4116       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4117                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4118     }
4119   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4120     {
4121       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4122                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4123     }
4124   else if (strcmp (option->name, "gtk-billing-info") == 0)
4125     {
4126       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
4127       if (cups_value)
4128         gtk_printer_option_set (option, cups_value);
4129     } 
4130   else if (strcmp (option->name, "gtk-job-prio") == 0)
4131     {
4132       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
4133       if (cups_value)
4134         gtk_printer_option_set (option, cups_value);
4135     } 
4136   else if (strcmp (option->name, "gtk-cover-before") == 0)
4137     {
4138       cups_value = gtk_print_settings_get (settings, "cover-before");
4139       if (cups_value)
4140         gtk_printer_option_set (option, cups_value);
4141     } 
4142   else if (strcmp (option->name, "gtk-cover-after") == 0)
4143     {
4144       cups_value = gtk_print_settings_get (settings, "cover-after");
4145       if (cups_value)
4146         gtk_printer_option_set (option, cups_value);
4147     } 
4148   else if (strcmp (option->name, "gtk-print-time") == 0)
4149     {
4150       cups_value = gtk_print_settings_get (settings, "print-at");
4151       if (cups_value)
4152         gtk_printer_option_set (option, cups_value);
4153     } 
4154   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4155     {
4156       cups_value = gtk_print_settings_get (settings, "print-at-time");
4157       if (cups_value)
4158         gtk_printer_option_set (option, cups_value);
4159     } 
4160   else if (g_str_has_prefix (option->name, "cups-"))
4161     {
4162       cups_value = gtk_print_settings_get (settings, option->name);
4163       if (cups_value)
4164         gtk_printer_option_set (option, cups_value);
4165     } 
4166 }
4167
4168 static void
4169 foreach_option_get_settings (GtkPrinterOption *option,
4170                              gpointer          user_data)
4171 {
4172   struct OptionData *data = user_data;
4173   GtkPrintSettings *settings = data->settings;
4174   const char *value;
4175
4176   value = option->value;
4177
4178   if (strcmp (option->name, "gtk-paper-source") == 0)
4179     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
4180                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4181   else if (strcmp (option->name, "gtk-output-tray") == 0)
4182     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
4183                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4184   else if (strcmp (option->name, "gtk-duplex") == 0)
4185     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
4186                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4187   else if (strcmp (option->name, "cups-OutputMode") == 0)
4188     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
4189                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4190   else if (strcmp (option->name, "cups-Resolution") == 0)
4191     {
4192       int res, res_x, res_y;
4193
4194       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
4195         {
4196           if (res_x > 0 && res_y > 0)
4197             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
4198         }
4199       else if (sscanf (value, "%ddpi", &res) == 1)
4200         {
4201           if (res > 0)
4202             gtk_print_settings_set_resolution (settings, res);
4203         }
4204
4205       gtk_print_settings_set (settings, option->name, value);
4206     }
4207   else if (strcmp (option->name, "gtk-paper-type") == 0)
4208     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
4209                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4210   else if (strcmp (option->name, "gtk-n-up") == 0)
4211     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4212                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4213   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4214     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4215                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4216   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
4217     gtk_print_settings_set (settings, "cups-job-billing", value);
4218   else if (strcmp (option->name, "gtk-job-prio") == 0)
4219     gtk_print_settings_set (settings, "cups-job-priority", value);
4220   else if (strcmp (option->name, "gtk-cover-before") == 0)
4221     gtk_print_settings_set (settings, "cover-before", value);
4222   else if (strcmp (option->name, "gtk-cover-after") == 0)
4223     gtk_print_settings_set (settings, "cover-after", value);
4224   else if (strcmp (option->name, "gtk-print-time") == 0)
4225     gtk_print_settings_set (settings, "print-at", value);
4226   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4227     gtk_print_settings_set (settings, "print-at-time", value);
4228   else if (g_str_has_prefix (option->name, "cups-"))
4229     gtk_print_settings_set (settings, option->name, value);
4230 }
4231
4232 static gboolean
4233 supports_am_pm (void)
4234 {
4235   struct tm tmp_tm = { 0 };
4236   char   time[8];
4237   int    length;
4238
4239   length = strftime (time, sizeof (time), "%p", &tmp_tm);
4240
4241   return length != 0;
4242 }
4243
4244 /* Converts local time to UTC time. Local time has to be in one of these
4245  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
4246  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
4247  * Returns a newly allocated string holding UTC time in HH:MM:SS format
4248  * or NULL.
4249  */
4250 gchar *
4251 localtime_to_utctime (const char *local_time)
4252 {
4253   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
4254                              " %H : %M : %S ",
4255                              " %I : %M %p ", " %p %I : %M ",
4256                              " %H : %M ",
4257                              " %I %p ", " %p %I "};
4258   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
4259   const char *end = NULL;
4260   struct tm  *actual_local_time;
4261   struct tm  *actual_utc_time;
4262   struct tm   local_print_time;
4263   struct tm   utc_print_time;
4264   struct tm   diff_time;
4265   gchar      *utc_time = NULL;
4266   int         i, n;
4267
4268   if (local_time == NULL || local_time[0] == '\0')
4269     return NULL;
4270
4271   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
4272
4273   for (i = 0; i < n; i++)
4274     {
4275       local_print_time.tm_hour = 0;
4276       local_print_time.tm_min  = 0;
4277       local_print_time.tm_sec  = 0;
4278
4279       if (supports_am_pm ())
4280         end = strptime (local_time, formats_0[i], &local_print_time);
4281       else
4282         end = strptime (local_time, formats_1[i], &local_print_time);
4283
4284       if (end != NULL && end[0] == '\0')
4285         break;
4286     }
4287
4288   if (end != NULL && end[0] == '\0')
4289     {
4290       time_t rawtime;
4291       time (&rawtime);
4292
4293       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
4294       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
4295
4296       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
4297       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
4298       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
4299
4300       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
4301       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
4302       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
4303
4304       utc_time = g_strdup_printf ("%02d:%02d:%02d",
4305                                   utc_print_time.tm_hour,
4306                                   utc_print_time.tm_min,
4307                                   utc_print_time.tm_sec);
4308     }
4309
4310   return utc_time;
4311 }
4312
4313 static void
4314 cups_printer_get_settings_from_options (GtkPrinter          *printer,
4315                                         GtkPrinterOptionSet *options,
4316                                         GtkPrintSettings    *settings)
4317 {
4318   struct OptionData data;
4319   const char *print_at, *print_at_time;
4320
4321   data.printer = printer;
4322   data.options = options;
4323   data.settings = settings;
4324   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4325  
4326   if (data.ppd_file != NULL)
4327     {
4328       GtkPrinterOption *cover_before, *cover_after;
4329       
4330       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
4331
4332       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
4333       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
4334       if (cover_before && cover_after)
4335         {
4336           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
4337           gtk_print_settings_set (settings, "cups-job-sheets", value);
4338           g_free (value);
4339         }
4340
4341       print_at = gtk_print_settings_get (settings, "print-at");
4342       print_at_time = gtk_print_settings_get (settings, "print-at-time");
4343
4344       if (strcmp (print_at, "at") == 0)
4345         {
4346           gchar *utc_time = NULL;
4347           
4348           utc_time = localtime_to_utctime (print_at_time);
4349
4350           if (utc_time != NULL)
4351             {
4352               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
4353               g_free (utc_time);
4354             }
4355           else
4356             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
4357         }
4358       else if (strcmp (print_at, "on-hold") == 0)
4359         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
4360     }
4361 }
4362
4363 static void
4364 cups_printer_prepare_for_print (GtkPrinter       *printer,
4365                                 GtkPrintJob      *print_job,
4366                                 GtkPrintSettings *settings,
4367                                 GtkPageSetup     *page_setup)
4368 {
4369   GtkPrintPages pages;
4370   GtkPageRange *ranges;
4371   gint n_ranges;
4372   GtkPageSet page_set;
4373   GtkPaperSize *paper_size;
4374   const char *ppd_paper_name;
4375   double scale;
4376
4377   pages = gtk_print_settings_get_print_pages (settings);
4378   gtk_print_job_set_pages (print_job, pages);
4379
4380   if (pages == GTK_PRINT_PAGES_RANGES)
4381     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
4382   else
4383     {
4384       ranges = NULL;
4385       n_ranges = 0;
4386     }
4387
4388   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
4389   if (gtk_print_settings_get_collate (settings))
4390     gtk_print_settings_set (settings, "cups-Collate", "True");
4391   gtk_print_job_set_collate (print_job, FALSE);
4392
4393   if (gtk_print_settings_get_reverse (settings))
4394     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
4395   gtk_print_job_set_reverse (print_job, FALSE);
4396
4397   if (gtk_print_settings_get_n_copies (settings) > 1)
4398     gtk_print_settings_set_int (settings, "cups-copies",
4399                                 gtk_print_settings_get_n_copies (settings));
4400   gtk_print_job_set_num_copies (print_job, 1);
4401
4402   scale = gtk_print_settings_get_scale (settings);
4403   if (scale != 100.0)
4404     gtk_print_job_set_scale (print_job, scale / 100.0);
4405
4406   page_set = gtk_print_settings_get_page_set (settings);
4407   if (page_set == GTK_PAGE_SET_EVEN)
4408     gtk_print_settings_set (settings, "cups-page-set", "even");
4409   else if (page_set == GTK_PAGE_SET_ODD)
4410     gtk_print_settings_set (settings, "cups-page-set", "odd");
4411   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
4412
4413   paper_size = gtk_page_setup_get_paper_size (page_setup);
4414   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
4415   if (ppd_paper_name != NULL)
4416     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
4417   else
4418     {
4419       char width[G_ASCII_DTOSTR_BUF_SIZE];
4420       char height[G_ASCII_DTOSTR_BUF_SIZE];
4421       char *custom_name;
4422
4423       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
4424       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
4425       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
4426       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
4427       g_free (custom_name);
4428     }
4429
4430   if (gtk_print_settings_get_number_up (settings) > 1)
4431     {
4432       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
4433       GEnumClass        *enum_class;
4434       GEnumValue        *enum_value;
4435
4436       switch (gtk_page_setup_get_orientation (page_setup))
4437         {
4438           case GTK_PAGE_ORIENTATION_PORTRAIT:
4439             break;
4440           case GTK_PAGE_ORIENTATION_LANDSCAPE:
4441             if (layout < 4)
4442               layout = layout + 2 + 4 * (1 - layout / 2);
4443             else
4444               layout = layout - 3 - 2 * (layout % 2);
4445             break;
4446           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4447             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
4448             break;
4449           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4450             if (layout < 4)
4451               layout = layout + 5 - 2 * (layout % 2);
4452             else
4453               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
4454             break;
4455         }
4456
4457       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
4458       enum_value = g_enum_get_value (enum_class, layout);
4459       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
4460       g_type_class_unref (enum_class);
4461     }
4462
4463   gtk_print_job_set_rotate (print_job, TRUE);
4464 }
4465
4466 static GtkPageSetup *
4467 create_page_setup (ppd_file_t *ppd_file,
4468                    ppd_size_t *size)
4469  {
4470    char *display_name;
4471    GtkPageSetup *page_setup;
4472    GtkPaperSize *paper_size;
4473    ppd_option_t *option;
4474    ppd_choice_t *choice;
4475
4476   display_name = NULL;
4477   option = ppdFindOption (ppd_file, "PageSize");
4478   if (option)
4479     {
4480       choice = ppdFindChoice (option, size->name);
4481       if (choice)
4482         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
4483     }
4484
4485   if (display_name == NULL)
4486     display_name = g_strdup (size->name);
4487   
4488   page_setup = gtk_page_setup_new ();
4489   paper_size = gtk_paper_size_new_from_ppd (size->name,
4490                                             display_name,
4491                                             size->width,
4492                                             size->length);
4493   gtk_page_setup_set_paper_size (page_setup, paper_size);
4494   gtk_paper_size_free (paper_size);
4495   
4496   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
4497   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
4498   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
4499   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
4500   
4501   g_free (display_name);
4502
4503   return page_setup;
4504 }
4505
4506 static GList *
4507 cups_printer_list_papers (GtkPrinter *printer)
4508 {
4509   ppd_file_t *ppd_file;
4510   ppd_size_t *size;
4511   GtkPageSetup *page_setup;
4512   GList *l;
4513   int i;
4514
4515   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4516   if (ppd_file == NULL)
4517     return NULL;
4518
4519   l = NULL;
4520   
4521   for (i = 0; i < ppd_file->num_sizes; i++)
4522     {
4523       size = &ppd_file->sizes[i];      
4524
4525       page_setup = create_page_setup (ppd_file, size);
4526
4527       l = g_list_prepend (l, page_setup);
4528     }
4529
4530   return g_list_reverse (l);
4531 }
4532
4533 static GtkPageSetup *
4534 cups_printer_get_default_page_size (GtkPrinter *printer)
4535 {
4536   ppd_file_t *ppd_file;
4537   ppd_size_t *size;
4538   ppd_option_t *option;
4539
4540
4541   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4542   if (ppd_file == NULL)
4543     return NULL;
4544
4545   option = ppdFindOption (ppd_file, "PageSize");
4546   size = ppdPageSize (ppd_file, option->defchoice); 
4547
4548   return create_page_setup (ppd_file, size);
4549 }
4550
4551 static gboolean
4552 cups_printer_get_hard_margins (GtkPrinter *printer,
4553                                gdouble    *top,
4554                                gdouble    *bottom,
4555                                gdouble    *left,
4556                                gdouble    *right)
4557 {
4558   ppd_file_t *ppd_file;
4559
4560   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4561   if (ppd_file == NULL)
4562     return FALSE;
4563
4564   *left = ppd_file->custom_margins[0];
4565   *bottom = ppd_file->custom_margins[1];
4566   *right = ppd_file->custom_margins[2];
4567   *top = ppd_file->custom_margins[3];
4568
4569   return TRUE;
4570 }
4571
4572 static GtkPrintCapabilities
4573 cups_printer_get_capabilities (GtkPrinter *printer)
4574 {
4575   return
4576     GTK_PRINT_CAPABILITY_COPIES |
4577     GTK_PRINT_CAPABILITY_COLLATE |
4578     GTK_PRINT_CAPABILITY_REVERSE |
4579 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 1 && CUPS_VERSION_PATCH >= 15) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || CUPS_VERSION_MAJOR > 1
4580     GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
4581 #endif
4582     GTK_PRINT_CAPABILITY_NUMBER_UP;
4583 }