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