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