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