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