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