]> Pileus Git - ~andy/gtk/blob - modules/printbackends/cups/gtkprintbackendcups.c
120f0c13038e3ea873103bd27e502a23c09e7702
[~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 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
2322   ppdLocalize (data->printer->ppd_file);
2323 #endif
2324
2325   ppdMarkDefaults (data->printer->ppd_file);
2326   
2327   gtk_printer_set_has_details (printer, TRUE);
2328   g_signal_emit_by_name (printer, "details-acquired", TRUE);
2329
2330 done:
2331   GDK_THREADS_LEAVE ();
2332 }
2333
2334 static gboolean
2335 cups_request_ppd (GtkPrinter *printer)
2336 {
2337   GError *error;
2338   GtkPrintBackend *print_backend;
2339   GtkPrinterCups *cups_printer;
2340   GtkCupsRequest *request;
2341   char *ppd_filename;
2342   gchar *resource;
2343   http_t *http;
2344   GetPPDData *data;
2345   int fd;
2346
2347   cups_printer = GTK_PRINTER_CUPS (printer);
2348
2349   error = NULL;
2350
2351   GTK_NOTE (PRINTING,
2352             g_print ("CUPS Backend: %s\n", G_STRFUNC));
2353
2354   if (cups_printer->remote)
2355     {
2356       GtkCupsConnectionState state;
2357
2358       state = gtk_cups_connection_test_get_state (cups_printer->remote_cups_connection_test);
2359
2360       if (state == GTK_CUPS_CONNECTION_IN_PROGRESS)
2361         {
2362           if (cups_printer->get_remote_ppd_attempts == 60)
2363             {
2364               cups_printer->get_remote_ppd_attempts = -1;
2365               if (cups_printer->get_remote_ppd_poll > 0)
2366                 g_source_remove (cups_printer->get_remote_ppd_poll);
2367               cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (200,
2368                                                     (GSourceFunc) cups_request_ppd,
2369                                                     printer);
2370             }
2371           else if (cups_printer->get_remote_ppd_attempts != -1)
2372             cups_printer->get_remote_ppd_attempts++;
2373
2374           return TRUE;
2375         }
2376
2377       gtk_cups_connection_test_free (cups_printer->remote_cups_connection_test);
2378       cups_printer->remote_cups_connection_test = NULL;
2379       cups_printer->get_remote_ppd_poll = 0;
2380       cups_printer->get_remote_ppd_attempts = 0;
2381
2382       if (state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2383         {
2384           g_signal_emit_by_name (printer, "details-acquired", FALSE);
2385           return FALSE;
2386         }
2387     }
2388
2389   http = httpConnectEncrypt (cups_printer->hostname, 
2390                              cups_printer->port,
2391                              cupsEncryption ());
2392   
2393   data = g_new0 (GetPPDData, 1);
2394
2395   fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX", 
2396                         &ppd_filename, 
2397                         &error);
2398
2399 #ifdef G_ENABLE_DEBUG 
2400   /* If we are debugging printing don't delete the tmp files */
2401   if (!(gtk_get_debug_flags () & GTK_DEBUG_PRINTING))
2402     unlink (ppd_filename);
2403 #else
2404   unlink (ppd_filename);
2405 #endif /* G_ENABLE_DEBUG */
2406
2407   if (error != NULL)
2408     {
2409       GTK_NOTE (PRINTING, 
2410                 g_warning ("CUPS Backend: Failed to create temp file, %s\n", 
2411                            error->message));
2412       g_error_free (error);
2413       httpClose (http);
2414       g_free (ppd_filename);
2415       g_free (data);
2416
2417       g_signal_emit_by_name (printer, "details-acquired", FALSE);
2418       return FALSE;
2419     }
2420     
2421   data->http = http;
2422   fchmod (fd, S_IRUSR | S_IWUSR);
2423   data->ppd_io = g_io_channel_unix_new (fd);
2424   g_io_channel_set_encoding (data->ppd_io, NULL, NULL);
2425   g_io_channel_set_close_on_unref (data->ppd_io, TRUE);
2426
2427   data->printer = g_object_ref (printer);
2428
2429   resource = g_strdup_printf ("/printers/%s.ppd", 
2430                               gtk_printer_cups_get_ppd_name (GTK_PRINTER_CUPS (printer)));
2431
2432   print_backend = gtk_printer_get_backend (printer);
2433
2434   request = gtk_cups_request_new_with_username (data->http,
2435                                                 GTK_CUPS_GET,
2436                                                 0,
2437                                                 data->ppd_io,
2438                                                 cups_printer->hostname,
2439                                                 resource,
2440                                                 GTK_PRINT_BACKEND_CUPS (print_backend)->username);
2441
2442   GTK_NOTE (PRINTING,
2443             g_print ("CUPS Backend: Requesting resource %s to be written to temp file %s\n", resource, ppd_filename));
2444
2445
2446   cups_printer->reading_ppd = TRUE;
2447   GTK_PRINT_BACKEND_CUPS (print_backend)->reading_ppds++;
2448
2449   cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
2450                         request,
2451                         (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
2452                         data,
2453                         (GDestroyNotify)get_ppd_data_free);
2454
2455   g_free (resource);
2456   g_free (ppd_filename);
2457
2458   return FALSE;
2459 }
2460
2461 /* Ordering matters for default preference */
2462 static const char *lpoptions_locations[] = {
2463   "/etc/cups/lpoptions",
2464   ".lpoptions", 
2465   ".cups/lpoptions"
2466 };
2467
2468 static void
2469 cups_parse_user_default_printer (const char  *filename,
2470                                  char       **printer_name)
2471 {
2472   FILE *fp;
2473   char line[1024], *lineptr, *defname = NULL;
2474   
2475   if ((fp = g_fopen (filename, "r")) == NULL)
2476     return;
2477
2478   while (fgets (line, sizeof (line), fp) != NULL)
2479     {
2480       if (strncasecmp (line, "default", 7) != 0 || !isspace (line[7]))
2481         continue;
2482
2483       lineptr = line + 8;
2484       while (isspace (*lineptr))
2485         lineptr++;
2486
2487       if (!*lineptr)
2488         continue;
2489
2490       defname = lineptr;
2491       while (!isspace (*lineptr) && *lineptr && *lineptr != '/')
2492         lineptr++;
2493
2494       *lineptr = '\0';
2495
2496       if (*printer_name != NULL)
2497         g_free (*printer_name);
2498
2499       *printer_name = g_strdup (defname);
2500     }
2501
2502   fclose (fp);
2503 }
2504
2505 static void
2506 cups_get_user_default_printer (char **printer_name)
2507 {
2508   int i;
2509
2510   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2511     {
2512       if (g_path_is_absolute (lpoptions_locations[i]))
2513         {
2514           cups_parse_user_default_printer (lpoptions_locations[i],
2515                                            printer_name);
2516         }
2517       else 
2518         {
2519           char *filename;
2520
2521           filename = g_build_filename (g_get_home_dir (), 
2522                                        lpoptions_locations[i], NULL);
2523           cups_parse_user_default_printer (filename, printer_name);
2524           g_free (filename);
2525         }
2526     }
2527 }
2528
2529 static int
2530 cups_parse_user_options (const char     *filename,
2531                          const char     *printer_name,
2532                          int             num_options,
2533                          cups_option_t **options)
2534 {
2535   FILE *fp;
2536   gchar line[1024], *lineptr, *name;
2537
2538   if ((fp = g_fopen (filename, "r")) == NULL)
2539     return num_options;
2540
2541   while (fgets (line, sizeof (line), fp) != NULL)
2542     {
2543       if (strncasecmp (line, "dest", 4) == 0 && isspace (line[4]))
2544         lineptr = line + 4;
2545       else if (strncasecmp (line, "default", 7) == 0 && isspace (line[7]))
2546         lineptr = line + 7;
2547       else
2548         continue;
2549
2550       /* Skip leading whitespace */
2551       while (isspace (*lineptr))
2552         lineptr++;
2553
2554       if (!*lineptr)
2555         continue;
2556
2557       /* NUL-terminate the name, stripping the instance name */
2558       name = lineptr;
2559       while (!isspace (*lineptr) && *lineptr)
2560         {
2561           if (*lineptr == '/')
2562             *lineptr = '\0';
2563           lineptr++;
2564         }
2565
2566       if (!*lineptr)
2567         continue;
2568
2569       *lineptr++ = '\0';
2570
2571       if (strncasecmp (name, printer_name, strlen (printer_name)) != 0)
2572           continue;
2573
2574       /* We found our printer, parse the options */
2575       num_options = cupsParseOptions (lineptr, num_options, options);
2576     }
2577
2578   fclose (fp);
2579
2580   return num_options;
2581 }
2582
2583 static int
2584 cups_get_user_options (const char     *printer_name,
2585                        int             num_options,
2586                        cups_option_t **options)
2587 {
2588   int i;
2589
2590   for (i = 0; i < G_N_ELEMENTS (lpoptions_locations); i++)
2591     {
2592       if (g_path_is_absolute (lpoptions_locations[i]))
2593         { 
2594            num_options = cups_parse_user_options (lpoptions_locations[i],
2595                                                   printer_name,
2596                                                   num_options,
2597                                                   options);
2598         }
2599       else
2600         {
2601           char *filename;
2602
2603           filename = g_build_filename (g_get_home_dir (), 
2604                                        lpoptions_locations[i], NULL);
2605           num_options = cups_parse_user_options (filename, printer_name,
2606                                                  num_options, options);
2607           g_free (filename);
2608         }
2609     }
2610
2611   return num_options;
2612 }
2613
2614 /* This function requests default printer from a CUPS server in regular intervals.
2615  * In the case of unreachable CUPS server the request is repeated later.
2616  * The default printer is not requested in the case of previous success.
2617  */
2618 static void
2619 cups_get_default_printer (GtkPrintBackendCups *backend)
2620 {
2621   GtkPrintBackendCups *cups_backend;
2622
2623   cups_backend = backend;
2624
2625   if (cups_backend->cups_connection_test == NULL)
2626     cups_backend->cups_connection_test = gtk_cups_connection_test_new (NULL);
2627
2628   if (cups_backend->default_printer_poll == 0)
2629     {
2630       if (cups_request_default_printer (cups_backend))
2631         cups_backend->default_printer_poll = gdk_threads_add_timeout (200,
2632                                                (GSourceFunc) cups_request_default_printer,
2633                                                backend);
2634     }
2635 }
2636
2637 /* This function gets default printer from local settings.*/
2638 static void
2639 cups_get_local_default_printer (GtkPrintBackendCups *backend)
2640 {
2641   const char *str;
2642   char *name = NULL;
2643
2644   if ((str = g_getenv ("LPDEST")) != NULL)
2645     {
2646       backend->default_printer = g_strdup (str);
2647       backend->got_default_printer = TRUE;
2648       return;
2649     }
2650   else if ((str = g_getenv ("PRINTER")) != NULL &&
2651            strcmp (str, "lp") != 0)
2652     {
2653       backend->default_printer = g_strdup (str);
2654       backend->got_default_printer = TRUE;
2655       return;
2656     }
2657   
2658   /* Figure out user setting for default printer */  
2659   cups_get_user_default_printer (&name);
2660   if (name != NULL)
2661     {
2662       backend->default_printer = name;
2663       backend->got_default_printer = TRUE;
2664       return;
2665     }
2666 }
2667
2668 static void
2669 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
2670                                  GtkCupsResult       *result,
2671                                  gpointer             user_data)
2672 {
2673   ipp_t *response;
2674   ipp_attribute_t *attr;
2675   GtkPrinter *printer;
2676
2677   GDK_THREADS_ENTER ();
2678
2679   if (gtk_cups_result_is_error (result))
2680     {
2681       if (gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_AUTH &&
2682           gtk_cups_result_get_error_code (result) == 1)
2683         {
2684           /* Canceled by user, stop popping up more password dialogs */
2685           if (print_backend->list_printers_poll > 0)
2686             g_source_remove (print_backend->list_printers_poll);
2687           print_backend->list_printers_poll = 0;
2688         }
2689
2690       return;
2691     }
2692
2693   response = gtk_cups_result_get_response (result);
2694   
2695   if ((attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME)) != NULL)
2696     print_backend->default_printer = g_strdup (attr->values[0].string.text);
2697
2698   print_backend->got_default_printer = TRUE;
2699
2700   if (print_backend->default_printer != NULL)
2701     {
2702       printer = gtk_print_backend_find_printer (GTK_PRINT_BACKEND (print_backend), print_backend->default_printer);
2703       if (printer != NULL)
2704         {
2705           gtk_printer_set_is_default (printer, TRUE);
2706           g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
2707         }
2708     }
2709
2710   /* Make sure to kick off get_printers if we are polling it, 
2711    * as we could have blocked this reading the default printer 
2712    */
2713   if (print_backend->list_printers_poll != 0)
2714     cups_request_printer_list (print_backend);
2715
2716   GDK_THREADS_LEAVE ();
2717 }
2718
2719 static gboolean
2720 cups_request_default_printer (GtkPrintBackendCups *print_backend)
2721 {
2722   GtkCupsConnectionState state;
2723   GtkCupsRequest *request;
2724
2725   state = gtk_cups_connection_test_get_state (print_backend->cups_connection_test);
2726   update_backend_status (print_backend, state);
2727
2728   if (state == GTK_CUPS_CONNECTION_IN_PROGRESS || state == GTK_CUPS_CONNECTION_NOT_AVAILABLE)
2729     return TRUE;
2730
2731   request = gtk_cups_request_new_with_username (NULL,
2732                                                 GTK_CUPS_POST,
2733                                                 CUPS_GET_DEFAULT,
2734                                                 NULL,
2735                                                 NULL,
2736                                                 NULL,
2737                                                 print_backend->username);
2738   
2739   cups_request_execute (print_backend,
2740                         request,
2741                         (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
2742                         g_object_ref (print_backend),
2743                         g_object_unref);
2744
2745   return FALSE;
2746 }
2747
2748 static void
2749 cups_printer_request_details (GtkPrinter *printer)
2750 {
2751   GtkPrinterCups *cups_printer;
2752
2753   cups_printer = GTK_PRINTER_CUPS (printer);
2754   if (!cups_printer->reading_ppd && 
2755       gtk_printer_cups_get_ppd (cups_printer) == NULL)
2756     {
2757       if (cups_printer->remote)
2758         {
2759           if (cups_printer->get_remote_ppd_poll == 0)
2760             {
2761               cups_printer->remote_cups_connection_test = gtk_cups_connection_test_new (cups_printer->hostname);
2762
2763               if (cups_request_ppd (printer))
2764                 cups_printer->get_remote_ppd_poll = gdk_threads_add_timeout (50,
2765                                                     (GSourceFunc) cups_request_ppd,
2766                                                     printer);
2767             }
2768         }
2769       else
2770         cups_request_ppd (printer);
2771     }
2772 }
2773
2774 static char *
2775 ppd_text_to_utf8 (ppd_file_t *ppd_file, 
2776                   const char *text)
2777 {
2778   const char *encoding = NULL;
2779   char *res;
2780   
2781   if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
2782     {
2783       return g_strdup (text);
2784     }
2785   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
2786     {
2787       encoding = "ISO-8859-1";
2788     }
2789   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
2790     {
2791       encoding = "ISO-8859-2";
2792     }
2793   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
2794     {
2795       encoding = "ISO-8859-5";
2796     }
2797   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
2798     {
2799       encoding = "SHIFT-JIS";
2800     }
2801   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
2802     {
2803       encoding = "MACINTOSH";
2804     }
2805   else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
2806     {
2807       encoding = "WINDOWS-1252";
2808     }
2809   else 
2810     {
2811       /* Fallback, try iso-8859-1... */
2812       encoding = "ISO-8859-1";
2813     }
2814
2815   res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
2816
2817   if (res == NULL)
2818     {
2819       GTK_NOTE (PRINTING,
2820                 g_warning ("CUPS Backend: Unable to convert PPD text\n"));
2821       res = g_strdup ("???");
2822     }
2823   
2824   return res;
2825 }
2826
2827 /* TODO: Add more translations for common settings here */
2828
2829 static const struct {
2830   const char *keyword;
2831   const char *translation;
2832 } cups_option_translations[] = {
2833   { "Duplex", N_("Two Sided") },
2834   { "MediaType", N_("Paper Type") },
2835   { "InputSlot", N_("Paper Source") },
2836   { "OutputBin", N_("Output Tray") },
2837   { "Resolution", N_("Resolution") },
2838   { "PreFilter", N_("GhostScript pre-filtering") },
2839 };
2840
2841
2842 static const struct {
2843   const char *keyword;
2844   const char *choice;
2845   const char *translation;
2846 } cups_choice_translations[] = {
2847   { "Duplex", "None", N_("One Sided") },
2848   /* Translators: this is an option of "Two Sided" */
2849   { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
2850   /* Translators: this is an option of "Two Sided" */
2851   { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
2852   /* Translators: this is an option of "Paper Source" */
2853   { "InputSlot", "Auto", N_("Auto Select") },
2854   /* Translators: this is an option of "Paper Source" */
2855   { "InputSlot", "AutoSelect", N_("Auto Select") },
2856   /* Translators: this is an option of "Paper Source" */
2857   { "InputSlot", "Default", N_("Printer Default") },
2858   /* Translators: this is an option of "Paper Source" */
2859   { "InputSlot", "None", N_("Printer Default") },
2860   /* Translators: this is an option of "Paper Source" */
2861   { "InputSlot", "PrinterDefault", N_("Printer Default") },
2862   /* Translators: this is an option of "Paper Source" */
2863   { "InputSlot", "Unspecified", N_("Auto Select") },
2864   /* Translators: this is an option of "Resolution" */
2865   { "Resolution", "default", N_("Printer Default") },
2866   /* Translators: this is an option of "GhostScript" */
2867   { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
2868   /* Translators: this is an option of "GhostScript" */
2869   { "PreFilter", "Level1", N_("Convert to PS level 1") },
2870   /* Translators: this is an option of "GhostScript" */
2871   { "PreFilter", "Level2", N_("Convert to PS level 2") },
2872   /* Translators: this is an option of "GhostScript" */
2873   { "PreFilter", "No", N_("No pre-filtering") },
2874 };
2875
2876 static const struct {
2877   const char *name;
2878   const char *translation;
2879 } cups_group_translations[] = {
2880 /* Translators: "Miscellaneous" is the label for a button, that opens
2881    up an extra panel of settings in a print dialog. */
2882   { "Miscellaneous", N_("Miscellaneous") },
2883 };
2884
2885 static const struct {
2886   const char *ppd_keyword;
2887   const char *name;
2888 } ppd_option_names[] = {
2889   {"Duplex", "gtk-duplex" },
2890   {"MediaType", "gtk-paper-type"},
2891   {"InputSlot", "gtk-paper-source"},
2892   {"OutputBin", "gtk-output-tray"},
2893 };
2894
2895 static const struct {
2896   const char *lpoption;
2897   const char *name;
2898 } lpoption_names[] = {
2899   {"number-up", "gtk-n-up" },
2900   {"number-up-layout", "gtk-n-up-layout"},
2901   {"job-billing", "gtk-billing-info"},
2902   {"job-priority", "gtk-job-prio"},
2903 };
2904
2905 /* keep sorted when changing */
2906 static const char *color_option_whitelist[] = {
2907   "BRColorEnhancement",
2908   "BRColorMatching",
2909   "BRColorMatching",
2910   "BRColorMode",
2911   "BRGammaValue",
2912   "BRImprovedGray",
2913   "BlackSubstitution",
2914   "ColorModel",
2915   "HPCMYKInks",
2916   "HPCSGraphics",
2917   "HPCSImages",
2918   "HPCSText",
2919   "HPColorSmart",
2920   "RPSBlackMode",
2921   "RPSBlackOverPrint",
2922   "Rcmyksimulation",
2923 };
2924
2925 /* keep sorted when changing */
2926 static const char *color_group_whitelist[] = {
2927   "ColorPage",
2928   "FPColorWise1",
2929   "FPColorWise2",
2930   "FPColorWise3",
2931   "FPColorWise4",
2932   "FPColorWise5",
2933   "HPColorOptionsPanel",
2934 };
2935   
2936 /* keep sorted when changing */
2937 static const char *image_quality_option_whitelist[] = {
2938   "BRDocument",
2939   "BRHalfTonePattern",
2940   "BRNormalPrt",
2941   "BRPrintQuality",
2942   "BitsPerPixel",
2943   "Darkness",
2944   "Dithering",
2945   "EconoMode",
2946   "Economode",
2947   "HPEconoMode",
2948   "HPEdgeControl",
2949   "HPGraphicsHalftone",
2950   "HPHalftone",
2951   "HPLJDensity",
2952   "HPPhotoHalftone",
2953   "OutputMode",
2954   "REt",
2955   "RPSBitsPerPixel",
2956   "RPSDitherType",
2957   "Resolution",
2958   "ScreenLock",
2959   "Smoothing",
2960   "TonerSaveMode",
2961   "UCRGCRForImage",
2962 };
2963
2964 /* keep sorted when changing */
2965 static const char *image_quality_group_whitelist[] = {
2966   "FPImageQuality1",
2967   "FPImageQuality2",
2968   "FPImageQuality3",
2969   "ImageQualityPage",
2970 };
2971
2972 /* keep sorted when changing */
2973 static const char * finishing_option_whitelist[] = {
2974   "BindColor",
2975   "BindEdge",
2976   "BindType",
2977   "BindWhen",
2978   "Booklet",
2979   "FoldType",
2980   "FoldWhen",
2981   "HPStaplerOptions",
2982   "Jog",
2983   "Slipsheet",
2984   "Sorter",
2985   "StapleLocation",
2986   "StapleOrientation",
2987   "StapleWhen",
2988   "StapleX",
2989   "StapleY",
2990 };
2991
2992 /* keep sorted when changing */
2993 static const char *finishing_group_whitelist[] = {
2994   "FPFinishing1",
2995   "FPFinishing2",
2996   "FPFinishing3",
2997   "FPFinishing4",
2998   "FinishingPage",
2999   "HPFinishingPanel",
3000 };
3001
3002 /* keep sorted when changing */
3003 static const char *cups_option_blacklist[] = {
3004   "Collate",
3005   "Copies", 
3006   "OutputOrder",
3007   "PageRegion",
3008   "PageSize",
3009 };
3010
3011 static char *
3012 get_option_text (ppd_file_t   *ppd_file, 
3013                  ppd_option_t *option)
3014 {
3015   int i;
3016   char *utf8;
3017   
3018   for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
3019     {
3020       if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
3021         return g_strdup (_(cups_option_translations[i].translation));
3022     }
3023
3024   utf8 = ppd_text_to_utf8 (ppd_file, option->text);
3025
3026   /* Some ppd files have spaces in the text before the colon */
3027   g_strchomp (utf8);
3028   
3029   return utf8;
3030 }
3031
3032 static char *
3033 get_choice_text (ppd_file_t   *ppd_file, 
3034                  ppd_choice_t *choice)
3035 {
3036   int i;
3037   ppd_option_t *option = choice->option;
3038   const char *keyword = option->keyword;
3039   
3040   for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
3041     {
3042       if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
3043           strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
3044         return g_strdup (_(cups_choice_translations[i].translation));
3045     }
3046   return ppd_text_to_utf8 (ppd_file, choice->text);
3047 }
3048
3049 static gboolean
3050 group_has_option (ppd_group_t  *group, 
3051                   ppd_option_t *option)
3052 {
3053   int i;
3054
3055   if (group == NULL)
3056     return FALSE;
3057   
3058   if (group->num_options > 0 &&
3059       option >= group->options && option < group->options + group->num_options)
3060     return TRUE;
3061   
3062   for (i = 0; i < group->num_subgroups; i++)
3063     {
3064       if (group_has_option (&group->subgroups[i],option))
3065         return TRUE;
3066     }
3067   return FALSE;
3068 }
3069
3070 static void
3071 set_option_off (GtkPrinterOption *option)
3072 {
3073   /* Any of these will do, _set only applies the value
3074    * if its allowed of the option */
3075   gtk_printer_option_set (option, "False");
3076   gtk_printer_option_set (option, "Off");
3077   gtk_printer_option_set (option, "None");
3078 }
3079
3080 static gboolean
3081 value_is_off (const char *value)
3082 {
3083   return  (strcasecmp (value, "None") == 0 ||
3084            strcasecmp (value, "Off") == 0 ||
3085            strcasecmp (value, "False") == 0);
3086 }
3087
3088 static char *
3089 ppd_group_name (ppd_group_t *group)
3090 {
3091 #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) 
3092   return group->name;
3093 #else
3094   return group->text;
3095 #endif
3096 }
3097
3098 static int
3099 available_choices (ppd_file_t     *ppd,
3100                    ppd_option_t   *option,
3101                    ppd_choice_t ***available,
3102                    gboolean        keep_if_only_one_option)
3103 {
3104   ppd_option_t *other_option;
3105   int i, j;
3106   gchar *conflicts;
3107   ppd_const_t *constraint;
3108   const char *choice, *other_choice;
3109   ppd_option_t *option1, *option2;
3110   ppd_group_t *installed_options;
3111   int num_conflicts;
3112   gboolean all_default;
3113   int add_auto;
3114
3115   if (available)
3116     *available = NULL;
3117
3118   conflicts = g_new0 (char, option->num_choices);
3119
3120   installed_options = NULL;
3121   for (i = 0; i < ppd->num_groups; i++)
3122     {
3123       char *name; 
3124
3125       name = ppd_group_name (&ppd->groups[i]);
3126       if (strcmp (name, "InstallableOptions") == 0)
3127         {
3128           installed_options = &ppd->groups[i];
3129           break;
3130         }
3131     }
3132
3133   for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
3134     {
3135       option1 = ppdFindOption (ppd, constraint->option1);
3136       if (option1 == NULL)
3137         continue;
3138
3139       option2 = ppdFindOption (ppd, constraint->option2);
3140       if (option2 == NULL)
3141         continue;
3142
3143       if (option == option1)
3144         {
3145           choice = constraint->choice1;
3146           other_option = option2;
3147           other_choice = constraint->choice2;
3148         }
3149       else if (option == option2)
3150         {
3151           choice = constraint->choice2;
3152           other_option = option1;
3153           other_choice = constraint->choice1;
3154         }
3155       else
3156         continue;
3157
3158       /* We only care of conflicts with installed_options and
3159          PageSize */
3160       if (!group_has_option (installed_options, other_option) &&
3161           (strcmp (other_option->keyword, "PageSize") != 0))
3162         continue;
3163
3164       if (*other_choice == 0)
3165         {
3166           /* Conflict only if the installed option is not off */
3167           if (value_is_off (other_option->defchoice))
3168             continue;
3169         }
3170       /* Conflict if the installed option has the specified default */
3171       else if (strcasecmp (other_choice, other_option->defchoice) != 0)
3172         continue;
3173
3174       if (*choice == 0)
3175         {
3176           /* Conflict with all non-off choices */
3177           for (j = 0; j < option->num_choices; j++)
3178             {
3179               if (!value_is_off (option->choices[j].choice))
3180                 conflicts[j] = 1;
3181             }
3182         }
3183       else
3184         {
3185           for (j = 0; j < option->num_choices; j++)
3186             {
3187               if (strcasecmp (option->choices[j].choice, choice) == 0)
3188                 conflicts[j] = 1;
3189             }
3190         }
3191     }
3192
3193   num_conflicts = 0;
3194   all_default = TRUE;
3195   for (j = 0; j < option->num_choices; j++)
3196     {
3197       if (conflicts[j])
3198         num_conflicts++;
3199       else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
3200         all_default = FALSE;
3201     }
3202
3203   if ((all_default && !keep_if_only_one_option) ||
3204       (num_conflicts == option->num_choices))
3205     {
3206       g_free (conflicts);
3207
3208       return 0;
3209     }
3210
3211   /* Some ppds don't have a "use printer default" option for
3212    * InputSlot. This means you always have to select a particular slot,
3213    * and you can't auto-pick source based on the paper size. To support
3214    * this we always add an auto option if there isn't one already. If
3215    * the user chooses the generated option we don't send any InputSlot
3216    * value when printing. The way we detect existing auto-cases is based
3217    * on feedback from Michael Sweet of cups fame.
3218    */
3219   add_auto = 0;
3220   if (strcmp (option->keyword, "InputSlot") == 0)
3221     {
3222       gboolean found_auto = FALSE;
3223       for (j = 0; j < option->num_choices; j++)
3224         {
3225           if (!conflicts[j])
3226             {
3227               if (strcmp (option->choices[j].choice, "Auto") == 0 ||
3228                   strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
3229                   strcmp (option->choices[j].choice, "Default") == 0 ||
3230                   strcmp (option->choices[j].choice, "None") == 0 ||
3231                   strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
3232                   strcmp (option->choices[j].choice, "Unspecified") == 0 ||
3233                   option->choices[j].code == NULL ||
3234                   option->choices[j].code[0] == 0)
3235                 {
3236                   found_auto = TRUE;
3237                   break;
3238                 }
3239             }
3240         }
3241
3242       if (!found_auto)
3243         add_auto = 1;
3244     }
3245   
3246   if (available)
3247     {
3248       *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
3249
3250       i = 0;
3251       for (j = 0; j < option->num_choices; j++)
3252         {
3253           if (!conflicts[j])
3254             (*available)[i++] = &option->choices[j];
3255         }
3256
3257       if (add_auto) 
3258         (*available)[i++] = NULL;
3259     }
3260
3261   g_free (conflicts);
3262   
3263   return option->num_choices - num_conflicts + add_auto;
3264 }
3265
3266 static GtkPrinterOption *
3267 create_pickone_option (ppd_file_t   *ppd_file,
3268                        ppd_option_t *ppd_option,
3269                        const gchar  *gtk_name)
3270 {
3271   GtkPrinterOption *option;
3272   ppd_choice_t **available;
3273   char *label;
3274   int n_choices;
3275   int i;
3276   ppd_coption_t *coption;
3277
3278   g_assert (ppd_option->ui == PPD_UI_PICKONE);
3279   
3280   option = NULL;
3281
3282   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3283   if (n_choices > 0)
3284     {
3285       
3286       /* right now only support one parameter per custom option 
3287        * if more than one print warning and only offer the default choices
3288        */
3289
3290       label = get_option_text (ppd_file, ppd_option);
3291
3292       coption = ppdFindCustomOption (ppd_file, ppd_option->keyword);
3293
3294       if (coption)
3295         {
3296           ppd_cparam_t *cparam;
3297
3298           cparam = ppdFirstCustomParam (coption);
3299
3300           if (ppdNextCustomParam (coption) == NULL)
3301             {
3302               switch (cparam->type)
3303                 {
3304                 case PPD_CUSTOM_INT:
3305                   option = gtk_printer_option_new (gtk_name, label,
3306                                          GTK_PRINTER_OPTION_TYPE_PICKONE_INT);
3307                   break;
3308                 case PPD_CUSTOM_PASSCODE:
3309                   option = gtk_printer_option_new (gtk_name, label,
3310                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE);
3311                   break;
3312                 case PPD_CUSTOM_PASSWORD:
3313                     option = gtk_printer_option_new (gtk_name, label,
3314                                          GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD);
3315                   break;
3316                case PPD_CUSTOM_REAL:
3317                     option = gtk_printer_option_new (gtk_name, label,
3318                                          GTK_PRINTER_OPTION_TYPE_PICKONE_REAL);
3319                   break;
3320                 case PPD_CUSTOM_STRING:
3321                   option = gtk_printer_option_new (gtk_name, label,
3322                                          GTK_PRINTER_OPTION_TYPE_PICKONE_STRING);
3323                   break;
3324 #ifdef PRINT_IGNORED_OPTIONS
3325                 case PPD_CUSTOM_POINTS: 
3326                   g_warning ("CUPS Backend: PPD Custom Points Option not supported");
3327                   break;
3328                 case PPD_CUSTOM_CURVE:
3329                   g_warning ("CUPS Backend: PPD Custom Curve Option not supported");
3330                   break;
3331                 case PPD_CUSTOM_INVCURVE:       
3332                   g_warning ("CUPS Backend: PPD Custom Inverse Curve Option not supported");
3333                   break;
3334 #endif
3335                 default: 
3336                   break;
3337                 }
3338             }
3339 #ifdef PRINT_IGNORED_OPTIONS
3340           else
3341             g_warning ("CUPS Backend: Multi-parameter PPD Custom Option not supported");
3342 #endif
3343         }
3344
3345       if (!option)
3346         option = gtk_printer_option_new (gtk_name, label,
3347                                          GTK_PRINTER_OPTION_TYPE_PICKONE);
3348       g_free (label);
3349       
3350       gtk_printer_option_allocate_choices (option, n_choices);
3351       for (i = 0; i < n_choices; i++)
3352         {
3353           if (available[i] == NULL)
3354             {
3355               /* This was auto-added */
3356               option->choices[i] = g_strdup ("gtk-ignore-value");
3357               option->choices_display[i] = g_strdup (_("Printer Default"));
3358             }
3359           else
3360             {
3361               option->choices[i] = g_strdup (available[i]->choice);
3362               option->choices_display[i] = get_choice_text (ppd_file, available[i]);
3363             }
3364         }
3365
3366       if (option->type != GTK_PRINTER_OPTION_TYPE_PICKONE)
3367         {
3368           if (g_str_has_prefix (ppd_option->defchoice, "Custom."))
3369             gtk_printer_option_set (option, ppd_option->defchoice + 7);
3370           else
3371             gtk_printer_option_set (option, ppd_option->defchoice);
3372         }
3373       else
3374         {
3375           gtk_printer_option_set (option, ppd_option->defchoice);
3376         }
3377     }
3378 #ifdef PRINT_IGNORED_OPTIONS
3379   else
3380     g_warning ("CUPS Backend: Ignoring pickone %s\n", ppd_option->text);
3381 #endif
3382   g_free (available);
3383
3384   return option;
3385 }
3386
3387 static GtkPrinterOption *
3388 create_boolean_option (ppd_file_t   *ppd_file,
3389                        ppd_option_t *ppd_option,
3390                        const gchar  *gtk_name)
3391 {
3392   GtkPrinterOption *option;
3393   ppd_choice_t **available;
3394   char *label;
3395   int n_choices;
3396
3397   g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
3398   
3399   option = NULL;
3400
3401   n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
3402   if (n_choices == 2)
3403     {
3404       label = get_option_text (ppd_file, ppd_option);
3405       option = gtk_printer_option_new (gtk_name, label,
3406                                        GTK_PRINTER_OPTION_TYPE_BOOLEAN);
3407       g_free (label);
3408       
3409       gtk_printer_option_allocate_choices (option, 2);
3410       option->choices[0] = g_strdup ("True");
3411       option->choices_display[0] = g_strdup ("True");
3412       option->choices[1] = g_strdup ("False");
3413       option->choices_display[1] = g_strdup ("False");
3414       
3415       gtk_printer_option_set (option, ppd_option->defchoice);
3416     }
3417 #ifdef PRINT_IGNORED_OPTIONS
3418   else
3419     g_warning ("CUPS Backend: Ignoring boolean %s\n", ppd_option->text);
3420 #endif
3421   g_free (available);
3422
3423   return option;
3424 }
3425
3426 static gchar *
3427 get_ppd_option_name (const gchar *keyword)
3428 {
3429   int i;
3430
3431   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3432     if (strcmp (ppd_option_names[i].ppd_keyword, keyword) == 0)
3433       return g_strdup (ppd_option_names[i].name);
3434
3435   return g_strdup_printf ("cups-%s", keyword);
3436 }
3437
3438 static gchar *
3439 get_lpoption_name (const gchar *lpoption)
3440 {
3441   int i;
3442
3443   for (i = 0; i < G_N_ELEMENTS (ppd_option_names); i++)
3444     if (strcmp (ppd_option_names[i].ppd_keyword, lpoption) == 0)
3445       return g_strdup (ppd_option_names[i].name);
3446
3447   for (i = 0; i < G_N_ELEMENTS (lpoption_names); i++)
3448     if (strcmp (lpoption_names[i].lpoption, lpoption) == 0)
3449       return g_strdup (lpoption_names[i].name);
3450
3451   return g_strdup_printf ("cups-%s", lpoption);
3452 }
3453
3454 static int
3455 strptr_cmp (const void *a, 
3456             const void *b)
3457 {
3458   char **aa = (char **)a;
3459   char **bb = (char **)b;
3460   return strcmp (*aa, *bb);
3461 }
3462
3463
3464 static gboolean
3465 string_in_table (gchar       *str, 
3466                  const gchar *table[], 
3467                  gint         table_len)
3468 {
3469   return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
3470 }
3471
3472 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
3473
3474 static void
3475 handle_option (GtkPrinterOptionSet *set,
3476                ppd_file_t          *ppd_file,
3477                ppd_option_t        *ppd_option,
3478                ppd_group_t         *toplevel_group,
3479                GtkPrintSettings    *settings)
3480 {
3481   GtkPrinterOption *option;
3482   char *name;
3483   int i;
3484
3485   if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
3486     return;
3487
3488   name = get_ppd_option_name (ppd_option->keyword);
3489
3490   option = NULL;
3491   if (ppd_option->ui == PPD_UI_PICKONE)
3492     {
3493       option = create_pickone_option (ppd_file, ppd_option, name);
3494     }
3495   else if (ppd_option->ui == PPD_UI_BOOLEAN)
3496     {
3497       option = create_boolean_option (ppd_file, ppd_option, name);
3498     }
3499 #ifdef PRINT_IGNORED_OPTIONS
3500   else
3501     g_warning ("CUPS Backend: Ignoring pickmany setting %s\n", ppd_option->text);
3502 #endif  
3503   
3504   if (option)
3505     {
3506       char *name;
3507
3508       name = ppd_group_name (toplevel_group);
3509       if (STRING_IN_TABLE (name,
3510                            color_group_whitelist) ||
3511           STRING_IN_TABLE (ppd_option->keyword,
3512                            color_option_whitelist))
3513         {
3514           option->group = g_strdup ("ColorPage");
3515         }
3516       else if (STRING_IN_TABLE (name,
3517                                 image_quality_group_whitelist) ||
3518                STRING_IN_TABLE (ppd_option->keyword,
3519                                 image_quality_option_whitelist))
3520         {
3521           option->group = g_strdup ("ImageQualityPage");
3522         }
3523       else if (STRING_IN_TABLE (name,
3524                                 finishing_group_whitelist) ||
3525                STRING_IN_TABLE (ppd_option->keyword,
3526                                 finishing_option_whitelist))
3527         {
3528           option->group = g_strdup ("FinishingPage");
3529         }
3530       else
3531         {
3532           for (i = 0; i < G_N_ELEMENTS (cups_group_translations); i++)
3533             {
3534               if (strcmp (cups_group_translations[i].name, toplevel_group->name) == 0)
3535                 {
3536                   option->group = g_strdup (_(cups_group_translations[i].translation));
3537                   break;
3538                 }
3539             }
3540
3541           if (i == G_N_ELEMENTS (cups_group_translations))
3542             option->group = g_strdup (toplevel_group->text);
3543         }
3544
3545       set_option_from_settings (option, settings);
3546       
3547       gtk_printer_option_set_add (set, option);
3548     }
3549   
3550   g_free (name);
3551 }
3552
3553 static void
3554 handle_group (GtkPrinterOptionSet *set,
3555               ppd_file_t          *ppd_file,
3556               ppd_group_t         *group,
3557               ppd_group_t         *toplevel_group,
3558               GtkPrintSettings    *settings)
3559 {
3560   gint i;
3561   gchar *name;
3562   
3563   /* Ignore installable options */
3564   name = ppd_group_name (toplevel_group);
3565   if (strcmp (name, "InstallableOptions") == 0)
3566     return;
3567   
3568   for (i = 0; i < group->num_options; i++)
3569     handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
3570
3571   for (i = 0; i < group->num_subgroups; i++)
3572     handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
3573
3574 }
3575
3576 #ifdef HAVE_COLORD
3577
3578 typedef struct {
3579         GtkPrintSettings     *settings;
3580         GtkPrinter           *printer;
3581 } GtkPrintBackendCupsColordHelper;
3582
3583 static void
3584 colord_printer_option_set_changed_cb (GtkPrinterOptionSet *set,
3585                                       GtkPrintBackendCupsColordHelper *helper)
3586 {
3587   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (helper->printer),
3588                                     helper->settings,
3589                                     set);
3590 }
3591 #endif
3592
3593 static GtkPrinterOptionSet *
3594 cups_printer_get_options (GtkPrinter           *printer,
3595                           GtkPrintSettings     *settings,
3596                           GtkPageSetup         *page_setup,
3597                           GtkPrintCapabilities  capabilities)
3598 {
3599   GtkPrinterOptionSet *set;
3600   GtkPrinterOption *option;
3601   ppd_file_t *ppd_file;
3602   int i;
3603   char *print_at[] = { "now", "at", "on-hold" };
3604   char *n_up[] = {"1", "2", "4", "6", "9", "16" };
3605   char *prio[] = {"100", "80", "50", "30" };
3606   /* Translators: These strings name the possible values of the 
3607    * job priority option in the print dialog
3608    */
3609   char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
3610   char *n_up_layout[] = { "lrtb", "lrbt", "rltb", "rlbt", "tblr", "tbrl", "btlr", "btrl" };
3611   /* Translators: These strings name the possible arrangements of
3612    * multiple pages on a sheet when printing
3613    */
3614   char *n_up_layout_display[] = { N_("Left to right, top to bottom"), N_("Left to right, bottom to top"), 
3615                                   N_("Right to left, top to bottom"), N_("Right to left, bottom to top"), 
3616                                   N_("Top to bottom, left to right"), N_("Top to bottom, right to left"), 
3617                                   N_("Bottom to top, left to right"), N_("Bottom to top, right to left") };
3618   char *name;
3619   int num_opts;
3620   cups_option_t *opts = NULL;
3621   GtkPrintBackendCups *backend;
3622   GtkTextDirection text_direction;
3623   GtkPrinterCups *cups_printer = NULL;
3624 #ifdef HAVE_COLORD
3625   GtkPrintBackendCupsColordHelper *helper;
3626 #endif
3627   char *default_number_up;
3628
3629   set = gtk_printer_option_set_new ();
3630
3631   /* Cups specific, non-ppd related settings */
3632
3633   for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
3634     prio_display[i] = _(prio_display[i]);
3635   
3636   /* Translators, this string is used to label the job priority option 
3637    * in the print dialog 
3638    */
3639   option = gtk_printer_option_new ("gtk-job-prio", _("Job Priority"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3640   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
3641                                          prio, prio_display);
3642   gtk_printer_option_set (option, "50");
3643   set_option_from_settings (option, settings);
3644   gtk_printer_option_set_add (set, option);
3645   g_object_unref (option);
3646
3647   /* Translators, this string is used to label the billing info entry
3648    * in the print dialog 
3649    */
3650   option = gtk_printer_option_new ("gtk-billing-info", _("Billing Info"), GTK_PRINTER_OPTION_TYPE_STRING);
3651   gtk_printer_option_set (option, "");
3652   set_option_from_settings (option, settings);
3653   gtk_printer_option_set_add (set, option);
3654   g_object_unref (option);
3655
3656   backend = GTK_PRINT_BACKEND_CUPS (gtk_printer_get_backend (printer));
3657   cups_printer = GTK_PRINTER_CUPS (printer);
3658
3659   if (backend != NULL && printer != NULL)
3660     {
3661       char *cover_default[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
3662       /* Translators, these strings are names for various 'standard' cover 
3663        * pages that the printing system may support.
3664        */
3665       char *cover_display_default[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
3666       char **cover = NULL;
3667       char **cover_display = NULL;
3668       char **cover_display_translated = NULL;
3669       gint num_of_covers = 0;
3670       gpointer value;
3671       gint j;
3672
3673        /* Translators, this string is used to label the pages-per-sheet option 
3674         * in the print dialog 
3675         */
3676       option = gtk_printer_option_new ("gtk-n-up", _("Pages per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3677       gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
3678                                              n_up, n_up);
3679       default_number_up = g_strdup_printf ("%d", cups_printer->default_number_up);
3680       gtk_printer_option_set (option, default_number_up);
3681       g_free (default_number_up);
3682       set_option_from_settings (option, settings);
3683       gtk_printer_option_set_add (set, option);
3684       g_object_unref (option);
3685
3686       if (cups_printer_get_capabilities (printer) & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT)
3687         {
3688           for (i = 0; i < G_N_ELEMENTS (n_up_layout_display); i++)
3689             n_up_layout_display[i] = _(n_up_layout_display[i]);
3690
3691            /* Translators, this string is used to label the option in the print 
3692             * dialog that controls in what order multiple pages are arranged 
3693             */
3694           option = gtk_printer_option_new ("gtk-n-up-layout", _("Page Ordering"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3695           gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up_layout),
3696                                                  n_up_layout, n_up_layout_display);
3697
3698           text_direction = gtk_widget_get_default_direction ();
3699           if (text_direction == GTK_TEXT_DIR_LTR)
3700             gtk_printer_option_set (option, "lrtb");
3701           else
3702             gtk_printer_option_set (option, "rltb");
3703
3704           set_option_from_settings (option, settings);
3705           gtk_printer_option_set_add (set, option);
3706           g_object_unref (option);
3707         }
3708
3709       num_of_covers = backend->number_of_covers;
3710       cover = g_new (char *, num_of_covers + 1);
3711       cover[num_of_covers] = NULL;
3712       cover_display = g_new (char *, num_of_covers + 1);
3713       cover_display[num_of_covers] = NULL;
3714       cover_display_translated = g_new (char *, num_of_covers + 1);
3715       cover_display_translated[num_of_covers] = NULL;
3716
3717       for (i = 0; i < num_of_covers; i++)
3718         {
3719           cover[i] = g_strdup (backend->covers[i]);
3720           value = NULL;
3721           for (j = 0; j < G_N_ELEMENTS (cover_default); j++)
3722             if (strcmp (cover_default[j], cover[i]) == 0)
3723               {
3724                 value = cover_display_default[j];
3725                 break;
3726               }
3727           cover_display[i] = (value != NULL) ? g_strdup (value) : g_strdup (backend->covers[i]);
3728         }
3729
3730       for (i = 0; i < num_of_covers; i++)
3731         cover_display_translated[i] = _(cover_display[i]);
3732   
3733       /* Translators, this is the label used for the option in the print 
3734        * dialog that controls the front cover page.
3735        */
3736       option = gtk_printer_option_new ("gtk-cover-before", _("Before"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3737       gtk_printer_option_choices_from_array (option, num_of_covers,
3738                                          cover, cover_display_translated);
3739
3740       if (cups_printer->default_cover_before != NULL)
3741         gtk_printer_option_set (option, cups_printer->default_cover_before);
3742       else
3743         gtk_printer_option_set (option, "none");
3744       set_option_from_settings (option, settings);
3745       gtk_printer_option_set_add (set, option);
3746       g_object_unref (option);
3747
3748       /* Translators, this is the label used for the option in the print 
3749        * dialog that controls the back cover page.
3750        */
3751       option = gtk_printer_option_new ("gtk-cover-after", _("After"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3752       gtk_printer_option_choices_from_array (option, num_of_covers,
3753                                          cover, cover_display_translated);
3754       if (cups_printer->default_cover_after != NULL)
3755         gtk_printer_option_set (option, cups_printer->default_cover_after);
3756       else
3757         gtk_printer_option_set (option, "none");
3758       set_option_from_settings (option, settings);
3759       gtk_printer_option_set_add (set, option);
3760       g_object_unref (option);
3761
3762       g_strfreev (cover);
3763       g_strfreev (cover_display);
3764       g_free (cover_display_translated);
3765     }
3766
3767   /* Translators: this is the name of the option that controls when
3768    * a print job is printed. Possible values are 'now', a specified time,
3769    * or 'on hold'
3770    */
3771   option = gtk_printer_option_new ("gtk-print-time", _("Print at"), GTK_PRINTER_OPTION_TYPE_PICKONE);
3772   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
3773                                          print_at, print_at);
3774   gtk_printer_option_set (option, "now");
3775   set_option_from_settings (option, settings);
3776   gtk_printer_option_set_add (set, option);
3777   g_object_unref (option);
3778   
3779   /* Translators: this is the name of the option that allows the user
3780    * to specify a time when a print job will be printed.
3781    */
3782   option = gtk_printer_option_new ("gtk-print-time-text", _("Print at time"), GTK_PRINTER_OPTION_TYPE_STRING);
3783   gtk_printer_option_set (option, "");
3784   set_option_from_settings (option, settings);
3785   gtk_printer_option_set_add (set, option);
3786   g_object_unref (option);
3787   
3788   /* Printer (ppd) specific settings */
3789   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
3790   if (ppd_file)
3791     {
3792       GtkPaperSize *paper_size;
3793       ppd_option_t *option;
3794       const gchar  *ppd_name;
3795
3796       ppdMarkDefaults (ppd_file);
3797
3798       paper_size = gtk_page_setup_get_paper_size (page_setup);
3799
3800       option = ppdFindOption (ppd_file, "PageSize");
3801       ppd_name = gtk_paper_size_get_ppd_name (paper_size);
3802       
3803       if (ppd_name)
3804         strncpy (option->defchoice, ppd_name, PPD_MAX_NAME);
3805       else
3806         {
3807           gchar *custom_name;
3808           char width[G_ASCII_DTOSTR_BUF_SIZE];
3809           char height[G_ASCII_DTOSTR_BUF_SIZE];
3810
3811           g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
3812           g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
3813           /* Translators: this format is used to display a custom paper
3814            * size. The two placeholders are replaced with the width and height
3815            * in points. E.g: "Custom 230.4x142.9"
3816            */
3817           custom_name = g_strdup_printf (_("Custom %sx%s"), width, height);
3818           strncpy (option->defchoice, custom_name, PPD_MAX_NAME);
3819           g_free (custom_name);
3820         }
3821
3822       for (i = 0; i < ppd_file->num_groups; i++)
3823         handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
3824     }
3825
3826   /* Now honor the user set defaults for this printer */
3827   num_opts = cups_get_user_options (gtk_printer_get_name (printer), 0, &opts);
3828
3829   for (i = 0; i < num_opts; i++)
3830     {
3831       if (STRING_IN_TABLE (opts[i].name, cups_option_blacklist))
3832         continue;
3833
3834       name = get_lpoption_name (opts[i].name);
3835       if (strcmp (name, "cups-job-sheets") == 0)
3836         {
3837           gchar **values;
3838           gint    num_values;
3839           
3840           values = g_strsplit (opts[i].value, ",", 2);
3841           num_values = g_strv_length (values);
3842
3843           option = gtk_printer_option_set_lookup (set, "gtk-cover-before");
3844           if (option && num_values > 0)
3845             gtk_printer_option_set (option, g_strstrip (values[0]));
3846
3847           option = gtk_printer_option_set_lookup (set, "gtk-cover-after");
3848           if (option && num_values > 1)
3849             gtk_printer_option_set (option, g_strstrip (values[1]));
3850
3851           g_strfreev (values);
3852         }
3853       else if (strcmp (name, "cups-job-hold-until") == 0)
3854         {
3855           GtkPrinterOption *option2 = NULL;
3856
3857           option = gtk_printer_option_set_lookup (set, "gtk-print-time-text");
3858           if (option && opts[i].value)
3859             {
3860               option2 = gtk_printer_option_set_lookup (set, "gtk-print-time");
3861               if (option2)
3862                 {
3863                   if (strcmp (opts[i].value, "indefinite") == 0)
3864                     gtk_printer_option_set (option2, "on-hold");
3865                   else
3866                     {
3867                       gtk_printer_option_set (option2, "at");
3868                       gtk_printer_option_set (option, opts[i].value);
3869                     }
3870                 }
3871             }
3872         }
3873       else if (strcmp (name, "cups-sides") == 0)
3874         {
3875           option = gtk_printer_option_set_lookup (set, "gtk-duplex");
3876           if (option && opts[i].value)
3877             {
3878               if (strcmp (opts[i].value, "two-sided-short-edge") == 0)
3879                 gtk_printer_option_set (option, "DuplexTumble");
3880               else if (strcmp (opts[i].value, "two-sided-long-edge") == 0)
3881                 gtk_printer_option_set (option, "DuplexNoTumble");
3882             }
3883         }
3884       else
3885         {
3886           option = gtk_printer_option_set_lookup (set, name);
3887           if (option)
3888             gtk_printer_option_set (option, opts[i].value);
3889         }
3890       g_free (name);
3891     }
3892
3893   cupsFreeOptions (num_opts, opts);
3894
3895 #ifdef HAVE_COLORD
3896   /* TRANSLATORS: this this the ICC color profile to use for this job */
3897   option = gtk_printer_option_new ("colord-profile",
3898                                    _("Printer Profile"),
3899                                    GTK_PRINTER_OPTION_TYPE_INFO);
3900
3901   /* assign it to the color page */
3902   option->group = g_strdup ("ColorPage");
3903
3904   /* TRANSLATORS: this is when color profile information is unavailable */
3905   gtk_printer_option_set (option, _("Unavailable"));
3906   gtk_printer_option_set_add (set, option);
3907
3908   /* watch to see if the user changed the options */
3909   helper = g_new (GtkPrintBackendCupsColordHelper, 1);
3910   helper->printer = printer;
3911   helper->settings = settings;
3912   g_signal_connect_data (set, "changed",
3913                          G_CALLBACK (colord_printer_option_set_changed_cb),
3914                          helper,
3915                          (GClosureNotify) g_free,
3916                          0);
3917
3918   /* initial coldplug */
3919   gtk_printer_cups_update_settings (GTK_PRINTER_CUPS (printer),
3920                                     settings, set);
3921   g_object_bind_property (printer, "profile-title",
3922                           option, "value",
3923                           G_BINDING_DEFAULT);
3924
3925 #endif
3926
3927   return set;
3928 }
3929
3930
3931 static void
3932 mark_option_from_set (GtkPrinterOptionSet *set,
3933                       ppd_file_t          *ppd_file,
3934                       ppd_option_t        *ppd_option)
3935 {
3936   GtkPrinterOption *option;
3937   char *name = get_ppd_option_name (ppd_option->keyword);
3938
3939   option = gtk_printer_option_set_lookup (set, name);
3940
3941   if (option)
3942     ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
3943   
3944   g_free (name);
3945 }
3946
3947
3948 static void
3949 mark_group_from_set (GtkPrinterOptionSet *set,
3950                      ppd_file_t          *ppd_file,
3951                      ppd_group_t         *group)
3952 {
3953   int i;
3954
3955   for (i = 0; i < group->num_options; i++)
3956     mark_option_from_set (set, ppd_file, &group->options[i]);
3957
3958   for (i = 0; i < group->num_subgroups; i++)
3959     mark_group_from_set (set, ppd_file, &group->subgroups[i]);
3960 }
3961
3962 static void
3963 set_conflicts_from_option (GtkPrinterOptionSet *set,
3964                            ppd_file_t          *ppd_file,
3965                            ppd_option_t        *ppd_option)
3966 {
3967   GtkPrinterOption *option;
3968   char *name;
3969
3970   if (ppd_option->conflicted)
3971     {
3972       name = get_ppd_option_name (ppd_option->keyword);
3973       option = gtk_printer_option_set_lookup (set, name);
3974
3975       if (option)
3976         gtk_printer_option_set_has_conflict (option, TRUE);
3977 #ifdef PRINT_IGNORED_OPTIONS
3978       else
3979         g_warning ("CUPS Backend: Ignoring conflict for option %s", ppd_option->keyword);
3980 #endif
3981       
3982       g_free (name);
3983     }
3984 }
3985
3986 static void
3987 set_conflicts_from_group (GtkPrinterOptionSet *set,
3988                           ppd_file_t          *ppd_file,
3989                           ppd_group_t         *group)
3990 {
3991   int i;
3992
3993   for (i = 0; i < group->num_options; i++)
3994     set_conflicts_from_option (set, ppd_file, &group->options[i]);
3995
3996   for (i = 0; i < group->num_subgroups; i++)
3997     set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
3998 }
3999
4000 static gboolean
4001 cups_printer_mark_conflicts (GtkPrinter          *printer,
4002                              GtkPrinterOptionSet *options)
4003 {
4004   ppd_file_t *ppd_file;
4005   int num_conflicts;
4006   int i;
4007  
4008   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4009
4010   if (ppd_file == NULL)
4011     return FALSE;
4012
4013   ppdMarkDefaults (ppd_file);
4014
4015   for (i = 0; i < ppd_file->num_groups; i++)
4016     mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
4017
4018   num_conflicts = ppdConflicts (ppd_file);
4019
4020   if (num_conflicts > 0)
4021     {
4022       for (i = 0; i < ppd_file->num_groups; i++)
4023         set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
4024     }
4025  
4026   return num_conflicts > 0;
4027 }
4028
4029 struct OptionData {
4030   GtkPrinter *printer;
4031   GtkPrinterOptionSet *options;
4032   GtkPrintSettings *settings;
4033   ppd_file_t *ppd_file;
4034 };
4035
4036 typedef struct {
4037   const char *cups;
4038   const char *standard;
4039 } NameMapping;
4040
4041 static void
4042 map_settings_to_option (GtkPrinterOption  *option,
4043                         const NameMapping  table[],
4044                         gint               n_elements,
4045                         GtkPrintSettings  *settings,
4046                         const gchar       *standard_name,
4047                         const gchar       *cups_name)
4048 {
4049   int i;
4050   char *name;
4051   const char *cups_value;
4052   const char *standard_value;
4053
4054   /* If the cups-specific setting is set, always use that */
4055   name = g_strdup_printf ("cups-%s", cups_name);
4056   cups_value = gtk_print_settings_get (settings, name);
4057   g_free (name);
4058   
4059   if (cups_value != NULL) 
4060     {
4061       gtk_printer_option_set (option, cups_value);
4062       return;
4063     }
4064
4065   /* Otherwise we try to convert from the general setting */
4066   standard_value = gtk_print_settings_get (settings, standard_name);
4067   if (standard_value == NULL)
4068     return;
4069
4070   for (i = 0; i < n_elements; i++)
4071     {
4072       if (table[i].cups == NULL && table[i].standard == NULL)
4073         {
4074           gtk_printer_option_set (option, standard_value);
4075           break;
4076         }
4077       else if (table[i].cups == NULL &&
4078                strcmp (table[i].standard, standard_value) == 0)
4079         {
4080           set_option_off (option);
4081           break;
4082         }
4083       else if (strcmp (table[i].standard, standard_value) == 0)
4084         {
4085           gtk_printer_option_set (option, table[i].cups);
4086           break;
4087         }
4088     }
4089 }
4090
4091 static void
4092 map_option_to_settings (const gchar       *value,
4093                         const NameMapping  table[],
4094                         gint               n_elements,
4095                         GtkPrintSettings  *settings,
4096                         const gchar       *standard_name,
4097                         const gchar       *cups_name)
4098 {
4099   int i;
4100   char *name;
4101
4102   for (i = 0; i < n_elements; i++)
4103     {
4104       if (table[i].cups == NULL && table[i].standard == NULL)
4105         {
4106           gtk_print_settings_set (settings,
4107                                   standard_name,
4108                                   value);
4109           break;
4110         }
4111       else if (table[i].cups == NULL && table[i].standard != NULL)
4112         {
4113           if (value_is_off (value))
4114             {
4115               gtk_print_settings_set (settings,
4116                                       standard_name,
4117                                       table[i].standard);
4118               break;
4119             }
4120         }
4121       else if (strcmp (table[i].cups, value) == 0)
4122         {
4123           gtk_print_settings_set (settings,
4124                                   standard_name,
4125                                   table[i].standard);
4126           break;
4127         }
4128     }
4129
4130   /* Always set the corresponding cups-specific setting */
4131   name = g_strdup_printf ("cups-%s", cups_name);
4132   gtk_print_settings_set (settings, name, value);
4133   g_free (name);
4134 }
4135
4136
4137 static const NameMapping paper_source_map[] = {
4138   { "Lower", "lower"},
4139   { "Middle", "middle"},
4140   { "Upper", "upper"},
4141   { "Rear", "rear"},
4142   { "Envelope", "envelope"},
4143   { "Cassette", "cassette"},
4144   { "LargeCapacity", "large-capacity"},
4145   { "AnySmallFormat", "small-format"},
4146   { "AnyLargeFormat", "large-format"},
4147   { NULL, NULL}
4148 };
4149
4150 static const NameMapping output_tray_map[] = {
4151   { "Upper", "upper"},
4152   { "Lower", "lower"},
4153   { "Rear", "rear"},
4154   { NULL, NULL}
4155 };
4156
4157 static const NameMapping duplex_map[] = {
4158   { "DuplexTumble", "vertical" },
4159   { "DuplexNoTumble", "horizontal" },
4160   { NULL, "simplex" }
4161 };
4162
4163 static const NameMapping output_mode_map[] = {
4164   { "Standard", "normal" },
4165   { "Normal", "normal" },
4166   { "Draft", "draft" },
4167   { "Fast", "draft" },
4168 };
4169
4170 static const NameMapping media_type_map[] = {
4171   { "Transparency", "transparency"},
4172   { "Standard", "stationery"},
4173   { NULL, NULL}
4174 };
4175
4176 static const NameMapping all_map[] = {
4177   { NULL, NULL}
4178 };
4179
4180
4181 static void
4182 set_option_from_settings (GtkPrinterOption *option,
4183                           GtkPrintSettings *settings)
4184 {
4185   const char *cups_value;
4186   char *value;
4187   
4188   if (settings == NULL)
4189     return;
4190
4191   if (strcmp (option->name, "gtk-paper-source") == 0)
4192     map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
4193                              settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4194   else if (strcmp (option->name, "gtk-output-tray") == 0)
4195     map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
4196                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4197   else if (strcmp (option->name, "gtk-duplex") == 0)
4198     map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
4199                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4200   else if (strcmp (option->name, "cups-OutputMode") == 0)
4201     map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
4202                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4203   else if (strcmp (option->name, "cups-Resolution") == 0)
4204     {
4205       cups_value = gtk_print_settings_get (settings, option->name);
4206       if (cups_value)
4207         gtk_printer_option_set (option, cups_value);
4208       else
4209         {
4210           if (gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, -1) != -1 ||
4211               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, -1) != -1 ||
4212               gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, -1) != -1 ||
4213               option->value == NULL || option->value[0] == '\0')
4214             {
4215               int res = gtk_print_settings_get_resolution (settings);
4216               int res_x = gtk_print_settings_get_resolution_x (settings);
4217               int res_y = gtk_print_settings_get_resolution_y (settings);
4218
4219               if (res_x != res_y)
4220                 {
4221                   value = g_strdup_printf ("%dx%ddpi", res_x, res_y);
4222                   gtk_printer_option_set (option, value);
4223                   g_free (value);
4224                 }
4225               else if (res != 0)
4226                 {
4227                   value = g_strdup_printf ("%ddpi", res);
4228                   gtk_printer_option_set (option, value);
4229                   g_free (value);
4230                 }
4231             }
4232         }
4233     }
4234   else if (strcmp (option->name, "gtk-paper-type") == 0)
4235     map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
4236                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4237   else if (strcmp (option->name, "gtk-n-up") == 0)
4238     {
4239       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4240                               settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4241     }
4242   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4243     {
4244       map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
4245                               settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4246     }
4247   else if (strcmp (option->name, "gtk-billing-info") == 0)
4248     {
4249       cups_value = gtk_print_settings_get (settings, "cups-job-billing");
4250       if (cups_value)
4251         gtk_printer_option_set (option, cups_value);
4252     } 
4253   else if (strcmp (option->name, "gtk-job-prio") == 0)
4254     {
4255       cups_value = gtk_print_settings_get (settings, "cups-job-priority");
4256       if (cups_value)
4257         gtk_printer_option_set (option, cups_value);
4258     } 
4259   else if (strcmp (option->name, "gtk-cover-before") == 0)
4260     {
4261       cups_value = gtk_print_settings_get (settings, "cover-before");
4262       if (cups_value)
4263         gtk_printer_option_set (option, cups_value);
4264     } 
4265   else if (strcmp (option->name, "gtk-cover-after") == 0)
4266     {
4267       cups_value = gtk_print_settings_get (settings, "cover-after");
4268       if (cups_value)
4269         gtk_printer_option_set (option, cups_value);
4270     } 
4271   else if (strcmp (option->name, "gtk-print-time") == 0)
4272     {
4273       cups_value = gtk_print_settings_get (settings, "print-at");
4274       if (cups_value)
4275         gtk_printer_option_set (option, cups_value);
4276     } 
4277   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4278     {
4279       cups_value = gtk_print_settings_get (settings, "print-at-time");
4280       if (cups_value)
4281         gtk_printer_option_set (option, cups_value);
4282     } 
4283   else if (g_str_has_prefix (option->name, "cups-"))
4284     {
4285       cups_value = gtk_print_settings_get (settings, option->name);
4286       if (cups_value)
4287         gtk_printer_option_set (option, cups_value);
4288     } 
4289 }
4290
4291 static void
4292 foreach_option_get_settings (GtkPrinterOption *option,
4293                              gpointer          user_data)
4294 {
4295   struct OptionData *data = user_data;
4296   GtkPrintSettings *settings = data->settings;
4297   const char *value;
4298
4299   value = option->value;
4300
4301   if (strcmp (option->name, "gtk-paper-source") == 0)
4302     map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
4303                             settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
4304   else if (strcmp (option->name, "gtk-output-tray") == 0)
4305     map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
4306                             settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
4307   else if (strcmp (option->name, "gtk-duplex") == 0)
4308     map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
4309                             settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
4310   else if (strcmp (option->name, "cups-OutputMode") == 0)
4311     map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
4312                             settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
4313   else if (strcmp (option->name, "cups-Resolution") == 0)
4314     {
4315       int res, res_x, res_y;
4316
4317       if (sscanf (value, "%dx%ddpi", &res_x, &res_y) == 2)
4318         {
4319           if (res_x > 0 && res_y > 0)
4320             gtk_print_settings_set_resolution_xy (settings, res_x, res_y);
4321         }
4322       else if (sscanf (value, "%ddpi", &res) == 1)
4323         {
4324           if (res > 0)
4325             gtk_print_settings_set_resolution (settings, res);
4326         }
4327
4328       gtk_print_settings_set (settings, option->name, value);
4329     }
4330   else if (strcmp (option->name, "gtk-paper-type") == 0)
4331     map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
4332                             settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
4333   else if (strcmp (option->name, "gtk-n-up") == 0)
4334     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4335                             settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
4336   else if (strcmp (option->name, "gtk-n-up-layout") == 0)
4337     map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
4338                             settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, "number-up-layout");
4339   else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
4340     gtk_print_settings_set (settings, "cups-job-billing", value);
4341   else if (strcmp (option->name, "gtk-job-prio") == 0)
4342     gtk_print_settings_set (settings, "cups-job-priority", value);
4343   else if (strcmp (option->name, "gtk-cover-before") == 0)
4344     gtk_print_settings_set (settings, "cover-before", value);
4345   else if (strcmp (option->name, "gtk-cover-after") == 0)
4346     gtk_print_settings_set (settings, "cover-after", value);
4347   else if (strcmp (option->name, "gtk-print-time") == 0)
4348     gtk_print_settings_set (settings, "print-at", value);
4349   else if (strcmp (option->name, "gtk-print-time-text") == 0)
4350     gtk_print_settings_set (settings, "print-at-time", value);
4351   else if (g_str_has_prefix (option->name, "cups-"))
4352     gtk_print_settings_set (settings, option->name, value);
4353 }
4354
4355 static gboolean
4356 supports_am_pm (void)
4357 {
4358   struct tm tmp_tm = { 0 };
4359   char   time[8];
4360   int    length;
4361
4362   length = strftime (time, sizeof (time), "%p", &tmp_tm);
4363
4364   return length != 0;
4365 }
4366
4367 /* Converts local time to UTC time. Local time has to be in one of these
4368  * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
4369  * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
4370  * Returns a newly allocated string holding UTC time in HH:MM:SS format
4371  * or NULL.
4372  */
4373 gchar *
4374 localtime_to_utctime (const char *local_time)
4375 {
4376   const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
4377                              " %H : %M : %S ",
4378                              " %I : %M %p ", " %p %I : %M ",
4379                              " %H : %M ",
4380                              " %I %p ", " %p %I "};
4381   const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
4382   const char *end = NULL;
4383   struct tm  *actual_local_time;
4384   struct tm  *actual_utc_time;
4385   struct tm   local_print_time;
4386   struct tm   utc_print_time;
4387   struct tm   diff_time;
4388   gchar      *utc_time = NULL;
4389   int         i, n;
4390
4391   if (local_time == NULL || local_time[0] == '\0')
4392     return NULL;
4393
4394   n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
4395
4396   for (i = 0; i < n; i++)
4397     {
4398       local_print_time.tm_hour = 0;
4399       local_print_time.tm_min  = 0;
4400       local_print_time.tm_sec  = 0;
4401
4402       if (supports_am_pm ())
4403         end = strptime (local_time, formats_0[i], &local_print_time);
4404       else
4405         end = strptime (local_time, formats_1[i], &local_print_time);
4406
4407       if (end != NULL && end[0] == '\0')
4408         break;
4409     }
4410
4411   if (end != NULL && end[0] == '\0')
4412     {
4413       time_t rawtime;
4414       time (&rawtime);
4415
4416       actual_utc_time = g_memdup (gmtime (&rawtime), sizeof (struct tm));
4417       actual_local_time = g_memdup (localtime (&rawtime), sizeof (struct tm));
4418
4419       diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
4420       diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
4421       diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
4422
4423       utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
4424       utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
4425       utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
4426
4427       utc_time = g_strdup_printf ("%02d:%02d:%02d",
4428                                   utc_print_time.tm_hour,
4429                                   utc_print_time.tm_min,
4430                                   utc_print_time.tm_sec);
4431     }
4432
4433   return utc_time;
4434 }
4435
4436 static void
4437 cups_printer_get_settings_from_options (GtkPrinter          *printer,
4438                                         GtkPrinterOptionSet *options,
4439                                         GtkPrintSettings    *settings)
4440 {
4441   struct OptionData data;
4442   const char *print_at, *print_at_time;
4443
4444   data.printer = printer;
4445   data.options = options;
4446   data.settings = settings;
4447   data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4448  
4449   if (data.ppd_file != NULL)
4450     {
4451       GtkPrinterOption *cover_before, *cover_after;
4452       
4453       gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
4454
4455       cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
4456       cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
4457       if (cover_before && cover_after)
4458         {
4459           char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
4460           gtk_print_settings_set (settings, "cups-job-sheets", value);
4461           g_free (value);
4462         }
4463
4464       print_at = gtk_print_settings_get (settings, "print-at");
4465       print_at_time = gtk_print_settings_get (settings, "print-at-time");
4466
4467       if (strcmp (print_at, "at") == 0)
4468         {
4469           gchar *utc_time = NULL;
4470           
4471           utc_time = localtime_to_utctime (print_at_time);
4472
4473           if (utc_time != NULL)
4474             {
4475               gtk_print_settings_set (settings, "cups-job-hold-until", utc_time);
4476               g_free (utc_time);
4477             }
4478           else
4479             gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
4480         }
4481       else if (strcmp (print_at, "on-hold") == 0)
4482         gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
4483     }
4484 }
4485
4486 static void
4487 cups_printer_prepare_for_print (GtkPrinter       *printer,
4488                                 GtkPrintJob      *print_job,
4489                                 GtkPrintSettings *settings,
4490                                 GtkPageSetup     *page_setup)
4491 {
4492   GtkPrintPages pages;
4493   GtkPageRange *ranges;
4494   gint n_ranges;
4495   GtkPageSet page_set;
4496   GtkPaperSize *paper_size;
4497   const char *ppd_paper_name;
4498   double scale;
4499
4500   pages = gtk_print_settings_get_print_pages (settings);
4501   gtk_print_job_set_pages (print_job, pages);
4502
4503   if (pages == GTK_PRINT_PAGES_RANGES)
4504     ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
4505   else
4506     {
4507       ranges = NULL;
4508       n_ranges = 0;
4509     }
4510
4511   gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
4512   if (gtk_print_settings_get_collate (settings))
4513     gtk_print_settings_set (settings, "cups-Collate", "True");
4514   gtk_print_job_set_collate (print_job, FALSE);
4515
4516   if (gtk_print_settings_get_reverse (settings))
4517     gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
4518   gtk_print_job_set_reverse (print_job, FALSE);
4519
4520   if (gtk_print_settings_get_n_copies (settings) > 1)
4521     gtk_print_settings_set_int (settings, "cups-copies",
4522                                 gtk_print_settings_get_n_copies (settings));
4523   gtk_print_job_set_num_copies (print_job, 1);
4524
4525   scale = gtk_print_settings_get_scale (settings);
4526   if (scale != 100.0)
4527     gtk_print_job_set_scale (print_job, scale / 100.0);
4528
4529   page_set = gtk_print_settings_get_page_set (settings);
4530   if (page_set == GTK_PAGE_SET_EVEN)
4531     gtk_print_settings_set (settings, "cups-page-set", "even");
4532   else if (page_set == GTK_PAGE_SET_ODD)
4533     gtk_print_settings_set (settings, "cups-page-set", "odd");
4534   gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
4535
4536   paper_size = gtk_page_setup_get_paper_size (page_setup);
4537   ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
4538   if (ppd_paper_name != NULL)
4539     gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
4540   else
4541     {
4542       char width[G_ASCII_DTOSTR_BUF_SIZE];
4543       char height[G_ASCII_DTOSTR_BUF_SIZE];
4544       char *custom_name;
4545
4546       g_ascii_formatd (width, sizeof (width), "%.2f", gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS));
4547       g_ascii_formatd (height, sizeof (height), "%.2f", gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
4548       custom_name = g_strdup_printf (("Custom.%sx%s"), width, height);
4549       gtk_print_settings_set (settings, "cups-PageSize", custom_name);
4550       g_free (custom_name);
4551     }
4552
4553   if (gtk_print_settings_get_number_up (settings) > 1)
4554     {
4555       GtkNumberUpLayout  layout = gtk_print_settings_get_number_up_layout (settings);
4556       GEnumClass        *enum_class;
4557       GEnumValue        *enum_value;
4558
4559       switch (gtk_page_setup_get_orientation (page_setup))
4560         {
4561           case GTK_PAGE_ORIENTATION_PORTRAIT:
4562             break;
4563           case GTK_PAGE_ORIENTATION_LANDSCAPE:
4564             if (layout < 4)
4565               layout = layout + 2 + 4 * (1 - layout / 2);
4566             else
4567               layout = layout - 3 - 2 * (layout % 2);
4568             break;
4569           case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4570             layout = (layout + 3 - 2 * (layout % 2)) % 4 + 4 * (layout / 4);
4571             break;
4572           case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4573             if (layout < 4)
4574               layout = layout + 5 - 2 * (layout % 2);
4575             else
4576               layout = layout - 6 + 4 * (1 - (layout - 4) / 2);
4577             break;
4578         }
4579
4580       enum_class = g_type_class_ref (GTK_TYPE_NUMBER_UP_LAYOUT);
4581       enum_value = g_enum_get_value (enum_class, layout);
4582       gtk_print_settings_set (settings, "cups-number-up-layout", enum_value->value_nick);
4583       g_type_class_unref (enum_class);
4584     }
4585
4586   gtk_print_job_set_rotate (print_job, TRUE);
4587 }
4588
4589 static GtkPageSetup *
4590 create_page_setup (ppd_file_t *ppd_file,
4591                    ppd_size_t *size)
4592  {
4593    char *display_name;
4594    GtkPageSetup *page_setup;
4595    GtkPaperSize *paper_size;
4596    ppd_option_t *option;
4597    ppd_choice_t *choice;
4598
4599   display_name = NULL;
4600   option = ppdFindOption (ppd_file, "PageSize");
4601   if (option)
4602     {
4603       choice = ppdFindChoice (option, size->name);
4604       if (choice)
4605         display_name = ppd_text_to_utf8 (ppd_file, choice->text);
4606     }
4607
4608   if (display_name == NULL)
4609     display_name = g_strdup (size->name);
4610   
4611   page_setup = gtk_page_setup_new ();
4612   paper_size = gtk_paper_size_new_from_ppd (size->name,
4613                                             display_name,
4614                                             size->width,
4615                                             size->length);
4616   gtk_page_setup_set_paper_size (page_setup, paper_size);
4617   gtk_paper_size_free (paper_size);
4618   
4619   gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
4620   gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
4621   gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
4622   gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
4623   
4624   g_free (display_name);
4625
4626   return page_setup;
4627 }
4628
4629 static GList *
4630 cups_printer_list_papers (GtkPrinter *printer)
4631 {
4632   ppd_file_t *ppd_file;
4633   ppd_size_t *size;
4634   GtkPageSetup *page_setup;
4635   GList *l;
4636   int i;
4637
4638   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4639   if (ppd_file == NULL)
4640     return NULL;
4641
4642   l = NULL;
4643   
4644   for (i = 0; i < ppd_file->num_sizes; i++)
4645     {
4646       size = &ppd_file->sizes[i];      
4647
4648       page_setup = create_page_setup (ppd_file, size);
4649
4650       l = g_list_prepend (l, page_setup);
4651     }
4652
4653   return g_list_reverse (l);
4654 }
4655
4656 static GtkPageSetup *
4657 cups_printer_get_default_page_size (GtkPrinter *printer)
4658 {
4659   ppd_file_t *ppd_file;
4660   ppd_size_t *size;
4661   ppd_option_t *option;
4662
4663
4664   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4665   if (ppd_file == NULL)
4666     return NULL;
4667
4668   option = ppdFindOption (ppd_file, "PageSize");
4669   if (option == NULL)
4670     return NULL;
4671
4672   size = ppdPageSize (ppd_file, option->defchoice); 
4673   if (size == NULL)
4674     return NULL;
4675
4676   return create_page_setup (ppd_file, size);
4677 }
4678
4679 static gboolean
4680 cups_printer_get_hard_margins (GtkPrinter *printer,
4681                                gdouble    *top,
4682                                gdouble    *bottom,
4683                                gdouble    *left,
4684                                gdouble    *right)
4685 {
4686   ppd_file_t *ppd_file;
4687
4688   ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
4689   if (ppd_file == NULL)
4690     return FALSE;
4691
4692   *left = ppd_file->custom_margins[0];
4693   *bottom = ppd_file->custom_margins[1];
4694   *right = ppd_file->custom_margins[2];
4695   *top = ppd_file->custom_margins[3];
4696
4697   return TRUE;
4698 }
4699
4700 static GtkPrintCapabilities
4701 cups_printer_get_capabilities (GtkPrinter *printer)
4702 {
4703   return
4704     GTK_PRINT_CAPABILITY_COPIES |
4705     GTK_PRINT_CAPABILITY_COLLATE |
4706     GTK_PRINT_CAPABILITY_REVERSE |
4707 #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
4708     GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT |
4709 #endif
4710     GTK_PRINT_CAPABILITY_NUMBER_UP;
4711 }