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