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