]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
Extract reasons and reasons_desc arrays to file level
[~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 /* Printer messages we're interested in */
1656 static const char * const printer_messages[] =
1657   {
1658     "toner-low",
1659     "toner-empty",
1660     "developer-low",
1661     "developer-empty",
1662     "marker-supply-low",
1663     "marker-supply-empty",
1664     "cover-open",
1665     "door-open",
1666     "media-low",
1667     "media-empty",
1668     "offline",
1669     "other"
1670   };
1671 /* Our translatable versions of the printer messages */
1672 static const char * printer_strings[] =
1673   {
1674     N_("Printer '%s' is low on toner."),
1675     N_("Printer '%s' has no toner left."),
1676     /* Translators: "Developer" like on photo development context */
1677     N_("Printer '%s' is low on developer."),
1678     /* Translators: "Developer" like on photo development context */
1679     N_("Printer '%s' is out of developer."),
1680     /* Translators: "marker" is one color bin of the printer */
1681     N_("Printer '%s' is low on at least one marker supply."),
1682     /* Translators: "marker" is one color bin of the printer */
1683     N_("Printer '%s' is out of at least one marker supply."),
1684     N_("The cover is open on printer '%s'."),
1685     N_("The door is open on printer '%s'."),
1686     N_("Printer '%s' is low on paper."),
1687     N_("Printer '%s' is out of paper."),
1688     N_("Printer '%s' is currently offline."),
1689     N_("There is a problem on printer '%s'.")
1690   };
1691
1692 static void
1693 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
1694                               GtkCupsResult       *result,
1695                               gpointer             user_data)
1696 {
1697   GtkPrintBackend *backend = GTK_PRINT_BACKEND (cups_backend);
1698   ipp_attribute_t *attr;
1699   ipp_t *response;
1700   gboolean list_has_changed;
1701   GList *removed_printer_checklist;
1702   gchar *remote_default_printer = NULL;
1703
1704   GDK_THREADS_ENTER ();
1705
1706   list_has_changed = FALSE;
1707
1708   GTK_NOTE (PRINTING,
1709             g_print ("CUPS Backend: %s\n", G_STRFUNC));
1710
1711   cups_backend->list_printers_pending = FALSE;
1712
1713   if (gtk_cups_result_is_error (result))
1714     {
1715       GTK_NOTE (PRINTING, 
1716                 g_warning ("CUPS Backend: Error getting printer list: %s %d %d", 
1717                            gtk_cups_result_get_error_string (result),
1718                            gtk_cups_result_get_error_type (result),
1719                            gtk_cups_result_get_error_code (result)));
1720
1721       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
1722           gtk_cups_result_get_error_code (result) == 1)
1723         {
1724           /* Canceled by user, stop popping up more password dialogs */
1725           if (cups_backend->list_printers_poll > 0)
1726             g_source_remove (cups_backend->list_printers_poll);
1727           cups_backend->list_printers_poll = 0;
1728           cups_backend->list_printers_attempts = 0;
1729         }
1730
1731       goto done;
1732     }
1733   
1734   /* Gather the names of the printers in the current queue
1735    * so we may check to see if they were removed 
1736    */
1737   removed_printer_checklist = gtk_print_backend_get_printer_list (backend);
1738                                                                   
1739   response = gtk_cups_result_get_response (result);
1740
1741   for (attr = response->attrs; attr != NULL; attr = attr->next)
1742     {
1743       GtkPrinter *printer;
1744       const gchar *printer_name = NULL;
1745       const gchar *printer_uri = NULL;
1746       const gchar *member_uris = NULL;
1747       const gchar *location = NULL;
1748       const gchar *description = NULL;
1749       const gchar *state_msg = NULL;
1750       gint state = 0;
1751       gint job_count = 0;
1752       gboolean status_changed = FALSE;
1753       GList *node;
1754       gint i,j;
1755       const gchar *reason_msg = NULL;
1756       gchar *reason_msg_desc = NULL;
1757       gchar *tmp_msg = NULL;
1758       gchar *tmp_msg2 = NULL;
1759       gint printer_state_reason_level = 0; /* 0 - none, 1 - report, 2 - warning, 3 - error */
1760       gboolean interested_in = FALSE;
1761       gboolean found = FALSE;
1762       gboolean is_paused = FALSE;
1763       gboolean is_accepting_jobs = TRUE;
1764       gboolean default_printer = FALSE;
1765       gboolean got_printer_type = FALSE;
1766       const gchar   *default_cover_before = NULL;
1767       const gchar   *default_cover_after = NULL;
1768       gboolean remote_printer = FALSE;
1769       gchar  **auth_info_required = NULL;
1770       gint     default_number_up = 1;
1771
1772       /* Skip leading attributes until we hit a printer...
1773        */
1774       while (attr != NULL && ippGetGroupTag (attr) != IPP_TAG_PRINTER)
1775         attr = attr->next;
1776
1777       if (attr == NULL)
1778         break;
1779
1780       while (attr != NULL && ippGetGroupTag (attr) == IPP_TAG_PRINTER)
1781       {
1782         if (strcmp (ippGetName (attr), "printer-name") == 0 &&
1783             ippGetValueTag (attr) == IPP_TAG_NAME)
1784             printer_name = ippGetString (attr, 0, NULL);
1785         else if (strcmp (ippGetName (attr), "printer-uri-supported") == 0 &&
1786                  ippGetValueTag (attr) == IPP_TAG_URI)
1787             printer_uri = ippGetString (attr, 0, NULL);
1788         else if (strcmp (ippGetName (attr), "member-uris") == 0 &&
1789                  ippGetValueTag (attr) == IPP_TAG_URI)
1790             member_uris = ippGetString (attr, 0, NULL);
1791         else if (strcmp (ippGetName (attr), "printer-location") == 0)
1792             location = ippGetString (attr, 0, NULL);
1793         else if (strcmp (ippGetName (attr), "printer-info") == 0)
1794             description = ippGetString (attr, 0, NULL);
1795         else if (strcmp (ippGetName (attr), "printer-state-message") == 0)
1796             state_msg = ippGetString (attr, 0, NULL);
1797         else if (strcmp (ippGetName (attr), "printer-state-reasons") == 0)
1798           /* Store most important reason to reason_msg and set
1799              its importance at printer_state_reason_level */
1800           {
1801             for (i = 0; i < ippGetCount (attr); i++)
1802               {
1803                   if (strcmp (ippGetString (attr, i, NULL), "none") != 0)
1804                   {
1805                     /* Sets is_paused flag for paused printer. */
1806                       if (strcmp (ippGetString (attr, i, NULL), "paused") == 0)
1807                       {
1808                         is_paused = TRUE;
1809                       }
1810
1811                     interested_in = FALSE;
1812                     for (j = 0; j < G_N_ELEMENTS (reasons); j++)
1813                         if (strncmp (ippGetString (attr, i, NULL), reasons[j], strlen (reasons[j])) == 0)
1814                           {
1815                             interested_in = TRUE;
1816                             break;
1817                           }
1818
1819                     if (interested_in)
1820                       {
1821                           if (g_str_has_suffix (ippGetString (attr, i, NULL), "-report"))
1822                           {
1823                             if (printer_state_reason_level <= 1)
1824                               {
1825                                   reason_msg = ippGetString (attr, i, NULL);
1826                                 printer_state_reason_level = 1;
1827                               }
1828                           }
1829                           else if (g_str_has_suffix (ippGetString (attr, i, NULL), "-warning"))
1830                           {
1831                             if (printer_state_reason_level <= 2)
1832                               {
1833                                   reason_msg = ippGetString (attr, i, NULL);
1834                                 printer_state_reason_level = 2;
1835                              }
1836                           }
1837                         else  /* It is error in the case of no suffix. */
1838                           {
1839                               reason_msg = ippGetString (attr, i, NULL);
1840                             printer_state_reason_level = 3;
1841                           }
1842                       }
1843                   }
1844               }
1845           }
1846         else if (strcmp (ippGetName (attr), "printer-state") == 0)
1847           state = ippGetInteger (attr, 0);
1848         else if (strcmp (ippGetName (attr), "queued-job-count") == 0)
1849           job_count = ippGetInteger (attr, 0);
1850         else if (strcmp (ippGetName (attr), "printer-is-accepting-jobs") == 0)
1851           {
1852             if (ippGetBoolean (attr, 0) == 1)
1853               is_accepting_jobs = TRUE;
1854             else
1855               is_accepting_jobs = FALSE;
1856           }
1857         else if (strcmp (ippGetName (attr), "job-sheets-supported") == 0)
1858           {
1859             if (cups_backend->covers == NULL)
1860               {
1861                 cups_backend->number_of_covers = ippGetCount (attr);
1862                 cups_backend->covers = g_new (char *, cups_backend->number_of_covers + 1);
1863                 for (i = 0; i < cups_backend->number_of_covers; i++)
1864                     cups_backend->covers[i] = g_strdup (ippGetString (attr, i, NULL));
1865                 cups_backend->covers[cups_backend->number_of_covers] = NULL;
1866               }
1867           }
1868         else if (strcmp (ippGetName (attr), "job-sheets-default") == 0)
1869           {
1870             if (ippGetCount (attr) == 2)
1871               {
1872                   default_cover_before = ippGetString (attr, 0, NULL);
1873                   default_cover_after = ippGetString (attr, 1, NULL);
1874               }
1875           }
1876         else if (strcmp (ippGetName (attr), "printer-type") == 0)
1877           {
1878             got_printer_type = TRUE;
1879             if (ippGetInteger (attr, 0) & 0x00020000)
1880               default_printer = TRUE;
1881             else
1882               default_printer = FALSE;
1883
1884             if (ippGetInteger (attr, 0) & 0x00000002)
1885               remote_printer = TRUE;
1886             else
1887               remote_printer = FALSE;
1888           }
1889         else if (strcmp (ippGetName (attr), "auth-info-required") == 0)
1890           {
1891               if (strcmp (ippGetString (attr, 0, NULL), "none") != 0)
1892               {
1893                 auth_info_required = g_new0 (gchar *, ippGetCount (attr) + 1);
1894                 for (i = 0; i < ippGetCount (attr); i++)
1895                     auth_info_required[i] = g_strdup (ippGetString (attr, i, NULL));
1896               }
1897           }
1898         else if (strcmp (attr->name, "number-up-default") == 0)
1899           {
1900             default_number_up = attr->values[0].integer;
1901           }
1902         else
1903           {
1904             GTK_NOTE (PRINTING,
1905                       g_print ("CUPS Backend: Attribute %s ignored", ippGetName (attr)));
1906           }
1907
1908         attr = attr->next;
1909       }
1910
1911       if (printer_name == NULL ||
1912           (printer_uri == NULL && member_uris == NULL))
1913       {
1914         if (attr == NULL)
1915           break;
1916         else
1917           continue;
1918       }
1919
1920       if (got_printer_type)
1921         {
1922           if (default_printer && !cups_backend->got_default_printer)
1923             {
1924               if (!remote_printer)
1925                 {
1926                   cups_backend->got_default_printer = TRUE;
1927                   cups_backend->default_printer = g_strdup (printer_name);
1928                 }
1929               else
1930                 {
1931                   if (remote_default_printer == NULL)
1932                     remote_default_printer = g_strdup (printer_name);
1933                 }
1934             }
1935         }
1936       else
1937         {
1938           if (!cups_backend->got_default_printer)
1939             cups_get_default_printer (cups_backend);
1940         }
1941
1942       /* remove name from checklist if it was found */
1943       node = g_list_find_custom (removed_printer_checklist, printer_name, (GCompareFunc) find_printer);
1944       removed_printer_checklist = g_list_delete_link (removed_printer_checklist, node);
1945  
1946       printer = gtk_print_backend_find_printer (backend, printer_name);
1947       if (!printer)
1948         {
1949           GtkPrinterCups *cups_printer;
1950           char uri[HTTP_MAX_URI];       /* Printer URI */
1951           char method[HTTP_MAX_URI];    /* Method/scheme name */
1952           char username[HTTP_MAX_URI];  /* Username:password */
1953           char hostname[HTTP_MAX_URI];  /* Hostname */
1954           char resource[HTTP_MAX_URI];  /* Resource name */
1955           int  port;                    /* Port number */
1956           char *cups_server;            /* CUPS server */
1957           
1958           list_has_changed = TRUE;
1959 #ifdef HAVE_COLORD
1960           cups_printer = gtk_printer_cups_new (printer_name,
1961                                                backend,
1962                                                cups_backend->colord_client);
1963 #else
1964           cups_printer = gtk_printer_cups_new (printer_name, backend, NULL);
1965 #endif
1966
1967           cups_printer->device_uri = g_strdup_printf ("/printers/%s", printer_name);
1968
1969           /* Check to see if we are looking at a class */
1970           if (member_uris)
1971             {
1972               cups_printer->printer_uri = g_strdup (member_uris);
1973               /* TODO if member_uris is a class we need to recursivly find a printer */
1974               GTK_NOTE (PRINTING,
1975                         g_print ("CUPS Backend: Found class with printer %s\n", member_uris));
1976             }
1977           else
1978             {
1979               cups_printer->printer_uri = g_strdup (printer_uri);
1980               GTK_NOTE (PRINTING,
1981                         g_print ("CUPS Backend: Found printer %s\n", printer_uri));
1982             }
1983
1984 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
1985           httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri, 
1986                            method, sizeof (method), 
1987                            username, sizeof (username),
1988                            hostname, sizeof (hostname),
1989                            &port, 
1990                            resource, sizeof (resource));
1991
1992 #else
1993           httpSeparate (cups_printer->printer_uri, 
1994                         method, 
1995                         username, 
1996                         hostname,
1997                         &port, 
1998                         resource);
1999 #endif
2000
2001           if (strncmp (resource, "/printers/", 10) == 0)
2002             {
2003               cups_printer->ppd_name = g_strdup (resource + 10);
2004               GTK_NOTE (PRINTING,
2005                         g_print ("CUPS Backend: Setting ppd name '%s' for printer/class '%s'\n", cups_printer->ppd_name, printer_name));
2006             }
2007
2008           gethostname (uri, sizeof (uri));
2009           cups_server = g_strdup (cupsServer());
2010
2011           if (strcasecmp (uri, hostname) == 0)
2012             strcpy (hostname, "localhost");
2013
2014           /* if the cups server is local and listening at a unix domain socket 
2015            * then use the socket connection
2016            */
2017           if ((strstr (hostname, "localhost") != NULL) &&
2018               (cups_server[0] == '/'))
2019             strcpy (hostname, cups_server);
2020
2021           g_free (cups_server);
2022
2023           cups_printer->default_cover_before = g_strdup (default_cover_before);
2024           cups_printer->default_cover_after = g_strdup (default_cover_after);
2025
2026           cups_printer->default_number_up = default_number_up;
2027
2028           cups_printer->hostname = g_strdup (hostname);
2029           cups_printer->port = port;
2030           
2031           cups_printer->auth_info_required = g_strdupv (auth_info_required);
2032           g_strfreev (auth_info_required);
2033
2034           printer = GTK_PRINTER (cups_printer);
2035           
2036           if (cups_backend->default_printer != NULL &&
2037               strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
2038             gtk_printer_set_is_default (printer, TRUE);
2039
2040           
2041           gtk_print_backend_add_printer (backend, printer);
2042         }
2043       else
2044         g_object_ref (printer);
2045
2046       GTK_PRINTER_CUPS (printer)->remote = remote_printer;
2047
2048       gtk_printer_set_is_paused (printer, is_paused);
2049       gtk_printer_set_is_accepting_jobs (printer, is_accepting_jobs);
2050
2051       if (!gtk_printer_is_active (printer))
2052         {
2053           gtk_printer_set_is_active (printer, TRUE);
2054           gtk_printer_set_is_new (printer, TRUE);
2055           list_has_changed = TRUE;
2056         }
2057
2058       if (gtk_printer_is_new (printer))
2059         {
2060           g_signal_emit_by_name (backend, "printer-added", printer);
2061
2062           gtk_printer_set_is_new (printer, FALSE);
2063         }
2064
2065 #if 0
2066       /* Getting printer info with separate requests overwhelms cups
2067        * when the printer list has more than a handful of printers.
2068        */
2069       cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
2070 #endif
2071
2072       GTK_PRINTER_CUPS (printer)->state = state;
2073       status_changed = gtk_printer_set_job_count (printer, job_count);
2074       status_changed |= gtk_printer_set_location (printer, location);
2075       status_changed |= gtk_printer_set_description (printer, description);
2076
2077       if (state_msg != NULL && strlen (state_msg) == 0)
2078         {
2079           if (is_paused && !is_accepting_jobs)
2080                   /* Translators: this is a printer status. */
2081             tmp_msg2 = g_strdup ( N_("Paused ; Rejecting Jobs"));
2082           if (is_paused && is_accepting_jobs)
2083                   /* Translators: this is a printer status. */
2084             tmp_msg2 = g_strdup ( N_("Paused"));
2085           if (!is_paused && !is_accepting_jobs)
2086                   /* Translators: this is a printer status. */
2087             tmp_msg2 = g_strdup ( N_("Rejecting Jobs"));
2088
2089           if (tmp_msg2 != NULL)
2090             state_msg = tmp_msg2;
2091         }
2092
2093       /* Set description of the reason and combine it with printer-state-message. */
2094       if ( (reason_msg != NULL))
2095         {
2096           for (i = 0; i < G_N_ELEMENTS (reasons); i++)
2097             {
2098               if (strncmp (reason_msg, reasons[i], strlen (reasons[i])) == 0)
2099                 {
2100                   reason_msg_desc = g_strdup_printf (reasons_descs[i], printer_name);
2101                   found = TRUE;
2102                   break;
2103                 }
2104             }
2105
2106           if (!found)
2107             printer_state_reason_level = 0;
2108
2109           if (printer_state_reason_level >= 2)
2110             {
2111               if (strlen (state_msg) == 0)
2112                 state_msg = reason_msg_desc;
2113               else
2114                 {
2115                   tmp_msg = g_strjoin (" ; ", state_msg, reason_msg_desc, NULL);
2116                   state_msg = tmp_msg;
2117                 }
2118             }
2119         }
2120
2121       status_changed |= gtk_printer_set_state_message (printer, state_msg);
2122       status_changed |= gtk_printer_set_is_accepting_jobs (printer, is_accepting_jobs);
2123
2124       if (tmp_msg != NULL)
2125         g_free (tmp_msg);
2126
2127       if (tmp_msg2 != NULL)
2128         g_free (tmp_msg2);
2129
2130       if (reason_msg_desc != NULL)
2131         g_free (reason_msg_desc);
2132
2133       /* Set printer icon according to importance
2134          (none, report, warning, error - report is omitted). */
2135       if (printer_state_reason_level == 3)
2136         gtk_printer_set_icon_name (printer, "printer-error");
2137       else if (printer_state_reason_level == 2)
2138         gtk_printer_set_icon_name (printer, "printer-warning");
2139       else if (gtk_printer_is_paused (printer))
2140         gtk_printer_set_icon_name (printer, "printer-paused");
2141       else
2142         gtk_printer_set_icon_name (printer, "printer");
2143
2144       if (status_changed)
2145         g_signal_emit_by_name (GTK_PRINT_BACKEND (backend),
2146                                "printer-status-changed", printer);
2147
2148       /* The ref is held by GtkPrintBackend, in add_printer() */
2149       g_object_unref (printer);
2150       
2151       if (attr == NULL)
2152         break;
2153     }
2154
2155   /* look at the removed printers checklist and mark any printer
2156      as inactive if it is in the list, emitting a printer_removed signal */
2157   if (removed_printer_checklist != NULL)
2158     {
2159       g_list_free_full (removed_printer_checklist, (GDestroyNotify) mark_printer_inactive);
2160       list_has_changed = TRUE;
2161     }
2162   
2163 done:
2164   if (list_has_changed)
2165     g_signal_emit_by_name (backend, "printer-list-changed");
2166   
2167   gtk_print_backend_set_list_done (backend);
2168
2169   if (!cups_backend->got_default_printer && remote_default_printer != NULL)
2170     {
2171       cups_backend->default_printer = g_strdup (remote_default_printer);
2172       cups_backend->got_default_printer = TRUE;
2173       g_free (remote_default_printer);
2174
2175       if (cups_backend->default_printer != NULL)
2176         {
2177           GtkPrinter *default_printer = NULL;
2178           default_printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (cups_backend),
2179                                                             cups_backend->default_printer);
2180           if (default_printer != NULL)
2181             {
2182               gtk_printer_set_is_default (default_printer, TRUE);
2183               g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
2184                                      "printer-status-changed", default_printer);
2185             }
2186         }
2187     }
2188
2189   GDK_THREADS_LEAVE ();
2190 }
2191
2192 static void
2193 update_backend_status (GtkPrintBackendCups    *cups_backend,
2194                        GtkCupsConnectionState  state)
2195 {
2196   switch (state)
2197     {
2198     case GTK_CUPS_CONNECTION_NOT_AVAILABLE:
2199       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_UNAVAILABLE, NULL);
2200       break;
2201     case GTK_CUPS_CONNECTION_AVAILABLE:
2202       g_object_set (cups_backend, "status", GTK_PRINT_BACKEND_STATUS_OK, NULL);
2203       break;
2204     default: ;
2205     }
2206 }
2207
2208 static gboolean
2209 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
2210 {
2211   GtkCupsConnectionState state;
2212   GtkCupsRequest *request;
2213   static const char * const pattrs[] =  /* Attributes we're interested in */
2214     {
2215       "printer-name",
2216       "printer-uri-supported",
2217       "member-uris",
2218       "printer-location",
2219       "printer-info",
2220       "printer-state-message",
2221       "printer-state-reasons",
2222       "printer-state",
2223       "queued-job-count",
2224       "printer-is-accepting-jobs",
2225       "job-sheets-supported",
2226       "job-sheets-default",
2227       "printer-type",
2228       "auth-info-required",
2229       "number-up-default"
2230     };
2231
2232   if (cups_backend->reading_ppds > 0 || cups_backend->list_printers_pending)
2233     return TRUE;
2234
2235   state = gtk_cups_connection_test_get_state (cups_backend->cups_connection_test);
2236   update_backend_status (cups_backend, state);
2237
2238   if (cups_backend->list_printers_attempts == 60)
2239     {
2240       cups_backend->list_printers_attempts = -1;
2241       if (cups_backend->list_printers_poll > 0)
2242         g_source_remove (cups_backend->list_printers_poll);
2243       cups_backend->list_printers_poll = gdk_threads_add_timeout (200,
2244                                            (GSourceFunc) cups_request_printer_list,
2245                                            cups_backend);
2246     }
2247   else if (cups_backend->list_printers_attempts != -1)
2248     cups_backend->list_printers_attempts++;
2249
2250   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2251     return TRUE;
2252   else
2253     if (cups_backend->list_printers_attempts > 0)
2254       cups_backend->list_printers_attempts = 60;
2255
2256   cups_backend->list_printers_pending = TRUE;
2257
2258   request = gtk_cups_request_new_with_username (NULL,
2259                                                 GTK_CUPS_POST,
2260                                                 CUPS_GET_PRINTERS,
2261                                                 NULL,
2262                                                 NULL,
2263                                                 NULL,
2264                                                 cups_backend->username);
2265
2266   gtk_cups_request_ipp_add_strings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2267                                     "requested-attributes", G_N_ELEMENTS (pattrs),
2268                                     NULL, pattrs);
2269
2270   cups_request_execute (cups_backend,
2271                         request,
2272                         (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
2273                         request,
2274                         NULL);
2275
2276   return TRUE;
2277 }
2278
2279 static void
2280 cups_get_printer_list (GtkPrintBackend *backend)
2281 {
2282   GtkPrintBackendCups *cups_backend;
2283
2284   cups_backend = GTK_PRINT_BACKEND_CUPS (backend);
2285
2286   if (cups_backend->cups_connection_test == NULL)
2287     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2288
2289   if (cups_backend->list_printers_poll == 0)
2290     {
2291       if (cups_request_printer_list (cups_backend))
2292         cups_backend->list_printers_poll = gdk_threads_add_timeout (50,
2293                                              (GSourceFunc) cups_request_printer_list,
2294                                              backend);
2295     }
2296 }
2297
2298 typedef struct {
2299   GtkPrinterCups *printer;
2300   GIOChannel *ppd_io;
2301   http_t *http;
2302 } GetPPDData;
2303
2304 static void
2305 get_ppd_data_free (GetPPDData *data)
2306 {
2307   GTK_NOTE (PRINTING,
2308             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2309   httpClose (data->http);
2310   g_io_channel_unref (data->ppd_io);
2311   g_object_unref (data->printer);
2312   g_free (data);
2313 }
2314
2315 static void
2316 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
2317                      GtkCupsResult       *result,
2318                      GetPPDData          *data)
2319 {
2320   GtkPrinter *printer;
2321
2322   GDK_THREADS_ENTER ();
2323
2324   GTK_NOTE (PRINTING,
2325             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2326
2327   printer = GTK_PRINTER (data->printer);
2328   GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
2329   print_backend->reading_ppds--;
2330
2331   if (gtk_cups_result_is_error (result))
2332     {
2333       gboolean success = FALSE;
2334
2335       /* if we get a 404 then it is just a raw printer without a ppd
2336          and not an error */
2337       if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) &&
2338           (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND))
2339         {
2340           gtk_printer_set_has_details (printer, TRUE);
2341           success = TRUE;
2342         } 
2343         
2344       g_signal_emit_by_name (printer, "details-acquired", success);
2345       goto done;
2346     }
2347
2348   /* let ppdOpenFd take over the ownership of the open file */
2349   g_io_channel_seek_position (data->ppd_io, 0, G_SEEK_SET, NULL);
2350   data->printer->ppd_file = ppdOpenFd (dup (g_io_channel_unix_get_fd (data->ppd_io)));
2351 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
2352   ppdLocalize (data->printer->ppd_file);
2353 #endif
2354
2355   ppdMarkDefaults (data->printer->ppd_file);
2356   
2357   gtk_printer_set_has_details (printer, TRUE);
2358   g_signal_emit_by_name (printer, "details-acquired", TRUE);
2359
2360 done:
2361   GDK_THREADS_LEAVE ();
2362 }
2363
2364 static gboolean
2365 cups_request_ppd (GtkPrinter *printer)
2366 {
2367   GError *error;
2368   GtkPrintBackend *print_backend;
2369   GtkPrinterCups *cups_printer;
2370   GtkCupsRequest *request;
2371   char *ppd_filename;
2372   gchar *resource;
2373   http_t *http;
2374   GetPPDData *data;
2375   int fd;
2376
2377   cups_printer = GTK_PRINTER_CUPS (printer);
2378
2379   error = NULL;
2380
2381   GTK_NOTE (PRINTING,
2382             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2383
2384   if (cups_printer->remote)
2385     {
2386       GtkCupsConnectionState state;
2387
2388       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
2389
2390       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
2391         {
2392           if (cups_printer->get_remote_ppd_attempts == 60)
2393             {
2394               cups_printer->get_remote_ppd_attempts = -1;
2395               if (cups_printer->get_remote_ppd_poll > 0)
2396                 g_source_remove (cups_printer->get_remote_ppd_poll);
2397               cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
2398                                                     (GSourceFunc) cups_request_ppd,
2399                                                     printer);
2400             }
2401           else if (cups_printer->get_remote_ppd_attempts != -1)
2402             cups_printer->get_remote_ppd_attempts++;
2403
2404           return TRUE;
2405         }
2406
2407       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
2408       cups_printer->remote_cups_connection_test = NULL;
2409       cups_printer->get_remote_ppd_poll = 0;
2410       cups_printer->get_remote_ppd_attempts = 0;
2411
2412       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2413         {
2414           g_signal_emit_by_name (printer, "details-acquired", FALSE);
2415           return FALSE;
2416         }
2417     }
2418
2419   http = httpConnectEncrypt (cups_printer->hostname, 
2420                              cups_printer->port,
2421                              cupsEncryption ());
2422   
2423   data = g_new0 (GetPPDData, 1);
2424
2425   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", 
2426                         &ppd_filename, 
2427                         &error);
2428
2429 #ifdef G_ENABLE_DEBUG 
2430   /* If we are debugging printing don't delete the tmp files */
2431   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
2432     unlink (ppd_filename);
2433 #else
2434   unlink (ppd_filename);
2435 #endif /* G_ENABLE_DEBUG */
2436
2437   if (error != NULL)
2438     {
2439       GTK_NOTE (PRINTING, 
2440                 g_warning ("CUPS Backend: Failed to create temp file, %s\n", 
2441                            error->message));
2442       g_error_free (error);
2443       httpClose (http);
2444       g_free (ppd_filename);
2445       g_free (data);
2446
2447       g_signal_emit_by_name (printer, "details-acquired", FALSE);
2448       return FALSE;
2449     }
2450     
2451   data->http = http;
2452   fchmod (fd, S_IRUSR | S_IWUSR);
2453   data->ppd_io = g_io_channel_unix_new (fd);
2454   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
2455   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
2456
2457   data->printer = g_object_ref (printer);
2458
2459   resource = g_strdup_printf ("/printers/%s.ppd", 
2460                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
2461
2462   print_backend = gtk_printer_get_backend (printer);
2463
2464   request = gtk_cups_request_new_with_username (data->http,
2465                                                 GTK_CUPS_GET,
2466                                                 0,
2467                                                 data->ppd_io,
2468                                                 cups_printer->hostname,
2469                                                 resource,
2470                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
2471
2472   GTK_NOTE (PRINTING,
2473             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
2474
2475
2476   cups_printer->reading_ppd = TRUE;
2477   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
2478
2479   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
2480                         request,
2481                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
2482                         data,
2483                         (GDestroyNotify)get_ppd_data_free);
2484
2485   g_free (resource);
2486   g_free (ppd_filename);
2487
2488   return FALSE;
2489 }
2490
2491 /* Ordering matters for default preference */
2492 static const char *lpoptions_locations[] = {
2493   "/etc/cups/lpoptions",
2494   ".lpoptions", 
2495   ".cups/lpoptions"
2496 };
2497
2498 static void
2499 cups_parse_user_default_printer (const char  *filename,
2500                                  char       **printer_name)
2501 {
2502   FILE *fp;
2503   char line[1024], *lineptr, *defname = NULL;
2504   
2505   if ((fp = g_fopen (filename, "r")) == NULL)
2506     return;
2507
2508   while (fgets (line, sizeof (line), fp) != NULL)
2509     {
2510       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
2511         continue;
2512
2513       lineptr = line + 8;
2514       while (isspace (*lineptr))
2515         lineptr++;
2516
2517       if (!*lineptr)
2518         continue;
2519
2520       defname = lineptr;
2521       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
2522         lineptr++;
2523
2524       *lineptr = '\0';
2525
2526       if (*printer_name != NULL)
2527         g_free (*printer_name);
2528
2529       *printer_name = g_strdup (defname);
2530     }
2531
2532   fclose (fp);
2533 }
2534
2535 static void
2536 cups_get_user_default_printer (char **printer_name)
2537 {
2538   int i;
2539
2540   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2541     {
2542       if (g_path_is_absolute (lpoptions_locations[i]))
2543         {
2544           cups_parse_user_default_printer (lpoptions_locations[i],
2545                                            printer_name);
2546         }
2547       else 
2548         {
2549           char *filename;
2550
2551           filename = g_build_filename (g_get_home_dir (), 
2552                                        lpoptions_locations[i], NULL);
2553           cups_parse_user_default_printer (filename, printer_name);
2554           g_free (filename);
2555         }
2556     }
2557 }
2558
2559 static int
2560 cups_parse_user_options (const char     *filename,
2561                          const char     *printer_name,
2562                          int             num_options,
2563                          cups_option_t **options)
2564 {
2565   FILE *fp;
2566   gchar line[1024], *lineptr, *name;
2567
2568   if ((fp = g_fopen (filename, "r")) == NULL)
2569     return num_options;
2570
2571   while (fgets (line, sizeof (line), fp) != NULL)
2572     {
2573       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
2574         lineptr = line + 4;
2575       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
2576         lineptr = line + 7;
2577       else
2578         continue;
2579
2580       /* Skip leading whitespace */
2581       while (isspace (*lineptr))
2582         lineptr++;
2583
2584       if (!*lineptr)
2585         continue;
2586
2587       /* NUL-terminate the name, stripping the instance name */
2588       name = lineptr;
2589       while (!isspace (*lineptr) && *lineptr)
2590         {
2591           if (*lineptr == '/')
2592             *lineptr = '\0';
2593           lineptr++;
2594         }
2595
2596       if (!*lineptr)
2597         continue;
2598
2599       *lineptr++ = '\0';
2600
2601       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
2602           continue;
2603
2604       /* We found our printer, parse the options */
2605       num_options = cupsParseOptions (lineptr, num_options, options);
2606     }
2607
2608   fclose (fp);
2609
2610   return num_options;
2611 }
2612
2613 static int
2614 cups_get_user_options (const char     *printer_name,
2615                        int             num_options,
2616                        cups_option_t **options)
2617 {
2618   int i;
2619
2620   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2621     {
2622       if (g_path_is_absolute (lpoptions_locations[i]))
2623         { 
2624            num_options = cups_parse_user_options (lpoptions_locations[i],
2625                                                   printer_name,
2626                                                   num_options,
2627                                                   options);
2628         }
2629       else
2630         {
2631           char *filename;
2632
2633           filename = g_build_filename (g_get_home_dir (), 
2634                                        lpoptions_locations[i], NULL);
2635           num_options = cups_parse_user_options (filename, printer_name,
2636                                                  num_options, options);
2637           g_free (filename);
2638         }
2639     }
2640
2641   return num_options;
2642 }
2643
2644 /* This function requests default printer from a CUPS server in regular intervals.
2645  * In the case of unreachable CUPS server the request is repeated later.
2646  * The default printer is not requested in the case of previous success.
2647  */
2648 static void
2649 cups_get_default_printer (GtkPrintBackendCups *backend)
2650 {
2651   GtkPrintBackendCups *cups_backend;
2652
2653   cups_backend = backend;
2654
2655   if (cups_backend->cups_connection_test == NULL)
2656     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2657
2658   if (cups_backend->default_printer_poll == 0)
2659     {
2660       if (cups_request_default_printer (cups_backend))
2661         cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
2662                                                (GSourceFunc) cups_request_default_printer,
2663                                                backend);
2664     }
2665 }
2666
2667 /* This function gets default printer from local settings.*/
2668 static void
2669 cups_get_local_default_printer (GtkPrintBackendCups *backend)
2670 {
2671   const char *str;
2672   char *name = NULL;
2673
2674   if ((str = g_getenv ("LPDEST")) != NULL)
2675     {
2676       backend->default_printer = g_strdup (str);
2677       backend->got_default_printer = TRUE;
2678       return;
2679     }
2680   else if ((str = g_getenv ("PRINTER")) != NULL &&
2681            strcmp (str, "lp") != 0)
2682     {
2683       backend->default_printer = g_strdup (str);
2684       backend->got_default_printer = TRUE;
2685       return;
2686     }
2687   
2688   /* Figure out user setting for default printer */  
2689   cups_get_user_default_printer (&name);
2690   if (name != NULL)
2691     {
2692       backend->default_printer = name;
2693       backend->got_default_printer = TRUE;
2694       return;
2695     }
2696 }
2697
2698 static void
2699 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
2700                                  GtkCupsResult       *result,
2701                                  gpointer             user_data)
2702 {
2703   ipp_t *response;
2704   ipp_attribute_t *attr;
2705   GtkPrinter *printer;
2706
2707   GDK_THREADS_ENTER ();
2708
2709   if (gtk_cups_result_is_error (result))
2710     {
2711       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
2712           gtk_cups_result_get_error_code (result) == 1)
2713         {
2714           /* Canceled by user, stop popping up more password dialogs */
2715           if (print_backend->list_printers_poll > 0)
2716             g_source_remove (print_backend->list_printers_poll);
2717           print_backend->list_printers_poll = 0;
2718         }
2719
2720       return;
2721     }
2722
2723   response = gtk_cups_result_get_response (result);
2724   
2725   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
2726       print_backend->default_printer = g_strdup (ippGetString (attr, 0, NULL));
2727
2728   print_backend->got_default_printer = TRUE;
2729
2730   if (print_backend->default_printer != NULL)
2731     {
2732       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
2733       if (printer != NULL)
2734         {
2735           gtk_printer_set_is_default (printer, TRUE);
2736           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
2737         }
2738     }
2739
2740   /* Make sure to kick off get_printers if we are polling it, 
2741    * as we could have blocked this reading the default printer 
2742    */
2743   if (print_backend->list_printers_poll != 0)
2744     cups_request_printer_list (print_backend);
2745
2746   GDK_THREADS_LEAVE ();
2747 }
2748
2749 static gboolean
2750 cups_request_default_printer (GtkPrintBackendCups *print_backend)
2751 {
2752   GtkCupsConnectionState state;
2753   GtkCupsRequest *request;
2754
2755   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
2756   update_backend_status (print_backend, state);
2757
2758   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2759     return TRUE;
2760
2761   request = gtk_cups_request_new_with_username (NULL,
2762                                                 GTK_CUPS_POST,
2763                                                 CUPS_GET_DEFAULT,
2764                                                 NULL,
2765                                                 NULL,
2766                                                 NULL,
2767                                                 print_backend->username);
2768   
2769   cups_request_execute (print_backend,
2770                         request,
2771                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
2772                         g_object_ref (print_backend),
2773                         g_object_unref);
2774
2775   return FALSE;
2776 }
2777
2778 static void
2779 cups_printer_request_details (GtkPrinter *printer)
2780 {
2781   GtkPrinterCups *cups_printer;
2782
2783   cups_printer = GTK_PRINTER_CUPS (printer);
2784   if (!cups_printer->reading_ppd && 
2785       gtk_printer_cups_get_ppd (cups_printer) == NULL)
2786     {
2787       if (cups_printer->remote)
2788         {
2789           if (cups_printer->get_remote_ppd_poll == 0)
2790             {
2791               cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname);
2792
2793               if (cups_request_ppd (printer))
2794                 cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
2795                                                     (GSourceFunc) cups_request_ppd,
2796                                                     printer);
2797             }
2798         }
2799       else
2800         cups_request_ppd (printer);
2801     }
2802 }
2803
2804 static char *
2805 ppd_text_to_utf8 (ppd_file_t *ppd_file, 
2806                   const char *text)
2807 {
2808   const char *encoding = NULL;
2809   char *res;
2810   
2811   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
2812     {
2813       return g_strdup (text);
2814     }
2815   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
2816     {
2817       encoding = "ISO-8859-1";
2818     }
2819   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
2820     {
2821       encoding = "ISO-8859-2";
2822     }
2823   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
2824     {
2825       encoding = "ISO-8859-5";
2826     }
2827   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
2828     {
2829       encoding = "SHIFT-JIS";
2830     }
2831   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
2832     {
2833       encoding = "MACINTOSH";
2834     }
2835   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
2836     {
2837       encoding = "WINDOWS-1252";
2838     }
2839   else 
2840     {
2841       /* Fallback, try iso-8859-1... */
2842       encoding = "ISO-8859-1";
2843     }
2844
2845   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
2846
2847   if (res == NULL)
2848     {
2849       GTK_NOTE (PRINTING,
2850                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
2851       res = g_strdup ("???");
2852     }
2853   
2854   return res;
2855 }
2856
2857 /* TODO: Add more translations for common settings here */
2858
2859 static const struct {
2860   const char *keyword;
2861   const char *translation;
2862 } cups_option_translations[] = {
2863   { "Duplex", N_("Two Sided") },
2864   { "MediaType", N_("Paper Type") },
2865   { "InputSlot", N_("Paper Source") },
2866   { "OutputBin", N_("Output Tray") },
2867   { "Resolution", N_("Resolution") },
2868   { "PreFilter", N_("GhostScript pre-filtering") },
2869 };
2870
2871
2872 static const struct {
2873   const char *keyword;
2874   const char *choice;
2875   const char *translation;
2876 } cups_choice_translations[] = {
2877   { "Duplex", "None", N_("One Sided") },
2878   /* Translators: this is an option of "Two Sided" */
2879   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
2880   /* Translators: this is an option of "Two Sided" */
2881   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
2882   /* Translators: this is an option of "Paper Source" */
2883   { "InputSlot", "Auto", N_("Auto Select") },
2884   /* Translators: this is an option of "Paper Source" */
2885   { "InputSlot", "AutoSelect", N_("Auto Select") },
2886   /* Translators: this is an option of "Paper Source" */
2887   { "InputSlot", "Default", N_("Printer Default") },
2888   /* Translators: this is an option of "Paper Source" */
2889   { "InputSlot", "None", N_("Printer Default") },
2890   /* Translators: this is an option of "Paper Source" */
2891   { "InputSlot", "PrinterDefault", N_("Printer Default") },
2892   /* Translators: this is an option of "Paper Source" */
2893   { "InputSlot", "Unspecified", N_("Auto Select") },
2894   /* Translators: this is an option of "Resolution" */
2895   { "Resolution", "default", N_("Printer Default") },
2896   /* Translators: this is an option of "GhostScript" */
2897   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
2898   /* Translators: this is an option of "GhostScript" */
2899   { "PreFilter", "Level1", N_("Convert to PS level 1") },
2900   /* Translators: this is an option of "GhostScript" */
2901   { "PreFilter", "Level2", N_("Convert to PS level 2") },
2902   /* Translators: this is an option of "GhostScript" */
2903   { "PreFilter", "No", N_("No pre-filtering") },
2904 };
2905
2906 static const struct {
2907   const char *name;
2908   const char *translation;
2909 } cups_group_translations[] = {
2910 /* Translators: "Miscellaneous" is the label for a button, that opens
2911    up an extra panel of settings in a print dialog. */
2912   { "Miscellaneous", N_("Miscellaneous") },
2913 };
2914
2915 static const struct {
2916   const char *ppd_keyword;
2917   const char *name;
2918 } ppd_option_names[] = {
2919   {"Duplex", "gtk-duplex" },
2920   {"MediaType", "gtk-paper-type"},
2921   {"InputSlot", "gtk-paper-source"},
2922   {"OutputBin", "gtk-output-tray"},
2923 };
2924
2925 static const struct {
2926   const char *lpoption;
2927   const char *name;
2928 } lpoption_names[] = {
2929   {"number-up", "gtk-n-up" },
2930   {"number-up-layout", "gtk-n-up-layout"},
2931   {"job-billing", "gtk-billing-info"},
2932   {"job-priority", "gtk-job-prio"},
2933 };
2934
2935 /* keep sorted when changing */
2936 static const char *color_option_whitelist[] = {
2937   "BRColorEnhancement",
2938   "BRColorMatching",
2939   "BRColorMatching",
2940   "BRColorMode",
2941   "BRGammaValue",
2942   "BRImprovedGray",
2943   "BlackSubstitution",
2944   "ColorModel",
2945   "HPCMYKInks",
2946   "HPCSGraphics",
2947   "HPCSImages",
2948   "HPCSText",
2949   "HPColorSmart",
2950   "RPSBlackMode",
2951   "RPSBlackOverPrint",
2952   "Rcmyksimulation",
2953 };
2954
2955 /* keep sorted when changing */
2956 static const char *color_group_whitelist[] = {
2957   "ColorPage",
2958   "FPColorWise1",
2959   "FPColorWise2",
2960   "FPColorWise3",
2961   "FPColorWise4",
2962   "FPColorWise5",
2963   "HPColorOptionsPanel",
2964 };
2965   
2966 /* keep sorted when changing */
2967 static const char *image_quality_option_whitelist[] = {
2968   "BRDocument",
2969   "BRHalfTonePattern",
2970   "BRNormalPrt",
2971   "BRPrintQuality",
2972   "BitsPerPixel",
2973   "Darkness",
2974   "Dithering",
2975   "EconoMode",
2976   "Economode",
2977   "HPEconoMode",
2978   "HPEdgeControl",
2979   "HPGraphicsHalftone",
2980   "HPHalftone",
2981   "HPLJDensity",
2982   "HPPhotoHalftone",
2983   "OutputMode",
2984   "REt",
2985   "RPSBitsPerPixel",
2986   "RPSDitherType",
2987   "Resolution",
2988   "ScreenLock",
2989   "Smoothing",
2990   "TonerSaveMode",
2991   "UCRGCRForImage",
2992 };
2993
2994 /* keep sorted when changing */
2995 static const char *image_quality_group_whitelist[] = {
2996   "FPImageQuality1",
2997   "FPImageQuality2",
2998   "FPImageQuality3",
2999   "ImageQualityPage",
3000 };
3001
3002 /* keep sorted when changing */
3003 static const char * finishing_option_whitelist[] = {
3004   "BindColor",
3005   "BindEdge",
3006   "BindType",
3007   "BindWhen",
3008   "Booklet",
3009   "FoldType",
3010   "FoldWhen",
3011   "HPStaplerOptions",
3012   "Jog",
3013   "Slipsheet",
3014   "Sorter",
3015   "StapleLocation",
3016   "StapleOrientation",
3017   "StapleWhen",
3018   "StapleX",
3019   "StapleY",
3020 };
3021
3022 /* keep sorted when changing */
3023 static const char *finishing_group_whitelist[] = {
3024   "FPFinishing1",
3025   "FPFinishing2",
3026   "FPFinishing3",
3027   "FPFinishing4",
3028   "FinishingPage",
3029   "HPFinishingPanel",
3030 };
3031
3032 /* keep sorted when changing */
3033 static const char *cups_option_blacklist[] = {
3034   "Collate",
3035   "Copies", 
3036   "OutputOrder",
3037   "PageRegion",
3038   "PageSize",
3039 };
3040
3041 static char *
3042 get_option_text (ppd_file_t   *ppd_file, 
3043                  ppd_option_t *option)
3044 {
3045   int i;
3046   char *utf8;
3047   
3048   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
3049     {
3050       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
3051         return g_strdup (_(cups_option_translations[i].translation));
3052     }
3053
3054   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
3055
3056   /* Some ppd files have spaces in the text before the colon */
3057   g_strchomp (utf8);
3058   
3059   return utf8;
3060 }
3061
3062 static char *
3063 get_choice_text (ppd_file_t   *ppd_file, 
3064                  ppd_choice_t *choice)
3065 {
3066   int i;
3067   ppd_option_t *option = choice->option;
3068   const char *keyword = option->keyword;
3069   
3070   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
3071     {
3072       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
3073           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
3074         return g_strdup (_(cups_choice_translations[i].translation));
3075     }
3076   return ppd_text_to_utf8 (ppd_file, choice->text);
3077 }
3078
3079 static gboolean
3080 group_has_option (ppd_group_t  *group, 
3081                   ppd_option_t *option)
3082 {
3083   int i;
3084
3085   if (group == NULL)
3086     return FALSE;
3087   
3088   if (group->num_options > 0 &&
3089       option >= group->options && option < group->options + group->num_options)
3090     return TRUE;
3091   
3092   for (i = 0; i < group->num_subgroups; i++)
3093     {
3094       if (group_has_option (&group->subgroups[i],option))
3095         return TRUE;
3096     }
3097   return FALSE;
3098 }
3099
3100 static void
3101 set_option_off (GtkPrinterOption *option)
3102 {
3103   /* Any of these will do, _set only applies the value
3104    * if its allowed of the option */
3105   gtk_printer_option_set (option, "False");
3106   gtk_printer_option_set (option, "Off");
3107   gtk_printer_option_set (option, "None");
3108 }
3109
3110 static gboolean
3111 value_is_off (const char *value)
3112 {
3113   return  (strcasecmp (value, "None") == 0 ||
3114            strcasecmp (value, "Off") == 0 ||
3115            strcasecmp (value, "False") == 0);
3116 }
3117
3118 static char *
3119 ppd_group_name (ppd_group_t *group)
3120 {
3121 #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) 
3122   return group->name;
3123 #else
3124   return group->text;
3125 #endif
3126 }
3127
3128 static int
3129 available_choices (ppd_file_t     *ppd,
3130                    ppd_option_t   *option,
3131                    ppd_choice_t ***available,
3132                    gboolean        keep_if_only_one_option)
3133 {
3134   ppd_option_t *other_option;
3135   int i, j;
3136   gchar *conflicts;
3137   ppd_const_t *constraint;
3138   const char *choice, *other_choice;
3139   ppd_option_t *option1, *option2;
3140   ppd_group_t *installed_options;
3141   int num_conflicts;
3142   gboolean all_default;
3143   int add_auto;
3144
3145   if (available)
3146     *available = NULL;
3147
3148   conflicts = g_new0 (char, option->num_choices);
3149
3150   installed_options = NULL;
3151   for (i = 0; i < ppd->num_groups; i++)
3152     {
3153       char *name; 
3154
3155       name = ppd_group_name (&ppd->groups[i]);
3156       if (strcmp (name, "InstallableOptions") == 0)
3157         {
3158           installed_options = &ppd->groups[i];
3159           break;
3160         }
3161     }
3162
3163   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
3164     {
3165       option1 = ppdFindOption (ppd, constraint->option1);
3166       if (option1 == NULL)
3167         continue;
3168
3169       option2 = ppdFindOption (ppd, constraint->option2);
3170       if (option2 == NULL)
3171         continue;
3172
3173       if (option == option1)
3174         {
3175           choice = constraint->choice1;
3176           other_option = option2;
3177           other_choice = constraint->choice2;
3178         }
3179       else if (option == option2)
3180         {
3181           choice = constraint->choice2;
3182           other_option = option1;
3183           other_choice = constraint->choice1;
3184         }
3185       else
3186         continue;
3187
3188       /* We only care of conflicts with installed_options and
3189          PageSize */
3190       if (!group_has_option (installed_options, other_option) &&
3191           (strcmp (other_option->keyword, "PageSize") != 0))
3192         continue;
3193
3194       if (*other_choice == 0)
3195         {
3196           /* Conflict only if the installed option is not off */
3197           if (value_is_off (other_option->defchoice))
3198             continue;
3199         }
3200       /* Conflict if the installed option has the specified default */
3201       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
3202         continue;
3203
3204       if (*choice == 0)
3205         {
3206           /* Conflict with all non-off choices */
3207           for (j = 0; j < option->num_choices; j++)
3208             {
3209               if (!value_is_off (option->choices[j].choice))
3210                 conflicts[j] = 1;
3211             }
3212         }
3213       else
3214         {
3215           for (j = 0; j < option->num_choices; j++)
3216             {
3217               if (strcasecmp (option->choices[j].choice, choice) == 0)
3218                 conflicts[j] = 1;
3219             }
3220         }
3221     }
3222
3223   num_conflicts = 0;
3224   all_default = TRUE;
3225   for (j = 0; j < option->num_choices; j++)
3226     {
3227       if (conflicts[j])
3228         num_conflicts++;
3229       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
3230         all_default = FALSE;
3231     }
3232
3233   if ((all_default && !keep_if_only_one_option) ||
3234       (num_conflicts == option->num_choices))
3235     {
3236       g_free (conflicts);
3237
3238       return 0;
3239     }
3240
3241   /* Some ppds don't have a "use printer default" option for
3242    * InputSlot. This means you always have to select a particular slot,
3243    * and you can't auto-pick source based on the paper size. To support
3244    * this we always add an auto option if there isn't one already. If
3245    * the user chooses the generated option we don't send any InputSlot
3246    * value when printing. The way we detect existing auto-cases is based
3247    * on feedback from Michael Sweet of cups fame.
3248    */
3249   add_auto = 0;
3250   if (strcmp (option->keyword, "InputSlot") == 0)
3251     {
3252       gboolean found_auto = FALSE;
3253       for (j = 0; j < option->num_choices; j++)
3254         {
3255           if (!conflicts[j])
3256             {
3257               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
3258                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
3259                   strcmp (option->choices[j].choice, "Default") == 0 ||
3260                   strcmp (option->choices[j].choice, "None") == 0 ||
3261                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
3262                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
3263                   option->choices[j].code == NULL ||
3264                   option->choices[j].code[0] == 0)
3265                 {
3266                   found_auto = TRUE;
3267                   break;
3268                 }
3269             }
3270         }
3271
3272       if (!found_auto)
3273         add_auto = 1;
3274     }
3275   
3276   if (available)
3277     {
3278       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
3279
3280       i = 0;
3281       for (j = 0; j < option->num_choices; j++)
3282         {
3283           if (!conflicts[j])
3284             (*available)[i++] = &option->choices[j];
3285         }
3286
3287       if (add_auto) 
3288         (*available)[i++] = NULL;
3289     }
3290
3291   g_free (conflicts);
3292   
3293   return option->num_choices - num_conflicts + add_auto;
3294 }
3295
3296 static GtkPrinterOption *
3297 create_pickone_option (ppd_file_t   *ppd_file,
3298                        ppd_option_t *ppd_option,
3299                        const gchar  *gtk_name)
3300 {
3301   GtkPrinterOption *option;
3302   ppd_choice_t **available;
3303   char *label;
3304   int n_choices;
3305   int i;
3306   ppd_coption_t *coption;
3307
3308   g_assert (ppd_option->ui == PPD_UI_PICKONE);
3309   
3310   option = NULL;
3311
3312   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3313   if (n_choices > 0)
3314     {
3315       
3316       /* right now only support one parameter per custom option 
3317        * if more than one print warning and only offer the default choices
3318        */
3319
3320       label = get_option_text (ppd_file, ppd_option);
3321
3322       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
3323
3324       if (coption)
3325         {
3326           ppd_cparam_t *cparam;
3327
3328           cparam = ppdFirstCustomParam (coption);
3329
3330           if (ppdNextCustomParam (coption) == NULL)
3331             {
3332               switch (cparam->type)
3333                 {
3334                 case PPD_CUSTOM_INT:
3335                   option = gtk_printer_option_new (gtk_name, label,
3336                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
3337                   break;
3338                 case PPD_CUSTOM_PASSCODE:
3339                   option = gtk_printer_option_new (gtk_name, label,
3340                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
3341                   break;
3342                 case PPD_CUSTOM_PASSWORD:
3343                     option = gtk_printer_option_new (gtk_name, label,
3344                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
3345                   break;
3346                case PPD_CUSTOM_REAL:
3347                     option = gtk_printer_option_new (gtk_name, label,
3348                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
3349                   break;
3350                 case PPD_CUSTOM_STRING:
3351                   option = gtk_printer_option_new (gtk_name, label,
3352                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
3353                   break;
3354 #ifdef PRINT_IGNORED_OPTIONS
3355                 case PPD_CUSTOM_POINTS: 
3356                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
3357                   break;
3358                 case PPD_CUSTOM_CURVE:
3359                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
3360                   break;
3361                 case PPD_CUSTOM_INVCURVE:       
3362                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
3363                   break;
3364 #endif
3365                 default: 
3366                   break;
3367                 }
3368             }
3369 #ifdef PRINT_IGNORED_OPTIONS
3370           else
3371             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
3372 #endif
3373         }
3374
3375       if (!option)
3376         option = gtk_printer_option_new (gtk_name, label,
3377                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
3378       g_free (label);
3379       
3380       gtk_printer_option_allocate_choices (option, n_choices);
3381       for (i = 0; i < n_choices; i++)
3382         {
3383           if (available[i] == NULL)
3384             {
3385               /* This was auto-added */
3386               option->choices[i] = g_strdup ("gtk-ignore-value");
3387               option->choices_display[i] = g_strdup (_("Printer Default"));
3388             }
3389           else
3390             {
3391               option->choices[i] = g_strdup (available[i]->choice);
3392               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
3393             }
3394         }
3395
3396       if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
3397         {
3398           if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
3399             gtk_printer_option_set (option, ppd_option->defchoice + 7);
3400           else
3401             gtk_printer_option_set (option, ppd_option->defchoice);
3402         }
3403       else
3404         {
3405           gtk_printer_option_set (option, ppd_option->defchoice);
3406         }
3407     }
3408 #ifdef PRINT_IGNORED_OPTIONS
3409   else
3410     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
3411 #endif
3412   g_free (available);
3413
3414   return option;
3415 }
3416
3417 static GtkPrinterOption *
3418 create_boolean_option (ppd_file_t   *ppd_file,
3419                        ppd_option_t *ppd_option,
3420                        const gchar  *gtk_name)
3421 {
3422   GtkPrinterOption *option;
3423   ppd_choice_t **available;
3424   char *label;
3425   int n_choices;
3426
3427   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
3428   
3429   option = NULL;
3430
3431   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3432   if (n_choices == 2)
3433     {
3434       label = get_option_text (ppd_file, ppd_option);
3435       option = gtk_printer_option_new (gtk_name, label,
3436                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
3437       g_free (label);
3438       
3439       gtk_printer_option_allocate_choices (option, 2);
3440       option->choices[0] = g_strdup ("True");
3441       option->choices_display[0] = g_strdup ("True");
3442       option->choices[1] = g_strdup ("False");
3443       option->choices_display[1] = g_strdup ("False");
3444       
3445       gtk_printer_option_set (option, ppd_option->defchoice);
3446     }
3447 #ifdef PRINT_IGNORED_OPTIONS
3448   else
3449     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
3450 #endif
3451   g_free (available);
3452
3453   return option;
3454 }
3455
3456 static gchar *
3457 get_ppd_option_name (const gchar *keyword)
3458 {
3459   int i;
3460
3461   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3462     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
3463       return g_strdup (ppd_option_names[i].name);
3464
3465   return g_strdup_printf ("cups-%s", keyword);
3466 }
3467
3468 static gchar *
3469 get_lpoption_name (const gchar *lpoption)
3470 {
3471   int i;
3472
3473   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3474     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
3475       return g_strdup (ppd_option_names[i].name);
3476
3477   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
3478     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
3479       return g_strdup (lpoption_names[i].name);
3480
3481   return g_strdup_printf ("cups-%s", lpoption);
3482 }
3483
3484 static int
3485 strptr_cmp (const void *a, 
3486             const void *b)
3487 {
3488   char **aa = (char **)a;
3489   char **bb = (char **)b;
3490   return strcmp (*aa, *bb);
3491 }
3492
3493
3494 static gboolean
3495 string_in_table (gchar       *str, 
3496                  const gchar *table[], 
3497                  gint         table_len)
3498 {
3499   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
3500 }
3501
3502 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
3503
3504 static void
3505 handle_option (GtkPrinterOptionSet *set,
3506                ppd_file_t          *ppd_file,
3507                ppd_option_t        *ppd_option,
3508                ppd_group_t         *toplevel_group,
3509                GtkPrintSettings    *settings)
3510 {
3511   GtkPrinterOption *option;
3512   char *name;
3513   int i;
3514
3515   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
3516     return;
3517
3518   name = get_ppd_option_name (ppd_option->keyword);
3519
3520   option = NULL;
3521   if (ppd_option->ui == PPD_UI_PICKONE)
3522     {
3523       option = create_pickone_option (ppd_file, ppd_option, name);
3524     }
3525   else if (ppd_option->ui == PPD_UI_BOOLEAN)
3526     {
3527       option = create_boolean_option (ppd_file, ppd_option, name);
3528     }
3529 #ifdef PRINT_IGNORED_OPTIONS
3530   else
3531     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
3532 #endif  
3533   
3534   if (option)
3535     {
3536       char *name;
3537
3538       name = ppd_group_name (toplevel_group);
3539       if (STRING_IN_TABLE (name,
3540                            color_group_whitelist) ||
3541           STRING_IN_TABLE (ppd_option->keyword,
3542                            color_option_whitelist))
3543         {
3544           option->group = g_strdup ("ColorPage");
3545         }
3546       else if (STRING_IN_TABLE (name,
3547                                 image_quality_group_whitelist) ||
3548                STRING_IN_TABLE (ppd_option->keyword,
3549                                 image_quality_option_whitelist))
3550         {
3551           option->group = g_strdup ("ImageQualityPage");
3552         }
3553       else if (STRING_IN_TABLE (name,
3554                                 finishing_group_whitelist) ||
3555                STRING_IN_TABLE (ppd_option->keyword,
3556                                 finishing_option_whitelist))
3557         {
3558           option->group = g_strdup ("FinishingPage");
3559         }
3560       else
3561         {
3562           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
3563             {
3564               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
3565                 {
3566                   option->group = g_strdup (_(cups_group_translations[i].translation));
3567                   break;
3568                 }
3569             }
3570
3571           if (i == G_N_ELEMENTS (cups_group_translations))
3572             option->group = g_strdup (toplevel_group->text);
3573         }
3574
3575       set_option_from_settings (option, settings);
3576       
3577       gtk_printer_option_set_add (set, option);
3578     }
3579   
3580   g_free (name);
3581 }
3582
3583 static void
3584 handle_group (GtkPrinterOptionSet *set,
3585               ppd_file_t          *ppd_file,
3586               ppd_group_t         *group,
3587               ppd_group_t         *toplevel_group,
3588               GtkPrintSettings    *settings)
3589 {
3590   gint i;
3591   gchar *name;
3592   
3593   /* Ignore installable options */
3594   name = ppd_group_name (toplevel_group);
3595   if (strcmp (name, "InstallableOptions") == 0)
3596     return;
3597   
3598   for (i = 0; i < group->num_options; i++)
3599     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
3600
3601   for (i = 0; i < group->num_subgroups; i++)
3602     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
3603
3604 }
3605
3606 #ifdef HAVE_COLORD
3607
3608 typedef struct {
3609         GtkPrintSettings     *settings;
3610         GtkPrinter           *printer;
3611 } GtkPrintBackendCupsColordHelper;
3612
3613 static void
3614 colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
3615                                       GtkPrintBackendCupsColordHelper *helper)
3616 {
3617   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
3618                                     helper->settings,
3619                                     set);
3620 }
3621 #endif
3622
3623 static GtkPrinterOptionSet *
3624 cups_printer_get_options (GtkPrinter           *printer,
3625                           GtkPrintSettings     *settings,
3626                           GtkPageSetup         *page_setup,
3627                           GtkPrintCapabilities  capabilities)
3628 {
3629   GtkPrinterOptionSet *set;
3630   GtkPrinterOption *option;
3631   ppd_file_t *ppd_file;
3632   int i;
3633   char *print_at[] = { "now", "at", "on-hold" };
3634   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
3635   char *prio[] = {"100", "80", "50", "30" };
3636   /* Translators: These strings name the possible values of the 
3637    * job priority option in the print dialog
3638    */
3639   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
3640   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
3641   /* Translators: These strings name the possible arrangements of
3642    * multiple pages on a sheet when printing
3643    */
3644   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"), 
3645                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"), 
3646                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"), 
3647                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
3648   char *name;
3649   int num_opts;
3650   cups_option_t *opts = NULL;
3651   GtkPrintBackendCups *backend;
3652   GtkTextDirection text_direction;
3653   GtkPrinterCups *cups_printer = NULL;
3654 #ifdef HAVE_COLORD
3655   GtkPrintBackendCupsColordHelper *helper;
3656 #endif
3657   char *default_number_up;
3658
3659   set = gtk_printer_option_set_new ();
3660
3661   /* Cups specific, non-ppd related settings */
3662
3663   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
3664     prio_display[i] = _(prio_display[i]);
3665   
3666   /* Translators, this string is used to label the job priority option 
3667    * in the print dialog 
3668    */
3669   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3670   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
3671                                          prio, prio_display);
3672   gtk_printer_option_set (option, "50");
3673   set_option_from_settings (option, settings);
3674   gtk_printer_option_set_add (set, option);
3675   g_object_unref (option);
3676
3677   /* Translators, this string is used to label the billing info entry
3678    * in the print dialog 
3679    */
3680   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
3681   gtk_printer_option_set (option, "");
3682   set_option_from_settings (option, settings);
3683   gtk_printer_option_set_add (set, option);
3684   g_object_unref (option);
3685
3686   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
3687   cups_printer = GTK_PRINTER_CUPS (printer);
3688
3689   if (backend != NULL && printer != NULL)
3690     {
3691       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
3692       /* Translators, these strings are names for various 'standard' cover 
3693        * pages that the printing system may support.
3694        */
3695       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
3696       char **cover = NULL;
3697       char **cover_display = NULL;
3698       char **cover_display_translated = NULL;
3699       gint num_of_covers = 0;
3700       gpointer value;
3701       gint j;
3702
3703        /* Translators, this string is used to label the pages-per-sheet option 
3704         * in the print dialog 
3705         */
3706       option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3707       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
3708                                              n_up, n_up);
3709       default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
3710       gtk_printer_option_set (option, default_number_up);
3711       g_free (default_number_up);
3712       set_option_from_settings (option, settings);
3713       gtk_printer_option_set_add (set, option);
3714       g_object_unref (option);
3715
3716       if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
3717         {
3718           for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
3719             n_up_layout_display[i] = _(n_up_layout_display[i]);
3720
3721            /* Translators, this string is used to label the option in the print 
3722             * dialog that controls in what order multiple pages are arranged 
3723             */
3724           option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3725           gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
3726                                                  n_up_layout, n_up_layout_display);
3727
3728           text_direction = gtk_widget_get_default_direction ();
3729           if (text_direction == GTK_TEXT_DIR_LTR)
3730             gtk_printer_option_set (option, "lrtb");
3731           else
3732             gtk_printer_option_set (option, "rltb");
3733
3734           set_option_from_settings (option, settings);
3735           gtk_printer_option_set_add (set, option);
3736           g_object_unref (option);
3737         }
3738
3739       num_of_covers = backend->number_of_covers;
3740       cover = g_new (char *, num_of_covers + 1);
3741       cover[num_of_covers] = NULL;
3742       cover_display = g_new (char *, num_of_covers + 1);
3743       cover_display[num_of_covers] = NULL;
3744       cover_display_translated = g_new (char *, num_of_covers + 1);
3745       cover_display_translated[num_of_covers] = NULL;
3746
3747       for (i = 0; i < num_of_covers; i++)
3748         {
3749           cover[i] = g_strdup (backend->covers[i]);
3750           value = NULL;
3751           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
3752             if (strcmp (cover_default[j], cover[i]) == 0)
3753               {
3754                 value = cover_display_default[j];
3755                 break;
3756               }
3757           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
3758         }
3759
3760       for (i = 0; i < num_of_covers; i++)
3761         cover_display_translated[i] = _(cover_display[i]);
3762   
3763       /* Translators, this is the label used for the option in the print 
3764        * dialog that controls the front cover page.
3765        */
3766       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3767       gtk_printer_option_choices_from_array (option, num_of_covers,
3768                                          cover, cover_display_translated);
3769
3770       if (cups_printer->default_cover_before != NULL)
3771         gtk_printer_option_set (option, cups_printer->default_cover_before);
3772       else
3773         gtk_printer_option_set (option, "none");
3774       set_option_from_settings (option, settings);
3775       gtk_printer_option_set_add (set, option);
3776       g_object_unref (option);
3777
3778       /* Translators, this is the label used for the option in the print 
3779        * dialog that controls the back cover page.
3780        */
3781       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3782       gtk_printer_option_choices_from_array (option, num_of_covers,
3783                                          cover, cover_display_translated);
3784       if (cups_printer->default_cover_after != NULL)
3785         gtk_printer_option_set (option, cups_printer->default_cover_after);
3786       else
3787         gtk_printer_option_set (option, "none");
3788       set_option_from_settings (option, settings);
3789       gtk_printer_option_set_add (set, option);
3790       g_object_unref (option);
3791
3792       g_strfreev (cover);
3793       g_strfreev (cover_display);
3794       g_free (cover_display_translated);
3795     }
3796
3797   /* Translators: this is the name of the option that controls when
3798    * a print job is printed. Possible values are 'now', a specified time,
3799    * or 'on hold'
3800    */
3801   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3802   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
3803                                          print_at, print_at);
3804   gtk_printer_option_set (option, "now");
3805   set_option_from_settings (option, settings);
3806   gtk_printer_option_set_add (set, option);
3807   g_object_unref (option);
3808   
3809   /* Translators: this is the name of the option that allows the user
3810    * to specify a time when a print job will be printed.
3811    */
3812   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
3813   gtk_printer_option_set (option, "");
3814   set_option_from_settings (option, settings);
3815   gtk_printer_option_set_add (set, option);
3816   g_object_unref (option);
3817   
3818   /* Printer (ppd) specific settings */
3819   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3820   if (ppd_file)
3821     {
3822       GtkPaperSize *paper_size;
3823       ppd_option_t *option;
3824       const gchar  *ppd_name;
3825
3826       ppdMarkDefaults (ppd_file);
3827
3828       paper_size = gtk_page_setup_get_paper_size (page_setup);
3829
3830       option = ppdFindOption (ppd_file, "PageSize");
3831       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
3832       
3833       if (ppd_name)
3834         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
3835       else
3836         {
3837           gchar *custom_name;
3838           char width[G_ASCII_DTOSTR_BUF_SIZE];
3839           char height[G_ASCII_DTOSTR_BUF_SIZE];
3840
3841           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3842           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3843           /* Translators: this format is used to display a custom paper
3844            * size. The two placeholders are replaced with the width and height
3845            * in points. E.g: "Custom 230.4x142.9"
3846            */
3847           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
3848           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
3849           g_free (custom_name);
3850         }
3851
3852       for (i = 0; i < ppd_file->num_groups; i++)
3853         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
3854     }
3855
3856   /* Now honor the user set defaults for this printer */
3857   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
3858
3859   for (i = 0; i < num_opts; i++)
3860     {
3861       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
3862         continue;
3863
3864       name = get_lpoption_name (opts[i].name);
3865       if (strcmp (name, "cups-job-sheets") == 0)
3866         {
3867           gchar **values;
3868           gint    num_values;
3869           
3870           values = g_strsplit (opts[i].value, ",", 2);
3871           num_values = g_strv_length (values);
3872
3873           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
3874           if (option && num_values > 0)
3875             gtk_printer_option_set (option, g_strstrip (values[0]));
3876
3877           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
3878           if (option && num_values > 1)
3879             gtk_printer_option_set (option, g_strstrip (values[1]));
3880
3881           g_strfreev (values);
3882         }
3883       else if (strcmp (name, "cups-job-hold-until") == 0)
3884         {
3885           GtkPrinterOption *option2 = NULL;
3886
3887           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
3888           if (option && opts[i].value)
3889             {
3890               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
3891               if (option2)
3892                 {
3893                   if (strcmp (opts[i].value, "indefinite") == 0)
3894                     gtk_printer_option_set (option2, "on-hold");
3895                   else
3896                     {
3897                       gtk_printer_option_set (option2, "at");
3898                       gtk_printer_option_set (option, opts[i].value);
3899                     }
3900                 }
3901             }
3902         }
3903       else if (strcmp (name, "cups-sides") == 0)
3904         {
3905           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
3906           if (option && opts[i].value)
3907             {
3908               if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
3909                 gtk_printer_option_set (option, "DuplexTumble");
3910               else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
3911                 gtk_printer_option_set (option, "DuplexNoTumble");
3912             }
3913         }
3914       else
3915         {
3916           option = gtk_printer_option_set_lookup (set, name);
3917           if (option)
3918             gtk_printer_option_set (option, opts[i].value);
3919         }
3920       g_free (name);
3921     }
3922
3923   cupsFreeOptions (num_opts, opts);
3924
3925 #ifdef HAVE_COLORD
3926   /* TRANSLATORS: this this the ICC color profile to use for this job */
3927   option = gtk_printer_option_new ("colord-profile",
3928                                    _("Printer Profile"),
3929                                    GTK_PRINTER_OPTION_TYPE_INFO);
3930
3931   /* assign it to the color page */
3932   option->group = g_strdup ("ColorPage");
3933
3934   /* TRANSLATORS: this is when color profile information is unavailable */
3935   gtk_printer_option_set (option, _("Unavailable"));
3936   gtk_printer_option_set_add (set, option);
3937
3938   /* watch to see if the user changed the options */
3939   helper = g_new (GtkPrintBackendCupsColordHelper, 1);
3940   helper->printer = printer;
3941   helper->settings = settings;
3942   g_signal_connect_data (set, "changed",
3943                          G_CALLBACK (colord_printer_option_set_changed_cb),
3944                          helper,
3945                          (GClosureNotify) g_free,
3946                          0);
3947
3948   /* initial coldplug */
3949   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
3950                                     settings, set);
3951   g_object_bind_property (printer, "profile-title",
3952                           option, "value",
3953                           G_BINDING_DEFAULT);
3954
3955 #endif
3956
3957   return set;
3958 }
3959
3960
3961 static void
3962 mark_option_from_set (GtkPrinterOptionSet *set,
3963                       ppd_file_t          *ppd_file,
3964                       ppd_option_t        *ppd_option)
3965 {
3966   GtkPrinterOption *option;
3967   char *name = get_ppd_option_name (ppd_option->keyword);
3968
3969   option = gtk_printer_option_set_lookup (set, name);
3970
3971   if (option)
3972     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
3973   
3974   g_free (name);
3975 }
3976
3977
3978 static void
3979 mark_group_from_set (GtkPrinterOptionSet *set,
3980                      ppd_file_t          *ppd_file,
3981                      ppd_group_t         *group)
3982 {
3983   int i;
3984
3985   for (i = 0; i < group->num_options; i++)
3986     mark_option_from_set (set, ppd_file, &group->options[i]);
3987
3988   for (i = 0; i < group->num_subgroups; i++)
3989     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
3990 }
3991
3992 static void
3993 set_conflicts_from_option (GtkPrinterOptionSet *set,
3994                            ppd_file_t          *ppd_file,
3995                            ppd_option_t        *ppd_option)
3996 {
3997   GtkPrinterOption *option;
3998   char *name;
3999
4000   if (ppd_option->conflicted)
4001     {
4002       name = get_ppd_option_name (ppd_option->keyword);
4003       option = gtk_printer_option_set_lookup (set, name);
4004
4005       if (option)
4006         gtk_printer_option_set_has_conflict (option, TRUE);
4007 #ifdef PRINT_IGNORED_OPTIONS
4008       else
4009         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
4010 #endif
4011       
4012       g_free (name);
4013     }
4014 }
4015
4016 static void
4017 set_conflicts_from_group (GtkPrinterOptionSet *set,
4018                           ppd_file_t          *ppd_file,
4019                           ppd_group_t         *group)
4020 {
4021   int i;
4022
4023   for (i = 0; i < group->num_options; i++)
4024     set_conflicts_from_option (set, ppd_file, &group->options[i]);
4025
4026   for (i = 0; i < group->num_subgroups; i++)
4027     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
4028 }
4029
4030 static gboolean
4031 cups_printer_mark_conflicts (GtkPrinter          *printer,
4032                              GtkPrinterOptionSet *options)
4033 {
4034   ppd_file_t *ppd_file;
4035   int num_conflicts;
4036   int i;
4037  
4038   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4039
4040   if (ppd_file == NULL)
4041     return FALSE;
4042
4043   ppdMarkDefaults (ppd_file);
4044
4045   for (i = 0; i < ppd_file->num_groups; i++)
4046     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
4047
4048   num_conflicts = ppdConflicts (ppd_file);
4049
4050   if (num_conflicts > 0)
4051     {
4052       for (i = 0; i < ppd_file->num_groups; i++)
4053         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
4054     }
4055  
4056   return num_conflicts > 0;
4057 }
4058
4059 struct OptionData {
4060   GtkPrinter *printer;
4061   GtkPrinterOptionSet *options;
4062   GtkPrintSettings *settings;
4063   ppd_file_t *ppd_file;
4064 };
4065
4066 typedef struct {
4067   const char *cups;
4068   const char *standard;
4069 } NameMapping;
4070
4071 static void
4072 map_settings_to_option (GtkPrinterOption  *option,
4073                         const NameMapping  table[],
4074                         gint               n_elements,
4075                         GtkPrintSettings  *settings,
4076                         const gchar       *standard_name,
4077                         const gchar       *cups_name)
4078 {
4079   int i;
4080   char *name;
4081   const char *cups_value;
4082   const char *standard_value;
4083
4084   /* If the cups-specific setting is set, always use that */
4085   name = g_strdup_printf ("cups-%s", cups_name);
4086   cups_value = gtk_print_settings_get (settings, name);
4087   g_free (name);
4088   
4089   if (cups_value != NULL) 
4090     {
4091       gtk_printer_option_set (option, cups_value);
4092       return;
4093     }
4094
4095   /* Otherwise we try to convert from the general setting */
4096   standard_value = gtk_print_settings_get (settings, standard_name);
4097   if (standard_value == NULL)
4098     return;
4099
4100   for (i = 0; i < n_elements; i++)
4101     {
4102       if (table[i].cups == NULL && table[i].standard == NULL)
4103         {
4104           gtk_printer_option_set (option, standard_value);
4105           break;
4106         }
4107       else if (table[i].cups == NULL &&
4108                strcmp (table[i].standard, standard_value) == 0)
4109         {
4110           set_option_off (option);
4111           break;
4112         }
4113       else if (strcmp (table[i].standard, standard_value) == 0)
4114         {
4115           gtk_printer_option_set (option, table[i].cups);
4116           break;
4117         }
4118     }
4119 }
4120
4121 static void
4122 map_option_to_settings (const gchar       *value,
4123                         const NameMapping  table[],
4124                         gint               n_elements,
4125                         GtkPrintSettings  *settings,
4126                         const gchar       *standard_name,
4127                         const gchar       *cups_name)
4128 {
4129   int i;
4130   char *name;
4131
4132   for (i = 0; i < n_elements; i++)
4133     {
4134       if (table[i].cups == NULL && table[i].standard == NULL)
4135         {
4136           gtk_print_settings_set (settings,
4137                                   standard_name,
4138                                   value);
4139           break;
4140         }
4141       else if (table[i].cups == NULL && table[i].standard != NULL)
4142         {
4143           if (value_is_off (value))
4144             {
4145               gtk_print_settings_set (settings,
4146                                       standard_name,
4147                                       table[i].standard);
4148               break;
4149             }
4150         }
4151       else if (strcmp (table[i].cups, value) == 0)
4152         {
4153           gtk_print_settings_set (settings,
4154                                   standard_name,
4155                                   table[i].standard);
4156           break;
4157         }
4158     }
4159
4160   /* Always set the corresponding cups-specific setting */
4161   name = g_strdup_printf ("cups-%s", cups_name);
4162   gtk_print_settings_set (settings, name, value);
4163   g_free (name);
4164 }
4165
4166
4167 static const NameMapping paper_source_map[] = {
4168   { "Lower", "lower"},
4169   { "Middle", "middle"},
4170   { "Upper", "upper"},
4171   { "Rear", "rear"},
4172   { "Envelope", "envelope"},
4173   { "Cassette", "cassette"},
4174   { "LargeCapacity", "large-capacity"},
4175   { "AnySmallFormat", "small-format"},
4176   { "AnyLargeFormat", "large-format"},
4177   { NULL, NULL}
4178 };
4179
4180 static const NameMapping output_tray_map[] = {
4181   { "Upper", "upper"},
4182   { "Lower", "lower"},
4183   { "Rear", "rear"},
4184   { NULL, NULL}
4185 };
4186
4187 static const NameMapping duplex_map[] = {
4188   { "DuplexTumble", "vertical" },
4189   { "DuplexNoTumble", "horizontal" },
4190   { NULL, "simplex" }
4191 };
4192
4193 static const NameMapping output_mode_map[] = {
4194   { "Standard", "normal" },
4195   { "Normal", "normal" },
4196   { "Draft", "draft" },
4197   { "Fast", "draft" },
4198 };
4199
4200 static const NameMapping media_type_map[] = {
4201   { "Transparency", "transparency"},
4202   { "Standard", "stationery"},
4203   { NULL, NULL}
4204 };
4205
4206 static const NameMapping all_map[] = {
4207   { NULL, NULL}
4208 };
4209
4210
4211 static void
4212 set_option_from_settings (GtkPrinterOption *option,
4213                           GtkPrintSettings *settings)
4214 {
4215   const char *cups_value;
4216   char *value;
4217   
4218   if (settings == NULL)
4219     return;
4220
4221   if (strcmp (option->name, "gtk-paper-source") == 0)
4222     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
4223                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4224   else if (strcmp (option->name, "gtk-output-tray") == 0)
4225     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
4226                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4227   else if (strcmp (option->name, "gtk-duplex") == 0)
4228     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
4229                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4230   else if (strcmp (option->name, "cups-OutputMode") == 0)
4231     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
4232                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4233   else if (strcmp (option->name, "cups-Resolution") == 0)
4234     {
4235       cups_value = gtk_print_settings_get (settings, option->name);
4236       if (cups_value)
4237         gtk_printer_option_set (option, cups_value);
4238       else
4239         {
4240           if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
4241               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
4242               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
4243               option->value == NULL || option->value[0] == '\0')
4244             {
4245               int res = gtk_print_settings_get_resolution (settings);
4246               int res_x = gtk_print_settings_get_resolution_x (settings);
4247               int res_y = gtk_print_settings_get_resolution_y (settings);
4248
4249               if (res_x != res_y)
4250                 {
4251                   value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
4252                   gtk_printer_option_set (option, value);
4253                   g_free (value);
4254                 }
4255               else if (res != 0)
4256                 {
4257                   value = g_strdup_printf ("%ddpi", res);
4258                   gtk_printer_option_set (option, value);
4259                   g_free (value);
4260                 }
4261             }
4262         }
4263     }
4264   else if (strcmp (option->name, "gtk-paper-type") == 0)
4265     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
4266                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4267   else if (strcmp (option->name, "gtk-n-up") == 0)
4268     {
4269       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4270                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4271     }
4272   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4273     {
4274       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4275                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4276     }
4277   else if (strcmp (option->name, "gtk-billing-info") == 0)
4278     {
4279       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
4280       if (cups_value)
4281         gtk_printer_option_set (option, cups_value);
4282     } 
4283   else if (strcmp (option->name, "gtk-job-prio") == 0)
4284     {
4285       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
4286       if (cups_value)
4287         gtk_printer_option_set (option, cups_value);
4288     } 
4289   else if (strcmp (option->name, "gtk-cover-before") == 0)
4290     {
4291       cups_value = gtk_print_settings_get (settings, "cover-before");
4292       if (cups_value)
4293         gtk_printer_option_set (option, cups_value);
4294     } 
4295   else if (strcmp (option->name, "gtk-cover-after") == 0)
4296     {
4297       cups_value = gtk_print_settings_get (settings, "cover-after");
4298       if (cups_value)
4299         gtk_printer_option_set (option, cups_value);
4300     } 
4301   else if (strcmp (option->name, "gtk-print-time") == 0)
4302     {
4303       cups_value = gtk_print_settings_get (settings, "print-at");
4304       if (cups_value)
4305         gtk_printer_option_set (option, cups_value);
4306     } 
4307   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4308     {
4309       cups_value = gtk_print_settings_get (settings, "print-at-time");
4310       if (cups_value)
4311         gtk_printer_option_set (option, cups_value);
4312     } 
4313   else if (g_str_has_prefix (option->name, "cups-"))
4314     {
4315       cups_value = gtk_print_settings_get (settings, option->name);
4316       if (cups_value)
4317         gtk_printer_option_set (option, cups_value);
4318     } 
4319 }
4320
4321 static void
4322 foreach_option_get_settings (GtkPrinterOption *option,
4323                              gpointer          user_data)
4324 {
4325   struct OptionData *data = user_data;
4326   GtkPrintSettings *settings = data->settings;
4327   const char *value;
4328
4329   value = option->value;
4330
4331   if (strcmp (option->name, "gtk-paper-source") == 0)
4332     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
4333                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4334   else if (strcmp (option->name, "gtk-output-tray") == 0)
4335     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
4336                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4337   else if (strcmp (option->name, "gtk-duplex") == 0)
4338     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
4339                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4340   else if (strcmp (option->name, "cups-OutputMode") == 0)
4341     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
4342                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4343   else if (strcmp (option->name, "cups-Resolution") == 0)
4344     {
4345       int res, res_x, res_y;
4346
4347       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
4348         {
4349           if (res_x > 0 && res_y > 0)
4350             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
4351         }
4352       else if (sscanf (value, "%ddpi", &res) == 1)
4353         {
4354           if (res > 0)
4355             gtk_print_settings_set_resolution (settings, res);
4356         }
4357
4358       gtk_print_settings_set (settings, option->name, value);
4359     }
4360   else if (strcmp (option->name, "gtk-paper-type") == 0)
4361     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
4362                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4363   else if (strcmp (option->name, "gtk-n-up") == 0)
4364     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4365                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4366   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4367     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4368                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4369   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
4370     gtk_print_settings_set (settings, "cups-job-billing", value);
4371   else if (strcmp (option->name, "gtk-job-prio") == 0)
4372     gtk_print_settings_set (settings, "cups-job-priority", value);
4373   else if (strcmp (option->name, "gtk-cover-before") == 0)
4374     gtk_print_settings_set (settings, "cover-before", value);
4375   else if (strcmp (option->name, "gtk-cover-after") == 0)
4376     gtk_print_settings_set (settings, "cover-after", value);
4377   else if (strcmp (option->name, "gtk-print-time") == 0)
4378     gtk_print_settings_set (settings, "print-at", value);
4379   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4380     gtk_print_settings_set (settings, "print-at-time", value);
4381   else if (g_str_has_prefix (option->name, "cups-"))
4382     gtk_print_settings_set (settings, option->name, value);
4383 }
4384
4385 static gboolean
4386 supports_am_pm (void)
4387 {
4388   struct tm tmp_tm = { 0 };
4389   char   time[8];
4390   int    length;
4391
4392   length = strftime (time, sizeof (time), "%p", &tmp_tm);
4393
4394   return length != 0;
4395 }
4396
4397 /* Converts local time to UTC time. Local time has to be in one of these
4398  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
4399  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
4400  * Returns a newly allocated string holding UTC time in HH:MM:SS format
4401  * or NULL.
4402  */
4403 gchar *
4404 localtime_to_utctime (const char *local_time)
4405 {
4406   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
4407                              " %H : %M : %S ",
4408                              " %I : %M %p ", " %p %I : %M ",
4409                              " %H : %M ",
4410                              " %I %p ", " %p %I "};
4411   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
4412   const char *end = NULL;
4413   struct tm  *actual_local_time;
4414   struct tm  *actual_utc_time;
4415   struct tm   local_print_time;
4416   struct tm   utc_print_time;
4417   struct tm   diff_time;
4418   gchar      *utc_time = NULL;
4419   int         i, n;
4420
4421   if (local_time == NULL || local_time[0] == '\0')
4422     return NULL;
4423
4424   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
4425
4426   for (i = 0; i < n; i++)
4427     {
4428       local_print_time.tm_hour = 0;
4429       local_print_time.tm_min  = 0;
4430       local_print_time.tm_sec  = 0;
4431
4432       if (supports_am_pm ())
4433         end = strptime (local_time, formats_0[i], &local_print_time);
4434       else
4435         end = strptime (local_time, formats_1[i], &local_print_time);
4436
4437       if (end != NULL && end[0] == '\0')
4438         break;
4439     }
4440
4441   if (end != NULL && end[0] == '\0')
4442     {
4443       time_t rawtime;
4444       time (&rawtime);
4445
4446       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
4447       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
4448
4449       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
4450       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
4451       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
4452
4453       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
4454       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
4455       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
4456
4457       utc_time = g_strdup_printf ("%02d:%02d:%02d",
4458                                   utc_print_time.tm_hour,
4459                                   utc_print_time.tm_min,
4460                                   utc_print_time.tm_sec);
4461     }
4462
4463   return utc_time;
4464 }
4465
4466 static void
4467 cups_printer_get_settings_from_options (GtkPrinter          *printer,
4468                                         GtkPrinterOptionSet *options,
4469                                         GtkPrintSettings    *settings)
4470 {
4471   struct OptionData data;
4472   const char *print_at, *print_at_time;
4473
4474   data.printer = printer;
4475   data.options = options;
4476   data.settings = settings;
4477   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4478  
4479   if (data.ppd_file != NULL)
4480     {
4481       GtkPrinterOption *cover_before, *cover_after;
4482       
4483       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
4484
4485       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
4486       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
4487       if (cover_before && cover_after)
4488         {
4489           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
4490           gtk_print_settings_set (settings, "cups-job-sheets", value);
4491           g_free (value);
4492         }
4493
4494       print_at = gtk_print_settings_get (settings, "print-at");
4495       print_at_time = gtk_print_settings_get (settings, "print-at-time");
4496
4497       if (strcmp (print_at, "at") == 0)
4498         {
4499           gchar *utc_time = NULL;
4500           
4501           utc_time = localtime_to_utctime (print_at_time);
4502
4503           if (utc_time != NULL)
4504             {
4505               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
4506               g_free (utc_time);
4507             }
4508           else
4509             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
4510         }
4511       else if (strcmp (print_at, "on-hold") == 0)
4512         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
4513     }
4514 }
4515
4516 static void
4517 cups_printer_prepare_for_print (GtkPrinter       *printer,
4518                                 GtkPrintJob      *print_job,
4519                                 GtkPrintSettings *settings,
4520                                 GtkPageSetup     *page_setup)
4521 {
4522   GtkPrintPages pages;
4523   GtkPageRange *ranges;
4524   gint n_ranges;
4525   GtkPageSet page_set;
4526   GtkPaperSize *paper_size;
4527   const char *ppd_paper_name;
4528   double scale;
4529
4530   pages = gtk_print_settings_get_print_pages (settings);
4531   gtk_print_job_set_pages (print_job, pages);
4532
4533   if (pages == GTK_PRINT_PAGES_RANGES)
4534     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
4535   else
4536     {
4537       ranges = NULL;
4538       n_ranges = 0;
4539     }
4540
4541   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
4542   if (gtk_print_settings_get_collate (settings))
4543     gtk_print_settings_set (settings, "cups-Collate", "True");
4544   gtk_print_job_set_collate (print_job, FALSE);
4545
4546   if (gtk_print_settings_get_reverse (settings))
4547     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
4548   gtk_print_job_set_reverse (print_job, FALSE);
4549
4550   if (gtk_print_settings_get_n_copies (settings) > 1)
4551     gtk_print_settings_set_int (settings, "cups-copies",
4552                                 gtk_print_settings_get_n_copies (settings));
4553   gtk_print_job_set_num_copies (print_job, 1);
4554
4555   scale = gtk_print_settings_get_scale (settings);
4556   if (scale != 100.0)
4557     gtk_print_job_set_scale (print_job, scale / 100.0);
4558
4559   page_set = gtk_print_settings_get_page_set (settings);
4560   if (page_set == GTK_PAGE_SET_EVEN)
4561     gtk_print_settings_set (settings, "cups-page-set", "even");
4562   else if (page_set == GTK_PAGE_SET_ODD)
4563     gtk_print_settings_set (settings, "cups-page-set", "odd");
4564   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
4565
4566   paper_size = gtk_page_setup_get_paper_size (page_setup);
4567   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
4568   if (ppd_paper_name != NULL)
4569     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
4570   else
4571     {
4572       char width[G_ASCII_DTOSTR_BUF_SIZE];
4573       char height[G_ASCII_DTOSTR_BUF_SIZE];
4574       char *custom_name;
4575
4576       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
4577       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
4578       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
4579       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
4580       g_free (custom_name);
4581     }
4582
4583   if (gtk_print_settings_get_number_up (settings) > 1)
4584     {
4585       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
4586       GEnumClass        *enum_class;
4587       GEnumValue        *enum_value;
4588
4589       switch (gtk_page_setup_get_orientation (page_setup))
4590         {
4591           case GTK_PAGE_ORIENTATION_PORTRAIT:
4592             break;
4593           case GTK_PAGE_ORIENTATION_LANDSCAPE:
4594             if (layout < 4)
4595               layout = layout + 2 + 4 * (1 - layout / 2);
4596             else
4597               layout = layout - 3 - 2 * (layout % 2);
4598             break;
4599           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4600             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
4601             break;
4602           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4603             if (layout < 4)
4604               layout = layout + 5 - 2 * (layout % 2);
4605             else
4606               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
4607             break;
4608         }
4609
4610       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
4611       enum_value = g_enum_get_value (enum_class, layout);
4612       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
4613       g_type_class_unref (enum_class);
4614     }
4615
4616   gtk_print_job_set_rotate (print_job, TRUE);
4617 }
4618
4619 static GtkPageSetup *
4620 create_page_setup (ppd_file_t *ppd_file,
4621                    ppd_size_t *size)
4622  {
4623    char *display_name;
4624    GtkPageSetup *page_setup;
4625    GtkPaperSize *paper_size;
4626    ppd_option_t *option;
4627    ppd_choice_t *choice;
4628
4629   display_name = NULL;
4630   option = ppdFindOption (ppd_file, "PageSize");
4631   if (option)
4632     {
4633       choice = ppdFindChoice (option, size->name);
4634       if (choice)
4635         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
4636     }
4637
4638   if (display_name == NULL)
4639     display_name = g_strdup (size->name);
4640   
4641   page_setup = gtk_page_setup_new ();
4642   paper_size = gtk_paper_size_new_from_ppd (size->name,
4643                                             display_name,
4644                                             size->width,
4645                                             size->length);
4646   gtk_page_setup_set_paper_size (page_setup, paper_size);
4647   gtk_paper_size_free (paper_size);
4648   
4649   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
4650   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
4651   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
4652   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
4653   
4654   g_free (display_name);
4655
4656   return page_setup;
4657 }
4658
4659 static GList *
4660 cups_printer_list_papers (GtkPrinter *printer)
4661 {
4662   ppd_file_t *ppd_file;
4663   ppd_size_t *size;
4664   GtkPageSetup *page_setup;
4665   GList *l;
4666   int i;
4667
4668   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4669   if (ppd_file == NULL)
4670     return NULL;
4671
4672   l = NULL;
4673   
4674   for (i = 0; i < ppd_file->num_sizes; i++)
4675     {
4676       size = &ppd_file->sizes[i];      
4677
4678       page_setup = create_page_setup (ppd_file, size);
4679
4680       l = g_list_prepend (l, page_setup);
4681     }
4682
4683   return g_list_reverse (l);
4684 }
4685
4686 static GtkPageSetup *
4687 cups_printer_get_default_page_size (GtkPrinter *printer)
4688 {
4689   ppd_file_t *ppd_file;
4690   ppd_size_t *size;
4691   ppd_option_t *option;
4692
4693
4694   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4695   if (ppd_file == NULL)
4696     return NULL;
4697
4698   option = ppdFindOption (ppd_file, "PageSize");
4699   if (option == NULL)
4700     return NULL;
4701
4702   size = ppdPageSize (ppd_file, option->defchoice); 
4703   if (size == NULL)
4704     return NULL;
4705
4706   return create_page_setup (ppd_file, size);
4707 }
4708
4709 static gboolean
4710 cups_printer_get_hard_margins (GtkPrinter *printer,
4711                                gdouble    *top,
4712                                gdouble    *bottom,
4713                                gdouble    *left,
4714                                gdouble    *right)
4715 {
4716   ppd_file_t *ppd_file;
4717
4718   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4719   if (ppd_file == NULL)
4720     return FALSE;
4721
4722   *left = ppd_file->custom_margins[0];
4723   *bottom = ppd_file->custom_margins[1];
4724   *right = ppd_file->custom_margins[2];
4725   *top = ppd_file->custom_margins[3];
4726
4727   return TRUE;
4728 }
4729
4730 static GtkPrintCapabilities
4731 cups_printer_get_capabilities (GtkPrinter *printer)
4732 {
4733   return
4734     GTK_PRINT_CAPABILITY_COPIES |
4735     GTK_PRINT_CAPABILITY_COLLATE |
4736     GTK_PRINT_CAPABILITY_REVERSE |
4737 #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
4738     GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
4739 #endif
4740     GTK_PRINT_CAPABILITY_NUMBER_UP;
4741 }