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