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